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