integration_tests.rs

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