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::{repository::GitFileStatus, 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),
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),
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),
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),
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),
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),
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)
2587                .collect::<Vec<_>>()
2588        );
2589
2590        git::diff::assert_hunks(
2591            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
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),
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    deterministic.run_until_parked();
2692    project_remote_c.read_with(cx_c, |project, cx| {
2693        assert_branch(Some("branch-2"), project, cx)
2694    });
2695}
2696
2697#[gpui::test]
2698async fn test_git_status_sync(
2699    deterministic: Arc<Deterministic>,
2700    cx_a: &mut TestAppContext,
2701    cx_b: &mut TestAppContext,
2702    cx_c: &mut TestAppContext,
2703) {
2704    deterministic.forbid_parking();
2705    let mut server = TestServer::start(&deterministic).await;
2706    let client_a = server.create_client(cx_a, "user_a").await;
2707    let client_b = server.create_client(cx_b, "user_b").await;
2708    let client_c = server.create_client(cx_c, "user_c").await;
2709    server
2710        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
2711        .await;
2712    let active_call_a = cx_a.read(ActiveCall::global);
2713
2714    client_a
2715        .fs
2716        .insert_tree(
2717            "/dir",
2718            json!({
2719            ".git": {},
2720            "a.txt": "a",
2721            "b.txt": "b",
2722            }),
2723        )
2724        .await;
2725
2726    const A_TXT: &'static str = "a.txt";
2727    const B_TXT: &'static str = "b.txt";
2728
2729    client_a
2730        .fs
2731        .as_fake()
2732        .set_status_for_repo(
2733            Path::new("/dir/.git"),
2734            &[
2735                (&Path::new(A_TXT), GitFileStatus::Added),
2736                (&Path::new(B_TXT), GitFileStatus::Added),
2737            ],
2738        )
2739        .await;
2740
2741    let (project_local, _worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2742    let project_id = active_call_a
2743        .update(cx_a, |call, cx| {
2744            call.share_project(project_local.clone(), cx)
2745        })
2746        .await
2747        .unwrap();
2748
2749    let project_remote = client_b.build_remote_project(project_id, cx_b).await;
2750
2751    // Wait for it to catch up to the new status
2752    deterministic.run_until_parked();
2753
2754    #[track_caller]
2755    fn assert_status(
2756        file: &impl AsRef<Path>,
2757        status: Option<GitFileStatus>,
2758        project: &Project,
2759        cx: &AppContext,
2760    ) {
2761        let file = file.as_ref();
2762        let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
2763        assert_eq!(worktrees.len(), 1);
2764        let worktree = worktrees[0].clone();
2765        let snapshot = worktree.read(cx).snapshot();
2766        let root_entry = snapshot.root_git_entry().unwrap();
2767        assert_eq!(root_entry.status_for_file(&snapshot, file), status);
2768    }
2769
2770    // Smoke test status reading
2771    project_local.read_with(cx_a, |project, cx| {
2772        assert_status(&Path::new(A_TXT), Some(GitFileStatus::Added), project, cx);
2773        assert_status(&Path::new(B_TXT), Some(GitFileStatus::Added), project, cx);
2774    });
2775    project_remote.read_with(cx_b, |project, cx| {
2776        assert_status(&Path::new(A_TXT), Some(GitFileStatus::Added), project, cx);
2777        assert_status(&Path::new(B_TXT), Some(GitFileStatus::Added), project, cx);
2778    });
2779
2780    client_a
2781        .fs
2782        .as_fake()
2783        .set_status_for_repo(
2784            Path::new("/dir/.git"),
2785            &[
2786                (&Path::new(A_TXT), GitFileStatus::Modified),
2787                (&Path::new(B_TXT), GitFileStatus::Modified),
2788            ],
2789        )
2790        .await;
2791
2792    // Wait for buffer_local_a to receive it
2793    deterministic.run_until_parked();
2794
2795    // Smoke test status reading
2796    project_local.read_with(cx_a, |project, cx| {
2797        assert_status(
2798            &Path::new(A_TXT),
2799            Some(GitFileStatus::Modified),
2800            project,
2801            cx,
2802        );
2803        assert_status(
2804            &Path::new(B_TXT),
2805            Some(GitFileStatus::Modified),
2806            project,
2807            cx,
2808        );
2809    });
2810    project_remote.read_with(cx_b, |project, cx| {
2811        assert_status(
2812            &Path::new(A_TXT),
2813            Some(GitFileStatus::Modified),
2814            project,
2815            cx,
2816        );
2817        assert_status(
2818            &Path::new(B_TXT),
2819            Some(GitFileStatus::Modified),
2820            project,
2821            cx,
2822        );
2823    });
2824
2825    // And synchronization while joining
2826    let project_remote_c = client_c.build_remote_project(project_id, cx_c).await;
2827    deterministic.run_until_parked();
2828
2829    project_remote_c.read_with(cx_c, |project, cx| {
2830        assert_status(
2831            &Path::new(A_TXT),
2832            Some(GitFileStatus::Modified),
2833            project,
2834            cx,
2835        );
2836        assert_status(
2837            &Path::new(B_TXT),
2838            Some(GitFileStatus::Modified),
2839            project,
2840            cx,
2841        );
2842    });
2843}
2844
2845#[gpui::test(iterations = 10)]
2846async fn test_fs_operations(
2847    deterministic: Arc<Deterministic>,
2848    cx_a: &mut TestAppContext,
2849    cx_b: &mut TestAppContext,
2850) {
2851    deterministic.forbid_parking();
2852    let mut server = TestServer::start(&deterministic).await;
2853    let client_a = server.create_client(cx_a, "user_a").await;
2854    let client_b = server.create_client(cx_b, "user_b").await;
2855    server
2856        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2857        .await;
2858    let active_call_a = cx_a.read(ActiveCall::global);
2859
2860    client_a
2861        .fs
2862        .insert_tree(
2863            "/dir",
2864            json!({
2865                "a.txt": "a-contents",
2866                "b.txt": "b-contents",
2867            }),
2868        )
2869        .await;
2870    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2871    let project_id = active_call_a
2872        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2873        .await
2874        .unwrap();
2875    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2876
2877    let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
2878    let worktree_b = project_b.read_with(cx_b, |project, cx| project.worktrees(cx).next().unwrap());
2879
2880    let entry = project_b
2881        .update(cx_b, |project, cx| {
2882            project
2883                .create_entry((worktree_id, "c.txt"), false, cx)
2884                .unwrap()
2885        })
2886        .await
2887        .unwrap();
2888    worktree_a.read_with(cx_a, |worktree, _| {
2889        assert_eq!(
2890            worktree
2891                .paths()
2892                .map(|p| p.to_string_lossy())
2893                .collect::<Vec<_>>(),
2894            ["a.txt", "b.txt", "c.txt"]
2895        );
2896    });
2897    worktree_b.read_with(cx_b, |worktree, _| {
2898        assert_eq!(
2899            worktree
2900                .paths()
2901                .map(|p| p.to_string_lossy())
2902                .collect::<Vec<_>>(),
2903            ["a.txt", "b.txt", "c.txt"]
2904        );
2905    });
2906
2907    project_b
2908        .update(cx_b, |project, cx| {
2909            project.rename_entry(entry.id, Path::new("d.txt"), cx)
2910        })
2911        .unwrap()
2912        .await
2913        .unwrap();
2914    worktree_a.read_with(cx_a, |worktree, _| {
2915        assert_eq!(
2916            worktree
2917                .paths()
2918                .map(|p| p.to_string_lossy())
2919                .collect::<Vec<_>>(),
2920            ["a.txt", "b.txt", "d.txt"]
2921        );
2922    });
2923    worktree_b.read_with(cx_b, |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"]
2930        );
2931    });
2932
2933    let dir_entry = project_b
2934        .update(cx_b, |project, cx| {
2935            project
2936                .create_entry((worktree_id, "DIR"), true, cx)
2937                .unwrap()
2938        })
2939        .await
2940        .unwrap();
2941    worktree_a.read_with(cx_a, |worktree, _| {
2942        assert_eq!(
2943            worktree
2944                .paths()
2945                .map(|p| p.to_string_lossy())
2946                .collect::<Vec<_>>(),
2947            ["DIR", "a.txt", "b.txt", "d.txt"]
2948        );
2949    });
2950    worktree_b.read_with(cx_b, |worktree, _| {
2951        assert_eq!(
2952            worktree
2953                .paths()
2954                .map(|p| p.to_string_lossy())
2955                .collect::<Vec<_>>(),
2956            ["DIR", "a.txt", "b.txt", "d.txt"]
2957        );
2958    });
2959
2960    project_b
2961        .update(cx_b, |project, cx| {
2962            project
2963                .create_entry((worktree_id, "DIR/e.txt"), false, cx)
2964                .unwrap()
2965        })
2966        .await
2967        .unwrap();
2968    project_b
2969        .update(cx_b, |project, cx| {
2970            project
2971                .create_entry((worktree_id, "DIR/SUBDIR"), true, cx)
2972                .unwrap()
2973        })
2974        .await
2975        .unwrap();
2976    project_b
2977        .update(cx_b, |project, cx| {
2978            project
2979                .create_entry((worktree_id, "DIR/SUBDIR/f.txt"), false, cx)
2980                .unwrap()
2981        })
2982        .await
2983        .unwrap();
2984    worktree_a.read_with(cx_a, |worktree, _| {
2985        assert_eq!(
2986            worktree
2987                .paths()
2988                .map(|p| p.to_string_lossy())
2989                .collect::<Vec<_>>(),
2990            [
2991                "DIR",
2992                "DIR/SUBDIR",
2993                "DIR/SUBDIR/f.txt",
2994                "DIR/e.txt",
2995                "a.txt",
2996                "b.txt",
2997                "d.txt"
2998            ]
2999        );
3000    });
3001    worktree_b.read_with(cx_b, |worktree, _| {
3002        assert_eq!(
3003            worktree
3004                .paths()
3005                .map(|p| p.to_string_lossy())
3006                .collect::<Vec<_>>(),
3007            [
3008                "DIR",
3009                "DIR/SUBDIR",
3010                "DIR/SUBDIR/f.txt",
3011                "DIR/e.txt",
3012                "a.txt",
3013                "b.txt",
3014                "d.txt"
3015            ]
3016        );
3017    });
3018
3019    project_b
3020        .update(cx_b, |project, cx| {
3021            project
3022                .copy_entry(entry.id, Path::new("f.txt"), cx)
3023                .unwrap()
3024        })
3025        .await
3026        .unwrap();
3027    worktree_a.read_with(cx_a, |worktree, _| {
3028        assert_eq!(
3029            worktree
3030                .paths()
3031                .map(|p| p.to_string_lossy())
3032                .collect::<Vec<_>>(),
3033            [
3034                "DIR",
3035                "DIR/SUBDIR",
3036                "DIR/SUBDIR/f.txt",
3037                "DIR/e.txt",
3038                "a.txt",
3039                "b.txt",
3040                "d.txt",
3041                "f.txt"
3042            ]
3043        );
3044    });
3045    worktree_b.read_with(cx_b, |worktree, _| {
3046        assert_eq!(
3047            worktree
3048                .paths()
3049                .map(|p| p.to_string_lossy())
3050                .collect::<Vec<_>>(),
3051            [
3052                "DIR",
3053                "DIR/SUBDIR",
3054                "DIR/SUBDIR/f.txt",
3055                "DIR/e.txt",
3056                "a.txt",
3057                "b.txt",
3058                "d.txt",
3059                "f.txt"
3060            ]
3061        );
3062    });
3063
3064    project_b
3065        .update(cx_b, |project, cx| {
3066            project.delete_entry(dir_entry.id, cx).unwrap()
3067        })
3068        .await
3069        .unwrap();
3070    deterministic.run_until_parked();
3071
3072    worktree_a.read_with(cx_a, |worktree, _| {
3073        assert_eq!(
3074            worktree
3075                .paths()
3076                .map(|p| p.to_string_lossy())
3077                .collect::<Vec<_>>(),
3078            ["a.txt", "b.txt", "d.txt", "f.txt"]
3079        );
3080    });
3081    worktree_b.read_with(cx_b, |worktree, _| {
3082        assert_eq!(
3083            worktree
3084                .paths()
3085                .map(|p| p.to_string_lossy())
3086                .collect::<Vec<_>>(),
3087            ["a.txt", "b.txt", "d.txt", "f.txt"]
3088        );
3089    });
3090
3091    project_b
3092        .update(cx_b, |project, cx| {
3093            project.delete_entry(entry.id, cx).unwrap()
3094        })
3095        .await
3096        .unwrap();
3097    worktree_a.read_with(cx_a, |worktree, _| {
3098        assert_eq!(
3099            worktree
3100                .paths()
3101                .map(|p| p.to_string_lossy())
3102                .collect::<Vec<_>>(),
3103            ["a.txt", "b.txt", "f.txt"]
3104        );
3105    });
3106    worktree_b.read_with(cx_b, |worktree, _| {
3107        assert_eq!(
3108            worktree
3109                .paths()
3110                .map(|p| p.to_string_lossy())
3111                .collect::<Vec<_>>(),
3112            ["a.txt", "b.txt", "f.txt"]
3113        );
3114    });
3115}
3116
3117#[gpui::test(iterations = 10)]
3118async fn test_buffer_conflict_after_save(
3119    deterministic: Arc<Deterministic>,
3120    cx_a: &mut TestAppContext,
3121    cx_b: &mut TestAppContext,
3122) {
3123    deterministic.forbid_parking();
3124    let mut server = TestServer::start(&deterministic).await;
3125    let client_a = server.create_client(cx_a, "user_a").await;
3126    let client_b = server.create_client(cx_b, "user_b").await;
3127    server
3128        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3129        .await;
3130    let active_call_a = cx_a.read(ActiveCall::global);
3131
3132    client_a
3133        .fs
3134        .insert_tree(
3135            "/dir",
3136            json!({
3137                "a.txt": "a-contents",
3138            }),
3139        )
3140        .await;
3141    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3142    let project_id = active_call_a
3143        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3144        .await
3145        .unwrap();
3146    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3147
3148    // Open a buffer as client B
3149    let buffer_b = project_b
3150        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3151        .await
3152        .unwrap();
3153
3154    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "world ")], None, cx));
3155    buffer_b.read_with(cx_b, |buf, _| {
3156        assert!(buf.is_dirty());
3157        assert!(!buf.has_conflict());
3158    });
3159
3160    project_b
3161        .update(cx_b, |project, cx| {
3162            project.save_buffer(buffer_b.clone(), cx)
3163        })
3164        .await
3165        .unwrap();
3166    cx_a.foreground().forbid_parking();
3167    buffer_b.read_with(cx_b, |buffer_b, _| assert!(!buffer_b.is_dirty()));
3168    buffer_b.read_with(cx_b, |buf, _| {
3169        assert!(!buf.has_conflict());
3170    });
3171
3172    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "hello ")], None, cx));
3173    buffer_b.read_with(cx_b, |buf, _| {
3174        assert!(buf.is_dirty());
3175        assert!(!buf.has_conflict());
3176    });
3177}
3178
3179#[gpui::test(iterations = 10)]
3180async fn test_buffer_reloading(
3181    deterministic: Arc<Deterministic>,
3182    cx_a: &mut TestAppContext,
3183    cx_b: &mut TestAppContext,
3184) {
3185    deterministic.forbid_parking();
3186    let mut server = TestServer::start(&deterministic).await;
3187    let client_a = server.create_client(cx_a, "user_a").await;
3188    let client_b = server.create_client(cx_b, "user_b").await;
3189    server
3190        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3191        .await;
3192    let active_call_a = cx_a.read(ActiveCall::global);
3193
3194    client_a
3195        .fs
3196        .insert_tree(
3197            "/dir",
3198            json!({
3199                "a.txt": "a\nb\nc",
3200            }),
3201        )
3202        .await;
3203    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3204    let project_id = active_call_a
3205        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3206        .await
3207        .unwrap();
3208    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3209
3210    // Open a buffer as client B
3211    let buffer_b = project_b
3212        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3213        .await
3214        .unwrap();
3215    buffer_b.read_with(cx_b, |buf, _| {
3216        assert!(!buf.is_dirty());
3217        assert!(!buf.has_conflict());
3218        assert_eq!(buf.line_ending(), LineEnding::Unix);
3219    });
3220
3221    let new_contents = Rope::from("d\ne\nf");
3222    client_a
3223        .fs
3224        .save("/dir/a.txt".as_ref(), &new_contents, LineEnding::Windows)
3225        .await
3226        .unwrap();
3227    cx_a.foreground().run_until_parked();
3228    buffer_b.read_with(cx_b, |buf, _| {
3229        assert_eq!(buf.text(), new_contents.to_string());
3230        assert!(!buf.is_dirty());
3231        assert!(!buf.has_conflict());
3232        assert_eq!(buf.line_ending(), LineEnding::Windows);
3233    });
3234}
3235
3236#[gpui::test(iterations = 10)]
3237async fn test_editing_while_guest_opens_buffer(
3238    deterministic: Arc<Deterministic>,
3239    cx_a: &mut TestAppContext,
3240    cx_b: &mut TestAppContext,
3241) {
3242    deterministic.forbid_parking();
3243    let mut server = TestServer::start(&deterministic).await;
3244    let client_a = server.create_client(cx_a, "user_a").await;
3245    let client_b = server.create_client(cx_b, "user_b").await;
3246    server
3247        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3248        .await;
3249    let active_call_a = cx_a.read(ActiveCall::global);
3250
3251    client_a
3252        .fs
3253        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3254        .await;
3255    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3256    let project_id = active_call_a
3257        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3258        .await
3259        .unwrap();
3260    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3261
3262    // Open a buffer as client A
3263    let buffer_a = project_a
3264        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3265        .await
3266        .unwrap();
3267
3268    // Start opening the same buffer as client B
3269    let buffer_b = cx_b
3270        .background()
3271        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)));
3272
3273    // Edit the buffer as client A while client B is still opening it.
3274    cx_b.background().simulate_random_delay().await;
3275    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "X")], None, cx));
3276    cx_b.background().simulate_random_delay().await;
3277    buffer_a.update(cx_a, |buf, cx| buf.edit([(1..1, "Y")], None, cx));
3278
3279    let text = buffer_a.read_with(cx_a, |buf, _| buf.text());
3280    let buffer_b = buffer_b.await.unwrap();
3281    cx_a.foreground().run_until_parked();
3282    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), text));
3283}
3284
3285#[gpui::test]
3286async fn test_newline_above_or_below_does_not_move_guest_cursor(
3287    deterministic: Arc<Deterministic>,
3288    cx_a: &mut TestAppContext,
3289    cx_b: &mut TestAppContext,
3290) {
3291    deterministic.forbid_parking();
3292    let mut server = TestServer::start(&deterministic).await;
3293    let client_a = server.create_client(cx_a, "user_a").await;
3294    let client_b = server.create_client(cx_b, "user_b").await;
3295    server
3296        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3297        .await;
3298    let active_call_a = cx_a.read(ActiveCall::global);
3299
3300    client_a
3301        .fs
3302        .insert_tree("/dir", json!({ "a.txt": "Some text\n" }))
3303        .await;
3304    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3305    let project_id = active_call_a
3306        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3307        .await
3308        .unwrap();
3309
3310    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3311
3312    // Open a buffer as client A
3313    let buffer_a = project_a
3314        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3315        .await
3316        .unwrap();
3317    let (window_a, _) = cx_a.add_window(|_| EmptyView);
3318    let editor_a = cx_a.add_view(window_a, |cx| {
3319        Editor::for_buffer(buffer_a, Some(project_a), cx)
3320    });
3321    let mut editor_cx_a = EditorTestContext {
3322        cx: cx_a,
3323        window_id: window_a,
3324        editor: editor_a,
3325    };
3326
3327    // Open a buffer as client B
3328    let buffer_b = project_b
3329        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3330        .await
3331        .unwrap();
3332    let (window_b, _) = cx_b.add_window(|_| EmptyView);
3333    let editor_b = cx_b.add_view(window_b, |cx| {
3334        Editor::for_buffer(buffer_b, Some(project_b), cx)
3335    });
3336    let mut editor_cx_b = EditorTestContext {
3337        cx: cx_b,
3338        window_id: window_b,
3339        editor: editor_b,
3340    };
3341
3342    // Test newline above
3343    editor_cx_a.set_selections_state(indoc! {"
3344        Some textˇ
3345    "});
3346    editor_cx_b.set_selections_state(indoc! {"
3347        Some textˇ
3348    "});
3349    editor_cx_a.update_editor(|editor, cx| editor.newline_above(&editor::NewlineAbove, cx));
3350    deterministic.run_until_parked();
3351    editor_cx_a.assert_editor_state(indoc! {"
3352        ˇ
3353        Some text
3354    "});
3355    editor_cx_b.assert_editor_state(indoc! {"
3356
3357        Some textˇ
3358    "});
3359
3360    // Test newline below
3361    editor_cx_a.set_selections_state(indoc! {"
3362
3363        Some textˇ
3364    "});
3365    editor_cx_b.set_selections_state(indoc! {"
3366
3367        Some textˇ
3368    "});
3369    editor_cx_a.update_editor(|editor, cx| editor.newline_below(&editor::NewlineBelow, cx));
3370    deterministic.run_until_parked();
3371    editor_cx_a.assert_editor_state(indoc! {"
3372
3373        Some text
3374        ˇ
3375    "});
3376    editor_cx_b.assert_editor_state(indoc! {"
3377
3378        Some textˇ
3379
3380    "});
3381}
3382
3383#[gpui::test(iterations = 10)]
3384async fn test_leaving_worktree_while_opening_buffer(
3385    deterministic: Arc<Deterministic>,
3386    cx_a: &mut TestAppContext,
3387    cx_b: &mut TestAppContext,
3388) {
3389    deterministic.forbid_parking();
3390    let mut server = TestServer::start(&deterministic).await;
3391    let client_a = server.create_client(cx_a, "user_a").await;
3392    let client_b = server.create_client(cx_b, "user_b").await;
3393    server
3394        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3395        .await;
3396    let active_call_a = cx_a.read(ActiveCall::global);
3397
3398    client_a
3399        .fs
3400        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3401        .await;
3402    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3403    let project_id = active_call_a
3404        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3405        .await
3406        .unwrap();
3407    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3408
3409    // See that a guest has joined as client A.
3410    cx_a.foreground().run_until_parked();
3411    project_a.read_with(cx_a, |p, _| assert_eq!(p.collaborators().len(), 1));
3412
3413    // Begin opening a buffer as client B, but leave the project before the open completes.
3414    let buffer_b = cx_b
3415        .background()
3416        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)));
3417    cx_b.update(|_| drop(project_b));
3418    drop(buffer_b);
3419
3420    // See that the guest has left.
3421    cx_a.foreground().run_until_parked();
3422    project_a.read_with(cx_a, |p, _| assert!(p.collaborators().is_empty()));
3423}
3424
3425#[gpui::test(iterations = 10)]
3426async fn test_canceling_buffer_opening(
3427    deterministic: Arc<Deterministic>,
3428    cx_a: &mut TestAppContext,
3429    cx_b: &mut TestAppContext,
3430) {
3431    deterministic.forbid_parking();
3432
3433    let mut server = TestServer::start(&deterministic).await;
3434    let client_a = server.create_client(cx_a, "user_a").await;
3435    let client_b = server.create_client(cx_b, "user_b").await;
3436    server
3437        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3438        .await;
3439    let active_call_a = cx_a.read(ActiveCall::global);
3440
3441    client_a
3442        .fs
3443        .insert_tree(
3444            "/dir",
3445            json!({
3446                "a.txt": "abc",
3447            }),
3448        )
3449        .await;
3450    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3451    let project_id = active_call_a
3452        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3453        .await
3454        .unwrap();
3455    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3456
3457    let buffer_a = project_a
3458        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3459        .await
3460        .unwrap();
3461
3462    // Open a buffer as client B but cancel after a random amount of time.
3463    let buffer_b = project_b.update(cx_b, |p, cx| {
3464        p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3465    });
3466    deterministic.simulate_random_delay().await;
3467    drop(buffer_b);
3468
3469    // Try opening the same buffer again as client B, and ensure we can
3470    // still do it despite the cancellation above.
3471    let buffer_b = project_b
3472        .update(cx_b, |p, cx| {
3473            p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3474        })
3475        .await
3476        .unwrap();
3477    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "abc"));
3478}
3479
3480#[gpui::test(iterations = 10)]
3481async fn test_leaving_project(
3482    deterministic: Arc<Deterministic>,
3483    cx_a: &mut TestAppContext,
3484    cx_b: &mut TestAppContext,
3485    cx_c: &mut TestAppContext,
3486) {
3487    deterministic.forbid_parking();
3488    let mut server = TestServer::start(&deterministic).await;
3489    let client_a = server.create_client(cx_a, "user_a").await;
3490    let client_b = server.create_client(cx_b, "user_b").await;
3491    let client_c = server.create_client(cx_c, "user_c").await;
3492    server
3493        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3494        .await;
3495    let active_call_a = cx_a.read(ActiveCall::global);
3496
3497    client_a
3498        .fs
3499        .insert_tree(
3500            "/a",
3501            json!({
3502                "a.txt": "a-contents",
3503                "b.txt": "b-contents",
3504            }),
3505        )
3506        .await;
3507    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
3508    let project_id = active_call_a
3509        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3510        .await
3511        .unwrap();
3512    let project_b1 = client_b.build_remote_project(project_id, cx_b).await;
3513    let project_c = client_c.build_remote_project(project_id, cx_c).await;
3514
3515    // Client A sees that a guest has joined.
3516    deterministic.run_until_parked();
3517    project_a.read_with(cx_a, |project, _| {
3518        assert_eq!(project.collaborators().len(), 2);
3519    });
3520    project_b1.read_with(cx_b, |project, _| {
3521        assert_eq!(project.collaborators().len(), 2);
3522    });
3523    project_c.read_with(cx_c, |project, _| {
3524        assert_eq!(project.collaborators().len(), 2);
3525    });
3526
3527    // Client B opens a buffer.
3528    let buffer_b1 = project_b1
3529        .update(cx_b, |project, cx| {
3530            let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
3531            project.open_buffer((worktree_id, "a.txt"), cx)
3532        })
3533        .await
3534        .unwrap();
3535    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3536
3537    // Drop client B's project and ensure client A and client C observe client B leaving.
3538    cx_b.update(|_| drop(project_b1));
3539    deterministic.run_until_parked();
3540    project_a.read_with(cx_a, |project, _| {
3541        assert_eq!(project.collaborators().len(), 1);
3542    });
3543    project_c.read_with(cx_c, |project, _| {
3544        assert_eq!(project.collaborators().len(), 1);
3545    });
3546
3547    // Client B re-joins the project and can open buffers as before.
3548    let project_b2 = client_b.build_remote_project(project_id, cx_b).await;
3549    deterministic.run_until_parked();
3550    project_a.read_with(cx_a, |project, _| {
3551        assert_eq!(project.collaborators().len(), 2);
3552    });
3553    project_b2.read_with(cx_b, |project, _| {
3554        assert_eq!(project.collaborators().len(), 2);
3555    });
3556    project_c.read_with(cx_c, |project, _| {
3557        assert_eq!(project.collaborators().len(), 2);
3558    });
3559
3560    let buffer_b2 = project_b2
3561        .update(cx_b, |project, cx| {
3562            let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
3563            project.open_buffer((worktree_id, "a.txt"), cx)
3564        })
3565        .await
3566        .unwrap();
3567    buffer_b2.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3568
3569    // Drop client B's connection and ensure client A and client C observe client B leaving.
3570    client_b.disconnect(&cx_b.to_async());
3571    deterministic.advance_clock(RECONNECT_TIMEOUT);
3572    project_a.read_with(cx_a, |project, _| {
3573        assert_eq!(project.collaborators().len(), 1);
3574    });
3575    project_b2.read_with(cx_b, |project, _| {
3576        assert!(project.is_read_only());
3577    });
3578    project_c.read_with(cx_c, |project, _| {
3579        assert_eq!(project.collaborators().len(), 1);
3580    });
3581
3582    // Client B can't join the project, unless they re-join the room.
3583    cx_b.spawn(|cx| {
3584        Project::remote(
3585            project_id,
3586            client_b.client.clone(),
3587            client_b.user_store.clone(),
3588            client_b.language_registry.clone(),
3589            FakeFs::new(cx.background()),
3590            cx,
3591        )
3592    })
3593    .await
3594    .unwrap_err();
3595
3596    // Simulate connection loss for client C and ensure client A observes client C leaving the project.
3597    client_c.wait_for_current_user(cx_c).await;
3598    server.forbid_connections();
3599    server.disconnect_client(client_c.peer_id().unwrap());
3600    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
3601    deterministic.run_until_parked();
3602    project_a.read_with(cx_a, |project, _| {
3603        assert_eq!(project.collaborators().len(), 0);
3604    });
3605    project_b2.read_with(cx_b, |project, _| {
3606        assert!(project.is_read_only());
3607    });
3608    project_c.read_with(cx_c, |project, _| {
3609        assert!(project.is_read_only());
3610    });
3611}
3612
3613#[gpui::test(iterations = 10)]
3614async fn test_collaborating_with_diagnostics(
3615    deterministic: Arc<Deterministic>,
3616    cx_a: &mut TestAppContext,
3617    cx_b: &mut TestAppContext,
3618    cx_c: &mut TestAppContext,
3619) {
3620    deterministic.forbid_parking();
3621    let mut server = TestServer::start(&deterministic).await;
3622    let client_a = server.create_client(cx_a, "user_a").await;
3623    let client_b = server.create_client(cx_b, "user_b").await;
3624    let client_c = server.create_client(cx_c, "user_c").await;
3625    server
3626        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3627        .await;
3628    let active_call_a = cx_a.read(ActiveCall::global);
3629
3630    // Set up a fake language server.
3631    let mut language = Language::new(
3632        LanguageConfig {
3633            name: "Rust".into(),
3634            path_suffixes: vec!["rs".to_string()],
3635            ..Default::default()
3636        },
3637        Some(tree_sitter_rust::language()),
3638    );
3639    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
3640    client_a.language_registry.add(Arc::new(language));
3641
3642    // Share a project as client A
3643    client_a
3644        .fs
3645        .insert_tree(
3646            "/a",
3647            json!({
3648                "a.rs": "let one = two",
3649                "other.rs": "",
3650            }),
3651        )
3652        .await;
3653    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3654
3655    // Cause the language server to start.
3656    let _buffer = project_a
3657        .update(cx_a, |project, cx| {
3658            project.open_buffer(
3659                ProjectPath {
3660                    worktree_id,
3661                    path: Path::new("other.rs").into(),
3662                },
3663                cx,
3664            )
3665        })
3666        .await
3667        .unwrap();
3668
3669    // Simulate a language server reporting errors for a file.
3670    let mut fake_language_server = fake_language_servers.next().await.unwrap();
3671    fake_language_server
3672        .receive_notification::<lsp::notification::DidOpenTextDocument>()
3673        .await;
3674    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3675        lsp::PublishDiagnosticsParams {
3676            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3677            version: None,
3678            diagnostics: vec![lsp::Diagnostic {
3679                severity: Some(lsp::DiagnosticSeverity::WARNING),
3680                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3681                message: "message 0".to_string(),
3682                ..Default::default()
3683            }],
3684        },
3685    );
3686
3687    // Client A shares the project and, simultaneously, the language server
3688    // publishes a diagnostic. This is done to ensure that the server always
3689    // observes the latest diagnostics for a worktree.
3690    let project_id = active_call_a
3691        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3692        .await
3693        .unwrap();
3694    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3695        lsp::PublishDiagnosticsParams {
3696            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3697            version: None,
3698            diagnostics: vec![lsp::Diagnostic {
3699                severity: Some(lsp::DiagnosticSeverity::ERROR),
3700                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3701                message: "message 1".to_string(),
3702                ..Default::default()
3703            }],
3704        },
3705    );
3706
3707    // Join the worktree as client B.
3708    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3709
3710    // Wait for server to see the diagnostics update.
3711    deterministic.run_until_parked();
3712
3713    // Ensure client B observes the new diagnostics.
3714    project_b.read_with(cx_b, |project, cx| {
3715        assert_eq!(
3716            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
3717            &[(
3718                ProjectPath {
3719                    worktree_id,
3720                    path: Arc::from(Path::new("a.rs")),
3721                },
3722                LanguageServerId(0),
3723                DiagnosticSummary {
3724                    error_count: 1,
3725                    warning_count: 0,
3726                    ..Default::default()
3727                },
3728            )]
3729        )
3730    });
3731
3732    // Join project as client C and observe the diagnostics.
3733    let project_c = client_c.build_remote_project(project_id, cx_c).await;
3734    let project_c_diagnostic_summaries =
3735        Rc::new(RefCell::new(project_c.read_with(cx_c, |project, cx| {
3736            project.diagnostic_summaries(cx).collect::<Vec<_>>()
3737        })));
3738    project_c.update(cx_c, |_, cx| {
3739        let summaries = project_c_diagnostic_summaries.clone();
3740        cx.subscribe(&project_c, {
3741            move |p, _, event, cx| {
3742                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
3743                    *summaries.borrow_mut() = p.diagnostic_summaries(cx).collect();
3744                }
3745            }
3746        })
3747        .detach();
3748    });
3749
3750    deterministic.run_until_parked();
3751    assert_eq!(
3752        project_c_diagnostic_summaries.borrow().as_slice(),
3753        &[(
3754            ProjectPath {
3755                worktree_id,
3756                path: Arc::from(Path::new("a.rs")),
3757            },
3758            LanguageServerId(0),
3759            DiagnosticSummary {
3760                error_count: 1,
3761                warning_count: 0,
3762                ..Default::default()
3763            },
3764        )]
3765    );
3766
3767    // Simulate a language server reporting more errors for a file.
3768    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3769        lsp::PublishDiagnosticsParams {
3770            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3771            version: None,
3772            diagnostics: vec![
3773                lsp::Diagnostic {
3774                    severity: Some(lsp::DiagnosticSeverity::ERROR),
3775                    range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3776                    message: "message 1".to_string(),
3777                    ..Default::default()
3778                },
3779                lsp::Diagnostic {
3780                    severity: Some(lsp::DiagnosticSeverity::WARNING),
3781                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 13)),
3782                    message: "message 2".to_string(),
3783                    ..Default::default()
3784                },
3785            ],
3786        },
3787    );
3788
3789    // Clients B and C get the updated summaries
3790    deterministic.run_until_parked();
3791    project_b.read_with(cx_b, |project, cx| {
3792        assert_eq!(
3793            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
3794            [(
3795                ProjectPath {
3796                    worktree_id,
3797                    path: Arc::from(Path::new("a.rs")),
3798                },
3799                LanguageServerId(0),
3800                DiagnosticSummary {
3801                    error_count: 1,
3802                    warning_count: 1,
3803                },
3804            )]
3805        );
3806    });
3807    project_c.read_with(cx_c, |project, cx| {
3808        assert_eq!(
3809            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
3810            [(
3811                ProjectPath {
3812                    worktree_id,
3813                    path: Arc::from(Path::new("a.rs")),
3814                },
3815                LanguageServerId(0),
3816                DiagnosticSummary {
3817                    error_count: 1,
3818                    warning_count: 1,
3819                },
3820            )]
3821        );
3822    });
3823
3824    // Open the file with the errors on client B. They should be present.
3825    let buffer_b = cx_b
3826        .background()
3827        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
3828        .await
3829        .unwrap();
3830
3831    buffer_b.read_with(cx_b, |buffer, _| {
3832        assert_eq!(
3833            buffer
3834                .snapshot()
3835                .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
3836                .collect::<Vec<_>>(),
3837            &[
3838                DiagnosticEntry {
3839                    range: Point::new(0, 4)..Point::new(0, 7),
3840                    diagnostic: Diagnostic {
3841                        group_id: 2,
3842                        message: "message 1".to_string(),
3843                        severity: lsp::DiagnosticSeverity::ERROR,
3844                        is_primary: true,
3845                        ..Default::default()
3846                    }
3847                },
3848                DiagnosticEntry {
3849                    range: Point::new(0, 10)..Point::new(0, 13),
3850                    diagnostic: Diagnostic {
3851                        group_id: 3,
3852                        severity: lsp::DiagnosticSeverity::WARNING,
3853                        message: "message 2".to_string(),
3854                        is_primary: true,
3855                        ..Default::default()
3856                    }
3857                }
3858            ]
3859        );
3860    });
3861
3862    // Simulate a language server reporting no errors for a file.
3863    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3864        lsp::PublishDiagnosticsParams {
3865            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3866            version: None,
3867            diagnostics: vec![],
3868        },
3869    );
3870    deterministic.run_until_parked();
3871    project_a.read_with(cx_a, |project, cx| {
3872        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
3873    });
3874    project_b.read_with(cx_b, |project, cx| {
3875        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
3876    });
3877    project_c.read_with(cx_c, |project, cx| {
3878        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
3879    });
3880}
3881
3882#[gpui::test(iterations = 10)]
3883async fn test_collaborating_with_lsp_progress_updates_and_diagnostics_ordering(
3884    deterministic: Arc<Deterministic>,
3885    cx_a: &mut TestAppContext,
3886    cx_b: &mut TestAppContext,
3887) {
3888    deterministic.forbid_parking();
3889    let mut server = TestServer::start(&deterministic).await;
3890    let client_a = server.create_client(cx_a, "user_a").await;
3891    let client_b = server.create_client(cx_b, "user_b").await;
3892    server
3893        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3894        .await;
3895
3896    // Set up a fake language server.
3897    let mut language = Language::new(
3898        LanguageConfig {
3899            name: "Rust".into(),
3900            path_suffixes: vec!["rs".to_string()],
3901            ..Default::default()
3902        },
3903        Some(tree_sitter_rust::language()),
3904    );
3905    let mut fake_language_servers = language
3906        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3907            disk_based_diagnostics_progress_token: Some("the-disk-based-token".into()),
3908            disk_based_diagnostics_sources: vec!["the-disk-based-diagnostics-source".into()],
3909            ..Default::default()
3910        }))
3911        .await;
3912    client_a.language_registry.add(Arc::new(language));
3913
3914    let file_names = &["one.rs", "two.rs", "three.rs", "four.rs", "five.rs"];
3915    client_a
3916        .fs
3917        .insert_tree(
3918            "/test",
3919            json!({
3920                "one.rs": "const ONE: usize = 1;",
3921                "two.rs": "const TWO: usize = 2;",
3922                "three.rs": "const THREE: usize = 3;",
3923                "four.rs": "const FOUR: usize = 3;",
3924                "five.rs": "const FIVE: usize = 3;",
3925            }),
3926        )
3927        .await;
3928
3929    let (project_a, worktree_id) = client_a.build_local_project("/test", cx_a).await;
3930
3931    // Share a project as client A
3932    let active_call_a = cx_a.read(ActiveCall::global);
3933    let project_id = active_call_a
3934        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3935        .await
3936        .unwrap();
3937
3938    // Join the project as client B and open all three files.
3939    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3940    let guest_buffers = futures::future::try_join_all(file_names.iter().map(|file_name| {
3941        project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, file_name), cx))
3942    }))
3943    .await
3944    .unwrap();
3945
3946    // Simulate a language server reporting errors for a file.
3947    let fake_language_server = fake_language_servers.next().await.unwrap();
3948    fake_language_server
3949        .request::<lsp::request::WorkDoneProgressCreate>(lsp::WorkDoneProgressCreateParams {
3950            token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
3951        })
3952        .await
3953        .unwrap();
3954    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
3955        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
3956        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin(
3957            lsp::WorkDoneProgressBegin {
3958                title: "Progress Began".into(),
3959                ..Default::default()
3960            },
3961        )),
3962    });
3963    for file_name in file_names {
3964        fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3965            lsp::PublishDiagnosticsParams {
3966                uri: lsp::Url::from_file_path(Path::new("/test").join(file_name)).unwrap(),
3967                version: None,
3968                diagnostics: vec![lsp::Diagnostic {
3969                    severity: Some(lsp::DiagnosticSeverity::WARNING),
3970                    source: Some("the-disk-based-diagnostics-source".into()),
3971                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
3972                    message: "message one".to_string(),
3973                    ..Default::default()
3974                }],
3975            },
3976        );
3977    }
3978    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
3979        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
3980        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End(
3981            lsp::WorkDoneProgressEnd { message: None },
3982        )),
3983    });
3984
3985    // When the "disk base diagnostics finished" message is received, the buffers'
3986    // diagnostics are expected to be present.
3987    let disk_based_diagnostics_finished = Arc::new(AtomicBool::new(false));
3988    project_b.update(cx_b, {
3989        let project_b = project_b.clone();
3990        let disk_based_diagnostics_finished = disk_based_diagnostics_finished.clone();
3991        move |_, cx| {
3992            cx.subscribe(&project_b, move |_, _, event, cx| {
3993                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
3994                    disk_based_diagnostics_finished.store(true, SeqCst);
3995                    for buffer in &guest_buffers {
3996                        assert_eq!(
3997                            buffer
3998                                .read(cx)
3999                                .snapshot()
4000                                .diagnostics_in_range::<_, usize>(0..5, false)
4001                                .count(),
4002                            1,
4003                            "expected a diagnostic for buffer {:?}",
4004                            buffer.read(cx).file().unwrap().path(),
4005                        );
4006                    }
4007                }
4008            })
4009            .detach();
4010        }
4011    });
4012
4013    deterministic.run_until_parked();
4014    assert!(disk_based_diagnostics_finished.load(SeqCst));
4015}
4016
4017#[gpui::test(iterations = 10)]
4018async fn test_collaborating_with_completion(
4019    deterministic: Arc<Deterministic>,
4020    cx_a: &mut TestAppContext,
4021    cx_b: &mut TestAppContext,
4022) {
4023    deterministic.forbid_parking();
4024    let mut server = TestServer::start(&deterministic).await;
4025    let client_a = server.create_client(cx_a, "user_a").await;
4026    let client_b = server.create_client(cx_b, "user_b").await;
4027    server
4028        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4029        .await;
4030    let active_call_a = cx_a.read(ActiveCall::global);
4031
4032    // Set up a fake language server.
4033    let mut language = Language::new(
4034        LanguageConfig {
4035            name: "Rust".into(),
4036            path_suffixes: vec!["rs".to_string()],
4037            ..Default::default()
4038        },
4039        Some(tree_sitter_rust::language()),
4040    );
4041    let mut fake_language_servers = language
4042        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4043            capabilities: lsp::ServerCapabilities {
4044                completion_provider: Some(lsp::CompletionOptions {
4045                    trigger_characters: Some(vec![".".to_string()]),
4046                    ..Default::default()
4047                }),
4048                ..Default::default()
4049            },
4050            ..Default::default()
4051        }))
4052        .await;
4053    client_a.language_registry.add(Arc::new(language));
4054
4055    client_a
4056        .fs
4057        .insert_tree(
4058            "/a",
4059            json!({
4060                "main.rs": "fn main() { a }",
4061                "other.rs": "",
4062            }),
4063        )
4064        .await;
4065    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
4066    let project_id = active_call_a
4067        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4068        .await
4069        .unwrap();
4070    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4071
4072    // Open a file in an editor as the guest.
4073    let buffer_b = project_b
4074        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
4075        .await
4076        .unwrap();
4077    let (window_b, _) = cx_b.add_window(|_| EmptyView);
4078    let editor_b = cx_b.add_view(window_b, |cx| {
4079        Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx)
4080    });
4081
4082    let fake_language_server = fake_language_servers.next().await.unwrap();
4083    cx_a.foreground().run_until_parked();
4084    buffer_b.read_with(cx_b, |buffer, _| {
4085        assert!(!buffer.completion_triggers().is_empty())
4086    });
4087
4088    // Type a completion trigger character as the guest.
4089    editor_b.update(cx_b, |editor, cx| {
4090        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
4091        editor.handle_input(".", cx);
4092        cx.focus(&editor_b);
4093    });
4094
4095    // Receive a completion request as the host's language server.
4096    // Return some completions from the host's language server.
4097    cx_a.foreground().start_waiting();
4098    fake_language_server
4099        .handle_request::<lsp::request::Completion, _, _>(|params, _| async move {
4100            assert_eq!(
4101                params.text_document_position.text_document.uri,
4102                lsp::Url::from_file_path("/a/main.rs").unwrap(),
4103            );
4104            assert_eq!(
4105                params.text_document_position.position,
4106                lsp::Position::new(0, 14),
4107            );
4108
4109            Ok(Some(lsp::CompletionResponse::Array(vec![
4110                lsp::CompletionItem {
4111                    label: "first_method(…)".into(),
4112                    detail: Some("fn(&mut self, B) -> C".into()),
4113                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
4114                        new_text: "first_method($1)".to_string(),
4115                        range: lsp::Range::new(
4116                            lsp::Position::new(0, 14),
4117                            lsp::Position::new(0, 14),
4118                        ),
4119                    })),
4120                    insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
4121                    ..Default::default()
4122                },
4123                lsp::CompletionItem {
4124                    label: "second_method(…)".into(),
4125                    detail: Some("fn(&mut self, C) -> D<E>".into()),
4126                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
4127                        new_text: "second_method()".to_string(),
4128                        range: lsp::Range::new(
4129                            lsp::Position::new(0, 14),
4130                            lsp::Position::new(0, 14),
4131                        ),
4132                    })),
4133                    insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
4134                    ..Default::default()
4135                },
4136            ])))
4137        })
4138        .next()
4139        .await
4140        .unwrap();
4141    cx_a.foreground().finish_waiting();
4142
4143    // Open the buffer on the host.
4144    let buffer_a = project_a
4145        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
4146        .await
4147        .unwrap();
4148    cx_a.foreground().run_until_parked();
4149    buffer_a.read_with(cx_a, |buffer, _| {
4150        assert_eq!(buffer.text(), "fn main() { a. }")
4151    });
4152
4153    // Confirm a completion on the guest.
4154    editor_b.read_with(cx_b, |editor, _| assert!(editor.context_menu_visible()));
4155    editor_b.update(cx_b, |editor, cx| {
4156        editor.confirm_completion(&ConfirmCompletion { item_ix: Some(0) }, cx);
4157        assert_eq!(editor.text(cx), "fn main() { a.first_method() }");
4158    });
4159
4160    // Return a resolved completion from the host's language server.
4161    // The resolved completion has an additional text edit.
4162    fake_language_server.handle_request::<lsp::request::ResolveCompletionItem, _, _>(
4163        |params, _| async move {
4164            assert_eq!(params.label, "first_method(…)");
4165            Ok(lsp::CompletionItem {
4166                label: "first_method(…)".into(),
4167                detail: Some("fn(&mut self, B) -> C".into()),
4168                text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
4169                    new_text: "first_method($1)".to_string(),
4170                    range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
4171                })),
4172                additional_text_edits: Some(vec![lsp::TextEdit {
4173                    new_text: "use d::SomeTrait;\n".to_string(),
4174                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
4175                }]),
4176                insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
4177                ..Default::default()
4178            })
4179        },
4180    );
4181
4182    // The additional edit is applied.
4183    cx_a.foreground().run_until_parked();
4184    buffer_a.read_with(cx_a, |buffer, _| {
4185        assert_eq!(
4186            buffer.text(),
4187            "use d::SomeTrait;\nfn main() { a.first_method() }"
4188        );
4189    });
4190    buffer_b.read_with(cx_b, |buffer, _| {
4191        assert_eq!(
4192            buffer.text(),
4193            "use d::SomeTrait;\nfn main() { a.first_method() }"
4194        );
4195    });
4196}
4197
4198#[gpui::test(iterations = 10)]
4199async fn test_reloading_buffer_manually(
4200    deterministic: Arc<Deterministic>,
4201    cx_a: &mut TestAppContext,
4202    cx_b: &mut TestAppContext,
4203) {
4204    deterministic.forbid_parking();
4205    let mut server = TestServer::start(&deterministic).await;
4206    let client_a = server.create_client(cx_a, "user_a").await;
4207    let client_b = server.create_client(cx_b, "user_b").await;
4208    server
4209        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4210        .await;
4211    let active_call_a = cx_a.read(ActiveCall::global);
4212
4213    client_a
4214        .fs
4215        .insert_tree("/a", json!({ "a.rs": "let one = 1;" }))
4216        .await;
4217    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
4218    let buffer_a = project_a
4219        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
4220        .await
4221        .unwrap();
4222    let project_id = active_call_a
4223        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4224        .await
4225        .unwrap();
4226
4227    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4228
4229    let buffer_b = cx_b
4230        .background()
4231        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4232        .await
4233        .unwrap();
4234    buffer_b.update(cx_b, |buffer, cx| {
4235        buffer.edit([(4..7, "six")], None, cx);
4236        buffer.edit([(10..11, "6")], None, cx);
4237        assert_eq!(buffer.text(), "let six = 6;");
4238        assert!(buffer.is_dirty());
4239        assert!(!buffer.has_conflict());
4240    });
4241    cx_a.foreground().run_until_parked();
4242    buffer_a.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "let six = 6;"));
4243
4244    client_a
4245        .fs
4246        .save(
4247            "/a/a.rs".as_ref(),
4248            &Rope::from("let seven = 7;"),
4249            LineEnding::Unix,
4250        )
4251        .await
4252        .unwrap();
4253    cx_a.foreground().run_until_parked();
4254    buffer_a.read_with(cx_a, |buffer, _| assert!(buffer.has_conflict()));
4255    buffer_b.read_with(cx_b, |buffer, _| assert!(buffer.has_conflict()));
4256
4257    project_b
4258        .update(cx_b, |project, cx| {
4259            project.reload_buffers(HashSet::from_iter([buffer_b.clone()]), true, cx)
4260        })
4261        .await
4262        .unwrap();
4263    buffer_a.read_with(cx_a, |buffer, _| {
4264        assert_eq!(buffer.text(), "let seven = 7;");
4265        assert!(!buffer.is_dirty());
4266        assert!(!buffer.has_conflict());
4267    });
4268    buffer_b.read_with(cx_b, |buffer, _| {
4269        assert_eq!(buffer.text(), "let seven = 7;");
4270        assert!(!buffer.is_dirty());
4271        assert!(!buffer.has_conflict());
4272    });
4273
4274    buffer_a.update(cx_a, |buffer, cx| {
4275        // Undoing on the host is a no-op when the reload was initiated by the guest.
4276        buffer.undo(cx);
4277        assert_eq!(buffer.text(), "let seven = 7;");
4278        assert!(!buffer.is_dirty());
4279        assert!(!buffer.has_conflict());
4280    });
4281    buffer_b.update(cx_b, |buffer, cx| {
4282        // Undoing on the guest rolls back the buffer to before it was reloaded but the conflict gets cleared.
4283        buffer.undo(cx);
4284        assert_eq!(buffer.text(), "let six = 6;");
4285        assert!(buffer.is_dirty());
4286        assert!(!buffer.has_conflict());
4287    });
4288}
4289
4290#[gpui::test(iterations = 10)]
4291async fn test_formatting_buffer(
4292    deterministic: Arc<Deterministic>,
4293    cx_a: &mut TestAppContext,
4294    cx_b: &mut TestAppContext,
4295) {
4296    use project::FormatTrigger;
4297
4298    let mut server = TestServer::start(&deterministic).await;
4299    let client_a = server.create_client(cx_a, "user_a").await;
4300    let client_b = server.create_client(cx_b, "user_b").await;
4301    server
4302        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4303        .await;
4304    let active_call_a = cx_a.read(ActiveCall::global);
4305
4306    // Set up a fake language server.
4307    let mut language = Language::new(
4308        LanguageConfig {
4309            name: "Rust".into(),
4310            path_suffixes: vec!["rs".to_string()],
4311            ..Default::default()
4312        },
4313        Some(tree_sitter_rust::language()),
4314    );
4315    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4316    client_a.language_registry.add(Arc::new(language));
4317
4318    // Here we insert a fake tree with a directory that exists on disk. This is needed
4319    // because later we'll invoke a command, which requires passing a working directory
4320    // that points to a valid location on disk.
4321    let directory = env::current_dir().unwrap();
4322    client_a
4323        .fs
4324        .insert_tree(&directory, json!({ "a.rs": "let one = \"two\"" }))
4325        .await;
4326    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
4327    let project_id = active_call_a
4328        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4329        .await
4330        .unwrap();
4331    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4332
4333    let buffer_b = cx_b
4334        .background()
4335        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4336        .await
4337        .unwrap();
4338
4339    let fake_language_server = fake_language_servers.next().await.unwrap();
4340    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
4341        Ok(Some(vec![
4342            lsp::TextEdit {
4343                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 4)),
4344                new_text: "h".to_string(),
4345            },
4346            lsp::TextEdit {
4347                range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 7)),
4348                new_text: "y".to_string(),
4349            },
4350        ]))
4351    });
4352
4353    project_b
4354        .update(cx_b, |project, cx| {
4355            project.format(
4356                HashSet::from_iter([buffer_b.clone()]),
4357                true,
4358                FormatTrigger::Save,
4359                cx,
4360            )
4361        })
4362        .await
4363        .unwrap();
4364
4365    // The edits from the LSP are applied, and a final newline is added.
4366    assert_eq!(
4367        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4368        "let honey = \"two\"\n"
4369    );
4370
4371    // Ensure buffer can be formatted using an external command. Notice how the
4372    // host's configuration is honored as opposed to using the guest's settings.
4373    cx_a.update(|cx| {
4374        cx.update_global(|store: &mut SettingsStore, cx| {
4375            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4376                file.defaults.formatter = Some(Formatter::External {
4377                    command: "awk".into(),
4378                    arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()].into(),
4379                });
4380            });
4381        });
4382    });
4383    project_b
4384        .update(cx_b, |project, cx| {
4385            project.format(
4386                HashSet::from_iter([buffer_b.clone()]),
4387                true,
4388                FormatTrigger::Save,
4389                cx,
4390            )
4391        })
4392        .await
4393        .unwrap();
4394    assert_eq!(
4395        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4396        format!("let honey = \"{}/a.rs\"\n", directory.to_str().unwrap())
4397    );
4398}
4399
4400#[gpui::test(iterations = 10)]
4401async fn test_definition(
4402    deterministic: Arc<Deterministic>,
4403    cx_a: &mut TestAppContext,
4404    cx_b: &mut TestAppContext,
4405) {
4406    deterministic.forbid_parking();
4407    let mut server = TestServer::start(&deterministic).await;
4408    let client_a = server.create_client(cx_a, "user_a").await;
4409    let client_b = server.create_client(cx_b, "user_b").await;
4410    server
4411        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4412        .await;
4413    let active_call_a = cx_a.read(ActiveCall::global);
4414
4415    // Set up a fake language server.
4416    let mut language = Language::new(
4417        LanguageConfig {
4418            name: "Rust".into(),
4419            path_suffixes: vec!["rs".to_string()],
4420            ..Default::default()
4421        },
4422        Some(tree_sitter_rust::language()),
4423    );
4424    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4425    client_a.language_registry.add(Arc::new(language));
4426
4427    client_a
4428        .fs
4429        .insert_tree(
4430            "/root",
4431            json!({
4432                "dir-1": {
4433                    "a.rs": "const ONE: usize = b::TWO + b::THREE;",
4434                },
4435                "dir-2": {
4436                    "b.rs": "const TWO: c::T2 = 2;\nconst THREE: usize = 3;",
4437                    "c.rs": "type T2 = usize;",
4438                }
4439            }),
4440        )
4441        .await;
4442    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4443    let project_id = active_call_a
4444        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4445        .await
4446        .unwrap();
4447    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4448
4449    // Open the file on client B.
4450    let buffer_b = cx_b
4451        .background()
4452        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4453        .await
4454        .unwrap();
4455
4456    // Request the definition of a symbol as the guest.
4457    let fake_language_server = fake_language_servers.next().await.unwrap();
4458    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4459        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4460            lsp::Location::new(
4461                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4462                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4463            ),
4464        )))
4465    });
4466
4467    let definitions_1 = project_b
4468        .update(cx_b, |p, cx| p.definition(&buffer_b, 23, cx))
4469        .await
4470        .unwrap();
4471    cx_b.read(|cx| {
4472        assert_eq!(definitions_1.len(), 1);
4473        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4474        let target_buffer = definitions_1[0].target.buffer.read(cx);
4475        assert_eq!(
4476            target_buffer.text(),
4477            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4478        );
4479        assert_eq!(
4480            definitions_1[0].target.range.to_point(target_buffer),
4481            Point::new(0, 6)..Point::new(0, 9)
4482        );
4483    });
4484
4485    // Try getting more definitions for the same buffer, ensuring the buffer gets reused from
4486    // the previous call to `definition`.
4487    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4488        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4489            lsp::Location::new(
4490                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4491                lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)),
4492            ),
4493        )))
4494    });
4495
4496    let definitions_2 = project_b
4497        .update(cx_b, |p, cx| p.definition(&buffer_b, 33, cx))
4498        .await
4499        .unwrap();
4500    cx_b.read(|cx| {
4501        assert_eq!(definitions_2.len(), 1);
4502        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4503        let target_buffer = definitions_2[0].target.buffer.read(cx);
4504        assert_eq!(
4505            target_buffer.text(),
4506            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4507        );
4508        assert_eq!(
4509            definitions_2[0].target.range.to_point(target_buffer),
4510            Point::new(1, 6)..Point::new(1, 11)
4511        );
4512    });
4513    assert_eq!(
4514        definitions_1[0].target.buffer,
4515        definitions_2[0].target.buffer
4516    );
4517
4518    fake_language_server.handle_request::<lsp::request::GotoTypeDefinition, _, _>(
4519        |req, _| async move {
4520            assert_eq!(
4521                req.text_document_position_params.position,
4522                lsp::Position::new(0, 7)
4523            );
4524            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4525                lsp::Location::new(
4526                    lsp::Url::from_file_path("/root/dir-2/c.rs").unwrap(),
4527                    lsp::Range::new(lsp::Position::new(0, 5), lsp::Position::new(0, 7)),
4528                ),
4529            )))
4530        },
4531    );
4532
4533    let type_definitions = project_b
4534        .update(cx_b, |p, cx| p.type_definition(&buffer_b, 7, cx))
4535        .await
4536        .unwrap();
4537    cx_b.read(|cx| {
4538        assert_eq!(type_definitions.len(), 1);
4539        let target_buffer = type_definitions[0].target.buffer.read(cx);
4540        assert_eq!(target_buffer.text(), "type T2 = usize;");
4541        assert_eq!(
4542            type_definitions[0].target.range.to_point(target_buffer),
4543            Point::new(0, 5)..Point::new(0, 7)
4544        );
4545    });
4546}
4547
4548#[gpui::test(iterations = 10)]
4549async fn test_references(
4550    deterministic: Arc<Deterministic>,
4551    cx_a: &mut TestAppContext,
4552    cx_b: &mut TestAppContext,
4553) {
4554    deterministic.forbid_parking();
4555    let mut server = TestServer::start(&deterministic).await;
4556    let client_a = server.create_client(cx_a, "user_a").await;
4557    let client_b = server.create_client(cx_b, "user_b").await;
4558    server
4559        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4560        .await;
4561    let active_call_a = cx_a.read(ActiveCall::global);
4562
4563    // Set up a fake language server.
4564    let mut language = Language::new(
4565        LanguageConfig {
4566            name: "Rust".into(),
4567            path_suffixes: vec!["rs".to_string()],
4568            ..Default::default()
4569        },
4570        Some(tree_sitter_rust::language()),
4571    );
4572    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4573    client_a.language_registry.add(Arc::new(language));
4574
4575    client_a
4576        .fs
4577        .insert_tree(
4578            "/root",
4579            json!({
4580                "dir-1": {
4581                    "one.rs": "const ONE: usize = 1;",
4582                    "two.rs": "const TWO: usize = one::ONE + one::ONE;",
4583                },
4584                "dir-2": {
4585                    "three.rs": "const THREE: usize = two::TWO + one::ONE;",
4586                }
4587            }),
4588        )
4589        .await;
4590    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4591    let project_id = active_call_a
4592        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4593        .await
4594        .unwrap();
4595    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4596
4597    // Open the file on client B.
4598    let buffer_b = cx_b
4599        .background()
4600        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
4601        .await
4602        .unwrap();
4603
4604    // Request references to a symbol as the guest.
4605    let fake_language_server = fake_language_servers.next().await.unwrap();
4606    fake_language_server.handle_request::<lsp::request::References, _, _>(|params, _| async move {
4607        assert_eq!(
4608            params.text_document_position.text_document.uri.as_str(),
4609            "file:///root/dir-1/one.rs"
4610        );
4611        Ok(Some(vec![
4612            lsp::Location {
4613                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4614                range: lsp::Range::new(lsp::Position::new(0, 24), lsp::Position::new(0, 27)),
4615            },
4616            lsp::Location {
4617                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4618                range: lsp::Range::new(lsp::Position::new(0, 35), lsp::Position::new(0, 38)),
4619            },
4620            lsp::Location {
4621                uri: lsp::Url::from_file_path("/root/dir-2/three.rs").unwrap(),
4622                range: lsp::Range::new(lsp::Position::new(0, 37), lsp::Position::new(0, 40)),
4623            },
4624        ]))
4625    });
4626
4627    let references = project_b
4628        .update(cx_b, |p, cx| p.references(&buffer_b, 7, cx))
4629        .await
4630        .unwrap();
4631    cx_b.read(|cx| {
4632        assert_eq!(references.len(), 3);
4633        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4634
4635        let two_buffer = references[0].buffer.read(cx);
4636        let three_buffer = references[2].buffer.read(cx);
4637        assert_eq!(
4638            two_buffer.file().unwrap().path().as_ref(),
4639            Path::new("two.rs")
4640        );
4641        assert_eq!(references[1].buffer, references[0].buffer);
4642        assert_eq!(
4643            three_buffer.file().unwrap().full_path(cx),
4644            Path::new("/root/dir-2/three.rs")
4645        );
4646
4647        assert_eq!(references[0].range.to_offset(two_buffer), 24..27);
4648        assert_eq!(references[1].range.to_offset(two_buffer), 35..38);
4649        assert_eq!(references[2].range.to_offset(three_buffer), 37..40);
4650    });
4651}
4652
4653#[gpui::test(iterations = 10)]
4654async fn test_project_search(
4655    deterministic: Arc<Deterministic>,
4656    cx_a: &mut TestAppContext,
4657    cx_b: &mut TestAppContext,
4658) {
4659    deterministic.forbid_parking();
4660    let mut server = TestServer::start(&deterministic).await;
4661    let client_a = server.create_client(cx_a, "user_a").await;
4662    let client_b = server.create_client(cx_b, "user_b").await;
4663    server
4664        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4665        .await;
4666    let active_call_a = cx_a.read(ActiveCall::global);
4667
4668    client_a
4669        .fs
4670        .insert_tree(
4671            "/root",
4672            json!({
4673                "dir-1": {
4674                    "a": "hello world",
4675                    "b": "goodnight moon",
4676                    "c": "a world of goo",
4677                    "d": "world champion of clown world",
4678                },
4679                "dir-2": {
4680                    "e": "disney world is fun",
4681                }
4682            }),
4683        )
4684        .await;
4685    let (project_a, _) = client_a.build_local_project("/root/dir-1", cx_a).await;
4686    let (worktree_2, _) = project_a
4687        .update(cx_a, |p, cx| {
4688            p.find_or_create_local_worktree("/root/dir-2", true, cx)
4689        })
4690        .await
4691        .unwrap();
4692    worktree_2
4693        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
4694        .await;
4695    let project_id = active_call_a
4696        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4697        .await
4698        .unwrap();
4699
4700    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4701
4702    // Perform a search as the guest.
4703    let results = project_b
4704        .update(cx_b, |project, cx| {
4705            project.search(
4706                SearchQuery::text("world", false, false, Vec::new(), Vec::new()),
4707                cx,
4708            )
4709        })
4710        .await
4711        .unwrap();
4712
4713    let mut ranges_by_path = results
4714        .into_iter()
4715        .map(|(buffer, ranges)| {
4716            buffer.read_with(cx_b, |buffer, cx| {
4717                let path = buffer.file().unwrap().full_path(cx);
4718                let offset_ranges = ranges
4719                    .into_iter()
4720                    .map(|range| range.to_offset(buffer))
4721                    .collect::<Vec<_>>();
4722                (path, offset_ranges)
4723            })
4724        })
4725        .collect::<Vec<_>>();
4726    ranges_by_path.sort_by_key(|(path, _)| path.clone());
4727
4728    assert_eq!(
4729        ranges_by_path,
4730        &[
4731            (PathBuf::from("dir-1/a"), vec![6..11]),
4732            (PathBuf::from("dir-1/c"), vec![2..7]),
4733            (PathBuf::from("dir-1/d"), vec![0..5, 24..29]),
4734            (PathBuf::from("dir-2/e"), vec![7..12]),
4735        ]
4736    );
4737}
4738
4739#[gpui::test(iterations = 10)]
4740async fn test_document_highlights(
4741    deterministic: Arc<Deterministic>,
4742    cx_a: &mut TestAppContext,
4743    cx_b: &mut TestAppContext,
4744) {
4745    deterministic.forbid_parking();
4746    let mut server = TestServer::start(&deterministic).await;
4747    let client_a = server.create_client(cx_a, "user_a").await;
4748    let client_b = server.create_client(cx_b, "user_b").await;
4749    server
4750        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4751        .await;
4752    let active_call_a = cx_a.read(ActiveCall::global);
4753
4754    client_a
4755        .fs
4756        .insert_tree(
4757            "/root-1",
4758            json!({
4759                "main.rs": "fn double(number: i32) -> i32 { number + number }",
4760            }),
4761        )
4762        .await;
4763
4764    // Set up a fake language server.
4765    let mut language = Language::new(
4766        LanguageConfig {
4767            name: "Rust".into(),
4768            path_suffixes: vec!["rs".to_string()],
4769            ..Default::default()
4770        },
4771        Some(tree_sitter_rust::language()),
4772    );
4773    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4774    client_a.language_registry.add(Arc::new(language));
4775
4776    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4777    let project_id = active_call_a
4778        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4779        .await
4780        .unwrap();
4781    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4782
4783    // Open the file on client B.
4784    let buffer_b = cx_b
4785        .background()
4786        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
4787        .await
4788        .unwrap();
4789
4790    // Request document highlights as the guest.
4791    let fake_language_server = fake_language_servers.next().await.unwrap();
4792    fake_language_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
4793        |params, _| async move {
4794            assert_eq!(
4795                params
4796                    .text_document_position_params
4797                    .text_document
4798                    .uri
4799                    .as_str(),
4800                "file:///root-1/main.rs"
4801            );
4802            assert_eq!(
4803                params.text_document_position_params.position,
4804                lsp::Position::new(0, 34)
4805            );
4806            Ok(Some(vec![
4807                lsp::DocumentHighlight {
4808                    kind: Some(lsp::DocumentHighlightKind::WRITE),
4809                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 16)),
4810                },
4811                lsp::DocumentHighlight {
4812                    kind: Some(lsp::DocumentHighlightKind::READ),
4813                    range: lsp::Range::new(lsp::Position::new(0, 32), lsp::Position::new(0, 38)),
4814                },
4815                lsp::DocumentHighlight {
4816                    kind: Some(lsp::DocumentHighlightKind::READ),
4817                    range: lsp::Range::new(lsp::Position::new(0, 41), lsp::Position::new(0, 47)),
4818                },
4819            ]))
4820        },
4821    );
4822
4823    let highlights = project_b
4824        .update(cx_b, |p, cx| p.document_highlights(&buffer_b, 34, cx))
4825        .await
4826        .unwrap();
4827    buffer_b.read_with(cx_b, |buffer, _| {
4828        let snapshot = buffer.snapshot();
4829
4830        let highlights = highlights
4831            .into_iter()
4832            .map(|highlight| (highlight.kind, highlight.range.to_offset(&snapshot)))
4833            .collect::<Vec<_>>();
4834        assert_eq!(
4835            highlights,
4836            &[
4837                (lsp::DocumentHighlightKind::WRITE, 10..16),
4838                (lsp::DocumentHighlightKind::READ, 32..38),
4839                (lsp::DocumentHighlightKind::READ, 41..47)
4840            ]
4841        )
4842    });
4843}
4844
4845#[gpui::test(iterations = 10)]
4846async fn test_lsp_hover(
4847    deterministic: Arc<Deterministic>,
4848    cx_a: &mut TestAppContext,
4849    cx_b: &mut TestAppContext,
4850) {
4851    deterministic.forbid_parking();
4852    let mut server = TestServer::start(&deterministic).await;
4853    let client_a = server.create_client(cx_a, "user_a").await;
4854    let client_b = server.create_client(cx_b, "user_b").await;
4855    server
4856        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4857        .await;
4858    let active_call_a = cx_a.read(ActiveCall::global);
4859
4860    client_a
4861        .fs
4862        .insert_tree(
4863            "/root-1",
4864            json!({
4865                "main.rs": "use std::collections::HashMap;",
4866            }),
4867        )
4868        .await;
4869
4870    // Set up a fake language server.
4871    let mut language = Language::new(
4872        LanguageConfig {
4873            name: "Rust".into(),
4874            path_suffixes: vec!["rs".to_string()],
4875            ..Default::default()
4876        },
4877        Some(tree_sitter_rust::language()),
4878    );
4879    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4880    client_a.language_registry.add(Arc::new(language));
4881
4882    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4883    let project_id = active_call_a
4884        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4885        .await
4886        .unwrap();
4887    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4888
4889    // Open the file as the guest
4890    let buffer_b = cx_b
4891        .background()
4892        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
4893        .await
4894        .unwrap();
4895
4896    // Request hover information as the guest.
4897    let fake_language_server = fake_language_servers.next().await.unwrap();
4898    fake_language_server.handle_request::<lsp::request::HoverRequest, _, _>(
4899        |params, _| async move {
4900            assert_eq!(
4901                params
4902                    .text_document_position_params
4903                    .text_document
4904                    .uri
4905                    .as_str(),
4906                "file:///root-1/main.rs"
4907            );
4908            assert_eq!(
4909                params.text_document_position_params.position,
4910                lsp::Position::new(0, 22)
4911            );
4912            Ok(Some(lsp::Hover {
4913                contents: lsp::HoverContents::Array(vec![
4914                    lsp::MarkedString::String("Test hover content.".to_string()),
4915                    lsp::MarkedString::LanguageString(lsp::LanguageString {
4916                        language: "Rust".to_string(),
4917                        value: "let foo = 42;".to_string(),
4918                    }),
4919                ]),
4920                range: Some(lsp::Range::new(
4921                    lsp::Position::new(0, 22),
4922                    lsp::Position::new(0, 29),
4923                )),
4924            }))
4925        },
4926    );
4927
4928    let hover_info = project_b
4929        .update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
4930        .await
4931        .unwrap()
4932        .unwrap();
4933    buffer_b.read_with(cx_b, |buffer, _| {
4934        let snapshot = buffer.snapshot();
4935        assert_eq!(hover_info.range.unwrap().to_offset(&snapshot), 22..29);
4936        assert_eq!(
4937            hover_info.contents,
4938            vec![
4939                project::HoverBlock {
4940                    text: "Test hover content.".to_string(),
4941                    kind: HoverBlockKind::Markdown,
4942                },
4943                project::HoverBlock {
4944                    text: "let foo = 42;".to_string(),
4945                    kind: HoverBlockKind::Code {
4946                        language: "Rust".to_string()
4947                    },
4948                }
4949            ]
4950        );
4951    });
4952}
4953
4954#[gpui::test(iterations = 10)]
4955async fn test_project_symbols(
4956    deterministic: Arc<Deterministic>,
4957    cx_a: &mut TestAppContext,
4958    cx_b: &mut TestAppContext,
4959) {
4960    deterministic.forbid_parking();
4961    let mut server = TestServer::start(&deterministic).await;
4962    let client_a = server.create_client(cx_a, "user_a").await;
4963    let client_b = server.create_client(cx_b, "user_b").await;
4964    server
4965        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4966        .await;
4967    let active_call_a = cx_a.read(ActiveCall::global);
4968
4969    // Set up a fake language server.
4970    let mut language = Language::new(
4971        LanguageConfig {
4972            name: "Rust".into(),
4973            path_suffixes: vec!["rs".to_string()],
4974            ..Default::default()
4975        },
4976        Some(tree_sitter_rust::language()),
4977    );
4978    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4979    client_a.language_registry.add(Arc::new(language));
4980
4981    client_a
4982        .fs
4983        .insert_tree(
4984            "/code",
4985            json!({
4986                "crate-1": {
4987                    "one.rs": "const ONE: usize = 1;",
4988                },
4989                "crate-2": {
4990                    "two.rs": "const TWO: usize = 2; const THREE: usize = 3;",
4991                },
4992                "private": {
4993                    "passwords.txt": "the-password",
4994                }
4995            }),
4996        )
4997        .await;
4998    let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await;
4999    let project_id = active_call_a
5000        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5001        .await
5002        .unwrap();
5003    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5004
5005    // Cause the language server to start.
5006    let _buffer = cx_b
5007        .background()
5008        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
5009        .await
5010        .unwrap();
5011
5012    let fake_language_server = fake_language_servers.next().await.unwrap();
5013    fake_language_server.handle_request::<lsp::WorkspaceSymbolRequest, _, _>(|_, _| async move {
5014        Ok(Some(lsp::WorkspaceSymbolResponse::Flat(vec![
5015            #[allow(deprecated)]
5016            lsp::SymbolInformation {
5017                name: "TWO".into(),
5018                location: lsp::Location {
5019                    uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
5020                    range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5021                },
5022                kind: lsp::SymbolKind::CONSTANT,
5023                tags: None,
5024                container_name: None,
5025                deprecated: None,
5026            },
5027        ])))
5028    });
5029
5030    // Request the definition of a symbol as the guest.
5031    let symbols = project_b
5032        .update(cx_b, |p, cx| p.symbols("two", cx))
5033        .await
5034        .unwrap();
5035    assert_eq!(symbols.len(), 1);
5036    assert_eq!(symbols[0].name, "TWO");
5037
5038    // Open one of the returned symbols.
5039    let buffer_b_2 = project_b
5040        .update(cx_b, |project, cx| {
5041            project.open_buffer_for_symbol(&symbols[0], cx)
5042        })
5043        .await
5044        .unwrap();
5045    buffer_b_2.read_with(cx_b, |buffer, _| {
5046        assert_eq!(
5047            buffer.file().unwrap().path().as_ref(),
5048            Path::new("../crate-2/two.rs")
5049        );
5050    });
5051
5052    // Attempt to craft a symbol and violate host's privacy by opening an arbitrary file.
5053    let mut fake_symbol = symbols[0].clone();
5054    fake_symbol.path.path = Path::new("/code/secrets").into();
5055    let error = project_b
5056        .update(cx_b, |project, cx| {
5057            project.open_buffer_for_symbol(&fake_symbol, cx)
5058        })
5059        .await
5060        .unwrap_err();
5061    assert!(error.to_string().contains("invalid symbol signature"));
5062}
5063
5064#[gpui::test(iterations = 10)]
5065async fn test_open_buffer_while_getting_definition_pointing_to_it(
5066    deterministic: Arc<Deterministic>,
5067    cx_a: &mut TestAppContext,
5068    cx_b: &mut TestAppContext,
5069    mut rng: StdRng,
5070) {
5071    deterministic.forbid_parking();
5072    let mut server = TestServer::start(&deterministic).await;
5073    let client_a = server.create_client(cx_a, "user_a").await;
5074    let client_b = server.create_client(cx_b, "user_b").await;
5075    server
5076        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5077        .await;
5078    let active_call_a = cx_a.read(ActiveCall::global);
5079
5080    // Set up a fake language server.
5081    let mut language = Language::new(
5082        LanguageConfig {
5083            name: "Rust".into(),
5084            path_suffixes: vec!["rs".to_string()],
5085            ..Default::default()
5086        },
5087        Some(tree_sitter_rust::language()),
5088    );
5089    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5090    client_a.language_registry.add(Arc::new(language));
5091
5092    client_a
5093        .fs
5094        .insert_tree(
5095            "/root",
5096            json!({
5097                "a.rs": "const ONE: usize = b::TWO;",
5098                "b.rs": "const TWO: usize = 2",
5099            }),
5100        )
5101        .await;
5102    let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await;
5103    let project_id = active_call_a
5104        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5105        .await
5106        .unwrap();
5107    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5108
5109    let buffer_b1 = cx_b
5110        .background()
5111        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
5112        .await
5113        .unwrap();
5114
5115    let fake_language_server = fake_language_servers.next().await.unwrap();
5116    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
5117        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
5118            lsp::Location::new(
5119                lsp::Url::from_file_path("/root/b.rs").unwrap(),
5120                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5121            ),
5122        )))
5123    });
5124
5125    let definitions;
5126    let buffer_b2;
5127    if rng.gen() {
5128        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5129        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5130    } else {
5131        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5132        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5133    }
5134
5135    let buffer_b2 = buffer_b2.await.unwrap();
5136    let definitions = definitions.await.unwrap();
5137    assert_eq!(definitions.len(), 1);
5138    assert_eq!(definitions[0].target.buffer, buffer_b2);
5139}
5140
5141#[gpui::test(iterations = 10)]
5142async fn test_collaborating_with_code_actions(
5143    deterministic: Arc<Deterministic>,
5144    cx_a: &mut TestAppContext,
5145    cx_b: &mut TestAppContext,
5146) {
5147    deterministic.forbid_parking();
5148    let mut server = TestServer::start(&deterministic).await;
5149    let client_a = server.create_client(cx_a, "user_a").await;
5150    let client_b = server.create_client(cx_b, "user_b").await;
5151    server
5152        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5153        .await;
5154    let active_call_a = cx_a.read(ActiveCall::global);
5155
5156    cx_b.update(editor::init);
5157
5158    // Set up a fake language server.
5159    let mut language = Language::new(
5160        LanguageConfig {
5161            name: "Rust".into(),
5162            path_suffixes: vec!["rs".to_string()],
5163            ..Default::default()
5164        },
5165        Some(tree_sitter_rust::language()),
5166    );
5167    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5168    client_a.language_registry.add(Arc::new(language));
5169
5170    client_a
5171        .fs
5172        .insert_tree(
5173            "/a",
5174            json!({
5175                "main.rs": "mod other;\nfn main() { let foo = other::foo(); }",
5176                "other.rs": "pub fn foo() -> usize { 4 }",
5177            }),
5178        )
5179        .await;
5180    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
5181    let project_id = active_call_a
5182        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5183        .await
5184        .unwrap();
5185
5186    // Join the project as client B.
5187    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5188    let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx));
5189    let editor_b = workspace_b
5190        .update(cx_b, |workspace, cx| {
5191            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
5192        })
5193        .await
5194        .unwrap()
5195        .downcast::<Editor>()
5196        .unwrap();
5197
5198    let mut fake_language_server = fake_language_servers.next().await.unwrap();
5199    fake_language_server
5200        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
5201            assert_eq!(
5202                params.text_document.uri,
5203                lsp::Url::from_file_path("/a/main.rs").unwrap(),
5204            );
5205            assert_eq!(params.range.start, lsp::Position::new(0, 0));
5206            assert_eq!(params.range.end, lsp::Position::new(0, 0));
5207            Ok(None)
5208        })
5209        .next()
5210        .await;
5211
5212    // Move cursor to a location that contains code actions.
5213    editor_b.update(cx_b, |editor, cx| {
5214        editor.change_selections(None, cx, |s| {
5215            s.select_ranges([Point::new(1, 31)..Point::new(1, 31)])
5216        });
5217        cx.focus(&editor_b);
5218    });
5219
5220    fake_language_server
5221        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
5222            assert_eq!(
5223                params.text_document.uri,
5224                lsp::Url::from_file_path("/a/main.rs").unwrap(),
5225            );
5226            assert_eq!(params.range.start, lsp::Position::new(1, 31));
5227            assert_eq!(params.range.end, lsp::Position::new(1, 31));
5228
5229            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
5230                lsp::CodeAction {
5231                    title: "Inline into all callers".to_string(),
5232                    edit: Some(lsp::WorkspaceEdit {
5233                        changes: Some(
5234                            [
5235                                (
5236                                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
5237                                    vec![lsp::TextEdit::new(
5238                                        lsp::Range::new(
5239                                            lsp::Position::new(1, 22),
5240                                            lsp::Position::new(1, 34),
5241                                        ),
5242                                        "4".to_string(),
5243                                    )],
5244                                ),
5245                                (
5246                                    lsp::Url::from_file_path("/a/other.rs").unwrap(),
5247                                    vec![lsp::TextEdit::new(
5248                                        lsp::Range::new(
5249                                            lsp::Position::new(0, 0),
5250                                            lsp::Position::new(0, 27),
5251                                        ),
5252                                        "".to_string(),
5253                                    )],
5254                                ),
5255                            ]
5256                            .into_iter()
5257                            .collect(),
5258                        ),
5259                        ..Default::default()
5260                    }),
5261                    data: Some(json!({
5262                        "codeActionParams": {
5263                            "range": {
5264                                "start": {"line": 1, "column": 31},
5265                                "end": {"line": 1, "column": 31},
5266                            }
5267                        }
5268                    })),
5269                    ..Default::default()
5270                },
5271            )]))
5272        })
5273        .next()
5274        .await;
5275
5276    // Toggle code actions and wait for them to display.
5277    editor_b.update(cx_b, |editor, cx| {
5278        editor.toggle_code_actions(
5279            &ToggleCodeActions {
5280                deployed_from_indicator: false,
5281            },
5282            cx,
5283        );
5284    });
5285    cx_a.foreground().run_until_parked();
5286    editor_b.read_with(cx_b, |editor, _| assert!(editor.context_menu_visible()));
5287
5288    fake_language_server.remove_request_handler::<lsp::request::CodeActionRequest>();
5289
5290    // Confirming the code action will trigger a resolve request.
5291    let confirm_action = workspace_b
5292        .update(cx_b, |workspace, cx| {
5293            Editor::confirm_code_action(workspace, &ConfirmCodeAction { item_ix: Some(0) }, cx)
5294        })
5295        .unwrap();
5296    fake_language_server.handle_request::<lsp::request::CodeActionResolveRequest, _, _>(
5297        |_, _| async move {
5298            Ok(lsp::CodeAction {
5299                title: "Inline into all callers".to_string(),
5300                edit: Some(lsp::WorkspaceEdit {
5301                    changes: Some(
5302                        [
5303                            (
5304                                lsp::Url::from_file_path("/a/main.rs").unwrap(),
5305                                vec![lsp::TextEdit::new(
5306                                    lsp::Range::new(
5307                                        lsp::Position::new(1, 22),
5308                                        lsp::Position::new(1, 34),
5309                                    ),
5310                                    "4".to_string(),
5311                                )],
5312                            ),
5313                            (
5314                                lsp::Url::from_file_path("/a/other.rs").unwrap(),
5315                                vec![lsp::TextEdit::new(
5316                                    lsp::Range::new(
5317                                        lsp::Position::new(0, 0),
5318                                        lsp::Position::new(0, 27),
5319                                    ),
5320                                    "".to_string(),
5321                                )],
5322                            ),
5323                        ]
5324                        .into_iter()
5325                        .collect(),
5326                    ),
5327                    ..Default::default()
5328                }),
5329                ..Default::default()
5330            })
5331        },
5332    );
5333
5334    // After the action is confirmed, an editor containing both modified files is opened.
5335    confirm_action.await.unwrap();
5336    let code_action_editor = workspace_b.read_with(cx_b, |workspace, cx| {
5337        workspace
5338            .active_item(cx)
5339            .unwrap()
5340            .downcast::<Editor>()
5341            .unwrap()
5342    });
5343    code_action_editor.update(cx_b, |editor, cx| {
5344        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
5345        editor.undo(&Undo, cx);
5346        assert_eq!(
5347            editor.text(cx),
5348            "mod other;\nfn main() { let foo = other::foo(); }\npub fn foo() -> usize { 4 }"
5349        );
5350        editor.redo(&Redo, cx);
5351        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
5352    });
5353}
5354
5355#[gpui::test(iterations = 10)]
5356async fn test_collaborating_with_renames(
5357    deterministic: Arc<Deterministic>,
5358    cx_a: &mut TestAppContext,
5359    cx_b: &mut TestAppContext,
5360) {
5361    deterministic.forbid_parking();
5362    let mut server = TestServer::start(&deterministic).await;
5363    let client_a = server.create_client(cx_a, "user_a").await;
5364    let client_b = server.create_client(cx_b, "user_b").await;
5365    server
5366        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5367        .await;
5368    let active_call_a = cx_a.read(ActiveCall::global);
5369
5370    cx_b.update(editor::init);
5371
5372    // Set up a fake language server.
5373    let mut language = Language::new(
5374        LanguageConfig {
5375            name: "Rust".into(),
5376            path_suffixes: vec!["rs".to_string()],
5377            ..Default::default()
5378        },
5379        Some(tree_sitter_rust::language()),
5380    );
5381    let mut fake_language_servers = language
5382        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
5383            capabilities: lsp::ServerCapabilities {
5384                rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
5385                    prepare_provider: Some(true),
5386                    work_done_progress_options: Default::default(),
5387                })),
5388                ..Default::default()
5389            },
5390            ..Default::default()
5391        }))
5392        .await;
5393    client_a.language_registry.add(Arc::new(language));
5394
5395    client_a
5396        .fs
5397        .insert_tree(
5398            "/dir",
5399            json!({
5400                "one.rs": "const ONE: usize = 1;",
5401                "two.rs": "const TWO: usize = one::ONE + one::ONE;"
5402            }),
5403        )
5404        .await;
5405    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
5406    let project_id = active_call_a
5407        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5408        .await
5409        .unwrap();
5410    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5411
5412    let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx));
5413    let editor_b = workspace_b
5414        .update(cx_b, |workspace, cx| {
5415            workspace.open_path((worktree_id, "one.rs"), None, true, cx)
5416        })
5417        .await
5418        .unwrap()
5419        .downcast::<Editor>()
5420        .unwrap();
5421    let fake_language_server = fake_language_servers.next().await.unwrap();
5422
5423    // Move cursor to a location that can be renamed.
5424    let prepare_rename = editor_b.update(cx_b, |editor, cx| {
5425        editor.change_selections(None, cx, |s| s.select_ranges([7..7]));
5426        editor.rename(&Rename, cx).unwrap()
5427    });
5428
5429    fake_language_server
5430        .handle_request::<lsp::request::PrepareRenameRequest, _, _>(|params, _| async move {
5431            assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs");
5432            assert_eq!(params.position, lsp::Position::new(0, 7));
5433            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
5434                lsp::Position::new(0, 6),
5435                lsp::Position::new(0, 9),
5436            ))))
5437        })
5438        .next()
5439        .await
5440        .unwrap();
5441    prepare_rename.await.unwrap();
5442    editor_b.update(cx_b, |editor, cx| {
5443        let rename = editor.pending_rename().unwrap();
5444        let buffer = editor.buffer().read(cx).snapshot(cx);
5445        assert_eq!(
5446            rename.range.start.to_offset(&buffer)..rename.range.end.to_offset(&buffer),
5447            6..9
5448        );
5449        rename.editor.update(cx, |rename_editor, cx| {
5450            rename_editor.buffer().update(cx, |rename_buffer, cx| {
5451                rename_buffer.edit([(0..3, "THREE")], None, cx);
5452            });
5453        });
5454    });
5455
5456    let confirm_rename = workspace_b.update(cx_b, |workspace, cx| {
5457        Editor::confirm_rename(workspace, &ConfirmRename, cx).unwrap()
5458    });
5459    fake_language_server
5460        .handle_request::<lsp::request::Rename, _, _>(|params, _| async move {
5461            assert_eq!(
5462                params.text_document_position.text_document.uri.as_str(),
5463                "file:///dir/one.rs"
5464            );
5465            assert_eq!(
5466                params.text_document_position.position,
5467                lsp::Position::new(0, 6)
5468            );
5469            assert_eq!(params.new_name, "THREE");
5470            Ok(Some(lsp::WorkspaceEdit {
5471                changes: Some(
5472                    [
5473                        (
5474                            lsp::Url::from_file_path("/dir/one.rs").unwrap(),
5475                            vec![lsp::TextEdit::new(
5476                                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5477                                "THREE".to_string(),
5478                            )],
5479                        ),
5480                        (
5481                            lsp::Url::from_file_path("/dir/two.rs").unwrap(),
5482                            vec![
5483                                lsp::TextEdit::new(
5484                                    lsp::Range::new(
5485                                        lsp::Position::new(0, 24),
5486                                        lsp::Position::new(0, 27),
5487                                    ),
5488                                    "THREE".to_string(),
5489                                ),
5490                                lsp::TextEdit::new(
5491                                    lsp::Range::new(
5492                                        lsp::Position::new(0, 35),
5493                                        lsp::Position::new(0, 38),
5494                                    ),
5495                                    "THREE".to_string(),
5496                                ),
5497                            ],
5498                        ),
5499                    ]
5500                    .into_iter()
5501                    .collect(),
5502                ),
5503                ..Default::default()
5504            }))
5505        })
5506        .next()
5507        .await
5508        .unwrap();
5509    confirm_rename.await.unwrap();
5510
5511    let rename_editor = workspace_b.read_with(cx_b, |workspace, cx| {
5512        workspace
5513            .active_item(cx)
5514            .unwrap()
5515            .downcast::<Editor>()
5516            .unwrap()
5517    });
5518    rename_editor.update(cx_b, |editor, cx| {
5519        assert_eq!(
5520            editor.text(cx),
5521            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
5522        );
5523        editor.undo(&Undo, cx);
5524        assert_eq!(
5525            editor.text(cx),
5526            "const ONE: usize = 1;\nconst TWO: usize = one::ONE + one::ONE;"
5527        );
5528        editor.redo(&Redo, cx);
5529        assert_eq!(
5530            editor.text(cx),
5531            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
5532        );
5533    });
5534
5535    // Ensure temporary rename edits cannot be undone/redone.
5536    editor_b.update(cx_b, |editor, cx| {
5537        editor.undo(&Undo, cx);
5538        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
5539        editor.undo(&Undo, cx);
5540        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
5541        editor.redo(&Redo, cx);
5542        assert_eq!(editor.text(cx), "const THREE: usize = 1;");
5543    })
5544}
5545
5546#[gpui::test(iterations = 10)]
5547async fn test_language_server_statuses(
5548    deterministic: Arc<Deterministic>,
5549    cx_a: &mut TestAppContext,
5550    cx_b: &mut TestAppContext,
5551) {
5552    deterministic.forbid_parking();
5553    let mut server = TestServer::start(&deterministic).await;
5554    let client_a = server.create_client(cx_a, "user_a").await;
5555    let client_b = server.create_client(cx_b, "user_b").await;
5556    server
5557        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5558        .await;
5559    let active_call_a = cx_a.read(ActiveCall::global);
5560
5561    cx_b.update(editor::init);
5562
5563    // Set up a fake language server.
5564    let mut language = Language::new(
5565        LanguageConfig {
5566            name: "Rust".into(),
5567            path_suffixes: vec!["rs".to_string()],
5568            ..Default::default()
5569        },
5570        Some(tree_sitter_rust::language()),
5571    );
5572    let mut fake_language_servers = language
5573        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
5574            name: "the-language-server",
5575            ..Default::default()
5576        }))
5577        .await;
5578    client_a.language_registry.add(Arc::new(language));
5579
5580    client_a
5581        .fs
5582        .insert_tree(
5583            "/dir",
5584            json!({
5585                "main.rs": "const ONE: usize = 1;",
5586            }),
5587        )
5588        .await;
5589    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
5590
5591    let _buffer_a = project_a
5592        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
5593        .await
5594        .unwrap();
5595
5596    let fake_language_server = fake_language_servers.next().await.unwrap();
5597    fake_language_server.start_progress("the-token").await;
5598    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
5599        token: lsp::NumberOrString::String("the-token".to_string()),
5600        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
5601            lsp::WorkDoneProgressReport {
5602                message: Some("the-message".to_string()),
5603                ..Default::default()
5604            },
5605        )),
5606    });
5607    deterministic.run_until_parked();
5608    project_a.read_with(cx_a, |project, _| {
5609        let status = project.language_server_statuses().next().unwrap();
5610        assert_eq!(status.name, "the-language-server");
5611        assert_eq!(status.pending_work.len(), 1);
5612        assert_eq!(
5613            status.pending_work["the-token"].message.as_ref().unwrap(),
5614            "the-message"
5615        );
5616    });
5617
5618    let project_id = active_call_a
5619        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5620        .await
5621        .unwrap();
5622    deterministic.run_until_parked();
5623    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5624    project_b.read_with(cx_b, |project, _| {
5625        let status = project.language_server_statuses().next().unwrap();
5626        assert_eq!(status.name, "the-language-server");
5627    });
5628
5629    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
5630        token: lsp::NumberOrString::String("the-token".to_string()),
5631        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
5632            lsp::WorkDoneProgressReport {
5633                message: Some("the-message-2".to_string()),
5634                ..Default::default()
5635            },
5636        )),
5637    });
5638    deterministic.run_until_parked();
5639    project_a.read_with(cx_a, |project, _| {
5640        let status = project.language_server_statuses().next().unwrap();
5641        assert_eq!(status.name, "the-language-server");
5642        assert_eq!(status.pending_work.len(), 1);
5643        assert_eq!(
5644            status.pending_work["the-token"].message.as_ref().unwrap(),
5645            "the-message-2"
5646        );
5647    });
5648    project_b.read_with(cx_b, |project, _| {
5649        let status = project.language_server_statuses().next().unwrap();
5650        assert_eq!(status.name, "the-language-server");
5651        assert_eq!(status.pending_work.len(), 1);
5652        assert_eq!(
5653            status.pending_work["the-token"].message.as_ref().unwrap(),
5654            "the-message-2"
5655        );
5656    });
5657}
5658
5659#[gpui::test(iterations = 10)]
5660async fn test_contacts(
5661    deterministic: Arc<Deterministic>,
5662    cx_a: &mut TestAppContext,
5663    cx_b: &mut TestAppContext,
5664    cx_c: &mut TestAppContext,
5665    cx_d: &mut TestAppContext,
5666) {
5667    deterministic.forbid_parking();
5668    let mut server = TestServer::start(&deterministic).await;
5669    let client_a = server.create_client(cx_a, "user_a").await;
5670    let client_b = server.create_client(cx_b, "user_b").await;
5671    let client_c = server.create_client(cx_c, "user_c").await;
5672    let client_d = server.create_client(cx_d, "user_d").await;
5673    server
5674        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
5675        .await;
5676    let active_call_a = cx_a.read(ActiveCall::global);
5677    let active_call_b = cx_b.read(ActiveCall::global);
5678    let active_call_c = cx_c.read(ActiveCall::global);
5679    let _active_call_d = cx_d.read(ActiveCall::global);
5680
5681    deterministic.run_until_parked();
5682    assert_eq!(
5683        contacts(&client_a, cx_a),
5684        [
5685            ("user_b".to_string(), "online", "free"),
5686            ("user_c".to_string(), "online", "free")
5687        ]
5688    );
5689    assert_eq!(
5690        contacts(&client_b, cx_b),
5691        [
5692            ("user_a".to_string(), "online", "free"),
5693            ("user_c".to_string(), "online", "free")
5694        ]
5695    );
5696    assert_eq!(
5697        contacts(&client_c, cx_c),
5698        [
5699            ("user_a".to_string(), "online", "free"),
5700            ("user_b".to_string(), "online", "free")
5701        ]
5702    );
5703    assert_eq!(contacts(&client_d, cx_d), []);
5704
5705    server.disconnect_client(client_c.peer_id().unwrap());
5706    server.forbid_connections();
5707    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5708    assert_eq!(
5709        contacts(&client_a, cx_a),
5710        [
5711            ("user_b".to_string(), "online", "free"),
5712            ("user_c".to_string(), "offline", "free")
5713        ]
5714    );
5715    assert_eq!(
5716        contacts(&client_b, cx_b),
5717        [
5718            ("user_a".to_string(), "online", "free"),
5719            ("user_c".to_string(), "offline", "free")
5720        ]
5721    );
5722    assert_eq!(contacts(&client_c, cx_c), []);
5723    assert_eq!(contacts(&client_d, cx_d), []);
5724
5725    server.allow_connections();
5726    client_c
5727        .authenticate_and_connect(false, &cx_c.to_async())
5728        .await
5729        .unwrap();
5730
5731    deterministic.run_until_parked();
5732    assert_eq!(
5733        contacts(&client_a, cx_a),
5734        [
5735            ("user_b".to_string(), "online", "free"),
5736            ("user_c".to_string(), "online", "free")
5737        ]
5738    );
5739    assert_eq!(
5740        contacts(&client_b, cx_b),
5741        [
5742            ("user_a".to_string(), "online", "free"),
5743            ("user_c".to_string(), "online", "free")
5744        ]
5745    );
5746    assert_eq!(
5747        contacts(&client_c, cx_c),
5748        [
5749            ("user_a".to_string(), "online", "free"),
5750            ("user_b".to_string(), "online", "free")
5751        ]
5752    );
5753    assert_eq!(contacts(&client_d, cx_d), []);
5754
5755    active_call_a
5756        .update(cx_a, |call, cx| {
5757            call.invite(client_b.user_id().unwrap(), None, cx)
5758        })
5759        .await
5760        .unwrap();
5761    deterministic.run_until_parked();
5762    assert_eq!(
5763        contacts(&client_a, cx_a),
5764        [
5765            ("user_b".to_string(), "online", "busy"),
5766            ("user_c".to_string(), "online", "free")
5767        ]
5768    );
5769    assert_eq!(
5770        contacts(&client_b, cx_b),
5771        [
5772            ("user_a".to_string(), "online", "busy"),
5773            ("user_c".to_string(), "online", "free")
5774        ]
5775    );
5776    assert_eq!(
5777        contacts(&client_c, cx_c),
5778        [
5779            ("user_a".to_string(), "online", "busy"),
5780            ("user_b".to_string(), "online", "busy")
5781        ]
5782    );
5783    assert_eq!(contacts(&client_d, cx_d), []);
5784
5785    // Client B and client D become contacts while client B is being called.
5786    server
5787        .make_contacts(&mut [(&client_b, cx_b), (&client_d, cx_d)])
5788        .await;
5789    deterministic.run_until_parked();
5790    assert_eq!(
5791        contacts(&client_a, cx_a),
5792        [
5793            ("user_b".to_string(), "online", "busy"),
5794            ("user_c".to_string(), "online", "free")
5795        ]
5796    );
5797    assert_eq!(
5798        contacts(&client_b, cx_b),
5799        [
5800            ("user_a".to_string(), "online", "busy"),
5801            ("user_c".to_string(), "online", "free"),
5802            ("user_d".to_string(), "online", "free"),
5803        ]
5804    );
5805    assert_eq!(
5806        contacts(&client_c, cx_c),
5807        [
5808            ("user_a".to_string(), "online", "busy"),
5809            ("user_b".to_string(), "online", "busy")
5810        ]
5811    );
5812    assert_eq!(
5813        contacts(&client_d, cx_d),
5814        [("user_b".to_string(), "online", "busy")]
5815    );
5816
5817    active_call_b.update(cx_b, |call, _| call.decline_incoming().unwrap());
5818    deterministic.run_until_parked();
5819    assert_eq!(
5820        contacts(&client_a, cx_a),
5821        [
5822            ("user_b".to_string(), "online", "free"),
5823            ("user_c".to_string(), "online", "free")
5824        ]
5825    );
5826    assert_eq!(
5827        contacts(&client_b, cx_b),
5828        [
5829            ("user_a".to_string(), "online", "free"),
5830            ("user_c".to_string(), "online", "free"),
5831            ("user_d".to_string(), "online", "free")
5832        ]
5833    );
5834    assert_eq!(
5835        contacts(&client_c, cx_c),
5836        [
5837            ("user_a".to_string(), "online", "free"),
5838            ("user_b".to_string(), "online", "free")
5839        ]
5840    );
5841    assert_eq!(
5842        contacts(&client_d, cx_d),
5843        [("user_b".to_string(), "online", "free")]
5844    );
5845
5846    active_call_c
5847        .update(cx_c, |call, cx| {
5848            call.invite(client_a.user_id().unwrap(), None, cx)
5849        })
5850        .await
5851        .unwrap();
5852    deterministic.run_until_parked();
5853    assert_eq!(
5854        contacts(&client_a, cx_a),
5855        [
5856            ("user_b".to_string(), "online", "free"),
5857            ("user_c".to_string(), "online", "busy")
5858        ]
5859    );
5860    assert_eq!(
5861        contacts(&client_b, cx_b),
5862        [
5863            ("user_a".to_string(), "online", "busy"),
5864            ("user_c".to_string(), "online", "busy"),
5865            ("user_d".to_string(), "online", "free")
5866        ]
5867    );
5868    assert_eq!(
5869        contacts(&client_c, cx_c),
5870        [
5871            ("user_a".to_string(), "online", "busy"),
5872            ("user_b".to_string(), "online", "free")
5873        ]
5874    );
5875    assert_eq!(
5876        contacts(&client_d, cx_d),
5877        [("user_b".to_string(), "online", "free")]
5878    );
5879
5880    active_call_a
5881        .update(cx_a, |call, cx| call.accept_incoming(cx))
5882        .await
5883        .unwrap();
5884    deterministic.run_until_parked();
5885    assert_eq!(
5886        contacts(&client_a, cx_a),
5887        [
5888            ("user_b".to_string(), "online", "free"),
5889            ("user_c".to_string(), "online", "busy")
5890        ]
5891    );
5892    assert_eq!(
5893        contacts(&client_b, cx_b),
5894        [
5895            ("user_a".to_string(), "online", "busy"),
5896            ("user_c".to_string(), "online", "busy"),
5897            ("user_d".to_string(), "online", "free")
5898        ]
5899    );
5900    assert_eq!(
5901        contacts(&client_c, cx_c),
5902        [
5903            ("user_a".to_string(), "online", "busy"),
5904            ("user_b".to_string(), "online", "free")
5905        ]
5906    );
5907    assert_eq!(
5908        contacts(&client_d, cx_d),
5909        [("user_b".to_string(), "online", "free")]
5910    );
5911
5912    active_call_a
5913        .update(cx_a, |call, cx| {
5914            call.invite(client_b.user_id().unwrap(), None, cx)
5915        })
5916        .await
5917        .unwrap();
5918    deterministic.run_until_parked();
5919    assert_eq!(
5920        contacts(&client_a, cx_a),
5921        [
5922            ("user_b".to_string(), "online", "busy"),
5923            ("user_c".to_string(), "online", "busy")
5924        ]
5925    );
5926    assert_eq!(
5927        contacts(&client_b, cx_b),
5928        [
5929            ("user_a".to_string(), "online", "busy"),
5930            ("user_c".to_string(), "online", "busy"),
5931            ("user_d".to_string(), "online", "free")
5932        ]
5933    );
5934    assert_eq!(
5935        contacts(&client_c, cx_c),
5936        [
5937            ("user_a".to_string(), "online", "busy"),
5938            ("user_b".to_string(), "online", "busy")
5939        ]
5940    );
5941    assert_eq!(
5942        contacts(&client_d, cx_d),
5943        [("user_b".to_string(), "online", "busy")]
5944    );
5945
5946    active_call_a
5947        .update(cx_a, |call, cx| call.hang_up(cx))
5948        .await
5949        .unwrap();
5950    deterministic.run_until_parked();
5951    assert_eq!(
5952        contacts(&client_a, cx_a),
5953        [
5954            ("user_b".to_string(), "online", "free"),
5955            ("user_c".to_string(), "online", "free")
5956        ]
5957    );
5958    assert_eq!(
5959        contacts(&client_b, cx_b),
5960        [
5961            ("user_a".to_string(), "online", "free"),
5962            ("user_c".to_string(), "online", "free"),
5963            ("user_d".to_string(), "online", "free")
5964        ]
5965    );
5966    assert_eq!(
5967        contacts(&client_c, cx_c),
5968        [
5969            ("user_a".to_string(), "online", "free"),
5970            ("user_b".to_string(), "online", "free")
5971        ]
5972    );
5973    assert_eq!(
5974        contacts(&client_d, cx_d),
5975        [("user_b".to_string(), "online", "free")]
5976    );
5977
5978    active_call_a
5979        .update(cx_a, |call, cx| {
5980            call.invite(client_b.user_id().unwrap(), None, cx)
5981        })
5982        .await
5983        .unwrap();
5984    deterministic.run_until_parked();
5985    assert_eq!(
5986        contacts(&client_a, cx_a),
5987        [
5988            ("user_b".to_string(), "online", "busy"),
5989            ("user_c".to_string(), "online", "free")
5990        ]
5991    );
5992    assert_eq!(
5993        contacts(&client_b, cx_b),
5994        [
5995            ("user_a".to_string(), "online", "busy"),
5996            ("user_c".to_string(), "online", "free"),
5997            ("user_d".to_string(), "online", "free")
5998        ]
5999    );
6000    assert_eq!(
6001        contacts(&client_c, cx_c),
6002        [
6003            ("user_a".to_string(), "online", "busy"),
6004            ("user_b".to_string(), "online", "busy")
6005        ]
6006    );
6007    assert_eq!(
6008        contacts(&client_d, cx_d),
6009        [("user_b".to_string(), "online", "busy")]
6010    );
6011
6012    server.forbid_connections();
6013    server.disconnect_client(client_a.peer_id().unwrap());
6014    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
6015    assert_eq!(contacts(&client_a, cx_a), []);
6016    assert_eq!(
6017        contacts(&client_b, cx_b),
6018        [
6019            ("user_a".to_string(), "offline", "free"),
6020            ("user_c".to_string(), "online", "free"),
6021            ("user_d".to_string(), "online", "free")
6022        ]
6023    );
6024    assert_eq!(
6025        contacts(&client_c, cx_c),
6026        [
6027            ("user_a".to_string(), "offline", "free"),
6028            ("user_b".to_string(), "online", "free")
6029        ]
6030    );
6031    assert_eq!(
6032        contacts(&client_d, cx_d),
6033        [("user_b".to_string(), "online", "free")]
6034    );
6035
6036    // Test removing a contact
6037    client_b
6038        .user_store
6039        .update(cx_b, |store, cx| {
6040            store.remove_contact(client_c.user_id().unwrap(), cx)
6041        })
6042        .await
6043        .unwrap();
6044    deterministic.run_until_parked();
6045    assert_eq!(
6046        contacts(&client_b, cx_b),
6047        [
6048            ("user_a".to_string(), "offline", "free"),
6049            ("user_d".to_string(), "online", "free")
6050        ]
6051    );
6052    assert_eq!(
6053        contacts(&client_c, cx_c),
6054        [("user_a".to_string(), "offline", "free"),]
6055    );
6056
6057    fn contacts(
6058        client: &TestClient,
6059        cx: &TestAppContext,
6060    ) -> Vec<(String, &'static str, &'static str)> {
6061        client.user_store.read_with(cx, |store, _| {
6062            store
6063                .contacts()
6064                .iter()
6065                .map(|contact| {
6066                    (
6067                        contact.user.github_login.clone(),
6068                        if contact.online { "online" } else { "offline" },
6069                        if contact.busy { "busy" } else { "free" },
6070                    )
6071                })
6072                .collect()
6073        })
6074    }
6075}
6076
6077#[gpui::test(iterations = 10)]
6078async fn test_contact_requests(
6079    deterministic: Arc<Deterministic>,
6080    cx_a: &mut TestAppContext,
6081    cx_a2: &mut TestAppContext,
6082    cx_b: &mut TestAppContext,
6083    cx_b2: &mut TestAppContext,
6084    cx_c: &mut TestAppContext,
6085    cx_c2: &mut TestAppContext,
6086) {
6087    deterministic.forbid_parking();
6088
6089    // Connect to a server as 3 clients.
6090    let mut server = TestServer::start(&deterministic).await;
6091    let client_a = server.create_client(cx_a, "user_a").await;
6092    let client_a2 = server.create_client(cx_a2, "user_a").await;
6093    let client_b = server.create_client(cx_b, "user_b").await;
6094    let client_b2 = server.create_client(cx_b2, "user_b").await;
6095    let client_c = server.create_client(cx_c, "user_c").await;
6096    let client_c2 = server.create_client(cx_c2, "user_c").await;
6097
6098    assert_eq!(client_a.user_id().unwrap(), client_a2.user_id().unwrap());
6099    assert_eq!(client_b.user_id().unwrap(), client_b2.user_id().unwrap());
6100    assert_eq!(client_c.user_id().unwrap(), client_c2.user_id().unwrap());
6101
6102    // User A and User C request that user B become their contact.
6103    client_a
6104        .user_store
6105        .update(cx_a, |store, cx| {
6106            store.request_contact(client_b.user_id().unwrap(), cx)
6107        })
6108        .await
6109        .unwrap();
6110    client_c
6111        .user_store
6112        .update(cx_c, |store, cx| {
6113            store.request_contact(client_b.user_id().unwrap(), cx)
6114        })
6115        .await
6116        .unwrap();
6117    deterministic.run_until_parked();
6118
6119    // All users see the pending request appear in all their clients.
6120    assert_eq!(
6121        client_a.summarize_contacts(cx_a).outgoing_requests,
6122        &["user_b"]
6123    );
6124    assert_eq!(
6125        client_a2.summarize_contacts(cx_a2).outgoing_requests,
6126        &["user_b"]
6127    );
6128    assert_eq!(
6129        client_b.summarize_contacts(cx_b).incoming_requests,
6130        &["user_a", "user_c"]
6131    );
6132    assert_eq!(
6133        client_b2.summarize_contacts(cx_b2).incoming_requests,
6134        &["user_a", "user_c"]
6135    );
6136    assert_eq!(
6137        client_c.summarize_contacts(cx_c).outgoing_requests,
6138        &["user_b"]
6139    );
6140    assert_eq!(
6141        client_c2.summarize_contacts(cx_c2).outgoing_requests,
6142        &["user_b"]
6143    );
6144
6145    // Contact requests are present upon connecting (tested here via disconnect/reconnect)
6146    disconnect_and_reconnect(&client_a, cx_a).await;
6147    disconnect_and_reconnect(&client_b, cx_b).await;
6148    disconnect_and_reconnect(&client_c, cx_c).await;
6149    deterministic.run_until_parked();
6150    assert_eq!(
6151        client_a.summarize_contacts(cx_a).outgoing_requests,
6152        &["user_b"]
6153    );
6154    assert_eq!(
6155        client_b.summarize_contacts(cx_b).incoming_requests,
6156        &["user_a", "user_c"]
6157    );
6158    assert_eq!(
6159        client_c.summarize_contacts(cx_c).outgoing_requests,
6160        &["user_b"]
6161    );
6162
6163    // User B accepts the request from user A.
6164    client_b
6165        .user_store
6166        .update(cx_b, |store, cx| {
6167            store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
6168        })
6169        .await
6170        .unwrap();
6171
6172    deterministic.run_until_parked();
6173
6174    // User B sees user A as their contact now in all client, and the incoming request from them is removed.
6175    let contacts_b = client_b.summarize_contacts(cx_b);
6176    assert_eq!(contacts_b.current, &["user_a"]);
6177    assert_eq!(contacts_b.incoming_requests, &["user_c"]);
6178    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
6179    assert_eq!(contacts_b2.current, &["user_a"]);
6180    assert_eq!(contacts_b2.incoming_requests, &["user_c"]);
6181
6182    // User A sees user B as their contact now in all clients, and the outgoing request to them is removed.
6183    let contacts_a = client_a.summarize_contacts(cx_a);
6184    assert_eq!(contacts_a.current, &["user_b"]);
6185    assert!(contacts_a.outgoing_requests.is_empty());
6186    let contacts_a2 = client_a2.summarize_contacts(cx_a2);
6187    assert_eq!(contacts_a2.current, &["user_b"]);
6188    assert!(contacts_a2.outgoing_requests.is_empty());
6189
6190    // Contacts are present upon connecting (tested here via disconnect/reconnect)
6191    disconnect_and_reconnect(&client_a, cx_a).await;
6192    disconnect_and_reconnect(&client_b, cx_b).await;
6193    disconnect_and_reconnect(&client_c, cx_c).await;
6194    deterministic.run_until_parked();
6195    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
6196    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
6197    assert_eq!(
6198        client_b.summarize_contacts(cx_b).incoming_requests,
6199        &["user_c"]
6200    );
6201    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
6202    assert_eq!(
6203        client_c.summarize_contacts(cx_c).outgoing_requests,
6204        &["user_b"]
6205    );
6206
6207    // User B rejects the request from user C.
6208    client_b
6209        .user_store
6210        .update(cx_b, |store, cx| {
6211            store.respond_to_contact_request(client_c.user_id().unwrap(), false, cx)
6212        })
6213        .await
6214        .unwrap();
6215
6216    deterministic.run_until_parked();
6217
6218    // User B doesn't see user C as their contact, and the incoming request from them is removed.
6219    let contacts_b = client_b.summarize_contacts(cx_b);
6220    assert_eq!(contacts_b.current, &["user_a"]);
6221    assert!(contacts_b.incoming_requests.is_empty());
6222    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
6223    assert_eq!(contacts_b2.current, &["user_a"]);
6224    assert!(contacts_b2.incoming_requests.is_empty());
6225
6226    // User C doesn't see user B as their contact, and the outgoing request to them is removed.
6227    let contacts_c = client_c.summarize_contacts(cx_c);
6228    assert!(contacts_c.current.is_empty());
6229    assert!(contacts_c.outgoing_requests.is_empty());
6230    let contacts_c2 = client_c2.summarize_contacts(cx_c2);
6231    assert!(contacts_c2.current.is_empty());
6232    assert!(contacts_c2.outgoing_requests.is_empty());
6233
6234    // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect)
6235    disconnect_and_reconnect(&client_a, cx_a).await;
6236    disconnect_and_reconnect(&client_b, cx_b).await;
6237    disconnect_and_reconnect(&client_c, cx_c).await;
6238    deterministic.run_until_parked();
6239    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
6240    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
6241    assert!(client_b
6242        .summarize_contacts(cx_b)
6243        .incoming_requests
6244        .is_empty());
6245    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
6246    assert!(client_c
6247        .summarize_contacts(cx_c)
6248        .outgoing_requests
6249        .is_empty());
6250
6251    async fn disconnect_and_reconnect(client: &TestClient, cx: &mut TestAppContext) {
6252        client.disconnect(&cx.to_async());
6253        client.clear_contacts(cx).await;
6254        client
6255            .authenticate_and_connect(false, &cx.to_async())
6256            .await
6257            .unwrap();
6258    }
6259}
6260
6261#[gpui::test(iterations = 10)]
6262async fn test_basic_following(
6263    deterministic: Arc<Deterministic>,
6264    cx_a: &mut TestAppContext,
6265    cx_b: &mut TestAppContext,
6266    cx_c: &mut TestAppContext,
6267    cx_d: &mut TestAppContext,
6268) {
6269    deterministic.forbid_parking();
6270
6271    let mut server = TestServer::start(&deterministic).await;
6272    let client_a = server.create_client(cx_a, "user_a").await;
6273    let client_b = server.create_client(cx_b, "user_b").await;
6274    let client_c = server.create_client(cx_c, "user_c").await;
6275    let client_d = server.create_client(cx_d, "user_d").await;
6276    server
6277        .create_room(&mut [
6278            (&client_a, cx_a),
6279            (&client_b, cx_b),
6280            (&client_c, cx_c),
6281            (&client_d, cx_d),
6282        ])
6283        .await;
6284    let active_call_a = cx_a.read(ActiveCall::global);
6285    let active_call_b = cx_b.read(ActiveCall::global);
6286
6287    cx_a.update(editor::init);
6288    cx_b.update(editor::init);
6289
6290    client_a
6291        .fs
6292        .insert_tree(
6293            "/a",
6294            json!({
6295                "1.txt": "one\none\none",
6296                "2.txt": "two\ntwo\ntwo",
6297                "3.txt": "three\nthree\nthree",
6298            }),
6299        )
6300        .await;
6301    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
6302    active_call_a
6303        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
6304        .await
6305        .unwrap();
6306
6307    let project_id = active_call_a
6308        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6309        .await
6310        .unwrap();
6311    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6312    active_call_b
6313        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6314        .await
6315        .unwrap();
6316
6317    let workspace_a = client_a.build_workspace(&project_a, cx_a);
6318    let workspace_b = client_b.build_workspace(&project_b, cx_b);
6319
6320    // Client A opens some editors.
6321    let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
6322    let editor_a1 = workspace_a
6323        .update(cx_a, |workspace, cx| {
6324            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6325        })
6326        .await
6327        .unwrap()
6328        .downcast::<Editor>()
6329        .unwrap();
6330    let editor_a2 = workspace_a
6331        .update(cx_a, |workspace, cx| {
6332            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
6333        })
6334        .await
6335        .unwrap()
6336        .downcast::<Editor>()
6337        .unwrap();
6338
6339    // Client B opens an editor.
6340    let editor_b1 = workspace_b
6341        .update(cx_b, |workspace, cx| {
6342            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6343        })
6344        .await
6345        .unwrap()
6346        .downcast::<Editor>()
6347        .unwrap();
6348
6349    let peer_id_a = client_a.peer_id().unwrap();
6350    let peer_id_b = client_b.peer_id().unwrap();
6351    let peer_id_c = client_c.peer_id().unwrap();
6352    let peer_id_d = client_d.peer_id().unwrap();
6353
6354    // Client A updates their selections in those editors
6355    editor_a1.update(cx_a, |editor, cx| {
6356        editor.handle_input("a", cx);
6357        editor.handle_input("b", cx);
6358        editor.handle_input("c", cx);
6359        editor.select_left(&Default::default(), cx);
6360        assert_eq!(editor.selections.ranges(cx), vec![3..2]);
6361    });
6362    editor_a2.update(cx_a, |editor, cx| {
6363        editor.handle_input("d", cx);
6364        editor.handle_input("e", cx);
6365        editor.select_left(&Default::default(), cx);
6366        assert_eq!(editor.selections.ranges(cx), vec![2..1]);
6367    });
6368
6369    // When client B starts following client A, all visible view states are replicated to client B.
6370    workspace_b
6371        .update(cx_b, |workspace, cx| {
6372            workspace.toggle_follow(peer_id_a, cx).unwrap()
6373        })
6374        .await
6375        .unwrap();
6376
6377    cx_c.foreground().run_until_parked();
6378    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
6379        workspace
6380            .active_item(cx)
6381            .unwrap()
6382            .downcast::<Editor>()
6383            .unwrap()
6384    });
6385    assert_eq!(
6386        cx_b.read(|cx| editor_b2.project_path(cx)),
6387        Some((worktree_id, "2.txt").into())
6388    );
6389    assert_eq!(
6390        editor_b2.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
6391        vec![2..1]
6392    );
6393    assert_eq!(
6394        editor_b1.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
6395        vec![3..2]
6396    );
6397
6398    cx_c.foreground().run_until_parked();
6399    let active_call_c = cx_c.read(ActiveCall::global);
6400    let project_c = client_c.build_remote_project(project_id, cx_c).await;
6401    let workspace_c = client_c.build_workspace(&project_c, cx_c);
6402    active_call_c
6403        .update(cx_c, |call, cx| call.set_location(Some(&project_c), cx))
6404        .await
6405        .unwrap();
6406    drop(project_c);
6407
6408    // Client C also follows client A.
6409    workspace_c
6410        .update(cx_c, |workspace, cx| {
6411            workspace.toggle_follow(peer_id_a, cx).unwrap()
6412        })
6413        .await
6414        .unwrap();
6415
6416    cx_d.foreground().run_until_parked();
6417    let active_call_d = cx_d.read(ActiveCall::global);
6418    let project_d = client_d.build_remote_project(project_id, cx_d).await;
6419    let workspace_d = client_d.build_workspace(&project_d, cx_d);
6420    active_call_d
6421        .update(cx_d, |call, cx| call.set_location(Some(&project_d), cx))
6422        .await
6423        .unwrap();
6424    drop(project_d);
6425
6426    // All clients see that clients B and C are following client A.
6427    cx_c.foreground().run_until_parked();
6428    for (name, active_call, cx) in [
6429        ("A", &active_call_a, &cx_a),
6430        ("B", &active_call_b, &cx_b),
6431        ("C", &active_call_c, &cx_c),
6432        ("D", &active_call_d, &cx_d),
6433    ] {
6434        active_call.read_with(*cx, |call, cx| {
6435            let room = call.room().unwrap().read(cx);
6436            assert_eq!(
6437                room.followers_for(peer_id_a, project_id),
6438                &[peer_id_b, peer_id_c],
6439                "checking followers for A as {name}"
6440            );
6441        });
6442    }
6443
6444    // Client C unfollows client A.
6445    workspace_c.update(cx_c, |workspace, cx| {
6446        workspace.toggle_follow(peer_id_a, cx);
6447    });
6448
6449    // All clients see that clients B is following client A.
6450    cx_c.foreground().run_until_parked();
6451    for (name, active_call, cx) in [
6452        ("A", &active_call_a, &cx_a),
6453        ("B", &active_call_b, &cx_b),
6454        ("C", &active_call_c, &cx_c),
6455        ("D", &active_call_d, &cx_d),
6456    ] {
6457        active_call.read_with(*cx, |call, cx| {
6458            let room = call.room().unwrap().read(cx);
6459            assert_eq!(
6460                room.followers_for(peer_id_a, project_id),
6461                &[peer_id_b],
6462                "checking followers for A as {name}"
6463            );
6464        });
6465    }
6466
6467    // Client C re-follows client A.
6468    workspace_c.update(cx_c, |workspace, cx| {
6469        workspace.toggle_follow(peer_id_a, cx);
6470    });
6471
6472    // All clients see that clients B and C are following client A.
6473    cx_c.foreground().run_until_parked();
6474    for (name, active_call, cx) in [
6475        ("A", &active_call_a, &cx_a),
6476        ("B", &active_call_b, &cx_b),
6477        ("C", &active_call_c, &cx_c),
6478        ("D", &active_call_d, &cx_d),
6479    ] {
6480        active_call.read_with(*cx, |call, cx| {
6481            let room = call.room().unwrap().read(cx);
6482            assert_eq!(
6483                room.followers_for(peer_id_a, project_id),
6484                &[peer_id_b, peer_id_c],
6485                "checking followers for A as {name}"
6486            );
6487        });
6488    }
6489
6490    // Client D follows client C.
6491    workspace_d
6492        .update(cx_d, |workspace, cx| {
6493            workspace.toggle_follow(peer_id_c, cx).unwrap()
6494        })
6495        .await
6496        .unwrap();
6497
6498    // All clients see that D is following C
6499    cx_d.foreground().run_until_parked();
6500    for (name, active_call, cx) in [
6501        ("A", &active_call_a, &cx_a),
6502        ("B", &active_call_b, &cx_b),
6503        ("C", &active_call_c, &cx_c),
6504        ("D", &active_call_d, &cx_d),
6505    ] {
6506        active_call.read_with(*cx, |call, cx| {
6507            let room = call.room().unwrap().read(cx);
6508            assert_eq!(
6509                room.followers_for(peer_id_c, project_id),
6510                &[peer_id_d],
6511                "checking followers for C as {name}"
6512            );
6513        });
6514    }
6515
6516    // Client C closes the project.
6517    cx_c.drop_last(workspace_c);
6518
6519    // Clients A and B see that client B is following A, and client C is not present in the followers.
6520    cx_c.foreground().run_until_parked();
6521    for (name, active_call, cx) in [("A", &active_call_a, &cx_a), ("B", &active_call_b, &cx_b)] {
6522        active_call.read_with(*cx, |call, cx| {
6523            let room = call.room().unwrap().read(cx);
6524            assert_eq!(
6525                room.followers_for(peer_id_a, project_id),
6526                &[peer_id_b],
6527                "checking followers for A as {name}"
6528            );
6529        });
6530    }
6531
6532    // All clients see that no-one is following C
6533    for (name, active_call, cx) in [
6534        ("A", &active_call_a, &cx_a),
6535        ("B", &active_call_b, &cx_b),
6536        ("C", &active_call_c, &cx_c),
6537        ("D", &active_call_d, &cx_d),
6538    ] {
6539        active_call.read_with(*cx, |call, cx| {
6540            let room = call.room().unwrap().read(cx);
6541            assert_eq!(
6542                room.followers_for(peer_id_c, project_id),
6543                &[],
6544                "checking followers for C as {name}"
6545            );
6546        });
6547    }
6548
6549    // When client A activates a different editor, client B does so as well.
6550    workspace_a.update(cx_a, |workspace, cx| {
6551        workspace.activate_item(&editor_a1, cx)
6552    });
6553    deterministic.run_until_parked();
6554    workspace_b.read_with(cx_b, |workspace, cx| {
6555        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
6556    });
6557
6558    // When client A opens a multibuffer, client B does so as well.
6559    let multibuffer_a = cx_a.add_model(|cx| {
6560        let buffer_a1 = project_a.update(cx, |project, cx| {
6561            project
6562                .get_open_buffer(&(worktree_id, "1.txt").into(), cx)
6563                .unwrap()
6564        });
6565        let buffer_a2 = project_a.update(cx, |project, cx| {
6566            project
6567                .get_open_buffer(&(worktree_id, "2.txt").into(), cx)
6568                .unwrap()
6569        });
6570        let mut result = MultiBuffer::new(0);
6571        result.push_excerpts(
6572            buffer_a1,
6573            [ExcerptRange {
6574                context: 0..3,
6575                primary: None,
6576            }],
6577            cx,
6578        );
6579        result.push_excerpts(
6580            buffer_a2,
6581            [ExcerptRange {
6582                context: 4..7,
6583                primary: None,
6584            }],
6585            cx,
6586        );
6587        result
6588    });
6589    let multibuffer_editor_a = workspace_a.update(cx_a, |workspace, cx| {
6590        let editor =
6591            cx.add_view(|cx| Editor::for_multibuffer(multibuffer_a, Some(project_a.clone()), cx));
6592        workspace.add_item(Box::new(editor.clone()), cx);
6593        editor
6594    });
6595    deterministic.run_until_parked();
6596    let multibuffer_editor_b = workspace_b.read_with(cx_b, |workspace, cx| {
6597        workspace
6598            .active_item(cx)
6599            .unwrap()
6600            .downcast::<Editor>()
6601            .unwrap()
6602    });
6603    assert_eq!(
6604        multibuffer_editor_a.read_with(cx_a, |editor, cx| editor.text(cx)),
6605        multibuffer_editor_b.read_with(cx_b, |editor, cx| editor.text(cx)),
6606    );
6607
6608    // When client A navigates back and forth, client B does so as well.
6609    workspace_a
6610        .update(cx_a, |workspace, cx| {
6611            workspace.go_back(workspace.active_pane().downgrade(), cx)
6612        })
6613        .await
6614        .unwrap();
6615    deterministic.run_until_parked();
6616    workspace_b.read_with(cx_b, |workspace, cx| {
6617        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
6618    });
6619
6620    workspace_a
6621        .update(cx_a, |workspace, cx| {
6622            workspace.go_back(workspace.active_pane().downgrade(), cx)
6623        })
6624        .await
6625        .unwrap();
6626    deterministic.run_until_parked();
6627    workspace_b.read_with(cx_b, |workspace, cx| {
6628        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b2.id());
6629    });
6630
6631    workspace_a
6632        .update(cx_a, |workspace, cx| {
6633            workspace.go_forward(workspace.active_pane().downgrade(), cx)
6634        })
6635        .await
6636        .unwrap();
6637    deterministic.run_until_parked();
6638    workspace_b.read_with(cx_b, |workspace, cx| {
6639        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
6640    });
6641
6642    // Changes to client A's editor are reflected on client B.
6643    editor_a1.update(cx_a, |editor, cx| {
6644        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2]));
6645    });
6646    deterministic.run_until_parked();
6647    editor_b1.read_with(cx_b, |editor, cx| {
6648        assert_eq!(editor.selections.ranges(cx), &[1..1, 2..2]);
6649    });
6650
6651    editor_a1.update(cx_a, |editor, cx| editor.set_text("TWO", cx));
6652    deterministic.run_until_parked();
6653    editor_b1.read_with(cx_b, |editor, cx| assert_eq!(editor.text(cx), "TWO"));
6654
6655    editor_a1.update(cx_a, |editor, cx| {
6656        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
6657        editor.set_scroll_position(vec2f(0., 100.), cx);
6658    });
6659    deterministic.run_until_parked();
6660    editor_b1.read_with(cx_b, |editor, cx| {
6661        assert_eq!(editor.selections.ranges(cx), &[3..3]);
6662    });
6663
6664    // After unfollowing, client B stops receiving updates from client A.
6665    workspace_b.update(cx_b, |workspace, cx| {
6666        workspace.unfollow(&workspace.active_pane().clone(), cx)
6667    });
6668    workspace_a.update(cx_a, |workspace, cx| {
6669        workspace.activate_item(&editor_a2, cx)
6670    });
6671    deterministic.run_until_parked();
6672    assert_eq!(
6673        workspace_b.read_with(cx_b, |workspace, cx| workspace
6674            .active_item(cx)
6675            .unwrap()
6676            .id()),
6677        editor_b1.id()
6678    );
6679
6680    // Client A starts following client B.
6681    workspace_a
6682        .update(cx_a, |workspace, cx| {
6683            workspace.toggle_follow(peer_id_b, cx).unwrap()
6684        })
6685        .await
6686        .unwrap();
6687    assert_eq!(
6688        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
6689        Some(peer_id_b)
6690    );
6691    assert_eq!(
6692        workspace_a.read_with(cx_a, |workspace, cx| workspace
6693            .active_item(cx)
6694            .unwrap()
6695            .id()),
6696        editor_a1.id()
6697    );
6698
6699    // Client B activates an external window, which causes a new screen-sharing item to be added to the pane.
6700    let display = MacOSDisplay::new();
6701    active_call_b
6702        .update(cx_b, |call, cx| call.set_location(None, cx))
6703        .await
6704        .unwrap();
6705    active_call_b
6706        .update(cx_b, |call, cx| {
6707            call.room().unwrap().update(cx, |room, cx| {
6708                room.set_display_sources(vec![display.clone()]);
6709                room.share_screen(cx)
6710            })
6711        })
6712        .await
6713        .unwrap();
6714    deterministic.run_until_parked();
6715    let shared_screen = workspace_a.read_with(cx_a, |workspace, cx| {
6716        workspace
6717            .active_item(cx)
6718            .unwrap()
6719            .downcast::<SharedScreen>()
6720            .unwrap()
6721    });
6722
6723    // Client B activates Zed again, which causes the previous editor to become focused again.
6724    active_call_b
6725        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6726        .await
6727        .unwrap();
6728    deterministic.run_until_parked();
6729    workspace_a.read_with(cx_a, |workspace, cx| {
6730        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_a1.id())
6731    });
6732
6733    // Client B activates a multibuffer that was created by following client A. Client A returns to that multibuffer.
6734    workspace_b.update(cx_b, |workspace, cx| {
6735        workspace.activate_item(&multibuffer_editor_b, cx)
6736    });
6737    deterministic.run_until_parked();
6738    workspace_a.read_with(cx_a, |workspace, cx| {
6739        assert_eq!(
6740            workspace.active_item(cx).unwrap().id(),
6741            multibuffer_editor_a.id()
6742        )
6743    });
6744
6745    // Client B activates an external window again, and the previously-opened screen-sharing item
6746    // gets activated.
6747    active_call_b
6748        .update(cx_b, |call, cx| call.set_location(None, cx))
6749        .await
6750        .unwrap();
6751    deterministic.run_until_parked();
6752    assert_eq!(
6753        workspace_a.read_with(cx_a, |workspace, cx| workspace
6754            .active_item(cx)
6755            .unwrap()
6756            .id()),
6757        shared_screen.id()
6758    );
6759
6760    // Following interrupts when client B disconnects.
6761    client_b.disconnect(&cx_b.to_async());
6762    deterministic.advance_clock(RECONNECT_TIMEOUT);
6763    assert_eq!(
6764        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
6765        None
6766    );
6767}
6768
6769#[gpui::test(iterations = 10)]
6770async fn test_join_call_after_screen_was_shared(
6771    deterministic: Arc<Deterministic>,
6772    cx_a: &mut TestAppContext,
6773    cx_b: &mut TestAppContext,
6774) {
6775    deterministic.forbid_parking();
6776    let mut server = TestServer::start(&deterministic).await;
6777
6778    let client_a = server.create_client(cx_a, "user_a").await;
6779    let client_b = server.create_client(cx_b, "user_b").await;
6780    server
6781        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6782        .await;
6783
6784    let active_call_a = cx_a.read(ActiveCall::global);
6785    let active_call_b = cx_b.read(ActiveCall::global);
6786
6787    // Call users B and C from client A.
6788    active_call_a
6789        .update(cx_a, |call, cx| {
6790            call.invite(client_b.user_id().unwrap(), None, cx)
6791        })
6792        .await
6793        .unwrap();
6794    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
6795    deterministic.run_until_parked();
6796    assert_eq!(
6797        room_participants(&room_a, cx_a),
6798        RoomParticipants {
6799            remote: Default::default(),
6800            pending: vec!["user_b".to_string()]
6801        }
6802    );
6803
6804    // User B receives the call.
6805    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
6806    let call_b = incoming_call_b.next().await.unwrap().unwrap();
6807    assert_eq!(call_b.calling_user.github_login, "user_a");
6808
6809    // User A shares their screen
6810    let display = MacOSDisplay::new();
6811    active_call_a
6812        .update(cx_a, |call, cx| {
6813            call.room().unwrap().update(cx, |room, cx| {
6814                room.set_display_sources(vec![display.clone()]);
6815                room.share_screen(cx)
6816            })
6817        })
6818        .await
6819        .unwrap();
6820
6821    client_b.user_store.update(cx_b, |user_store, _| {
6822        user_store.clear_cache();
6823    });
6824
6825    // User B joins the room
6826    active_call_b
6827        .update(cx_b, |call, cx| call.accept_incoming(cx))
6828        .await
6829        .unwrap();
6830    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
6831    assert!(incoming_call_b.next().await.unwrap().is_none());
6832
6833    deterministic.run_until_parked();
6834    assert_eq!(
6835        room_participants(&room_a, cx_a),
6836        RoomParticipants {
6837            remote: vec!["user_b".to_string()],
6838            pending: vec![],
6839        }
6840    );
6841    assert_eq!(
6842        room_participants(&room_b, cx_b),
6843        RoomParticipants {
6844            remote: vec!["user_a".to_string()],
6845            pending: vec![],
6846        }
6847    );
6848
6849    // Ensure User B sees User A's screenshare.
6850    room_b.read_with(cx_b, |room, _| {
6851        assert_eq!(
6852            room.remote_participants()
6853                .get(&client_a.user_id().unwrap())
6854                .unwrap()
6855                .tracks
6856                .len(),
6857            1
6858        );
6859    });
6860}
6861
6862#[gpui::test]
6863async fn test_following_tab_order(
6864    deterministic: Arc<Deterministic>,
6865    cx_a: &mut TestAppContext,
6866    cx_b: &mut TestAppContext,
6867) {
6868    let mut server = TestServer::start(&deterministic).await;
6869    let client_a = server.create_client(cx_a, "user_a").await;
6870    let client_b = server.create_client(cx_b, "user_b").await;
6871    server
6872        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6873        .await;
6874    let active_call_a = cx_a.read(ActiveCall::global);
6875    let active_call_b = cx_b.read(ActiveCall::global);
6876
6877    cx_a.update(editor::init);
6878    cx_b.update(editor::init);
6879
6880    client_a
6881        .fs
6882        .insert_tree(
6883            "/a",
6884            json!({
6885                "1.txt": "one",
6886                "2.txt": "two",
6887                "3.txt": "three",
6888            }),
6889        )
6890        .await;
6891    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
6892    active_call_a
6893        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
6894        .await
6895        .unwrap();
6896
6897    let project_id = active_call_a
6898        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6899        .await
6900        .unwrap();
6901    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6902    active_call_b
6903        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6904        .await
6905        .unwrap();
6906
6907    let workspace_a = client_a.build_workspace(&project_a, cx_a);
6908    let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
6909
6910    let workspace_b = client_b.build_workspace(&project_b, cx_b);
6911    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
6912
6913    let client_b_id = project_a.read_with(cx_a, |project, _| {
6914        project.collaborators().values().next().unwrap().peer_id
6915    });
6916
6917    //Open 1, 3 in that order on client A
6918    workspace_a
6919        .update(cx_a, |workspace, cx| {
6920            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6921        })
6922        .await
6923        .unwrap();
6924    workspace_a
6925        .update(cx_a, |workspace, cx| {
6926            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
6927        })
6928        .await
6929        .unwrap();
6930
6931    let pane_paths = |pane: &ViewHandle<workspace::Pane>, cx: &mut TestAppContext| {
6932        pane.update(cx, |pane, cx| {
6933            pane.items()
6934                .map(|item| {
6935                    item.project_path(cx)
6936                        .unwrap()
6937                        .path
6938                        .to_str()
6939                        .unwrap()
6940                        .to_owned()
6941                })
6942                .collect::<Vec<_>>()
6943        })
6944    };
6945
6946    //Verify that the tabs opened in the order we expect
6947    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt"]);
6948
6949    //Follow client B as client A
6950    workspace_a
6951        .update(cx_a, |workspace, cx| {
6952            workspace.toggle_follow(client_b_id, cx).unwrap()
6953        })
6954        .await
6955        .unwrap();
6956
6957    //Open just 2 on client B
6958    workspace_b
6959        .update(cx_b, |workspace, cx| {
6960            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
6961        })
6962        .await
6963        .unwrap();
6964    deterministic.run_until_parked();
6965
6966    // Verify that newly opened followed file is at the end
6967    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt", "2.txt"]);
6968
6969    //Open just 1 on client B
6970    workspace_b
6971        .update(cx_b, |workspace, cx| {
6972            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6973        })
6974        .await
6975        .unwrap();
6976    assert_eq!(&pane_paths(&pane_b, cx_b), &["2.txt", "1.txt"]);
6977    deterministic.run_until_parked();
6978
6979    // Verify that following into 1 did not reorder
6980    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt", "2.txt"]);
6981}
6982
6983#[gpui::test(iterations = 10)]
6984async fn test_peers_following_each_other(
6985    deterministic: Arc<Deterministic>,
6986    cx_a: &mut TestAppContext,
6987    cx_b: &mut TestAppContext,
6988) {
6989    deterministic.forbid_parking();
6990    let mut server = TestServer::start(&deterministic).await;
6991    let client_a = server.create_client(cx_a, "user_a").await;
6992    let client_b = server.create_client(cx_b, "user_b").await;
6993    server
6994        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6995        .await;
6996    let active_call_a = cx_a.read(ActiveCall::global);
6997    let active_call_b = cx_b.read(ActiveCall::global);
6998
6999    cx_a.update(editor::init);
7000    cx_b.update(editor::init);
7001
7002    // Client A shares a project.
7003    client_a
7004        .fs
7005        .insert_tree(
7006            "/a",
7007            json!({
7008                "1.txt": "one",
7009                "2.txt": "two",
7010                "3.txt": "three",
7011                "4.txt": "four",
7012            }),
7013        )
7014        .await;
7015    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7016    active_call_a
7017        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
7018        .await
7019        .unwrap();
7020    let project_id = active_call_a
7021        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7022        .await
7023        .unwrap();
7024
7025    // Client B joins the project.
7026    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7027    active_call_b
7028        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
7029        .await
7030        .unwrap();
7031
7032    // Client A opens some editors.
7033    let workspace_a = client_a.build_workspace(&project_a, cx_a);
7034    let pane_a1 = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
7035    let _editor_a1 = workspace_a
7036        .update(cx_a, |workspace, cx| {
7037            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
7038        })
7039        .await
7040        .unwrap()
7041        .downcast::<Editor>()
7042        .unwrap();
7043
7044    // Client B opens an editor.
7045    let workspace_b = client_b.build_workspace(&project_b, cx_b);
7046    let pane_b1 = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
7047    let _editor_b1 = workspace_b
7048        .update(cx_b, |workspace, cx| {
7049            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
7050        })
7051        .await
7052        .unwrap()
7053        .downcast::<Editor>()
7054        .unwrap();
7055
7056    // Clients A and B follow each other in split panes
7057    workspace_a.update(cx_a, |workspace, cx| {
7058        workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
7059    });
7060    workspace_a
7061        .update(cx_a, |workspace, cx| {
7062            assert_ne!(*workspace.active_pane(), pane_a1);
7063            let leader_id = *project_a.read(cx).collaborators().keys().next().unwrap();
7064            workspace.toggle_follow(leader_id, cx).unwrap()
7065        })
7066        .await
7067        .unwrap();
7068    workspace_b.update(cx_b, |workspace, cx| {
7069        workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
7070    });
7071    workspace_b
7072        .update(cx_b, |workspace, cx| {
7073            assert_ne!(*workspace.active_pane(), pane_b1);
7074            let leader_id = *project_b.read(cx).collaborators().keys().next().unwrap();
7075            workspace.toggle_follow(leader_id, cx).unwrap()
7076        })
7077        .await
7078        .unwrap();
7079
7080    workspace_a.update(cx_a, |workspace, cx| {
7081        workspace.activate_next_pane(cx);
7082    });
7083    // Wait for focus effects to be fully flushed
7084    workspace_a.update(cx_a, |workspace, _| {
7085        assert_eq!(*workspace.active_pane(), pane_a1);
7086    });
7087
7088    workspace_a
7089        .update(cx_a, |workspace, cx| {
7090            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
7091        })
7092        .await
7093        .unwrap();
7094    workspace_b.update(cx_b, |workspace, cx| {
7095        workspace.activate_next_pane(cx);
7096    });
7097
7098    workspace_b
7099        .update(cx_b, |workspace, cx| {
7100            assert_eq!(*workspace.active_pane(), pane_b1);
7101            workspace.open_path((worktree_id, "4.txt"), None, true, cx)
7102        })
7103        .await
7104        .unwrap();
7105    cx_a.foreground().run_until_parked();
7106
7107    // Ensure leader updates don't change the active pane of followers
7108    workspace_a.read_with(cx_a, |workspace, _| {
7109        assert_eq!(*workspace.active_pane(), pane_a1);
7110    });
7111    workspace_b.read_with(cx_b, |workspace, _| {
7112        assert_eq!(*workspace.active_pane(), pane_b1);
7113    });
7114
7115    // Ensure peers following each other doesn't cause an infinite loop.
7116    assert_eq!(
7117        workspace_a.read_with(cx_a, |workspace, cx| workspace
7118            .active_item(cx)
7119            .unwrap()
7120            .project_path(cx)),
7121        Some((worktree_id, "3.txt").into())
7122    );
7123    workspace_a.update(cx_a, |workspace, cx| {
7124        assert_eq!(
7125            workspace.active_item(cx).unwrap().project_path(cx),
7126            Some((worktree_id, "3.txt").into())
7127        );
7128        workspace.activate_next_pane(cx);
7129    });
7130
7131    workspace_a.update(cx_a, |workspace, cx| {
7132        assert_eq!(
7133            workspace.active_item(cx).unwrap().project_path(cx),
7134            Some((worktree_id, "4.txt").into())
7135        );
7136    });
7137
7138    workspace_b.update(cx_b, |workspace, cx| {
7139        assert_eq!(
7140            workspace.active_item(cx).unwrap().project_path(cx),
7141            Some((worktree_id, "4.txt").into())
7142        );
7143        workspace.activate_next_pane(cx);
7144    });
7145
7146    workspace_b.update(cx_b, |workspace, cx| {
7147        assert_eq!(
7148            workspace.active_item(cx).unwrap().project_path(cx),
7149            Some((worktree_id, "3.txt").into())
7150        );
7151    });
7152}
7153
7154#[gpui::test(iterations = 10)]
7155async fn test_auto_unfollowing(
7156    deterministic: Arc<Deterministic>,
7157    cx_a: &mut TestAppContext,
7158    cx_b: &mut TestAppContext,
7159) {
7160    deterministic.forbid_parking();
7161
7162    // 2 clients connect to a server.
7163    let mut server = TestServer::start(&deterministic).await;
7164    let client_a = server.create_client(cx_a, "user_a").await;
7165    let client_b = server.create_client(cx_b, "user_b").await;
7166    server
7167        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7168        .await;
7169    let active_call_a = cx_a.read(ActiveCall::global);
7170    let active_call_b = cx_b.read(ActiveCall::global);
7171
7172    cx_a.update(editor::init);
7173    cx_b.update(editor::init);
7174
7175    // Client A shares a project.
7176    client_a
7177        .fs
7178        .insert_tree(
7179            "/a",
7180            json!({
7181                "1.txt": "one",
7182                "2.txt": "two",
7183                "3.txt": "three",
7184            }),
7185        )
7186        .await;
7187    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7188    active_call_a
7189        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
7190        .await
7191        .unwrap();
7192
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    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7198    active_call_b
7199        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
7200        .await
7201        .unwrap();
7202
7203    // Client A opens some editors.
7204    let workspace_a = client_a.build_workspace(&project_a, cx_a);
7205    let _editor_a1 = workspace_a
7206        .update(cx_a, |workspace, cx| {
7207            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
7208        })
7209        .await
7210        .unwrap()
7211        .downcast::<Editor>()
7212        .unwrap();
7213
7214    // Client B starts following client A.
7215    let workspace_b = client_b.build_workspace(&project_b, cx_b);
7216    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
7217    let leader_id = project_b.read_with(cx_b, |project, _| {
7218        project.collaborators().values().next().unwrap().peer_id
7219    });
7220    workspace_b
7221        .update(cx_b, |workspace, cx| {
7222            workspace.toggle_follow(leader_id, cx).unwrap()
7223        })
7224        .await
7225        .unwrap();
7226    assert_eq!(
7227        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7228        Some(leader_id)
7229    );
7230    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
7231        workspace
7232            .active_item(cx)
7233            .unwrap()
7234            .downcast::<Editor>()
7235            .unwrap()
7236    });
7237
7238    // When client B moves, it automatically stops following client A.
7239    editor_b2.update(cx_b, |editor, cx| editor.move_right(&editor::MoveRight, cx));
7240    assert_eq!(
7241        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7242        None
7243    );
7244
7245    workspace_b
7246        .update(cx_b, |workspace, cx| {
7247            workspace.toggle_follow(leader_id, cx).unwrap()
7248        })
7249        .await
7250        .unwrap();
7251    assert_eq!(
7252        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7253        Some(leader_id)
7254    );
7255
7256    // When client B edits, it automatically stops following client A.
7257    editor_b2.update(cx_b, |editor, cx| editor.insert("X", cx));
7258    assert_eq!(
7259        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7260        None
7261    );
7262
7263    workspace_b
7264        .update(cx_b, |workspace, cx| {
7265            workspace.toggle_follow(leader_id, cx).unwrap()
7266        })
7267        .await
7268        .unwrap();
7269    assert_eq!(
7270        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7271        Some(leader_id)
7272    );
7273
7274    // When client B scrolls, it automatically stops following client A.
7275    editor_b2.update(cx_b, |editor, cx| {
7276        editor.set_scroll_position(vec2f(0., 3.), cx)
7277    });
7278    assert_eq!(
7279        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7280        None
7281    );
7282
7283    workspace_b
7284        .update(cx_b, |workspace, cx| {
7285            workspace.toggle_follow(leader_id, cx).unwrap()
7286        })
7287        .await
7288        .unwrap();
7289    assert_eq!(
7290        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7291        Some(leader_id)
7292    );
7293
7294    // When client B activates a different pane, it continues following client A in the original pane.
7295    workspace_b.update(cx_b, |workspace, cx| {
7296        workspace.split_pane(pane_b.clone(), SplitDirection::Right, cx)
7297    });
7298    assert_eq!(
7299        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7300        Some(leader_id)
7301    );
7302
7303    workspace_b.update(cx_b, |workspace, cx| workspace.activate_next_pane(cx));
7304    assert_eq!(
7305        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7306        Some(leader_id)
7307    );
7308
7309    // When client B activates a different item in the original pane, it automatically stops following client A.
7310    workspace_b
7311        .update(cx_b, |workspace, cx| {
7312            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
7313        })
7314        .await
7315        .unwrap();
7316    assert_eq!(
7317        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7318        None
7319    );
7320}
7321
7322#[gpui::test(iterations = 10)]
7323async fn test_peers_simultaneously_following_each_other(
7324    deterministic: Arc<Deterministic>,
7325    cx_a: &mut TestAppContext,
7326    cx_b: &mut TestAppContext,
7327) {
7328    deterministic.forbid_parking();
7329
7330    let mut server = TestServer::start(&deterministic).await;
7331    let client_a = server.create_client(cx_a, "user_a").await;
7332    let client_b = server.create_client(cx_b, "user_b").await;
7333    server
7334        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7335        .await;
7336    let active_call_a = cx_a.read(ActiveCall::global);
7337
7338    cx_a.update(editor::init);
7339    cx_b.update(editor::init);
7340
7341    client_a.fs.insert_tree("/a", json!({})).await;
7342    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
7343    let workspace_a = client_a.build_workspace(&project_a, cx_a);
7344    let project_id = active_call_a
7345        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7346        .await
7347        .unwrap();
7348
7349    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7350    let workspace_b = client_b.build_workspace(&project_b, cx_b);
7351
7352    deterministic.run_until_parked();
7353    let client_a_id = project_b.read_with(cx_b, |project, _| {
7354        project.collaborators().values().next().unwrap().peer_id
7355    });
7356    let client_b_id = project_a.read_with(cx_a, |project, _| {
7357        project.collaborators().values().next().unwrap().peer_id
7358    });
7359
7360    let a_follow_b = workspace_a.update(cx_a, |workspace, cx| {
7361        workspace.toggle_follow(client_b_id, cx).unwrap()
7362    });
7363    let b_follow_a = workspace_b.update(cx_b, |workspace, cx| {
7364        workspace.toggle_follow(client_a_id, cx).unwrap()
7365    });
7366
7367    futures::try_join!(a_follow_b, b_follow_a).unwrap();
7368    workspace_a.read_with(cx_a, |workspace, _| {
7369        assert_eq!(
7370            workspace.leader_for_pane(workspace.active_pane()),
7371            Some(client_b_id)
7372        );
7373    });
7374    workspace_b.read_with(cx_b, |workspace, _| {
7375        assert_eq!(
7376            workspace.leader_for_pane(workspace.active_pane()),
7377            Some(client_a_id)
7378        );
7379    });
7380}
7381
7382#[gpui::test(iterations = 10)]
7383async fn test_on_input_format_from_host_to_guest(
7384    deterministic: Arc<Deterministic>,
7385    cx_a: &mut TestAppContext,
7386    cx_b: &mut TestAppContext,
7387) {
7388    deterministic.forbid_parking();
7389    let mut server = TestServer::start(&deterministic).await;
7390    let client_a = server.create_client(cx_a, "user_a").await;
7391    let client_b = server.create_client(cx_b, "user_b").await;
7392    server
7393        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7394        .await;
7395    let active_call_a = cx_a.read(ActiveCall::global);
7396
7397    // Set up a fake language server.
7398    let mut language = Language::new(
7399        LanguageConfig {
7400            name: "Rust".into(),
7401            path_suffixes: vec!["rs".to_string()],
7402            ..Default::default()
7403        },
7404        Some(tree_sitter_rust::language()),
7405    );
7406    let mut fake_language_servers = language
7407        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7408            capabilities: lsp::ServerCapabilities {
7409                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
7410                    first_trigger_character: ":".to_string(),
7411                    more_trigger_character: Some(vec![">".to_string()]),
7412                }),
7413                ..Default::default()
7414            },
7415            ..Default::default()
7416        }))
7417        .await;
7418    client_a.language_registry.add(Arc::new(language));
7419
7420    client_a
7421        .fs
7422        .insert_tree(
7423            "/a",
7424            json!({
7425                "main.rs": "fn main() { a }",
7426                "other.rs": "// Test file",
7427            }),
7428        )
7429        .await;
7430    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7431    let project_id = active_call_a
7432        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7433        .await
7434        .unwrap();
7435    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7436
7437    // Open a file in an editor as the host.
7438    let buffer_a = project_a
7439        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
7440        .await
7441        .unwrap();
7442    let (window_a, _) = cx_a.add_window(|_| EmptyView);
7443    let editor_a = cx_a.add_view(window_a, |cx| {
7444        Editor::for_buffer(buffer_a, Some(project_a.clone()), cx)
7445    });
7446
7447    let fake_language_server = fake_language_servers.next().await.unwrap();
7448    cx_b.foreground().run_until_parked();
7449
7450    // Receive an OnTypeFormatting request as the host's language server.
7451    // Return some formattings from the host's language server.
7452    fake_language_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(
7453        |params, _| async move {
7454            assert_eq!(
7455                params.text_document_position.text_document.uri,
7456                lsp::Url::from_file_path("/a/main.rs").unwrap(),
7457            );
7458            assert_eq!(
7459                params.text_document_position.position,
7460                lsp::Position::new(0, 14),
7461            );
7462
7463            Ok(Some(vec![lsp::TextEdit {
7464                new_text: "~<".to_string(),
7465                range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
7466            }]))
7467        },
7468    );
7469
7470    // Open the buffer on the guest and see that the formattings worked
7471    let buffer_b = project_b
7472        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
7473        .await
7474        .unwrap();
7475
7476    // Type a on type formatting trigger character as the guest.
7477    editor_a.update(cx_a, |editor, cx| {
7478        cx.focus(&editor_a);
7479        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
7480        editor.handle_input(">", cx);
7481    });
7482
7483    cx_b.foreground().run_until_parked();
7484
7485    buffer_b.read_with(cx_b, |buffer, _| {
7486        assert_eq!(buffer.text(), "fn main() { a>~< }")
7487    });
7488
7489    // Undo should remove LSP edits first
7490    editor_a.update(cx_a, |editor, cx| {
7491        assert_eq!(editor.text(cx), "fn main() { a>~< }");
7492        editor.undo(&Undo, cx);
7493        assert_eq!(editor.text(cx), "fn main() { a> }");
7494    });
7495    cx_b.foreground().run_until_parked();
7496    buffer_b.read_with(cx_b, |buffer, _| {
7497        assert_eq!(buffer.text(), "fn main() { a> }")
7498    });
7499
7500    editor_a.update(cx_a, |editor, cx| {
7501        assert_eq!(editor.text(cx), "fn main() { a> }");
7502        editor.undo(&Undo, cx);
7503        assert_eq!(editor.text(cx), "fn main() { a }");
7504    });
7505    cx_b.foreground().run_until_parked();
7506    buffer_b.read_with(cx_b, |buffer, _| {
7507        assert_eq!(buffer.text(), "fn main() { a }")
7508    });
7509}
7510
7511#[gpui::test(iterations = 10)]
7512async fn test_on_input_format_from_guest_to_host(
7513    deterministic: Arc<Deterministic>,
7514    cx_a: &mut TestAppContext,
7515    cx_b: &mut TestAppContext,
7516) {
7517    deterministic.forbid_parking();
7518    let mut server = TestServer::start(&deterministic).await;
7519    let client_a = server.create_client(cx_a, "user_a").await;
7520    let client_b = server.create_client(cx_b, "user_b").await;
7521    server
7522        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7523        .await;
7524    let active_call_a = cx_a.read(ActiveCall::global);
7525
7526    // Set up a fake language server.
7527    let mut language = Language::new(
7528        LanguageConfig {
7529            name: "Rust".into(),
7530            path_suffixes: vec!["rs".to_string()],
7531            ..Default::default()
7532        },
7533        Some(tree_sitter_rust::language()),
7534    );
7535    let mut fake_language_servers = language
7536        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7537            capabilities: lsp::ServerCapabilities {
7538                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
7539                    first_trigger_character: ":".to_string(),
7540                    more_trigger_character: Some(vec![">".to_string()]),
7541                }),
7542                ..Default::default()
7543            },
7544            ..Default::default()
7545        }))
7546        .await;
7547    client_a.language_registry.add(Arc::new(language));
7548
7549    client_a
7550        .fs
7551        .insert_tree(
7552            "/a",
7553            json!({
7554                "main.rs": "fn main() { a }",
7555                "other.rs": "// Test file",
7556            }),
7557        )
7558        .await;
7559    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7560    let project_id = active_call_a
7561        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7562        .await
7563        .unwrap();
7564    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7565
7566    // Open a file in an editor as the guest.
7567    let buffer_b = project_b
7568        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
7569        .await
7570        .unwrap();
7571    let (window_b, _) = cx_b.add_window(|_| EmptyView);
7572    let editor_b = cx_b.add_view(window_b, |cx| {
7573        Editor::for_buffer(buffer_b, Some(project_b.clone()), cx)
7574    });
7575
7576    let fake_language_server = fake_language_servers.next().await.unwrap();
7577    cx_a.foreground().run_until_parked();
7578    // Type a on type formatting trigger character as the guest.
7579    editor_b.update(cx_b, |editor, cx| {
7580        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
7581        editor.handle_input(":", cx);
7582        cx.focus(&editor_b);
7583    });
7584
7585    // Receive an OnTypeFormatting request as the host's language server.
7586    // Return some formattings from the host's language server.
7587    cx_a.foreground().start_waiting();
7588    fake_language_server
7589        .handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
7590            assert_eq!(
7591                params.text_document_position.text_document.uri,
7592                lsp::Url::from_file_path("/a/main.rs").unwrap(),
7593            );
7594            assert_eq!(
7595                params.text_document_position.position,
7596                lsp::Position::new(0, 14),
7597            );
7598
7599            Ok(Some(vec![lsp::TextEdit {
7600                new_text: "~:".to_string(),
7601                range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
7602            }]))
7603        })
7604        .next()
7605        .await
7606        .unwrap();
7607    cx_a.foreground().finish_waiting();
7608
7609    // Open the buffer on the host and see that the formattings worked
7610    let buffer_a = project_a
7611        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
7612        .await
7613        .unwrap();
7614    cx_a.foreground().run_until_parked();
7615    buffer_a.read_with(cx_a, |buffer, _| {
7616        assert_eq!(buffer.text(), "fn main() { a:~: }")
7617    });
7618
7619    // Undo should remove LSP edits first
7620    editor_b.update(cx_b, |editor, cx| {
7621        assert_eq!(editor.text(cx), "fn main() { a:~: }");
7622        editor.undo(&Undo, cx);
7623        assert_eq!(editor.text(cx), "fn main() { a: }");
7624    });
7625    cx_a.foreground().run_until_parked();
7626    buffer_a.read_with(cx_a, |buffer, _| {
7627        assert_eq!(buffer.text(), "fn main() { a: }")
7628    });
7629
7630    editor_b.update(cx_b, |editor, cx| {
7631        assert_eq!(editor.text(cx), "fn main() { a: }");
7632        editor.undo(&Undo, cx);
7633        assert_eq!(editor.text(cx), "fn main() { a }");
7634    });
7635    cx_a.foreground().run_until_parked();
7636    buffer_a.read_with(cx_a, |buffer, _| {
7637        assert_eq!(buffer.text(), "fn main() { a }")
7638    });
7639}
7640
7641#[derive(Debug, Eq, PartialEq)]
7642struct RoomParticipants {
7643    remote: Vec<String>,
7644    pending: Vec<String>,
7645}
7646
7647fn room_participants(room: &ModelHandle<Room>, cx: &mut TestAppContext) -> RoomParticipants {
7648    room.read_with(cx, |room, _| {
7649        let mut remote = room
7650            .remote_participants()
7651            .iter()
7652            .map(|(_, participant)| participant.user.github_login.clone())
7653            .collect::<Vec<_>>();
7654        let mut pending = room
7655            .pending_participants()
7656            .iter()
7657            .map(|user| user.github_login.clone())
7658            .collect::<Vec<_>>();
7659        remote.sort();
7660        pending.sort();
7661        RoomParticipants { remote, pending }
7662    })
7663}