integration_tests.rs

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