integration_tests.rs

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