integration_tests.rs

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