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