integration_tests.rs

   1use crate::{
   2    rpc::{CLEANUP_TIMEOUT, RECONNECT_TIMEOUT},
   3    tests::{channel_id, room_participants, RoomParticipants, TestClient, TestServer},
   4};
   5use call::{room, ActiveCall, ParticipantLocation, Room};
   6use client::{User, RECEIVE_TIMEOUT};
   7use collections::{HashMap, HashSet};
   8use fs::{repository::GitFileStatus, FakeFs, Fs as _, RemoveOptions};
   9use futures::StreamExt as _;
  10use gpui::{AppContext, BackgroundExecutor, Model, TestAppContext};
  11use language::{
  12    language_settings::{AllLanguageSettings, Formatter},
  13    tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language, LanguageConfig,
  14    LineEnding, OffsetRangeExt, Point, Rope,
  15};
  16use live_kit_client::MacOSDisplay;
  17use lsp::LanguageServerId;
  18use project::{
  19    search::SearchQuery, DiagnosticSummary, FormatTrigger, HoverBlockKind, Project, ProjectPath,
  20};
  21use rand::prelude::*;
  22use rpc::proto::ChannelRole;
  23use serde_json::json;
  24use settings::SettingsStore;
  25use std::{
  26    cell::{Cell, RefCell},
  27    env, future, mem,
  28    path::{Path, PathBuf},
  29    rc::Rc,
  30    sync::{
  31        atomic::{AtomicBool, Ordering::SeqCst},
  32        Arc,
  33    },
  34};
  35use unindent::Unindent as _;
  36
  37#[ctor::ctor]
  38fn init_logger() {
  39    if std::env::var("RUST_LOG").is_ok() {
  40        env_logger::init();
  41    }
  42}
  43
  44#[gpui::test(iterations = 10)]
  45async fn test_basic_calls(
  46    executor: BackgroundExecutor,
  47    cx_a: &mut TestAppContext,
  48    cx_b: &mut TestAppContext,
  49    cx_b2: &mut TestAppContext,
  50    cx_c: &mut TestAppContext,
  51) {
  52    let mut server = TestServer::start(executor.clone()).await;
  53
  54    let client_a = server.create_client(cx_a, "user_a").await;
  55    let client_b = server.create_client(cx_b, "user_b").await;
  56    let client_c = server.create_client(cx_c, "user_c").await;
  57    server
  58        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
  59        .await;
  60
  61    let active_call_a = cx_a.read(ActiveCall::global);
  62    let active_call_b = cx_b.read(ActiveCall::global);
  63    let active_call_c = cx_c.read(ActiveCall::global);
  64
  65    // Call user B from client A.
  66    active_call_a
  67        .update(cx_a, |call, cx| {
  68            call.invite(client_b.user_id().unwrap(), None, cx)
  69        })
  70        .await
  71        .unwrap();
  72    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
  73    executor.run_until_parked();
  74    assert_eq!(
  75        room_participants(&room_a, cx_a),
  76        RoomParticipants {
  77            remote: Default::default(),
  78            pending: vec!["user_b".to_string()]
  79        }
  80    );
  81
  82    // User B receives the call.
  83
  84    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
  85    let call_b = incoming_call_b.next().await.unwrap().unwrap();
  86    assert_eq!(call_b.calling_user.github_login, "user_a");
  87
  88    // User B connects via another client and also receives a ring on the newly-connected client.
  89    let _client_b2 = server.create_client(cx_b2, "user_b").await;
  90    let active_call_b2 = cx_b2.read(ActiveCall::global);
  91
  92    let mut incoming_call_b2 = active_call_b2.read_with(cx_b2, |call, _| call.incoming());
  93    executor.run_until_parked();
  94    let call_b2 = incoming_call_b2.next().await.unwrap().unwrap();
  95    assert_eq!(call_b2.calling_user.github_login, "user_a");
  96
  97    // User B joins the room using the first client.
  98    active_call_b
  99        .update(cx_b, |call, cx| call.accept_incoming(cx))
 100        .await
 101        .unwrap();
 102
 103    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 104    assert!(incoming_call_b.next().await.unwrap().is_none());
 105
 106    executor.run_until_parked();
 107    assert_eq!(
 108        room_participants(&room_a, cx_a),
 109        RoomParticipants {
 110            remote: vec!["user_b".to_string()],
 111            pending: Default::default()
 112        }
 113    );
 114    assert_eq!(
 115        room_participants(&room_b, cx_b),
 116        RoomParticipants {
 117            remote: vec!["user_a".to_string()],
 118            pending: Default::default()
 119        }
 120    );
 121
 122    // Call user C from client B.
 123
 124    let mut incoming_call_c = active_call_c.read_with(cx_c, |call, _| call.incoming());
 125    active_call_b
 126        .update(cx_b, |call, cx| {
 127            call.invite(client_c.user_id().unwrap(), None, cx)
 128        })
 129        .await
 130        .unwrap();
 131
 132    executor.run_until_parked();
 133    assert_eq!(
 134        room_participants(&room_a, cx_a),
 135        RoomParticipants {
 136            remote: vec!["user_b".to_string()],
 137            pending: vec!["user_c".to_string()]
 138        }
 139    );
 140    assert_eq!(
 141        room_participants(&room_b, cx_b),
 142        RoomParticipants {
 143            remote: vec!["user_a".to_string()],
 144            pending: vec!["user_c".to_string()]
 145        }
 146    );
 147
 148    // User C receives the call, but declines it.
 149    let call_c = incoming_call_c.next().await.unwrap().unwrap();
 150    assert_eq!(call_c.calling_user.github_login, "user_b");
 151    active_call_c.update(cx_c, |call, cx| call.decline_incoming(cx).unwrap());
 152    assert!(incoming_call_c.next().await.unwrap().is_none());
 153
 154    executor.run_until_parked();
 155    assert_eq!(
 156        room_participants(&room_a, cx_a),
 157        RoomParticipants {
 158            remote: vec!["user_b".to_string()],
 159            pending: Default::default()
 160        }
 161    );
 162    assert_eq!(
 163        room_participants(&room_b, cx_b),
 164        RoomParticipants {
 165            remote: vec!["user_a".to_string()],
 166            pending: Default::default()
 167        }
 168    );
 169
 170    // Call user C again from user A.
 171    active_call_a
 172        .update(cx_a, |call, cx| {
 173            call.invite(client_c.user_id().unwrap(), None, cx)
 174        })
 175        .await
 176        .unwrap();
 177
 178    executor.run_until_parked();
 179    assert_eq!(
 180        room_participants(&room_a, cx_a),
 181        RoomParticipants {
 182            remote: vec!["user_b".to_string()],
 183            pending: vec!["user_c".to_string()]
 184        }
 185    );
 186    assert_eq!(
 187        room_participants(&room_b, cx_b),
 188        RoomParticipants {
 189            remote: vec!["user_a".to_string()],
 190            pending: vec!["user_c".to_string()]
 191        }
 192    );
 193
 194    // User C accepts the call.
 195    let call_c = incoming_call_c.next().await.unwrap().unwrap();
 196    assert_eq!(call_c.calling_user.github_login, "user_a");
 197    active_call_c
 198        .update(cx_c, |call, cx| call.accept_incoming(cx))
 199        .await
 200        .unwrap();
 201    assert!(incoming_call_c.next().await.unwrap().is_none());
 202
 203    let room_c = active_call_c.read_with(cx_c, |call, _| call.room().unwrap().clone());
 204
 205    executor.run_until_parked();
 206    assert_eq!(
 207        room_participants(&room_a, cx_a),
 208        RoomParticipants {
 209            remote: vec!["user_b".to_string(), "user_c".to_string()],
 210            pending: Default::default()
 211        }
 212    );
 213    assert_eq!(
 214        room_participants(&room_b, cx_b),
 215        RoomParticipants {
 216            remote: vec!["user_a".to_string(), "user_c".to_string()],
 217            pending: Default::default()
 218        }
 219    );
 220    assert_eq!(
 221        room_participants(&room_c, cx_c),
 222        RoomParticipants {
 223            remote: vec!["user_a".to_string(), "user_b".to_string()],
 224            pending: Default::default()
 225        }
 226    );
 227
 228    // User A shares their screen
 229    let display = MacOSDisplay::new();
 230    let events_b = active_call_events(cx_b);
 231    let events_c = active_call_events(cx_c);
 232    active_call_a
 233        .update(cx_a, |call, cx| {
 234            call.room().unwrap().update(cx, |room, cx| {
 235                room.set_display_sources(vec![display.clone()]);
 236                room.share_screen(cx)
 237            })
 238        })
 239        .await
 240        .unwrap();
 241
 242    executor.run_until_parked();
 243
 244    // User B observes the remote screen sharing track.
 245    assert_eq!(events_b.borrow().len(), 1);
 246    let event_b = events_b.borrow().first().unwrap().clone();
 247    if let call::room::Event::RemoteVideoTracksChanged { participant_id } = event_b {
 248        assert_eq!(participant_id, client_a.peer_id().unwrap());
 249
 250        room_b.read_with(cx_b, |room, _| {
 251            assert_eq!(
 252                room.remote_participants()[&client_a.user_id().unwrap()]
 253                    .video_tracks
 254                    .len(),
 255                1
 256            );
 257        });
 258    } else {
 259        panic!("unexpected event")
 260    }
 261
 262    // User C observes the remote screen sharing track.
 263    assert_eq!(events_c.borrow().len(), 1);
 264    let event_c = events_c.borrow().first().unwrap().clone();
 265    if let call::room::Event::RemoteVideoTracksChanged { participant_id } = event_c {
 266        assert_eq!(participant_id, client_a.peer_id().unwrap());
 267
 268        room_c.read_with(cx_c, |room, _| {
 269            assert_eq!(
 270                room.remote_participants()[&client_a.user_id().unwrap()]
 271                    .video_tracks
 272                    .len(),
 273                1
 274            );
 275        });
 276    } else {
 277        panic!("unexpected event")
 278    }
 279
 280    // User A leaves the room.
 281    active_call_a
 282        .update(cx_a, |call, cx| {
 283            let hang_up = call.hang_up(cx);
 284            assert!(call.room().is_none());
 285            hang_up
 286        })
 287        .await
 288        .unwrap();
 289    executor.run_until_parked();
 290    assert_eq!(
 291        room_participants(&room_a, cx_a),
 292        RoomParticipants {
 293            remote: Default::default(),
 294            pending: Default::default()
 295        }
 296    );
 297    assert_eq!(
 298        room_participants(&room_b, cx_b),
 299        RoomParticipants {
 300            remote: vec!["user_c".to_string()],
 301            pending: Default::default()
 302        }
 303    );
 304    assert_eq!(
 305        room_participants(&room_c, cx_c),
 306        RoomParticipants {
 307            remote: vec!["user_b".to_string()],
 308            pending: Default::default()
 309        }
 310    );
 311
 312    // User B gets disconnected from the LiveKit server, which causes them
 313    // to automatically leave the room. User C leaves the room as well because
 314    // nobody else is in there.
 315    server
 316        .test_live_kit_server
 317        .disconnect_client(client_b.user_id().unwrap().to_string())
 318        .await;
 319    executor.run_until_parked();
 320
 321    active_call_b.read_with(cx_b, |call, _| assert!(call.room().is_none()));
 322
 323    active_call_c.read_with(cx_c, |call, _| assert!(call.room().is_none()));
 324    assert_eq!(
 325        room_participants(&room_a, cx_a),
 326        RoomParticipants {
 327            remote: Default::default(),
 328            pending: Default::default()
 329        }
 330    );
 331    assert_eq!(
 332        room_participants(&room_b, cx_b),
 333        RoomParticipants {
 334            remote: Default::default(),
 335            pending: Default::default()
 336        }
 337    );
 338    assert_eq!(
 339        room_participants(&room_c, cx_c),
 340        RoomParticipants {
 341            remote: Default::default(),
 342            pending: Default::default()
 343        }
 344    );
 345}
 346
 347#[gpui::test(iterations = 10)]
 348async fn test_calling_multiple_users_simultaneously(
 349    executor: BackgroundExecutor,
 350    cx_a: &mut TestAppContext,
 351    cx_b: &mut TestAppContext,
 352    cx_c: &mut TestAppContext,
 353    cx_d: &mut TestAppContext,
 354) {
 355    let mut server = TestServer::start(executor.clone()).await;
 356
 357    let client_a = server.create_client(cx_a, "user_a").await;
 358    let client_b = server.create_client(cx_b, "user_b").await;
 359    let client_c = server.create_client(cx_c, "user_c").await;
 360    let client_d = server.create_client(cx_d, "user_d").await;
 361    server
 362        .make_contacts(&mut [
 363            (&client_a, cx_a),
 364            (&client_b, cx_b),
 365            (&client_c, cx_c),
 366            (&client_d, cx_d),
 367        ])
 368        .await;
 369
 370    let active_call_a = cx_a.read(ActiveCall::global);
 371    let active_call_b = cx_b.read(ActiveCall::global);
 372    let active_call_c = cx_c.read(ActiveCall::global);
 373    let active_call_d = cx_d.read(ActiveCall::global);
 374
 375    // Simultaneously call user B and user C from client A.
 376    let b_invite = active_call_a.update(cx_a, |call, cx| {
 377        call.invite(client_b.user_id().unwrap(), None, cx)
 378    });
 379    let c_invite = active_call_a.update(cx_a, |call, cx| {
 380        call.invite(client_c.user_id().unwrap(), None, cx)
 381    });
 382    b_invite.await.unwrap();
 383    c_invite.await.unwrap();
 384
 385    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 386    executor.run_until_parked();
 387    assert_eq!(
 388        room_participants(&room_a, cx_a),
 389        RoomParticipants {
 390            remote: Default::default(),
 391            pending: vec!["user_b".to_string(), "user_c".to_string()]
 392        }
 393    );
 394
 395    // Call client D from client A.
 396    active_call_a
 397        .update(cx_a, |call, cx| {
 398            call.invite(client_d.user_id().unwrap(), None, cx)
 399        })
 400        .await
 401        .unwrap();
 402    executor.run_until_parked();
 403    assert_eq!(
 404        room_participants(&room_a, cx_a),
 405        RoomParticipants {
 406            remote: Default::default(),
 407            pending: vec![
 408                "user_b".to_string(),
 409                "user_c".to_string(),
 410                "user_d".to_string()
 411            ]
 412        }
 413    );
 414
 415    // Accept the call on all clients simultaneously.
 416    let accept_b = active_call_b.update(cx_b, |call, cx| call.accept_incoming(cx));
 417    let accept_c = active_call_c.update(cx_c, |call, cx| call.accept_incoming(cx));
 418    let accept_d = active_call_d.update(cx_d, |call, cx| call.accept_incoming(cx));
 419    accept_b.await.unwrap();
 420    accept_c.await.unwrap();
 421    accept_d.await.unwrap();
 422
 423    executor.run_until_parked();
 424
 425    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 426
 427    let room_c = active_call_c.read_with(cx_c, |call, _| call.room().unwrap().clone());
 428
 429    let room_d = active_call_d.read_with(cx_d, |call, _| call.room().unwrap().clone());
 430    assert_eq!(
 431        room_participants(&room_a, cx_a),
 432        RoomParticipants {
 433            remote: vec![
 434                "user_b".to_string(),
 435                "user_c".to_string(),
 436                "user_d".to_string(),
 437            ],
 438            pending: Default::default()
 439        }
 440    );
 441    assert_eq!(
 442        room_participants(&room_b, cx_b),
 443        RoomParticipants {
 444            remote: vec![
 445                "user_a".to_string(),
 446                "user_c".to_string(),
 447                "user_d".to_string(),
 448            ],
 449            pending: Default::default()
 450        }
 451    );
 452    assert_eq!(
 453        room_participants(&room_c, cx_c),
 454        RoomParticipants {
 455            remote: vec![
 456                "user_a".to_string(),
 457                "user_b".to_string(),
 458                "user_d".to_string(),
 459            ],
 460            pending: Default::default()
 461        }
 462    );
 463    assert_eq!(
 464        room_participants(&room_d, cx_d),
 465        RoomParticipants {
 466            remote: vec![
 467                "user_a".to_string(),
 468                "user_b".to_string(),
 469                "user_c".to_string(),
 470            ],
 471            pending: Default::default()
 472        }
 473    );
 474}
 475
 476#[gpui::test(iterations = 10)]
 477async fn test_joining_channels_and_calling_multiple_users_simultaneously(
 478    executor: BackgroundExecutor,
 479    cx_a: &mut TestAppContext,
 480    cx_b: &mut TestAppContext,
 481    cx_c: &mut TestAppContext,
 482) {
 483    let mut server = TestServer::start(executor.clone()).await;
 484
 485    let client_a = server.create_client(cx_a, "user_a").await;
 486    let client_b = server.create_client(cx_b, "user_b").await;
 487    let client_c = server.create_client(cx_c, "user_c").await;
 488    server
 489        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
 490        .await;
 491
 492    let channel_1 = server
 493        .make_channel(
 494            "channel1",
 495            None,
 496            (&client_a, cx_a),
 497            &mut [(&client_b, cx_b), (&client_c, cx_c)],
 498        )
 499        .await;
 500
 501    let channel_2 = server
 502        .make_channel(
 503            "channel2",
 504            None,
 505            (&client_a, cx_a),
 506            &mut [(&client_b, cx_b), (&client_c, cx_c)],
 507        )
 508        .await;
 509
 510    let active_call_a = cx_a.read(ActiveCall::global);
 511
 512    // Simultaneously join channel 1 and then channel 2
 513    active_call_a
 514        .update(cx_a, |call, cx| call.join_channel(channel_1, cx))
 515        .detach();
 516    let join_channel_2 = active_call_a.update(cx_a, |call, cx| call.join_channel(channel_2, cx));
 517
 518    join_channel_2.await.unwrap();
 519
 520    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 521    executor.run_until_parked();
 522
 523    assert_eq!(channel_id(&room_a, cx_a), Some(channel_2));
 524
 525    // Leave the room
 526    active_call_a
 527        .update(cx_a, |call, cx| {
 528            let hang_up = call.hang_up(cx);
 529            hang_up
 530        })
 531        .await
 532        .unwrap();
 533
 534    // Initiating invites and then joining a channel should fail gracefully
 535    let b_invite = active_call_a.update(cx_a, |call, cx| {
 536        call.invite(client_b.user_id().unwrap(), None, cx)
 537    });
 538    let c_invite = active_call_a.update(cx_a, |call, cx| {
 539        call.invite(client_c.user_id().unwrap(), None, cx)
 540    });
 541
 542    let join_channel = active_call_a.update(cx_a, |call, cx| call.join_channel(channel_1, cx));
 543
 544    b_invite.await.unwrap();
 545    c_invite.await.unwrap();
 546    join_channel.await.unwrap();
 547
 548    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 549    executor.run_until_parked();
 550
 551    assert_eq!(
 552        room_participants(&room_a, cx_a),
 553        RoomParticipants {
 554            remote: Default::default(),
 555            pending: vec!["user_b".to_string(), "user_c".to_string()]
 556        }
 557    );
 558
 559    assert_eq!(channel_id(&room_a, cx_a), None);
 560
 561    // Leave the room
 562    active_call_a
 563        .update(cx_a, |call, cx| {
 564            let hang_up = call.hang_up(cx);
 565            hang_up
 566        })
 567        .await
 568        .unwrap();
 569
 570    // Simultaneously join channel 1 and call user B and user C from client A.
 571    let join_channel = active_call_a.update(cx_a, |call, cx| call.join_channel(channel_1, cx));
 572
 573    let b_invite = active_call_a.update(cx_a, |call, cx| {
 574        call.invite(client_b.user_id().unwrap(), None, cx)
 575    });
 576    let c_invite = active_call_a.update(cx_a, |call, cx| {
 577        call.invite(client_c.user_id().unwrap(), None, cx)
 578    });
 579
 580    join_channel.await.unwrap();
 581    b_invite.await.unwrap();
 582    c_invite.await.unwrap();
 583
 584    active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 585    executor.run_until_parked();
 586}
 587
 588#[gpui::test(iterations = 10)]
 589async fn test_room_uniqueness(
 590    executor: BackgroundExecutor,
 591    cx_a: &mut TestAppContext,
 592    cx_a2: &mut TestAppContext,
 593    cx_b: &mut TestAppContext,
 594    cx_b2: &mut TestAppContext,
 595    cx_c: &mut TestAppContext,
 596) {
 597    let mut server = TestServer::start(executor.clone()).await;
 598    let client_a = server.create_client(cx_a, "user_a").await;
 599    let _client_a2 = server.create_client(cx_a2, "user_a").await;
 600    let client_b = server.create_client(cx_b, "user_b").await;
 601    let _client_b2 = server.create_client(cx_b2, "user_b").await;
 602    let client_c = server.create_client(cx_c, "user_c").await;
 603    server
 604        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
 605        .await;
 606
 607    let active_call_a = cx_a.read(ActiveCall::global);
 608    let active_call_a2 = cx_a2.read(ActiveCall::global);
 609    let active_call_b = cx_b.read(ActiveCall::global);
 610    let active_call_b2 = cx_b2.read(ActiveCall::global);
 611    let active_call_c = cx_c.read(ActiveCall::global);
 612
 613    // Call user B from client A.
 614    active_call_a
 615        .update(cx_a, |call, cx| {
 616            call.invite(client_b.user_id().unwrap(), None, cx)
 617        })
 618        .await
 619        .unwrap();
 620
 621    // Ensure a new room can't be created given user A just created one.
 622    active_call_a2
 623        .update(cx_a2, |call, cx| {
 624            call.invite(client_c.user_id().unwrap(), None, cx)
 625        })
 626        .await
 627        .unwrap_err();
 628
 629    active_call_a2.read_with(cx_a2, |call, _| assert!(call.room().is_none()));
 630
 631    // User B receives the call from user A.
 632
 633    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
 634    let call_b1 = incoming_call_b.next().await.unwrap().unwrap();
 635    assert_eq!(call_b1.calling_user.github_login, "user_a");
 636
 637    // Ensure calling users A and B from client C fails.
 638    active_call_c
 639        .update(cx_c, |call, cx| {
 640            call.invite(client_a.user_id().unwrap(), None, cx)
 641        })
 642        .await
 643        .unwrap_err();
 644    active_call_c
 645        .update(cx_c, |call, cx| {
 646            call.invite(client_b.user_id().unwrap(), None, cx)
 647        })
 648        .await
 649        .unwrap_err();
 650
 651    // Ensure User B can't create a room while they still have an incoming call.
 652    active_call_b2
 653        .update(cx_b2, |call, cx| {
 654            call.invite(client_c.user_id().unwrap(), None, cx)
 655        })
 656        .await
 657        .unwrap_err();
 658
 659    active_call_b2.read_with(cx_b2, |call, _| assert!(call.room().is_none()));
 660
 661    // User B joins the room and calling them after they've joined still fails.
 662    active_call_b
 663        .update(cx_b, |call, cx| call.accept_incoming(cx))
 664        .await
 665        .unwrap();
 666    active_call_c
 667        .update(cx_c, |call, cx| {
 668            call.invite(client_b.user_id().unwrap(), None, cx)
 669        })
 670        .await
 671        .unwrap_err();
 672
 673    // Ensure User B can't create a room while they belong to another room.
 674    active_call_b2
 675        .update(cx_b2, |call, cx| {
 676            call.invite(client_c.user_id().unwrap(), None, cx)
 677        })
 678        .await
 679        .unwrap_err();
 680
 681    active_call_b2.read_with(cx_b2, |call, _| assert!(call.room().is_none()));
 682
 683    // Client C can successfully call client B after client B leaves the room.
 684    active_call_b
 685        .update(cx_b, |call, cx| call.hang_up(cx))
 686        .await
 687        .unwrap();
 688    executor.run_until_parked();
 689    active_call_c
 690        .update(cx_c, |call, cx| {
 691            call.invite(client_b.user_id().unwrap(), None, cx)
 692        })
 693        .await
 694        .unwrap();
 695    executor.run_until_parked();
 696    let call_b2 = incoming_call_b.next().await.unwrap().unwrap();
 697    assert_eq!(call_b2.calling_user.github_login, "user_c");
 698}
 699
 700#[gpui::test(iterations = 10)]
 701async fn test_client_disconnecting_from_room(
 702    executor: BackgroundExecutor,
 703    cx_a: &mut TestAppContext,
 704    cx_b: &mut TestAppContext,
 705) {
 706    let mut server = TestServer::start(executor.clone()).await;
 707    let client_a = server.create_client(cx_a, "user_a").await;
 708    let client_b = server.create_client(cx_b, "user_b").await;
 709    server
 710        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
 711        .await;
 712
 713    let active_call_a = cx_a.read(ActiveCall::global);
 714    let active_call_b = cx_b.read(ActiveCall::global);
 715
 716    // Call user B from client A.
 717    active_call_a
 718        .update(cx_a, |call, cx| {
 719            call.invite(client_b.user_id().unwrap(), None, cx)
 720        })
 721        .await
 722        .unwrap();
 723
 724    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 725
 726    // User B receives the call and joins the room.
 727
 728    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
 729    incoming_call_b.next().await.unwrap().unwrap();
 730    active_call_b
 731        .update(cx_b, |call, cx| call.accept_incoming(cx))
 732        .await
 733        .unwrap();
 734
 735    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 736    executor.run_until_parked();
 737    assert_eq!(
 738        room_participants(&room_a, cx_a),
 739        RoomParticipants {
 740            remote: vec!["user_b".to_string()],
 741            pending: Default::default()
 742        }
 743    );
 744    assert_eq!(
 745        room_participants(&room_b, cx_b),
 746        RoomParticipants {
 747            remote: vec!["user_a".to_string()],
 748            pending: Default::default()
 749        }
 750    );
 751
 752    // User A automatically reconnects to the room upon disconnection.
 753    server.disconnect_client(client_a.peer_id().unwrap());
 754    executor.advance_clock(RECEIVE_TIMEOUT);
 755    executor.run_until_parked();
 756    assert_eq!(
 757        room_participants(&room_a, cx_a),
 758        RoomParticipants {
 759            remote: vec!["user_b".to_string()],
 760            pending: Default::default()
 761        }
 762    );
 763    assert_eq!(
 764        room_participants(&room_b, cx_b),
 765        RoomParticipants {
 766            remote: vec!["user_a".to_string()],
 767            pending: Default::default()
 768        }
 769    );
 770
 771    // When user A disconnects, both client A and B clear their room on the active call.
 772    server.forbid_connections();
 773    server.disconnect_client(client_a.peer_id().unwrap());
 774    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
 775
 776    active_call_a.read_with(cx_a, |call, _| assert!(call.room().is_none()));
 777
 778    active_call_b.read_with(cx_b, |call, _| assert!(call.room().is_none()));
 779    assert_eq!(
 780        room_participants(&room_a, cx_a),
 781        RoomParticipants {
 782            remote: Default::default(),
 783            pending: Default::default()
 784        }
 785    );
 786    assert_eq!(
 787        room_participants(&room_b, cx_b),
 788        RoomParticipants {
 789            remote: Default::default(),
 790            pending: Default::default()
 791        }
 792    );
 793
 794    // Allow user A to reconnect to the server.
 795    server.allow_connections();
 796    executor.advance_clock(RECEIVE_TIMEOUT);
 797
 798    // Call user B again from client A.
 799    active_call_a
 800        .update(cx_a, |call, cx| {
 801            call.invite(client_b.user_id().unwrap(), None, cx)
 802        })
 803        .await
 804        .unwrap();
 805
 806    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 807
 808    // User B receives the call and joins the room.
 809
 810    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
 811    incoming_call_b.next().await.unwrap().unwrap();
 812    active_call_b
 813        .update(cx_b, |call, cx| call.accept_incoming(cx))
 814        .await
 815        .unwrap();
 816
 817    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 818    executor.run_until_parked();
 819    assert_eq!(
 820        room_participants(&room_a, cx_a),
 821        RoomParticipants {
 822            remote: vec!["user_b".to_string()],
 823            pending: Default::default()
 824        }
 825    );
 826    assert_eq!(
 827        room_participants(&room_b, cx_b),
 828        RoomParticipants {
 829            remote: vec!["user_a".to_string()],
 830            pending: Default::default()
 831        }
 832    );
 833
 834    // User B gets disconnected from the LiveKit server, which causes it
 835    // to automatically leave the room.
 836    server
 837        .test_live_kit_server
 838        .disconnect_client(client_b.user_id().unwrap().to_string())
 839        .await;
 840    executor.run_until_parked();
 841    active_call_a.update(cx_a, |call, _| assert!(call.room().is_none()));
 842    active_call_b.update(cx_b, |call, _| assert!(call.room().is_none()));
 843    assert_eq!(
 844        room_participants(&room_a, cx_a),
 845        RoomParticipants {
 846            remote: Default::default(),
 847            pending: Default::default()
 848        }
 849    );
 850    assert_eq!(
 851        room_participants(&room_b, cx_b),
 852        RoomParticipants {
 853            remote: Default::default(),
 854            pending: Default::default()
 855        }
 856    );
 857}
 858
 859#[gpui::test(iterations = 10)]
 860async fn test_server_restarts(
 861    executor: BackgroundExecutor,
 862    cx_a: &mut TestAppContext,
 863    cx_b: &mut TestAppContext,
 864    cx_c: &mut TestAppContext,
 865    cx_d: &mut TestAppContext,
 866) {
 867    let mut server = TestServer::start(executor.clone()).await;
 868    let client_a = server.create_client(cx_a, "user_a").await;
 869    client_a
 870        .fs()
 871        .insert_tree("/a", json!({ "a.txt": "a-contents" }))
 872        .await;
 873
 874    // Invite client B to collaborate on a project
 875    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
 876
 877    let client_b = server.create_client(cx_b, "user_b").await;
 878    let client_c = server.create_client(cx_c, "user_c").await;
 879    let client_d = server.create_client(cx_d, "user_d").await;
 880    server
 881        .make_contacts(&mut [
 882            (&client_a, cx_a),
 883            (&client_b, cx_b),
 884            (&client_c, cx_c),
 885            (&client_d, cx_d),
 886        ])
 887        .await;
 888
 889    let active_call_a = cx_a.read(ActiveCall::global);
 890    let active_call_b = cx_b.read(ActiveCall::global);
 891    let active_call_c = cx_c.read(ActiveCall::global);
 892    let active_call_d = cx_d.read(ActiveCall::global);
 893
 894    // User A calls users B, C, and D.
 895    active_call_a
 896        .update(cx_a, |call, cx| {
 897            call.invite(client_b.user_id().unwrap(), Some(project_a.clone()), cx)
 898        })
 899        .await
 900        .unwrap();
 901    active_call_a
 902        .update(cx_a, |call, cx| {
 903            call.invite(client_c.user_id().unwrap(), Some(project_a.clone()), cx)
 904        })
 905        .await
 906        .unwrap();
 907    active_call_a
 908        .update(cx_a, |call, cx| {
 909            call.invite(client_d.user_id().unwrap(), Some(project_a.clone()), cx)
 910        })
 911        .await
 912        .unwrap();
 913
 914    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 915
 916    // User B receives the call and joins the room.
 917
 918    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
 919    assert!(incoming_call_b.next().await.unwrap().is_some());
 920    active_call_b
 921        .update(cx_b, |call, cx| call.accept_incoming(cx))
 922        .await
 923        .unwrap();
 924
 925    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 926
 927    // User C receives the call and joins the room.
 928
 929    let mut incoming_call_c = active_call_c.read_with(cx_c, |call, _| call.incoming());
 930    assert!(incoming_call_c.next().await.unwrap().is_some());
 931    active_call_c
 932        .update(cx_c, |call, cx| call.accept_incoming(cx))
 933        .await
 934        .unwrap();
 935
 936    let room_c = active_call_c.read_with(cx_c, |call, _| call.room().unwrap().clone());
 937
 938    // User D receives the call but doesn't join the room yet.
 939
 940    let mut incoming_call_d = active_call_d.read_with(cx_d, |call, _| call.incoming());
 941    assert!(incoming_call_d.next().await.unwrap().is_some());
 942
 943    executor.run_until_parked();
 944    assert_eq!(
 945        room_participants(&room_a, cx_a),
 946        RoomParticipants {
 947            remote: vec!["user_b".to_string(), "user_c".to_string()],
 948            pending: vec!["user_d".to_string()]
 949        }
 950    );
 951    assert_eq!(
 952        room_participants(&room_b, cx_b),
 953        RoomParticipants {
 954            remote: vec!["user_a".to_string(), "user_c".to_string()],
 955            pending: vec!["user_d".to_string()]
 956        }
 957    );
 958    assert_eq!(
 959        room_participants(&room_c, cx_c),
 960        RoomParticipants {
 961            remote: vec!["user_a".to_string(), "user_b".to_string()],
 962            pending: vec!["user_d".to_string()]
 963        }
 964    );
 965
 966    // The server is torn down.
 967    server.reset().await;
 968
 969    // Users A and B reconnect to the call. User C has troubles reconnecting, so it leaves the room.
 970    client_c.override_establish_connection(|_, cx| cx.spawn(|_| future::pending()));
 971    executor.advance_clock(RECONNECT_TIMEOUT);
 972    assert_eq!(
 973        room_participants(&room_a, cx_a),
 974        RoomParticipants {
 975            remote: vec!["user_b".to_string(), "user_c".to_string()],
 976            pending: vec!["user_d".to_string()]
 977        }
 978    );
 979    assert_eq!(
 980        room_participants(&room_b, cx_b),
 981        RoomParticipants {
 982            remote: vec!["user_a".to_string(), "user_c".to_string()],
 983            pending: vec!["user_d".to_string()]
 984        }
 985    );
 986    assert_eq!(
 987        room_participants(&room_c, cx_c),
 988        RoomParticipants {
 989            remote: vec![],
 990            pending: vec![]
 991        }
 992    );
 993
 994    // User D is notified again of the incoming call and accepts it.
 995    assert!(incoming_call_d.next().await.unwrap().is_some());
 996    active_call_d
 997        .update(cx_d, |call, cx| call.accept_incoming(cx))
 998        .await
 999        .unwrap();
1000    executor.run_until_parked();
1001
1002    let room_d = active_call_d.read_with(cx_d, |call, _| call.room().unwrap().clone());
1003    assert_eq!(
1004        room_participants(&room_a, cx_a),
1005        RoomParticipants {
1006            remote: vec![
1007                "user_b".to_string(),
1008                "user_c".to_string(),
1009                "user_d".to_string(),
1010            ],
1011            pending: vec![]
1012        }
1013    );
1014    assert_eq!(
1015        room_participants(&room_b, cx_b),
1016        RoomParticipants {
1017            remote: vec![
1018                "user_a".to_string(),
1019                "user_c".to_string(),
1020                "user_d".to_string(),
1021            ],
1022            pending: vec![]
1023        }
1024    );
1025    assert_eq!(
1026        room_participants(&room_c, cx_c),
1027        RoomParticipants {
1028            remote: vec![],
1029            pending: vec![]
1030        }
1031    );
1032    assert_eq!(
1033        room_participants(&room_d, cx_d),
1034        RoomParticipants {
1035            remote: vec![
1036                "user_a".to_string(),
1037                "user_b".to_string(),
1038                "user_c".to_string(),
1039            ],
1040            pending: vec![]
1041        }
1042    );
1043
1044    // The server finishes restarting, cleaning up stale connections.
1045    server.start().await.unwrap();
1046    executor.advance_clock(CLEANUP_TIMEOUT);
1047    assert_eq!(
1048        room_participants(&room_a, cx_a),
1049        RoomParticipants {
1050            remote: vec!["user_b".to_string(), "user_d".to_string()],
1051            pending: vec![]
1052        }
1053    );
1054    assert_eq!(
1055        room_participants(&room_b, cx_b),
1056        RoomParticipants {
1057            remote: vec!["user_a".to_string(), "user_d".to_string()],
1058            pending: vec![]
1059        }
1060    );
1061    assert_eq!(
1062        room_participants(&room_c, cx_c),
1063        RoomParticipants {
1064            remote: vec![],
1065            pending: vec![]
1066        }
1067    );
1068    assert_eq!(
1069        room_participants(&room_d, cx_d),
1070        RoomParticipants {
1071            remote: vec!["user_a".to_string(), "user_b".to_string()],
1072            pending: vec![]
1073        }
1074    );
1075
1076    // User D hangs up.
1077    active_call_d
1078        .update(cx_d, |call, cx| call.hang_up(cx))
1079        .await
1080        .unwrap();
1081    executor.run_until_parked();
1082    assert_eq!(
1083        room_participants(&room_a, cx_a),
1084        RoomParticipants {
1085            remote: vec!["user_b".to_string()],
1086            pending: vec![]
1087        }
1088    );
1089    assert_eq!(
1090        room_participants(&room_b, cx_b),
1091        RoomParticipants {
1092            remote: vec!["user_a".to_string()],
1093            pending: vec![]
1094        }
1095    );
1096    assert_eq!(
1097        room_participants(&room_c, cx_c),
1098        RoomParticipants {
1099            remote: vec![],
1100            pending: vec![]
1101        }
1102    );
1103    assert_eq!(
1104        room_participants(&room_d, cx_d),
1105        RoomParticipants {
1106            remote: vec![],
1107            pending: vec![]
1108        }
1109    );
1110
1111    // User B calls user D again.
1112    active_call_b
1113        .update(cx_b, |call, cx| {
1114            call.invite(client_d.user_id().unwrap(), None, cx)
1115        })
1116        .await
1117        .unwrap();
1118
1119    // User D receives the call but doesn't join the room yet.
1120
1121    let mut incoming_call_d = active_call_d.read_with(cx_d, |call, _| call.incoming());
1122    assert!(incoming_call_d.next().await.unwrap().is_some());
1123    executor.run_until_parked();
1124    assert_eq!(
1125        room_participants(&room_a, cx_a),
1126        RoomParticipants {
1127            remote: vec!["user_b".to_string()],
1128            pending: vec!["user_d".to_string()]
1129        }
1130    );
1131    assert_eq!(
1132        room_participants(&room_b, cx_b),
1133        RoomParticipants {
1134            remote: vec!["user_a".to_string()],
1135            pending: vec!["user_d".to_string()]
1136        }
1137    );
1138
1139    // The server is torn down.
1140    server.reset().await;
1141
1142    // Users A and B have troubles reconnecting, so they leave the room.
1143    client_a.override_establish_connection(|_, cx| cx.spawn(|_| future::pending()));
1144    client_b.override_establish_connection(|_, cx| cx.spawn(|_| future::pending()));
1145    client_c.override_establish_connection(|_, cx| cx.spawn(|_| future::pending()));
1146    executor.advance_clock(RECONNECT_TIMEOUT);
1147    assert_eq!(
1148        room_participants(&room_a, cx_a),
1149        RoomParticipants {
1150            remote: vec![],
1151            pending: vec![]
1152        }
1153    );
1154    assert_eq!(
1155        room_participants(&room_b, cx_b),
1156        RoomParticipants {
1157            remote: vec![],
1158            pending: vec![]
1159        }
1160    );
1161
1162    // User D is notified again of the incoming call but doesn't accept it.
1163    assert!(incoming_call_d.next().await.unwrap().is_some());
1164
1165    // The server finishes restarting, cleaning up stale connections and canceling the
1166    // call to user D because the room has become empty.
1167    server.start().await.unwrap();
1168    executor.advance_clock(CLEANUP_TIMEOUT);
1169    assert!(incoming_call_d.next().await.unwrap().is_none());
1170}
1171
1172#[gpui::test(iterations = 10)]
1173async fn test_calls_on_multiple_connections(
1174    executor: BackgroundExecutor,
1175    cx_a: &mut TestAppContext,
1176    cx_b1: &mut TestAppContext,
1177    cx_b2: &mut TestAppContext,
1178) {
1179    let mut server = TestServer::start(executor.clone()).await;
1180    let client_a = server.create_client(cx_a, "user_a").await;
1181    let client_b1 = server.create_client(cx_b1, "user_b").await;
1182    let client_b2 = server.create_client(cx_b2, "user_b").await;
1183    server
1184        .make_contacts(&mut [(&client_a, cx_a), (&client_b1, cx_b1)])
1185        .await;
1186
1187    let active_call_a = cx_a.read(ActiveCall::global);
1188    let active_call_b1 = cx_b1.read(ActiveCall::global);
1189    let active_call_b2 = cx_b2.read(ActiveCall::global);
1190
1191    let mut incoming_call_b1 = active_call_b1.read_with(cx_b1, |call, _| call.incoming());
1192
1193    let mut incoming_call_b2 = active_call_b2.read_with(cx_b2, |call, _| call.incoming());
1194    assert!(incoming_call_b1.next().await.unwrap().is_none());
1195    assert!(incoming_call_b2.next().await.unwrap().is_none());
1196
1197    // Call user B from client A, ensuring both clients for user B ring.
1198    active_call_a
1199        .update(cx_a, |call, cx| {
1200            call.invite(client_b1.user_id().unwrap(), None, cx)
1201        })
1202        .await
1203        .unwrap();
1204    executor.run_until_parked();
1205    assert!(incoming_call_b1.next().await.unwrap().is_some());
1206    assert!(incoming_call_b2.next().await.unwrap().is_some());
1207
1208    // User B declines the call on one of the two connections, causing both connections
1209    // to stop ringing.
1210    active_call_b2.update(cx_b2, |call, cx| call.decline_incoming(cx).unwrap());
1211    executor.run_until_parked();
1212    assert!(incoming_call_b1.next().await.unwrap().is_none());
1213    assert!(incoming_call_b2.next().await.unwrap().is_none());
1214
1215    // Call user B again from client A.
1216    active_call_a
1217        .update(cx_a, |call, cx| {
1218            call.invite(client_b1.user_id().unwrap(), None, cx)
1219        })
1220        .await
1221        .unwrap();
1222    executor.run_until_parked();
1223    assert!(incoming_call_b1.next().await.unwrap().is_some());
1224    assert!(incoming_call_b2.next().await.unwrap().is_some());
1225
1226    // User B accepts the call on one of the two connections, causing both connections
1227    // to stop ringing.
1228    active_call_b2
1229        .update(cx_b2, |call, cx| call.accept_incoming(cx))
1230        .await
1231        .unwrap();
1232    executor.run_until_parked();
1233    assert!(incoming_call_b1.next().await.unwrap().is_none());
1234    assert!(incoming_call_b2.next().await.unwrap().is_none());
1235
1236    // User B disconnects the client that is not on the call. Everything should be fine.
1237    client_b1.disconnect(&cx_b1.to_async());
1238    executor.advance_clock(RECEIVE_TIMEOUT);
1239    client_b1
1240        .authenticate_and_connect(false, &cx_b1.to_async())
1241        .await
1242        .unwrap();
1243
1244    // User B hangs up, and user A calls them again.
1245    active_call_b2
1246        .update(cx_b2, |call, cx| call.hang_up(cx))
1247        .await
1248        .unwrap();
1249    executor.run_until_parked();
1250    active_call_a
1251        .update(cx_a, |call, cx| {
1252            call.invite(client_b1.user_id().unwrap(), None, cx)
1253        })
1254        .await
1255        .unwrap();
1256    executor.run_until_parked();
1257    assert!(incoming_call_b1.next().await.unwrap().is_some());
1258    assert!(incoming_call_b2.next().await.unwrap().is_some());
1259
1260    // User A cancels the call, causing both connections to stop ringing.
1261    active_call_a
1262        .update(cx_a, |call, cx| {
1263            call.cancel_invite(client_b1.user_id().unwrap(), cx)
1264        })
1265        .await
1266        .unwrap();
1267    executor.run_until_parked();
1268    assert!(incoming_call_b1.next().await.unwrap().is_none());
1269    assert!(incoming_call_b2.next().await.unwrap().is_none());
1270
1271    // User A calls user B again.
1272    active_call_a
1273        .update(cx_a, |call, cx| {
1274            call.invite(client_b1.user_id().unwrap(), None, cx)
1275        })
1276        .await
1277        .unwrap();
1278    executor.run_until_parked();
1279    assert!(incoming_call_b1.next().await.unwrap().is_some());
1280    assert!(incoming_call_b2.next().await.unwrap().is_some());
1281
1282    // User A hangs up, causing both connections to stop ringing.
1283    active_call_a
1284        .update(cx_a, |call, cx| call.hang_up(cx))
1285        .await
1286        .unwrap();
1287    executor.run_until_parked();
1288    assert!(incoming_call_b1.next().await.unwrap().is_none());
1289    assert!(incoming_call_b2.next().await.unwrap().is_none());
1290
1291    // User A calls user B again.
1292    active_call_a
1293        .update(cx_a, |call, cx| {
1294            call.invite(client_b1.user_id().unwrap(), None, cx)
1295        })
1296        .await
1297        .unwrap();
1298    executor.run_until_parked();
1299    assert!(incoming_call_b1.next().await.unwrap().is_some());
1300    assert!(incoming_call_b2.next().await.unwrap().is_some());
1301
1302    // User A disconnects, causing both connections to stop ringing.
1303    server.forbid_connections();
1304    server.disconnect_client(client_a.peer_id().unwrap());
1305    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
1306    assert!(incoming_call_b1.next().await.unwrap().is_none());
1307    assert!(incoming_call_b2.next().await.unwrap().is_none());
1308
1309    // User A reconnects automatically, then calls user B again.
1310    server.allow_connections();
1311    executor.advance_clock(RECEIVE_TIMEOUT);
1312    active_call_a
1313        .update(cx_a, |call, cx| {
1314            call.invite(client_b1.user_id().unwrap(), None, cx)
1315        })
1316        .await
1317        .unwrap();
1318    executor.run_until_parked();
1319    assert!(incoming_call_b1.next().await.unwrap().is_some());
1320    assert!(incoming_call_b2.next().await.unwrap().is_some());
1321
1322    // User B disconnects all clients, causing user A to no longer see a pending call for them.
1323    server.forbid_connections();
1324    server.disconnect_client(client_b1.peer_id().unwrap());
1325    server.disconnect_client(client_b2.peer_id().unwrap());
1326    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
1327
1328    active_call_a.read_with(cx_a, |call, _| assert!(call.room().is_none()));
1329}
1330
1331#[gpui::test(iterations = 10)]
1332async fn test_unshare_project(
1333    executor: BackgroundExecutor,
1334    cx_a: &mut TestAppContext,
1335    cx_b: &mut TestAppContext,
1336    cx_c: &mut TestAppContext,
1337) {
1338    let mut server = TestServer::start(executor.clone()).await;
1339    let client_a = server.create_client(cx_a, "user_a").await;
1340    let client_b = server.create_client(cx_b, "user_b").await;
1341    let client_c = server.create_client(cx_c, "user_c").await;
1342    server
1343        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
1344        .await;
1345
1346    let active_call_a = cx_a.read(ActiveCall::global);
1347    let active_call_b = cx_b.read(ActiveCall::global);
1348
1349    client_a
1350        .fs()
1351        .insert_tree(
1352            "/a",
1353            json!({
1354                "a.txt": "a-contents",
1355                "b.txt": "b-contents",
1356            }),
1357        )
1358        .await;
1359
1360    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
1361    let project_id = active_call_a
1362        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
1363        .await
1364        .unwrap();
1365
1366    let worktree_a = project_a.read_with(cx_a, |project, _| project.worktrees().next().unwrap());
1367    let project_b = client_b.build_remote_project(project_id, cx_b).await;
1368    executor.run_until_parked();
1369
1370    assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
1371
1372    project_b
1373        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
1374        .await
1375        .unwrap();
1376
1377    // When client B leaves the room, the project becomes read-only.
1378    active_call_b
1379        .update(cx_b, |call, cx| call.hang_up(cx))
1380        .await
1381        .unwrap();
1382    executor.run_until_parked();
1383
1384    assert!(project_b.read_with(cx_b, |project, _| project.is_disconnected()));
1385
1386    // Client C opens the project.
1387    let project_c = client_c.build_remote_project(project_id, cx_c).await;
1388
1389    // When client A unshares the project, client C's project becomes read-only.
1390    project_a
1391        .update(cx_a, |project, cx| project.unshare(cx))
1392        .unwrap();
1393    executor.run_until_parked();
1394
1395    assert!(worktree_a.read_with(cx_a, |tree, _| !tree.as_local().unwrap().is_shared()));
1396
1397    assert!(project_c.read_with(cx_c, |project, _| project.is_disconnected()));
1398
1399    // Client C can open the project again after client A re-shares.
1400    let project_id = active_call_a
1401        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
1402        .await
1403        .unwrap();
1404    let project_c2 = client_c.build_remote_project(project_id, cx_c).await;
1405    executor.run_until_parked();
1406
1407    assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
1408    project_c2
1409        .update(cx_c, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
1410        .await
1411        .unwrap();
1412
1413    // When client A (the host) leaves the room, the project gets unshared and guests are notified.
1414    active_call_a
1415        .update(cx_a, |call, cx| call.hang_up(cx))
1416        .await
1417        .unwrap();
1418    executor.run_until_parked();
1419
1420    project_a.read_with(cx_a, |project, _| assert!(!project.is_shared()));
1421
1422    project_c2.read_with(cx_c, |project, _| {
1423        assert!(project.is_disconnected());
1424        assert!(project.collaborators().is_empty());
1425    });
1426}
1427
1428#[gpui::test(iterations = 10)]
1429async fn test_project_reconnect(
1430    executor: BackgroundExecutor,
1431    cx_a: &mut TestAppContext,
1432    cx_b: &mut TestAppContext,
1433) {
1434    let mut server = TestServer::start(executor.clone()).await;
1435    let client_a = server.create_client(cx_a, "user_a").await;
1436    let client_b = server.create_client(cx_b, "user_b").await;
1437    server
1438        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
1439        .await;
1440
1441    cx_b.update(editor::init);
1442
1443    client_a
1444        .fs()
1445        .insert_tree(
1446            "/root-1",
1447            json!({
1448                "dir1": {
1449                    "a.txt": "a",
1450                    "b.txt": "b",
1451                    "subdir1": {
1452                        "c.txt": "c",
1453                        "d.txt": "d",
1454                        "e.txt": "e",
1455                    }
1456                },
1457                "dir2": {
1458                    "v.txt": "v",
1459                },
1460                "dir3": {
1461                    "w.txt": "w",
1462                    "x.txt": "x",
1463                    "y.txt": "y",
1464                },
1465                "dir4": {
1466                    "z.txt": "z",
1467                },
1468            }),
1469        )
1470        .await;
1471    client_a
1472        .fs()
1473        .insert_tree(
1474            "/root-2",
1475            json!({
1476                "2.txt": "2",
1477            }),
1478        )
1479        .await;
1480    client_a
1481        .fs()
1482        .insert_tree(
1483            "/root-3",
1484            json!({
1485                "3.txt": "3",
1486            }),
1487        )
1488        .await;
1489
1490    let active_call_a = cx_a.read(ActiveCall::global);
1491    let (project_a1, _) = client_a.build_local_project("/root-1/dir1", cx_a).await;
1492    let (project_a2, _) = client_a.build_local_project("/root-2", cx_a).await;
1493    let (project_a3, _) = client_a.build_local_project("/root-3", cx_a).await;
1494    let worktree_a1 = project_a1.read_with(cx_a, |project, _| project.worktrees().next().unwrap());
1495    let project1_id = active_call_a
1496        .update(cx_a, |call, cx| call.share_project(project_a1.clone(), cx))
1497        .await
1498        .unwrap();
1499    let project2_id = active_call_a
1500        .update(cx_a, |call, cx| call.share_project(project_a2.clone(), cx))
1501        .await
1502        .unwrap();
1503    let project3_id = active_call_a
1504        .update(cx_a, |call, cx| call.share_project(project_a3.clone(), cx))
1505        .await
1506        .unwrap();
1507
1508    let project_b1 = client_b.build_remote_project(project1_id, cx_b).await;
1509    let project_b2 = client_b.build_remote_project(project2_id, cx_b).await;
1510    let project_b3 = client_b.build_remote_project(project3_id, cx_b).await;
1511    executor.run_until_parked();
1512
1513    let worktree1_id = worktree_a1.read_with(cx_a, |worktree, _| {
1514        assert!(worktree.as_local().unwrap().is_shared());
1515        worktree.id()
1516    });
1517    let (worktree_a2, _) = project_a1
1518        .update(cx_a, |p, cx| {
1519            p.find_or_create_local_worktree("/root-1/dir2", true, cx)
1520        })
1521        .await
1522        .unwrap();
1523    executor.run_until_parked();
1524
1525    let worktree2_id = worktree_a2.read_with(cx_a, |tree, _| {
1526        assert!(tree.as_local().unwrap().is_shared());
1527        tree.id()
1528    });
1529    executor.run_until_parked();
1530
1531    project_b1.read_with(cx_b, |project, cx| {
1532        assert!(project.worktree_for_id(worktree2_id, cx).is_some())
1533    });
1534
1535    let buffer_a1 = project_a1
1536        .update(cx_a, |p, cx| p.open_buffer((worktree1_id, "a.txt"), cx))
1537        .await
1538        .unwrap();
1539    let buffer_b1 = project_b1
1540        .update(cx_b, |p, cx| p.open_buffer((worktree1_id, "a.txt"), cx))
1541        .await
1542        .unwrap();
1543
1544    // Drop client A's connection.
1545    server.forbid_connections();
1546    server.disconnect_client(client_a.peer_id().unwrap());
1547    executor.advance_clock(RECEIVE_TIMEOUT);
1548
1549    project_a1.read_with(cx_a, |project, _| {
1550        assert!(project.is_shared());
1551        assert_eq!(project.collaborators().len(), 1);
1552    });
1553
1554    project_b1.read_with(cx_b, |project, _| {
1555        assert!(!project.is_disconnected());
1556        assert_eq!(project.collaborators().len(), 1);
1557    });
1558
1559    worktree_a1.read_with(cx_a, |tree, _| {
1560        assert!(tree.as_local().unwrap().is_shared())
1561    });
1562
1563    // While client A is disconnected, add and remove files from client A's project.
1564    client_a
1565        .fs()
1566        .insert_tree(
1567            "/root-1/dir1/subdir2",
1568            json!({
1569                "f.txt": "f-contents",
1570                "g.txt": "g-contents",
1571                "h.txt": "h-contents",
1572                "i.txt": "i-contents",
1573            }),
1574        )
1575        .await;
1576    client_a
1577        .fs()
1578        .remove_dir(
1579            "/root-1/dir1/subdir1".as_ref(),
1580            RemoveOptions {
1581                recursive: true,
1582                ..Default::default()
1583            },
1584        )
1585        .await
1586        .unwrap();
1587
1588    // While client A is disconnected, add and remove worktrees from client A's project.
1589    project_a1.update(cx_a, |project, cx| {
1590        project.remove_worktree(worktree2_id, cx)
1591    });
1592    let (worktree_a3, _) = project_a1
1593        .update(cx_a, |p, cx| {
1594            p.find_or_create_local_worktree("/root-1/dir3", true, cx)
1595        })
1596        .await
1597        .unwrap();
1598    worktree_a3
1599        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
1600        .await;
1601
1602    let worktree3_id = worktree_a3.read_with(cx_a, |tree, _| {
1603        assert!(!tree.as_local().unwrap().is_shared());
1604        tree.id()
1605    });
1606    executor.run_until_parked();
1607
1608    // While client A is disconnected, close project 2
1609    cx_a.update(|_| drop(project_a2));
1610
1611    // While client A is disconnected, mutate a buffer on both the host and the guest.
1612    buffer_a1.update(cx_a, |buf, cx| buf.edit([(0..0, "W")], None, cx));
1613    buffer_b1.update(cx_b, |buf, cx| buf.edit([(1..1, "Z")], None, cx));
1614    executor.run_until_parked();
1615
1616    // Client A reconnects. Their project is re-shared, and client B re-joins it.
1617    server.allow_connections();
1618    client_a
1619        .authenticate_and_connect(false, &cx_a.to_async())
1620        .await
1621        .unwrap();
1622    executor.run_until_parked();
1623
1624    project_a1.read_with(cx_a, |project, cx| {
1625        assert!(project.is_shared());
1626        assert!(worktree_a1.read(cx).as_local().unwrap().is_shared());
1627        assert_eq!(
1628            worktree_a1
1629                .read(cx)
1630                .snapshot()
1631                .paths()
1632                .map(|p| p.to_str().unwrap())
1633                .collect::<Vec<_>>(),
1634            vec![
1635                "a.txt",
1636                "b.txt",
1637                "subdir2",
1638                "subdir2/f.txt",
1639                "subdir2/g.txt",
1640                "subdir2/h.txt",
1641                "subdir2/i.txt"
1642            ]
1643        );
1644        assert!(worktree_a3.read(cx).as_local().unwrap().is_shared());
1645        assert_eq!(
1646            worktree_a3
1647                .read(cx)
1648                .snapshot()
1649                .paths()
1650                .map(|p| p.to_str().unwrap())
1651                .collect::<Vec<_>>(),
1652            vec!["w.txt", "x.txt", "y.txt"]
1653        );
1654    });
1655
1656    project_b1.read_with(cx_b, |project, cx| {
1657        assert!(!project.is_disconnected());
1658        assert_eq!(
1659            project
1660                .worktree_for_id(worktree1_id, cx)
1661                .unwrap()
1662                .read(cx)
1663                .snapshot()
1664                .paths()
1665                .map(|p| p.to_str().unwrap())
1666                .collect::<Vec<_>>(),
1667            vec![
1668                "a.txt",
1669                "b.txt",
1670                "subdir2",
1671                "subdir2/f.txt",
1672                "subdir2/g.txt",
1673                "subdir2/h.txt",
1674                "subdir2/i.txt"
1675            ]
1676        );
1677        assert!(project.worktree_for_id(worktree2_id, cx).is_none());
1678        assert_eq!(
1679            project
1680                .worktree_for_id(worktree3_id, cx)
1681                .unwrap()
1682                .read(cx)
1683                .snapshot()
1684                .paths()
1685                .map(|p| p.to_str().unwrap())
1686                .collect::<Vec<_>>(),
1687            vec!["w.txt", "x.txt", "y.txt"]
1688        );
1689    });
1690
1691    project_b2.read_with(cx_b, |project, _| assert!(project.is_disconnected()));
1692
1693    project_b3.read_with(cx_b, |project, _| assert!(!project.is_disconnected()));
1694
1695    buffer_a1.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "WaZ"));
1696
1697    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "WaZ"));
1698
1699    // Drop client B's connection.
1700    server.forbid_connections();
1701    server.disconnect_client(client_b.peer_id().unwrap());
1702    executor.advance_clock(RECEIVE_TIMEOUT);
1703
1704    // While client B is disconnected, add and remove files from client A's project
1705    client_a
1706        .fs()
1707        .insert_file("/root-1/dir1/subdir2/j.txt", "j-contents".into())
1708        .await;
1709    client_a
1710        .fs()
1711        .remove_file("/root-1/dir1/subdir2/i.txt".as_ref(), Default::default())
1712        .await
1713        .unwrap();
1714
1715    // While client B is disconnected, add and remove worktrees from client A's project.
1716    let (worktree_a4, _) = project_a1
1717        .update(cx_a, |p, cx| {
1718            p.find_or_create_local_worktree("/root-1/dir4", true, cx)
1719        })
1720        .await
1721        .unwrap();
1722    executor.run_until_parked();
1723
1724    let worktree4_id = worktree_a4.read_with(cx_a, |tree, _| {
1725        assert!(tree.as_local().unwrap().is_shared());
1726        tree.id()
1727    });
1728    project_a1.update(cx_a, |project, cx| {
1729        project.remove_worktree(worktree3_id, cx)
1730    });
1731    executor.run_until_parked();
1732
1733    // While client B is disconnected, mutate a buffer on both the host and the guest.
1734    buffer_a1.update(cx_a, |buf, cx| buf.edit([(1..1, "X")], None, cx));
1735    buffer_b1.update(cx_b, |buf, cx| buf.edit([(2..2, "Y")], None, cx));
1736    executor.run_until_parked();
1737
1738    // While disconnected, close project 3
1739    cx_a.update(|_| drop(project_a3));
1740
1741    // Client B reconnects. They re-join the room and the remaining shared project.
1742    server.allow_connections();
1743    client_b
1744        .authenticate_and_connect(false, &cx_b.to_async())
1745        .await
1746        .unwrap();
1747    executor.run_until_parked();
1748
1749    project_b1.read_with(cx_b, |project, cx| {
1750        assert!(!project.is_disconnected());
1751        assert_eq!(
1752            project
1753                .worktree_for_id(worktree1_id, cx)
1754                .unwrap()
1755                .read(cx)
1756                .snapshot()
1757                .paths()
1758                .map(|p| p.to_str().unwrap())
1759                .collect::<Vec<_>>(),
1760            vec![
1761                "a.txt",
1762                "b.txt",
1763                "subdir2",
1764                "subdir2/f.txt",
1765                "subdir2/g.txt",
1766                "subdir2/h.txt",
1767                "subdir2/j.txt"
1768            ]
1769        );
1770        assert!(project.worktree_for_id(worktree2_id, cx).is_none());
1771        assert_eq!(
1772            project
1773                .worktree_for_id(worktree4_id, cx)
1774                .unwrap()
1775                .read(cx)
1776                .snapshot()
1777                .paths()
1778                .map(|p| p.to_str().unwrap())
1779                .collect::<Vec<_>>(),
1780            vec!["z.txt"]
1781        );
1782    });
1783
1784    project_b3.read_with(cx_b, |project, _| assert!(project.is_disconnected()));
1785
1786    buffer_a1.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "WXaYZ"));
1787
1788    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "WXaYZ"));
1789}
1790
1791#[gpui::test(iterations = 10)]
1792async fn test_active_call_events(
1793    executor: BackgroundExecutor,
1794    cx_a: &mut TestAppContext,
1795    cx_b: &mut TestAppContext,
1796) {
1797    let mut server = TestServer::start(executor.clone()).await;
1798    let client_a = server.create_client(cx_a, "user_a").await;
1799    let client_b = server.create_client(cx_b, "user_b").await;
1800    client_a.fs().insert_tree("/a", json!({})).await;
1801    client_b.fs().insert_tree("/b", json!({})).await;
1802
1803    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
1804    let (project_b, _) = client_b.build_local_project("/b", cx_b).await;
1805
1806    server
1807        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
1808        .await;
1809    let active_call_a = cx_a.read(ActiveCall::global);
1810    let active_call_b = cx_b.read(ActiveCall::global);
1811
1812    let events_a = active_call_events(cx_a);
1813    let events_b = active_call_events(cx_b);
1814
1815    let project_a_id = active_call_a
1816        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
1817        .await
1818        .unwrap();
1819    executor.run_until_parked();
1820    assert_eq!(mem::take(&mut *events_a.borrow_mut()), vec![]);
1821    assert_eq!(
1822        mem::take(&mut *events_b.borrow_mut()),
1823        vec![room::Event::RemoteProjectShared {
1824            owner: Arc::new(User {
1825                id: client_a.user_id().unwrap(),
1826                github_login: "user_a".to_string(),
1827                avatar_uri: "avatar_a".into(),
1828            }),
1829            project_id: project_a_id,
1830            worktree_root_names: vec!["a".to_string()],
1831        }]
1832    );
1833
1834    let project_b_id = active_call_b
1835        .update(cx_b, |call, cx| call.share_project(project_b.clone(), cx))
1836        .await
1837        .unwrap();
1838    executor.run_until_parked();
1839    assert_eq!(
1840        mem::take(&mut *events_a.borrow_mut()),
1841        vec![room::Event::RemoteProjectShared {
1842            owner: Arc::new(User {
1843                id: client_b.user_id().unwrap(),
1844                github_login: "user_b".to_string(),
1845                avatar_uri: "avatar_b".into(),
1846            }),
1847            project_id: project_b_id,
1848            worktree_root_names: vec!["b".to_string()]
1849        }]
1850    );
1851    assert_eq!(mem::take(&mut *events_b.borrow_mut()), vec![]);
1852
1853    // Sharing a project twice is idempotent.
1854    let project_b_id_2 = active_call_b
1855        .update(cx_b, |call, cx| call.share_project(project_b.clone(), cx))
1856        .await
1857        .unwrap();
1858    assert_eq!(project_b_id_2, project_b_id);
1859    executor.run_until_parked();
1860    assert_eq!(mem::take(&mut *events_a.borrow_mut()), vec![]);
1861    assert_eq!(mem::take(&mut *events_b.borrow_mut()), vec![]);
1862}
1863
1864fn active_call_events(cx: &mut TestAppContext) -> Rc<RefCell<Vec<room::Event>>> {
1865    let events = Rc::new(RefCell::new(Vec::new()));
1866    let active_call = cx.read(ActiveCall::global);
1867    cx.update({
1868        let events = events.clone();
1869        |cx| {
1870            cx.subscribe(&active_call, move |_, event, _| {
1871                events.borrow_mut().push(event.clone())
1872            })
1873            .detach()
1874        }
1875    });
1876    events
1877}
1878
1879#[gpui::test]
1880async fn test_mute_deafen(
1881    executor: BackgroundExecutor,
1882    cx_a: &mut TestAppContext,
1883    cx_b: &mut TestAppContext,
1884    cx_c: &mut TestAppContext,
1885) {
1886    let mut server = TestServer::start(executor.clone()).await;
1887    let client_a = server.create_client(cx_a, "user_a").await;
1888    let client_b = server.create_client(cx_b, "user_b").await;
1889    let client_c = server.create_client(cx_c, "user_c").await;
1890
1891    server
1892        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
1893        .await;
1894
1895    let active_call_a = cx_a.read(ActiveCall::global);
1896    let active_call_b = cx_b.read(ActiveCall::global);
1897    let active_call_c = cx_c.read(ActiveCall::global);
1898
1899    // User A calls user B, B answers.
1900    active_call_a
1901        .update(cx_a, |call, cx| {
1902            call.invite(client_b.user_id().unwrap(), None, cx)
1903        })
1904        .await
1905        .unwrap();
1906    executor.run_until_parked();
1907    active_call_b
1908        .update(cx_b, |call, cx| call.accept_incoming(cx))
1909        .await
1910        .unwrap();
1911    executor.run_until_parked();
1912
1913    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
1914    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
1915
1916    room_a.read_with(cx_a, |room, _| assert!(!room.is_muted()));
1917    room_b.read_with(cx_b, |room, _| assert!(!room.is_muted()));
1918
1919    // Users A and B are both muted.
1920    assert_eq!(
1921        participant_audio_state(&room_a, cx_a),
1922        &[ParticipantAudioState {
1923            user_id: client_b.user_id().unwrap(),
1924            is_muted: false,
1925            audio_tracks_playing: vec![true],
1926        }]
1927    );
1928    assert_eq!(
1929        participant_audio_state(&room_b, cx_b),
1930        &[ParticipantAudioState {
1931            user_id: client_a.user_id().unwrap(),
1932            is_muted: false,
1933            audio_tracks_playing: vec![true],
1934        }]
1935    );
1936
1937    // User A mutes
1938    room_a.update(cx_a, |room, cx| room.toggle_mute(cx));
1939    executor.run_until_parked();
1940
1941    // User A hears user B, but B doesn't hear A.
1942    room_a.read_with(cx_a, |room, _| assert!(room.is_muted()));
1943    room_b.read_with(cx_b, |room, _| assert!(!room.is_muted()));
1944    assert_eq!(
1945        participant_audio_state(&room_a, cx_a),
1946        &[ParticipantAudioState {
1947            user_id: client_b.user_id().unwrap(),
1948            is_muted: false,
1949            audio_tracks_playing: vec![true],
1950        }]
1951    );
1952    assert_eq!(
1953        participant_audio_state(&room_b, cx_b),
1954        &[ParticipantAudioState {
1955            user_id: client_a.user_id().unwrap(),
1956            is_muted: true,
1957            audio_tracks_playing: vec![true],
1958        }]
1959    );
1960
1961    // User A deafens
1962    room_a.update(cx_a, |room, cx| room.toggle_deafen(cx));
1963    executor.run_until_parked();
1964
1965    // User A does not hear user B.
1966    room_a.read_with(cx_a, |room, _| assert!(room.is_muted()));
1967    room_b.read_with(cx_b, |room, _| assert!(!room.is_muted()));
1968    assert_eq!(
1969        participant_audio_state(&room_a, cx_a),
1970        &[ParticipantAudioState {
1971            user_id: client_b.user_id().unwrap(),
1972            is_muted: false,
1973            audio_tracks_playing: vec![false],
1974        }]
1975    );
1976    assert_eq!(
1977        participant_audio_state(&room_b, cx_b),
1978        &[ParticipantAudioState {
1979            user_id: client_a.user_id().unwrap(),
1980            is_muted: true,
1981            audio_tracks_playing: vec![true],
1982        }]
1983    );
1984
1985    // User B calls user C, C joins.
1986    active_call_b
1987        .update(cx_b, |call, cx| {
1988            call.invite(client_c.user_id().unwrap(), None, cx)
1989        })
1990        .await
1991        .unwrap();
1992    executor.run_until_parked();
1993    active_call_c
1994        .update(cx_c, |call, cx| call.accept_incoming(cx))
1995        .await
1996        .unwrap();
1997    executor.run_until_parked();
1998
1999    // User A does not hear users B or C.
2000    assert_eq!(
2001        participant_audio_state(&room_a, cx_a),
2002        &[
2003            ParticipantAudioState {
2004                user_id: client_b.user_id().unwrap(),
2005                is_muted: false,
2006                audio_tracks_playing: vec![false],
2007            },
2008            ParticipantAudioState {
2009                user_id: client_c.user_id().unwrap(),
2010                is_muted: false,
2011                audio_tracks_playing: vec![false],
2012            }
2013        ]
2014    );
2015    assert_eq!(
2016        participant_audio_state(&room_b, cx_b),
2017        &[
2018            ParticipantAudioState {
2019                user_id: client_a.user_id().unwrap(),
2020                is_muted: true,
2021                audio_tracks_playing: vec![true],
2022            },
2023            ParticipantAudioState {
2024                user_id: client_c.user_id().unwrap(),
2025                is_muted: false,
2026                audio_tracks_playing: vec![true],
2027            }
2028        ]
2029    );
2030
2031    #[derive(PartialEq, Eq, Debug)]
2032    struct ParticipantAudioState {
2033        user_id: u64,
2034        is_muted: bool,
2035        audio_tracks_playing: Vec<bool>,
2036    }
2037
2038    fn participant_audio_state(
2039        room: &Model<Room>,
2040        cx: &TestAppContext,
2041    ) -> Vec<ParticipantAudioState> {
2042        room.read_with(cx, |room, _| {
2043            room.remote_participants()
2044                .iter()
2045                .map(|(user_id, participant)| ParticipantAudioState {
2046                    user_id: *user_id,
2047                    is_muted: participant.muted,
2048                    audio_tracks_playing: participant
2049                        .audio_tracks
2050                        .values()
2051                        .map(|track| track.is_playing())
2052                        .collect(),
2053                })
2054                .collect::<Vec<_>>()
2055        })
2056    }
2057}
2058
2059#[gpui::test(iterations = 10)]
2060async fn test_room_location(
2061    executor: BackgroundExecutor,
2062    cx_a: &mut TestAppContext,
2063    cx_b: &mut TestAppContext,
2064) {
2065    let mut server = TestServer::start(executor.clone()).await;
2066    let client_a = server.create_client(cx_a, "user_a").await;
2067    let client_b = server.create_client(cx_b, "user_b").await;
2068    client_a.fs().insert_tree("/a", json!({})).await;
2069    client_b.fs().insert_tree("/b", json!({})).await;
2070
2071    let active_call_a = cx_a.read(ActiveCall::global);
2072    let active_call_b = cx_b.read(ActiveCall::global);
2073
2074    let a_notified = Rc::new(Cell::new(false));
2075    cx_a.update({
2076        let notified = a_notified.clone();
2077        |cx| {
2078            cx.observe(&active_call_a, move |_, _| notified.set(true))
2079                .detach()
2080        }
2081    });
2082
2083    let b_notified = Rc::new(Cell::new(false));
2084    cx_b.update({
2085        let b_notified = b_notified.clone();
2086        |cx| {
2087            cx.observe(&active_call_b, move |_, _| b_notified.set(true))
2088                .detach()
2089        }
2090    });
2091
2092    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
2093    active_call_a
2094        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
2095        .await
2096        .unwrap();
2097    let (project_b, _) = client_b.build_local_project("/b", cx_b).await;
2098
2099    server
2100        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2101        .await;
2102
2103    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
2104
2105    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
2106    executor.run_until_parked();
2107    assert!(a_notified.take());
2108    assert_eq!(
2109        participant_locations(&room_a, cx_a),
2110        vec![("user_b".to_string(), ParticipantLocation::External)]
2111    );
2112    assert!(b_notified.take());
2113    assert_eq!(
2114        participant_locations(&room_b, cx_b),
2115        vec![("user_a".to_string(), ParticipantLocation::UnsharedProject)]
2116    );
2117
2118    let project_a_id = active_call_a
2119        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2120        .await
2121        .unwrap();
2122    executor.run_until_parked();
2123    assert!(a_notified.take());
2124    assert_eq!(
2125        participant_locations(&room_a, cx_a),
2126        vec![("user_b".to_string(), ParticipantLocation::External)]
2127    );
2128    assert!(b_notified.take());
2129    assert_eq!(
2130        participant_locations(&room_b, cx_b),
2131        vec![(
2132            "user_a".to_string(),
2133            ParticipantLocation::SharedProject {
2134                project_id: project_a_id
2135            }
2136        )]
2137    );
2138
2139    let project_b_id = active_call_b
2140        .update(cx_b, |call, cx| call.share_project(project_b.clone(), cx))
2141        .await
2142        .unwrap();
2143    executor.run_until_parked();
2144    assert!(a_notified.take());
2145    assert_eq!(
2146        participant_locations(&room_a, cx_a),
2147        vec![("user_b".to_string(), ParticipantLocation::External)]
2148    );
2149    assert!(b_notified.take());
2150    assert_eq!(
2151        participant_locations(&room_b, cx_b),
2152        vec![(
2153            "user_a".to_string(),
2154            ParticipantLocation::SharedProject {
2155                project_id: project_a_id
2156            }
2157        )]
2158    );
2159
2160    active_call_b
2161        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
2162        .await
2163        .unwrap();
2164    executor.run_until_parked();
2165    assert!(a_notified.take());
2166    assert_eq!(
2167        participant_locations(&room_a, cx_a),
2168        vec![(
2169            "user_b".to_string(),
2170            ParticipantLocation::SharedProject {
2171                project_id: project_b_id
2172            }
2173        )]
2174    );
2175    assert!(b_notified.take());
2176    assert_eq!(
2177        participant_locations(&room_b, cx_b),
2178        vec![(
2179            "user_a".to_string(),
2180            ParticipantLocation::SharedProject {
2181                project_id: project_a_id
2182            }
2183        )]
2184    );
2185
2186    active_call_b
2187        .update(cx_b, |call, cx| call.set_location(None, cx))
2188        .await
2189        .unwrap();
2190    executor.run_until_parked();
2191    assert!(a_notified.take());
2192    assert_eq!(
2193        participant_locations(&room_a, cx_a),
2194        vec![("user_b".to_string(), ParticipantLocation::External)]
2195    );
2196    assert!(b_notified.take());
2197    assert_eq!(
2198        participant_locations(&room_b, cx_b),
2199        vec![(
2200            "user_a".to_string(),
2201            ParticipantLocation::SharedProject {
2202                project_id: project_a_id
2203            }
2204        )]
2205    );
2206
2207    fn participant_locations(
2208        room: &Model<Room>,
2209        cx: &TestAppContext,
2210    ) -> Vec<(String, ParticipantLocation)> {
2211        room.read_with(cx, |room, _| {
2212            room.remote_participants()
2213                .values()
2214                .map(|participant| {
2215                    (
2216                        participant.user.github_login.to_string(),
2217                        participant.location,
2218                    )
2219                })
2220                .collect()
2221        })
2222    }
2223}
2224
2225#[gpui::test(iterations = 10)]
2226async fn test_propagate_saves_and_fs_changes(
2227    executor: BackgroundExecutor,
2228    cx_a: &mut TestAppContext,
2229    cx_b: &mut TestAppContext,
2230    cx_c: &mut TestAppContext,
2231) {
2232    let mut server = TestServer::start(executor.clone()).await;
2233    let client_a = server.create_client(cx_a, "user_a").await;
2234    let client_b = server.create_client(cx_b, "user_b").await;
2235    let client_c = server.create_client(cx_c, "user_c").await;
2236
2237    server
2238        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
2239        .await;
2240    let active_call_a = cx_a.read(ActiveCall::global);
2241
2242    let rust = Arc::new(Language::new(
2243        LanguageConfig {
2244            name: "Rust".into(),
2245            path_suffixes: vec!["rs".to_string()],
2246            ..Default::default()
2247        },
2248        Some(tree_sitter_rust::language()),
2249    ));
2250    let javascript = Arc::new(Language::new(
2251        LanguageConfig {
2252            name: "JavaScript".into(),
2253            path_suffixes: vec!["js".to_string()],
2254            ..Default::default()
2255        },
2256        Some(tree_sitter_rust::language()),
2257    ));
2258    for client in [&client_a, &client_b, &client_c] {
2259        client.language_registry().add(rust.clone());
2260        client.language_registry().add(javascript.clone());
2261    }
2262
2263    client_a
2264        .fs()
2265        .insert_tree(
2266            "/a",
2267            json!({
2268                "file1.rs": "",
2269                "file2": ""
2270            }),
2271        )
2272        .await;
2273    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
2274
2275    let worktree_a = project_a.read_with(cx_a, |p, _| p.worktrees().next().unwrap());
2276    let project_id = active_call_a
2277        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2278        .await
2279        .unwrap();
2280
2281    // Join that worktree as clients B and C.
2282    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2283    let project_c = client_c.build_remote_project(project_id, cx_c).await;
2284
2285    let worktree_b = project_b.read_with(cx_b, |p, _| p.worktrees().next().unwrap());
2286
2287    let worktree_c = project_c.read_with(cx_c, |p, _| p.worktrees().next().unwrap());
2288
2289    // Open and edit a buffer as both guests B and C.
2290    let buffer_b = project_b
2291        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "file1.rs"), cx))
2292        .await
2293        .unwrap();
2294    let buffer_c = project_c
2295        .update(cx_c, |p, cx| p.open_buffer((worktree_id, "file1.rs"), cx))
2296        .await
2297        .unwrap();
2298
2299    buffer_b.read_with(cx_b, |buffer, _| {
2300        assert_eq!(&*buffer.language().unwrap().name(), "Rust");
2301    });
2302
2303    buffer_c.read_with(cx_c, |buffer, _| {
2304        assert_eq!(&*buffer.language().unwrap().name(), "Rust");
2305    });
2306    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "i-am-b, ")], None, cx));
2307    buffer_c.update(cx_c, |buf, cx| buf.edit([(0..0, "i-am-c, ")], None, cx));
2308
2309    // Open and edit that buffer as the host.
2310    let buffer_a = project_a
2311        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "file1.rs"), cx))
2312        .await
2313        .unwrap();
2314
2315    executor.run_until_parked();
2316
2317    buffer_a.read_with(cx_a, |buf, _| assert_eq!(buf.text(), "i-am-c, i-am-b, "));
2318    buffer_a.update(cx_a, |buf, cx| {
2319        buf.edit([(buf.len()..buf.len(), "i-am-a")], None, cx)
2320    });
2321
2322    executor.run_until_parked();
2323
2324    buffer_a.read_with(cx_a, |buf, _| {
2325        assert_eq!(buf.text(), "i-am-c, i-am-b, i-am-a");
2326    });
2327
2328    buffer_b.read_with(cx_b, |buf, _| {
2329        assert_eq!(buf.text(), "i-am-c, i-am-b, i-am-a");
2330    });
2331
2332    buffer_c.read_with(cx_c, |buf, _| {
2333        assert_eq!(buf.text(), "i-am-c, i-am-b, i-am-a");
2334    });
2335
2336    // Edit the buffer as the host and concurrently save as guest B.
2337    let save_b = project_b.update(cx_b, |project, cx| {
2338        project.save_buffer(buffer_b.clone(), cx)
2339    });
2340    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "hi-a, ")], None, cx));
2341    save_b.await.unwrap();
2342    assert_eq!(
2343        client_a.fs().load("/a/file1.rs".as_ref()).await.unwrap(),
2344        "hi-a, i-am-c, i-am-b, i-am-a"
2345    );
2346
2347    executor.run_until_parked();
2348
2349    buffer_a.read_with(cx_a, |buf, _| assert!(!buf.is_dirty()));
2350
2351    buffer_b.read_with(cx_b, |buf, _| assert!(!buf.is_dirty()));
2352
2353    buffer_c.read_with(cx_c, |buf, _| assert!(!buf.is_dirty()));
2354
2355    // Make changes on host's file system, see those changes on guest worktrees.
2356    client_a
2357        .fs()
2358        .rename(
2359            "/a/file1.rs".as_ref(),
2360            "/a/file1.js".as_ref(),
2361            Default::default(),
2362        )
2363        .await
2364        .unwrap();
2365    client_a
2366        .fs()
2367        .rename("/a/file2".as_ref(), "/a/file3".as_ref(), Default::default())
2368        .await
2369        .unwrap();
2370    client_a.fs().insert_file("/a/file4", "4".into()).await;
2371    executor.run_until_parked();
2372
2373    worktree_a.read_with(cx_a, |tree, _| {
2374        assert_eq!(
2375            tree.paths()
2376                .map(|p| p.to_string_lossy())
2377                .collect::<Vec<_>>(),
2378            ["file1.js", "file3", "file4"]
2379        )
2380    });
2381
2382    worktree_b.read_with(cx_b, |tree, _| {
2383        assert_eq!(
2384            tree.paths()
2385                .map(|p| p.to_string_lossy())
2386                .collect::<Vec<_>>(),
2387            ["file1.js", "file3", "file4"]
2388        )
2389    });
2390
2391    worktree_c.read_with(cx_c, |tree, _| {
2392        assert_eq!(
2393            tree.paths()
2394                .map(|p| p.to_string_lossy())
2395                .collect::<Vec<_>>(),
2396            ["file1.js", "file3", "file4"]
2397        )
2398    });
2399
2400    // Ensure buffer files are updated as well.
2401
2402    buffer_a.read_with(cx_a, |buffer, _| {
2403        assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
2404        assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
2405    });
2406
2407    buffer_b.read_with(cx_b, |buffer, _| {
2408        assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
2409        assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
2410    });
2411
2412    buffer_c.read_with(cx_c, |buffer, _| {
2413        assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
2414        assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
2415    });
2416
2417    let new_buffer_a = project_a
2418        .update(cx_a, |p, cx| p.create_buffer("", None, cx))
2419        .unwrap();
2420
2421    let new_buffer_id = new_buffer_a.read_with(cx_a, |buffer, _| buffer.remote_id());
2422    let new_buffer_b = project_b
2423        .update(cx_b, |p, cx| p.open_buffer_by_id(new_buffer_id, cx))
2424        .await
2425        .unwrap();
2426
2427    new_buffer_b.read_with(cx_b, |buffer, _| {
2428        assert!(buffer.file().is_none());
2429    });
2430
2431    new_buffer_a.update(cx_a, |buffer, cx| {
2432        buffer.edit([(0..0, "ok")], None, cx);
2433    });
2434    project_a
2435        .update(cx_a, |project, cx| {
2436            project.save_buffer_as(new_buffer_a.clone(), "/a/file3.rs".into(), cx)
2437        })
2438        .await
2439        .unwrap();
2440
2441    executor.run_until_parked();
2442
2443    new_buffer_b.read_with(cx_b, |buffer_b, _| {
2444        assert_eq!(
2445            buffer_b.file().unwrap().path().as_ref(),
2446            Path::new("file3.rs")
2447        );
2448
2449        new_buffer_a.read_with(cx_a, |buffer_a, _| {
2450            assert_eq!(buffer_b.saved_mtime(), buffer_a.saved_mtime());
2451            assert_eq!(buffer_b.saved_version(), buffer_a.saved_version());
2452        });
2453    });
2454}
2455
2456#[gpui::test(iterations = 10)]
2457async fn test_git_diff_base_change(
2458    executor: BackgroundExecutor,
2459    cx_a: &mut TestAppContext,
2460    cx_b: &mut TestAppContext,
2461) {
2462    let mut server = TestServer::start(executor.clone()).await;
2463    let client_a = server.create_client(cx_a, "user_a").await;
2464    let client_b = server.create_client(cx_b, "user_b").await;
2465    server
2466        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2467        .await;
2468    let active_call_a = cx_a.read(ActiveCall::global);
2469
2470    client_a
2471        .fs()
2472        .insert_tree(
2473            "/dir",
2474            json!({
2475            ".git": {},
2476            "sub": {
2477                ".git": {},
2478                "b.txt": "
2479                    one
2480                    two
2481                    three
2482                ".unindent(),
2483            },
2484            "a.txt": "
2485                    one
2486                    two
2487                    three
2488                ".unindent(),
2489            }),
2490        )
2491        .await;
2492
2493    let (project_local, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2494    let project_id = active_call_a
2495        .update(cx_a, |call, cx| {
2496            call.share_project(project_local.clone(), cx)
2497        })
2498        .await
2499        .unwrap();
2500
2501    let project_remote = client_b.build_remote_project(project_id, cx_b).await;
2502
2503    let diff_base = "
2504        one
2505        three
2506    "
2507    .unindent();
2508
2509    let new_diff_base = "
2510        one
2511        two
2512    "
2513    .unindent();
2514
2515    client_a.fs().set_index_for_repo(
2516        Path::new("/dir/.git"),
2517        &[(Path::new("a.txt"), diff_base.clone())],
2518    );
2519
2520    // Create the buffer
2521    let buffer_local_a = project_local
2522        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2523        .await
2524        .unwrap();
2525
2526    // Wait for it to catch up to the new diff
2527    executor.run_until_parked();
2528
2529    // Smoke test diffing
2530
2531    buffer_local_a.read_with(cx_a, |buffer, _| {
2532        assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
2533        git::diff::assert_hunks(
2534            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2535            &buffer,
2536            &diff_base,
2537            &[(1..2, "", "two\n")],
2538        );
2539    });
2540
2541    // Create remote buffer
2542    let buffer_remote_a = project_remote
2543        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2544        .await
2545        .unwrap();
2546
2547    // Wait remote buffer to catch up to the new diff
2548    executor.run_until_parked();
2549
2550    // Smoke test diffing
2551
2552    buffer_remote_a.read_with(cx_b, |buffer, _| {
2553        assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
2554        git::diff::assert_hunks(
2555            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2556            &buffer,
2557            &diff_base,
2558            &[(1..2, "", "two\n")],
2559        );
2560    });
2561
2562    client_a.fs().set_index_for_repo(
2563        Path::new("/dir/.git"),
2564        &[(Path::new("a.txt"), new_diff_base.clone())],
2565    );
2566
2567    // Wait for buffer_local_a to receive it
2568    executor.run_until_parked();
2569
2570    // Smoke test new diffing
2571
2572    buffer_local_a.read_with(cx_a, |buffer, _| {
2573        assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
2574
2575        git::diff::assert_hunks(
2576            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2577            &buffer,
2578            &diff_base,
2579            &[(2..3, "", "three\n")],
2580        );
2581    });
2582
2583    // Smoke test B
2584
2585    buffer_remote_a.read_with(cx_b, |buffer, _| {
2586        assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
2587        git::diff::assert_hunks(
2588            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2589            &buffer,
2590            &diff_base,
2591            &[(2..3, "", "three\n")],
2592        );
2593    });
2594
2595    //Nested git dir
2596
2597    let diff_base = "
2598        one
2599        three
2600    "
2601    .unindent();
2602
2603    let new_diff_base = "
2604        one
2605        two
2606    "
2607    .unindent();
2608
2609    client_a.fs().set_index_for_repo(
2610        Path::new("/dir/sub/.git"),
2611        &[(Path::new("b.txt"), diff_base.clone())],
2612    );
2613
2614    // Create the buffer
2615    let buffer_local_b = project_local
2616        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "sub/b.txt"), cx))
2617        .await
2618        .unwrap();
2619
2620    // Wait for it to catch up to the new diff
2621    executor.run_until_parked();
2622
2623    // Smoke test diffing
2624
2625    buffer_local_b.read_with(cx_a, |buffer, _| {
2626        assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
2627        git::diff::assert_hunks(
2628            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2629            &buffer,
2630            &diff_base,
2631            &[(1..2, "", "two\n")],
2632        );
2633    });
2634
2635    // Create remote buffer
2636    let buffer_remote_b = project_remote
2637        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "sub/b.txt"), cx))
2638        .await
2639        .unwrap();
2640
2641    // Wait remote buffer to catch up to the new diff
2642    executor.run_until_parked();
2643
2644    // Smoke test diffing
2645
2646    buffer_remote_b.read_with(cx_b, |buffer, _| {
2647        assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
2648        git::diff::assert_hunks(
2649            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2650            &buffer,
2651            &diff_base,
2652            &[(1..2, "", "two\n")],
2653        );
2654    });
2655
2656    client_a.fs().set_index_for_repo(
2657        Path::new("/dir/sub/.git"),
2658        &[(Path::new("b.txt"), new_diff_base.clone())],
2659    );
2660
2661    // Wait for buffer_local_b to receive it
2662    executor.run_until_parked();
2663
2664    // Smoke test new diffing
2665
2666    buffer_local_b.read_with(cx_a, |buffer, _| {
2667        assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
2668        println!("{:?}", buffer.as_rope().to_string());
2669        println!("{:?}", buffer.diff_base());
2670        println!(
2671            "{:?}",
2672            buffer
2673                .snapshot()
2674                .git_diff_hunks_in_row_range(0..4)
2675                .collect::<Vec<_>>()
2676        );
2677
2678        git::diff::assert_hunks(
2679            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2680            &buffer,
2681            &diff_base,
2682            &[(2..3, "", "three\n")],
2683        );
2684    });
2685
2686    // Smoke test B
2687
2688    buffer_remote_b.read_with(cx_b, |buffer, _| {
2689        assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
2690        git::diff::assert_hunks(
2691            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2692            &buffer,
2693            &diff_base,
2694            &[(2..3, "", "three\n")],
2695        );
2696    });
2697}
2698
2699#[gpui::test]
2700async fn test_git_branch_name(
2701    executor: BackgroundExecutor,
2702    cx_a: &mut TestAppContext,
2703    cx_b: &mut TestAppContext,
2704    cx_c: &mut TestAppContext,
2705) {
2706    let mut server = TestServer::start(executor.clone()).await;
2707    let client_a = server.create_client(cx_a, "user_a").await;
2708    let client_b = server.create_client(cx_b, "user_b").await;
2709    let client_c = server.create_client(cx_c, "user_c").await;
2710    server
2711        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
2712        .await;
2713    let active_call_a = cx_a.read(ActiveCall::global);
2714
2715    client_a
2716        .fs()
2717        .insert_tree(
2718            "/dir",
2719            json!({
2720            ".git": {},
2721            }),
2722        )
2723        .await;
2724
2725    let (project_local, _worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2726    let project_id = active_call_a
2727        .update(cx_a, |call, cx| {
2728            call.share_project(project_local.clone(), cx)
2729        })
2730        .await
2731        .unwrap();
2732
2733    let project_remote = client_b.build_remote_project(project_id, cx_b).await;
2734    client_a
2735        .fs()
2736        .set_branch_name(Path::new("/dir/.git"), Some("branch-1"));
2737
2738    // Wait for it to catch up to the new branch
2739    executor.run_until_parked();
2740
2741    #[track_caller]
2742    fn assert_branch(branch_name: Option<impl Into<String>>, project: &Project, cx: &AppContext) {
2743        let branch_name = branch_name.map(Into::into);
2744        let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
2745        assert_eq!(worktrees.len(), 1);
2746        let worktree = worktrees[0].clone();
2747        let root_entry = worktree.read(cx).snapshot().root_git_entry().unwrap();
2748        assert_eq!(root_entry.branch(), branch_name.map(Into::into));
2749    }
2750
2751    // Smoke test branch reading
2752
2753    project_local.read_with(cx_a, |project, cx| {
2754        assert_branch(Some("branch-1"), project, cx)
2755    });
2756
2757    project_remote.read_with(cx_b, |project, cx| {
2758        assert_branch(Some("branch-1"), project, cx)
2759    });
2760
2761    client_a
2762        .fs()
2763        .set_branch_name(Path::new("/dir/.git"), Some("branch-2"));
2764
2765    // Wait for buffer_local_a to receive it
2766    executor.run_until_parked();
2767
2768    // Smoke test branch reading
2769
2770    project_local.read_with(cx_a, |project, cx| {
2771        assert_branch(Some("branch-2"), project, cx)
2772    });
2773
2774    project_remote.read_with(cx_b, |project, cx| {
2775        assert_branch(Some("branch-2"), project, cx)
2776    });
2777
2778    let project_remote_c = client_c.build_remote_project(project_id, cx_c).await;
2779    executor.run_until_parked();
2780
2781    project_remote_c.read_with(cx_c, |project, cx| {
2782        assert_branch(Some("branch-2"), project, cx)
2783    });
2784}
2785
2786#[gpui::test]
2787async fn test_git_status_sync(
2788    executor: BackgroundExecutor,
2789    cx_a: &mut TestAppContext,
2790    cx_b: &mut TestAppContext,
2791    cx_c: &mut TestAppContext,
2792) {
2793    let mut server = TestServer::start(executor.clone()).await;
2794    let client_a = server.create_client(cx_a, "user_a").await;
2795    let client_b = server.create_client(cx_b, "user_b").await;
2796    let client_c = server.create_client(cx_c, "user_c").await;
2797    server
2798        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
2799        .await;
2800    let active_call_a = cx_a.read(ActiveCall::global);
2801
2802    client_a
2803        .fs()
2804        .insert_tree(
2805            "/dir",
2806            json!({
2807            ".git": {},
2808            "a.txt": "a",
2809            "b.txt": "b",
2810            }),
2811        )
2812        .await;
2813
2814    const A_TXT: &'static str = "a.txt";
2815    const B_TXT: &'static str = "b.txt";
2816
2817    client_a.fs().set_status_for_repo_via_git_operation(
2818        Path::new("/dir/.git"),
2819        &[
2820            (&Path::new(A_TXT), GitFileStatus::Added),
2821            (&Path::new(B_TXT), GitFileStatus::Added),
2822        ],
2823    );
2824
2825    let (project_local, _worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2826    let project_id = active_call_a
2827        .update(cx_a, |call, cx| {
2828            call.share_project(project_local.clone(), cx)
2829        })
2830        .await
2831        .unwrap();
2832
2833    let project_remote = client_b.build_remote_project(project_id, cx_b).await;
2834
2835    // Wait for it to catch up to the new status
2836    executor.run_until_parked();
2837
2838    #[track_caller]
2839    fn assert_status(
2840        file: &impl AsRef<Path>,
2841        status: Option<GitFileStatus>,
2842        project: &Project,
2843        cx: &AppContext,
2844    ) {
2845        let file = file.as_ref();
2846        let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
2847        assert_eq!(worktrees.len(), 1);
2848        let worktree = worktrees[0].clone();
2849        let snapshot = worktree.read(cx).snapshot();
2850        assert_eq!(snapshot.status_for_file(file), status);
2851    }
2852
2853    // Smoke test status reading
2854
2855    project_local.read_with(cx_a, |project, cx| {
2856        assert_status(&Path::new(A_TXT), Some(GitFileStatus::Added), project, cx);
2857        assert_status(&Path::new(B_TXT), Some(GitFileStatus::Added), project, cx);
2858    });
2859
2860    project_remote.read_with(cx_b, |project, cx| {
2861        assert_status(&Path::new(A_TXT), Some(GitFileStatus::Added), project, cx);
2862        assert_status(&Path::new(B_TXT), Some(GitFileStatus::Added), project, cx);
2863    });
2864
2865    client_a.fs().set_status_for_repo_via_working_copy_change(
2866        Path::new("/dir/.git"),
2867        &[
2868            (&Path::new(A_TXT), GitFileStatus::Modified),
2869            (&Path::new(B_TXT), GitFileStatus::Modified),
2870        ],
2871    );
2872
2873    // Wait for buffer_local_a to receive it
2874    executor.run_until_parked();
2875
2876    // Smoke test status reading
2877
2878    project_local.read_with(cx_a, |project, cx| {
2879        assert_status(
2880            &Path::new(A_TXT),
2881            Some(GitFileStatus::Modified),
2882            project,
2883            cx,
2884        );
2885        assert_status(
2886            &Path::new(B_TXT),
2887            Some(GitFileStatus::Modified),
2888            project,
2889            cx,
2890        );
2891    });
2892
2893    project_remote.read_with(cx_b, |project, cx| {
2894        assert_status(
2895            &Path::new(A_TXT),
2896            Some(GitFileStatus::Modified),
2897            project,
2898            cx,
2899        );
2900        assert_status(
2901            &Path::new(B_TXT),
2902            Some(GitFileStatus::Modified),
2903            project,
2904            cx,
2905        );
2906    });
2907
2908    // And synchronization while joining
2909    let project_remote_c = client_c.build_remote_project(project_id, cx_c).await;
2910    executor.run_until_parked();
2911
2912    project_remote_c.read_with(cx_c, |project, cx| {
2913        assert_status(
2914            &Path::new(A_TXT),
2915            Some(GitFileStatus::Modified),
2916            project,
2917            cx,
2918        );
2919        assert_status(
2920            &Path::new(B_TXT),
2921            Some(GitFileStatus::Modified),
2922            project,
2923            cx,
2924        );
2925    });
2926}
2927
2928#[gpui::test(iterations = 10)]
2929async fn test_fs_operations(
2930    executor: BackgroundExecutor,
2931    cx_a: &mut TestAppContext,
2932    cx_b: &mut TestAppContext,
2933) {
2934    let mut server = TestServer::start(executor.clone()).await;
2935    let client_a = server.create_client(cx_a, "user_a").await;
2936    let client_b = server.create_client(cx_b, "user_b").await;
2937    server
2938        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2939        .await;
2940    let active_call_a = cx_a.read(ActiveCall::global);
2941
2942    client_a
2943        .fs()
2944        .insert_tree(
2945            "/dir",
2946            json!({
2947                "a.txt": "a-contents",
2948                "b.txt": "b-contents",
2949            }),
2950        )
2951        .await;
2952    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2953    let project_id = active_call_a
2954        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2955        .await
2956        .unwrap();
2957    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2958
2959    let worktree_a = project_a.read_with(cx_a, |project, _| project.worktrees().next().unwrap());
2960
2961    let worktree_b = project_b.read_with(cx_b, |project, _| project.worktrees().next().unwrap());
2962
2963    let entry = project_b
2964        .update(cx_b, |project, cx| {
2965            project.create_entry((worktree_id, "c.txt"), false, cx)
2966        })
2967        .await
2968        .unwrap()
2969        .unwrap();
2970
2971    worktree_a.read_with(cx_a, |worktree, _| {
2972        assert_eq!(
2973            worktree
2974                .paths()
2975                .map(|p| p.to_string_lossy())
2976                .collect::<Vec<_>>(),
2977            ["a.txt", "b.txt", "c.txt"]
2978        );
2979    });
2980
2981    worktree_b.read_with(cx_b, |worktree, _| {
2982        assert_eq!(
2983            worktree
2984                .paths()
2985                .map(|p| p.to_string_lossy())
2986                .collect::<Vec<_>>(),
2987            ["a.txt", "b.txt", "c.txt"]
2988        );
2989    });
2990
2991    project_b
2992        .update(cx_b, |project, cx| {
2993            project.rename_entry(entry.id, Path::new("d.txt"), cx)
2994        })
2995        .await
2996        .unwrap()
2997        .unwrap();
2998
2999    worktree_a.read_with(cx_a, |worktree, _| {
3000        assert_eq!(
3001            worktree
3002                .paths()
3003                .map(|p| p.to_string_lossy())
3004                .collect::<Vec<_>>(),
3005            ["a.txt", "b.txt", "d.txt"]
3006        );
3007    });
3008
3009    worktree_b.read_with(cx_b, |worktree, _| {
3010        assert_eq!(
3011            worktree
3012                .paths()
3013                .map(|p| p.to_string_lossy())
3014                .collect::<Vec<_>>(),
3015            ["a.txt", "b.txt", "d.txt"]
3016        );
3017    });
3018
3019    let dir_entry = project_b
3020        .update(cx_b, |project, cx| {
3021            project.create_entry((worktree_id, "DIR"), true, cx)
3022        })
3023        .await
3024        .unwrap()
3025        .unwrap();
3026
3027    worktree_a.read_with(cx_a, |worktree, _| {
3028        assert_eq!(
3029            worktree
3030                .paths()
3031                .map(|p| p.to_string_lossy())
3032                .collect::<Vec<_>>(),
3033            ["DIR", "a.txt", "b.txt", "d.txt"]
3034        );
3035    });
3036
3037    worktree_b.read_with(cx_b, |worktree, _| {
3038        assert_eq!(
3039            worktree
3040                .paths()
3041                .map(|p| p.to_string_lossy())
3042                .collect::<Vec<_>>(),
3043            ["DIR", "a.txt", "b.txt", "d.txt"]
3044        );
3045    });
3046
3047    project_b
3048        .update(cx_b, |project, cx| {
3049            project.create_entry((worktree_id, "DIR/e.txt"), false, cx)
3050        })
3051        .await
3052        .unwrap()
3053        .unwrap();
3054    project_b
3055        .update(cx_b, |project, cx| {
3056            project.create_entry((worktree_id, "DIR/SUBDIR"), true, cx)
3057        })
3058        .await
3059        .unwrap()
3060        .unwrap();
3061    project_b
3062        .update(cx_b, |project, cx| {
3063            project.create_entry((worktree_id, "DIR/SUBDIR/f.txt"), false, cx)
3064        })
3065        .await
3066        .unwrap()
3067        .unwrap();
3068
3069    worktree_a.read_with(cx_a, |worktree, _| {
3070        assert_eq!(
3071            worktree
3072                .paths()
3073                .map(|p| p.to_string_lossy())
3074                .collect::<Vec<_>>(),
3075            [
3076                "DIR",
3077                "DIR/SUBDIR",
3078                "DIR/SUBDIR/f.txt",
3079                "DIR/e.txt",
3080                "a.txt",
3081                "b.txt",
3082                "d.txt"
3083            ]
3084        );
3085    });
3086
3087    worktree_b.read_with(cx_b, |worktree, _| {
3088        assert_eq!(
3089            worktree
3090                .paths()
3091                .map(|p| p.to_string_lossy())
3092                .collect::<Vec<_>>(),
3093            [
3094                "DIR",
3095                "DIR/SUBDIR",
3096                "DIR/SUBDIR/f.txt",
3097                "DIR/e.txt",
3098                "a.txt",
3099                "b.txt",
3100                "d.txt"
3101            ]
3102        );
3103    });
3104
3105    project_b
3106        .update(cx_b, |project, cx| {
3107            project.copy_entry(entry.id, Path::new("f.txt"), cx)
3108        })
3109        .await
3110        .unwrap()
3111        .unwrap();
3112
3113    worktree_a.read_with(cx_a, |worktree, _| {
3114        assert_eq!(
3115            worktree
3116                .paths()
3117                .map(|p| p.to_string_lossy())
3118                .collect::<Vec<_>>(),
3119            [
3120                "DIR",
3121                "DIR/SUBDIR",
3122                "DIR/SUBDIR/f.txt",
3123                "DIR/e.txt",
3124                "a.txt",
3125                "b.txt",
3126                "d.txt",
3127                "f.txt"
3128            ]
3129        );
3130    });
3131
3132    worktree_b.read_with(cx_b, |worktree, _| {
3133        assert_eq!(
3134            worktree
3135                .paths()
3136                .map(|p| p.to_string_lossy())
3137                .collect::<Vec<_>>(),
3138            [
3139                "DIR",
3140                "DIR/SUBDIR",
3141                "DIR/SUBDIR/f.txt",
3142                "DIR/e.txt",
3143                "a.txt",
3144                "b.txt",
3145                "d.txt",
3146                "f.txt"
3147            ]
3148        );
3149    });
3150
3151    project_b
3152        .update(cx_b, |project, cx| {
3153            project.delete_entry(dir_entry.id, cx).unwrap()
3154        })
3155        .await
3156        .unwrap();
3157    executor.run_until_parked();
3158
3159    worktree_a.read_with(cx_a, |worktree, _| {
3160        assert_eq!(
3161            worktree
3162                .paths()
3163                .map(|p| p.to_string_lossy())
3164                .collect::<Vec<_>>(),
3165            ["a.txt", "b.txt", "d.txt", "f.txt"]
3166        );
3167    });
3168
3169    worktree_b.read_with(cx_b, |worktree, _| {
3170        assert_eq!(
3171            worktree
3172                .paths()
3173                .map(|p| p.to_string_lossy())
3174                .collect::<Vec<_>>(),
3175            ["a.txt", "b.txt", "d.txt", "f.txt"]
3176        );
3177    });
3178
3179    project_b
3180        .update(cx_b, |project, cx| {
3181            project.delete_entry(entry.id, cx).unwrap()
3182        })
3183        .await
3184        .unwrap();
3185
3186    worktree_a.read_with(cx_a, |worktree, _| {
3187        assert_eq!(
3188            worktree
3189                .paths()
3190                .map(|p| p.to_string_lossy())
3191                .collect::<Vec<_>>(),
3192            ["a.txt", "b.txt", "f.txt"]
3193        );
3194    });
3195
3196    worktree_b.read_with(cx_b, |worktree, _| {
3197        assert_eq!(
3198            worktree
3199                .paths()
3200                .map(|p| p.to_string_lossy())
3201                .collect::<Vec<_>>(),
3202            ["a.txt", "b.txt", "f.txt"]
3203        );
3204    });
3205}
3206
3207#[gpui::test(iterations = 10)]
3208async fn test_local_settings(
3209    executor: BackgroundExecutor,
3210    cx_a: &mut TestAppContext,
3211    cx_b: &mut TestAppContext,
3212) {
3213    let mut server = TestServer::start(executor.clone()).await;
3214    let client_a = server.create_client(cx_a, "user_a").await;
3215    let client_b = server.create_client(cx_b, "user_b").await;
3216    server
3217        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3218        .await;
3219    let active_call_a = cx_a.read(ActiveCall::global);
3220
3221    // As client A, open a project that contains some local settings files
3222    client_a
3223        .fs()
3224        .insert_tree(
3225            "/dir",
3226            json!({
3227                ".zed": {
3228                    "settings.json": r#"{ "tab_size": 2 }"#
3229                },
3230                "a": {
3231                    ".zed": {
3232                        "settings.json": r#"{ "tab_size": 8 }"#
3233                    },
3234                    "a.txt": "a-contents",
3235                },
3236                "b": {
3237                    "b.txt": "b-contents",
3238                }
3239            }),
3240        )
3241        .await;
3242    let (project_a, _) = client_a.build_local_project("/dir", cx_a).await;
3243    executor.run_until_parked();
3244    let project_id = active_call_a
3245        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3246        .await
3247        .unwrap();
3248    executor.run_until_parked();
3249
3250    // As client B, join that project and observe the local settings.
3251    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3252
3253    let worktree_b = project_b.read_with(cx_b, |project, _| project.worktrees().next().unwrap());
3254    executor.run_until_parked();
3255    cx_b.read(|cx| {
3256        let store = cx.global::<SettingsStore>();
3257        assert_eq!(
3258            store
3259                .local_settings(worktree_b.read(cx).id().to_usize())
3260                .collect::<Vec<_>>(),
3261            &[
3262                (Path::new("").into(), r#"{"tab_size":2}"#.to_string()),
3263                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3264            ]
3265        )
3266    });
3267
3268    // As client A, update a settings file. As Client B, see the changed settings.
3269    client_a
3270        .fs()
3271        .insert_file("/dir/.zed/settings.json", r#"{}"#.into())
3272        .await;
3273    executor.run_until_parked();
3274    cx_b.read(|cx| {
3275        let store = cx.global::<SettingsStore>();
3276        assert_eq!(
3277            store
3278                .local_settings(worktree_b.read(cx).id().to_usize())
3279                .collect::<Vec<_>>(),
3280            &[
3281                (Path::new("").into(), r#"{}"#.to_string()),
3282                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3283            ]
3284        )
3285    });
3286
3287    // As client A, create and remove some settings files. As client B, see the changed settings.
3288    client_a
3289        .fs()
3290        .remove_file("/dir/.zed/settings.json".as_ref(), Default::default())
3291        .await
3292        .unwrap();
3293    client_a
3294        .fs()
3295        .create_dir("/dir/b/.zed".as_ref())
3296        .await
3297        .unwrap();
3298    client_a
3299        .fs()
3300        .insert_file("/dir/b/.zed/settings.json", r#"{"tab_size": 4}"#.into())
3301        .await;
3302    executor.run_until_parked();
3303    cx_b.read(|cx| {
3304        let store = cx.global::<SettingsStore>();
3305        assert_eq!(
3306            store
3307                .local_settings(worktree_b.read(cx).id().to_usize())
3308                .collect::<Vec<_>>(),
3309            &[
3310                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3311                (Path::new("b").into(), r#"{"tab_size":4}"#.to_string()),
3312            ]
3313        )
3314    });
3315
3316    // As client B, disconnect.
3317    server.forbid_connections();
3318    server.disconnect_client(client_b.peer_id().unwrap());
3319
3320    // As client A, change and remove settings files while client B is disconnected.
3321    client_a
3322        .fs()
3323        .insert_file("/dir/a/.zed/settings.json", r#"{"hard_tabs":true}"#.into())
3324        .await;
3325    client_a
3326        .fs()
3327        .remove_file("/dir/b/.zed/settings.json".as_ref(), Default::default())
3328        .await
3329        .unwrap();
3330    executor.run_until_parked();
3331
3332    // As client B, reconnect and see the changed settings.
3333    server.allow_connections();
3334    executor.advance_clock(RECEIVE_TIMEOUT);
3335    cx_b.read(|cx| {
3336        let store = cx.global::<SettingsStore>();
3337        assert_eq!(
3338            store
3339                .local_settings(worktree_b.read(cx).id().to_usize())
3340                .collect::<Vec<_>>(),
3341            &[(Path::new("a").into(), r#"{"hard_tabs":true}"#.to_string()),]
3342        )
3343    });
3344}
3345
3346#[gpui::test(iterations = 10)]
3347async fn test_buffer_conflict_after_save(
3348    executor: BackgroundExecutor,
3349    cx_a: &mut TestAppContext,
3350    cx_b: &mut TestAppContext,
3351) {
3352    let mut server = TestServer::start(executor.clone()).await;
3353    let client_a = server.create_client(cx_a, "user_a").await;
3354    let client_b = server.create_client(cx_b, "user_b").await;
3355    server
3356        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3357        .await;
3358    let active_call_a = cx_a.read(ActiveCall::global);
3359
3360    client_a
3361        .fs()
3362        .insert_tree(
3363            "/dir",
3364            json!({
3365                "a.txt": "a-contents",
3366            }),
3367        )
3368        .await;
3369    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3370    let project_id = active_call_a
3371        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3372        .await
3373        .unwrap();
3374    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3375
3376    // Open a buffer as client B
3377    let buffer_b = project_b
3378        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3379        .await
3380        .unwrap();
3381
3382    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "world ")], None, cx));
3383
3384    buffer_b.read_with(cx_b, |buf, _| {
3385        assert!(buf.is_dirty());
3386        assert!(!buf.has_conflict());
3387    });
3388
3389    project_b
3390        .update(cx_b, |project, cx| {
3391            project.save_buffer(buffer_b.clone(), cx)
3392        })
3393        .await
3394        .unwrap();
3395
3396    buffer_b.read_with(cx_b, |buffer_b, _| assert!(!buffer_b.is_dirty()));
3397
3398    buffer_b.read_with(cx_b, |buf, _| {
3399        assert!(!buf.has_conflict());
3400    });
3401
3402    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "hello ")], None, cx));
3403
3404    buffer_b.read_with(cx_b, |buf, _| {
3405        assert!(buf.is_dirty());
3406        assert!(!buf.has_conflict());
3407    });
3408}
3409
3410#[gpui::test(iterations = 10)]
3411async fn test_buffer_reloading(
3412    executor: BackgroundExecutor,
3413    cx_a: &mut TestAppContext,
3414    cx_b: &mut TestAppContext,
3415) {
3416    let mut server = TestServer::start(executor.clone()).await;
3417    let client_a = server.create_client(cx_a, "user_a").await;
3418    let client_b = server.create_client(cx_b, "user_b").await;
3419    server
3420        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3421        .await;
3422    let active_call_a = cx_a.read(ActiveCall::global);
3423
3424    client_a
3425        .fs()
3426        .insert_tree(
3427            "/dir",
3428            json!({
3429                "a.txt": "a\nb\nc",
3430            }),
3431        )
3432        .await;
3433    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3434    let project_id = active_call_a
3435        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3436        .await
3437        .unwrap();
3438    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3439
3440    // Open a buffer as client B
3441    let buffer_b = project_b
3442        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3443        .await
3444        .unwrap();
3445
3446    buffer_b.read_with(cx_b, |buf, _| {
3447        assert!(!buf.is_dirty());
3448        assert!(!buf.has_conflict());
3449        assert_eq!(buf.line_ending(), LineEnding::Unix);
3450    });
3451
3452    let new_contents = Rope::from("d\ne\nf");
3453    client_a
3454        .fs()
3455        .save("/dir/a.txt".as_ref(), &new_contents, LineEnding::Windows)
3456        .await
3457        .unwrap();
3458
3459    executor.run_until_parked();
3460
3461    buffer_b.read_with(cx_b, |buf, _| {
3462        assert_eq!(buf.text(), new_contents.to_string());
3463        assert!(!buf.is_dirty());
3464        assert!(!buf.has_conflict());
3465        assert_eq!(buf.line_ending(), LineEnding::Windows);
3466    });
3467}
3468
3469#[gpui::test(iterations = 10)]
3470async fn test_editing_while_guest_opens_buffer(
3471    executor: BackgroundExecutor,
3472    cx_a: &mut TestAppContext,
3473    cx_b: &mut TestAppContext,
3474) {
3475    let mut server = TestServer::start(executor.clone()).await;
3476    let client_a = server.create_client(cx_a, "user_a").await;
3477    let client_b = server.create_client(cx_b, "user_b").await;
3478    server
3479        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3480        .await;
3481    let active_call_a = cx_a.read(ActiveCall::global);
3482
3483    client_a
3484        .fs()
3485        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3486        .await;
3487    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3488    let project_id = active_call_a
3489        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3490        .await
3491        .unwrap();
3492    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3493
3494    // Open a buffer as client A
3495    let buffer_a = project_a
3496        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3497        .await
3498        .unwrap();
3499
3500    // Start opening the same buffer as client B
3501    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx));
3502    let buffer_b = cx_b.executor().spawn(open_buffer);
3503
3504    // Edit the buffer as client A while client B is still opening it.
3505    cx_b.executor().simulate_random_delay().await;
3506    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "X")], None, cx));
3507    cx_b.executor().simulate_random_delay().await;
3508    buffer_a.update(cx_a, |buf, cx| buf.edit([(1..1, "Y")], None, cx));
3509
3510    let text = buffer_a.read_with(cx_a, |buf, _| buf.text());
3511    let buffer_b = buffer_b.await.unwrap();
3512    executor.run_until_parked();
3513
3514    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), text));
3515}
3516
3517#[gpui::test(iterations = 10)]
3518async fn test_leaving_worktree_while_opening_buffer(
3519    executor: BackgroundExecutor,
3520    cx_a: &mut TestAppContext,
3521    cx_b: &mut TestAppContext,
3522) {
3523    let mut server = TestServer::start(executor.clone()).await;
3524    let client_a = server.create_client(cx_a, "user_a").await;
3525    let client_b = server.create_client(cx_b, "user_b").await;
3526    server
3527        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3528        .await;
3529    let active_call_a = cx_a.read(ActiveCall::global);
3530
3531    client_a
3532        .fs()
3533        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3534        .await;
3535    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3536    let project_id = active_call_a
3537        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3538        .await
3539        .unwrap();
3540    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3541
3542    // See that a guest has joined as client A.
3543    executor.run_until_parked();
3544
3545    project_a.read_with(cx_a, |p, _| assert_eq!(p.collaborators().len(), 1));
3546
3547    // Begin opening a buffer as client B, but leave the project before the open completes.
3548    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx));
3549    let buffer_b = cx_b.executor().spawn(open_buffer);
3550    cx_b.update(|_| drop(project_b));
3551    drop(buffer_b);
3552
3553    // See that the guest has left.
3554    executor.run_until_parked();
3555
3556    project_a.read_with(cx_a, |p, _| assert!(p.collaborators().is_empty()));
3557}
3558
3559#[gpui::test(iterations = 10)]
3560async fn test_canceling_buffer_opening(
3561    executor: BackgroundExecutor,
3562    cx_a: &mut TestAppContext,
3563    cx_b: &mut TestAppContext,
3564) {
3565    let mut server = TestServer::start(executor.clone()).await;
3566    let client_a = server.create_client(cx_a, "user_a").await;
3567    let client_b = server.create_client(cx_b, "user_b").await;
3568    server
3569        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3570        .await;
3571    let active_call_a = cx_a.read(ActiveCall::global);
3572
3573    client_a
3574        .fs()
3575        .insert_tree(
3576            "/dir",
3577            json!({
3578                "a.txt": "abc",
3579            }),
3580        )
3581        .await;
3582    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3583    let project_id = active_call_a
3584        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3585        .await
3586        .unwrap();
3587    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3588
3589    let buffer_a = project_a
3590        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3591        .await
3592        .unwrap();
3593
3594    // Open a buffer as client B but cancel after a random amount of time.
3595    let buffer_b = project_b.update(cx_b, |p, cx| {
3596        p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3597    });
3598    executor.simulate_random_delay().await;
3599    drop(buffer_b);
3600
3601    // Try opening the same buffer again as client B, and ensure we can
3602    // still do it despite the cancellation above.
3603    let buffer_b = project_b
3604        .update(cx_b, |p, cx| {
3605            p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3606        })
3607        .await
3608        .unwrap();
3609
3610    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "abc"));
3611}
3612
3613#[gpui::test(iterations = 10)]
3614async fn test_leaving_project(
3615    executor: BackgroundExecutor,
3616    cx_a: &mut TestAppContext,
3617    cx_b: &mut TestAppContext,
3618    cx_c: &mut TestAppContext,
3619) {
3620    let mut server = TestServer::start(executor.clone()).await;
3621    let client_a = server.create_client(cx_a, "user_a").await;
3622    let client_b = server.create_client(cx_b, "user_b").await;
3623    let client_c = server.create_client(cx_c, "user_c").await;
3624    server
3625        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3626        .await;
3627    let active_call_a = cx_a.read(ActiveCall::global);
3628
3629    client_a
3630        .fs()
3631        .insert_tree(
3632            "/a",
3633            json!({
3634                "a.txt": "a-contents",
3635                "b.txt": "b-contents",
3636            }),
3637        )
3638        .await;
3639    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
3640    let project_id = active_call_a
3641        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3642        .await
3643        .unwrap();
3644    let project_b1 = client_b.build_remote_project(project_id, cx_b).await;
3645    let project_c = client_c.build_remote_project(project_id, cx_c).await;
3646
3647    // Client A sees that a guest has joined.
3648    executor.run_until_parked();
3649
3650    project_a.read_with(cx_a, |project, _| {
3651        assert_eq!(project.collaborators().len(), 2);
3652    });
3653
3654    project_b1.read_with(cx_b, |project, _| {
3655        assert_eq!(project.collaborators().len(), 2);
3656    });
3657
3658    project_c.read_with(cx_c, |project, _| {
3659        assert_eq!(project.collaborators().len(), 2);
3660    });
3661
3662    // Client B opens a buffer.
3663    let buffer_b1 = project_b1
3664        .update(cx_b, |project, cx| {
3665            let worktree_id = project.worktrees().next().unwrap().read(cx).id();
3666            project.open_buffer((worktree_id, "a.txt"), cx)
3667        })
3668        .await
3669        .unwrap();
3670
3671    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3672
3673    // Drop client B's project and ensure client A and client C observe client B leaving.
3674    cx_b.update(|_| drop(project_b1));
3675    executor.run_until_parked();
3676
3677    project_a.read_with(cx_a, |project, _| {
3678        assert_eq!(project.collaborators().len(), 1);
3679    });
3680
3681    project_c.read_with(cx_c, |project, _| {
3682        assert_eq!(project.collaborators().len(), 1);
3683    });
3684
3685    // Client B re-joins the project and can open buffers as before.
3686    let project_b2 = client_b.build_remote_project(project_id, cx_b).await;
3687    executor.run_until_parked();
3688
3689    project_a.read_with(cx_a, |project, _| {
3690        assert_eq!(project.collaborators().len(), 2);
3691    });
3692
3693    project_b2.read_with(cx_b, |project, _| {
3694        assert_eq!(project.collaborators().len(), 2);
3695    });
3696
3697    project_c.read_with(cx_c, |project, _| {
3698        assert_eq!(project.collaborators().len(), 2);
3699    });
3700
3701    let buffer_b2 = project_b2
3702        .update(cx_b, |project, cx| {
3703            let worktree_id = project.worktrees().next().unwrap().read(cx).id();
3704            project.open_buffer((worktree_id, "a.txt"), cx)
3705        })
3706        .await
3707        .unwrap();
3708
3709    buffer_b2.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3710
3711    // Drop client B's connection and ensure client A and client C observe client B leaving.
3712    client_b.disconnect(&cx_b.to_async());
3713    executor.advance_clock(RECONNECT_TIMEOUT);
3714
3715    project_a.read_with(cx_a, |project, _| {
3716        assert_eq!(project.collaborators().len(), 1);
3717    });
3718
3719    project_b2.read_with(cx_b, |project, _| {
3720        assert!(project.is_disconnected());
3721    });
3722
3723    project_c.read_with(cx_c, |project, _| {
3724        assert_eq!(project.collaborators().len(), 1);
3725    });
3726
3727    // Client B can't join the project, unless they re-join the room.
3728    cx_b.spawn(|cx| {
3729        Project::remote(
3730            project_id,
3731            client_b.app_state.client.clone(),
3732            client_b.user_store().clone(),
3733            client_b.language_registry().clone(),
3734            FakeFs::new(cx.background_executor().clone()),
3735            ChannelRole::Member,
3736            cx,
3737        )
3738    })
3739    .await
3740    .unwrap_err();
3741
3742    // Simulate connection loss for client C and ensure client A observes client C leaving the project.
3743    client_c.wait_for_current_user(cx_c).await;
3744    server.forbid_connections();
3745    server.disconnect_client(client_c.peer_id().unwrap());
3746    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
3747    executor.run_until_parked();
3748
3749    project_a.read_with(cx_a, |project, _| {
3750        assert_eq!(project.collaborators().len(), 0);
3751    });
3752
3753    project_b2.read_with(cx_b, |project, _| {
3754        assert!(project.is_disconnected());
3755    });
3756
3757    project_c.read_with(cx_c, |project, _| {
3758        assert!(project.is_disconnected());
3759    });
3760}
3761
3762#[gpui::test(iterations = 10)]
3763async fn test_collaborating_with_diagnostics(
3764    executor: BackgroundExecutor,
3765    cx_a: &mut TestAppContext,
3766    cx_b: &mut TestAppContext,
3767    cx_c: &mut TestAppContext,
3768) {
3769    let mut server = TestServer::start(executor.clone()).await;
3770    let client_a = server.create_client(cx_a, "user_a").await;
3771    let client_b = server.create_client(cx_b, "user_b").await;
3772    let client_c = server.create_client(cx_c, "user_c").await;
3773    server
3774        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3775        .await;
3776    let active_call_a = cx_a.read(ActiveCall::global);
3777
3778    // Set up a fake language server.
3779    let mut language = Language::new(
3780        LanguageConfig {
3781            name: "Rust".into(),
3782            path_suffixes: vec!["rs".to_string()],
3783            ..Default::default()
3784        },
3785        Some(tree_sitter_rust::language()),
3786    );
3787    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
3788    client_a.language_registry().add(Arc::new(language));
3789
3790    // Share a project as client A
3791    client_a
3792        .fs()
3793        .insert_tree(
3794            "/a",
3795            json!({
3796                "a.rs": "let one = two",
3797                "other.rs": "",
3798            }),
3799        )
3800        .await;
3801    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3802
3803    // Cause the language server to start.
3804    let _buffer = project_a
3805        .update(cx_a, |project, cx| {
3806            project.open_buffer(
3807                ProjectPath {
3808                    worktree_id,
3809                    path: Path::new("other.rs").into(),
3810                },
3811                cx,
3812            )
3813        })
3814        .await
3815        .unwrap();
3816
3817    // Simulate a language server reporting errors for a file.
3818    let mut fake_language_server = fake_language_servers.next().await.unwrap();
3819    fake_language_server
3820        .receive_notification::<lsp::notification::DidOpenTextDocument>()
3821        .await;
3822    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3823        lsp::PublishDiagnosticsParams {
3824            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3825            version: None,
3826            diagnostics: vec![lsp::Diagnostic {
3827                severity: Some(lsp::DiagnosticSeverity::WARNING),
3828                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3829                message: "message 0".to_string(),
3830                ..Default::default()
3831            }],
3832        },
3833    );
3834
3835    // Client A shares the project and, simultaneously, the language server
3836    // publishes a diagnostic. This is done to ensure that the server always
3837    // observes the latest diagnostics for a worktree.
3838    let project_id = active_call_a
3839        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3840        .await
3841        .unwrap();
3842    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3843        lsp::PublishDiagnosticsParams {
3844            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3845            version: None,
3846            diagnostics: vec![lsp::Diagnostic {
3847                severity: Some(lsp::DiagnosticSeverity::ERROR),
3848                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3849                message: "message 1".to_string(),
3850                ..Default::default()
3851            }],
3852        },
3853    );
3854
3855    // Join the worktree as client B.
3856    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3857
3858    // Wait for server to see the diagnostics update.
3859    executor.run_until_parked();
3860
3861    // Ensure client B observes the new diagnostics.
3862
3863    project_b.read_with(cx_b, |project, cx| {
3864        assert_eq!(
3865            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
3866            &[(
3867                ProjectPath {
3868                    worktree_id,
3869                    path: Arc::from(Path::new("a.rs")),
3870                },
3871                LanguageServerId(0),
3872                DiagnosticSummary {
3873                    error_count: 1,
3874                    warning_count: 0,
3875                    ..Default::default()
3876                },
3877            )]
3878        )
3879    });
3880
3881    // Join project as client C and observe the diagnostics.
3882    let project_c = client_c.build_remote_project(project_id, cx_c).await;
3883    let project_c_diagnostic_summaries =
3884        Rc::new(RefCell::new(project_c.read_with(cx_c, |project, cx| {
3885            project.diagnostic_summaries(false, cx).collect::<Vec<_>>()
3886        })));
3887    project_c.update(cx_c, |_, cx| {
3888        let summaries = project_c_diagnostic_summaries.clone();
3889        cx.subscribe(&project_c, {
3890            move |p, _, event, cx| {
3891                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
3892                    *summaries.borrow_mut() = p.diagnostic_summaries(false, cx).collect();
3893                }
3894            }
3895        })
3896        .detach();
3897    });
3898
3899    executor.run_until_parked();
3900    assert_eq!(
3901        project_c_diagnostic_summaries.borrow().as_slice(),
3902        &[(
3903            ProjectPath {
3904                worktree_id,
3905                path: Arc::from(Path::new("a.rs")),
3906            },
3907            LanguageServerId(0),
3908            DiagnosticSummary {
3909                error_count: 1,
3910                warning_count: 0,
3911                ..Default::default()
3912            },
3913        )]
3914    );
3915
3916    // Simulate a language server reporting more errors for a file.
3917    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3918        lsp::PublishDiagnosticsParams {
3919            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3920            version: None,
3921            diagnostics: vec![
3922                lsp::Diagnostic {
3923                    severity: Some(lsp::DiagnosticSeverity::ERROR),
3924                    range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3925                    message: "message 1".to_string(),
3926                    ..Default::default()
3927                },
3928                lsp::Diagnostic {
3929                    severity: Some(lsp::DiagnosticSeverity::WARNING),
3930                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 13)),
3931                    message: "message 2".to_string(),
3932                    ..Default::default()
3933                },
3934            ],
3935        },
3936    );
3937
3938    // Clients B and C get the updated summaries
3939    executor.run_until_parked();
3940
3941    project_b.read_with(cx_b, |project, cx| {
3942        assert_eq!(
3943            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
3944            [(
3945                ProjectPath {
3946                    worktree_id,
3947                    path: Arc::from(Path::new("a.rs")),
3948                },
3949                LanguageServerId(0),
3950                DiagnosticSummary {
3951                    error_count: 1,
3952                    warning_count: 1,
3953                },
3954            )]
3955        );
3956    });
3957
3958    project_c.read_with(cx_c, |project, cx| {
3959        assert_eq!(
3960            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
3961            [(
3962                ProjectPath {
3963                    worktree_id,
3964                    path: Arc::from(Path::new("a.rs")),
3965                },
3966                LanguageServerId(0),
3967                DiagnosticSummary {
3968                    error_count: 1,
3969                    warning_count: 1,
3970                },
3971            )]
3972        );
3973    });
3974
3975    // Open the file with the errors on client B. They should be present.
3976    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
3977    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
3978
3979    buffer_b.read_with(cx_b, |buffer, _| {
3980        assert_eq!(
3981            buffer
3982                .snapshot()
3983                .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
3984                .collect::<Vec<_>>(),
3985            &[
3986                DiagnosticEntry {
3987                    range: Point::new(0, 4)..Point::new(0, 7),
3988                    diagnostic: Diagnostic {
3989                        group_id: 2,
3990                        message: "message 1".to_string(),
3991                        severity: lsp::DiagnosticSeverity::ERROR,
3992                        is_primary: true,
3993                        ..Default::default()
3994                    }
3995                },
3996                DiagnosticEntry {
3997                    range: Point::new(0, 10)..Point::new(0, 13),
3998                    diagnostic: Diagnostic {
3999                        group_id: 3,
4000                        severity: lsp::DiagnosticSeverity::WARNING,
4001                        message: "message 2".to_string(),
4002                        is_primary: true,
4003                        ..Default::default()
4004                    }
4005                }
4006            ]
4007        );
4008    });
4009
4010    // Simulate a language server reporting no errors for a file.
4011    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
4012        lsp::PublishDiagnosticsParams {
4013            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
4014            version: None,
4015            diagnostics: vec![],
4016        },
4017    );
4018    executor.run_until_parked();
4019
4020    project_a.read_with(cx_a, |project, cx| {
4021        assert_eq!(
4022            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4023            []
4024        )
4025    });
4026
4027    project_b.read_with(cx_b, |project, cx| {
4028        assert_eq!(
4029            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4030            []
4031        )
4032    });
4033
4034    project_c.read_with(cx_c, |project, cx| {
4035        assert_eq!(
4036            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4037            []
4038        )
4039    });
4040}
4041
4042#[gpui::test(iterations = 10)]
4043async fn test_collaborating_with_lsp_progress_updates_and_diagnostics_ordering(
4044    executor: BackgroundExecutor,
4045    cx_a: &mut TestAppContext,
4046    cx_b: &mut TestAppContext,
4047) {
4048    let mut server = TestServer::start(executor.clone()).await;
4049    let client_a = server.create_client(cx_a, "user_a").await;
4050    let client_b = server.create_client(cx_b, "user_b").await;
4051    server
4052        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4053        .await;
4054
4055    // Set up a fake language server.
4056    let mut language = Language::new(
4057        LanguageConfig {
4058            name: "Rust".into(),
4059            path_suffixes: vec!["rs".to_string()],
4060            ..Default::default()
4061        },
4062        Some(tree_sitter_rust::language()),
4063    );
4064    let mut fake_language_servers = language
4065        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4066            disk_based_diagnostics_progress_token: Some("the-disk-based-token".into()),
4067            disk_based_diagnostics_sources: vec!["the-disk-based-diagnostics-source".into()],
4068            ..Default::default()
4069        }))
4070        .await;
4071    client_a.language_registry().add(Arc::new(language));
4072
4073    let file_names = &["one.rs", "two.rs", "three.rs", "four.rs", "five.rs"];
4074    client_a
4075        .fs()
4076        .insert_tree(
4077            "/test",
4078            json!({
4079                "one.rs": "const ONE: usize = 1;",
4080                "two.rs": "const TWO: usize = 2;",
4081                "three.rs": "const THREE: usize = 3;",
4082                "four.rs": "const FOUR: usize = 3;",
4083                "five.rs": "const FIVE: usize = 3;",
4084            }),
4085        )
4086        .await;
4087
4088    let (project_a, worktree_id) = client_a.build_local_project("/test", cx_a).await;
4089
4090    // Share a project as client A
4091    let active_call_a = cx_a.read(ActiveCall::global);
4092    let project_id = active_call_a
4093        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4094        .await
4095        .unwrap();
4096
4097    // Join the project as client B and open all three files.
4098    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4099    let guest_buffers = futures::future::try_join_all(file_names.iter().map(|file_name| {
4100        project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, file_name), cx))
4101    }))
4102    .await
4103    .unwrap();
4104
4105    // Simulate a language server reporting errors for a file.
4106    let fake_language_server = fake_language_servers.next().await.unwrap();
4107    fake_language_server
4108        .request::<lsp::request::WorkDoneProgressCreate>(lsp::WorkDoneProgressCreateParams {
4109            token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4110        })
4111        .await
4112        .unwrap();
4113    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
4114        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4115        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin(
4116            lsp::WorkDoneProgressBegin {
4117                title: "Progress Began".into(),
4118                ..Default::default()
4119            },
4120        )),
4121    });
4122    for file_name in file_names {
4123        fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
4124            lsp::PublishDiagnosticsParams {
4125                uri: lsp::Url::from_file_path(Path::new("/test").join(file_name)).unwrap(),
4126                version: None,
4127                diagnostics: vec![lsp::Diagnostic {
4128                    severity: Some(lsp::DiagnosticSeverity::WARNING),
4129                    source: Some("the-disk-based-diagnostics-source".into()),
4130                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
4131                    message: "message one".to_string(),
4132                    ..Default::default()
4133                }],
4134            },
4135        );
4136    }
4137    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
4138        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4139        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End(
4140            lsp::WorkDoneProgressEnd { message: None },
4141        )),
4142    });
4143
4144    // When the "disk base diagnostics finished" message is received, the buffers'
4145    // diagnostics are expected to be present.
4146    let disk_based_diagnostics_finished = Arc::new(AtomicBool::new(false));
4147    project_b.update(cx_b, {
4148        let project_b = project_b.clone();
4149        let disk_based_diagnostics_finished = disk_based_diagnostics_finished.clone();
4150        move |_, cx| {
4151            cx.subscribe(&project_b, move |_, _, event, cx| {
4152                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
4153                    disk_based_diagnostics_finished.store(true, SeqCst);
4154                    for buffer in &guest_buffers {
4155                        assert_eq!(
4156                            buffer
4157                                .read(cx)
4158                                .snapshot()
4159                                .diagnostics_in_range::<_, usize>(0..5, false)
4160                                .count(),
4161                            1,
4162                            "expected a diagnostic for buffer {:?}",
4163                            buffer.read(cx).file().unwrap().path(),
4164                        );
4165                    }
4166                }
4167            })
4168            .detach();
4169        }
4170    });
4171
4172    executor.run_until_parked();
4173    assert!(disk_based_diagnostics_finished.load(SeqCst));
4174}
4175
4176#[gpui::test(iterations = 10)]
4177async fn test_reloading_buffer_manually(
4178    executor: BackgroundExecutor,
4179    cx_a: &mut TestAppContext,
4180    cx_b: &mut TestAppContext,
4181) {
4182    let mut server = TestServer::start(executor.clone()).await;
4183    let client_a = server.create_client(cx_a, "user_a").await;
4184    let client_b = server.create_client(cx_b, "user_b").await;
4185    server
4186        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4187        .await;
4188    let active_call_a = cx_a.read(ActiveCall::global);
4189
4190    client_a
4191        .fs()
4192        .insert_tree("/a", json!({ "a.rs": "let one = 1;" }))
4193        .await;
4194    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
4195    let buffer_a = project_a
4196        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
4197        .await
4198        .unwrap();
4199    let project_id = active_call_a
4200        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4201        .await
4202        .unwrap();
4203
4204    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4205
4206    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4207    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4208    buffer_b.update(cx_b, |buffer, cx| {
4209        buffer.edit([(4..7, "six")], None, cx);
4210        buffer.edit([(10..11, "6")], None, cx);
4211        assert_eq!(buffer.text(), "let six = 6;");
4212        assert!(buffer.is_dirty());
4213        assert!(!buffer.has_conflict());
4214    });
4215    executor.run_until_parked();
4216
4217    buffer_a.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "let six = 6;"));
4218
4219    client_a
4220        .fs()
4221        .save(
4222            "/a/a.rs".as_ref(),
4223            &Rope::from("let seven = 7;"),
4224            LineEnding::Unix,
4225        )
4226        .await
4227        .unwrap();
4228    executor.run_until_parked();
4229
4230    buffer_a.read_with(cx_a, |buffer, _| assert!(buffer.has_conflict()));
4231
4232    buffer_b.read_with(cx_b, |buffer, _| assert!(buffer.has_conflict()));
4233
4234    project_b
4235        .update(cx_b, |project, cx| {
4236            project.reload_buffers(HashSet::from_iter([buffer_b.clone()]), true, cx)
4237        })
4238        .await
4239        .unwrap();
4240
4241    buffer_a.read_with(cx_a, |buffer, _| {
4242        assert_eq!(buffer.text(), "let seven = 7;");
4243        assert!(!buffer.is_dirty());
4244        assert!(!buffer.has_conflict());
4245    });
4246
4247    buffer_b.read_with(cx_b, |buffer, _| {
4248        assert_eq!(buffer.text(), "let seven = 7;");
4249        assert!(!buffer.is_dirty());
4250        assert!(!buffer.has_conflict());
4251    });
4252
4253    buffer_a.update(cx_a, |buffer, cx| {
4254        // Undoing on the host is a no-op when the reload was initiated by the guest.
4255        buffer.undo(cx);
4256        assert_eq!(buffer.text(), "let seven = 7;");
4257        assert!(!buffer.is_dirty());
4258        assert!(!buffer.has_conflict());
4259    });
4260    buffer_b.update(cx_b, |buffer, cx| {
4261        // Undoing on the guest rolls back the buffer to before it was reloaded but the conflict gets cleared.
4262        buffer.undo(cx);
4263        assert_eq!(buffer.text(), "let six = 6;");
4264        assert!(buffer.is_dirty());
4265        assert!(!buffer.has_conflict());
4266    });
4267}
4268
4269#[gpui::test(iterations = 10)]
4270async fn test_formatting_buffer(
4271    executor: BackgroundExecutor,
4272    cx_a: &mut TestAppContext,
4273    cx_b: &mut TestAppContext,
4274) {
4275    executor.allow_parking();
4276    let mut server = TestServer::start(executor.clone()).await;
4277    let client_a = server.create_client(cx_a, "user_a").await;
4278    let client_b = server.create_client(cx_b, "user_b").await;
4279    server
4280        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4281        .await;
4282    let active_call_a = cx_a.read(ActiveCall::global);
4283
4284    // Set up a fake language server.
4285    let mut language = Language::new(
4286        LanguageConfig {
4287            name: "Rust".into(),
4288            path_suffixes: vec!["rs".to_string()],
4289            ..Default::default()
4290        },
4291        Some(tree_sitter_rust::language()),
4292    );
4293    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4294    client_a.language_registry().add(Arc::new(language));
4295
4296    // Here we insert a fake tree with a directory that exists on disk. This is needed
4297    // because later we'll invoke a command, which requires passing a working directory
4298    // that points to a valid location on disk.
4299    let directory = env::current_dir().unwrap();
4300    client_a
4301        .fs()
4302        .insert_tree(&directory, json!({ "a.rs": "let one = \"two\"" }))
4303        .await;
4304    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
4305    let project_id = active_call_a
4306        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4307        .await
4308        .unwrap();
4309    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4310
4311    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4312    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4313
4314    let fake_language_server = fake_language_servers.next().await.unwrap();
4315    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
4316        Ok(Some(vec![
4317            lsp::TextEdit {
4318                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 4)),
4319                new_text: "h".to_string(),
4320            },
4321            lsp::TextEdit {
4322                range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 7)),
4323                new_text: "y".to_string(),
4324            },
4325        ]))
4326    });
4327
4328    project_b
4329        .update(cx_b, |project, cx| {
4330            project.format(
4331                HashSet::from_iter([buffer_b.clone()]),
4332                true,
4333                FormatTrigger::Save,
4334                cx,
4335            )
4336        })
4337        .await
4338        .unwrap();
4339
4340    // The edits from the LSP are applied, and a final newline is added.
4341    assert_eq!(
4342        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4343        "let honey = \"two\"\n"
4344    );
4345
4346    // Ensure buffer can be formatted using an external command. Notice how the
4347    // host's configuration is honored as opposed to using the guest's settings.
4348    cx_a.update(|cx| {
4349        cx.update_global(|store: &mut SettingsStore, cx| {
4350            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4351                file.defaults.formatter = Some(Formatter::External {
4352                    command: "awk".into(),
4353                    arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()].into(),
4354                });
4355            });
4356        });
4357    });
4358    project_b
4359        .update(cx_b, |project, cx| {
4360            project.format(
4361                HashSet::from_iter([buffer_b.clone()]),
4362                true,
4363                FormatTrigger::Save,
4364                cx,
4365            )
4366        })
4367        .await
4368        .unwrap();
4369    assert_eq!(
4370        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4371        format!("let honey = \"{}/a.rs\"\n", directory.to_str().unwrap())
4372    );
4373}
4374
4375#[gpui::test(iterations = 10)]
4376async fn test_prettier_formatting_buffer(
4377    executor: BackgroundExecutor,
4378    cx_a: &mut TestAppContext,
4379    cx_b: &mut TestAppContext,
4380) {
4381    let mut server = TestServer::start(executor.clone()).await;
4382    let client_a = server.create_client(cx_a, "user_a").await;
4383    let client_b = server.create_client(cx_b, "user_b").await;
4384    server
4385        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4386        .await;
4387    let active_call_a = cx_a.read(ActiveCall::global);
4388
4389    // Set up a fake language server.
4390    let mut language = Language::new(
4391        LanguageConfig {
4392            name: "Rust".into(),
4393            path_suffixes: vec!["rs".to_string()],
4394            prettier_parser_name: Some("test_parser".to_string()),
4395            ..Default::default()
4396        },
4397        Some(tree_sitter_rust::language()),
4398    );
4399    let test_plugin = "test_plugin";
4400    let mut fake_language_servers = language
4401        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4402            prettier_plugins: vec![test_plugin],
4403            ..Default::default()
4404        }))
4405        .await;
4406    let language = Arc::new(language);
4407    client_a.language_registry().add(Arc::clone(&language));
4408
4409    // Here we insert a fake tree with a directory that exists on disk. This is needed
4410    // because later we'll invoke a command, which requires passing a working directory
4411    // that points to a valid location on disk.
4412    let directory = env::current_dir().unwrap();
4413    let buffer_text = "let one = \"two\"";
4414    client_a
4415        .fs()
4416        .insert_tree(&directory, json!({ "a.rs": buffer_text }))
4417        .await;
4418    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
4419    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
4420    let open_buffer = project_a.update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4421    let buffer_a = cx_a.executor().spawn(open_buffer).await.unwrap();
4422
4423    let project_id = active_call_a
4424        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4425        .await
4426        .unwrap();
4427    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4428    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4429    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4430
4431    cx_a.update(|cx| {
4432        cx.update_global(|store: &mut SettingsStore, cx| {
4433            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4434                file.defaults.formatter = Some(Formatter::Auto);
4435            });
4436        });
4437    });
4438    cx_b.update(|cx| {
4439        cx.update_global(|store: &mut SettingsStore, cx| {
4440            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4441                file.defaults.formatter = Some(Formatter::LanguageServer);
4442            });
4443        });
4444    });
4445    let fake_language_server = fake_language_servers.next().await.unwrap();
4446    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
4447        panic!(
4448            "Unexpected: prettier should be preferred since it's enabled and language supports it"
4449        )
4450    });
4451
4452    project_b
4453        .update(cx_b, |project, cx| {
4454            project.format(
4455                HashSet::from_iter([buffer_b.clone()]),
4456                true,
4457                FormatTrigger::Save,
4458                cx,
4459            )
4460        })
4461        .await
4462        .unwrap();
4463
4464    executor.run_until_parked();
4465    assert_eq!(
4466        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4467        buffer_text.to_string() + "\n" + prettier_format_suffix,
4468        "Prettier formatting was not applied to client buffer after client's request"
4469    );
4470
4471    project_a
4472        .update(cx_a, |project, cx| {
4473            project.format(
4474                HashSet::from_iter([buffer_a.clone()]),
4475                true,
4476                FormatTrigger::Manual,
4477                cx,
4478            )
4479        })
4480        .await
4481        .unwrap();
4482
4483    executor.run_until_parked();
4484    assert_eq!(
4485        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4486        buffer_text.to_string() + "\n" + prettier_format_suffix + "\n" + prettier_format_suffix,
4487        "Prettier formatting was not applied to client buffer after host's request"
4488    );
4489}
4490
4491#[gpui::test(iterations = 10)]
4492async fn test_definition(
4493    executor: BackgroundExecutor,
4494    cx_a: &mut TestAppContext,
4495    cx_b: &mut TestAppContext,
4496) {
4497    let mut server = TestServer::start(executor.clone()).await;
4498    let client_a = server.create_client(cx_a, "user_a").await;
4499    let client_b = server.create_client(cx_b, "user_b").await;
4500    server
4501        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4502        .await;
4503    let active_call_a = cx_a.read(ActiveCall::global);
4504
4505    // Set up a fake language server.
4506    let mut language = Language::new(
4507        LanguageConfig {
4508            name: "Rust".into(),
4509            path_suffixes: vec!["rs".to_string()],
4510            ..Default::default()
4511        },
4512        Some(tree_sitter_rust::language()),
4513    );
4514    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4515    client_a.language_registry().add(Arc::new(language));
4516
4517    client_a
4518        .fs()
4519        .insert_tree(
4520            "/root",
4521            json!({
4522                "dir-1": {
4523                    "a.rs": "const ONE: usize = b::TWO + b::THREE;",
4524                },
4525                "dir-2": {
4526                    "b.rs": "const TWO: c::T2 = 2;\nconst THREE: usize = 3;",
4527                    "c.rs": "type T2 = usize;",
4528                }
4529            }),
4530        )
4531        .await;
4532    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4533    let project_id = active_call_a
4534        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4535        .await
4536        .unwrap();
4537    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4538
4539    // Open the file on client B.
4540    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4541    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4542
4543    // Request the definition of a symbol as the guest.
4544    let fake_language_server = fake_language_servers.next().await.unwrap();
4545    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4546        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4547            lsp::Location::new(
4548                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4549                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4550            ),
4551        )))
4552    });
4553
4554    let definitions_1 = project_b
4555        .update(cx_b, |p, cx| p.definition(&buffer_b, 23, cx))
4556        .await
4557        .unwrap();
4558    cx_b.read(|cx| {
4559        assert_eq!(definitions_1.len(), 1);
4560        assert_eq!(project_b.read(cx).worktrees().count(), 2);
4561        let target_buffer = definitions_1[0].target.buffer.read(cx);
4562        assert_eq!(
4563            target_buffer.text(),
4564            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4565        );
4566        assert_eq!(
4567            definitions_1[0].target.range.to_point(target_buffer),
4568            Point::new(0, 6)..Point::new(0, 9)
4569        );
4570    });
4571
4572    // Try getting more definitions for the same buffer, ensuring the buffer gets reused from
4573    // the previous call to `definition`.
4574    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4575        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4576            lsp::Location::new(
4577                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4578                lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)),
4579            ),
4580        )))
4581    });
4582
4583    let definitions_2 = project_b
4584        .update(cx_b, |p, cx| p.definition(&buffer_b, 33, cx))
4585        .await
4586        .unwrap();
4587    cx_b.read(|cx| {
4588        assert_eq!(definitions_2.len(), 1);
4589        assert_eq!(project_b.read(cx).worktrees().count(), 2);
4590        let target_buffer = definitions_2[0].target.buffer.read(cx);
4591        assert_eq!(
4592            target_buffer.text(),
4593            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4594        );
4595        assert_eq!(
4596            definitions_2[0].target.range.to_point(target_buffer),
4597            Point::new(1, 6)..Point::new(1, 11)
4598        );
4599    });
4600    assert_eq!(
4601        definitions_1[0].target.buffer,
4602        definitions_2[0].target.buffer
4603    );
4604
4605    fake_language_server.handle_request::<lsp::request::GotoTypeDefinition, _, _>(
4606        |req, _| async move {
4607            assert_eq!(
4608                req.text_document_position_params.position,
4609                lsp::Position::new(0, 7)
4610            );
4611            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4612                lsp::Location::new(
4613                    lsp::Url::from_file_path("/root/dir-2/c.rs").unwrap(),
4614                    lsp::Range::new(lsp::Position::new(0, 5), lsp::Position::new(0, 7)),
4615                ),
4616            )))
4617        },
4618    );
4619
4620    let type_definitions = project_b
4621        .update(cx_b, |p, cx| p.type_definition(&buffer_b, 7, cx))
4622        .await
4623        .unwrap();
4624    cx_b.read(|cx| {
4625        assert_eq!(type_definitions.len(), 1);
4626        let target_buffer = type_definitions[0].target.buffer.read(cx);
4627        assert_eq!(target_buffer.text(), "type T2 = usize;");
4628        assert_eq!(
4629            type_definitions[0].target.range.to_point(target_buffer),
4630            Point::new(0, 5)..Point::new(0, 7)
4631        );
4632    });
4633}
4634
4635#[gpui::test(iterations = 10)]
4636async fn test_references(
4637    executor: BackgroundExecutor,
4638    cx_a: &mut TestAppContext,
4639    cx_b: &mut TestAppContext,
4640) {
4641    let mut server = TestServer::start(executor.clone()).await;
4642    let client_a = server.create_client(cx_a, "user_a").await;
4643    let client_b = server.create_client(cx_b, "user_b").await;
4644    server
4645        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4646        .await;
4647    let active_call_a = cx_a.read(ActiveCall::global);
4648
4649    // Set up a fake language server.
4650    let mut language = Language::new(
4651        LanguageConfig {
4652            name: "Rust".into(),
4653            path_suffixes: vec!["rs".to_string()],
4654            ..Default::default()
4655        },
4656        Some(tree_sitter_rust::language()),
4657    );
4658    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4659    client_a.language_registry().add(Arc::new(language));
4660
4661    client_a
4662        .fs()
4663        .insert_tree(
4664            "/root",
4665            json!({
4666                "dir-1": {
4667                    "one.rs": "const ONE: usize = 1;",
4668                    "two.rs": "const TWO: usize = one::ONE + one::ONE;",
4669                },
4670                "dir-2": {
4671                    "three.rs": "const THREE: usize = two::TWO + one::ONE;",
4672                }
4673            }),
4674        )
4675        .await;
4676    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4677    let project_id = active_call_a
4678        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4679        .await
4680        .unwrap();
4681    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4682
4683    // Open the file on client B.
4684    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx));
4685    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4686
4687    // Request references to a symbol as the guest.
4688    let fake_language_server = fake_language_servers.next().await.unwrap();
4689    fake_language_server.handle_request::<lsp::request::References, _, _>(|params, _| async move {
4690        assert_eq!(
4691            params.text_document_position.text_document.uri.as_str(),
4692            "file:///root/dir-1/one.rs"
4693        );
4694        Ok(Some(vec![
4695            lsp::Location {
4696                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4697                range: lsp::Range::new(lsp::Position::new(0, 24), lsp::Position::new(0, 27)),
4698            },
4699            lsp::Location {
4700                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4701                range: lsp::Range::new(lsp::Position::new(0, 35), lsp::Position::new(0, 38)),
4702            },
4703            lsp::Location {
4704                uri: lsp::Url::from_file_path("/root/dir-2/three.rs").unwrap(),
4705                range: lsp::Range::new(lsp::Position::new(0, 37), lsp::Position::new(0, 40)),
4706            },
4707        ]))
4708    });
4709
4710    let references = project_b
4711        .update(cx_b, |p, cx| p.references(&buffer_b, 7, cx))
4712        .await
4713        .unwrap();
4714    cx_b.read(|cx| {
4715        assert_eq!(references.len(), 3);
4716        assert_eq!(project_b.read(cx).worktrees().count(), 2);
4717
4718        let two_buffer = references[0].buffer.read(cx);
4719        let three_buffer = references[2].buffer.read(cx);
4720        assert_eq!(
4721            two_buffer.file().unwrap().path().as_ref(),
4722            Path::new("two.rs")
4723        );
4724        assert_eq!(references[1].buffer, references[0].buffer);
4725        assert_eq!(
4726            three_buffer.file().unwrap().full_path(cx),
4727            Path::new("/root/dir-2/three.rs")
4728        );
4729
4730        assert_eq!(references[0].range.to_offset(two_buffer), 24..27);
4731        assert_eq!(references[1].range.to_offset(two_buffer), 35..38);
4732        assert_eq!(references[2].range.to_offset(three_buffer), 37..40);
4733    });
4734}
4735
4736#[gpui::test(iterations = 10)]
4737async fn test_project_search(
4738    executor: BackgroundExecutor,
4739    cx_a: &mut TestAppContext,
4740    cx_b: &mut TestAppContext,
4741) {
4742    let mut server = TestServer::start(executor.clone()).await;
4743    let client_a = server.create_client(cx_a, "user_a").await;
4744    let client_b = server.create_client(cx_b, "user_b").await;
4745    server
4746        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4747        .await;
4748    let active_call_a = cx_a.read(ActiveCall::global);
4749
4750    client_a
4751        .fs()
4752        .insert_tree(
4753            "/root",
4754            json!({
4755                "dir-1": {
4756                    "a": "hello world",
4757                    "b": "goodnight moon",
4758                    "c": "a world of goo",
4759                    "d": "world champion of clown world",
4760                },
4761                "dir-2": {
4762                    "e": "disney world is fun",
4763                }
4764            }),
4765        )
4766        .await;
4767    let (project_a, _) = client_a.build_local_project("/root/dir-1", cx_a).await;
4768    let (worktree_2, _) = project_a
4769        .update(cx_a, |p, cx| {
4770            p.find_or_create_local_worktree("/root/dir-2", true, cx)
4771        })
4772        .await
4773        .unwrap();
4774    worktree_2
4775        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
4776        .await;
4777    let project_id = active_call_a
4778        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4779        .await
4780        .unwrap();
4781
4782    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4783
4784    // Perform a search as the guest.
4785    let mut results = HashMap::default();
4786    let mut search_rx = project_b.update(cx_b, |project, cx| {
4787        project.search(
4788            SearchQuery::text("world", false, false, false, Vec::new(), Vec::new()).unwrap(),
4789            cx,
4790        )
4791    });
4792    while let Some((buffer, ranges)) = search_rx.next().await {
4793        results.entry(buffer).or_insert(ranges);
4794    }
4795
4796    let mut ranges_by_path = results
4797        .into_iter()
4798        .map(|(buffer, ranges)| {
4799            buffer.read_with(cx_b, |buffer, cx| {
4800                let path = buffer.file().unwrap().full_path(cx);
4801                let offset_ranges = ranges
4802                    .into_iter()
4803                    .map(|range| range.to_offset(buffer))
4804                    .collect::<Vec<_>>();
4805                (path, offset_ranges)
4806            })
4807        })
4808        .collect::<Vec<_>>();
4809    ranges_by_path.sort_by_key(|(path, _)| path.clone());
4810
4811    assert_eq!(
4812        ranges_by_path,
4813        &[
4814            (PathBuf::from("dir-1/a"), vec![6..11]),
4815            (PathBuf::from("dir-1/c"), vec![2..7]),
4816            (PathBuf::from("dir-1/d"), vec![0..5, 24..29]),
4817            (PathBuf::from("dir-2/e"), vec![7..12]),
4818        ]
4819    );
4820}
4821
4822#[gpui::test(iterations = 10)]
4823async fn test_document_highlights(
4824    executor: BackgroundExecutor,
4825    cx_a: &mut TestAppContext,
4826    cx_b: &mut TestAppContext,
4827) {
4828    let mut server = TestServer::start(executor.clone()).await;
4829    let client_a = server.create_client(cx_a, "user_a").await;
4830    let client_b = server.create_client(cx_b, "user_b").await;
4831    server
4832        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4833        .await;
4834    let active_call_a = cx_a.read(ActiveCall::global);
4835
4836    client_a
4837        .fs()
4838        .insert_tree(
4839            "/root-1",
4840            json!({
4841                "main.rs": "fn double(number: i32) -> i32 { number + number }",
4842            }),
4843        )
4844        .await;
4845
4846    // Set up a fake language server.
4847    let mut language = Language::new(
4848        LanguageConfig {
4849            name: "Rust".into(),
4850            path_suffixes: vec!["rs".to_string()],
4851            ..Default::default()
4852        },
4853        Some(tree_sitter_rust::language()),
4854    );
4855    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4856    client_a.language_registry().add(Arc::new(language));
4857
4858    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4859    let project_id = active_call_a
4860        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4861        .await
4862        .unwrap();
4863    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4864
4865    // Open the file on client B.
4866    let open_b = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx));
4867    let buffer_b = cx_b.executor().spawn(open_b).await.unwrap();
4868
4869    // Request document highlights as the guest.
4870    let fake_language_server = fake_language_servers.next().await.unwrap();
4871    fake_language_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
4872        |params, _| async move {
4873            assert_eq!(
4874                params
4875                    .text_document_position_params
4876                    .text_document
4877                    .uri
4878                    .as_str(),
4879                "file:///root-1/main.rs"
4880            );
4881            assert_eq!(
4882                params.text_document_position_params.position,
4883                lsp::Position::new(0, 34)
4884            );
4885            Ok(Some(vec![
4886                lsp::DocumentHighlight {
4887                    kind: Some(lsp::DocumentHighlightKind::WRITE),
4888                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 16)),
4889                },
4890                lsp::DocumentHighlight {
4891                    kind: Some(lsp::DocumentHighlightKind::READ),
4892                    range: lsp::Range::new(lsp::Position::new(0, 32), lsp::Position::new(0, 38)),
4893                },
4894                lsp::DocumentHighlight {
4895                    kind: Some(lsp::DocumentHighlightKind::READ),
4896                    range: lsp::Range::new(lsp::Position::new(0, 41), lsp::Position::new(0, 47)),
4897                },
4898            ]))
4899        },
4900    );
4901
4902    let highlights = project_b
4903        .update(cx_b, |p, cx| p.document_highlights(&buffer_b, 34, cx))
4904        .await
4905        .unwrap();
4906
4907    buffer_b.read_with(cx_b, |buffer, _| {
4908        let snapshot = buffer.snapshot();
4909
4910        let highlights = highlights
4911            .into_iter()
4912            .map(|highlight| (highlight.kind, highlight.range.to_offset(&snapshot)))
4913            .collect::<Vec<_>>();
4914        assert_eq!(
4915            highlights,
4916            &[
4917                (lsp::DocumentHighlightKind::WRITE, 10..16),
4918                (lsp::DocumentHighlightKind::READ, 32..38),
4919                (lsp::DocumentHighlightKind::READ, 41..47)
4920            ]
4921        )
4922    });
4923}
4924
4925#[gpui::test(iterations = 10)]
4926async fn test_lsp_hover(
4927    executor: BackgroundExecutor,
4928    cx_a: &mut TestAppContext,
4929    cx_b: &mut TestAppContext,
4930) {
4931    let mut server = TestServer::start(executor.clone()).await;
4932    let client_a = server.create_client(cx_a, "user_a").await;
4933    let client_b = server.create_client(cx_b, "user_b").await;
4934    server
4935        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4936        .await;
4937    let active_call_a = cx_a.read(ActiveCall::global);
4938
4939    client_a
4940        .fs()
4941        .insert_tree(
4942            "/root-1",
4943            json!({
4944                "main.rs": "use std::collections::HashMap;",
4945            }),
4946        )
4947        .await;
4948
4949    // Set up a fake language server.
4950    let mut language = Language::new(
4951        LanguageConfig {
4952            name: "Rust".into(),
4953            path_suffixes: vec!["rs".to_string()],
4954            ..Default::default()
4955        },
4956        Some(tree_sitter_rust::language()),
4957    );
4958    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4959    client_a.language_registry().add(Arc::new(language));
4960
4961    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4962    let project_id = active_call_a
4963        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4964        .await
4965        .unwrap();
4966    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4967
4968    // Open the file as the guest
4969    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx));
4970    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4971
4972    // Request hover information as the guest.
4973    let fake_language_server = fake_language_servers.next().await.unwrap();
4974    fake_language_server.handle_request::<lsp::request::HoverRequest, _, _>(
4975        |params, _| async move {
4976            assert_eq!(
4977                params
4978                    .text_document_position_params
4979                    .text_document
4980                    .uri
4981                    .as_str(),
4982                "file:///root-1/main.rs"
4983            );
4984            assert_eq!(
4985                params.text_document_position_params.position,
4986                lsp::Position::new(0, 22)
4987            );
4988            Ok(Some(lsp::Hover {
4989                contents: lsp::HoverContents::Array(vec![
4990                    lsp::MarkedString::String("Test hover content.".to_string()),
4991                    lsp::MarkedString::LanguageString(lsp::LanguageString {
4992                        language: "Rust".to_string(),
4993                        value: "let foo = 42;".to_string(),
4994                    }),
4995                ]),
4996                range: Some(lsp::Range::new(
4997                    lsp::Position::new(0, 22),
4998                    lsp::Position::new(0, 29),
4999                )),
5000            }))
5001        },
5002    );
5003
5004    let hover_info = project_b
5005        .update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
5006        .await
5007        .unwrap()
5008        .unwrap();
5009
5010    buffer_b.read_with(cx_b, |buffer, _| {
5011        let snapshot = buffer.snapshot();
5012        assert_eq!(hover_info.range.unwrap().to_offset(&snapshot), 22..29);
5013        assert_eq!(
5014            hover_info.contents,
5015            vec![
5016                project::HoverBlock {
5017                    text: "Test hover content.".to_string(),
5018                    kind: HoverBlockKind::Markdown,
5019                },
5020                project::HoverBlock {
5021                    text: "let foo = 42;".to_string(),
5022                    kind: HoverBlockKind::Code {
5023                        language: "Rust".to_string()
5024                    },
5025                }
5026            ]
5027        );
5028    });
5029}
5030
5031#[gpui::test(iterations = 10)]
5032async fn test_project_symbols(
5033    executor: BackgroundExecutor,
5034    cx_a: &mut TestAppContext,
5035    cx_b: &mut TestAppContext,
5036) {
5037    let mut server = TestServer::start(executor.clone()).await;
5038    let client_a = server.create_client(cx_a, "user_a").await;
5039    let client_b = server.create_client(cx_b, "user_b").await;
5040    server
5041        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5042        .await;
5043    let active_call_a = cx_a.read(ActiveCall::global);
5044
5045    // Set up a fake language server.
5046    let mut language = Language::new(
5047        LanguageConfig {
5048            name: "Rust".into(),
5049            path_suffixes: vec!["rs".to_string()],
5050            ..Default::default()
5051        },
5052        Some(tree_sitter_rust::language()),
5053    );
5054    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5055    client_a.language_registry().add(Arc::new(language));
5056
5057    client_a
5058        .fs()
5059        .insert_tree(
5060            "/code",
5061            json!({
5062                "crate-1": {
5063                    "one.rs": "const ONE: usize = 1;",
5064                },
5065                "crate-2": {
5066                    "two.rs": "const TWO: usize = 2; const THREE: usize = 3;",
5067                },
5068                "private": {
5069                    "passwords.txt": "the-password",
5070                }
5071            }),
5072        )
5073        .await;
5074    let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await;
5075    let project_id = active_call_a
5076        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5077        .await
5078        .unwrap();
5079    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5080
5081    // Cause the language server to start.
5082    let open_buffer_task =
5083        project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx));
5084    let _buffer = cx_b.executor().spawn(open_buffer_task).await.unwrap();
5085
5086    let fake_language_server = fake_language_servers.next().await.unwrap();
5087    fake_language_server.handle_request::<lsp::WorkspaceSymbolRequest, _, _>(|_, _| async move {
5088        Ok(Some(lsp::WorkspaceSymbolResponse::Flat(vec![
5089            #[allow(deprecated)]
5090            lsp::SymbolInformation {
5091                name: "TWO".into(),
5092                location: lsp::Location {
5093                    uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
5094                    range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5095                },
5096                kind: lsp::SymbolKind::CONSTANT,
5097                tags: None,
5098                container_name: None,
5099                deprecated: None,
5100            },
5101        ])))
5102    });
5103
5104    // Request the definition of a symbol as the guest.
5105    let symbols = project_b
5106        .update(cx_b, |p, cx| p.symbols("two", cx))
5107        .await
5108        .unwrap();
5109    assert_eq!(symbols.len(), 1);
5110    assert_eq!(symbols[0].name, "TWO");
5111
5112    // Open one of the returned symbols.
5113    let buffer_b_2 = project_b
5114        .update(cx_b, |project, cx| {
5115            project.open_buffer_for_symbol(&symbols[0], cx)
5116        })
5117        .await
5118        .unwrap();
5119
5120    buffer_b_2.read_with(cx_b, |buffer, cx| {
5121        assert_eq!(
5122            buffer.file().unwrap().full_path(cx),
5123            Path::new("/code/crate-2/two.rs")
5124        );
5125    });
5126
5127    // Attempt to craft a symbol and violate host's privacy by opening an arbitrary file.
5128    let mut fake_symbol = symbols[0].clone();
5129    fake_symbol.path.path = Path::new("/code/secrets").into();
5130    let error = project_b
5131        .update(cx_b, |project, cx| {
5132            project.open_buffer_for_symbol(&fake_symbol, cx)
5133        })
5134        .await
5135        .unwrap_err();
5136    assert!(error.to_string().contains("invalid symbol signature"));
5137}
5138
5139#[gpui::test(iterations = 10)]
5140async fn test_open_buffer_while_getting_definition_pointing_to_it(
5141    executor: BackgroundExecutor,
5142    cx_a: &mut TestAppContext,
5143    cx_b: &mut TestAppContext,
5144    mut rng: StdRng,
5145) {
5146    let mut server = TestServer::start(executor.clone()).await;
5147    let client_a = server.create_client(cx_a, "user_a").await;
5148    let client_b = server.create_client(cx_b, "user_b").await;
5149    server
5150        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5151        .await;
5152    let active_call_a = cx_a.read(ActiveCall::global);
5153
5154    // Set up a fake language server.
5155    let mut language = Language::new(
5156        LanguageConfig {
5157            name: "Rust".into(),
5158            path_suffixes: vec!["rs".to_string()],
5159            ..Default::default()
5160        },
5161        Some(tree_sitter_rust::language()),
5162    );
5163    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5164    client_a.language_registry().add(Arc::new(language));
5165
5166    client_a
5167        .fs()
5168        .insert_tree(
5169            "/root",
5170            json!({
5171                "a.rs": "const ONE: usize = b::TWO;",
5172                "b.rs": "const TWO: usize = 2",
5173            }),
5174        )
5175        .await;
5176    let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await;
5177    let project_id = active_call_a
5178        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5179        .await
5180        .unwrap();
5181    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5182
5183    let open_buffer_task = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
5184    let buffer_b1 = cx_b.executor().spawn(open_buffer_task).await.unwrap();
5185
5186    let fake_language_server = fake_language_servers.next().await.unwrap();
5187    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
5188        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
5189            lsp::Location::new(
5190                lsp::Url::from_file_path("/root/b.rs").unwrap(),
5191                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5192            ),
5193        )))
5194    });
5195
5196    let definitions;
5197    let buffer_b2;
5198    if rng.gen() {
5199        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5200        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5201    } else {
5202        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5203        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5204    }
5205
5206    let buffer_b2 = buffer_b2.await.unwrap();
5207    let definitions = definitions.await.unwrap();
5208    assert_eq!(definitions.len(), 1);
5209    assert_eq!(definitions[0].target.buffer, buffer_b2);
5210}
5211
5212#[gpui::test(iterations = 10)]
5213async fn test_contacts(
5214    executor: BackgroundExecutor,
5215    cx_a: &mut TestAppContext,
5216    cx_b: &mut TestAppContext,
5217    cx_c: &mut TestAppContext,
5218    cx_d: &mut TestAppContext,
5219) {
5220    let mut server = TestServer::start(executor.clone()).await;
5221    let client_a = server.create_client(cx_a, "user_a").await;
5222    let client_b = server.create_client(cx_b, "user_b").await;
5223    let client_c = server.create_client(cx_c, "user_c").await;
5224    let client_d = server.create_client(cx_d, "user_d").await;
5225    server
5226        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
5227        .await;
5228    let active_call_a = cx_a.read(ActiveCall::global);
5229    let active_call_b = cx_b.read(ActiveCall::global);
5230    let active_call_c = cx_c.read(ActiveCall::global);
5231    let _active_call_d = cx_d.read(ActiveCall::global);
5232
5233    executor.run_until_parked();
5234    assert_eq!(
5235        contacts(&client_a, cx_a),
5236        [
5237            ("user_b".to_string(), "online", "free"),
5238            ("user_c".to_string(), "online", "free")
5239        ]
5240    );
5241    assert_eq!(
5242        contacts(&client_b, cx_b),
5243        [
5244            ("user_a".to_string(), "online", "free"),
5245            ("user_c".to_string(), "online", "free")
5246        ]
5247    );
5248    assert_eq!(
5249        contacts(&client_c, cx_c),
5250        [
5251            ("user_a".to_string(), "online", "free"),
5252            ("user_b".to_string(), "online", "free")
5253        ]
5254    );
5255    assert_eq!(contacts(&client_d, cx_d), []);
5256
5257    server.disconnect_client(client_c.peer_id().unwrap());
5258    server.forbid_connections();
5259    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5260    assert_eq!(
5261        contacts(&client_a, cx_a),
5262        [
5263            ("user_b".to_string(), "online", "free"),
5264            ("user_c".to_string(), "offline", "free")
5265        ]
5266    );
5267    assert_eq!(
5268        contacts(&client_b, cx_b),
5269        [
5270            ("user_a".to_string(), "online", "free"),
5271            ("user_c".to_string(), "offline", "free")
5272        ]
5273    );
5274    assert_eq!(contacts(&client_c, cx_c), []);
5275    assert_eq!(contacts(&client_d, cx_d), []);
5276
5277    server.allow_connections();
5278    client_c
5279        .authenticate_and_connect(false, &cx_c.to_async())
5280        .await
5281        .unwrap();
5282
5283    executor.run_until_parked();
5284    assert_eq!(
5285        contacts(&client_a, cx_a),
5286        [
5287            ("user_b".to_string(), "online", "free"),
5288            ("user_c".to_string(), "online", "free")
5289        ]
5290    );
5291    assert_eq!(
5292        contacts(&client_b, cx_b),
5293        [
5294            ("user_a".to_string(), "online", "free"),
5295            ("user_c".to_string(), "online", "free")
5296        ]
5297    );
5298    assert_eq!(
5299        contacts(&client_c, cx_c),
5300        [
5301            ("user_a".to_string(), "online", "free"),
5302            ("user_b".to_string(), "online", "free")
5303        ]
5304    );
5305    assert_eq!(contacts(&client_d, cx_d), []);
5306
5307    active_call_a
5308        .update(cx_a, |call, cx| {
5309            call.invite(client_b.user_id().unwrap(), None, cx)
5310        })
5311        .await
5312        .unwrap();
5313    executor.run_until_parked();
5314    assert_eq!(
5315        contacts(&client_a, cx_a),
5316        [
5317            ("user_b".to_string(), "online", "busy"),
5318            ("user_c".to_string(), "online", "free")
5319        ]
5320    );
5321    assert_eq!(
5322        contacts(&client_b, cx_b),
5323        [
5324            ("user_a".to_string(), "online", "busy"),
5325            ("user_c".to_string(), "online", "free")
5326        ]
5327    );
5328    assert_eq!(
5329        contacts(&client_c, cx_c),
5330        [
5331            ("user_a".to_string(), "online", "busy"),
5332            ("user_b".to_string(), "online", "busy")
5333        ]
5334    );
5335    assert_eq!(contacts(&client_d, cx_d), []);
5336
5337    // Client B and client D become contacts while client B is being called.
5338    server
5339        .make_contacts(&mut [(&client_b, cx_b), (&client_d, cx_d)])
5340        .await;
5341    executor.run_until_parked();
5342    assert_eq!(
5343        contacts(&client_a, cx_a),
5344        [
5345            ("user_b".to_string(), "online", "busy"),
5346            ("user_c".to_string(), "online", "free")
5347        ]
5348    );
5349    assert_eq!(
5350        contacts(&client_b, cx_b),
5351        [
5352            ("user_a".to_string(), "online", "busy"),
5353            ("user_c".to_string(), "online", "free"),
5354            ("user_d".to_string(), "online", "free"),
5355        ]
5356    );
5357    assert_eq!(
5358        contacts(&client_c, cx_c),
5359        [
5360            ("user_a".to_string(), "online", "busy"),
5361            ("user_b".to_string(), "online", "busy")
5362        ]
5363    );
5364    assert_eq!(
5365        contacts(&client_d, cx_d),
5366        [("user_b".to_string(), "online", "busy")]
5367    );
5368
5369    active_call_b.update(cx_b, |call, cx| call.decline_incoming(cx).unwrap());
5370    executor.run_until_parked();
5371    assert_eq!(
5372        contacts(&client_a, cx_a),
5373        [
5374            ("user_b".to_string(), "online", "free"),
5375            ("user_c".to_string(), "online", "free")
5376        ]
5377    );
5378    assert_eq!(
5379        contacts(&client_b, cx_b),
5380        [
5381            ("user_a".to_string(), "online", "free"),
5382            ("user_c".to_string(), "online", "free"),
5383            ("user_d".to_string(), "online", "free")
5384        ]
5385    );
5386    assert_eq!(
5387        contacts(&client_c, cx_c),
5388        [
5389            ("user_a".to_string(), "online", "free"),
5390            ("user_b".to_string(), "online", "free")
5391        ]
5392    );
5393    assert_eq!(
5394        contacts(&client_d, cx_d),
5395        [("user_b".to_string(), "online", "free")]
5396    );
5397
5398    active_call_c
5399        .update(cx_c, |call, cx| {
5400            call.invite(client_a.user_id().unwrap(), None, cx)
5401        })
5402        .await
5403        .unwrap();
5404    executor.run_until_parked();
5405    assert_eq!(
5406        contacts(&client_a, cx_a),
5407        [
5408            ("user_b".to_string(), "online", "free"),
5409            ("user_c".to_string(), "online", "busy")
5410        ]
5411    );
5412    assert_eq!(
5413        contacts(&client_b, cx_b),
5414        [
5415            ("user_a".to_string(), "online", "busy"),
5416            ("user_c".to_string(), "online", "busy"),
5417            ("user_d".to_string(), "online", "free")
5418        ]
5419    );
5420    assert_eq!(
5421        contacts(&client_c, cx_c),
5422        [
5423            ("user_a".to_string(), "online", "busy"),
5424            ("user_b".to_string(), "online", "free")
5425        ]
5426    );
5427    assert_eq!(
5428        contacts(&client_d, cx_d),
5429        [("user_b".to_string(), "online", "free")]
5430    );
5431
5432    active_call_a
5433        .update(cx_a, |call, cx| call.accept_incoming(cx))
5434        .await
5435        .unwrap();
5436    executor.run_until_parked();
5437    assert_eq!(
5438        contacts(&client_a, cx_a),
5439        [
5440            ("user_b".to_string(), "online", "free"),
5441            ("user_c".to_string(), "online", "busy")
5442        ]
5443    );
5444    assert_eq!(
5445        contacts(&client_b, cx_b),
5446        [
5447            ("user_a".to_string(), "online", "busy"),
5448            ("user_c".to_string(), "online", "busy"),
5449            ("user_d".to_string(), "online", "free")
5450        ]
5451    );
5452    assert_eq!(
5453        contacts(&client_c, cx_c),
5454        [
5455            ("user_a".to_string(), "online", "busy"),
5456            ("user_b".to_string(), "online", "free")
5457        ]
5458    );
5459    assert_eq!(
5460        contacts(&client_d, cx_d),
5461        [("user_b".to_string(), "online", "free")]
5462    );
5463
5464    active_call_a
5465        .update(cx_a, |call, cx| {
5466            call.invite(client_b.user_id().unwrap(), None, cx)
5467        })
5468        .await
5469        .unwrap();
5470    executor.run_until_parked();
5471    assert_eq!(
5472        contacts(&client_a, cx_a),
5473        [
5474            ("user_b".to_string(), "online", "busy"),
5475            ("user_c".to_string(), "online", "busy")
5476        ]
5477    );
5478    assert_eq!(
5479        contacts(&client_b, cx_b),
5480        [
5481            ("user_a".to_string(), "online", "busy"),
5482            ("user_c".to_string(), "online", "busy"),
5483            ("user_d".to_string(), "online", "free")
5484        ]
5485    );
5486    assert_eq!(
5487        contacts(&client_c, cx_c),
5488        [
5489            ("user_a".to_string(), "online", "busy"),
5490            ("user_b".to_string(), "online", "busy")
5491        ]
5492    );
5493    assert_eq!(
5494        contacts(&client_d, cx_d),
5495        [("user_b".to_string(), "online", "busy")]
5496    );
5497
5498    active_call_a
5499        .update(cx_a, |call, cx| call.hang_up(cx))
5500        .await
5501        .unwrap();
5502    executor.run_until_parked();
5503    assert_eq!(
5504        contacts(&client_a, cx_a),
5505        [
5506            ("user_b".to_string(), "online", "free"),
5507            ("user_c".to_string(), "online", "free")
5508        ]
5509    );
5510    assert_eq!(
5511        contacts(&client_b, cx_b),
5512        [
5513            ("user_a".to_string(), "online", "free"),
5514            ("user_c".to_string(), "online", "free"),
5515            ("user_d".to_string(), "online", "free")
5516        ]
5517    );
5518    assert_eq!(
5519        contacts(&client_c, cx_c),
5520        [
5521            ("user_a".to_string(), "online", "free"),
5522            ("user_b".to_string(), "online", "free")
5523        ]
5524    );
5525    assert_eq!(
5526        contacts(&client_d, cx_d),
5527        [("user_b".to_string(), "online", "free")]
5528    );
5529
5530    active_call_a
5531        .update(cx_a, |call, cx| {
5532            call.invite(client_b.user_id().unwrap(), None, cx)
5533        })
5534        .await
5535        .unwrap();
5536    executor.run_until_parked();
5537    assert_eq!(
5538        contacts(&client_a, cx_a),
5539        [
5540            ("user_b".to_string(), "online", "busy"),
5541            ("user_c".to_string(), "online", "free")
5542        ]
5543    );
5544    assert_eq!(
5545        contacts(&client_b, cx_b),
5546        [
5547            ("user_a".to_string(), "online", "busy"),
5548            ("user_c".to_string(), "online", "free"),
5549            ("user_d".to_string(), "online", "free")
5550        ]
5551    );
5552    assert_eq!(
5553        contacts(&client_c, cx_c),
5554        [
5555            ("user_a".to_string(), "online", "busy"),
5556            ("user_b".to_string(), "online", "busy")
5557        ]
5558    );
5559    assert_eq!(
5560        contacts(&client_d, cx_d),
5561        [("user_b".to_string(), "online", "busy")]
5562    );
5563
5564    server.forbid_connections();
5565    server.disconnect_client(client_a.peer_id().unwrap());
5566    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5567    assert_eq!(contacts(&client_a, cx_a), []);
5568    assert_eq!(
5569        contacts(&client_b, cx_b),
5570        [
5571            ("user_a".to_string(), "offline", "free"),
5572            ("user_c".to_string(), "online", "free"),
5573            ("user_d".to_string(), "online", "free")
5574        ]
5575    );
5576    assert_eq!(
5577        contacts(&client_c, cx_c),
5578        [
5579            ("user_a".to_string(), "offline", "free"),
5580            ("user_b".to_string(), "online", "free")
5581        ]
5582    );
5583    assert_eq!(
5584        contacts(&client_d, cx_d),
5585        [("user_b".to_string(), "online", "free")]
5586    );
5587
5588    // Test removing a contact
5589    client_b
5590        .user_store()
5591        .update(cx_b, |store, cx| {
5592            store.remove_contact(client_c.user_id().unwrap(), cx)
5593        })
5594        .await
5595        .unwrap();
5596    executor.run_until_parked();
5597    assert_eq!(
5598        contacts(&client_b, cx_b),
5599        [
5600            ("user_a".to_string(), "offline", "free"),
5601            ("user_d".to_string(), "online", "free")
5602        ]
5603    );
5604    assert_eq!(
5605        contacts(&client_c, cx_c),
5606        [("user_a".to_string(), "offline", "free"),]
5607    );
5608
5609    fn contacts(
5610        client: &TestClient,
5611        cx: &TestAppContext,
5612    ) -> Vec<(String, &'static str, &'static str)> {
5613        client.user_store().read_with(cx, |store, _| {
5614            store
5615                .contacts()
5616                .iter()
5617                .map(|contact| {
5618                    (
5619                        contact.user.github_login.clone(),
5620                        if contact.online { "online" } else { "offline" },
5621                        if contact.busy { "busy" } else { "free" },
5622                    )
5623                })
5624                .collect()
5625        })
5626    }
5627}
5628
5629#[gpui::test(iterations = 10)]
5630async fn test_contact_requests(
5631    executor: BackgroundExecutor,
5632    cx_a: &mut TestAppContext,
5633    cx_a2: &mut TestAppContext,
5634    cx_b: &mut TestAppContext,
5635    cx_b2: &mut TestAppContext,
5636    cx_c: &mut TestAppContext,
5637    cx_c2: &mut TestAppContext,
5638) {
5639    // Connect to a server as 3 clients.
5640    let mut server = TestServer::start(executor.clone()).await;
5641    let client_a = server.create_client(cx_a, "user_a").await;
5642    let client_a2 = server.create_client(cx_a2, "user_a").await;
5643    let client_b = server.create_client(cx_b, "user_b").await;
5644    let client_b2 = server.create_client(cx_b2, "user_b").await;
5645    let client_c = server.create_client(cx_c, "user_c").await;
5646    let client_c2 = server.create_client(cx_c2, "user_c").await;
5647
5648    assert_eq!(client_a.user_id().unwrap(), client_a2.user_id().unwrap());
5649    assert_eq!(client_b.user_id().unwrap(), client_b2.user_id().unwrap());
5650    assert_eq!(client_c.user_id().unwrap(), client_c2.user_id().unwrap());
5651
5652    // User A and User C request that user B become their contact.
5653    client_a
5654        .user_store()
5655        .update(cx_a, |store, cx| {
5656            store.request_contact(client_b.user_id().unwrap(), cx)
5657        })
5658        .await
5659        .unwrap();
5660    client_c
5661        .user_store()
5662        .update(cx_c, |store, cx| {
5663            store.request_contact(client_b.user_id().unwrap(), cx)
5664        })
5665        .await
5666        .unwrap();
5667    executor.run_until_parked();
5668
5669    // All users see the pending request appear in all their clients.
5670    assert_eq!(
5671        client_a.summarize_contacts(cx_a).outgoing_requests,
5672        &["user_b"]
5673    );
5674    assert_eq!(
5675        client_a2.summarize_contacts(cx_a2).outgoing_requests,
5676        &["user_b"]
5677    );
5678    assert_eq!(
5679        client_b.summarize_contacts(cx_b).incoming_requests,
5680        &["user_a", "user_c"]
5681    );
5682    assert_eq!(
5683        client_b2.summarize_contacts(cx_b2).incoming_requests,
5684        &["user_a", "user_c"]
5685    );
5686    assert_eq!(
5687        client_c.summarize_contacts(cx_c).outgoing_requests,
5688        &["user_b"]
5689    );
5690    assert_eq!(
5691        client_c2.summarize_contacts(cx_c2).outgoing_requests,
5692        &["user_b"]
5693    );
5694
5695    // Contact requests are present upon connecting (tested here via disconnect/reconnect)
5696    disconnect_and_reconnect(&client_a, cx_a).await;
5697    disconnect_and_reconnect(&client_b, cx_b).await;
5698    disconnect_and_reconnect(&client_c, cx_c).await;
5699    executor.run_until_parked();
5700    assert_eq!(
5701        client_a.summarize_contacts(cx_a).outgoing_requests,
5702        &["user_b"]
5703    );
5704    assert_eq!(
5705        client_b.summarize_contacts(cx_b).incoming_requests,
5706        &["user_a", "user_c"]
5707    );
5708    assert_eq!(
5709        client_c.summarize_contacts(cx_c).outgoing_requests,
5710        &["user_b"]
5711    );
5712
5713    // User B accepts the request from user A.
5714    client_b
5715        .user_store()
5716        .update(cx_b, |store, cx| {
5717            store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
5718        })
5719        .await
5720        .unwrap();
5721
5722    executor.run_until_parked();
5723
5724    // User B sees user A as their contact now in all client, and the incoming request from them is removed.
5725    let contacts_b = client_b.summarize_contacts(cx_b);
5726    assert_eq!(contacts_b.current, &["user_a"]);
5727    assert_eq!(contacts_b.incoming_requests, &["user_c"]);
5728    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
5729    assert_eq!(contacts_b2.current, &["user_a"]);
5730    assert_eq!(contacts_b2.incoming_requests, &["user_c"]);
5731
5732    // User A sees user B as their contact now in all clients, and the outgoing request to them is removed.
5733    let contacts_a = client_a.summarize_contacts(cx_a);
5734    assert_eq!(contacts_a.current, &["user_b"]);
5735    assert!(contacts_a.outgoing_requests.is_empty());
5736    let contacts_a2 = client_a2.summarize_contacts(cx_a2);
5737    assert_eq!(contacts_a2.current, &["user_b"]);
5738    assert!(contacts_a2.outgoing_requests.is_empty());
5739
5740    // Contacts are present upon connecting (tested here via disconnect/reconnect)
5741    disconnect_and_reconnect(&client_a, cx_a).await;
5742    disconnect_and_reconnect(&client_b, cx_b).await;
5743    disconnect_and_reconnect(&client_c, cx_c).await;
5744    executor.run_until_parked();
5745    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
5746    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
5747    assert_eq!(
5748        client_b.summarize_contacts(cx_b).incoming_requests,
5749        &["user_c"]
5750    );
5751    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
5752    assert_eq!(
5753        client_c.summarize_contacts(cx_c).outgoing_requests,
5754        &["user_b"]
5755    );
5756
5757    // User B rejects the request from user C.
5758    client_b
5759        .user_store()
5760        .update(cx_b, |store, cx| {
5761            store.respond_to_contact_request(client_c.user_id().unwrap(), false, cx)
5762        })
5763        .await
5764        .unwrap();
5765
5766    executor.run_until_parked();
5767
5768    // User B doesn't see user C as their contact, and the incoming request from them is removed.
5769    let contacts_b = client_b.summarize_contacts(cx_b);
5770    assert_eq!(contacts_b.current, &["user_a"]);
5771    assert!(contacts_b.incoming_requests.is_empty());
5772    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
5773    assert_eq!(contacts_b2.current, &["user_a"]);
5774    assert!(contacts_b2.incoming_requests.is_empty());
5775
5776    // User C doesn't see user B as their contact, and the outgoing request to them is removed.
5777    let contacts_c = client_c.summarize_contacts(cx_c);
5778    assert!(contacts_c.current.is_empty());
5779    assert!(contacts_c.outgoing_requests.is_empty());
5780    let contacts_c2 = client_c2.summarize_contacts(cx_c2);
5781    assert!(contacts_c2.current.is_empty());
5782    assert!(contacts_c2.outgoing_requests.is_empty());
5783
5784    // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect)
5785    disconnect_and_reconnect(&client_a, cx_a).await;
5786    disconnect_and_reconnect(&client_b, cx_b).await;
5787    disconnect_and_reconnect(&client_c, cx_c).await;
5788    executor.run_until_parked();
5789    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
5790    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
5791    assert!(client_b
5792        .summarize_contacts(cx_b)
5793        .incoming_requests
5794        .is_empty());
5795    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
5796    assert!(client_c
5797        .summarize_contacts(cx_c)
5798        .outgoing_requests
5799        .is_empty());
5800
5801    async fn disconnect_and_reconnect(client: &TestClient, cx: &mut TestAppContext) {
5802        client.disconnect(&cx.to_async());
5803        client.clear_contacts(cx).await;
5804        client
5805            .authenticate_and_connect(false, &cx.to_async())
5806            .await
5807            .unwrap();
5808    }
5809}
5810
5811#[gpui::test(iterations = 10)]
5812async fn test_join_call_after_screen_was_shared(
5813    executor: BackgroundExecutor,
5814    cx_a: &mut TestAppContext,
5815    cx_b: &mut TestAppContext,
5816) {
5817    let mut server = TestServer::start(executor.clone()).await;
5818
5819    let client_a = server.create_client(cx_a, "user_a").await;
5820    let client_b = server.create_client(cx_b, "user_b").await;
5821    server
5822        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5823        .await;
5824
5825    let active_call_a = cx_a.read(ActiveCall::global);
5826    let active_call_b = cx_b.read(ActiveCall::global);
5827
5828    // Call users B and C from client A.
5829    active_call_a
5830        .update(cx_a, |call, cx| {
5831            call.invite(client_b.user_id().unwrap(), None, cx)
5832        })
5833        .await
5834        .unwrap();
5835
5836    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
5837    executor.run_until_parked();
5838    assert_eq!(
5839        room_participants(&room_a, cx_a),
5840        RoomParticipants {
5841            remote: Default::default(),
5842            pending: vec!["user_b".to_string()]
5843        }
5844    );
5845
5846    // User B receives the call.
5847
5848    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
5849    let call_b = incoming_call_b.next().await.unwrap().unwrap();
5850    assert_eq!(call_b.calling_user.github_login, "user_a");
5851
5852    // User A shares their screen
5853    let display = MacOSDisplay::new();
5854    active_call_a
5855        .update(cx_a, |call, cx| {
5856            call.room().unwrap().update(cx, |room, cx| {
5857                room.set_display_sources(vec![display.clone()]);
5858                room.share_screen(cx)
5859            })
5860        })
5861        .await
5862        .unwrap();
5863
5864    client_b.user_store().update(cx_b, |user_store, _| {
5865        user_store.clear_cache();
5866    });
5867
5868    // User B joins the room
5869    active_call_b
5870        .update(cx_b, |call, cx| call.accept_incoming(cx))
5871        .await
5872        .unwrap();
5873
5874    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
5875    assert!(incoming_call_b.next().await.unwrap().is_none());
5876
5877    executor.run_until_parked();
5878    assert_eq!(
5879        room_participants(&room_a, cx_a),
5880        RoomParticipants {
5881            remote: vec!["user_b".to_string()],
5882            pending: vec![],
5883        }
5884    );
5885    assert_eq!(
5886        room_participants(&room_b, cx_b),
5887        RoomParticipants {
5888            remote: vec!["user_a".to_string()],
5889            pending: vec![],
5890        }
5891    );
5892
5893    // Ensure User B sees User A's screenshare.
5894
5895    room_b.read_with(cx_b, |room, _| {
5896        assert_eq!(
5897            room.remote_participants()
5898                .get(&client_a.user_id().unwrap())
5899                .unwrap()
5900                .video_tracks
5901                .len(),
5902            1
5903        );
5904    });
5905}