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