integration_tests.rs

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