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