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