integration_tests.rs

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