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