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