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