integration_tests.rs

   1use crate::{
   2    rpc::{CLEANUP_TIMEOUT, RECONNECT_TIMEOUT},
   3    tests::{TestClient, TestServer},
   4};
   5use call::{room, ActiveCall, ParticipantLocation, Room};
   6use client::{User, RECEIVE_TIMEOUT};
   7use collections::HashSet;
   8use editor::{
   9    test::editor_test_context::EditorTestContext, ConfirmCodeAction, ConfirmCompletion,
  10    ConfirmRename, Editor, ExcerptRange, MultiBuffer, Redo, Rename, ToOffset, ToggleCodeActions,
  11    Undo,
  12};
  13use fs::{FakeFs, Fs as _, LineEnding, RemoveOptions};
  14use futures::StreamExt as _;
  15use gpui::{
  16    executor::Deterministic, geometry::vector::vec2f, test::EmptyView, AppContext, ModelHandle,
  17    TestAppContext, ViewHandle,
  18};
  19use indoc::indoc;
  20use language::{
  21    language_settings::{AllLanguageSettings, Formatter},
  22    tree_sitter_rust, Anchor, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language,
  23    LanguageConfig, OffsetRangeExt, Point, Rope,
  24};
  25use live_kit_client::MacOSDisplay;
  26use lsp::LanguageServerId;
  27use project::{search::SearchQuery, DiagnosticSummary, HoverBlockKind, Project, ProjectPath};
  28use rand::prelude::*;
  29use serde_json::json;
  30use settings::SettingsStore;
  31use std::{
  32    cell::{Cell, RefCell},
  33    env, future, mem,
  34    path::{Path, PathBuf},
  35    rc::Rc,
  36    sync::{
  37        atomic::{AtomicBool, Ordering::SeqCst},
  38        Arc,
  39    },
  40};
  41use unindent::Unindent as _;
  42use workspace::{item::ItemHandle as _, shared_screen::SharedScreen, SplitDirection, Workspace};
  43
  44#[ctor::ctor]
  45fn init_logger() {
  46    if std::env::var("RUST_LOG").is_ok() {
  47        env_logger::init();
  48    }
  49}
  50
  51#[gpui::test(iterations = 10)]
  52async fn test_basic_calls(
  53    deterministic: Arc<Deterministic>,
  54    cx_a: &mut TestAppContext,
  55    cx_b: &mut TestAppContext,
  56    cx_b2: &mut TestAppContext,
  57    cx_c: &mut TestAppContext,
  58) {
  59    deterministic.forbid_parking();
  60    let mut server = TestServer::start(&deterministic).await;
  61
  62    let client_a = server.create_client(cx_a, "user_a").await;
  63    let client_b = server.create_client(cx_b, "user_b").await;
  64    let client_c = server.create_client(cx_c, "user_c").await;
  65    server
  66        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
  67        .await;
  68
  69    let active_call_a = cx_a.read(ActiveCall::global);
  70    let active_call_b = cx_b.read(ActiveCall::global);
  71    let active_call_c = cx_c.read(ActiveCall::global);
  72
  73    // Call user B from client A.
  74    active_call_a
  75        .update(cx_a, |call, cx| {
  76            call.invite(client_b.user_id().unwrap(), None, cx)
  77        })
  78        .await
  79        .unwrap();
  80    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
  81    deterministic.run_until_parked();
  82    assert_eq!(
  83        room_participants(&room_a, cx_a),
  84        RoomParticipants {
  85            remote: Default::default(),
  86            pending: vec!["user_b".to_string()]
  87        }
  88    );
  89
  90    // User B receives the call.
  91    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
  92    let call_b = incoming_call_b.next().await.unwrap().unwrap();
  93    assert_eq!(call_b.calling_user.github_login, "user_a");
  94
  95    // User B connects via another client and also receives a ring on the newly-connected client.
  96    let _client_b2 = server.create_client(cx_b2, "user_b").await;
  97    let active_call_b2 = cx_b2.read(ActiveCall::global);
  98    let mut incoming_call_b2 = active_call_b2.read_with(cx_b2, |call, _| call.incoming());
  99    deterministic.run_until_parked();
 100    let call_b2 = incoming_call_b2.next().await.unwrap().unwrap();
 101    assert_eq!(call_b2.calling_user.github_login, "user_a");
 102
 103    // User B joins the room using the first client.
 104    active_call_b
 105        .update(cx_b, |call, cx| call.accept_incoming(cx))
 106        .await
 107        .unwrap();
 108    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 109    assert!(incoming_call_b.next().await.unwrap().is_none());
 110
 111    deterministic.run_until_parked();
 112    assert_eq!(
 113        room_participants(&room_a, cx_a),
 114        RoomParticipants {
 115            remote: vec!["user_b".to_string()],
 116            pending: Default::default()
 117        }
 118    );
 119    assert_eq!(
 120        room_participants(&room_b, cx_b),
 121        RoomParticipants {
 122            remote: vec!["user_a".to_string()],
 123            pending: Default::default()
 124        }
 125    );
 126
 127    // Call user C from client B.
 128    let mut incoming_call_c = active_call_c.read_with(cx_c, |call, _| call.incoming());
 129    active_call_b
 130        .update(cx_b, |call, cx| {
 131            call.invite(client_c.user_id().unwrap(), None, cx)
 132        })
 133        .await
 134        .unwrap();
 135
 136    deterministic.run_until_parked();
 137    assert_eq!(
 138        room_participants(&room_a, cx_a),
 139        RoomParticipants {
 140            remote: vec!["user_b".to_string()],
 141            pending: vec!["user_c".to_string()]
 142        }
 143    );
 144    assert_eq!(
 145        room_participants(&room_b, cx_b),
 146        RoomParticipants {
 147            remote: vec!["user_a".to_string()],
 148            pending: vec!["user_c".to_string()]
 149        }
 150    );
 151
 152    // User C receives the call, but declines it.
 153    let call_c = incoming_call_c.next().await.unwrap().unwrap();
 154    assert_eq!(call_c.calling_user.github_login, "user_b");
 155    active_call_c.update(cx_c, |call, _| call.decline_incoming().unwrap());
 156    assert!(incoming_call_c.next().await.unwrap().is_none());
 157
 158    deterministic.run_until_parked();
 159    assert_eq!(
 160        room_participants(&room_a, cx_a),
 161        RoomParticipants {
 162            remote: vec!["user_b".to_string()],
 163            pending: Default::default()
 164        }
 165    );
 166    assert_eq!(
 167        room_participants(&room_b, cx_b),
 168        RoomParticipants {
 169            remote: vec!["user_a".to_string()],
 170            pending: Default::default()
 171        }
 172    );
 173
 174    // Call user C again from user A.
 175    active_call_a
 176        .update(cx_a, |call, cx| {
 177            call.invite(client_c.user_id().unwrap(), None, cx)
 178        })
 179        .await
 180        .unwrap();
 181
 182    deterministic.run_until_parked();
 183    assert_eq!(
 184        room_participants(&room_a, cx_a),
 185        RoomParticipants {
 186            remote: vec!["user_b".to_string()],
 187            pending: vec!["user_c".to_string()]
 188        }
 189    );
 190    assert_eq!(
 191        room_participants(&room_b, cx_b),
 192        RoomParticipants {
 193            remote: vec!["user_a".to_string()],
 194            pending: vec!["user_c".to_string()]
 195        }
 196    );
 197
 198    // User C accepts the call.
 199    let call_c = incoming_call_c.next().await.unwrap().unwrap();
 200    assert_eq!(call_c.calling_user.github_login, "user_a");
 201    active_call_c
 202        .update(cx_c, |call, cx| call.accept_incoming(cx))
 203        .await
 204        .unwrap();
 205    assert!(incoming_call_c.next().await.unwrap().is_none());
 206    let room_c = active_call_c.read_with(cx_c, |call, _| call.room().unwrap().clone());
 207
 208    deterministic.run_until_parked();
 209    assert_eq!(
 210        room_participants(&room_a, cx_a),
 211        RoomParticipants {
 212            remote: vec!["user_b".to_string(), "user_c".to_string()],
 213            pending: Default::default()
 214        }
 215    );
 216    assert_eq!(
 217        room_participants(&room_b, cx_b),
 218        RoomParticipants {
 219            remote: vec!["user_a".to_string(), "user_c".to_string()],
 220            pending: Default::default()
 221        }
 222    );
 223    assert_eq!(
 224        room_participants(&room_c, cx_c),
 225        RoomParticipants {
 226            remote: vec!["user_a".to_string(), "user_b".to_string()],
 227            pending: Default::default()
 228        }
 229    );
 230
 231    // User A shares their screen
 232    let display = MacOSDisplay::new();
 233    let events_b = active_call_events(cx_b);
 234    let events_c = active_call_events(cx_c);
 235    active_call_a
 236        .update(cx_a, |call, cx| {
 237            call.room().unwrap().update(cx, |room, cx| {
 238                room.set_display_sources(vec![display.clone()]);
 239                room.share_screen(cx)
 240            })
 241        })
 242        .await
 243        .unwrap();
 244
 245    deterministic.run_until_parked();
 246
 247    // User B observes the remote screen sharing track.
 248    assert_eq!(events_b.borrow().len(), 1);
 249    let event_b = events_b.borrow().first().unwrap().clone();
 250    if let call::room::Event::RemoteVideoTracksChanged { participant_id } = event_b {
 251        assert_eq!(participant_id, client_a.peer_id().unwrap());
 252        room_b.read_with(cx_b, |room, _| {
 253            assert_eq!(
 254                room.remote_participants()[&client_a.user_id().unwrap()]
 255                    .tracks
 256                    .len(),
 257                1
 258            );
 259        });
 260    } else {
 261        panic!("unexpected event")
 262    }
 263
 264    // User C observes the remote screen sharing track.
 265    assert_eq!(events_c.borrow().len(), 1);
 266    let event_c = events_c.borrow().first().unwrap().clone();
 267    if let call::room::Event::RemoteVideoTracksChanged { participant_id } = event_c {
 268        assert_eq!(participant_id, client_a.peer_id().unwrap());
 269        room_c.read_with(cx_c, |room, _| {
 270            assert_eq!(
 271                room.remote_participants()[&client_a.user_id().unwrap()]
 272                    .tracks
 273                    .len(),
 274                1
 275            );
 276        });
 277    } else {
 278        panic!("unexpected event")
 279    }
 280
 281    // User A leaves the room.
 282    active_call_a
 283        .update(cx_a, |call, cx| {
 284            let hang_up = call.hang_up(cx);
 285            assert!(call.room().is_none());
 286            hang_up
 287        })
 288        .await
 289        .unwrap();
 290    deterministic.run_until_parked();
 291    assert_eq!(
 292        room_participants(&room_a, cx_a),
 293        RoomParticipants {
 294            remote: Default::default(),
 295            pending: Default::default()
 296        }
 297    );
 298    assert_eq!(
 299        room_participants(&room_b, cx_b),
 300        RoomParticipants {
 301            remote: vec!["user_c".to_string()],
 302            pending: Default::default()
 303        }
 304    );
 305    assert_eq!(
 306        room_participants(&room_c, cx_c),
 307        RoomParticipants {
 308            remote: vec!["user_b".to_string()],
 309            pending: Default::default()
 310        }
 311    );
 312
 313    // User B gets disconnected from the LiveKit server, which causes them
 314    // to automatically leave the room. User C leaves the room as well because
 315    // nobody else is in there.
 316    server
 317        .test_live_kit_server
 318        .disconnect_client(client_b.user_id().unwrap().to_string())
 319        .await;
 320    deterministic.run_until_parked();
 321    active_call_b.read_with(cx_b, |call, _| assert!(call.room().is_none()));
 322    active_call_c.read_with(cx_c, |call, _| assert!(call.room().is_none()));
 323    assert_eq!(
 324        room_participants(&room_a, cx_a),
 325        RoomParticipants {
 326            remote: Default::default(),
 327            pending: Default::default()
 328        }
 329    );
 330    assert_eq!(
 331        room_participants(&room_b, cx_b),
 332        RoomParticipants {
 333            remote: Default::default(),
 334            pending: Default::default()
 335        }
 336    );
 337    assert_eq!(
 338        room_participants(&room_c, cx_c),
 339        RoomParticipants {
 340            remote: Default::default(),
 341            pending: Default::default()
 342        }
 343    );
 344}
 345
 346#[gpui::test(iterations = 10)]
 347async fn test_calling_multiple_users_simultaneously(
 348    deterministic: Arc<Deterministic>,
 349    cx_a: &mut TestAppContext,
 350    cx_b: &mut TestAppContext,
 351    cx_c: &mut TestAppContext,
 352    cx_d: &mut TestAppContext,
 353) {
 354    deterministic.forbid_parking();
 355    let mut server = TestServer::start(&deterministic).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    deterministic.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    deterministic.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    deterministic.run_until_parked();
 424
 425    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 426    let room_c = active_call_c.read_with(cx_c, |call, _| call.room().unwrap().clone());
 427    let room_d = active_call_d.read_with(cx_d, |call, _| call.room().unwrap().clone());
 428    assert_eq!(
 429        room_participants(&room_a, cx_a),
 430        RoomParticipants {
 431            remote: vec![
 432                "user_b".to_string(),
 433                "user_c".to_string(),
 434                "user_d".to_string(),
 435            ],
 436            pending: Default::default()
 437        }
 438    );
 439    assert_eq!(
 440        room_participants(&room_b, cx_b),
 441        RoomParticipants {
 442            remote: vec![
 443                "user_a".to_string(),
 444                "user_c".to_string(),
 445                "user_d".to_string(),
 446            ],
 447            pending: Default::default()
 448        }
 449    );
 450    assert_eq!(
 451        room_participants(&room_c, cx_c),
 452        RoomParticipants {
 453            remote: vec![
 454                "user_a".to_string(),
 455                "user_b".to_string(),
 456                "user_d".to_string(),
 457            ],
 458            pending: Default::default()
 459        }
 460    );
 461    assert_eq!(
 462        room_participants(&room_d, cx_d),
 463        RoomParticipants {
 464            remote: vec![
 465                "user_a".to_string(),
 466                "user_b".to_string(),
 467                "user_c".to_string(),
 468            ],
 469            pending: Default::default()
 470        }
 471    );
 472}
 473
 474#[gpui::test(iterations = 10)]
 475async fn test_room_uniqueness(
 476    deterministic: Arc<Deterministic>,
 477    cx_a: &mut TestAppContext,
 478    cx_a2: &mut TestAppContext,
 479    cx_b: &mut TestAppContext,
 480    cx_b2: &mut TestAppContext,
 481    cx_c: &mut TestAppContext,
 482) {
 483    deterministic.forbid_parking();
 484    let mut server = TestServer::start(&deterministic).await;
 485    let client_a = server.create_client(cx_a, "user_a").await;
 486    let _client_a2 = server.create_client(cx_a2, "user_a").await;
 487    let client_b = server.create_client(cx_b, "user_b").await;
 488    let _client_b2 = server.create_client(cx_b2, "user_b").await;
 489    let client_c = server.create_client(cx_c, "user_c").await;
 490    server
 491        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
 492        .await;
 493
 494    let active_call_a = cx_a.read(ActiveCall::global);
 495    let active_call_a2 = cx_a2.read(ActiveCall::global);
 496    let active_call_b = cx_b.read(ActiveCall::global);
 497    let active_call_b2 = cx_b2.read(ActiveCall::global);
 498    let active_call_c = cx_c.read(ActiveCall::global);
 499
 500    // Call user B from client A.
 501    active_call_a
 502        .update(cx_a, |call, cx| {
 503            call.invite(client_b.user_id().unwrap(), None, cx)
 504        })
 505        .await
 506        .unwrap();
 507
 508    // Ensure a new room can't be created given user A just created one.
 509    active_call_a2
 510        .update(cx_a2, |call, cx| {
 511            call.invite(client_c.user_id().unwrap(), None, cx)
 512        })
 513        .await
 514        .unwrap_err();
 515    active_call_a2.read_with(cx_a2, |call, _| assert!(call.room().is_none()));
 516
 517    // User B receives the call from user A.
 518    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
 519    let call_b1 = incoming_call_b.next().await.unwrap().unwrap();
 520    assert_eq!(call_b1.calling_user.github_login, "user_a");
 521
 522    // Ensure calling users A and B from client C fails.
 523    active_call_c
 524        .update(cx_c, |call, cx| {
 525            call.invite(client_a.user_id().unwrap(), None, cx)
 526        })
 527        .await
 528        .unwrap_err();
 529    active_call_c
 530        .update(cx_c, |call, cx| {
 531            call.invite(client_b.user_id().unwrap(), None, cx)
 532        })
 533        .await
 534        .unwrap_err();
 535
 536    // Ensure User B can't create a room while they still have an incoming call.
 537    active_call_b2
 538        .update(cx_b2, |call, cx| {
 539            call.invite(client_c.user_id().unwrap(), None, cx)
 540        })
 541        .await
 542        .unwrap_err();
 543    active_call_b2.read_with(cx_b2, |call, _| assert!(call.room().is_none()));
 544
 545    // User B joins the room and calling them after they've joined still fails.
 546    active_call_b
 547        .update(cx_b, |call, cx| call.accept_incoming(cx))
 548        .await
 549        .unwrap();
 550    active_call_c
 551        .update(cx_c, |call, cx| {
 552            call.invite(client_b.user_id().unwrap(), None, cx)
 553        })
 554        .await
 555        .unwrap_err();
 556
 557    // Ensure User B can't create a room while they belong to another room.
 558    active_call_b2
 559        .update(cx_b2, |call, cx| {
 560            call.invite(client_c.user_id().unwrap(), None, cx)
 561        })
 562        .await
 563        .unwrap_err();
 564    active_call_b2.read_with(cx_b2, |call, _| assert!(call.room().is_none()));
 565
 566    // Client C can successfully call client B after client B leaves the room.
 567    active_call_b
 568        .update(cx_b, |call, cx| call.hang_up(cx))
 569        .await
 570        .unwrap();
 571    deterministic.run_until_parked();
 572    active_call_c
 573        .update(cx_c, |call, cx| {
 574            call.invite(client_b.user_id().unwrap(), None, cx)
 575        })
 576        .await
 577        .unwrap();
 578    deterministic.run_until_parked();
 579    let call_b2 = incoming_call_b.next().await.unwrap().unwrap();
 580    assert_eq!(call_b2.calling_user.github_login, "user_c");
 581}
 582
 583#[gpui::test(iterations = 10)]
 584async fn test_client_disconnecting_from_room(
 585    deterministic: Arc<Deterministic>,
 586    cx_a: &mut TestAppContext,
 587    cx_b: &mut TestAppContext,
 588) {
 589    deterministic.forbid_parking();
 590    let mut server = TestServer::start(&deterministic).await;
 591    let client_a = server.create_client(cx_a, "user_a").await;
 592    let client_b = server.create_client(cx_b, "user_b").await;
 593    server
 594        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
 595        .await;
 596
 597    let active_call_a = cx_a.read(ActiveCall::global);
 598    let active_call_b = cx_b.read(ActiveCall::global);
 599
 600    // Call user B from client A.
 601    active_call_a
 602        .update(cx_a, |call, cx| {
 603            call.invite(client_b.user_id().unwrap(), None, cx)
 604        })
 605        .await
 606        .unwrap();
 607    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 608
 609    // User B receives the call and joins the room.
 610    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
 611    incoming_call_b.next().await.unwrap().unwrap();
 612    active_call_b
 613        .update(cx_b, |call, cx| call.accept_incoming(cx))
 614        .await
 615        .unwrap();
 616    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 617    deterministic.run_until_parked();
 618    assert_eq!(
 619        room_participants(&room_a, cx_a),
 620        RoomParticipants {
 621            remote: vec!["user_b".to_string()],
 622            pending: Default::default()
 623        }
 624    );
 625    assert_eq!(
 626        room_participants(&room_b, cx_b),
 627        RoomParticipants {
 628            remote: vec!["user_a".to_string()],
 629            pending: Default::default()
 630        }
 631    );
 632
 633    // User A automatically reconnects to the room upon disconnection.
 634    server.disconnect_client(client_a.peer_id().unwrap());
 635    deterministic.advance_clock(RECEIVE_TIMEOUT);
 636    deterministic.run_until_parked();
 637    assert_eq!(
 638        room_participants(&room_a, cx_a),
 639        RoomParticipants {
 640            remote: vec!["user_b".to_string()],
 641            pending: Default::default()
 642        }
 643    );
 644    assert_eq!(
 645        room_participants(&room_b, cx_b),
 646        RoomParticipants {
 647            remote: vec!["user_a".to_string()],
 648            pending: Default::default()
 649        }
 650    );
 651
 652    // When user A disconnects, both client A and B clear their room on the active call.
 653    server.forbid_connections();
 654    server.disconnect_client(client_a.peer_id().unwrap());
 655    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
 656    active_call_a.read_with(cx_a, |call, _| assert!(call.room().is_none()));
 657    active_call_b.read_with(cx_b, |call, _| assert!(call.room().is_none()));
 658    assert_eq!(
 659        room_participants(&room_a, cx_a),
 660        RoomParticipants {
 661            remote: Default::default(),
 662            pending: Default::default()
 663        }
 664    );
 665    assert_eq!(
 666        room_participants(&room_b, cx_b),
 667        RoomParticipants {
 668            remote: Default::default(),
 669            pending: Default::default()
 670        }
 671    );
 672
 673    // Allow user A to reconnect to the server.
 674    server.allow_connections();
 675    deterministic.advance_clock(RECEIVE_TIMEOUT);
 676
 677    // Call user B again from client A.
 678    active_call_a
 679        .update(cx_a, |call, cx| {
 680            call.invite(client_b.user_id().unwrap(), None, cx)
 681        })
 682        .await
 683        .unwrap();
 684    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 685
 686    // User B receives the call and joins the room.
 687    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
 688    incoming_call_b.next().await.unwrap().unwrap();
 689    active_call_b
 690        .update(cx_b, |call, cx| call.accept_incoming(cx))
 691        .await
 692        .unwrap();
 693    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 694    deterministic.run_until_parked();
 695    assert_eq!(
 696        room_participants(&room_a, cx_a),
 697        RoomParticipants {
 698            remote: vec!["user_b".to_string()],
 699            pending: Default::default()
 700        }
 701    );
 702    assert_eq!(
 703        room_participants(&room_b, cx_b),
 704        RoomParticipants {
 705            remote: vec!["user_a".to_string()],
 706            pending: Default::default()
 707        }
 708    );
 709
 710    // User B gets disconnected from the LiveKit server, which causes it
 711    // to automatically leave the room.
 712    server
 713        .test_live_kit_server
 714        .disconnect_client(client_b.user_id().unwrap().to_string())
 715        .await;
 716    deterministic.run_until_parked();
 717    active_call_a.update(cx_a, |call, _| assert!(call.room().is_none()));
 718    active_call_b.update(cx_b, |call, _| assert!(call.room().is_none()));
 719    assert_eq!(
 720        room_participants(&room_a, cx_a),
 721        RoomParticipants {
 722            remote: Default::default(),
 723            pending: Default::default()
 724        }
 725    );
 726    assert_eq!(
 727        room_participants(&room_b, cx_b),
 728        RoomParticipants {
 729            remote: Default::default(),
 730            pending: Default::default()
 731        }
 732    );
 733}
 734
 735#[gpui::test(iterations = 10)]
 736async fn test_server_restarts(
 737    deterministic: Arc<Deterministic>,
 738    cx_a: &mut TestAppContext,
 739    cx_b: &mut TestAppContext,
 740    cx_c: &mut TestAppContext,
 741    cx_d: &mut TestAppContext,
 742) {
 743    deterministic.forbid_parking();
 744    let mut server = TestServer::start(&deterministic).await;
 745    let client_a = server.create_client(cx_a, "user_a").await;
 746    client_a
 747        .fs
 748        .insert_tree("/a", json!({ "a.txt": "a-contents" }))
 749        .await;
 750
 751    // Invite client B to collaborate on a project
 752    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
 753
 754    let client_b = server.create_client(cx_b, "user_b").await;
 755    let client_c = server.create_client(cx_c, "user_c").await;
 756    let client_d = server.create_client(cx_d, "user_d").await;
 757    server
 758        .make_contacts(&mut [
 759            (&client_a, cx_a),
 760            (&client_b, cx_b),
 761            (&client_c, cx_c),
 762            (&client_d, cx_d),
 763        ])
 764        .await;
 765
 766    let active_call_a = cx_a.read(ActiveCall::global);
 767    let active_call_b = cx_b.read(ActiveCall::global);
 768    let active_call_c = cx_c.read(ActiveCall::global);
 769    let active_call_d = cx_d.read(ActiveCall::global);
 770
 771    // User A calls users B, C, and D.
 772    active_call_a
 773        .update(cx_a, |call, cx| {
 774            call.invite(client_b.user_id().unwrap(), Some(project_a.clone()), cx)
 775        })
 776        .await
 777        .unwrap();
 778    active_call_a
 779        .update(cx_a, |call, cx| {
 780            call.invite(client_c.user_id().unwrap(), Some(project_a.clone()), cx)
 781        })
 782        .await
 783        .unwrap();
 784    active_call_a
 785        .update(cx_a, |call, cx| {
 786            call.invite(client_d.user_id().unwrap(), Some(project_a.clone()), cx)
 787        })
 788        .await
 789        .unwrap();
 790    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 791
 792    // User B receives the call and joins the room.
 793    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
 794    assert!(incoming_call_b.next().await.unwrap().is_some());
 795    active_call_b
 796        .update(cx_b, |call, cx| call.accept_incoming(cx))
 797        .await
 798        .unwrap();
 799    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 800
 801    // User C receives the call and joins the room.
 802    let mut incoming_call_c = active_call_c.read_with(cx_c, |call, _| call.incoming());
 803    assert!(incoming_call_c.next().await.unwrap().is_some());
 804    active_call_c
 805        .update(cx_c, |call, cx| call.accept_incoming(cx))
 806        .await
 807        .unwrap();
 808    let room_c = active_call_c.read_with(cx_c, |call, _| call.room().unwrap().clone());
 809
 810    // User D receives the call but doesn't join the room yet.
 811    let mut incoming_call_d = active_call_d.read_with(cx_d, |call, _| call.incoming());
 812    assert!(incoming_call_d.next().await.unwrap().is_some());
 813
 814    deterministic.run_until_parked();
 815    assert_eq!(
 816        room_participants(&room_a, cx_a),
 817        RoomParticipants {
 818            remote: vec!["user_b".to_string(), "user_c".to_string()],
 819            pending: vec!["user_d".to_string()]
 820        }
 821    );
 822    assert_eq!(
 823        room_participants(&room_b, cx_b),
 824        RoomParticipants {
 825            remote: vec!["user_a".to_string(), "user_c".to_string()],
 826            pending: vec!["user_d".to_string()]
 827        }
 828    );
 829    assert_eq!(
 830        room_participants(&room_c, cx_c),
 831        RoomParticipants {
 832            remote: vec!["user_a".to_string(), "user_b".to_string()],
 833            pending: vec!["user_d".to_string()]
 834        }
 835    );
 836
 837    // The server is torn down.
 838    server.reset().await;
 839
 840    // Users A and B reconnect to the call. User C has troubles reconnecting, so it leaves the room.
 841    client_c.override_establish_connection(|_, cx| cx.spawn(|_| future::pending()));
 842    deterministic.advance_clock(RECONNECT_TIMEOUT);
 843    assert_eq!(
 844        room_participants(&room_a, cx_a),
 845        RoomParticipants {
 846            remote: vec!["user_b".to_string(), "user_c".to_string()],
 847            pending: vec!["user_d".to_string()]
 848        }
 849    );
 850    assert_eq!(
 851        room_participants(&room_b, cx_b),
 852        RoomParticipants {
 853            remote: vec!["user_a".to_string(), "user_c".to_string()],
 854            pending: vec!["user_d".to_string()]
 855        }
 856    );
 857    assert_eq!(
 858        room_participants(&room_c, cx_c),
 859        RoomParticipants {
 860            remote: vec![],
 861            pending: vec![]
 862        }
 863    );
 864
 865    // User D is notified again of the incoming call and accepts it.
 866    assert!(incoming_call_d.next().await.unwrap().is_some());
 867    active_call_d
 868        .update(cx_d, |call, cx| call.accept_incoming(cx))
 869        .await
 870        .unwrap();
 871    deterministic.run_until_parked();
 872    let room_d = active_call_d.read_with(cx_d, |call, _| call.room().unwrap().clone());
 873    assert_eq!(
 874        room_participants(&room_a, cx_a),
 875        RoomParticipants {
 876            remote: vec![
 877                "user_b".to_string(),
 878                "user_c".to_string(),
 879                "user_d".to_string(),
 880            ],
 881            pending: vec![]
 882        }
 883    );
 884    assert_eq!(
 885        room_participants(&room_b, cx_b),
 886        RoomParticipants {
 887            remote: vec![
 888                "user_a".to_string(),
 889                "user_c".to_string(),
 890                "user_d".to_string(),
 891            ],
 892            pending: vec![]
 893        }
 894    );
 895    assert_eq!(
 896        room_participants(&room_c, cx_c),
 897        RoomParticipants {
 898            remote: vec![],
 899            pending: vec![]
 900        }
 901    );
 902    assert_eq!(
 903        room_participants(&room_d, cx_d),
 904        RoomParticipants {
 905            remote: vec![
 906                "user_a".to_string(),
 907                "user_b".to_string(),
 908                "user_c".to_string(),
 909            ],
 910            pending: vec![]
 911        }
 912    );
 913
 914    // The server finishes restarting, cleaning up stale connections.
 915    server.start().await.unwrap();
 916    deterministic.advance_clock(CLEANUP_TIMEOUT);
 917    assert_eq!(
 918        room_participants(&room_a, cx_a),
 919        RoomParticipants {
 920            remote: vec!["user_b".to_string(), "user_d".to_string()],
 921            pending: vec![]
 922        }
 923    );
 924    assert_eq!(
 925        room_participants(&room_b, cx_b),
 926        RoomParticipants {
 927            remote: vec!["user_a".to_string(), "user_d".to_string()],
 928            pending: vec![]
 929        }
 930    );
 931    assert_eq!(
 932        room_participants(&room_c, cx_c),
 933        RoomParticipants {
 934            remote: vec![],
 935            pending: vec![]
 936        }
 937    );
 938    assert_eq!(
 939        room_participants(&room_d, cx_d),
 940        RoomParticipants {
 941            remote: vec!["user_a".to_string(), "user_b".to_string()],
 942            pending: vec![]
 943        }
 944    );
 945
 946    // User D hangs up.
 947    active_call_d
 948        .update(cx_d, |call, cx| call.hang_up(cx))
 949        .await
 950        .unwrap();
 951    deterministic.run_until_parked();
 952    assert_eq!(
 953        room_participants(&room_a, cx_a),
 954        RoomParticipants {
 955            remote: vec!["user_b".to_string()],
 956            pending: vec![]
 957        }
 958    );
 959    assert_eq!(
 960        room_participants(&room_b, cx_b),
 961        RoomParticipants {
 962            remote: vec!["user_a".to_string()],
 963            pending: vec![]
 964        }
 965    );
 966    assert_eq!(
 967        room_participants(&room_c, cx_c),
 968        RoomParticipants {
 969            remote: vec![],
 970            pending: vec![]
 971        }
 972    );
 973    assert_eq!(
 974        room_participants(&room_d, cx_d),
 975        RoomParticipants {
 976            remote: vec![],
 977            pending: vec![]
 978        }
 979    );
 980
 981    // User B calls user D again.
 982    active_call_b
 983        .update(cx_b, |call, cx| {
 984            call.invite(client_d.user_id().unwrap(), None, cx)
 985        })
 986        .await
 987        .unwrap();
 988
 989    // User D receives the call but doesn't join the room yet.
 990    let mut incoming_call_d = active_call_d.read_with(cx_d, |call, _| call.incoming());
 991    assert!(incoming_call_d.next().await.unwrap().is_some());
 992    deterministic.run_until_parked();
 993    assert_eq!(
 994        room_participants(&room_a, cx_a),
 995        RoomParticipants {
 996            remote: vec!["user_b".to_string()],
 997            pending: vec!["user_d".to_string()]
 998        }
 999    );
1000    assert_eq!(
1001        room_participants(&room_b, cx_b),
1002        RoomParticipants {
1003            remote: vec!["user_a".to_string()],
1004            pending: vec!["user_d".to_string()]
1005        }
1006    );
1007
1008    // The server is torn down.
1009    server.reset().await;
1010
1011    // Users A and B have troubles reconnecting, so they leave the room.
1012    client_a.override_establish_connection(|_, cx| cx.spawn(|_| future::pending()));
1013    client_b.override_establish_connection(|_, cx| cx.spawn(|_| future::pending()));
1014    client_c.override_establish_connection(|_, cx| cx.spawn(|_| future::pending()));
1015    deterministic.advance_clock(RECONNECT_TIMEOUT);
1016    assert_eq!(
1017        room_participants(&room_a, cx_a),
1018        RoomParticipants {
1019            remote: vec![],
1020            pending: vec![]
1021        }
1022    );
1023    assert_eq!(
1024        room_participants(&room_b, cx_b),
1025        RoomParticipants {
1026            remote: vec![],
1027            pending: vec![]
1028        }
1029    );
1030
1031    // User D is notified again of the incoming call but doesn't accept it.
1032    assert!(incoming_call_d.next().await.unwrap().is_some());
1033
1034    // The server finishes restarting, cleaning up stale connections and canceling the
1035    // call to user D because the room has become empty.
1036    server.start().await.unwrap();
1037    deterministic.advance_clock(CLEANUP_TIMEOUT);
1038    assert!(incoming_call_d.next().await.unwrap().is_none());
1039}
1040
1041#[gpui::test(iterations = 10)]
1042async fn test_calls_on_multiple_connections(
1043    deterministic: Arc<Deterministic>,
1044    cx_a: &mut TestAppContext,
1045    cx_b1: &mut TestAppContext,
1046    cx_b2: &mut TestAppContext,
1047) {
1048    deterministic.forbid_parking();
1049    let mut server = TestServer::start(&deterministic).await;
1050    let client_a = server.create_client(cx_a, "user_a").await;
1051    let client_b1 = server.create_client(cx_b1, "user_b").await;
1052    let client_b2 = server.create_client(cx_b2, "user_b").await;
1053    server
1054        .make_contacts(&mut [(&client_a, cx_a), (&client_b1, cx_b1)])
1055        .await;
1056
1057    let active_call_a = cx_a.read(ActiveCall::global);
1058    let active_call_b1 = cx_b1.read(ActiveCall::global);
1059    let active_call_b2 = cx_b2.read(ActiveCall::global);
1060    let mut incoming_call_b1 = active_call_b1.read_with(cx_b1, |call, _| call.incoming());
1061    let mut incoming_call_b2 = active_call_b2.read_with(cx_b2, |call, _| call.incoming());
1062    assert!(incoming_call_b1.next().await.unwrap().is_none());
1063    assert!(incoming_call_b2.next().await.unwrap().is_none());
1064
1065    // Call user B from client A, ensuring both clients for user B ring.
1066    active_call_a
1067        .update(cx_a, |call, cx| {
1068            call.invite(client_b1.user_id().unwrap(), None, cx)
1069        })
1070        .await
1071        .unwrap();
1072    deterministic.run_until_parked();
1073    assert!(incoming_call_b1.next().await.unwrap().is_some());
1074    assert!(incoming_call_b2.next().await.unwrap().is_some());
1075
1076    // User B declines the call on one of the two connections, causing both connections
1077    // to stop ringing.
1078    active_call_b2.update(cx_b2, |call, _| call.decline_incoming().unwrap());
1079    deterministic.run_until_parked();
1080    assert!(incoming_call_b1.next().await.unwrap().is_none());
1081    assert!(incoming_call_b2.next().await.unwrap().is_none());
1082
1083    // Call user B again from client A.
1084    active_call_a
1085        .update(cx_a, |call, cx| {
1086            call.invite(client_b1.user_id().unwrap(), None, cx)
1087        })
1088        .await
1089        .unwrap();
1090    deterministic.run_until_parked();
1091    assert!(incoming_call_b1.next().await.unwrap().is_some());
1092    assert!(incoming_call_b2.next().await.unwrap().is_some());
1093
1094    // User B accepts the call on one of the two connections, causing both connections
1095    // to stop ringing.
1096    active_call_b2
1097        .update(cx_b2, |call, cx| call.accept_incoming(cx))
1098        .await
1099        .unwrap();
1100    deterministic.run_until_parked();
1101    assert!(incoming_call_b1.next().await.unwrap().is_none());
1102    assert!(incoming_call_b2.next().await.unwrap().is_none());
1103
1104    // User B disconnects the client that is not on the call. Everything should be fine.
1105    client_b1.disconnect(&cx_b1.to_async());
1106    deterministic.advance_clock(RECEIVE_TIMEOUT);
1107    client_b1
1108        .authenticate_and_connect(false, &cx_b1.to_async())
1109        .await
1110        .unwrap();
1111
1112    // User B hangs up, and user A calls them again.
1113    active_call_b2
1114        .update(cx_b2, |call, cx| call.hang_up(cx))
1115        .await
1116        .unwrap();
1117    deterministic.run_until_parked();
1118    active_call_a
1119        .update(cx_a, |call, cx| {
1120            call.invite(client_b1.user_id().unwrap(), None, cx)
1121        })
1122        .await
1123        .unwrap();
1124    deterministic.run_until_parked();
1125    assert!(incoming_call_b1.next().await.unwrap().is_some());
1126    assert!(incoming_call_b2.next().await.unwrap().is_some());
1127
1128    // User A cancels the call, causing both connections to stop ringing.
1129    active_call_a
1130        .update(cx_a, |call, cx| {
1131            call.cancel_invite(client_b1.user_id().unwrap(), cx)
1132        })
1133        .await
1134        .unwrap();
1135    deterministic.run_until_parked();
1136    assert!(incoming_call_b1.next().await.unwrap().is_none());
1137    assert!(incoming_call_b2.next().await.unwrap().is_none());
1138
1139    // User A calls user B again.
1140    active_call_a
1141        .update(cx_a, |call, cx| {
1142            call.invite(client_b1.user_id().unwrap(), None, cx)
1143        })
1144        .await
1145        .unwrap();
1146    deterministic.run_until_parked();
1147    assert!(incoming_call_b1.next().await.unwrap().is_some());
1148    assert!(incoming_call_b2.next().await.unwrap().is_some());
1149
1150    // User A hangs up, causing both connections to stop ringing.
1151    active_call_a
1152        .update(cx_a, |call, cx| call.hang_up(cx))
1153        .await
1154        .unwrap();
1155    deterministic.run_until_parked();
1156    assert!(incoming_call_b1.next().await.unwrap().is_none());
1157    assert!(incoming_call_b2.next().await.unwrap().is_none());
1158
1159    // User A calls user B again.
1160    active_call_a
1161        .update(cx_a, |call, cx| {
1162            call.invite(client_b1.user_id().unwrap(), None, cx)
1163        })
1164        .await
1165        .unwrap();
1166    deterministic.run_until_parked();
1167    assert!(incoming_call_b1.next().await.unwrap().is_some());
1168    assert!(incoming_call_b2.next().await.unwrap().is_some());
1169
1170    // User A disconnects, causing both connections to stop ringing.
1171    server.forbid_connections();
1172    server.disconnect_client(client_a.peer_id().unwrap());
1173    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
1174    assert!(incoming_call_b1.next().await.unwrap().is_none());
1175    assert!(incoming_call_b2.next().await.unwrap().is_none());
1176
1177    // User A reconnects automatically, then calls user B again.
1178    server.allow_connections();
1179    deterministic.advance_clock(RECEIVE_TIMEOUT);
1180    active_call_a
1181        .update(cx_a, |call, cx| {
1182            call.invite(client_b1.user_id().unwrap(), None, cx)
1183        })
1184        .await
1185        .unwrap();
1186    deterministic.run_until_parked();
1187    assert!(incoming_call_b1.next().await.unwrap().is_some());
1188    assert!(incoming_call_b2.next().await.unwrap().is_some());
1189
1190    // User B disconnects all clients, causing user A to no longer see a pending call for them.
1191    server.forbid_connections();
1192    server.disconnect_client(client_b1.peer_id().unwrap());
1193    server.disconnect_client(client_b2.peer_id().unwrap());
1194    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
1195    active_call_a.read_with(cx_a, |call, _| assert!(call.room().is_none()));
1196}
1197
1198#[gpui::test(iterations = 10)]
1199async fn test_share_project(
1200    deterministic: Arc<Deterministic>,
1201    cx_a: &mut TestAppContext,
1202    cx_b: &mut TestAppContext,
1203    cx_c: &mut TestAppContext,
1204) {
1205    deterministic.forbid_parking();
1206    let (window_b, _) = cx_b.add_window(|_| EmptyView);
1207    let mut server = TestServer::start(&deterministic).await;
1208    let client_a = server.create_client(cx_a, "user_a").await;
1209    let client_b = server.create_client(cx_b, "user_b").await;
1210    let client_c = server.create_client(cx_c, "user_c").await;
1211    server
1212        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
1213        .await;
1214    let active_call_a = cx_a.read(ActiveCall::global);
1215    let active_call_b = cx_b.read(ActiveCall::global);
1216    let active_call_c = cx_c.read(ActiveCall::global);
1217
1218    client_a
1219        .fs
1220        .insert_tree(
1221            "/a",
1222            json!({
1223                ".gitignore": "ignored-dir",
1224                "a.txt": "a-contents",
1225                "b.txt": "b-contents",
1226                "ignored-dir": {
1227                    "c.txt": "",
1228                    "d.txt": "",
1229                }
1230            }),
1231        )
1232        .await;
1233
1234    // Invite client B to collaborate on a project
1235    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
1236    active_call_a
1237        .update(cx_a, |call, cx| {
1238            call.invite(client_b.user_id().unwrap(), Some(project_a.clone()), cx)
1239        })
1240        .await
1241        .unwrap();
1242
1243    // Join that project as client B
1244    let incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
1245    deterministic.run_until_parked();
1246    let call = incoming_call_b.borrow().clone().unwrap();
1247    assert_eq!(call.calling_user.github_login, "user_a");
1248    let initial_project = call.initial_project.unwrap();
1249    active_call_b
1250        .update(cx_b, |call, cx| call.accept_incoming(cx))
1251        .await
1252        .unwrap();
1253    let client_b_peer_id = client_b.peer_id().unwrap();
1254    let project_b = client_b
1255        .build_remote_project(initial_project.id, cx_b)
1256        .await;
1257    let replica_id_b = project_b.read_with(cx_b, |project, _| project.replica_id());
1258
1259    deterministic.run_until_parked();
1260    project_a.read_with(cx_a, |project, _| {
1261        let client_b_collaborator = project.collaborators().get(&client_b_peer_id).unwrap();
1262        assert_eq!(client_b_collaborator.replica_id, replica_id_b);
1263    });
1264    project_b.read_with(cx_b, |project, cx| {
1265        let worktree = project.worktrees(cx).next().unwrap().read(cx);
1266        assert_eq!(
1267            worktree.paths().map(AsRef::as_ref).collect::<Vec<_>>(),
1268            [
1269                Path::new(".gitignore"),
1270                Path::new("a.txt"),
1271                Path::new("b.txt"),
1272                Path::new("ignored-dir"),
1273                Path::new("ignored-dir/c.txt"),
1274                Path::new("ignored-dir/d.txt"),
1275            ]
1276        );
1277    });
1278
1279    // Open the same file as client B and client A.
1280    let buffer_b = project_b
1281        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.txt"), cx))
1282        .await
1283        .unwrap();
1284    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "b-contents"));
1285    project_a.read_with(cx_a, |project, cx| {
1286        assert!(project.has_open_buffer((worktree_id, "b.txt"), cx))
1287    });
1288    let buffer_a = project_a
1289        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "b.txt"), cx))
1290        .await
1291        .unwrap();
1292
1293    let editor_b = cx_b.add_view(window_b, |cx| Editor::for_buffer(buffer_b, None, cx));
1294
1295    // Client A sees client B's selection
1296    deterministic.run_until_parked();
1297    buffer_a.read_with(cx_a, |buffer, _| {
1298        buffer
1299            .snapshot()
1300            .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
1301            .count()
1302            == 1
1303    });
1304
1305    // Edit the buffer as client B and see that edit as client A.
1306    editor_b.update(cx_b, |editor, cx| editor.handle_input("ok, ", cx));
1307    deterministic.run_until_parked();
1308    buffer_a.read_with(cx_a, |buffer, _| {
1309        assert_eq!(buffer.text(), "ok, b-contents")
1310    });
1311
1312    // Client B can invite client C on a project shared by client A.
1313    active_call_b
1314        .update(cx_b, |call, cx| {
1315            call.invite(client_c.user_id().unwrap(), Some(project_b.clone()), cx)
1316        })
1317        .await
1318        .unwrap();
1319
1320    let incoming_call_c = active_call_c.read_with(cx_c, |call, _| call.incoming());
1321    deterministic.run_until_parked();
1322    let call = incoming_call_c.borrow().clone().unwrap();
1323    assert_eq!(call.calling_user.github_login, "user_b");
1324    let initial_project = call.initial_project.unwrap();
1325    active_call_c
1326        .update(cx_c, |call, cx| call.accept_incoming(cx))
1327        .await
1328        .unwrap();
1329    let _project_c = client_c
1330        .build_remote_project(initial_project.id, cx_c)
1331        .await;
1332
1333    // Client B closes the editor, and client A sees client B's selections removed.
1334    cx_b.update(move |_| drop(editor_b));
1335    deterministic.run_until_parked();
1336    buffer_a.read_with(cx_a, |buffer, _| {
1337        buffer
1338            .snapshot()
1339            .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
1340            .count()
1341            == 0
1342    });
1343}
1344
1345#[gpui::test(iterations = 10)]
1346async fn test_unshare_project(
1347    deterministic: Arc<Deterministic>,
1348    cx_a: &mut TestAppContext,
1349    cx_b: &mut TestAppContext,
1350    cx_c: &mut TestAppContext,
1351) {
1352    deterministic.forbid_parking();
1353    let mut server = TestServer::start(&deterministic).await;
1354    let client_a = server.create_client(cx_a, "user_a").await;
1355    let client_b = server.create_client(cx_b, "user_b").await;
1356    let client_c = server.create_client(cx_c, "user_c").await;
1357    server
1358        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
1359        .await;
1360
1361    let active_call_a = cx_a.read(ActiveCall::global);
1362    let active_call_b = cx_b.read(ActiveCall::global);
1363
1364    client_a
1365        .fs
1366        .insert_tree(
1367            "/a",
1368            json!({
1369                "a.txt": "a-contents",
1370                "b.txt": "b-contents",
1371            }),
1372        )
1373        .await;
1374
1375    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
1376    let project_id = active_call_a
1377        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
1378        .await
1379        .unwrap();
1380    let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
1381    let project_b = client_b.build_remote_project(project_id, cx_b).await;
1382    deterministic.run_until_parked();
1383    assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
1384
1385    project_b
1386        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
1387        .await
1388        .unwrap();
1389
1390    // When client B leaves the room, the project becomes read-only.
1391    active_call_b
1392        .update(cx_b, |call, cx| call.hang_up(cx))
1393        .await
1394        .unwrap();
1395    deterministic.run_until_parked();
1396    assert!(project_b.read_with(cx_b, |project, _| project.is_read_only()));
1397
1398    // Client C opens the project.
1399    let project_c = client_c.build_remote_project(project_id, cx_c).await;
1400
1401    // When client A unshares the project, client C's project becomes read-only.
1402    project_a
1403        .update(cx_a, |project, cx| project.unshare(cx))
1404        .unwrap();
1405    deterministic.run_until_parked();
1406    assert!(worktree_a.read_with(cx_a, |tree, _| !tree.as_local().unwrap().is_shared()));
1407    assert!(project_c.read_with(cx_c, |project, _| project.is_read_only()));
1408
1409    // Client C can open the project again after client A re-shares.
1410    let project_id = active_call_a
1411        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
1412        .await
1413        .unwrap();
1414    let project_c2 = client_c.build_remote_project(project_id, cx_c).await;
1415    deterministic.run_until_parked();
1416    assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
1417    project_c2
1418        .update(cx_c, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
1419        .await
1420        .unwrap();
1421
1422    // When client A (the host) leaves the room, the project gets unshared and guests are notified.
1423    active_call_a
1424        .update(cx_a, |call, cx| call.hang_up(cx))
1425        .await
1426        .unwrap();
1427    deterministic.run_until_parked();
1428    project_a.read_with(cx_a, |project, _| assert!(!project.is_shared()));
1429    project_c2.read_with(cx_c, |project, _| {
1430        assert!(project.is_read_only());
1431        assert!(project.collaborators().is_empty());
1432    });
1433}
1434
1435#[gpui::test(iterations = 10)]
1436async fn test_host_disconnect(
1437    deterministic: Arc<Deterministic>,
1438    cx_a: &mut TestAppContext,
1439    cx_b: &mut TestAppContext,
1440    cx_c: &mut TestAppContext,
1441) {
1442    deterministic.forbid_parking();
1443    let mut server = TestServer::start(&deterministic).await;
1444    let client_a = server.create_client(cx_a, "user_a").await;
1445    let client_b = server.create_client(cx_b, "user_b").await;
1446    let client_c = server.create_client(cx_c, "user_c").await;
1447    server
1448        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
1449        .await;
1450
1451    cx_b.update(editor::init);
1452
1453    client_a
1454        .fs
1455        .insert_tree(
1456            "/a",
1457            json!({
1458                "a.txt": "a-contents",
1459                "b.txt": "b-contents",
1460            }),
1461        )
1462        .await;
1463
1464    let active_call_a = cx_a.read(ActiveCall::global);
1465    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
1466    let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
1467    let project_id = active_call_a
1468        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
1469        .await
1470        .unwrap();
1471
1472    let project_b = client_b.build_remote_project(project_id, cx_b).await;
1473    deterministic.run_until_parked();
1474    assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
1475
1476    let (window_id_b, workspace_b) =
1477        cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx));
1478    let editor_b = workspace_b
1479        .update(cx_b, |workspace, cx| {
1480            workspace.open_path((worktree_id, "b.txt"), None, true, cx)
1481        })
1482        .await
1483        .unwrap()
1484        .downcast::<Editor>()
1485        .unwrap();
1486    assert!(cx_b
1487        .read_window(window_id_b, |cx| editor_b.is_focused(cx))
1488        .unwrap());
1489    editor_b.update(cx_b, |editor, cx| editor.insert("X", cx));
1490    assert!(cx_b.is_window_edited(workspace_b.window_id()));
1491
1492    // Drop client A's connection. Collaborators should disappear and the project should not be shown as shared.
1493    server.forbid_connections();
1494    server.disconnect_client(client_a.peer_id().unwrap());
1495    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
1496    project_a.read_with(cx_a, |project, _| project.collaborators().is_empty());
1497    project_a.read_with(cx_a, |project, _| assert!(!project.is_shared()));
1498    project_b.read_with(cx_b, |project, _| project.is_read_only());
1499    assert!(worktree_a.read_with(cx_a, |tree, _| !tree.as_local().unwrap().is_shared()));
1500
1501    // Ensure client B's edited state is reset and that the whole window is blurred.
1502    cx_b.read_window(window_id_b, |cx| {
1503        assert_eq!(cx.focused_view_id(), None);
1504    });
1505    assert!(!cx_b.is_window_edited(workspace_b.window_id()));
1506
1507    // Ensure client B is not prompted to save edits when closing window after disconnecting.
1508    let can_close = workspace_b
1509        .update(cx_b, |workspace, cx| workspace.prepare_to_close(true, cx))
1510        .await
1511        .unwrap();
1512    assert!(can_close);
1513
1514    // Allow client A to reconnect to the server.
1515    server.allow_connections();
1516    deterministic.advance_clock(RECEIVE_TIMEOUT);
1517
1518    // Client B calls client A again after they reconnected.
1519    let active_call_b = cx_b.read(ActiveCall::global);
1520    active_call_b
1521        .update(cx_b, |call, cx| {
1522            call.invite(client_a.user_id().unwrap(), None, cx)
1523        })
1524        .await
1525        .unwrap();
1526    deterministic.run_until_parked();
1527    active_call_a
1528        .update(cx_a, |call, cx| call.accept_incoming(cx))
1529        .await
1530        .unwrap();
1531
1532    active_call_a
1533        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
1534        .await
1535        .unwrap();
1536
1537    // Drop client A's connection again. We should still unshare it successfully.
1538    server.forbid_connections();
1539    server.disconnect_client(client_a.peer_id().unwrap());
1540    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
1541    project_a.read_with(cx_a, |project, _| assert!(!project.is_shared()));
1542}
1543
1544#[gpui::test(iterations = 10)]
1545async fn test_project_reconnect(
1546    deterministic: Arc<Deterministic>,
1547    cx_a: &mut TestAppContext,
1548    cx_b: &mut TestAppContext,
1549) {
1550    deterministic.forbid_parking();
1551    let mut server = TestServer::start(&deterministic).await;
1552    let client_a = server.create_client(cx_a, "user_a").await;
1553    let client_b = server.create_client(cx_b, "user_b").await;
1554    server
1555        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
1556        .await;
1557
1558    cx_b.update(editor::init);
1559
1560    client_a
1561        .fs
1562        .insert_tree(
1563            "/root-1",
1564            json!({
1565                "dir1": {
1566                    "a.txt": "a",
1567                    "b.txt": "b",
1568                    "subdir1": {
1569                        "c.txt": "c",
1570                        "d.txt": "d",
1571                        "e.txt": "e",
1572                    }
1573                },
1574                "dir2": {
1575                    "v.txt": "v",
1576                },
1577                "dir3": {
1578                    "w.txt": "w",
1579                    "x.txt": "x",
1580                    "y.txt": "y",
1581                },
1582                "dir4": {
1583                    "z.txt": "z",
1584                },
1585            }),
1586        )
1587        .await;
1588    client_a
1589        .fs
1590        .insert_tree(
1591            "/root-2",
1592            json!({
1593                "2.txt": "2",
1594            }),
1595        )
1596        .await;
1597    client_a
1598        .fs
1599        .insert_tree(
1600            "/root-3",
1601            json!({
1602                "3.txt": "3",
1603            }),
1604        )
1605        .await;
1606
1607    let active_call_a = cx_a.read(ActiveCall::global);
1608    let (project_a1, _) = client_a.build_local_project("/root-1/dir1", cx_a).await;
1609    let (project_a2, _) = client_a.build_local_project("/root-2", cx_a).await;
1610    let (project_a3, _) = client_a.build_local_project("/root-3", cx_a).await;
1611    let worktree_a1 =
1612        project_a1.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
1613    let project1_id = active_call_a
1614        .update(cx_a, |call, cx| call.share_project(project_a1.clone(), cx))
1615        .await
1616        .unwrap();
1617    let project2_id = active_call_a
1618        .update(cx_a, |call, cx| call.share_project(project_a2.clone(), cx))
1619        .await
1620        .unwrap();
1621    let project3_id = active_call_a
1622        .update(cx_a, |call, cx| call.share_project(project_a3.clone(), cx))
1623        .await
1624        .unwrap();
1625
1626    let project_b1 = client_b.build_remote_project(project1_id, cx_b).await;
1627    let project_b2 = client_b.build_remote_project(project2_id, cx_b).await;
1628    let project_b3 = client_b.build_remote_project(project3_id, cx_b).await;
1629    deterministic.run_until_parked();
1630
1631    let worktree1_id = worktree_a1.read_with(cx_a, |worktree, _| {
1632        assert!(worktree.as_local().unwrap().is_shared());
1633        worktree.id()
1634    });
1635    let (worktree_a2, _) = project_a1
1636        .update(cx_a, |p, cx| {
1637            p.find_or_create_local_worktree("/root-1/dir2", true, cx)
1638        })
1639        .await
1640        .unwrap();
1641    deterministic.run_until_parked();
1642    let worktree2_id = worktree_a2.read_with(cx_a, |tree, _| {
1643        assert!(tree.as_local().unwrap().is_shared());
1644        tree.id()
1645    });
1646    deterministic.run_until_parked();
1647    project_b1.read_with(cx_b, |project, cx| {
1648        assert!(project.worktree_for_id(worktree2_id, cx).is_some())
1649    });
1650
1651    let buffer_a1 = project_a1
1652        .update(cx_a, |p, cx| p.open_buffer((worktree1_id, "a.txt"), cx))
1653        .await
1654        .unwrap();
1655    let buffer_b1 = project_b1
1656        .update(cx_b, |p, cx| p.open_buffer((worktree1_id, "a.txt"), cx))
1657        .await
1658        .unwrap();
1659
1660    // Drop client A's connection.
1661    server.forbid_connections();
1662    server.disconnect_client(client_a.peer_id().unwrap());
1663    deterministic.advance_clock(RECEIVE_TIMEOUT);
1664    project_a1.read_with(cx_a, |project, _| {
1665        assert!(project.is_shared());
1666        assert_eq!(project.collaborators().len(), 1);
1667    });
1668    project_b1.read_with(cx_b, |project, _| {
1669        assert!(!project.is_read_only());
1670        assert_eq!(project.collaborators().len(), 1);
1671    });
1672    worktree_a1.read_with(cx_a, |tree, _| {
1673        assert!(tree.as_local().unwrap().is_shared())
1674    });
1675
1676    // While client A is disconnected, add and remove files from client A's project.
1677    client_a
1678        .fs
1679        .insert_tree(
1680            "/root-1/dir1/subdir2",
1681            json!({
1682                "f.txt": "f-contents",
1683                "g.txt": "g-contents",
1684                "h.txt": "h-contents",
1685                "i.txt": "i-contents",
1686            }),
1687        )
1688        .await;
1689    client_a
1690        .fs
1691        .remove_dir(
1692            "/root-1/dir1/subdir1".as_ref(),
1693            RemoveOptions {
1694                recursive: true,
1695                ..Default::default()
1696            },
1697        )
1698        .await
1699        .unwrap();
1700
1701    // While client A is disconnected, add and remove worktrees from client A's project.
1702    project_a1.update(cx_a, |project, cx| {
1703        project.remove_worktree(worktree2_id, cx)
1704    });
1705    let (worktree_a3, _) = project_a1
1706        .update(cx_a, |p, cx| {
1707            p.find_or_create_local_worktree("/root-1/dir3", true, cx)
1708        })
1709        .await
1710        .unwrap();
1711    worktree_a3
1712        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
1713        .await;
1714    let worktree3_id = worktree_a3.read_with(cx_a, |tree, _| {
1715        assert!(!tree.as_local().unwrap().is_shared());
1716        tree.id()
1717    });
1718    deterministic.run_until_parked();
1719
1720    // While client A is disconnected, close project 2
1721    cx_a.update(|_| drop(project_a2));
1722
1723    // While client A is disconnected, mutate a buffer on both the host and the guest.
1724    buffer_a1.update(cx_a, |buf, cx| buf.edit([(0..0, "W")], None, cx));
1725    buffer_b1.update(cx_b, |buf, cx| buf.edit([(1..1, "Z")], None, cx));
1726    deterministic.run_until_parked();
1727
1728    // Client A reconnects. Their project is re-shared, and client B re-joins it.
1729    server.allow_connections();
1730    client_a
1731        .authenticate_and_connect(false, &cx_a.to_async())
1732        .await
1733        .unwrap();
1734    deterministic.run_until_parked();
1735    project_a1.read_with(cx_a, |project, cx| {
1736        assert!(project.is_shared());
1737        assert!(worktree_a1.read(cx).as_local().unwrap().is_shared());
1738        assert_eq!(
1739            worktree_a1
1740                .read(cx)
1741                .snapshot()
1742                .paths()
1743                .map(|p| p.to_str().unwrap())
1744                .collect::<Vec<_>>(),
1745            vec![
1746                "a.txt",
1747                "b.txt",
1748                "subdir2",
1749                "subdir2/f.txt",
1750                "subdir2/g.txt",
1751                "subdir2/h.txt",
1752                "subdir2/i.txt"
1753            ]
1754        );
1755        assert!(worktree_a3.read(cx).as_local().unwrap().is_shared());
1756        assert_eq!(
1757            worktree_a3
1758                .read(cx)
1759                .snapshot()
1760                .paths()
1761                .map(|p| p.to_str().unwrap())
1762                .collect::<Vec<_>>(),
1763            vec!["w.txt", "x.txt", "y.txt"]
1764        );
1765    });
1766    project_b1.read_with(cx_b, |project, cx| {
1767        assert!(!project.is_read_only());
1768        assert_eq!(
1769            project
1770                .worktree_for_id(worktree1_id, cx)
1771                .unwrap()
1772                .read(cx)
1773                .snapshot()
1774                .paths()
1775                .map(|p| p.to_str().unwrap())
1776                .collect::<Vec<_>>(),
1777            vec![
1778                "a.txt",
1779                "b.txt",
1780                "subdir2",
1781                "subdir2/f.txt",
1782                "subdir2/g.txt",
1783                "subdir2/h.txt",
1784                "subdir2/i.txt"
1785            ]
1786        );
1787        assert!(project.worktree_for_id(worktree2_id, cx).is_none());
1788        assert_eq!(
1789            project
1790                .worktree_for_id(worktree3_id, cx)
1791                .unwrap()
1792                .read(cx)
1793                .snapshot()
1794                .paths()
1795                .map(|p| p.to_str().unwrap())
1796                .collect::<Vec<_>>(),
1797            vec!["w.txt", "x.txt", "y.txt"]
1798        );
1799    });
1800    project_b2.read_with(cx_b, |project, _| assert!(project.is_read_only()));
1801    project_b3.read_with(cx_b, |project, _| assert!(!project.is_read_only()));
1802    buffer_a1.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "WaZ"));
1803    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "WaZ"));
1804
1805    // Drop client B's connection.
1806    server.forbid_connections();
1807    server.disconnect_client(client_b.peer_id().unwrap());
1808    deterministic.advance_clock(RECEIVE_TIMEOUT);
1809
1810    // While client B is disconnected, add and remove files from client A's project
1811    client_a
1812        .fs
1813        .insert_file("/root-1/dir1/subdir2/j.txt", "j-contents".into())
1814        .await;
1815    client_a
1816        .fs
1817        .remove_file("/root-1/dir1/subdir2/i.txt".as_ref(), Default::default())
1818        .await
1819        .unwrap();
1820
1821    // While client B is disconnected, add and remove worktrees from client A's project.
1822    let (worktree_a4, _) = project_a1
1823        .update(cx_a, |p, cx| {
1824            p.find_or_create_local_worktree("/root-1/dir4", true, cx)
1825        })
1826        .await
1827        .unwrap();
1828    deterministic.run_until_parked();
1829    let worktree4_id = worktree_a4.read_with(cx_a, |tree, _| {
1830        assert!(tree.as_local().unwrap().is_shared());
1831        tree.id()
1832    });
1833    project_a1.update(cx_a, |project, cx| {
1834        project.remove_worktree(worktree3_id, cx)
1835    });
1836    deterministic.run_until_parked();
1837
1838    // While client B is disconnected, mutate a buffer on both the host and the guest.
1839    buffer_a1.update(cx_a, |buf, cx| buf.edit([(1..1, "X")], None, cx));
1840    buffer_b1.update(cx_b, |buf, cx| buf.edit([(2..2, "Y")], None, cx));
1841    deterministic.run_until_parked();
1842
1843    // While disconnected, close project 3
1844    cx_a.update(|_| drop(project_a3));
1845
1846    // Client B reconnects. They re-join the room and the remaining shared project.
1847    server.allow_connections();
1848    client_b
1849        .authenticate_and_connect(false, &cx_b.to_async())
1850        .await
1851        .unwrap();
1852    deterministic.run_until_parked();
1853    project_b1.read_with(cx_b, |project, cx| {
1854        assert!(!project.is_read_only());
1855        assert_eq!(
1856            project
1857                .worktree_for_id(worktree1_id, cx)
1858                .unwrap()
1859                .read(cx)
1860                .snapshot()
1861                .paths()
1862                .map(|p| p.to_str().unwrap())
1863                .collect::<Vec<_>>(),
1864            vec![
1865                "a.txt",
1866                "b.txt",
1867                "subdir2",
1868                "subdir2/f.txt",
1869                "subdir2/g.txt",
1870                "subdir2/h.txt",
1871                "subdir2/j.txt"
1872            ]
1873        );
1874        assert!(project.worktree_for_id(worktree2_id, cx).is_none());
1875        assert_eq!(
1876            project
1877                .worktree_for_id(worktree4_id, cx)
1878                .unwrap()
1879                .read(cx)
1880                .snapshot()
1881                .paths()
1882                .map(|p| p.to_str().unwrap())
1883                .collect::<Vec<_>>(),
1884            vec!["z.txt"]
1885        );
1886    });
1887    project_b3.read_with(cx_b, |project, _| assert!(project.is_read_only()));
1888    buffer_a1.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "WXaYZ"));
1889    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "WXaYZ"));
1890}
1891
1892#[gpui::test(iterations = 10)]
1893async fn test_active_call_events(
1894    deterministic: Arc<Deterministic>,
1895    cx_a: &mut TestAppContext,
1896    cx_b: &mut TestAppContext,
1897) {
1898    deterministic.forbid_parking();
1899    let mut server = TestServer::start(&deterministic).await;
1900    let client_a = server.create_client(cx_a, "user_a").await;
1901    let client_b = server.create_client(cx_b, "user_b").await;
1902    client_a.fs.insert_tree("/a", json!({})).await;
1903    client_b.fs.insert_tree("/b", json!({})).await;
1904
1905    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
1906    let (project_b, _) = client_b.build_local_project("/b", cx_b).await;
1907
1908    server
1909        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
1910        .await;
1911    let active_call_a = cx_a.read(ActiveCall::global);
1912    let active_call_b = cx_b.read(ActiveCall::global);
1913
1914    let events_a = active_call_events(cx_a);
1915    let events_b = active_call_events(cx_b);
1916
1917    let project_a_id = active_call_a
1918        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
1919        .await
1920        .unwrap();
1921    deterministic.run_until_parked();
1922    assert_eq!(mem::take(&mut *events_a.borrow_mut()), vec![]);
1923    assert_eq!(
1924        mem::take(&mut *events_b.borrow_mut()),
1925        vec![room::Event::RemoteProjectShared {
1926            owner: Arc::new(User {
1927                id: client_a.user_id().unwrap(),
1928                github_login: "user_a".to_string(),
1929                avatar: None,
1930            }),
1931            project_id: project_a_id,
1932            worktree_root_names: vec!["a".to_string()],
1933        }]
1934    );
1935
1936    let project_b_id = active_call_b
1937        .update(cx_b, |call, cx| call.share_project(project_b.clone(), cx))
1938        .await
1939        .unwrap();
1940    deterministic.run_until_parked();
1941    assert_eq!(
1942        mem::take(&mut *events_a.borrow_mut()),
1943        vec![room::Event::RemoteProjectShared {
1944            owner: Arc::new(User {
1945                id: client_b.user_id().unwrap(),
1946                github_login: "user_b".to_string(),
1947                avatar: None,
1948            }),
1949            project_id: project_b_id,
1950            worktree_root_names: vec!["b".to_string()]
1951        }]
1952    );
1953    assert_eq!(mem::take(&mut *events_b.borrow_mut()), vec![]);
1954
1955    // Sharing a project twice is idempotent.
1956    let project_b_id_2 = active_call_b
1957        .update(cx_b, |call, cx| call.share_project(project_b.clone(), cx))
1958        .await
1959        .unwrap();
1960    assert_eq!(project_b_id_2, project_b_id);
1961    deterministic.run_until_parked();
1962    assert_eq!(mem::take(&mut *events_a.borrow_mut()), vec![]);
1963    assert_eq!(mem::take(&mut *events_b.borrow_mut()), vec![]);
1964}
1965
1966fn active_call_events(cx: &mut TestAppContext) -> Rc<RefCell<Vec<room::Event>>> {
1967    let events = Rc::new(RefCell::new(Vec::new()));
1968    let active_call = cx.read(ActiveCall::global);
1969    cx.update({
1970        let events = events.clone();
1971        |cx| {
1972            cx.subscribe(&active_call, move |_, event, _| {
1973                events.borrow_mut().push(event.clone())
1974            })
1975            .detach()
1976        }
1977    });
1978    events
1979}
1980
1981#[gpui::test(iterations = 10)]
1982async fn test_room_location(
1983    deterministic: Arc<Deterministic>,
1984    cx_a: &mut TestAppContext,
1985    cx_b: &mut TestAppContext,
1986) {
1987    deterministic.forbid_parking();
1988    let mut server = TestServer::start(&deterministic).await;
1989    let client_a = server.create_client(cx_a, "user_a").await;
1990    let client_b = server.create_client(cx_b, "user_b").await;
1991    client_a.fs.insert_tree("/a", json!({})).await;
1992    client_b.fs.insert_tree("/b", json!({})).await;
1993
1994    let active_call_a = cx_a.read(ActiveCall::global);
1995    let active_call_b = cx_b.read(ActiveCall::global);
1996
1997    let a_notified = Rc::new(Cell::new(false));
1998    cx_a.update({
1999        let notified = a_notified.clone();
2000        |cx| {
2001            cx.observe(&active_call_a, move |_, _| notified.set(true))
2002                .detach()
2003        }
2004    });
2005
2006    let b_notified = Rc::new(Cell::new(false));
2007    cx_b.update({
2008        let b_notified = b_notified.clone();
2009        |cx| {
2010            cx.observe(&active_call_b, move |_, _| b_notified.set(true))
2011                .detach()
2012        }
2013    });
2014
2015    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
2016    active_call_a
2017        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
2018        .await
2019        .unwrap();
2020    let (project_b, _) = client_b.build_local_project("/b", cx_b).await;
2021
2022    server
2023        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2024        .await;
2025    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
2026    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
2027    deterministic.run_until_parked();
2028    assert!(a_notified.take());
2029    assert_eq!(
2030        participant_locations(&room_a, cx_a),
2031        vec![("user_b".to_string(), ParticipantLocation::External)]
2032    );
2033    assert!(b_notified.take());
2034    assert_eq!(
2035        participant_locations(&room_b, cx_b),
2036        vec![("user_a".to_string(), ParticipantLocation::UnsharedProject)]
2037    );
2038
2039    let project_a_id = active_call_a
2040        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2041        .await
2042        .unwrap();
2043    deterministic.run_until_parked();
2044    assert!(a_notified.take());
2045    assert_eq!(
2046        participant_locations(&room_a, cx_a),
2047        vec![("user_b".to_string(), ParticipantLocation::External)]
2048    );
2049    assert!(b_notified.take());
2050    assert_eq!(
2051        participant_locations(&room_b, cx_b),
2052        vec![(
2053            "user_a".to_string(),
2054            ParticipantLocation::SharedProject {
2055                project_id: project_a_id
2056            }
2057        )]
2058    );
2059
2060    let project_b_id = active_call_b
2061        .update(cx_b, |call, cx| call.share_project(project_b.clone(), cx))
2062        .await
2063        .unwrap();
2064    deterministic.run_until_parked();
2065    assert!(a_notified.take());
2066    assert_eq!(
2067        participant_locations(&room_a, cx_a),
2068        vec![("user_b".to_string(), ParticipantLocation::External)]
2069    );
2070    assert!(b_notified.take());
2071    assert_eq!(
2072        participant_locations(&room_b, cx_b),
2073        vec![(
2074            "user_a".to_string(),
2075            ParticipantLocation::SharedProject {
2076                project_id: project_a_id
2077            }
2078        )]
2079    );
2080
2081    active_call_b
2082        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
2083        .await
2084        .unwrap();
2085    deterministic.run_until_parked();
2086    assert!(a_notified.take());
2087    assert_eq!(
2088        participant_locations(&room_a, cx_a),
2089        vec![(
2090            "user_b".to_string(),
2091            ParticipantLocation::SharedProject {
2092                project_id: project_b_id
2093            }
2094        )]
2095    );
2096    assert!(b_notified.take());
2097    assert_eq!(
2098        participant_locations(&room_b, cx_b),
2099        vec![(
2100            "user_a".to_string(),
2101            ParticipantLocation::SharedProject {
2102                project_id: project_a_id
2103            }
2104        )]
2105    );
2106
2107    active_call_b
2108        .update(cx_b, |call, cx| call.set_location(None, cx))
2109        .await
2110        .unwrap();
2111    deterministic.run_until_parked();
2112    assert!(a_notified.take());
2113    assert_eq!(
2114        participant_locations(&room_a, cx_a),
2115        vec![("user_b".to_string(), ParticipantLocation::External)]
2116    );
2117    assert!(b_notified.take());
2118    assert_eq!(
2119        participant_locations(&room_b, cx_b),
2120        vec![(
2121            "user_a".to_string(),
2122            ParticipantLocation::SharedProject {
2123                project_id: project_a_id
2124            }
2125        )]
2126    );
2127
2128    fn participant_locations(
2129        room: &ModelHandle<Room>,
2130        cx: &TestAppContext,
2131    ) -> Vec<(String, ParticipantLocation)> {
2132        room.read_with(cx, |room, _| {
2133            room.remote_participants()
2134                .values()
2135                .map(|participant| {
2136                    (
2137                        participant.user.github_login.to_string(),
2138                        participant.location,
2139                    )
2140                })
2141                .collect()
2142        })
2143    }
2144}
2145
2146#[gpui::test(iterations = 10)]
2147async fn test_propagate_saves_and_fs_changes(
2148    deterministic: Arc<Deterministic>,
2149    cx_a: &mut TestAppContext,
2150    cx_b: &mut TestAppContext,
2151    cx_c: &mut TestAppContext,
2152) {
2153    deterministic.forbid_parking();
2154    let mut server = TestServer::start(&deterministic).await;
2155    let client_a = server.create_client(cx_a, "user_a").await;
2156    let client_b = server.create_client(cx_b, "user_b").await;
2157    let client_c = server.create_client(cx_c, "user_c").await;
2158
2159    server
2160        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
2161        .await;
2162    let active_call_a = cx_a.read(ActiveCall::global);
2163
2164    let rust = Arc::new(Language::new(
2165        LanguageConfig {
2166            name: "Rust".into(),
2167            path_suffixes: vec!["rs".to_string()],
2168            ..Default::default()
2169        },
2170        Some(tree_sitter_rust::language()),
2171    ));
2172    let javascript = Arc::new(Language::new(
2173        LanguageConfig {
2174            name: "JavaScript".into(),
2175            path_suffixes: vec!["js".to_string()],
2176            ..Default::default()
2177        },
2178        Some(tree_sitter_rust::language()),
2179    ));
2180    for client in [&client_a, &client_b, &client_c] {
2181        client.language_registry.add(rust.clone());
2182        client.language_registry.add(javascript.clone());
2183    }
2184
2185    client_a
2186        .fs
2187        .insert_tree(
2188            "/a",
2189            json!({
2190                "file1.rs": "",
2191                "file2": ""
2192            }),
2193        )
2194        .await;
2195    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
2196    let worktree_a = project_a.read_with(cx_a, |p, cx| p.worktrees(cx).next().unwrap());
2197    let project_id = active_call_a
2198        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2199        .await
2200        .unwrap();
2201
2202    // Join that worktree as clients B and C.
2203    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2204    let project_c = client_c.build_remote_project(project_id, cx_c).await;
2205    let worktree_b = project_b.read_with(cx_b, |p, cx| p.worktrees(cx).next().unwrap());
2206    let worktree_c = project_c.read_with(cx_c, |p, cx| p.worktrees(cx).next().unwrap());
2207
2208    // Open and edit a buffer as both guests B and C.
2209    let buffer_b = project_b
2210        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "file1.rs"), cx))
2211        .await
2212        .unwrap();
2213    let buffer_c = project_c
2214        .update(cx_c, |p, cx| p.open_buffer((worktree_id, "file1.rs"), cx))
2215        .await
2216        .unwrap();
2217    buffer_b.read_with(cx_b, |buffer, _| {
2218        assert_eq!(&*buffer.language().unwrap().name(), "Rust");
2219    });
2220    buffer_c.read_with(cx_c, |buffer, _| {
2221        assert_eq!(&*buffer.language().unwrap().name(), "Rust");
2222    });
2223    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "i-am-b, ")], None, cx));
2224    buffer_c.update(cx_c, |buf, cx| buf.edit([(0..0, "i-am-c, ")], None, cx));
2225
2226    // Open and edit that buffer as the host.
2227    let buffer_a = project_a
2228        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "file1.rs"), cx))
2229        .await
2230        .unwrap();
2231
2232    deterministic.run_until_parked();
2233    buffer_a.read_with(cx_a, |buf, _| assert_eq!(buf.text(), "i-am-c, i-am-b, "));
2234    buffer_a.update(cx_a, |buf, cx| {
2235        buf.edit([(buf.len()..buf.len(), "i-am-a")], None, cx)
2236    });
2237
2238    deterministic.run_until_parked();
2239    buffer_a.read_with(cx_a, |buf, _| {
2240        assert_eq!(buf.text(), "i-am-c, i-am-b, i-am-a");
2241    });
2242    buffer_b.read_with(cx_b, |buf, _| {
2243        assert_eq!(buf.text(), "i-am-c, i-am-b, i-am-a");
2244    });
2245    buffer_c.read_with(cx_c, |buf, _| {
2246        assert_eq!(buf.text(), "i-am-c, i-am-b, i-am-a");
2247    });
2248
2249    // Edit the buffer as the host and concurrently save as guest B.
2250    let save_b = project_b.update(cx_b, |project, cx| {
2251        project.save_buffer(buffer_b.clone(), cx)
2252    });
2253    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "hi-a, ")], None, cx));
2254    save_b.await.unwrap();
2255    assert_eq!(
2256        client_a.fs.load("/a/file1.rs".as_ref()).await.unwrap(),
2257        "hi-a, i-am-c, i-am-b, i-am-a"
2258    );
2259
2260    deterministic.run_until_parked();
2261    buffer_a.read_with(cx_a, |buf, _| assert!(!buf.is_dirty()));
2262    buffer_b.read_with(cx_b, |buf, _| assert!(!buf.is_dirty()));
2263    buffer_c.read_with(cx_c, |buf, _| assert!(!buf.is_dirty()));
2264
2265    // Make changes on host's file system, see those changes on guest worktrees.
2266    client_a
2267        .fs
2268        .rename(
2269            "/a/file1.rs".as_ref(),
2270            "/a/file1.js".as_ref(),
2271            Default::default(),
2272        )
2273        .await
2274        .unwrap();
2275    client_a
2276        .fs
2277        .rename("/a/file2".as_ref(), "/a/file3".as_ref(), Default::default())
2278        .await
2279        .unwrap();
2280    client_a.fs.insert_file("/a/file4", "4".into()).await;
2281    deterministic.run_until_parked();
2282
2283    worktree_a.read_with(cx_a, |tree, _| {
2284        assert_eq!(
2285            tree.paths()
2286                .map(|p| p.to_string_lossy())
2287                .collect::<Vec<_>>(),
2288            ["file1.js", "file3", "file4"]
2289        )
2290    });
2291    worktree_b.read_with(cx_b, |tree, _| {
2292        assert_eq!(
2293            tree.paths()
2294                .map(|p| p.to_string_lossy())
2295                .collect::<Vec<_>>(),
2296            ["file1.js", "file3", "file4"]
2297        )
2298    });
2299    worktree_c.read_with(cx_c, |tree, _| {
2300        assert_eq!(
2301            tree.paths()
2302                .map(|p| p.to_string_lossy())
2303                .collect::<Vec<_>>(),
2304            ["file1.js", "file3", "file4"]
2305        )
2306    });
2307
2308    // Ensure buffer files are updated as well.
2309    buffer_a.read_with(cx_a, |buffer, _| {
2310        assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
2311        assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
2312    });
2313    buffer_b.read_with(cx_b, |buffer, _| {
2314        assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
2315        assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
2316    });
2317    buffer_c.read_with(cx_c, |buffer, _| {
2318        assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
2319        assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
2320    });
2321
2322    let new_buffer_a = project_a
2323        .update(cx_a, |p, cx| p.create_buffer("", None, cx))
2324        .unwrap();
2325    let new_buffer_id = new_buffer_a.read_with(cx_a, |buffer, _| buffer.remote_id());
2326    let new_buffer_b = project_b
2327        .update(cx_b, |p, cx| p.open_buffer_by_id(new_buffer_id, cx))
2328        .await
2329        .unwrap();
2330    new_buffer_b.read_with(cx_b, |buffer, _| {
2331        assert!(buffer.file().is_none());
2332    });
2333
2334    new_buffer_a.update(cx_a, |buffer, cx| {
2335        buffer.edit([(0..0, "ok")], None, cx);
2336    });
2337    project_a
2338        .update(cx_a, |project, cx| {
2339            project.save_buffer_as(new_buffer_a.clone(), "/a/file3.rs".into(), cx)
2340        })
2341        .await
2342        .unwrap();
2343
2344    deterministic.run_until_parked();
2345    new_buffer_b.read_with(cx_b, |buffer_b, _| {
2346        assert_eq!(
2347            buffer_b.file().unwrap().path().as_ref(),
2348            Path::new("file3.rs")
2349        );
2350
2351        new_buffer_a.read_with(cx_a, |buffer_a, _| {
2352            assert_eq!(buffer_b.saved_mtime(), buffer_a.saved_mtime());
2353            assert_eq!(buffer_b.saved_version(), buffer_a.saved_version());
2354        });
2355    });
2356}
2357
2358#[gpui::test(iterations = 10)]
2359async fn test_git_diff_base_change(
2360    deterministic: Arc<Deterministic>,
2361    cx_a: &mut TestAppContext,
2362    cx_b: &mut TestAppContext,
2363) {
2364    deterministic.forbid_parking();
2365    let mut server = TestServer::start(&deterministic).await;
2366    let client_a = server.create_client(cx_a, "user_a").await;
2367    let client_b = server.create_client(cx_b, "user_b").await;
2368    server
2369        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2370        .await;
2371    let active_call_a = cx_a.read(ActiveCall::global);
2372
2373    client_a
2374        .fs
2375        .insert_tree(
2376            "/dir",
2377            json!({
2378            ".git": {},
2379            "sub": {
2380                ".git": {},
2381                "b.txt": "
2382                    one
2383                    two
2384                    three
2385                ".unindent(),
2386            },
2387            "a.txt": "
2388                    one
2389                    two
2390                    three
2391                ".unindent(),
2392            }),
2393        )
2394        .await;
2395
2396    let (project_local, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2397    let project_id = active_call_a
2398        .update(cx_a, |call, cx| {
2399            call.share_project(project_local.clone(), cx)
2400        })
2401        .await
2402        .unwrap();
2403
2404    let project_remote = client_b.build_remote_project(project_id, cx_b).await;
2405
2406    let diff_base = "
2407        one
2408        three
2409    "
2410    .unindent();
2411
2412    let new_diff_base = "
2413        one
2414        two
2415    "
2416    .unindent();
2417
2418    client_a
2419        .fs
2420        .as_fake()
2421        .set_index_for_repo(
2422            Path::new("/dir/.git"),
2423            &[(Path::new("a.txt"), diff_base.clone())],
2424        )
2425        .await;
2426
2427    // Create the buffer
2428    let buffer_local_a = project_local
2429        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2430        .await
2431        .unwrap();
2432
2433    // Wait for it to catch up to the new diff
2434    deterministic.run_until_parked();
2435
2436    // Smoke test diffing
2437    buffer_local_a.read_with(cx_a, |buffer, _| {
2438        assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
2439        git::diff::assert_hunks(
2440            buffer.snapshot().git_diff_hunks_in_row_range(0..4, false),
2441            &buffer,
2442            &diff_base,
2443            &[(1..2, "", "two\n")],
2444        );
2445    });
2446
2447    // Create remote buffer
2448    let buffer_remote_a = project_remote
2449        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2450        .await
2451        .unwrap();
2452
2453    // Wait remote buffer to catch up to the new diff
2454    deterministic.run_until_parked();
2455
2456    // Smoke test diffing
2457    buffer_remote_a.read_with(cx_b, |buffer, _| {
2458        assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
2459        git::diff::assert_hunks(
2460            buffer.snapshot().git_diff_hunks_in_row_range(0..4, false),
2461            &buffer,
2462            &diff_base,
2463            &[(1..2, "", "two\n")],
2464        );
2465    });
2466
2467    client_a
2468        .fs
2469        .as_fake()
2470        .set_index_for_repo(
2471            Path::new("/dir/.git"),
2472            &[(Path::new("a.txt"), new_diff_base.clone())],
2473        )
2474        .await;
2475
2476    // Wait for buffer_local_a to receive it
2477    deterministic.run_until_parked();
2478
2479    // Smoke test new diffing
2480    buffer_local_a.read_with(cx_a, |buffer, _| {
2481        assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
2482
2483        git::diff::assert_hunks(
2484            buffer.snapshot().git_diff_hunks_in_row_range(0..4, false),
2485            &buffer,
2486            &diff_base,
2487            &[(2..3, "", "three\n")],
2488        );
2489    });
2490
2491    // Smoke test B
2492    buffer_remote_a.read_with(cx_b, |buffer, _| {
2493        assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
2494        git::diff::assert_hunks(
2495            buffer.snapshot().git_diff_hunks_in_row_range(0..4, false),
2496            &buffer,
2497            &diff_base,
2498            &[(2..3, "", "three\n")],
2499        );
2500    });
2501
2502    //Nested git dir
2503
2504    let diff_base = "
2505        one
2506        three
2507    "
2508    .unindent();
2509
2510    let new_diff_base = "
2511        one
2512        two
2513    "
2514    .unindent();
2515
2516    client_a
2517        .fs
2518        .as_fake()
2519        .set_index_for_repo(
2520            Path::new("/dir/sub/.git"),
2521            &[(Path::new("b.txt"), diff_base.clone())],
2522        )
2523        .await;
2524
2525    // Create the buffer
2526    let buffer_local_b = project_local
2527        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "sub/b.txt"), cx))
2528        .await
2529        .unwrap();
2530
2531    // Wait for it to catch up to the new diff
2532    deterministic.run_until_parked();
2533
2534    // Smoke test diffing
2535    buffer_local_b.read_with(cx_a, |buffer, _| {
2536        assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
2537        git::diff::assert_hunks(
2538            buffer.snapshot().git_diff_hunks_in_row_range(0..4, false),
2539            &buffer,
2540            &diff_base,
2541            &[(1..2, "", "two\n")],
2542        );
2543    });
2544
2545    // Create remote buffer
2546    let buffer_remote_b = project_remote
2547        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "sub/b.txt"), cx))
2548        .await
2549        .unwrap();
2550
2551    // Wait remote buffer to catch up to the new diff
2552    deterministic.run_until_parked();
2553
2554    // Smoke test diffing
2555    buffer_remote_b.read_with(cx_b, |buffer, _| {
2556        assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
2557        git::diff::assert_hunks(
2558            buffer.snapshot().git_diff_hunks_in_row_range(0..4, false),
2559            &buffer,
2560            &diff_base,
2561            &[(1..2, "", "two\n")],
2562        );
2563    });
2564
2565    client_a
2566        .fs
2567        .as_fake()
2568        .set_index_for_repo(
2569            Path::new("/dir/sub/.git"),
2570            &[(Path::new("b.txt"), new_diff_base.clone())],
2571        )
2572        .await;
2573
2574    // Wait for buffer_local_b to receive it
2575    deterministic.run_until_parked();
2576
2577    // Smoke test new diffing
2578    buffer_local_b.read_with(cx_a, |buffer, _| {
2579        assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
2580        println!("{:?}", buffer.as_rope().to_string());
2581        println!("{:?}", buffer.diff_base());
2582        println!(
2583            "{:?}",
2584            buffer
2585                .snapshot()
2586                .git_diff_hunks_in_row_range(0..4, false)
2587                .collect::<Vec<_>>()
2588        );
2589
2590        git::diff::assert_hunks(
2591            buffer.snapshot().git_diff_hunks_in_row_range(0..4, false),
2592            &buffer,
2593            &diff_base,
2594            &[(2..3, "", "three\n")],
2595        );
2596    });
2597
2598    // Smoke test B
2599    buffer_remote_b.read_with(cx_b, |buffer, _| {
2600        assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
2601        git::diff::assert_hunks(
2602            buffer.snapshot().git_diff_hunks_in_row_range(0..4, false),
2603            &buffer,
2604            &diff_base,
2605            &[(2..3, "", "three\n")],
2606        );
2607    });
2608}
2609
2610#[gpui::test]
2611async fn test_git_branch_name(
2612    deterministic: Arc<Deterministic>,
2613    cx_a: &mut TestAppContext,
2614    cx_b: &mut TestAppContext,
2615    cx_c: &mut TestAppContext,
2616) {
2617    deterministic.forbid_parking();
2618    let mut server = TestServer::start(&deterministic).await;
2619    let client_a = server.create_client(cx_a, "user_a").await;
2620    let client_b = server.create_client(cx_b, "user_b").await;
2621    let client_c = server.create_client(cx_c, "user_c").await;
2622    server
2623        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
2624        .await;
2625    let active_call_a = cx_a.read(ActiveCall::global);
2626
2627    client_a
2628        .fs
2629        .insert_tree(
2630            "/dir",
2631            json!({
2632            ".git": {},
2633            }),
2634        )
2635        .await;
2636
2637    let (project_local, _worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2638    let project_id = active_call_a
2639        .update(cx_a, |call, cx| {
2640            call.share_project(project_local.clone(), cx)
2641        })
2642        .await
2643        .unwrap();
2644
2645    let project_remote = client_b.build_remote_project(project_id, cx_b).await;
2646    client_a
2647        .fs
2648        .as_fake()
2649        .set_branch_name(Path::new("/dir/.git"), Some("branch-1"))
2650        .await;
2651
2652    // Wait for it to catch up to the new branch
2653    deterministic.run_until_parked();
2654
2655    #[track_caller]
2656    fn assert_branch(branch_name: Option<impl Into<String>>, project: &Project, cx: &AppContext) {
2657        let branch_name = branch_name.map(Into::into);
2658        let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
2659        assert_eq!(worktrees.len(), 1);
2660        let worktree = worktrees[0].clone();
2661        let root_entry = worktree.read(cx).snapshot().root_git_entry().unwrap();
2662        assert_eq!(root_entry.branch(), branch_name.map(Into::into));
2663    }
2664
2665    // Smoke test branch reading
2666    project_local.read_with(cx_a, |project, cx| {
2667        assert_branch(Some("branch-1"), project, cx)
2668    });
2669    project_remote.read_with(cx_b, |project, cx| {
2670        assert_branch(Some("branch-1"), project, cx)
2671    });
2672
2673    client_a
2674        .fs
2675        .as_fake()
2676        .set_branch_name(Path::new("/dir/.git"), Some("branch-2"))
2677        .await;
2678
2679    // Wait for buffer_local_a to receive it
2680    deterministic.run_until_parked();
2681
2682    // Smoke test branch reading
2683    project_local.read_with(cx_a, |project, cx| {
2684        assert_branch(Some("branch-2"), project, cx)
2685    });
2686    project_remote.read_with(cx_b, |project, cx| {
2687        assert_branch(Some("branch-2"), project, cx)
2688    });
2689
2690    let project_remote_c = client_c.build_remote_project(project_id, cx_c).await;
2691    project_remote_c.read_with(cx_c, |project, cx| {
2692        assert_branch(Some("branch-2"), project, cx)
2693    });
2694}
2695
2696#[gpui::test(iterations = 10)]
2697async fn test_fs_operations(
2698    deterministic: Arc<Deterministic>,
2699    cx_a: &mut TestAppContext,
2700    cx_b: &mut TestAppContext,
2701) {
2702    deterministic.forbid_parking();
2703    let mut server = TestServer::start(&deterministic).await;
2704    let client_a = server.create_client(cx_a, "user_a").await;
2705    let client_b = server.create_client(cx_b, "user_b").await;
2706    server
2707        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2708        .await;
2709    let active_call_a = cx_a.read(ActiveCall::global);
2710
2711    client_a
2712        .fs
2713        .insert_tree(
2714            "/dir",
2715            json!({
2716                "a.txt": "a-contents",
2717                "b.txt": "b-contents",
2718            }),
2719        )
2720        .await;
2721    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2722    let project_id = active_call_a
2723        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2724        .await
2725        .unwrap();
2726    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2727
2728    let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
2729    let worktree_b = project_b.read_with(cx_b, |project, cx| project.worktrees(cx).next().unwrap());
2730
2731    let entry = project_b
2732        .update(cx_b, |project, cx| {
2733            project
2734                .create_entry((worktree_id, "c.txt"), false, cx)
2735                .unwrap()
2736        })
2737        .await
2738        .unwrap();
2739    worktree_a.read_with(cx_a, |worktree, _| {
2740        assert_eq!(
2741            worktree
2742                .paths()
2743                .map(|p| p.to_string_lossy())
2744                .collect::<Vec<_>>(),
2745            ["a.txt", "b.txt", "c.txt"]
2746        );
2747    });
2748    worktree_b.read_with(cx_b, |worktree, _| {
2749        assert_eq!(
2750            worktree
2751                .paths()
2752                .map(|p| p.to_string_lossy())
2753                .collect::<Vec<_>>(),
2754            ["a.txt", "b.txt", "c.txt"]
2755        );
2756    });
2757
2758    project_b
2759        .update(cx_b, |project, cx| {
2760            project.rename_entry(entry.id, Path::new("d.txt"), cx)
2761        })
2762        .unwrap()
2763        .await
2764        .unwrap();
2765    worktree_a.read_with(cx_a, |worktree, _| {
2766        assert_eq!(
2767            worktree
2768                .paths()
2769                .map(|p| p.to_string_lossy())
2770                .collect::<Vec<_>>(),
2771            ["a.txt", "b.txt", "d.txt"]
2772        );
2773    });
2774    worktree_b.read_with(cx_b, |worktree, _| {
2775        assert_eq!(
2776            worktree
2777                .paths()
2778                .map(|p| p.to_string_lossy())
2779                .collect::<Vec<_>>(),
2780            ["a.txt", "b.txt", "d.txt"]
2781        );
2782    });
2783
2784    let dir_entry = project_b
2785        .update(cx_b, |project, cx| {
2786            project
2787                .create_entry((worktree_id, "DIR"), true, cx)
2788                .unwrap()
2789        })
2790        .await
2791        .unwrap();
2792    worktree_a.read_with(cx_a, |worktree, _| {
2793        assert_eq!(
2794            worktree
2795                .paths()
2796                .map(|p| p.to_string_lossy())
2797                .collect::<Vec<_>>(),
2798            ["DIR", "a.txt", "b.txt", "d.txt"]
2799        );
2800    });
2801    worktree_b.read_with(cx_b, |worktree, _| {
2802        assert_eq!(
2803            worktree
2804                .paths()
2805                .map(|p| p.to_string_lossy())
2806                .collect::<Vec<_>>(),
2807            ["DIR", "a.txt", "b.txt", "d.txt"]
2808        );
2809    });
2810
2811    project_b
2812        .update(cx_b, |project, cx| {
2813            project
2814                .create_entry((worktree_id, "DIR/e.txt"), false, cx)
2815                .unwrap()
2816        })
2817        .await
2818        .unwrap();
2819    project_b
2820        .update(cx_b, |project, cx| {
2821            project
2822                .create_entry((worktree_id, "DIR/SUBDIR"), true, cx)
2823                .unwrap()
2824        })
2825        .await
2826        .unwrap();
2827    project_b
2828        .update(cx_b, |project, cx| {
2829            project
2830                .create_entry((worktree_id, "DIR/SUBDIR/f.txt"), false, cx)
2831                .unwrap()
2832        })
2833        .await
2834        .unwrap();
2835    worktree_a.read_with(cx_a, |worktree, _| {
2836        assert_eq!(
2837            worktree
2838                .paths()
2839                .map(|p| p.to_string_lossy())
2840                .collect::<Vec<_>>(),
2841            [
2842                "DIR",
2843                "DIR/SUBDIR",
2844                "DIR/SUBDIR/f.txt",
2845                "DIR/e.txt",
2846                "a.txt",
2847                "b.txt",
2848                "d.txt"
2849            ]
2850        );
2851    });
2852    worktree_b.read_with(cx_b, |worktree, _| {
2853        assert_eq!(
2854            worktree
2855                .paths()
2856                .map(|p| p.to_string_lossy())
2857                .collect::<Vec<_>>(),
2858            [
2859                "DIR",
2860                "DIR/SUBDIR",
2861                "DIR/SUBDIR/f.txt",
2862                "DIR/e.txt",
2863                "a.txt",
2864                "b.txt",
2865                "d.txt"
2866            ]
2867        );
2868    });
2869
2870    project_b
2871        .update(cx_b, |project, cx| {
2872            project
2873                .copy_entry(entry.id, Path::new("f.txt"), cx)
2874                .unwrap()
2875        })
2876        .await
2877        .unwrap();
2878    worktree_a.read_with(cx_a, |worktree, _| {
2879        assert_eq!(
2880            worktree
2881                .paths()
2882                .map(|p| p.to_string_lossy())
2883                .collect::<Vec<_>>(),
2884            [
2885                "DIR",
2886                "DIR/SUBDIR",
2887                "DIR/SUBDIR/f.txt",
2888                "DIR/e.txt",
2889                "a.txt",
2890                "b.txt",
2891                "d.txt",
2892                "f.txt"
2893            ]
2894        );
2895    });
2896    worktree_b.read_with(cx_b, |worktree, _| {
2897        assert_eq!(
2898            worktree
2899                .paths()
2900                .map(|p| p.to_string_lossy())
2901                .collect::<Vec<_>>(),
2902            [
2903                "DIR",
2904                "DIR/SUBDIR",
2905                "DIR/SUBDIR/f.txt",
2906                "DIR/e.txt",
2907                "a.txt",
2908                "b.txt",
2909                "d.txt",
2910                "f.txt"
2911            ]
2912        );
2913    });
2914
2915    project_b
2916        .update(cx_b, |project, cx| {
2917            project.delete_entry(dir_entry.id, cx).unwrap()
2918        })
2919        .await
2920        .unwrap();
2921    deterministic.run_until_parked();
2922
2923    worktree_a.read_with(cx_a, |worktree, _| {
2924        assert_eq!(
2925            worktree
2926                .paths()
2927                .map(|p| p.to_string_lossy())
2928                .collect::<Vec<_>>(),
2929            ["a.txt", "b.txt", "d.txt", "f.txt"]
2930        );
2931    });
2932    worktree_b.read_with(cx_b, |worktree, _| {
2933        assert_eq!(
2934            worktree
2935                .paths()
2936                .map(|p| p.to_string_lossy())
2937                .collect::<Vec<_>>(),
2938            ["a.txt", "b.txt", "d.txt", "f.txt"]
2939        );
2940    });
2941
2942    project_b
2943        .update(cx_b, |project, cx| {
2944            project.delete_entry(entry.id, cx).unwrap()
2945        })
2946        .await
2947        .unwrap();
2948    worktree_a.read_with(cx_a, |worktree, _| {
2949        assert_eq!(
2950            worktree
2951                .paths()
2952                .map(|p| p.to_string_lossy())
2953                .collect::<Vec<_>>(),
2954            ["a.txt", "b.txt", "f.txt"]
2955        );
2956    });
2957    worktree_b.read_with(cx_b, |worktree, _| {
2958        assert_eq!(
2959            worktree
2960                .paths()
2961                .map(|p| p.to_string_lossy())
2962                .collect::<Vec<_>>(),
2963            ["a.txt", "b.txt", "f.txt"]
2964        );
2965    });
2966}
2967
2968#[gpui::test(iterations = 10)]
2969async fn test_buffer_conflict_after_save(
2970    deterministic: Arc<Deterministic>,
2971    cx_a: &mut TestAppContext,
2972    cx_b: &mut TestAppContext,
2973) {
2974    deterministic.forbid_parking();
2975    let mut server = TestServer::start(&deterministic).await;
2976    let client_a = server.create_client(cx_a, "user_a").await;
2977    let client_b = server.create_client(cx_b, "user_b").await;
2978    server
2979        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2980        .await;
2981    let active_call_a = cx_a.read(ActiveCall::global);
2982
2983    client_a
2984        .fs
2985        .insert_tree(
2986            "/dir",
2987            json!({
2988                "a.txt": "a-contents",
2989            }),
2990        )
2991        .await;
2992    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2993    let project_id = active_call_a
2994        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2995        .await
2996        .unwrap();
2997    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2998
2999    // Open a buffer as client B
3000    let buffer_b = project_b
3001        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3002        .await
3003        .unwrap();
3004
3005    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "world ")], None, cx));
3006    buffer_b.read_with(cx_b, |buf, _| {
3007        assert!(buf.is_dirty());
3008        assert!(!buf.has_conflict());
3009    });
3010
3011    project_b
3012        .update(cx_b, |project, cx| {
3013            project.save_buffer(buffer_b.clone(), cx)
3014        })
3015        .await
3016        .unwrap();
3017    cx_a.foreground().forbid_parking();
3018    buffer_b.read_with(cx_b, |buffer_b, _| assert!(!buffer_b.is_dirty()));
3019    buffer_b.read_with(cx_b, |buf, _| {
3020        assert!(!buf.has_conflict());
3021    });
3022
3023    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "hello ")], None, cx));
3024    buffer_b.read_with(cx_b, |buf, _| {
3025        assert!(buf.is_dirty());
3026        assert!(!buf.has_conflict());
3027    });
3028}
3029
3030#[gpui::test(iterations = 10)]
3031async fn test_buffer_reloading(
3032    deterministic: Arc<Deterministic>,
3033    cx_a: &mut TestAppContext,
3034    cx_b: &mut TestAppContext,
3035) {
3036    deterministic.forbid_parking();
3037    let mut server = TestServer::start(&deterministic).await;
3038    let client_a = server.create_client(cx_a, "user_a").await;
3039    let client_b = server.create_client(cx_b, "user_b").await;
3040    server
3041        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3042        .await;
3043    let active_call_a = cx_a.read(ActiveCall::global);
3044
3045    client_a
3046        .fs
3047        .insert_tree(
3048            "/dir",
3049            json!({
3050                "a.txt": "a\nb\nc",
3051            }),
3052        )
3053        .await;
3054    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3055    let project_id = active_call_a
3056        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3057        .await
3058        .unwrap();
3059    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3060
3061    // Open a buffer as client B
3062    let buffer_b = project_b
3063        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3064        .await
3065        .unwrap();
3066    buffer_b.read_with(cx_b, |buf, _| {
3067        assert!(!buf.is_dirty());
3068        assert!(!buf.has_conflict());
3069        assert_eq!(buf.line_ending(), LineEnding::Unix);
3070    });
3071
3072    let new_contents = Rope::from("d\ne\nf");
3073    client_a
3074        .fs
3075        .save("/dir/a.txt".as_ref(), &new_contents, LineEnding::Windows)
3076        .await
3077        .unwrap();
3078    cx_a.foreground().run_until_parked();
3079    buffer_b.read_with(cx_b, |buf, _| {
3080        assert_eq!(buf.text(), new_contents.to_string());
3081        assert!(!buf.is_dirty());
3082        assert!(!buf.has_conflict());
3083        assert_eq!(buf.line_ending(), LineEnding::Windows);
3084    });
3085}
3086
3087#[gpui::test(iterations = 10)]
3088async fn test_editing_while_guest_opens_buffer(
3089    deterministic: Arc<Deterministic>,
3090    cx_a: &mut TestAppContext,
3091    cx_b: &mut TestAppContext,
3092) {
3093    deterministic.forbid_parking();
3094    let mut server = TestServer::start(&deterministic).await;
3095    let client_a = server.create_client(cx_a, "user_a").await;
3096    let client_b = server.create_client(cx_b, "user_b").await;
3097    server
3098        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3099        .await;
3100    let active_call_a = cx_a.read(ActiveCall::global);
3101
3102    client_a
3103        .fs
3104        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3105        .await;
3106    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3107    let project_id = active_call_a
3108        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3109        .await
3110        .unwrap();
3111    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3112
3113    // Open a buffer as client A
3114    let buffer_a = project_a
3115        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3116        .await
3117        .unwrap();
3118
3119    // Start opening the same buffer as client B
3120    let buffer_b = cx_b
3121        .background()
3122        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)));
3123
3124    // Edit the buffer as client A while client B is still opening it.
3125    cx_b.background().simulate_random_delay().await;
3126    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "X")], None, cx));
3127    cx_b.background().simulate_random_delay().await;
3128    buffer_a.update(cx_a, |buf, cx| buf.edit([(1..1, "Y")], None, cx));
3129
3130    let text = buffer_a.read_with(cx_a, |buf, _| buf.text());
3131    let buffer_b = buffer_b.await.unwrap();
3132    cx_a.foreground().run_until_parked();
3133    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), text));
3134}
3135
3136#[gpui::test]
3137async fn test_newline_above_or_below_does_not_move_guest_cursor(
3138    deterministic: Arc<Deterministic>,
3139    cx_a: &mut TestAppContext,
3140    cx_b: &mut TestAppContext,
3141) {
3142    deterministic.forbid_parking();
3143    let mut server = TestServer::start(&deterministic).await;
3144    let client_a = server.create_client(cx_a, "user_a").await;
3145    let client_b = server.create_client(cx_b, "user_b").await;
3146    server
3147        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3148        .await;
3149    let active_call_a = cx_a.read(ActiveCall::global);
3150
3151    client_a
3152        .fs
3153        .insert_tree("/dir", json!({ "a.txt": "Some text\n" }))
3154        .await;
3155    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3156    let project_id = active_call_a
3157        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3158        .await
3159        .unwrap();
3160
3161    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3162
3163    // Open a buffer as client A
3164    let buffer_a = project_a
3165        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3166        .await
3167        .unwrap();
3168    let (window_a, _) = cx_a.add_window(|_| EmptyView);
3169    let editor_a = cx_a.add_view(window_a, |cx| {
3170        Editor::for_buffer(buffer_a, Some(project_a), cx)
3171    });
3172    let mut editor_cx_a = EditorTestContext {
3173        cx: cx_a,
3174        window_id: window_a,
3175        editor: editor_a,
3176    };
3177
3178    // Open a buffer as client B
3179    let buffer_b = project_b
3180        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3181        .await
3182        .unwrap();
3183    let (window_b, _) = cx_b.add_window(|_| EmptyView);
3184    let editor_b = cx_b.add_view(window_b, |cx| {
3185        Editor::for_buffer(buffer_b, Some(project_b), cx)
3186    });
3187    let mut editor_cx_b = EditorTestContext {
3188        cx: cx_b,
3189        window_id: window_b,
3190        editor: editor_b,
3191    };
3192
3193    // Test newline above
3194    editor_cx_a.set_selections_state(indoc! {"
3195        Some textˇ
3196    "});
3197    editor_cx_b.set_selections_state(indoc! {"
3198        Some textˇ
3199    "});
3200    editor_cx_a.update_editor(|editor, cx| editor.newline_above(&editor::NewlineAbove, cx));
3201    deterministic.run_until_parked();
3202    editor_cx_a.assert_editor_state(indoc! {"
3203        ˇ
3204        Some text
3205    "});
3206    editor_cx_b.assert_editor_state(indoc! {"
3207
3208        Some textˇ
3209    "});
3210
3211    // Test newline below
3212    editor_cx_a.set_selections_state(indoc! {"
3213
3214        Some textˇ
3215    "});
3216    editor_cx_b.set_selections_state(indoc! {"
3217
3218        Some textˇ
3219    "});
3220    editor_cx_a.update_editor(|editor, cx| editor.newline_below(&editor::NewlineBelow, cx));
3221    deterministic.run_until_parked();
3222    editor_cx_a.assert_editor_state(indoc! {"
3223
3224        Some text
3225        ˇ
3226    "});
3227    editor_cx_b.assert_editor_state(indoc! {"
3228
3229        Some textˇ
3230
3231    "});
3232}
3233
3234#[gpui::test(iterations = 10)]
3235async fn test_leaving_worktree_while_opening_buffer(
3236    deterministic: Arc<Deterministic>,
3237    cx_a: &mut TestAppContext,
3238    cx_b: &mut TestAppContext,
3239) {
3240    deterministic.forbid_parking();
3241    let mut server = TestServer::start(&deterministic).await;
3242    let client_a = server.create_client(cx_a, "user_a").await;
3243    let client_b = server.create_client(cx_b, "user_b").await;
3244    server
3245        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3246        .await;
3247    let active_call_a = cx_a.read(ActiveCall::global);
3248
3249    client_a
3250        .fs
3251        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3252        .await;
3253    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3254    let project_id = active_call_a
3255        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3256        .await
3257        .unwrap();
3258    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3259
3260    // See that a guest has joined as client A.
3261    cx_a.foreground().run_until_parked();
3262    project_a.read_with(cx_a, |p, _| assert_eq!(p.collaborators().len(), 1));
3263
3264    // Begin opening a buffer as client B, but leave the project before the open completes.
3265    let buffer_b = cx_b
3266        .background()
3267        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)));
3268    cx_b.update(|_| drop(project_b));
3269    drop(buffer_b);
3270
3271    // See that the guest has left.
3272    cx_a.foreground().run_until_parked();
3273    project_a.read_with(cx_a, |p, _| assert!(p.collaborators().is_empty()));
3274}
3275
3276#[gpui::test(iterations = 10)]
3277async fn test_canceling_buffer_opening(
3278    deterministic: Arc<Deterministic>,
3279    cx_a: &mut TestAppContext,
3280    cx_b: &mut TestAppContext,
3281) {
3282    deterministic.forbid_parking();
3283
3284    let mut server = TestServer::start(&deterministic).await;
3285    let client_a = server.create_client(cx_a, "user_a").await;
3286    let client_b = server.create_client(cx_b, "user_b").await;
3287    server
3288        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3289        .await;
3290    let active_call_a = cx_a.read(ActiveCall::global);
3291
3292    client_a
3293        .fs
3294        .insert_tree(
3295            "/dir",
3296            json!({
3297                "a.txt": "abc",
3298            }),
3299        )
3300        .await;
3301    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3302    let project_id = active_call_a
3303        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3304        .await
3305        .unwrap();
3306    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3307
3308    let buffer_a = project_a
3309        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3310        .await
3311        .unwrap();
3312
3313    // Open a buffer as client B but cancel after a random amount of time.
3314    let buffer_b = project_b.update(cx_b, |p, cx| {
3315        p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3316    });
3317    deterministic.simulate_random_delay().await;
3318    drop(buffer_b);
3319
3320    // Try opening the same buffer again as client B, and ensure we can
3321    // still do it despite the cancellation above.
3322    let buffer_b = project_b
3323        .update(cx_b, |p, cx| {
3324            p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3325        })
3326        .await
3327        .unwrap();
3328    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "abc"));
3329}
3330
3331#[gpui::test(iterations = 10)]
3332async fn test_leaving_project(
3333    deterministic: Arc<Deterministic>,
3334    cx_a: &mut TestAppContext,
3335    cx_b: &mut TestAppContext,
3336    cx_c: &mut TestAppContext,
3337) {
3338    deterministic.forbid_parking();
3339    let mut server = TestServer::start(&deterministic).await;
3340    let client_a = server.create_client(cx_a, "user_a").await;
3341    let client_b = server.create_client(cx_b, "user_b").await;
3342    let client_c = server.create_client(cx_c, "user_c").await;
3343    server
3344        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3345        .await;
3346    let active_call_a = cx_a.read(ActiveCall::global);
3347
3348    client_a
3349        .fs
3350        .insert_tree(
3351            "/a",
3352            json!({
3353                "a.txt": "a-contents",
3354                "b.txt": "b-contents",
3355            }),
3356        )
3357        .await;
3358    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
3359    let project_id = active_call_a
3360        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3361        .await
3362        .unwrap();
3363    let project_b1 = client_b.build_remote_project(project_id, cx_b).await;
3364    let project_c = client_c.build_remote_project(project_id, cx_c).await;
3365
3366    // Client A sees that a guest has joined.
3367    deterministic.run_until_parked();
3368    project_a.read_with(cx_a, |project, _| {
3369        assert_eq!(project.collaborators().len(), 2);
3370    });
3371    project_b1.read_with(cx_b, |project, _| {
3372        assert_eq!(project.collaborators().len(), 2);
3373    });
3374    project_c.read_with(cx_c, |project, _| {
3375        assert_eq!(project.collaborators().len(), 2);
3376    });
3377
3378    // Client B opens a buffer.
3379    let buffer_b1 = project_b1
3380        .update(cx_b, |project, cx| {
3381            let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
3382            project.open_buffer((worktree_id, "a.txt"), cx)
3383        })
3384        .await
3385        .unwrap();
3386    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3387
3388    // Drop client B's project and ensure client A and client C observe client B leaving.
3389    cx_b.update(|_| drop(project_b1));
3390    deterministic.run_until_parked();
3391    project_a.read_with(cx_a, |project, _| {
3392        assert_eq!(project.collaborators().len(), 1);
3393    });
3394    project_c.read_with(cx_c, |project, _| {
3395        assert_eq!(project.collaborators().len(), 1);
3396    });
3397
3398    // Client B re-joins the project and can open buffers as before.
3399    let project_b2 = client_b.build_remote_project(project_id, cx_b).await;
3400    deterministic.run_until_parked();
3401    project_a.read_with(cx_a, |project, _| {
3402        assert_eq!(project.collaborators().len(), 2);
3403    });
3404    project_b2.read_with(cx_b, |project, _| {
3405        assert_eq!(project.collaborators().len(), 2);
3406    });
3407    project_c.read_with(cx_c, |project, _| {
3408        assert_eq!(project.collaborators().len(), 2);
3409    });
3410
3411    let buffer_b2 = project_b2
3412        .update(cx_b, |project, cx| {
3413            let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
3414            project.open_buffer((worktree_id, "a.txt"), cx)
3415        })
3416        .await
3417        .unwrap();
3418    buffer_b2.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3419
3420    // Drop client B's connection and ensure client A and client C observe client B leaving.
3421    client_b.disconnect(&cx_b.to_async());
3422    deterministic.advance_clock(RECONNECT_TIMEOUT);
3423    project_a.read_with(cx_a, |project, _| {
3424        assert_eq!(project.collaborators().len(), 1);
3425    });
3426    project_b2.read_with(cx_b, |project, _| {
3427        assert!(project.is_read_only());
3428    });
3429    project_c.read_with(cx_c, |project, _| {
3430        assert_eq!(project.collaborators().len(), 1);
3431    });
3432
3433    // Client B can't join the project, unless they re-join the room.
3434    cx_b.spawn(|cx| {
3435        Project::remote(
3436            project_id,
3437            client_b.client.clone(),
3438            client_b.user_store.clone(),
3439            client_b.language_registry.clone(),
3440            FakeFs::new(cx.background()),
3441            cx,
3442        )
3443    })
3444    .await
3445    .unwrap_err();
3446
3447    // Simulate connection loss for client C and ensure client A observes client C leaving the project.
3448    client_c.wait_for_current_user(cx_c).await;
3449    server.forbid_connections();
3450    server.disconnect_client(client_c.peer_id().unwrap());
3451    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
3452    deterministic.run_until_parked();
3453    project_a.read_with(cx_a, |project, _| {
3454        assert_eq!(project.collaborators().len(), 0);
3455    });
3456    project_b2.read_with(cx_b, |project, _| {
3457        assert!(project.is_read_only());
3458    });
3459    project_c.read_with(cx_c, |project, _| {
3460        assert!(project.is_read_only());
3461    });
3462}
3463
3464#[gpui::test(iterations = 10)]
3465async fn test_collaborating_with_diagnostics(
3466    deterministic: Arc<Deterministic>,
3467    cx_a: &mut TestAppContext,
3468    cx_b: &mut TestAppContext,
3469    cx_c: &mut TestAppContext,
3470) {
3471    deterministic.forbid_parking();
3472    let mut server = TestServer::start(&deterministic).await;
3473    let client_a = server.create_client(cx_a, "user_a").await;
3474    let client_b = server.create_client(cx_b, "user_b").await;
3475    let client_c = server.create_client(cx_c, "user_c").await;
3476    server
3477        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3478        .await;
3479    let active_call_a = cx_a.read(ActiveCall::global);
3480
3481    // Set up a fake language server.
3482    let mut language = Language::new(
3483        LanguageConfig {
3484            name: "Rust".into(),
3485            path_suffixes: vec!["rs".to_string()],
3486            ..Default::default()
3487        },
3488        Some(tree_sitter_rust::language()),
3489    );
3490    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
3491    client_a.language_registry.add(Arc::new(language));
3492
3493    // Share a project as client A
3494    client_a
3495        .fs
3496        .insert_tree(
3497            "/a",
3498            json!({
3499                "a.rs": "let one = two",
3500                "other.rs": "",
3501            }),
3502        )
3503        .await;
3504    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3505
3506    // Cause the language server to start.
3507    let _buffer = project_a
3508        .update(cx_a, |project, cx| {
3509            project.open_buffer(
3510                ProjectPath {
3511                    worktree_id,
3512                    path: Path::new("other.rs").into(),
3513                },
3514                cx,
3515            )
3516        })
3517        .await
3518        .unwrap();
3519
3520    // Simulate a language server reporting errors for a file.
3521    let mut fake_language_server = fake_language_servers.next().await.unwrap();
3522    fake_language_server
3523        .receive_notification::<lsp::notification::DidOpenTextDocument>()
3524        .await;
3525    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3526        lsp::PublishDiagnosticsParams {
3527            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3528            version: None,
3529            diagnostics: vec![lsp::Diagnostic {
3530                severity: Some(lsp::DiagnosticSeverity::WARNING),
3531                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3532                message: "message 0".to_string(),
3533                ..Default::default()
3534            }],
3535        },
3536    );
3537
3538    // Client A shares the project and, simultaneously, the language server
3539    // publishes a diagnostic. This is done to ensure that the server always
3540    // observes the latest diagnostics for a worktree.
3541    let project_id = active_call_a
3542        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3543        .await
3544        .unwrap();
3545    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3546        lsp::PublishDiagnosticsParams {
3547            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3548            version: None,
3549            diagnostics: vec![lsp::Diagnostic {
3550                severity: Some(lsp::DiagnosticSeverity::ERROR),
3551                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3552                message: "message 1".to_string(),
3553                ..Default::default()
3554            }],
3555        },
3556    );
3557
3558    // Join the worktree as client B.
3559    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3560
3561    // Wait for server to see the diagnostics update.
3562    deterministic.run_until_parked();
3563
3564    // Ensure client B observes the new diagnostics.
3565    project_b.read_with(cx_b, |project, cx| {
3566        assert_eq!(
3567            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
3568            &[(
3569                ProjectPath {
3570                    worktree_id,
3571                    path: Arc::from(Path::new("a.rs")),
3572                },
3573                LanguageServerId(0),
3574                DiagnosticSummary {
3575                    error_count: 1,
3576                    warning_count: 0,
3577                    ..Default::default()
3578                },
3579            )]
3580        )
3581    });
3582
3583    // Join project as client C and observe the diagnostics.
3584    let project_c = client_c.build_remote_project(project_id, cx_c).await;
3585    let project_c_diagnostic_summaries =
3586        Rc::new(RefCell::new(project_c.read_with(cx_c, |project, cx| {
3587            project.diagnostic_summaries(cx).collect::<Vec<_>>()
3588        })));
3589    project_c.update(cx_c, |_, cx| {
3590        let summaries = project_c_diagnostic_summaries.clone();
3591        cx.subscribe(&project_c, {
3592            move |p, _, event, cx| {
3593                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
3594                    *summaries.borrow_mut() = p.diagnostic_summaries(cx).collect();
3595                }
3596            }
3597        })
3598        .detach();
3599    });
3600
3601    deterministic.run_until_parked();
3602    assert_eq!(
3603        project_c_diagnostic_summaries.borrow().as_slice(),
3604        &[(
3605            ProjectPath {
3606                worktree_id,
3607                path: Arc::from(Path::new("a.rs")),
3608            },
3609            LanguageServerId(0),
3610            DiagnosticSummary {
3611                error_count: 1,
3612                warning_count: 0,
3613                ..Default::default()
3614            },
3615        )]
3616    );
3617
3618    // Simulate a language server reporting more errors for a file.
3619    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3620        lsp::PublishDiagnosticsParams {
3621            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3622            version: None,
3623            diagnostics: vec![
3624                lsp::Diagnostic {
3625                    severity: Some(lsp::DiagnosticSeverity::ERROR),
3626                    range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3627                    message: "message 1".to_string(),
3628                    ..Default::default()
3629                },
3630                lsp::Diagnostic {
3631                    severity: Some(lsp::DiagnosticSeverity::WARNING),
3632                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 13)),
3633                    message: "message 2".to_string(),
3634                    ..Default::default()
3635                },
3636            ],
3637        },
3638    );
3639
3640    // Clients B and C get the updated summaries
3641    deterministic.run_until_parked();
3642    project_b.read_with(cx_b, |project, cx| {
3643        assert_eq!(
3644            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
3645            [(
3646                ProjectPath {
3647                    worktree_id,
3648                    path: Arc::from(Path::new("a.rs")),
3649                },
3650                LanguageServerId(0),
3651                DiagnosticSummary {
3652                    error_count: 1,
3653                    warning_count: 1,
3654                },
3655            )]
3656        );
3657    });
3658    project_c.read_with(cx_c, |project, cx| {
3659        assert_eq!(
3660            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
3661            [(
3662                ProjectPath {
3663                    worktree_id,
3664                    path: Arc::from(Path::new("a.rs")),
3665                },
3666                LanguageServerId(0),
3667                DiagnosticSummary {
3668                    error_count: 1,
3669                    warning_count: 1,
3670                },
3671            )]
3672        );
3673    });
3674
3675    // Open the file with the errors on client B. They should be present.
3676    let buffer_b = cx_b
3677        .background()
3678        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
3679        .await
3680        .unwrap();
3681
3682    buffer_b.read_with(cx_b, |buffer, _| {
3683        assert_eq!(
3684            buffer
3685                .snapshot()
3686                .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
3687                .collect::<Vec<_>>(),
3688            &[
3689                DiagnosticEntry {
3690                    range: Point::new(0, 4)..Point::new(0, 7),
3691                    diagnostic: Diagnostic {
3692                        group_id: 2,
3693                        message: "message 1".to_string(),
3694                        severity: lsp::DiagnosticSeverity::ERROR,
3695                        is_primary: true,
3696                        ..Default::default()
3697                    }
3698                },
3699                DiagnosticEntry {
3700                    range: Point::new(0, 10)..Point::new(0, 13),
3701                    diagnostic: Diagnostic {
3702                        group_id: 3,
3703                        severity: lsp::DiagnosticSeverity::WARNING,
3704                        message: "message 2".to_string(),
3705                        is_primary: true,
3706                        ..Default::default()
3707                    }
3708                }
3709            ]
3710        );
3711    });
3712
3713    // Simulate a language server reporting no errors for a file.
3714    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3715        lsp::PublishDiagnosticsParams {
3716            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3717            version: None,
3718            diagnostics: vec![],
3719        },
3720    );
3721    deterministic.run_until_parked();
3722    project_a.read_with(cx_a, |project, cx| {
3723        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
3724    });
3725    project_b.read_with(cx_b, |project, cx| {
3726        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
3727    });
3728    project_c.read_with(cx_c, |project, cx| {
3729        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
3730    });
3731}
3732
3733#[gpui::test(iterations = 10)]
3734async fn test_collaborating_with_lsp_progress_updates_and_diagnostics_ordering(
3735    deterministic: Arc<Deterministic>,
3736    cx_a: &mut TestAppContext,
3737    cx_b: &mut TestAppContext,
3738) {
3739    deterministic.forbid_parking();
3740    let mut server = TestServer::start(&deterministic).await;
3741    let client_a = server.create_client(cx_a, "user_a").await;
3742    let client_b = server.create_client(cx_b, "user_b").await;
3743    server
3744        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3745        .await;
3746
3747    // Set up a fake language server.
3748    let mut language = Language::new(
3749        LanguageConfig {
3750            name: "Rust".into(),
3751            path_suffixes: vec!["rs".to_string()],
3752            ..Default::default()
3753        },
3754        Some(tree_sitter_rust::language()),
3755    );
3756    let mut fake_language_servers = language
3757        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3758            disk_based_diagnostics_progress_token: Some("the-disk-based-token".into()),
3759            disk_based_diagnostics_sources: vec!["the-disk-based-diagnostics-source".into()],
3760            ..Default::default()
3761        }))
3762        .await;
3763    client_a.language_registry.add(Arc::new(language));
3764
3765    let file_names = &["one.rs", "two.rs", "three.rs", "four.rs", "five.rs"];
3766    client_a
3767        .fs
3768        .insert_tree(
3769            "/test",
3770            json!({
3771                "one.rs": "const ONE: usize = 1;",
3772                "two.rs": "const TWO: usize = 2;",
3773                "three.rs": "const THREE: usize = 3;",
3774                "four.rs": "const FOUR: usize = 3;",
3775                "five.rs": "const FIVE: usize = 3;",
3776            }),
3777        )
3778        .await;
3779
3780    let (project_a, worktree_id) = client_a.build_local_project("/test", cx_a).await;
3781
3782    // Share a project as client A
3783    let active_call_a = cx_a.read(ActiveCall::global);
3784    let project_id = active_call_a
3785        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3786        .await
3787        .unwrap();
3788
3789    // Join the project as client B and open all three files.
3790    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3791    let guest_buffers = futures::future::try_join_all(file_names.iter().map(|file_name| {
3792        project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, file_name), cx))
3793    }))
3794    .await
3795    .unwrap();
3796
3797    // Simulate a language server reporting errors for a file.
3798    let fake_language_server = fake_language_servers.next().await.unwrap();
3799    fake_language_server
3800        .request::<lsp::request::WorkDoneProgressCreate>(lsp::WorkDoneProgressCreateParams {
3801            token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
3802        })
3803        .await
3804        .unwrap();
3805    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
3806        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
3807        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin(
3808            lsp::WorkDoneProgressBegin {
3809                title: "Progress Began".into(),
3810                ..Default::default()
3811            },
3812        )),
3813    });
3814    for file_name in file_names {
3815        fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3816            lsp::PublishDiagnosticsParams {
3817                uri: lsp::Url::from_file_path(Path::new("/test").join(file_name)).unwrap(),
3818                version: None,
3819                diagnostics: vec![lsp::Diagnostic {
3820                    severity: Some(lsp::DiagnosticSeverity::WARNING),
3821                    source: Some("the-disk-based-diagnostics-source".into()),
3822                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
3823                    message: "message one".to_string(),
3824                    ..Default::default()
3825                }],
3826            },
3827        );
3828    }
3829    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
3830        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
3831        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End(
3832            lsp::WorkDoneProgressEnd { message: None },
3833        )),
3834    });
3835
3836    // When the "disk base diagnostics finished" message is received, the buffers'
3837    // diagnostics are expected to be present.
3838    let disk_based_diagnostics_finished = Arc::new(AtomicBool::new(false));
3839    project_b.update(cx_b, {
3840        let project_b = project_b.clone();
3841        let disk_based_diagnostics_finished = disk_based_diagnostics_finished.clone();
3842        move |_, cx| {
3843            cx.subscribe(&project_b, move |_, _, event, cx| {
3844                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
3845                    disk_based_diagnostics_finished.store(true, SeqCst);
3846                    for buffer in &guest_buffers {
3847                        assert_eq!(
3848                            buffer
3849                                .read(cx)
3850                                .snapshot()
3851                                .diagnostics_in_range::<_, usize>(0..5, false)
3852                                .count(),
3853                            1,
3854                            "expected a diagnostic for buffer {:?}",
3855                            buffer.read(cx).file().unwrap().path(),
3856                        );
3857                    }
3858                }
3859            })
3860            .detach();
3861        }
3862    });
3863
3864    deterministic.run_until_parked();
3865    assert!(disk_based_diagnostics_finished.load(SeqCst));
3866}
3867
3868#[gpui::test(iterations = 10)]
3869async fn test_collaborating_with_completion(
3870    deterministic: Arc<Deterministic>,
3871    cx_a: &mut TestAppContext,
3872    cx_b: &mut TestAppContext,
3873) {
3874    deterministic.forbid_parking();
3875    let mut server = TestServer::start(&deterministic).await;
3876    let client_a = server.create_client(cx_a, "user_a").await;
3877    let client_b = server.create_client(cx_b, "user_b").await;
3878    server
3879        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3880        .await;
3881    let active_call_a = cx_a.read(ActiveCall::global);
3882
3883    // Set up a fake language server.
3884    let mut language = Language::new(
3885        LanguageConfig {
3886            name: "Rust".into(),
3887            path_suffixes: vec!["rs".to_string()],
3888            ..Default::default()
3889        },
3890        Some(tree_sitter_rust::language()),
3891    );
3892    let mut fake_language_servers = language
3893        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3894            capabilities: lsp::ServerCapabilities {
3895                completion_provider: Some(lsp::CompletionOptions {
3896                    trigger_characters: Some(vec![".".to_string()]),
3897                    ..Default::default()
3898                }),
3899                ..Default::default()
3900            },
3901            ..Default::default()
3902        }))
3903        .await;
3904    client_a.language_registry.add(Arc::new(language));
3905
3906    client_a
3907        .fs
3908        .insert_tree(
3909            "/a",
3910            json!({
3911                "main.rs": "fn main() { a }",
3912                "other.rs": "",
3913            }),
3914        )
3915        .await;
3916    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3917    let project_id = active_call_a
3918        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3919        .await
3920        .unwrap();
3921    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3922
3923    // Open a file in an editor as the guest.
3924    let buffer_b = project_b
3925        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
3926        .await
3927        .unwrap();
3928    let (window_b, _) = cx_b.add_window(|_| EmptyView);
3929    let editor_b = cx_b.add_view(window_b, |cx| {
3930        Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx)
3931    });
3932
3933    let fake_language_server = fake_language_servers.next().await.unwrap();
3934    cx_a.foreground().run_until_parked();
3935    buffer_b.read_with(cx_b, |buffer, _| {
3936        assert!(!buffer.completion_triggers().is_empty())
3937    });
3938
3939    // Type a completion trigger character as the guest.
3940    editor_b.update(cx_b, |editor, cx| {
3941        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
3942        editor.handle_input(".", cx);
3943        cx.focus(&editor_b);
3944    });
3945
3946    // Receive a completion request as the host's language server.
3947    // Return some completions from the host's language server.
3948    cx_a.foreground().start_waiting();
3949    fake_language_server
3950        .handle_request::<lsp::request::Completion, _, _>(|params, _| async move {
3951            assert_eq!(
3952                params.text_document_position.text_document.uri,
3953                lsp::Url::from_file_path("/a/main.rs").unwrap(),
3954            );
3955            assert_eq!(
3956                params.text_document_position.position,
3957                lsp::Position::new(0, 14),
3958            );
3959
3960            Ok(Some(lsp::CompletionResponse::Array(vec![
3961                lsp::CompletionItem {
3962                    label: "first_method(…)".into(),
3963                    detail: Some("fn(&mut self, B) -> C".into()),
3964                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
3965                        new_text: "first_method($1)".to_string(),
3966                        range: lsp::Range::new(
3967                            lsp::Position::new(0, 14),
3968                            lsp::Position::new(0, 14),
3969                        ),
3970                    })),
3971                    insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
3972                    ..Default::default()
3973                },
3974                lsp::CompletionItem {
3975                    label: "second_method(…)".into(),
3976                    detail: Some("fn(&mut self, C) -> D<E>".into()),
3977                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
3978                        new_text: "second_method()".to_string(),
3979                        range: lsp::Range::new(
3980                            lsp::Position::new(0, 14),
3981                            lsp::Position::new(0, 14),
3982                        ),
3983                    })),
3984                    insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
3985                    ..Default::default()
3986                },
3987            ])))
3988        })
3989        .next()
3990        .await
3991        .unwrap();
3992    cx_a.foreground().finish_waiting();
3993
3994    // Open the buffer on the host.
3995    let buffer_a = project_a
3996        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
3997        .await
3998        .unwrap();
3999    cx_a.foreground().run_until_parked();
4000    buffer_a.read_with(cx_a, |buffer, _| {
4001        assert_eq!(buffer.text(), "fn main() { a. }")
4002    });
4003
4004    // Confirm a completion on the guest.
4005    editor_b.read_with(cx_b, |editor, _| assert!(editor.context_menu_visible()));
4006    editor_b.update(cx_b, |editor, cx| {
4007        editor.confirm_completion(&ConfirmCompletion { item_ix: Some(0) }, cx);
4008        assert_eq!(editor.text(cx), "fn main() { a.first_method() }");
4009    });
4010
4011    // Return a resolved completion from the host's language server.
4012    // The resolved completion has an additional text edit.
4013    fake_language_server.handle_request::<lsp::request::ResolveCompletionItem, _, _>(
4014        |params, _| async move {
4015            assert_eq!(params.label, "first_method(…)");
4016            Ok(lsp::CompletionItem {
4017                label: "first_method(…)".into(),
4018                detail: Some("fn(&mut self, B) -> C".into()),
4019                text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
4020                    new_text: "first_method($1)".to_string(),
4021                    range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
4022                })),
4023                additional_text_edits: Some(vec![lsp::TextEdit {
4024                    new_text: "use d::SomeTrait;\n".to_string(),
4025                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
4026                }]),
4027                insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
4028                ..Default::default()
4029            })
4030        },
4031    );
4032
4033    // The additional edit is applied.
4034    cx_a.foreground().run_until_parked();
4035    buffer_a.read_with(cx_a, |buffer, _| {
4036        assert_eq!(
4037            buffer.text(),
4038            "use d::SomeTrait;\nfn main() { a.first_method() }"
4039        );
4040    });
4041    buffer_b.read_with(cx_b, |buffer, _| {
4042        assert_eq!(
4043            buffer.text(),
4044            "use d::SomeTrait;\nfn main() { a.first_method() }"
4045        );
4046    });
4047}
4048
4049#[gpui::test(iterations = 10)]
4050async fn test_reloading_buffer_manually(
4051    deterministic: Arc<Deterministic>,
4052    cx_a: &mut TestAppContext,
4053    cx_b: &mut TestAppContext,
4054) {
4055    deterministic.forbid_parking();
4056    let mut server = TestServer::start(&deterministic).await;
4057    let client_a = server.create_client(cx_a, "user_a").await;
4058    let client_b = server.create_client(cx_b, "user_b").await;
4059    server
4060        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4061        .await;
4062    let active_call_a = cx_a.read(ActiveCall::global);
4063
4064    client_a
4065        .fs
4066        .insert_tree("/a", json!({ "a.rs": "let one = 1;" }))
4067        .await;
4068    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
4069    let buffer_a = project_a
4070        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
4071        .await
4072        .unwrap();
4073    let project_id = active_call_a
4074        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4075        .await
4076        .unwrap();
4077
4078    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4079
4080    let buffer_b = cx_b
4081        .background()
4082        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4083        .await
4084        .unwrap();
4085    buffer_b.update(cx_b, |buffer, cx| {
4086        buffer.edit([(4..7, "six")], None, cx);
4087        buffer.edit([(10..11, "6")], None, cx);
4088        assert_eq!(buffer.text(), "let six = 6;");
4089        assert!(buffer.is_dirty());
4090        assert!(!buffer.has_conflict());
4091    });
4092    cx_a.foreground().run_until_parked();
4093    buffer_a.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "let six = 6;"));
4094
4095    client_a
4096        .fs
4097        .save(
4098            "/a/a.rs".as_ref(),
4099            &Rope::from("let seven = 7;"),
4100            LineEnding::Unix,
4101        )
4102        .await
4103        .unwrap();
4104    cx_a.foreground().run_until_parked();
4105    buffer_a.read_with(cx_a, |buffer, _| assert!(buffer.has_conflict()));
4106    buffer_b.read_with(cx_b, |buffer, _| assert!(buffer.has_conflict()));
4107
4108    project_b
4109        .update(cx_b, |project, cx| {
4110            project.reload_buffers(HashSet::from_iter([buffer_b.clone()]), true, cx)
4111        })
4112        .await
4113        .unwrap();
4114    buffer_a.read_with(cx_a, |buffer, _| {
4115        assert_eq!(buffer.text(), "let seven = 7;");
4116        assert!(!buffer.is_dirty());
4117        assert!(!buffer.has_conflict());
4118    });
4119    buffer_b.read_with(cx_b, |buffer, _| {
4120        assert_eq!(buffer.text(), "let seven = 7;");
4121        assert!(!buffer.is_dirty());
4122        assert!(!buffer.has_conflict());
4123    });
4124
4125    buffer_a.update(cx_a, |buffer, cx| {
4126        // Undoing on the host is a no-op when the reload was initiated by the guest.
4127        buffer.undo(cx);
4128        assert_eq!(buffer.text(), "let seven = 7;");
4129        assert!(!buffer.is_dirty());
4130        assert!(!buffer.has_conflict());
4131    });
4132    buffer_b.update(cx_b, |buffer, cx| {
4133        // Undoing on the guest rolls back the buffer to before it was reloaded but the conflict gets cleared.
4134        buffer.undo(cx);
4135        assert_eq!(buffer.text(), "let six = 6;");
4136        assert!(buffer.is_dirty());
4137        assert!(!buffer.has_conflict());
4138    });
4139}
4140
4141#[gpui::test(iterations = 10)]
4142async fn test_formatting_buffer(
4143    deterministic: Arc<Deterministic>,
4144    cx_a: &mut TestAppContext,
4145    cx_b: &mut TestAppContext,
4146) {
4147    use project::FormatTrigger;
4148
4149    let mut server = TestServer::start(&deterministic).await;
4150    let client_a = server.create_client(cx_a, "user_a").await;
4151    let client_b = server.create_client(cx_b, "user_b").await;
4152    server
4153        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4154        .await;
4155    let active_call_a = cx_a.read(ActiveCall::global);
4156
4157    // Set up a fake language server.
4158    let mut language = Language::new(
4159        LanguageConfig {
4160            name: "Rust".into(),
4161            path_suffixes: vec!["rs".to_string()],
4162            ..Default::default()
4163        },
4164        Some(tree_sitter_rust::language()),
4165    );
4166    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4167    client_a.language_registry.add(Arc::new(language));
4168
4169    // Here we insert a fake tree with a directory that exists on disk. This is needed
4170    // because later we'll invoke a command, which requires passing a working directory
4171    // that points to a valid location on disk.
4172    let directory = env::current_dir().unwrap();
4173    client_a
4174        .fs
4175        .insert_tree(&directory, json!({ "a.rs": "let one = \"two\"" }))
4176        .await;
4177    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
4178    let project_id = active_call_a
4179        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4180        .await
4181        .unwrap();
4182    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4183
4184    let buffer_b = cx_b
4185        .background()
4186        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4187        .await
4188        .unwrap();
4189
4190    let fake_language_server = fake_language_servers.next().await.unwrap();
4191    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
4192        Ok(Some(vec![
4193            lsp::TextEdit {
4194                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 4)),
4195                new_text: "h".to_string(),
4196            },
4197            lsp::TextEdit {
4198                range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 7)),
4199                new_text: "y".to_string(),
4200            },
4201        ]))
4202    });
4203
4204    project_b
4205        .update(cx_b, |project, cx| {
4206            project.format(
4207                HashSet::from_iter([buffer_b.clone()]),
4208                true,
4209                FormatTrigger::Save,
4210                cx,
4211            )
4212        })
4213        .await
4214        .unwrap();
4215
4216    // The edits from the LSP are applied, and a final newline is added.
4217    assert_eq!(
4218        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4219        "let honey = \"two\"\n"
4220    );
4221
4222    // Ensure buffer can be formatted using an external command. Notice how the
4223    // host's configuration is honored as opposed to using the guest's settings.
4224    cx_a.update(|cx| {
4225        cx.update_global(|store: &mut SettingsStore, cx| {
4226            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4227                file.defaults.formatter = Some(Formatter::External {
4228                    command: "awk".into(),
4229                    arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()].into(),
4230                });
4231            });
4232        });
4233    });
4234    project_b
4235        .update(cx_b, |project, cx| {
4236            project.format(
4237                HashSet::from_iter([buffer_b.clone()]),
4238                true,
4239                FormatTrigger::Save,
4240                cx,
4241            )
4242        })
4243        .await
4244        .unwrap();
4245    assert_eq!(
4246        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4247        format!("let honey = \"{}/a.rs\"\n", directory.to_str().unwrap())
4248    );
4249}
4250
4251#[gpui::test(iterations = 10)]
4252async fn test_definition(
4253    deterministic: Arc<Deterministic>,
4254    cx_a: &mut TestAppContext,
4255    cx_b: &mut TestAppContext,
4256) {
4257    deterministic.forbid_parking();
4258    let mut server = TestServer::start(&deterministic).await;
4259    let client_a = server.create_client(cx_a, "user_a").await;
4260    let client_b = server.create_client(cx_b, "user_b").await;
4261    server
4262        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4263        .await;
4264    let active_call_a = cx_a.read(ActiveCall::global);
4265
4266    // Set up a fake language server.
4267    let mut language = Language::new(
4268        LanguageConfig {
4269            name: "Rust".into(),
4270            path_suffixes: vec!["rs".to_string()],
4271            ..Default::default()
4272        },
4273        Some(tree_sitter_rust::language()),
4274    );
4275    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4276    client_a.language_registry.add(Arc::new(language));
4277
4278    client_a
4279        .fs
4280        .insert_tree(
4281            "/root",
4282            json!({
4283                "dir-1": {
4284                    "a.rs": "const ONE: usize = b::TWO + b::THREE;",
4285                },
4286                "dir-2": {
4287                    "b.rs": "const TWO: c::T2 = 2;\nconst THREE: usize = 3;",
4288                    "c.rs": "type T2 = usize;",
4289                }
4290            }),
4291        )
4292        .await;
4293    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4294    let project_id = active_call_a
4295        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4296        .await
4297        .unwrap();
4298    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4299
4300    // Open the file on client B.
4301    let buffer_b = cx_b
4302        .background()
4303        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4304        .await
4305        .unwrap();
4306
4307    // Request the definition of a symbol as the guest.
4308    let fake_language_server = fake_language_servers.next().await.unwrap();
4309    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4310        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4311            lsp::Location::new(
4312                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4313                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4314            ),
4315        )))
4316    });
4317
4318    let definitions_1 = project_b
4319        .update(cx_b, |p, cx| p.definition(&buffer_b, 23, cx))
4320        .await
4321        .unwrap();
4322    cx_b.read(|cx| {
4323        assert_eq!(definitions_1.len(), 1);
4324        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4325        let target_buffer = definitions_1[0].target.buffer.read(cx);
4326        assert_eq!(
4327            target_buffer.text(),
4328            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4329        );
4330        assert_eq!(
4331            definitions_1[0].target.range.to_point(target_buffer),
4332            Point::new(0, 6)..Point::new(0, 9)
4333        );
4334    });
4335
4336    // Try getting more definitions for the same buffer, ensuring the buffer gets reused from
4337    // the previous call to `definition`.
4338    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4339        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4340            lsp::Location::new(
4341                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4342                lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)),
4343            ),
4344        )))
4345    });
4346
4347    let definitions_2 = project_b
4348        .update(cx_b, |p, cx| p.definition(&buffer_b, 33, cx))
4349        .await
4350        .unwrap();
4351    cx_b.read(|cx| {
4352        assert_eq!(definitions_2.len(), 1);
4353        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4354        let target_buffer = definitions_2[0].target.buffer.read(cx);
4355        assert_eq!(
4356            target_buffer.text(),
4357            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4358        );
4359        assert_eq!(
4360            definitions_2[0].target.range.to_point(target_buffer),
4361            Point::new(1, 6)..Point::new(1, 11)
4362        );
4363    });
4364    assert_eq!(
4365        definitions_1[0].target.buffer,
4366        definitions_2[0].target.buffer
4367    );
4368
4369    fake_language_server.handle_request::<lsp::request::GotoTypeDefinition, _, _>(
4370        |req, _| async move {
4371            assert_eq!(
4372                req.text_document_position_params.position,
4373                lsp::Position::new(0, 7)
4374            );
4375            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4376                lsp::Location::new(
4377                    lsp::Url::from_file_path("/root/dir-2/c.rs").unwrap(),
4378                    lsp::Range::new(lsp::Position::new(0, 5), lsp::Position::new(0, 7)),
4379                ),
4380            )))
4381        },
4382    );
4383
4384    let type_definitions = project_b
4385        .update(cx_b, |p, cx| p.type_definition(&buffer_b, 7, cx))
4386        .await
4387        .unwrap();
4388    cx_b.read(|cx| {
4389        assert_eq!(type_definitions.len(), 1);
4390        let target_buffer = type_definitions[0].target.buffer.read(cx);
4391        assert_eq!(target_buffer.text(), "type T2 = usize;");
4392        assert_eq!(
4393            type_definitions[0].target.range.to_point(target_buffer),
4394            Point::new(0, 5)..Point::new(0, 7)
4395        );
4396    });
4397}
4398
4399#[gpui::test(iterations = 10)]
4400async fn test_references(
4401    deterministic: Arc<Deterministic>,
4402    cx_a: &mut TestAppContext,
4403    cx_b: &mut TestAppContext,
4404) {
4405    deterministic.forbid_parking();
4406    let mut server = TestServer::start(&deterministic).await;
4407    let client_a = server.create_client(cx_a, "user_a").await;
4408    let client_b = server.create_client(cx_b, "user_b").await;
4409    server
4410        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4411        .await;
4412    let active_call_a = cx_a.read(ActiveCall::global);
4413
4414    // Set up a fake language server.
4415    let mut language = Language::new(
4416        LanguageConfig {
4417            name: "Rust".into(),
4418            path_suffixes: vec!["rs".to_string()],
4419            ..Default::default()
4420        },
4421        Some(tree_sitter_rust::language()),
4422    );
4423    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4424    client_a.language_registry.add(Arc::new(language));
4425
4426    client_a
4427        .fs
4428        .insert_tree(
4429            "/root",
4430            json!({
4431                "dir-1": {
4432                    "one.rs": "const ONE: usize = 1;",
4433                    "two.rs": "const TWO: usize = one::ONE + one::ONE;",
4434                },
4435                "dir-2": {
4436                    "three.rs": "const THREE: usize = two::TWO + one::ONE;",
4437                }
4438            }),
4439        )
4440        .await;
4441    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4442    let project_id = active_call_a
4443        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4444        .await
4445        .unwrap();
4446    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4447
4448    // Open the file on client B.
4449    let buffer_b = cx_b
4450        .background()
4451        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
4452        .await
4453        .unwrap();
4454
4455    // Request references to a symbol as the guest.
4456    let fake_language_server = fake_language_servers.next().await.unwrap();
4457    fake_language_server.handle_request::<lsp::request::References, _, _>(|params, _| async move {
4458        assert_eq!(
4459            params.text_document_position.text_document.uri.as_str(),
4460            "file:///root/dir-1/one.rs"
4461        );
4462        Ok(Some(vec![
4463            lsp::Location {
4464                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4465                range: lsp::Range::new(lsp::Position::new(0, 24), lsp::Position::new(0, 27)),
4466            },
4467            lsp::Location {
4468                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4469                range: lsp::Range::new(lsp::Position::new(0, 35), lsp::Position::new(0, 38)),
4470            },
4471            lsp::Location {
4472                uri: lsp::Url::from_file_path("/root/dir-2/three.rs").unwrap(),
4473                range: lsp::Range::new(lsp::Position::new(0, 37), lsp::Position::new(0, 40)),
4474            },
4475        ]))
4476    });
4477
4478    let references = project_b
4479        .update(cx_b, |p, cx| p.references(&buffer_b, 7, cx))
4480        .await
4481        .unwrap();
4482    cx_b.read(|cx| {
4483        assert_eq!(references.len(), 3);
4484        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4485
4486        let two_buffer = references[0].buffer.read(cx);
4487        let three_buffer = references[2].buffer.read(cx);
4488        assert_eq!(
4489            two_buffer.file().unwrap().path().as_ref(),
4490            Path::new("two.rs")
4491        );
4492        assert_eq!(references[1].buffer, references[0].buffer);
4493        assert_eq!(
4494            three_buffer.file().unwrap().full_path(cx),
4495            Path::new("/root/dir-2/three.rs")
4496        );
4497
4498        assert_eq!(references[0].range.to_offset(two_buffer), 24..27);
4499        assert_eq!(references[1].range.to_offset(two_buffer), 35..38);
4500        assert_eq!(references[2].range.to_offset(three_buffer), 37..40);
4501    });
4502}
4503
4504#[gpui::test(iterations = 10)]
4505async fn test_project_search(
4506    deterministic: Arc<Deterministic>,
4507    cx_a: &mut TestAppContext,
4508    cx_b: &mut TestAppContext,
4509) {
4510    deterministic.forbid_parking();
4511    let mut server = TestServer::start(&deterministic).await;
4512    let client_a = server.create_client(cx_a, "user_a").await;
4513    let client_b = server.create_client(cx_b, "user_b").await;
4514    server
4515        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4516        .await;
4517    let active_call_a = cx_a.read(ActiveCall::global);
4518
4519    client_a
4520        .fs
4521        .insert_tree(
4522            "/root",
4523            json!({
4524                "dir-1": {
4525                    "a": "hello world",
4526                    "b": "goodnight moon",
4527                    "c": "a world of goo",
4528                    "d": "world champion of clown world",
4529                },
4530                "dir-2": {
4531                    "e": "disney world is fun",
4532                }
4533            }),
4534        )
4535        .await;
4536    let (project_a, _) = client_a.build_local_project("/root/dir-1", cx_a).await;
4537    let (worktree_2, _) = project_a
4538        .update(cx_a, |p, cx| {
4539            p.find_or_create_local_worktree("/root/dir-2", true, cx)
4540        })
4541        .await
4542        .unwrap();
4543    worktree_2
4544        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
4545        .await;
4546    let project_id = active_call_a
4547        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4548        .await
4549        .unwrap();
4550
4551    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4552
4553    // Perform a search as the guest.
4554    let results = project_b
4555        .update(cx_b, |project, cx| {
4556            project.search(
4557                SearchQuery::text("world", false, false, Vec::new(), Vec::new()),
4558                cx,
4559            )
4560        })
4561        .await
4562        .unwrap();
4563
4564    let mut ranges_by_path = results
4565        .into_iter()
4566        .map(|(buffer, ranges)| {
4567            buffer.read_with(cx_b, |buffer, cx| {
4568                let path = buffer.file().unwrap().full_path(cx);
4569                let offset_ranges = ranges
4570                    .into_iter()
4571                    .map(|range| range.to_offset(buffer))
4572                    .collect::<Vec<_>>();
4573                (path, offset_ranges)
4574            })
4575        })
4576        .collect::<Vec<_>>();
4577    ranges_by_path.sort_by_key(|(path, _)| path.clone());
4578
4579    assert_eq!(
4580        ranges_by_path,
4581        &[
4582            (PathBuf::from("dir-1/a"), vec![6..11]),
4583            (PathBuf::from("dir-1/c"), vec![2..7]),
4584            (PathBuf::from("dir-1/d"), vec![0..5, 24..29]),
4585            (PathBuf::from("dir-2/e"), vec![7..12]),
4586        ]
4587    );
4588}
4589
4590#[gpui::test(iterations = 10)]
4591async fn test_document_highlights(
4592    deterministic: Arc<Deterministic>,
4593    cx_a: &mut TestAppContext,
4594    cx_b: &mut TestAppContext,
4595) {
4596    deterministic.forbid_parking();
4597    let mut server = TestServer::start(&deterministic).await;
4598    let client_a = server.create_client(cx_a, "user_a").await;
4599    let client_b = server.create_client(cx_b, "user_b").await;
4600    server
4601        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4602        .await;
4603    let active_call_a = cx_a.read(ActiveCall::global);
4604
4605    client_a
4606        .fs
4607        .insert_tree(
4608            "/root-1",
4609            json!({
4610                "main.rs": "fn double(number: i32) -> i32 { number + number }",
4611            }),
4612        )
4613        .await;
4614
4615    // Set up a fake language server.
4616    let mut language = Language::new(
4617        LanguageConfig {
4618            name: "Rust".into(),
4619            path_suffixes: vec!["rs".to_string()],
4620            ..Default::default()
4621        },
4622        Some(tree_sitter_rust::language()),
4623    );
4624    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4625    client_a.language_registry.add(Arc::new(language));
4626
4627    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4628    let project_id = active_call_a
4629        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4630        .await
4631        .unwrap();
4632    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4633
4634    // Open the file on client B.
4635    let buffer_b = cx_b
4636        .background()
4637        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
4638        .await
4639        .unwrap();
4640
4641    // Request document highlights as the guest.
4642    let fake_language_server = fake_language_servers.next().await.unwrap();
4643    fake_language_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
4644        |params, _| async move {
4645            assert_eq!(
4646                params
4647                    .text_document_position_params
4648                    .text_document
4649                    .uri
4650                    .as_str(),
4651                "file:///root-1/main.rs"
4652            );
4653            assert_eq!(
4654                params.text_document_position_params.position,
4655                lsp::Position::new(0, 34)
4656            );
4657            Ok(Some(vec![
4658                lsp::DocumentHighlight {
4659                    kind: Some(lsp::DocumentHighlightKind::WRITE),
4660                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 16)),
4661                },
4662                lsp::DocumentHighlight {
4663                    kind: Some(lsp::DocumentHighlightKind::READ),
4664                    range: lsp::Range::new(lsp::Position::new(0, 32), lsp::Position::new(0, 38)),
4665                },
4666                lsp::DocumentHighlight {
4667                    kind: Some(lsp::DocumentHighlightKind::READ),
4668                    range: lsp::Range::new(lsp::Position::new(0, 41), lsp::Position::new(0, 47)),
4669                },
4670            ]))
4671        },
4672    );
4673
4674    let highlights = project_b
4675        .update(cx_b, |p, cx| p.document_highlights(&buffer_b, 34, cx))
4676        .await
4677        .unwrap();
4678    buffer_b.read_with(cx_b, |buffer, _| {
4679        let snapshot = buffer.snapshot();
4680
4681        let highlights = highlights
4682            .into_iter()
4683            .map(|highlight| (highlight.kind, highlight.range.to_offset(&snapshot)))
4684            .collect::<Vec<_>>();
4685        assert_eq!(
4686            highlights,
4687            &[
4688                (lsp::DocumentHighlightKind::WRITE, 10..16),
4689                (lsp::DocumentHighlightKind::READ, 32..38),
4690                (lsp::DocumentHighlightKind::READ, 41..47)
4691            ]
4692        )
4693    });
4694}
4695
4696#[gpui::test(iterations = 10)]
4697async fn test_lsp_hover(
4698    deterministic: Arc<Deterministic>,
4699    cx_a: &mut TestAppContext,
4700    cx_b: &mut TestAppContext,
4701) {
4702    deterministic.forbid_parking();
4703    let mut server = TestServer::start(&deterministic).await;
4704    let client_a = server.create_client(cx_a, "user_a").await;
4705    let client_b = server.create_client(cx_b, "user_b").await;
4706    server
4707        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4708        .await;
4709    let active_call_a = cx_a.read(ActiveCall::global);
4710
4711    client_a
4712        .fs
4713        .insert_tree(
4714            "/root-1",
4715            json!({
4716                "main.rs": "use std::collections::HashMap;",
4717            }),
4718        )
4719        .await;
4720
4721    // Set up a fake language server.
4722    let mut language = Language::new(
4723        LanguageConfig {
4724            name: "Rust".into(),
4725            path_suffixes: vec!["rs".to_string()],
4726            ..Default::default()
4727        },
4728        Some(tree_sitter_rust::language()),
4729    );
4730    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4731    client_a.language_registry.add(Arc::new(language));
4732
4733    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4734    let project_id = active_call_a
4735        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4736        .await
4737        .unwrap();
4738    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4739
4740    // Open the file as the guest
4741    let buffer_b = cx_b
4742        .background()
4743        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
4744        .await
4745        .unwrap();
4746
4747    // Request hover information as the guest.
4748    let fake_language_server = fake_language_servers.next().await.unwrap();
4749    fake_language_server.handle_request::<lsp::request::HoverRequest, _, _>(
4750        |params, _| async move {
4751            assert_eq!(
4752                params
4753                    .text_document_position_params
4754                    .text_document
4755                    .uri
4756                    .as_str(),
4757                "file:///root-1/main.rs"
4758            );
4759            assert_eq!(
4760                params.text_document_position_params.position,
4761                lsp::Position::new(0, 22)
4762            );
4763            Ok(Some(lsp::Hover {
4764                contents: lsp::HoverContents::Array(vec![
4765                    lsp::MarkedString::String("Test hover content.".to_string()),
4766                    lsp::MarkedString::LanguageString(lsp::LanguageString {
4767                        language: "Rust".to_string(),
4768                        value: "let foo = 42;".to_string(),
4769                    }),
4770                ]),
4771                range: Some(lsp::Range::new(
4772                    lsp::Position::new(0, 22),
4773                    lsp::Position::new(0, 29),
4774                )),
4775            }))
4776        },
4777    );
4778
4779    let hover_info = project_b
4780        .update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
4781        .await
4782        .unwrap()
4783        .unwrap();
4784    buffer_b.read_with(cx_b, |buffer, _| {
4785        let snapshot = buffer.snapshot();
4786        assert_eq!(hover_info.range.unwrap().to_offset(&snapshot), 22..29);
4787        assert_eq!(
4788            hover_info.contents,
4789            vec![
4790                project::HoverBlock {
4791                    text: "Test hover content.".to_string(),
4792                    kind: HoverBlockKind::Markdown,
4793                },
4794                project::HoverBlock {
4795                    text: "let foo = 42;".to_string(),
4796                    kind: HoverBlockKind::Code {
4797                        language: "Rust".to_string()
4798                    },
4799                }
4800            ]
4801        );
4802    });
4803}
4804
4805#[gpui::test(iterations = 10)]
4806async fn test_project_symbols(
4807    deterministic: Arc<Deterministic>,
4808    cx_a: &mut TestAppContext,
4809    cx_b: &mut TestAppContext,
4810) {
4811    deterministic.forbid_parking();
4812    let mut server = TestServer::start(&deterministic).await;
4813    let client_a = server.create_client(cx_a, "user_a").await;
4814    let client_b = server.create_client(cx_b, "user_b").await;
4815    server
4816        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4817        .await;
4818    let active_call_a = cx_a.read(ActiveCall::global);
4819
4820    // Set up a fake language server.
4821    let mut language = Language::new(
4822        LanguageConfig {
4823            name: "Rust".into(),
4824            path_suffixes: vec!["rs".to_string()],
4825            ..Default::default()
4826        },
4827        Some(tree_sitter_rust::language()),
4828    );
4829    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4830    client_a.language_registry.add(Arc::new(language));
4831
4832    client_a
4833        .fs
4834        .insert_tree(
4835            "/code",
4836            json!({
4837                "crate-1": {
4838                    "one.rs": "const ONE: usize = 1;",
4839                },
4840                "crate-2": {
4841                    "two.rs": "const TWO: usize = 2; const THREE: usize = 3;",
4842                },
4843                "private": {
4844                    "passwords.txt": "the-password",
4845                }
4846            }),
4847        )
4848        .await;
4849    let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await;
4850    let project_id = active_call_a
4851        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4852        .await
4853        .unwrap();
4854    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4855
4856    // Cause the language server to start.
4857    let _buffer = cx_b
4858        .background()
4859        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
4860        .await
4861        .unwrap();
4862
4863    let fake_language_server = fake_language_servers.next().await.unwrap();
4864    fake_language_server.handle_request::<lsp::request::WorkspaceSymbol, _, _>(|_, _| async move {
4865        #[allow(deprecated)]
4866        Ok(Some(vec![lsp::SymbolInformation {
4867            name: "TWO".into(),
4868            location: lsp::Location {
4869                uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
4870                range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4871            },
4872            kind: lsp::SymbolKind::CONSTANT,
4873            tags: None,
4874            container_name: None,
4875            deprecated: None,
4876        }]))
4877    });
4878
4879    // Request the definition of a symbol as the guest.
4880    let symbols = project_b
4881        .update(cx_b, |p, cx| p.symbols("two", cx))
4882        .await
4883        .unwrap();
4884    assert_eq!(symbols.len(), 1);
4885    assert_eq!(symbols[0].name, "TWO");
4886
4887    // Open one of the returned symbols.
4888    let buffer_b_2 = project_b
4889        .update(cx_b, |project, cx| {
4890            project.open_buffer_for_symbol(&symbols[0], cx)
4891        })
4892        .await
4893        .unwrap();
4894    buffer_b_2.read_with(cx_b, |buffer, _| {
4895        assert_eq!(
4896            buffer.file().unwrap().path().as_ref(),
4897            Path::new("../crate-2/two.rs")
4898        );
4899    });
4900
4901    // Attempt to craft a symbol and violate host's privacy by opening an arbitrary file.
4902    let mut fake_symbol = symbols[0].clone();
4903    fake_symbol.path.path = Path::new("/code/secrets").into();
4904    let error = project_b
4905        .update(cx_b, |project, cx| {
4906            project.open_buffer_for_symbol(&fake_symbol, cx)
4907        })
4908        .await
4909        .unwrap_err();
4910    assert!(error.to_string().contains("invalid symbol signature"));
4911}
4912
4913#[gpui::test(iterations = 10)]
4914async fn test_open_buffer_while_getting_definition_pointing_to_it(
4915    deterministic: Arc<Deterministic>,
4916    cx_a: &mut TestAppContext,
4917    cx_b: &mut TestAppContext,
4918    mut rng: StdRng,
4919) {
4920    deterministic.forbid_parking();
4921    let mut server = TestServer::start(&deterministic).await;
4922    let client_a = server.create_client(cx_a, "user_a").await;
4923    let client_b = server.create_client(cx_b, "user_b").await;
4924    server
4925        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4926        .await;
4927    let active_call_a = cx_a.read(ActiveCall::global);
4928
4929    // Set up a fake language server.
4930    let mut language = Language::new(
4931        LanguageConfig {
4932            name: "Rust".into(),
4933            path_suffixes: vec!["rs".to_string()],
4934            ..Default::default()
4935        },
4936        Some(tree_sitter_rust::language()),
4937    );
4938    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4939    client_a.language_registry.add(Arc::new(language));
4940
4941    client_a
4942        .fs
4943        .insert_tree(
4944            "/root",
4945            json!({
4946                "a.rs": "const ONE: usize = b::TWO;",
4947                "b.rs": "const TWO: usize = 2",
4948            }),
4949        )
4950        .await;
4951    let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await;
4952    let project_id = active_call_a
4953        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4954        .await
4955        .unwrap();
4956    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4957
4958    let buffer_b1 = cx_b
4959        .background()
4960        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4961        .await
4962        .unwrap();
4963
4964    let fake_language_server = fake_language_servers.next().await.unwrap();
4965    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4966        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4967            lsp::Location::new(
4968                lsp::Url::from_file_path("/root/b.rs").unwrap(),
4969                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4970            ),
4971        )))
4972    });
4973
4974    let definitions;
4975    let buffer_b2;
4976    if rng.gen() {
4977        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
4978        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
4979    } else {
4980        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
4981        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
4982    }
4983
4984    let buffer_b2 = buffer_b2.await.unwrap();
4985    let definitions = definitions.await.unwrap();
4986    assert_eq!(definitions.len(), 1);
4987    assert_eq!(definitions[0].target.buffer, buffer_b2);
4988}
4989
4990#[gpui::test(iterations = 10)]
4991async fn test_collaborating_with_code_actions(
4992    deterministic: Arc<Deterministic>,
4993    cx_a: &mut TestAppContext,
4994    cx_b: &mut TestAppContext,
4995) {
4996    deterministic.forbid_parking();
4997    let mut server = TestServer::start(&deterministic).await;
4998    let client_a = server.create_client(cx_a, "user_a").await;
4999    let client_b = server.create_client(cx_b, "user_b").await;
5000    server
5001        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5002        .await;
5003    let active_call_a = cx_a.read(ActiveCall::global);
5004
5005    cx_b.update(editor::init);
5006
5007    // Set up a fake language server.
5008    let mut language = Language::new(
5009        LanguageConfig {
5010            name: "Rust".into(),
5011            path_suffixes: vec!["rs".to_string()],
5012            ..Default::default()
5013        },
5014        Some(tree_sitter_rust::language()),
5015    );
5016    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5017    client_a.language_registry.add(Arc::new(language));
5018
5019    client_a
5020        .fs
5021        .insert_tree(
5022            "/a",
5023            json!({
5024                "main.rs": "mod other;\nfn main() { let foo = other::foo(); }",
5025                "other.rs": "pub fn foo() -> usize { 4 }",
5026            }),
5027        )
5028        .await;
5029    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
5030    let project_id = active_call_a
5031        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5032        .await
5033        .unwrap();
5034
5035    // Join the project as client B.
5036    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5037    let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx));
5038    let editor_b = workspace_b
5039        .update(cx_b, |workspace, cx| {
5040            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
5041        })
5042        .await
5043        .unwrap()
5044        .downcast::<Editor>()
5045        .unwrap();
5046
5047    let mut fake_language_server = fake_language_servers.next().await.unwrap();
5048    fake_language_server
5049        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
5050            assert_eq!(
5051                params.text_document.uri,
5052                lsp::Url::from_file_path("/a/main.rs").unwrap(),
5053            );
5054            assert_eq!(params.range.start, lsp::Position::new(0, 0));
5055            assert_eq!(params.range.end, lsp::Position::new(0, 0));
5056            Ok(None)
5057        })
5058        .next()
5059        .await;
5060
5061    // Move cursor to a location that contains code actions.
5062    editor_b.update(cx_b, |editor, cx| {
5063        editor.change_selections(None, cx, |s| {
5064            s.select_ranges([Point::new(1, 31)..Point::new(1, 31)])
5065        });
5066        cx.focus(&editor_b);
5067    });
5068
5069    fake_language_server
5070        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
5071            assert_eq!(
5072                params.text_document.uri,
5073                lsp::Url::from_file_path("/a/main.rs").unwrap(),
5074            );
5075            assert_eq!(params.range.start, lsp::Position::new(1, 31));
5076            assert_eq!(params.range.end, lsp::Position::new(1, 31));
5077
5078            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
5079                lsp::CodeAction {
5080                    title: "Inline into all callers".to_string(),
5081                    edit: Some(lsp::WorkspaceEdit {
5082                        changes: Some(
5083                            [
5084                                (
5085                                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
5086                                    vec![lsp::TextEdit::new(
5087                                        lsp::Range::new(
5088                                            lsp::Position::new(1, 22),
5089                                            lsp::Position::new(1, 34),
5090                                        ),
5091                                        "4".to_string(),
5092                                    )],
5093                                ),
5094                                (
5095                                    lsp::Url::from_file_path("/a/other.rs").unwrap(),
5096                                    vec![lsp::TextEdit::new(
5097                                        lsp::Range::new(
5098                                            lsp::Position::new(0, 0),
5099                                            lsp::Position::new(0, 27),
5100                                        ),
5101                                        "".to_string(),
5102                                    )],
5103                                ),
5104                            ]
5105                            .into_iter()
5106                            .collect(),
5107                        ),
5108                        ..Default::default()
5109                    }),
5110                    data: Some(json!({
5111                        "codeActionParams": {
5112                            "range": {
5113                                "start": {"line": 1, "column": 31},
5114                                "end": {"line": 1, "column": 31},
5115                            }
5116                        }
5117                    })),
5118                    ..Default::default()
5119                },
5120            )]))
5121        })
5122        .next()
5123        .await;
5124
5125    // Toggle code actions and wait for them to display.
5126    editor_b.update(cx_b, |editor, cx| {
5127        editor.toggle_code_actions(
5128            &ToggleCodeActions {
5129                deployed_from_indicator: false,
5130            },
5131            cx,
5132        );
5133    });
5134    cx_a.foreground().run_until_parked();
5135    editor_b.read_with(cx_b, |editor, _| assert!(editor.context_menu_visible()));
5136
5137    fake_language_server.remove_request_handler::<lsp::request::CodeActionRequest>();
5138
5139    // Confirming the code action will trigger a resolve request.
5140    let confirm_action = workspace_b
5141        .update(cx_b, |workspace, cx| {
5142            Editor::confirm_code_action(workspace, &ConfirmCodeAction { item_ix: Some(0) }, cx)
5143        })
5144        .unwrap();
5145    fake_language_server.handle_request::<lsp::request::CodeActionResolveRequest, _, _>(
5146        |_, _| async move {
5147            Ok(lsp::CodeAction {
5148                title: "Inline into all callers".to_string(),
5149                edit: Some(lsp::WorkspaceEdit {
5150                    changes: Some(
5151                        [
5152                            (
5153                                lsp::Url::from_file_path("/a/main.rs").unwrap(),
5154                                vec![lsp::TextEdit::new(
5155                                    lsp::Range::new(
5156                                        lsp::Position::new(1, 22),
5157                                        lsp::Position::new(1, 34),
5158                                    ),
5159                                    "4".to_string(),
5160                                )],
5161                            ),
5162                            (
5163                                lsp::Url::from_file_path("/a/other.rs").unwrap(),
5164                                vec![lsp::TextEdit::new(
5165                                    lsp::Range::new(
5166                                        lsp::Position::new(0, 0),
5167                                        lsp::Position::new(0, 27),
5168                                    ),
5169                                    "".to_string(),
5170                                )],
5171                            ),
5172                        ]
5173                        .into_iter()
5174                        .collect(),
5175                    ),
5176                    ..Default::default()
5177                }),
5178                ..Default::default()
5179            })
5180        },
5181    );
5182
5183    // After the action is confirmed, an editor containing both modified files is opened.
5184    confirm_action.await.unwrap();
5185    let code_action_editor = workspace_b.read_with(cx_b, |workspace, cx| {
5186        workspace
5187            .active_item(cx)
5188            .unwrap()
5189            .downcast::<Editor>()
5190            .unwrap()
5191    });
5192    code_action_editor.update(cx_b, |editor, cx| {
5193        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
5194        editor.undo(&Undo, cx);
5195        assert_eq!(
5196            editor.text(cx),
5197            "mod other;\nfn main() { let foo = other::foo(); }\npub fn foo() -> usize { 4 }"
5198        );
5199        editor.redo(&Redo, cx);
5200        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
5201    });
5202}
5203
5204#[gpui::test(iterations = 10)]
5205async fn test_collaborating_with_renames(
5206    deterministic: Arc<Deterministic>,
5207    cx_a: &mut TestAppContext,
5208    cx_b: &mut TestAppContext,
5209) {
5210    deterministic.forbid_parking();
5211    let mut server = TestServer::start(&deterministic).await;
5212    let client_a = server.create_client(cx_a, "user_a").await;
5213    let client_b = server.create_client(cx_b, "user_b").await;
5214    server
5215        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5216        .await;
5217    let active_call_a = cx_a.read(ActiveCall::global);
5218
5219    cx_b.update(editor::init);
5220
5221    // Set up a fake language server.
5222    let mut language = Language::new(
5223        LanguageConfig {
5224            name: "Rust".into(),
5225            path_suffixes: vec!["rs".to_string()],
5226            ..Default::default()
5227        },
5228        Some(tree_sitter_rust::language()),
5229    );
5230    let mut fake_language_servers = language
5231        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
5232            capabilities: lsp::ServerCapabilities {
5233                rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
5234                    prepare_provider: Some(true),
5235                    work_done_progress_options: Default::default(),
5236                })),
5237                ..Default::default()
5238            },
5239            ..Default::default()
5240        }))
5241        .await;
5242    client_a.language_registry.add(Arc::new(language));
5243
5244    client_a
5245        .fs
5246        .insert_tree(
5247            "/dir",
5248            json!({
5249                "one.rs": "const ONE: usize = 1;",
5250                "two.rs": "const TWO: usize = one::ONE + one::ONE;"
5251            }),
5252        )
5253        .await;
5254    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
5255    let project_id = active_call_a
5256        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5257        .await
5258        .unwrap();
5259    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5260
5261    let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx));
5262    let editor_b = workspace_b
5263        .update(cx_b, |workspace, cx| {
5264            workspace.open_path((worktree_id, "one.rs"), None, true, cx)
5265        })
5266        .await
5267        .unwrap()
5268        .downcast::<Editor>()
5269        .unwrap();
5270    let fake_language_server = fake_language_servers.next().await.unwrap();
5271
5272    // Move cursor to a location that can be renamed.
5273    let prepare_rename = editor_b.update(cx_b, |editor, cx| {
5274        editor.change_selections(None, cx, |s| s.select_ranges([7..7]));
5275        editor.rename(&Rename, cx).unwrap()
5276    });
5277
5278    fake_language_server
5279        .handle_request::<lsp::request::PrepareRenameRequest, _, _>(|params, _| async move {
5280            assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs");
5281            assert_eq!(params.position, lsp::Position::new(0, 7));
5282            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
5283                lsp::Position::new(0, 6),
5284                lsp::Position::new(0, 9),
5285            ))))
5286        })
5287        .next()
5288        .await
5289        .unwrap();
5290    prepare_rename.await.unwrap();
5291    editor_b.update(cx_b, |editor, cx| {
5292        let rename = editor.pending_rename().unwrap();
5293        let buffer = editor.buffer().read(cx).snapshot(cx);
5294        assert_eq!(
5295            rename.range.start.to_offset(&buffer)..rename.range.end.to_offset(&buffer),
5296            6..9
5297        );
5298        rename.editor.update(cx, |rename_editor, cx| {
5299            rename_editor.buffer().update(cx, |rename_buffer, cx| {
5300                rename_buffer.edit([(0..3, "THREE")], None, cx);
5301            });
5302        });
5303    });
5304
5305    let confirm_rename = workspace_b.update(cx_b, |workspace, cx| {
5306        Editor::confirm_rename(workspace, &ConfirmRename, cx).unwrap()
5307    });
5308    fake_language_server
5309        .handle_request::<lsp::request::Rename, _, _>(|params, _| async move {
5310            assert_eq!(
5311                params.text_document_position.text_document.uri.as_str(),
5312                "file:///dir/one.rs"
5313            );
5314            assert_eq!(
5315                params.text_document_position.position,
5316                lsp::Position::new(0, 6)
5317            );
5318            assert_eq!(params.new_name, "THREE");
5319            Ok(Some(lsp::WorkspaceEdit {
5320                changes: Some(
5321                    [
5322                        (
5323                            lsp::Url::from_file_path("/dir/one.rs").unwrap(),
5324                            vec![lsp::TextEdit::new(
5325                                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5326                                "THREE".to_string(),
5327                            )],
5328                        ),
5329                        (
5330                            lsp::Url::from_file_path("/dir/two.rs").unwrap(),
5331                            vec![
5332                                lsp::TextEdit::new(
5333                                    lsp::Range::new(
5334                                        lsp::Position::new(0, 24),
5335                                        lsp::Position::new(0, 27),
5336                                    ),
5337                                    "THREE".to_string(),
5338                                ),
5339                                lsp::TextEdit::new(
5340                                    lsp::Range::new(
5341                                        lsp::Position::new(0, 35),
5342                                        lsp::Position::new(0, 38),
5343                                    ),
5344                                    "THREE".to_string(),
5345                                ),
5346                            ],
5347                        ),
5348                    ]
5349                    .into_iter()
5350                    .collect(),
5351                ),
5352                ..Default::default()
5353            }))
5354        })
5355        .next()
5356        .await
5357        .unwrap();
5358    confirm_rename.await.unwrap();
5359
5360    let rename_editor = workspace_b.read_with(cx_b, |workspace, cx| {
5361        workspace
5362            .active_item(cx)
5363            .unwrap()
5364            .downcast::<Editor>()
5365            .unwrap()
5366    });
5367    rename_editor.update(cx_b, |editor, cx| {
5368        assert_eq!(
5369            editor.text(cx),
5370            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
5371        );
5372        editor.undo(&Undo, cx);
5373        assert_eq!(
5374            editor.text(cx),
5375            "const ONE: usize = 1;\nconst TWO: usize = one::ONE + one::ONE;"
5376        );
5377        editor.redo(&Redo, cx);
5378        assert_eq!(
5379            editor.text(cx),
5380            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
5381        );
5382    });
5383
5384    // Ensure temporary rename edits cannot be undone/redone.
5385    editor_b.update(cx_b, |editor, cx| {
5386        editor.undo(&Undo, cx);
5387        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
5388        editor.undo(&Undo, cx);
5389        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
5390        editor.redo(&Redo, cx);
5391        assert_eq!(editor.text(cx), "const THREE: usize = 1;");
5392    })
5393}
5394
5395#[gpui::test(iterations = 10)]
5396async fn test_language_server_statuses(
5397    deterministic: Arc<Deterministic>,
5398    cx_a: &mut TestAppContext,
5399    cx_b: &mut TestAppContext,
5400) {
5401    deterministic.forbid_parking();
5402    let mut server = TestServer::start(&deterministic).await;
5403    let client_a = server.create_client(cx_a, "user_a").await;
5404    let client_b = server.create_client(cx_b, "user_b").await;
5405    server
5406        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5407        .await;
5408    let active_call_a = cx_a.read(ActiveCall::global);
5409
5410    cx_b.update(editor::init);
5411
5412    // Set up a fake language server.
5413    let mut language = Language::new(
5414        LanguageConfig {
5415            name: "Rust".into(),
5416            path_suffixes: vec!["rs".to_string()],
5417            ..Default::default()
5418        },
5419        Some(tree_sitter_rust::language()),
5420    );
5421    let mut fake_language_servers = language
5422        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
5423            name: "the-language-server",
5424            ..Default::default()
5425        }))
5426        .await;
5427    client_a.language_registry.add(Arc::new(language));
5428
5429    client_a
5430        .fs
5431        .insert_tree(
5432            "/dir",
5433            json!({
5434                "main.rs": "const ONE: usize = 1;",
5435            }),
5436        )
5437        .await;
5438    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
5439
5440    let _buffer_a = project_a
5441        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
5442        .await
5443        .unwrap();
5444
5445    let fake_language_server = fake_language_servers.next().await.unwrap();
5446    fake_language_server.start_progress("the-token").await;
5447    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
5448        token: lsp::NumberOrString::String("the-token".to_string()),
5449        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
5450            lsp::WorkDoneProgressReport {
5451                message: Some("the-message".to_string()),
5452                ..Default::default()
5453            },
5454        )),
5455    });
5456    deterministic.run_until_parked();
5457    project_a.read_with(cx_a, |project, _| {
5458        let status = project.language_server_statuses().next().unwrap();
5459        assert_eq!(status.name, "the-language-server");
5460        assert_eq!(status.pending_work.len(), 1);
5461        assert_eq!(
5462            status.pending_work["the-token"].message.as_ref().unwrap(),
5463            "the-message"
5464        );
5465    });
5466
5467    let project_id = active_call_a
5468        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5469        .await
5470        .unwrap();
5471    deterministic.run_until_parked();
5472    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5473    project_b.read_with(cx_b, |project, _| {
5474        let status = project.language_server_statuses().next().unwrap();
5475        assert_eq!(status.name, "the-language-server");
5476    });
5477
5478    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
5479        token: lsp::NumberOrString::String("the-token".to_string()),
5480        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
5481            lsp::WorkDoneProgressReport {
5482                message: Some("the-message-2".to_string()),
5483                ..Default::default()
5484            },
5485        )),
5486    });
5487    deterministic.run_until_parked();
5488    project_a.read_with(cx_a, |project, _| {
5489        let status = project.language_server_statuses().next().unwrap();
5490        assert_eq!(status.name, "the-language-server");
5491        assert_eq!(status.pending_work.len(), 1);
5492        assert_eq!(
5493            status.pending_work["the-token"].message.as_ref().unwrap(),
5494            "the-message-2"
5495        );
5496    });
5497    project_b.read_with(cx_b, |project, _| {
5498        let status = project.language_server_statuses().next().unwrap();
5499        assert_eq!(status.name, "the-language-server");
5500        assert_eq!(status.pending_work.len(), 1);
5501        assert_eq!(
5502            status.pending_work["the-token"].message.as_ref().unwrap(),
5503            "the-message-2"
5504        );
5505    });
5506}
5507
5508#[gpui::test(iterations = 10)]
5509async fn test_contacts(
5510    deterministic: Arc<Deterministic>,
5511    cx_a: &mut TestAppContext,
5512    cx_b: &mut TestAppContext,
5513    cx_c: &mut TestAppContext,
5514    cx_d: &mut TestAppContext,
5515) {
5516    deterministic.forbid_parking();
5517    let mut server = TestServer::start(&deterministic).await;
5518    let client_a = server.create_client(cx_a, "user_a").await;
5519    let client_b = server.create_client(cx_b, "user_b").await;
5520    let client_c = server.create_client(cx_c, "user_c").await;
5521    let client_d = server.create_client(cx_d, "user_d").await;
5522    server
5523        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
5524        .await;
5525    let active_call_a = cx_a.read(ActiveCall::global);
5526    let active_call_b = cx_b.read(ActiveCall::global);
5527    let active_call_c = cx_c.read(ActiveCall::global);
5528    let _active_call_d = cx_d.read(ActiveCall::global);
5529
5530    deterministic.run_until_parked();
5531    assert_eq!(
5532        contacts(&client_a, cx_a),
5533        [
5534            ("user_b".to_string(), "online", "free"),
5535            ("user_c".to_string(), "online", "free")
5536        ]
5537    );
5538    assert_eq!(
5539        contacts(&client_b, cx_b),
5540        [
5541            ("user_a".to_string(), "online", "free"),
5542            ("user_c".to_string(), "online", "free")
5543        ]
5544    );
5545    assert_eq!(
5546        contacts(&client_c, cx_c),
5547        [
5548            ("user_a".to_string(), "online", "free"),
5549            ("user_b".to_string(), "online", "free")
5550        ]
5551    );
5552    assert_eq!(contacts(&client_d, cx_d), []);
5553
5554    server.disconnect_client(client_c.peer_id().unwrap());
5555    server.forbid_connections();
5556    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5557    assert_eq!(
5558        contacts(&client_a, cx_a),
5559        [
5560            ("user_b".to_string(), "online", "free"),
5561            ("user_c".to_string(), "offline", "free")
5562        ]
5563    );
5564    assert_eq!(
5565        contacts(&client_b, cx_b),
5566        [
5567            ("user_a".to_string(), "online", "free"),
5568            ("user_c".to_string(), "offline", "free")
5569        ]
5570    );
5571    assert_eq!(contacts(&client_c, cx_c), []);
5572    assert_eq!(contacts(&client_d, cx_d), []);
5573
5574    server.allow_connections();
5575    client_c
5576        .authenticate_and_connect(false, &cx_c.to_async())
5577        .await
5578        .unwrap();
5579
5580    deterministic.run_until_parked();
5581    assert_eq!(
5582        contacts(&client_a, cx_a),
5583        [
5584            ("user_b".to_string(), "online", "free"),
5585            ("user_c".to_string(), "online", "free")
5586        ]
5587    );
5588    assert_eq!(
5589        contacts(&client_b, cx_b),
5590        [
5591            ("user_a".to_string(), "online", "free"),
5592            ("user_c".to_string(), "online", "free")
5593        ]
5594    );
5595    assert_eq!(
5596        contacts(&client_c, cx_c),
5597        [
5598            ("user_a".to_string(), "online", "free"),
5599            ("user_b".to_string(), "online", "free")
5600        ]
5601    );
5602    assert_eq!(contacts(&client_d, cx_d), []);
5603
5604    active_call_a
5605        .update(cx_a, |call, cx| {
5606            call.invite(client_b.user_id().unwrap(), None, cx)
5607        })
5608        .await
5609        .unwrap();
5610    deterministic.run_until_parked();
5611    assert_eq!(
5612        contacts(&client_a, cx_a),
5613        [
5614            ("user_b".to_string(), "online", "busy"),
5615            ("user_c".to_string(), "online", "free")
5616        ]
5617    );
5618    assert_eq!(
5619        contacts(&client_b, cx_b),
5620        [
5621            ("user_a".to_string(), "online", "busy"),
5622            ("user_c".to_string(), "online", "free")
5623        ]
5624    );
5625    assert_eq!(
5626        contacts(&client_c, cx_c),
5627        [
5628            ("user_a".to_string(), "online", "busy"),
5629            ("user_b".to_string(), "online", "busy")
5630        ]
5631    );
5632    assert_eq!(contacts(&client_d, cx_d), []);
5633
5634    // Client B and client D become contacts while client B is being called.
5635    server
5636        .make_contacts(&mut [(&client_b, cx_b), (&client_d, cx_d)])
5637        .await;
5638    deterministic.run_until_parked();
5639    assert_eq!(
5640        contacts(&client_a, cx_a),
5641        [
5642            ("user_b".to_string(), "online", "busy"),
5643            ("user_c".to_string(), "online", "free")
5644        ]
5645    );
5646    assert_eq!(
5647        contacts(&client_b, cx_b),
5648        [
5649            ("user_a".to_string(), "online", "busy"),
5650            ("user_c".to_string(), "online", "free"),
5651            ("user_d".to_string(), "online", "free"),
5652        ]
5653    );
5654    assert_eq!(
5655        contacts(&client_c, cx_c),
5656        [
5657            ("user_a".to_string(), "online", "busy"),
5658            ("user_b".to_string(), "online", "busy")
5659        ]
5660    );
5661    assert_eq!(
5662        contacts(&client_d, cx_d),
5663        [("user_b".to_string(), "online", "busy")]
5664    );
5665
5666    active_call_b.update(cx_b, |call, _| call.decline_incoming().unwrap());
5667    deterministic.run_until_parked();
5668    assert_eq!(
5669        contacts(&client_a, cx_a),
5670        [
5671            ("user_b".to_string(), "online", "free"),
5672            ("user_c".to_string(), "online", "free")
5673        ]
5674    );
5675    assert_eq!(
5676        contacts(&client_b, cx_b),
5677        [
5678            ("user_a".to_string(), "online", "free"),
5679            ("user_c".to_string(), "online", "free"),
5680            ("user_d".to_string(), "online", "free")
5681        ]
5682    );
5683    assert_eq!(
5684        contacts(&client_c, cx_c),
5685        [
5686            ("user_a".to_string(), "online", "free"),
5687            ("user_b".to_string(), "online", "free")
5688        ]
5689    );
5690    assert_eq!(
5691        contacts(&client_d, cx_d),
5692        [("user_b".to_string(), "online", "free")]
5693    );
5694
5695    active_call_c
5696        .update(cx_c, |call, cx| {
5697            call.invite(client_a.user_id().unwrap(), None, cx)
5698        })
5699        .await
5700        .unwrap();
5701    deterministic.run_until_parked();
5702    assert_eq!(
5703        contacts(&client_a, cx_a),
5704        [
5705            ("user_b".to_string(), "online", "free"),
5706            ("user_c".to_string(), "online", "busy")
5707        ]
5708    );
5709    assert_eq!(
5710        contacts(&client_b, cx_b),
5711        [
5712            ("user_a".to_string(), "online", "busy"),
5713            ("user_c".to_string(), "online", "busy"),
5714            ("user_d".to_string(), "online", "free")
5715        ]
5716    );
5717    assert_eq!(
5718        contacts(&client_c, cx_c),
5719        [
5720            ("user_a".to_string(), "online", "busy"),
5721            ("user_b".to_string(), "online", "free")
5722        ]
5723    );
5724    assert_eq!(
5725        contacts(&client_d, cx_d),
5726        [("user_b".to_string(), "online", "free")]
5727    );
5728
5729    active_call_a
5730        .update(cx_a, |call, cx| call.accept_incoming(cx))
5731        .await
5732        .unwrap();
5733    deterministic.run_until_parked();
5734    assert_eq!(
5735        contacts(&client_a, cx_a),
5736        [
5737            ("user_b".to_string(), "online", "free"),
5738            ("user_c".to_string(), "online", "busy")
5739        ]
5740    );
5741    assert_eq!(
5742        contacts(&client_b, cx_b),
5743        [
5744            ("user_a".to_string(), "online", "busy"),
5745            ("user_c".to_string(), "online", "busy"),
5746            ("user_d".to_string(), "online", "free")
5747        ]
5748    );
5749    assert_eq!(
5750        contacts(&client_c, cx_c),
5751        [
5752            ("user_a".to_string(), "online", "busy"),
5753            ("user_b".to_string(), "online", "free")
5754        ]
5755    );
5756    assert_eq!(
5757        contacts(&client_d, cx_d),
5758        [("user_b".to_string(), "online", "free")]
5759    );
5760
5761    active_call_a
5762        .update(cx_a, |call, cx| {
5763            call.invite(client_b.user_id().unwrap(), None, cx)
5764        })
5765        .await
5766        .unwrap();
5767    deterministic.run_until_parked();
5768    assert_eq!(
5769        contacts(&client_a, cx_a),
5770        [
5771            ("user_b".to_string(), "online", "busy"),
5772            ("user_c".to_string(), "online", "busy")
5773        ]
5774    );
5775    assert_eq!(
5776        contacts(&client_b, cx_b),
5777        [
5778            ("user_a".to_string(), "online", "busy"),
5779            ("user_c".to_string(), "online", "busy"),
5780            ("user_d".to_string(), "online", "free")
5781        ]
5782    );
5783    assert_eq!(
5784        contacts(&client_c, cx_c),
5785        [
5786            ("user_a".to_string(), "online", "busy"),
5787            ("user_b".to_string(), "online", "busy")
5788        ]
5789    );
5790    assert_eq!(
5791        contacts(&client_d, cx_d),
5792        [("user_b".to_string(), "online", "busy")]
5793    );
5794
5795    active_call_a
5796        .update(cx_a, |call, cx| call.hang_up(cx))
5797        .await
5798        .unwrap();
5799    deterministic.run_until_parked();
5800    assert_eq!(
5801        contacts(&client_a, cx_a),
5802        [
5803            ("user_b".to_string(), "online", "free"),
5804            ("user_c".to_string(), "online", "free")
5805        ]
5806    );
5807    assert_eq!(
5808        contacts(&client_b, cx_b),
5809        [
5810            ("user_a".to_string(), "online", "free"),
5811            ("user_c".to_string(), "online", "free"),
5812            ("user_d".to_string(), "online", "free")
5813        ]
5814    );
5815    assert_eq!(
5816        contacts(&client_c, cx_c),
5817        [
5818            ("user_a".to_string(), "online", "free"),
5819            ("user_b".to_string(), "online", "free")
5820        ]
5821    );
5822    assert_eq!(
5823        contacts(&client_d, cx_d),
5824        [("user_b".to_string(), "online", "free")]
5825    );
5826
5827    active_call_a
5828        .update(cx_a, |call, cx| {
5829            call.invite(client_b.user_id().unwrap(), None, cx)
5830        })
5831        .await
5832        .unwrap();
5833    deterministic.run_until_parked();
5834    assert_eq!(
5835        contacts(&client_a, cx_a),
5836        [
5837            ("user_b".to_string(), "online", "busy"),
5838            ("user_c".to_string(), "online", "free")
5839        ]
5840    );
5841    assert_eq!(
5842        contacts(&client_b, cx_b),
5843        [
5844            ("user_a".to_string(), "online", "busy"),
5845            ("user_c".to_string(), "online", "free"),
5846            ("user_d".to_string(), "online", "free")
5847        ]
5848    );
5849    assert_eq!(
5850        contacts(&client_c, cx_c),
5851        [
5852            ("user_a".to_string(), "online", "busy"),
5853            ("user_b".to_string(), "online", "busy")
5854        ]
5855    );
5856    assert_eq!(
5857        contacts(&client_d, cx_d),
5858        [("user_b".to_string(), "online", "busy")]
5859    );
5860
5861    server.forbid_connections();
5862    server.disconnect_client(client_a.peer_id().unwrap());
5863    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5864    assert_eq!(contacts(&client_a, cx_a), []);
5865    assert_eq!(
5866        contacts(&client_b, cx_b),
5867        [
5868            ("user_a".to_string(), "offline", "free"),
5869            ("user_c".to_string(), "online", "free"),
5870            ("user_d".to_string(), "online", "free")
5871        ]
5872    );
5873    assert_eq!(
5874        contacts(&client_c, cx_c),
5875        [
5876            ("user_a".to_string(), "offline", "free"),
5877            ("user_b".to_string(), "online", "free")
5878        ]
5879    );
5880    assert_eq!(
5881        contacts(&client_d, cx_d),
5882        [("user_b".to_string(), "online", "free")]
5883    );
5884
5885    // Test removing a contact
5886    client_b
5887        .user_store
5888        .update(cx_b, |store, cx| {
5889            store.remove_contact(client_c.user_id().unwrap(), cx)
5890        })
5891        .await
5892        .unwrap();
5893    deterministic.run_until_parked();
5894    assert_eq!(
5895        contacts(&client_b, cx_b),
5896        [
5897            ("user_a".to_string(), "offline", "free"),
5898            ("user_d".to_string(), "online", "free")
5899        ]
5900    );
5901    assert_eq!(
5902        contacts(&client_c, cx_c),
5903        [("user_a".to_string(), "offline", "free"),]
5904    );
5905
5906    fn contacts(
5907        client: &TestClient,
5908        cx: &TestAppContext,
5909    ) -> Vec<(String, &'static str, &'static str)> {
5910        client.user_store.read_with(cx, |store, _| {
5911            store
5912                .contacts()
5913                .iter()
5914                .map(|contact| {
5915                    (
5916                        contact.user.github_login.clone(),
5917                        if contact.online { "online" } else { "offline" },
5918                        if contact.busy { "busy" } else { "free" },
5919                    )
5920                })
5921                .collect()
5922        })
5923    }
5924}
5925
5926#[gpui::test(iterations = 10)]
5927async fn test_contact_requests(
5928    deterministic: Arc<Deterministic>,
5929    cx_a: &mut TestAppContext,
5930    cx_a2: &mut TestAppContext,
5931    cx_b: &mut TestAppContext,
5932    cx_b2: &mut TestAppContext,
5933    cx_c: &mut TestAppContext,
5934    cx_c2: &mut TestAppContext,
5935) {
5936    deterministic.forbid_parking();
5937
5938    // Connect to a server as 3 clients.
5939    let mut server = TestServer::start(&deterministic).await;
5940    let client_a = server.create_client(cx_a, "user_a").await;
5941    let client_a2 = server.create_client(cx_a2, "user_a").await;
5942    let client_b = server.create_client(cx_b, "user_b").await;
5943    let client_b2 = server.create_client(cx_b2, "user_b").await;
5944    let client_c = server.create_client(cx_c, "user_c").await;
5945    let client_c2 = server.create_client(cx_c2, "user_c").await;
5946
5947    assert_eq!(client_a.user_id().unwrap(), client_a2.user_id().unwrap());
5948    assert_eq!(client_b.user_id().unwrap(), client_b2.user_id().unwrap());
5949    assert_eq!(client_c.user_id().unwrap(), client_c2.user_id().unwrap());
5950
5951    // User A and User C request that user B become their contact.
5952    client_a
5953        .user_store
5954        .update(cx_a, |store, cx| {
5955            store.request_contact(client_b.user_id().unwrap(), cx)
5956        })
5957        .await
5958        .unwrap();
5959    client_c
5960        .user_store
5961        .update(cx_c, |store, cx| {
5962            store.request_contact(client_b.user_id().unwrap(), cx)
5963        })
5964        .await
5965        .unwrap();
5966    deterministic.run_until_parked();
5967
5968    // All users see the pending request appear in all their clients.
5969    assert_eq!(
5970        client_a.summarize_contacts(cx_a).outgoing_requests,
5971        &["user_b"]
5972    );
5973    assert_eq!(
5974        client_a2.summarize_contacts(cx_a2).outgoing_requests,
5975        &["user_b"]
5976    );
5977    assert_eq!(
5978        client_b.summarize_contacts(cx_b).incoming_requests,
5979        &["user_a", "user_c"]
5980    );
5981    assert_eq!(
5982        client_b2.summarize_contacts(cx_b2).incoming_requests,
5983        &["user_a", "user_c"]
5984    );
5985    assert_eq!(
5986        client_c.summarize_contacts(cx_c).outgoing_requests,
5987        &["user_b"]
5988    );
5989    assert_eq!(
5990        client_c2.summarize_contacts(cx_c2).outgoing_requests,
5991        &["user_b"]
5992    );
5993
5994    // Contact requests are present upon connecting (tested here via disconnect/reconnect)
5995    disconnect_and_reconnect(&client_a, cx_a).await;
5996    disconnect_and_reconnect(&client_b, cx_b).await;
5997    disconnect_and_reconnect(&client_c, cx_c).await;
5998    deterministic.run_until_parked();
5999    assert_eq!(
6000        client_a.summarize_contacts(cx_a).outgoing_requests,
6001        &["user_b"]
6002    );
6003    assert_eq!(
6004        client_b.summarize_contacts(cx_b).incoming_requests,
6005        &["user_a", "user_c"]
6006    );
6007    assert_eq!(
6008        client_c.summarize_contacts(cx_c).outgoing_requests,
6009        &["user_b"]
6010    );
6011
6012    // User B accepts the request from user A.
6013    client_b
6014        .user_store
6015        .update(cx_b, |store, cx| {
6016            store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
6017        })
6018        .await
6019        .unwrap();
6020
6021    deterministic.run_until_parked();
6022
6023    // User B sees user A as their contact now in all client, and the incoming request from them is removed.
6024    let contacts_b = client_b.summarize_contacts(cx_b);
6025    assert_eq!(contacts_b.current, &["user_a"]);
6026    assert_eq!(contacts_b.incoming_requests, &["user_c"]);
6027    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
6028    assert_eq!(contacts_b2.current, &["user_a"]);
6029    assert_eq!(contacts_b2.incoming_requests, &["user_c"]);
6030
6031    // User A sees user B as their contact now in all clients, and the outgoing request to them is removed.
6032    let contacts_a = client_a.summarize_contacts(cx_a);
6033    assert_eq!(contacts_a.current, &["user_b"]);
6034    assert!(contacts_a.outgoing_requests.is_empty());
6035    let contacts_a2 = client_a2.summarize_contacts(cx_a2);
6036    assert_eq!(contacts_a2.current, &["user_b"]);
6037    assert!(contacts_a2.outgoing_requests.is_empty());
6038
6039    // Contacts are present upon connecting (tested here via disconnect/reconnect)
6040    disconnect_and_reconnect(&client_a, cx_a).await;
6041    disconnect_and_reconnect(&client_b, cx_b).await;
6042    disconnect_and_reconnect(&client_c, cx_c).await;
6043    deterministic.run_until_parked();
6044    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
6045    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
6046    assert_eq!(
6047        client_b.summarize_contacts(cx_b).incoming_requests,
6048        &["user_c"]
6049    );
6050    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
6051    assert_eq!(
6052        client_c.summarize_contacts(cx_c).outgoing_requests,
6053        &["user_b"]
6054    );
6055
6056    // User B rejects the request from user C.
6057    client_b
6058        .user_store
6059        .update(cx_b, |store, cx| {
6060            store.respond_to_contact_request(client_c.user_id().unwrap(), false, cx)
6061        })
6062        .await
6063        .unwrap();
6064
6065    deterministic.run_until_parked();
6066
6067    // User B doesn't see user C as their contact, and the incoming request from them is removed.
6068    let contacts_b = client_b.summarize_contacts(cx_b);
6069    assert_eq!(contacts_b.current, &["user_a"]);
6070    assert!(contacts_b.incoming_requests.is_empty());
6071    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
6072    assert_eq!(contacts_b2.current, &["user_a"]);
6073    assert!(contacts_b2.incoming_requests.is_empty());
6074
6075    // User C doesn't see user B as their contact, and the outgoing request to them is removed.
6076    let contacts_c = client_c.summarize_contacts(cx_c);
6077    assert!(contacts_c.current.is_empty());
6078    assert!(contacts_c.outgoing_requests.is_empty());
6079    let contacts_c2 = client_c2.summarize_contacts(cx_c2);
6080    assert!(contacts_c2.current.is_empty());
6081    assert!(contacts_c2.outgoing_requests.is_empty());
6082
6083    // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect)
6084    disconnect_and_reconnect(&client_a, cx_a).await;
6085    disconnect_and_reconnect(&client_b, cx_b).await;
6086    disconnect_and_reconnect(&client_c, cx_c).await;
6087    deterministic.run_until_parked();
6088    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
6089    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
6090    assert!(client_b
6091        .summarize_contacts(cx_b)
6092        .incoming_requests
6093        .is_empty());
6094    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
6095    assert!(client_c
6096        .summarize_contacts(cx_c)
6097        .outgoing_requests
6098        .is_empty());
6099
6100    async fn disconnect_and_reconnect(client: &TestClient, cx: &mut TestAppContext) {
6101        client.disconnect(&cx.to_async());
6102        client.clear_contacts(cx).await;
6103        client
6104            .authenticate_and_connect(false, &cx.to_async())
6105            .await
6106            .unwrap();
6107    }
6108}
6109
6110#[gpui::test(iterations = 10)]
6111async fn test_basic_following(
6112    deterministic: Arc<Deterministic>,
6113    cx_a: &mut TestAppContext,
6114    cx_b: &mut TestAppContext,
6115    cx_c: &mut TestAppContext,
6116    cx_d: &mut TestAppContext,
6117) {
6118    deterministic.forbid_parking();
6119
6120    let mut server = TestServer::start(&deterministic).await;
6121    let client_a = server.create_client(cx_a, "user_a").await;
6122    let client_b = server.create_client(cx_b, "user_b").await;
6123    let client_c = server.create_client(cx_c, "user_c").await;
6124    let client_d = server.create_client(cx_d, "user_d").await;
6125    server
6126        .create_room(&mut [
6127            (&client_a, cx_a),
6128            (&client_b, cx_b),
6129            (&client_c, cx_c),
6130            (&client_d, cx_d),
6131        ])
6132        .await;
6133    let active_call_a = cx_a.read(ActiveCall::global);
6134    let active_call_b = cx_b.read(ActiveCall::global);
6135
6136    cx_a.update(editor::init);
6137    cx_b.update(editor::init);
6138
6139    client_a
6140        .fs
6141        .insert_tree(
6142            "/a",
6143            json!({
6144                "1.txt": "one\none\none",
6145                "2.txt": "two\ntwo\ntwo",
6146                "3.txt": "three\nthree\nthree",
6147            }),
6148        )
6149        .await;
6150    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
6151    active_call_a
6152        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
6153        .await
6154        .unwrap();
6155
6156    let project_id = active_call_a
6157        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6158        .await
6159        .unwrap();
6160    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6161    active_call_b
6162        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6163        .await
6164        .unwrap();
6165
6166    let workspace_a = client_a.build_workspace(&project_a, cx_a);
6167    let workspace_b = client_b.build_workspace(&project_b, cx_b);
6168
6169    // Client A opens some editors.
6170    let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
6171    let editor_a1 = workspace_a
6172        .update(cx_a, |workspace, cx| {
6173            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6174        })
6175        .await
6176        .unwrap()
6177        .downcast::<Editor>()
6178        .unwrap();
6179    let editor_a2 = workspace_a
6180        .update(cx_a, |workspace, cx| {
6181            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
6182        })
6183        .await
6184        .unwrap()
6185        .downcast::<Editor>()
6186        .unwrap();
6187
6188    // Client B opens an editor.
6189    let editor_b1 = workspace_b
6190        .update(cx_b, |workspace, cx| {
6191            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6192        })
6193        .await
6194        .unwrap()
6195        .downcast::<Editor>()
6196        .unwrap();
6197
6198    let peer_id_a = client_a.peer_id().unwrap();
6199    let peer_id_b = client_b.peer_id().unwrap();
6200    let peer_id_c = client_c.peer_id().unwrap();
6201    let peer_id_d = client_d.peer_id().unwrap();
6202
6203    // Client A updates their selections in those editors
6204    editor_a1.update(cx_a, |editor, cx| {
6205        editor.handle_input("a", cx);
6206        editor.handle_input("b", cx);
6207        editor.handle_input("c", cx);
6208        editor.select_left(&Default::default(), cx);
6209        assert_eq!(editor.selections.ranges(cx), vec![3..2]);
6210    });
6211    editor_a2.update(cx_a, |editor, cx| {
6212        editor.handle_input("d", cx);
6213        editor.handle_input("e", cx);
6214        editor.select_left(&Default::default(), cx);
6215        assert_eq!(editor.selections.ranges(cx), vec![2..1]);
6216    });
6217
6218    // When client B starts following client A, all visible view states are replicated to client B.
6219    workspace_b
6220        .update(cx_b, |workspace, cx| {
6221            workspace.toggle_follow(peer_id_a, cx).unwrap()
6222        })
6223        .await
6224        .unwrap();
6225
6226    cx_c.foreground().run_until_parked();
6227    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
6228        workspace
6229            .active_item(cx)
6230            .unwrap()
6231            .downcast::<Editor>()
6232            .unwrap()
6233    });
6234    assert_eq!(
6235        cx_b.read(|cx| editor_b2.project_path(cx)),
6236        Some((worktree_id, "2.txt").into())
6237    );
6238    assert_eq!(
6239        editor_b2.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
6240        vec![2..1]
6241    );
6242    assert_eq!(
6243        editor_b1.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
6244        vec![3..2]
6245    );
6246
6247    cx_c.foreground().run_until_parked();
6248    let active_call_c = cx_c.read(ActiveCall::global);
6249    let project_c = client_c.build_remote_project(project_id, cx_c).await;
6250    let workspace_c = client_c.build_workspace(&project_c, cx_c);
6251    active_call_c
6252        .update(cx_c, |call, cx| call.set_location(Some(&project_c), cx))
6253        .await
6254        .unwrap();
6255    drop(project_c);
6256
6257    // Client C also follows client A.
6258    workspace_c
6259        .update(cx_c, |workspace, cx| {
6260            workspace.toggle_follow(peer_id_a, cx).unwrap()
6261        })
6262        .await
6263        .unwrap();
6264
6265    cx_d.foreground().run_until_parked();
6266    let active_call_d = cx_d.read(ActiveCall::global);
6267    let project_d = client_d.build_remote_project(project_id, cx_d).await;
6268    let workspace_d = client_d.build_workspace(&project_d, cx_d);
6269    active_call_d
6270        .update(cx_d, |call, cx| call.set_location(Some(&project_d), cx))
6271        .await
6272        .unwrap();
6273    drop(project_d);
6274
6275    // All clients see that clients B and C are following client A.
6276    cx_c.foreground().run_until_parked();
6277    for (name, active_call, cx) in [
6278        ("A", &active_call_a, &cx_a),
6279        ("B", &active_call_b, &cx_b),
6280        ("C", &active_call_c, &cx_c),
6281        ("D", &active_call_d, &cx_d),
6282    ] {
6283        active_call.read_with(*cx, |call, cx| {
6284            let room = call.room().unwrap().read(cx);
6285            assert_eq!(
6286                room.followers_for(peer_id_a, project_id),
6287                &[peer_id_b, peer_id_c],
6288                "checking followers for A as {name}"
6289            );
6290        });
6291    }
6292
6293    // Client C unfollows client A.
6294    workspace_c.update(cx_c, |workspace, cx| {
6295        workspace.toggle_follow(peer_id_a, cx);
6296    });
6297
6298    // All clients see that clients B is following client A.
6299    cx_c.foreground().run_until_parked();
6300    for (name, active_call, cx) in [
6301        ("A", &active_call_a, &cx_a),
6302        ("B", &active_call_b, &cx_b),
6303        ("C", &active_call_c, &cx_c),
6304        ("D", &active_call_d, &cx_d),
6305    ] {
6306        active_call.read_with(*cx, |call, cx| {
6307            let room = call.room().unwrap().read(cx);
6308            assert_eq!(
6309                room.followers_for(peer_id_a, project_id),
6310                &[peer_id_b],
6311                "checking followers for A as {name}"
6312            );
6313        });
6314    }
6315
6316    // Client C re-follows client A.
6317    workspace_c.update(cx_c, |workspace, cx| {
6318        workspace.toggle_follow(peer_id_a, cx);
6319    });
6320
6321    // All clients see that clients B and C are following client A.
6322    cx_c.foreground().run_until_parked();
6323    for (name, active_call, cx) in [
6324        ("A", &active_call_a, &cx_a),
6325        ("B", &active_call_b, &cx_b),
6326        ("C", &active_call_c, &cx_c),
6327        ("D", &active_call_d, &cx_d),
6328    ] {
6329        active_call.read_with(*cx, |call, cx| {
6330            let room = call.room().unwrap().read(cx);
6331            assert_eq!(
6332                room.followers_for(peer_id_a, project_id),
6333                &[peer_id_b, peer_id_c],
6334                "checking followers for A as {name}"
6335            );
6336        });
6337    }
6338
6339    // Client D follows client C.
6340    workspace_d
6341        .update(cx_d, |workspace, cx| {
6342            workspace.toggle_follow(peer_id_c, cx).unwrap()
6343        })
6344        .await
6345        .unwrap();
6346
6347    // All clients see that D is following C
6348    cx_d.foreground().run_until_parked();
6349    for (name, active_call, cx) in [
6350        ("A", &active_call_a, &cx_a),
6351        ("B", &active_call_b, &cx_b),
6352        ("C", &active_call_c, &cx_c),
6353        ("D", &active_call_d, &cx_d),
6354    ] {
6355        active_call.read_with(*cx, |call, cx| {
6356            let room = call.room().unwrap().read(cx);
6357            assert_eq!(
6358                room.followers_for(peer_id_c, project_id),
6359                &[peer_id_d],
6360                "checking followers for C as {name}"
6361            );
6362        });
6363    }
6364
6365    // Client C closes the project.
6366    cx_c.drop_last(workspace_c);
6367
6368    // Clients A and B see that client B is following A, and client C is not present in the followers.
6369    cx_c.foreground().run_until_parked();
6370    for (name, active_call, cx) in [("A", &active_call_a, &cx_a), ("B", &active_call_b, &cx_b)] {
6371        active_call.read_with(*cx, |call, cx| {
6372            let room = call.room().unwrap().read(cx);
6373            assert_eq!(
6374                room.followers_for(peer_id_a, project_id),
6375                &[peer_id_b],
6376                "checking followers for A as {name}"
6377            );
6378        });
6379    }
6380
6381    // All clients see that no-one is following C
6382    for (name, active_call, cx) in [
6383        ("A", &active_call_a, &cx_a),
6384        ("B", &active_call_b, &cx_b),
6385        ("C", &active_call_c, &cx_c),
6386        ("D", &active_call_d, &cx_d),
6387    ] {
6388        active_call.read_with(*cx, |call, cx| {
6389            let room = call.room().unwrap().read(cx);
6390            assert_eq!(
6391                room.followers_for(peer_id_c, project_id),
6392                &[],
6393                "checking followers for C as {name}"
6394            );
6395        });
6396    }
6397
6398    // When client A activates a different editor, client B does so as well.
6399    workspace_a.update(cx_a, |workspace, cx| {
6400        workspace.activate_item(&editor_a1, cx)
6401    });
6402    deterministic.run_until_parked();
6403    workspace_b.read_with(cx_b, |workspace, cx| {
6404        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
6405    });
6406
6407    // When client A opens a multibuffer, client B does so as well.
6408    let multibuffer_a = cx_a.add_model(|cx| {
6409        let buffer_a1 = project_a.update(cx, |project, cx| {
6410            project
6411                .get_open_buffer(&(worktree_id, "1.txt").into(), cx)
6412                .unwrap()
6413        });
6414        let buffer_a2 = project_a.update(cx, |project, cx| {
6415            project
6416                .get_open_buffer(&(worktree_id, "2.txt").into(), cx)
6417                .unwrap()
6418        });
6419        let mut result = MultiBuffer::new(0);
6420        result.push_excerpts(
6421            buffer_a1,
6422            [ExcerptRange {
6423                context: 0..3,
6424                primary: None,
6425            }],
6426            cx,
6427        );
6428        result.push_excerpts(
6429            buffer_a2,
6430            [ExcerptRange {
6431                context: 4..7,
6432                primary: None,
6433            }],
6434            cx,
6435        );
6436        result
6437    });
6438    let multibuffer_editor_a = workspace_a.update(cx_a, |workspace, cx| {
6439        let editor =
6440            cx.add_view(|cx| Editor::for_multibuffer(multibuffer_a, Some(project_a.clone()), cx));
6441        workspace.add_item(Box::new(editor.clone()), cx);
6442        editor
6443    });
6444    deterministic.run_until_parked();
6445    let multibuffer_editor_b = workspace_b.read_with(cx_b, |workspace, cx| {
6446        workspace
6447            .active_item(cx)
6448            .unwrap()
6449            .downcast::<Editor>()
6450            .unwrap()
6451    });
6452    assert_eq!(
6453        multibuffer_editor_a.read_with(cx_a, |editor, cx| editor.text(cx)),
6454        multibuffer_editor_b.read_with(cx_b, |editor, cx| editor.text(cx)),
6455    );
6456
6457    // When client A navigates back and forth, client B does so as well.
6458    workspace_a
6459        .update(cx_a, |workspace, cx| {
6460            workspace::Pane::go_back(workspace, None, cx)
6461        })
6462        .await
6463        .unwrap();
6464    deterministic.run_until_parked();
6465    workspace_b.read_with(cx_b, |workspace, cx| {
6466        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
6467    });
6468
6469    workspace_a
6470        .update(cx_a, |workspace, cx| {
6471            workspace::Pane::go_back(workspace, None, cx)
6472        })
6473        .await
6474        .unwrap();
6475    deterministic.run_until_parked();
6476    workspace_b.read_with(cx_b, |workspace, cx| {
6477        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b2.id());
6478    });
6479
6480    workspace_a
6481        .update(cx_a, |workspace, cx| {
6482            workspace::Pane::go_forward(workspace, None, cx)
6483        })
6484        .await
6485        .unwrap();
6486    deterministic.run_until_parked();
6487    workspace_b.read_with(cx_b, |workspace, cx| {
6488        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
6489    });
6490
6491    // Changes to client A's editor are reflected on client B.
6492    editor_a1.update(cx_a, |editor, cx| {
6493        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2]));
6494    });
6495    deterministic.run_until_parked();
6496    editor_b1.read_with(cx_b, |editor, cx| {
6497        assert_eq!(editor.selections.ranges(cx), &[1..1, 2..2]);
6498    });
6499
6500    editor_a1.update(cx_a, |editor, cx| editor.set_text("TWO", cx));
6501    deterministic.run_until_parked();
6502    editor_b1.read_with(cx_b, |editor, cx| assert_eq!(editor.text(cx), "TWO"));
6503
6504    editor_a1.update(cx_a, |editor, cx| {
6505        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
6506        editor.set_scroll_position(vec2f(0., 100.), cx);
6507    });
6508    deterministic.run_until_parked();
6509    editor_b1.read_with(cx_b, |editor, cx| {
6510        assert_eq!(editor.selections.ranges(cx), &[3..3]);
6511    });
6512
6513    // After unfollowing, client B stops receiving updates from client A.
6514    workspace_b.update(cx_b, |workspace, cx| {
6515        workspace.unfollow(&workspace.active_pane().clone(), cx)
6516    });
6517    workspace_a.update(cx_a, |workspace, cx| {
6518        workspace.activate_item(&editor_a2, cx)
6519    });
6520    deterministic.run_until_parked();
6521    assert_eq!(
6522        workspace_b.read_with(cx_b, |workspace, cx| workspace
6523            .active_item(cx)
6524            .unwrap()
6525            .id()),
6526        editor_b1.id()
6527    );
6528
6529    // Client A starts following client B.
6530    workspace_a
6531        .update(cx_a, |workspace, cx| {
6532            workspace.toggle_follow(peer_id_b, cx).unwrap()
6533        })
6534        .await
6535        .unwrap();
6536    assert_eq!(
6537        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
6538        Some(peer_id_b)
6539    );
6540    assert_eq!(
6541        workspace_a.read_with(cx_a, |workspace, cx| workspace
6542            .active_item(cx)
6543            .unwrap()
6544            .id()),
6545        editor_a1.id()
6546    );
6547
6548    // Client B activates an external window, which causes a new screen-sharing item to be added to the pane.
6549    let display = MacOSDisplay::new();
6550    active_call_b
6551        .update(cx_b, |call, cx| call.set_location(None, cx))
6552        .await
6553        .unwrap();
6554    active_call_b
6555        .update(cx_b, |call, cx| {
6556            call.room().unwrap().update(cx, |room, cx| {
6557                room.set_display_sources(vec![display.clone()]);
6558                room.share_screen(cx)
6559            })
6560        })
6561        .await
6562        .unwrap();
6563    deterministic.run_until_parked();
6564    let shared_screen = workspace_a.read_with(cx_a, |workspace, cx| {
6565        workspace
6566            .active_item(cx)
6567            .unwrap()
6568            .downcast::<SharedScreen>()
6569            .unwrap()
6570    });
6571
6572    // Client B activates Zed again, which causes the previous editor to become focused again.
6573    active_call_b
6574        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6575        .await
6576        .unwrap();
6577    deterministic.run_until_parked();
6578    workspace_a.read_with(cx_a, |workspace, cx| {
6579        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_a1.id())
6580    });
6581
6582    // Client B activates a multibuffer that was created by following client A. Client A returns to that multibuffer.
6583    workspace_b.update(cx_b, |workspace, cx| {
6584        workspace.activate_item(&multibuffer_editor_b, cx)
6585    });
6586    deterministic.run_until_parked();
6587    workspace_a.read_with(cx_a, |workspace, cx| {
6588        assert_eq!(
6589            workspace.active_item(cx).unwrap().id(),
6590            multibuffer_editor_a.id()
6591        )
6592    });
6593
6594    // Client B activates an external window again, and the previously-opened screen-sharing item
6595    // gets activated.
6596    active_call_b
6597        .update(cx_b, |call, cx| call.set_location(None, cx))
6598        .await
6599        .unwrap();
6600    deterministic.run_until_parked();
6601    assert_eq!(
6602        workspace_a.read_with(cx_a, |workspace, cx| workspace
6603            .active_item(cx)
6604            .unwrap()
6605            .id()),
6606        shared_screen.id()
6607    );
6608
6609    // Following interrupts when client B disconnects.
6610    client_b.disconnect(&cx_b.to_async());
6611    deterministic.advance_clock(RECONNECT_TIMEOUT);
6612    assert_eq!(
6613        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
6614        None
6615    );
6616}
6617
6618#[gpui::test(iterations = 10)]
6619async fn test_join_call_after_screen_was_shared(
6620    deterministic: Arc<Deterministic>,
6621    cx_a: &mut TestAppContext,
6622    cx_b: &mut TestAppContext,
6623) {
6624    deterministic.forbid_parking();
6625    let mut server = TestServer::start(&deterministic).await;
6626
6627    let client_a = server.create_client(cx_a, "user_a").await;
6628    let client_b = server.create_client(cx_b, "user_b").await;
6629    server
6630        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6631        .await;
6632
6633    let active_call_a = cx_a.read(ActiveCall::global);
6634    let active_call_b = cx_b.read(ActiveCall::global);
6635
6636    // Call users B and C from client A.
6637    active_call_a
6638        .update(cx_a, |call, cx| {
6639            call.invite(client_b.user_id().unwrap(), None, cx)
6640        })
6641        .await
6642        .unwrap();
6643    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
6644    deterministic.run_until_parked();
6645    assert_eq!(
6646        room_participants(&room_a, cx_a),
6647        RoomParticipants {
6648            remote: Default::default(),
6649            pending: vec!["user_b".to_string()]
6650        }
6651    );
6652
6653    // User B receives the call.
6654    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
6655    let call_b = incoming_call_b.next().await.unwrap().unwrap();
6656    assert_eq!(call_b.calling_user.github_login, "user_a");
6657
6658    // User A shares their screen
6659    let display = MacOSDisplay::new();
6660    active_call_a
6661        .update(cx_a, |call, cx| {
6662            call.room().unwrap().update(cx, |room, cx| {
6663                room.set_display_sources(vec![display.clone()]);
6664                room.share_screen(cx)
6665            })
6666        })
6667        .await
6668        .unwrap();
6669
6670    client_b.user_store.update(cx_b, |user_store, _| {
6671        user_store.clear_cache();
6672    });
6673
6674    // User B joins the room
6675    active_call_b
6676        .update(cx_b, |call, cx| call.accept_incoming(cx))
6677        .await
6678        .unwrap();
6679    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
6680    assert!(incoming_call_b.next().await.unwrap().is_none());
6681
6682    deterministic.run_until_parked();
6683    assert_eq!(
6684        room_participants(&room_a, cx_a),
6685        RoomParticipants {
6686            remote: vec!["user_b".to_string()],
6687            pending: vec![],
6688        }
6689    );
6690    assert_eq!(
6691        room_participants(&room_b, cx_b),
6692        RoomParticipants {
6693            remote: vec!["user_a".to_string()],
6694            pending: vec![],
6695        }
6696    );
6697
6698    // Ensure User B sees User A's screenshare.
6699    room_b.read_with(cx_b, |room, _| {
6700        assert_eq!(
6701            room.remote_participants()
6702                .get(&client_a.user_id().unwrap())
6703                .unwrap()
6704                .tracks
6705                .len(),
6706            1
6707        );
6708    });
6709}
6710
6711#[gpui::test]
6712async fn test_following_tab_order(
6713    deterministic: Arc<Deterministic>,
6714    cx_a: &mut TestAppContext,
6715    cx_b: &mut TestAppContext,
6716) {
6717    let mut server = TestServer::start(&deterministic).await;
6718    let client_a = server.create_client(cx_a, "user_a").await;
6719    let client_b = server.create_client(cx_b, "user_b").await;
6720    server
6721        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6722        .await;
6723    let active_call_a = cx_a.read(ActiveCall::global);
6724    let active_call_b = cx_b.read(ActiveCall::global);
6725
6726    cx_a.update(editor::init);
6727    cx_b.update(editor::init);
6728
6729    client_a
6730        .fs
6731        .insert_tree(
6732            "/a",
6733            json!({
6734                "1.txt": "one",
6735                "2.txt": "two",
6736                "3.txt": "three",
6737            }),
6738        )
6739        .await;
6740    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
6741    active_call_a
6742        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
6743        .await
6744        .unwrap();
6745
6746    let project_id = active_call_a
6747        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6748        .await
6749        .unwrap();
6750    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6751    active_call_b
6752        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6753        .await
6754        .unwrap();
6755
6756    let workspace_a = client_a.build_workspace(&project_a, cx_a);
6757    let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
6758
6759    let workspace_b = client_b.build_workspace(&project_b, cx_b);
6760    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
6761
6762    let client_b_id = project_a.read_with(cx_a, |project, _| {
6763        project.collaborators().values().next().unwrap().peer_id
6764    });
6765
6766    //Open 1, 3 in that order on client A
6767    workspace_a
6768        .update(cx_a, |workspace, cx| {
6769            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6770        })
6771        .await
6772        .unwrap();
6773    workspace_a
6774        .update(cx_a, |workspace, cx| {
6775            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
6776        })
6777        .await
6778        .unwrap();
6779
6780    let pane_paths = |pane: &ViewHandle<workspace::Pane>, cx: &mut TestAppContext| {
6781        pane.update(cx, |pane, cx| {
6782            pane.items()
6783                .map(|item| {
6784                    item.project_path(cx)
6785                        .unwrap()
6786                        .path
6787                        .to_str()
6788                        .unwrap()
6789                        .to_owned()
6790                })
6791                .collect::<Vec<_>>()
6792        })
6793    };
6794
6795    //Verify that the tabs opened in the order we expect
6796    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt"]);
6797
6798    //Follow client B as client A
6799    workspace_a
6800        .update(cx_a, |workspace, cx| {
6801            workspace.toggle_follow(client_b_id, cx).unwrap()
6802        })
6803        .await
6804        .unwrap();
6805
6806    //Open just 2 on client B
6807    workspace_b
6808        .update(cx_b, |workspace, cx| {
6809            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
6810        })
6811        .await
6812        .unwrap();
6813    deterministic.run_until_parked();
6814
6815    // Verify that newly opened followed file is at the end
6816    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt", "2.txt"]);
6817
6818    //Open just 1 on client B
6819    workspace_b
6820        .update(cx_b, |workspace, cx| {
6821            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6822        })
6823        .await
6824        .unwrap();
6825    assert_eq!(&pane_paths(&pane_b, cx_b), &["2.txt", "1.txt"]);
6826    deterministic.run_until_parked();
6827
6828    // Verify that following into 1 did not reorder
6829    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt", "2.txt"]);
6830}
6831
6832#[gpui::test(iterations = 10)]
6833async fn test_peers_following_each_other(
6834    deterministic: Arc<Deterministic>,
6835    cx_a: &mut TestAppContext,
6836    cx_b: &mut TestAppContext,
6837) {
6838    deterministic.forbid_parking();
6839    let mut server = TestServer::start(&deterministic).await;
6840    let client_a = server.create_client(cx_a, "user_a").await;
6841    let client_b = server.create_client(cx_b, "user_b").await;
6842    server
6843        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6844        .await;
6845    let active_call_a = cx_a.read(ActiveCall::global);
6846    let active_call_b = cx_b.read(ActiveCall::global);
6847
6848    cx_a.update(editor::init);
6849    cx_b.update(editor::init);
6850
6851    // Client A shares a project.
6852    client_a
6853        .fs
6854        .insert_tree(
6855            "/a",
6856            json!({
6857                "1.txt": "one",
6858                "2.txt": "two",
6859                "3.txt": "three",
6860                "4.txt": "four",
6861            }),
6862        )
6863        .await;
6864    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
6865    active_call_a
6866        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
6867        .await
6868        .unwrap();
6869    let project_id = active_call_a
6870        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6871        .await
6872        .unwrap();
6873
6874    // Client B joins the project.
6875    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6876    active_call_b
6877        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6878        .await
6879        .unwrap();
6880
6881    // Client A opens some editors.
6882    let workspace_a = client_a.build_workspace(&project_a, cx_a);
6883    let pane_a1 = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
6884    let _editor_a1 = workspace_a
6885        .update(cx_a, |workspace, cx| {
6886            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6887        })
6888        .await
6889        .unwrap()
6890        .downcast::<Editor>()
6891        .unwrap();
6892
6893    // Client B opens an editor.
6894    let workspace_b = client_b.build_workspace(&project_b, cx_b);
6895    let pane_b1 = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
6896    let _editor_b1 = workspace_b
6897        .update(cx_b, |workspace, cx| {
6898            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
6899        })
6900        .await
6901        .unwrap()
6902        .downcast::<Editor>()
6903        .unwrap();
6904
6905    // Clients A and B follow each other in split panes
6906    workspace_a.update(cx_a, |workspace, cx| {
6907        workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
6908    });
6909    workspace_a
6910        .update(cx_a, |workspace, cx| {
6911            assert_ne!(*workspace.active_pane(), pane_a1);
6912            let leader_id = *project_a.read(cx).collaborators().keys().next().unwrap();
6913            workspace.toggle_follow(leader_id, cx).unwrap()
6914        })
6915        .await
6916        .unwrap();
6917    workspace_b.update(cx_b, |workspace, cx| {
6918        workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
6919    });
6920    workspace_b
6921        .update(cx_b, |workspace, cx| {
6922            assert_ne!(*workspace.active_pane(), pane_b1);
6923            let leader_id = *project_b.read(cx).collaborators().keys().next().unwrap();
6924            workspace.toggle_follow(leader_id, cx).unwrap()
6925        })
6926        .await
6927        .unwrap();
6928
6929    workspace_a.update(cx_a, |workspace, cx| {
6930        workspace.activate_next_pane(cx);
6931    });
6932    // Wait for focus effects to be fully flushed
6933    workspace_a.update(cx_a, |workspace, _| {
6934        assert_eq!(*workspace.active_pane(), pane_a1);
6935    });
6936
6937    workspace_a
6938        .update(cx_a, |workspace, cx| {
6939            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
6940        })
6941        .await
6942        .unwrap();
6943    workspace_b.update(cx_b, |workspace, cx| {
6944        workspace.activate_next_pane(cx);
6945    });
6946
6947    workspace_b
6948        .update(cx_b, |workspace, cx| {
6949            assert_eq!(*workspace.active_pane(), pane_b1);
6950            workspace.open_path((worktree_id, "4.txt"), None, true, cx)
6951        })
6952        .await
6953        .unwrap();
6954    cx_a.foreground().run_until_parked();
6955
6956    // Ensure leader updates don't change the active pane of followers
6957    workspace_a.read_with(cx_a, |workspace, _| {
6958        assert_eq!(*workspace.active_pane(), pane_a1);
6959    });
6960    workspace_b.read_with(cx_b, |workspace, _| {
6961        assert_eq!(*workspace.active_pane(), pane_b1);
6962    });
6963
6964    // Ensure peers following each other doesn't cause an infinite loop.
6965    assert_eq!(
6966        workspace_a.read_with(cx_a, |workspace, cx| workspace
6967            .active_item(cx)
6968            .unwrap()
6969            .project_path(cx)),
6970        Some((worktree_id, "3.txt").into())
6971    );
6972    workspace_a.update(cx_a, |workspace, cx| {
6973        assert_eq!(
6974            workspace.active_item(cx).unwrap().project_path(cx),
6975            Some((worktree_id, "3.txt").into())
6976        );
6977        workspace.activate_next_pane(cx);
6978    });
6979
6980    workspace_a.update(cx_a, |workspace, cx| {
6981        assert_eq!(
6982            workspace.active_item(cx).unwrap().project_path(cx),
6983            Some((worktree_id, "4.txt").into())
6984        );
6985    });
6986
6987    workspace_b.update(cx_b, |workspace, cx| {
6988        assert_eq!(
6989            workspace.active_item(cx).unwrap().project_path(cx),
6990            Some((worktree_id, "4.txt").into())
6991        );
6992        workspace.activate_next_pane(cx);
6993    });
6994
6995    workspace_b.update(cx_b, |workspace, cx| {
6996        assert_eq!(
6997            workspace.active_item(cx).unwrap().project_path(cx),
6998            Some((worktree_id, "3.txt").into())
6999        );
7000    });
7001}
7002
7003#[gpui::test(iterations = 10)]
7004async fn test_auto_unfollowing(
7005    deterministic: Arc<Deterministic>,
7006    cx_a: &mut TestAppContext,
7007    cx_b: &mut TestAppContext,
7008) {
7009    deterministic.forbid_parking();
7010
7011    // 2 clients connect to a server.
7012    let mut server = TestServer::start(&deterministic).await;
7013    let client_a = server.create_client(cx_a, "user_a").await;
7014    let client_b = server.create_client(cx_b, "user_b").await;
7015    server
7016        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7017        .await;
7018    let active_call_a = cx_a.read(ActiveCall::global);
7019    let active_call_b = cx_b.read(ActiveCall::global);
7020
7021    cx_a.update(editor::init);
7022    cx_b.update(editor::init);
7023
7024    // Client A shares a project.
7025    client_a
7026        .fs
7027        .insert_tree(
7028            "/a",
7029            json!({
7030                "1.txt": "one",
7031                "2.txt": "two",
7032                "3.txt": "three",
7033            }),
7034        )
7035        .await;
7036    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7037    active_call_a
7038        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
7039        .await
7040        .unwrap();
7041
7042    let project_id = active_call_a
7043        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7044        .await
7045        .unwrap();
7046    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7047    active_call_b
7048        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
7049        .await
7050        .unwrap();
7051
7052    // Client A opens some editors.
7053    let workspace_a = client_a.build_workspace(&project_a, cx_a);
7054    let _editor_a1 = workspace_a
7055        .update(cx_a, |workspace, cx| {
7056            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
7057        })
7058        .await
7059        .unwrap()
7060        .downcast::<Editor>()
7061        .unwrap();
7062
7063    // Client B starts following client A.
7064    let workspace_b = client_b.build_workspace(&project_b, cx_b);
7065    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
7066    let leader_id = project_b.read_with(cx_b, |project, _| {
7067        project.collaborators().values().next().unwrap().peer_id
7068    });
7069    workspace_b
7070        .update(cx_b, |workspace, cx| {
7071            workspace.toggle_follow(leader_id, cx).unwrap()
7072        })
7073        .await
7074        .unwrap();
7075    assert_eq!(
7076        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7077        Some(leader_id)
7078    );
7079    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
7080        workspace
7081            .active_item(cx)
7082            .unwrap()
7083            .downcast::<Editor>()
7084            .unwrap()
7085    });
7086
7087    // When client B moves, it automatically stops following client A.
7088    editor_b2.update(cx_b, |editor, cx| editor.move_right(&editor::MoveRight, cx));
7089    assert_eq!(
7090        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7091        None
7092    );
7093
7094    workspace_b
7095        .update(cx_b, |workspace, cx| {
7096            workspace.toggle_follow(leader_id, cx).unwrap()
7097        })
7098        .await
7099        .unwrap();
7100    assert_eq!(
7101        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7102        Some(leader_id)
7103    );
7104
7105    // When client B edits, it automatically stops following client A.
7106    editor_b2.update(cx_b, |editor, cx| editor.insert("X", cx));
7107    assert_eq!(
7108        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7109        None
7110    );
7111
7112    workspace_b
7113        .update(cx_b, |workspace, cx| {
7114            workspace.toggle_follow(leader_id, cx).unwrap()
7115        })
7116        .await
7117        .unwrap();
7118    assert_eq!(
7119        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7120        Some(leader_id)
7121    );
7122
7123    // When client B scrolls, it automatically stops following client A.
7124    editor_b2.update(cx_b, |editor, cx| {
7125        editor.set_scroll_position(vec2f(0., 3.), cx)
7126    });
7127    assert_eq!(
7128        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7129        None
7130    );
7131
7132    workspace_b
7133        .update(cx_b, |workspace, cx| {
7134            workspace.toggle_follow(leader_id, cx).unwrap()
7135        })
7136        .await
7137        .unwrap();
7138    assert_eq!(
7139        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7140        Some(leader_id)
7141    );
7142
7143    // When client B activates a different pane, it continues following client A in the original pane.
7144    workspace_b.update(cx_b, |workspace, cx| {
7145        workspace.split_pane(pane_b.clone(), SplitDirection::Right, cx)
7146    });
7147    assert_eq!(
7148        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7149        Some(leader_id)
7150    );
7151
7152    workspace_b.update(cx_b, |workspace, cx| workspace.activate_next_pane(cx));
7153    assert_eq!(
7154        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7155        Some(leader_id)
7156    );
7157
7158    // When client B activates a different item in the original pane, it automatically stops following client A.
7159    workspace_b
7160        .update(cx_b, |workspace, cx| {
7161            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
7162        })
7163        .await
7164        .unwrap();
7165    assert_eq!(
7166        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7167        None
7168    );
7169}
7170
7171#[gpui::test(iterations = 10)]
7172async fn test_peers_simultaneously_following_each_other(
7173    deterministic: Arc<Deterministic>,
7174    cx_a: &mut TestAppContext,
7175    cx_b: &mut TestAppContext,
7176) {
7177    deterministic.forbid_parking();
7178
7179    let mut server = TestServer::start(&deterministic).await;
7180    let client_a = server.create_client(cx_a, "user_a").await;
7181    let client_b = server.create_client(cx_b, "user_b").await;
7182    server
7183        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7184        .await;
7185    let active_call_a = cx_a.read(ActiveCall::global);
7186
7187    cx_a.update(editor::init);
7188    cx_b.update(editor::init);
7189
7190    client_a.fs.insert_tree("/a", json!({})).await;
7191    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
7192    let workspace_a = client_a.build_workspace(&project_a, cx_a);
7193    let project_id = active_call_a
7194        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7195        .await
7196        .unwrap();
7197
7198    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7199    let workspace_b = client_b.build_workspace(&project_b, cx_b);
7200
7201    deterministic.run_until_parked();
7202    let client_a_id = project_b.read_with(cx_b, |project, _| {
7203        project.collaborators().values().next().unwrap().peer_id
7204    });
7205    let client_b_id = project_a.read_with(cx_a, |project, _| {
7206        project.collaborators().values().next().unwrap().peer_id
7207    });
7208
7209    let a_follow_b = workspace_a.update(cx_a, |workspace, cx| {
7210        workspace.toggle_follow(client_b_id, cx).unwrap()
7211    });
7212    let b_follow_a = workspace_b.update(cx_b, |workspace, cx| {
7213        workspace.toggle_follow(client_a_id, cx).unwrap()
7214    });
7215
7216    futures::try_join!(a_follow_b, b_follow_a).unwrap();
7217    workspace_a.read_with(cx_a, |workspace, _| {
7218        assert_eq!(
7219            workspace.leader_for_pane(workspace.active_pane()),
7220            Some(client_b_id)
7221        );
7222    });
7223    workspace_b.read_with(cx_b, |workspace, _| {
7224        assert_eq!(
7225            workspace.leader_for_pane(workspace.active_pane()),
7226            Some(client_a_id)
7227        );
7228    });
7229}
7230
7231#[derive(Debug, Eq, PartialEq)]
7232struct RoomParticipants {
7233    remote: Vec<String>,
7234    pending: Vec<String>,
7235}
7236
7237fn room_participants(room: &ModelHandle<Room>, cx: &mut TestAppContext) -> RoomParticipants {
7238    room.read_with(cx, |room, _| {
7239        let mut remote = room
7240            .remote_participants()
7241            .iter()
7242            .map(|(_, participant)| participant.user.github_login.clone())
7243            .collect::<Vec<_>>();
7244        let mut pending = room
7245            .pending_participants()
7246            .iter()
7247            .map(|user| user.github_login.clone())
7248            .collect::<Vec<_>>();
7249        remote.sort();
7250        pending.sort();
7251        RoomParticipants { remote, pending }
7252    })
7253}