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