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