integration_tests.rs

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