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