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