integration_tests.rs

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