integration_tests.rs

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