integration_tests.rs

   1use crate::{
   2    db::{tests::TestDb, UserId},
   3    rpc::{Executor, Server, Store},
   4    AppState,
   5};
   6use ::rpc::Peer;
   7use anyhow::anyhow;
   8use client::{
   9    self, proto, test::FakeHttpClient, Channel, ChannelDetails, ChannelList, Client, Connection,
  10    Credentials, EstablishConnectionError, UserStore, RECEIVE_TIMEOUT,
  11};
  12use collections::{BTreeMap, HashMap, HashSet};
  13use editor::{
  14    self, ConfirmCodeAction, ConfirmCompletion, ConfirmRename, Editor, Input, Redo, Rename,
  15    ToOffset, ToggleCodeActions, Undo,
  16};
  17use futures::{channel::mpsc, Future, StreamExt as _};
  18use gpui::{
  19    executor::{self, Deterministic},
  20    geometry::vector::vec2f,
  21    ModelHandle, Task, TestAppContext, ViewHandle,
  22};
  23use language::{
  24    range_to_lsp, tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language,
  25    LanguageConfig, LanguageRegistry, OffsetRangeExt, Point, Rope,
  26};
  27use lsp::{self, FakeLanguageServer};
  28use parking_lot::Mutex;
  29use project::{
  30    fs::{FakeFs, Fs as _},
  31    search::SearchQuery,
  32    worktree::WorktreeHandle,
  33    DiagnosticSummary, Project, ProjectPath, ProjectStore, WorktreeId,
  34};
  35use rand::prelude::*;
  36use rpc::PeerId;
  37use serde_json::json;
  38use settings::Settings;
  39use sqlx::types::time::OffsetDateTime;
  40use std::{
  41    cell::RefCell,
  42    env,
  43    ops::Deref,
  44    path::{Path, PathBuf},
  45    rc::Rc,
  46    sync::{
  47        atomic::{AtomicBool, Ordering::SeqCst},
  48        Arc,
  49    },
  50    time::Duration,
  51};
  52use theme::ThemeRegistry;
  53use tokio::sync::RwLockReadGuard;
  54use workspace::{Item, SplitDirection, ToggleFollow, Workspace};
  55
  56#[ctor::ctor]
  57fn init_logger() {
  58    if std::env::var("RUST_LOG").is_ok() {
  59        env_logger::init();
  60    }
  61}
  62
  63#[gpui::test(iterations = 10)]
  64async fn test_share_project(
  65    deterministic: Arc<Deterministic>,
  66    cx_a: &mut TestAppContext,
  67    cx_b: &mut TestAppContext,
  68    cx_b2: &mut TestAppContext,
  69) {
  70    cx_a.foreground().forbid_parking();
  71    let (window_b, _) = cx_b.add_window(|_| EmptyView);
  72    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
  73    let client_a = server.create_client(cx_a, "user_a").await;
  74    let client_b = server.create_client(cx_b, "user_b").await;
  75    server
  76        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
  77        .await;
  78
  79    client_a
  80        .fs
  81        .insert_tree(
  82            "/a",
  83            json!({
  84                ".gitignore": "ignored-dir",
  85                "a.txt": "a-contents",
  86                "b.txt": "b-contents",
  87                "ignored-dir": {
  88                    "c.txt": "",
  89                    "d.txt": "",
  90                }
  91            }),
  92        )
  93        .await;
  94
  95    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
  96    let project_id = project_a.read_with(cx_a, |project, _| project.remote_id().unwrap());
  97
  98    // Join that project as client B
  99    let client_b_peer_id = client_b.peer_id;
 100    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
 101    let replica_id_b = project_b.read_with(cx_b, |project, _| {
 102        assert_eq!(
 103            project
 104                .collaborators()
 105                .get(&client_a.peer_id)
 106                .unwrap()
 107                .user
 108                .github_login,
 109            "user_a"
 110        );
 111        project.replica_id()
 112    });
 113
 114    deterministic.run_until_parked();
 115    project_a.read_with(cx_a, |project, _| {
 116        let client_b_collaborator = project.collaborators().get(&client_b_peer_id).unwrap();
 117        assert_eq!(client_b_collaborator.replica_id, replica_id_b);
 118        assert_eq!(client_b_collaborator.user.github_login, "user_b");
 119    });
 120    project_b.read_with(cx_b, |project, cx| {
 121        let worktree = project.worktrees(cx).next().unwrap().read(cx);
 122        assert_eq!(
 123            worktree.paths().map(AsRef::as_ref).collect::<Vec<_>>(),
 124            [
 125                Path::new(".gitignore"),
 126                Path::new("a.txt"),
 127                Path::new("b.txt"),
 128                Path::new("ignored-dir"),
 129                Path::new("ignored-dir/c.txt"),
 130                Path::new("ignored-dir/d.txt"),
 131            ]
 132        );
 133    });
 134
 135    // Open the same file as client B and client A.
 136    let buffer_b = project_b
 137        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.txt"), cx))
 138        .await
 139        .unwrap();
 140    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "b-contents"));
 141    project_a.read_with(cx_a, |project, cx| {
 142        assert!(project.has_open_buffer((worktree_id, "b.txt"), cx))
 143    });
 144    let buffer_a = project_a
 145        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "b.txt"), cx))
 146        .await
 147        .unwrap();
 148
 149    let editor_b = cx_b.add_view(window_b, |cx| Editor::for_buffer(buffer_b, None, cx));
 150
 151    // TODO
 152    // // Create a selection set as client B and see that selection set as client A.
 153    // buffer_a
 154    //     .condition(&cx_a, |buffer, _| buffer.selection_sets().count() == 1)
 155    //     .await;
 156
 157    // Edit the buffer as client B and see that edit as client A.
 158    editor_b.update(cx_b, |editor, cx| {
 159        editor.handle_input(&Input("ok, ".into()), cx)
 160    });
 161    buffer_a
 162        .condition(&cx_a, |buffer, _| buffer.text() == "ok, b-contents")
 163        .await;
 164
 165    // TODO
 166    // // Remove the selection set as client B, see those selections disappear as client A.
 167    cx_b.update(move |_| drop(editor_b));
 168    // buffer_a
 169    //     .condition(&cx_a, |buffer, _| buffer.selection_sets().count() == 0)
 170    //     .await;
 171
 172    // Client B can join again on a different window because they are already a participant.
 173    let client_b2 = server.create_client(cx_b2, "user_b").await;
 174    let project_b2 = Project::remote(
 175        project_id,
 176        client_b2.client.clone(),
 177        client_b2.user_store.clone(),
 178        client_b2.project_store.clone(),
 179        client_b2.language_registry.clone(),
 180        FakeFs::new(cx_b2.background()),
 181        cx_b2.to_async(),
 182    )
 183    .await
 184    .unwrap();
 185    deterministic.run_until_parked();
 186    project_a.read_with(cx_a, |project, _| {
 187        assert_eq!(project.collaborators().len(), 2);
 188    });
 189    project_b.read_with(cx_b, |project, _| {
 190        assert_eq!(project.collaborators().len(), 2);
 191    });
 192    project_b2.read_with(cx_b2, |project, _| {
 193        assert_eq!(project.collaborators().len(), 2);
 194    });
 195
 196    // Dropping client B's first project removes only that from client A's collaborators.
 197    cx_b.update(move |_| drop(project_b));
 198    deterministic.run_until_parked();
 199    project_a.read_with(cx_a, |project, _| {
 200        assert_eq!(project.collaborators().len(), 1);
 201    });
 202    project_b2.read_with(cx_b2, |project, _| {
 203        assert_eq!(project.collaborators().len(), 1);
 204    });
 205}
 206
 207#[gpui::test(iterations = 10)]
 208async fn test_unshare_project(
 209    deterministic: Arc<Deterministic>,
 210    cx_a: &mut TestAppContext,
 211    cx_b: &mut TestAppContext,
 212) {
 213    cx_a.foreground().forbid_parking();
 214    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
 215    let client_a = server.create_client(cx_a, "user_a").await;
 216    let client_b = server.create_client(cx_b, "user_b").await;
 217    server
 218        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
 219        .await;
 220
 221    client_a
 222        .fs
 223        .insert_tree(
 224            "/a",
 225            json!({
 226                "a.txt": "a-contents",
 227                "b.txt": "b-contents",
 228            }),
 229        )
 230        .await;
 231
 232    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
 233    let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
 234    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
 235    assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
 236
 237    project_b
 238        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
 239        .await
 240        .unwrap();
 241
 242    // When client B leaves the project, it gets automatically unshared.
 243    cx_b.update(|_| drop(project_b));
 244    deterministic.run_until_parked();
 245    assert!(worktree_a.read_with(cx_a, |tree, _| !tree.as_local().unwrap().is_shared()));
 246
 247    // When client B joins again, the project gets re-shared.
 248    let project_b2 = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
 249    assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
 250    project_b2
 251        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
 252        .await
 253        .unwrap();
 254
 255    // When client A (the host) leaves, the project gets unshared and guests are notified.
 256    cx_a.update(|_| drop(project_a));
 257    deterministic.run_until_parked();
 258    project_b2.read_with(cx_b, |project, _| {
 259        assert!(project.is_read_only());
 260        assert!(project.collaborators().is_empty());
 261    });
 262}
 263
 264#[gpui::test(iterations = 10)]
 265async fn test_host_disconnect(
 266    deterministic: Arc<Deterministic>,
 267    cx_a: &mut TestAppContext,
 268    cx_b: &mut TestAppContext,
 269    cx_c: &mut TestAppContext,
 270) {
 271    cx_a.foreground().forbid_parking();
 272    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
 273    let client_a = server.create_client(cx_a, "user_a").await;
 274    let client_b = server.create_client(cx_b, "user_b").await;
 275    let client_c = server.create_client(cx_c, "user_c").await;
 276    server
 277        .make_contacts(vec![
 278            (&client_a, cx_a),
 279            (&client_b, cx_b),
 280            (&client_c, cx_c),
 281        ])
 282        .await;
 283
 284    client_a
 285        .fs
 286        .insert_tree(
 287            "/a",
 288            json!({
 289                "a.txt": "a-contents",
 290                "b.txt": "b-contents",
 291            }),
 292        )
 293        .await;
 294
 295    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
 296    let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
 297    let project_id = project_a.read_with(cx_a, |project, _| project.remote_id().unwrap());
 298
 299    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
 300    assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
 301
 302    project_b
 303        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
 304        .await
 305        .unwrap();
 306
 307    // Request to join that project as client C
 308    let project_c = cx_c.spawn(|cx| {
 309        Project::remote(
 310            project_id,
 311            client_c.client.clone(),
 312            client_c.user_store.clone(),
 313            client_c.project_store.clone(),
 314            client_c.language_registry.clone(),
 315            FakeFs::new(cx.background()),
 316            cx,
 317        )
 318    });
 319    deterministic.run_until_parked();
 320
 321    // Drop client A's connection. Collaborators should disappear and the project should not be shown as shared.
 322    server.disconnect_client(client_a.current_user_id(cx_a));
 323    cx_a.foreground().advance_clock(rpc::RECEIVE_TIMEOUT);
 324    project_a
 325        .condition(cx_a, |project, _| project.collaborators().is_empty())
 326        .await;
 327    project_a.read_with(cx_a, |project, _| assert!(!project.is_shared()));
 328    project_b
 329        .condition(cx_b, |project, _| project.is_read_only())
 330        .await;
 331    assert!(worktree_a.read_with(cx_a, |tree, _| !tree.as_local().unwrap().is_shared()));
 332    cx_b.update(|_| {
 333        drop(project_b);
 334    });
 335    assert!(matches!(
 336        project_c.await.unwrap_err(),
 337        project::JoinProjectError::HostWentOffline
 338    ));
 339
 340    // Ensure guests can still join.
 341    let project_b2 = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
 342    assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
 343    project_b2
 344        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
 345        .await
 346        .unwrap();
 347}
 348
 349#[gpui::test(iterations = 10)]
 350async fn test_decline_join_request(
 351    deterministic: Arc<Deterministic>,
 352    cx_a: &mut TestAppContext,
 353    cx_b: &mut TestAppContext,
 354) {
 355    cx_a.foreground().forbid_parking();
 356    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
 357    let client_a = server.create_client(cx_a, "user_a").await;
 358    let client_b = server.create_client(cx_b, "user_b").await;
 359    server
 360        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
 361        .await;
 362
 363    client_a.fs.insert_tree("/a", json!({})).await;
 364
 365    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
 366    let project_id = project_a.read_with(cx_a, |project, _| project.remote_id().unwrap());
 367
 368    // Request to join that project as client B
 369    let project_b = cx_b.spawn(|cx| {
 370        Project::remote(
 371            project_id,
 372            client_b.client.clone(),
 373            client_b.user_store.clone(),
 374            client_b.project_store.clone(),
 375            client_b.language_registry.clone(),
 376            FakeFs::new(cx.background()),
 377            cx,
 378        )
 379    });
 380    deterministic.run_until_parked();
 381    project_a.update(cx_a, |project, cx| {
 382        project.respond_to_join_request(client_b.user_id().unwrap(), false, cx)
 383    });
 384    assert!(matches!(
 385        project_b.await.unwrap_err(),
 386        project::JoinProjectError::HostDeclined
 387    ));
 388
 389    // Request to join the project again as client B
 390    let project_b = cx_b.spawn(|cx| {
 391        Project::remote(
 392            project_id,
 393            client_b.client.clone(),
 394            client_b.user_store.clone(),
 395            client_b.project_store.clone(),
 396            client_b.language_registry.clone(),
 397            FakeFs::new(cx.background()),
 398            cx,
 399        )
 400    });
 401
 402    // Close the project on the host
 403    deterministic.run_until_parked();
 404    cx_a.update(|_| drop(project_a));
 405    deterministic.run_until_parked();
 406    assert!(matches!(
 407        project_b.await.unwrap_err(),
 408        project::JoinProjectError::HostClosedProject
 409    ));
 410}
 411
 412#[gpui::test(iterations = 10)]
 413async fn test_cancel_join_request(
 414    deterministic: Arc<Deterministic>,
 415    cx_a: &mut TestAppContext,
 416    cx_b: &mut TestAppContext,
 417) {
 418    cx_a.foreground().forbid_parking();
 419    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
 420    let client_a = server.create_client(cx_a, "user_a").await;
 421    let client_b = server.create_client(cx_b, "user_b").await;
 422    server
 423        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
 424        .await;
 425
 426    client_a.fs.insert_tree("/a", json!({})).await;
 427    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
 428    let project_id = project_a.read_with(cx_a, |project, _| project.remote_id().unwrap());
 429
 430    let user_b = client_a
 431        .user_store
 432        .update(cx_a, |store, cx| {
 433            store.fetch_user(client_b.user_id().unwrap(), cx)
 434        })
 435        .await
 436        .unwrap();
 437
 438    let project_a_events = Rc::new(RefCell::new(Vec::new()));
 439    project_a.update(cx_a, {
 440        let project_a_events = project_a_events.clone();
 441        move |_, cx| {
 442            cx.subscribe(&cx.handle(), move |_, _, event, _| {
 443                project_a_events.borrow_mut().push(event.clone());
 444            })
 445            .detach();
 446        }
 447    });
 448
 449    // Request to join that project as client B
 450    let project_b = cx_b.spawn(|cx| {
 451        Project::remote(
 452            project_id,
 453            client_b.client.clone(),
 454            client_b.user_store.clone(),
 455            client_b.project_store.clone(),
 456            client_b.language_registry.clone().clone(),
 457            FakeFs::new(cx.background()),
 458            cx,
 459        )
 460    });
 461    deterministic.run_until_parked();
 462    assert_eq!(
 463        &*project_a_events.borrow(),
 464        &[project::Event::ContactRequestedJoin(user_b.clone())]
 465    );
 466    project_a_events.borrow_mut().clear();
 467
 468    // Cancel the join request by leaving the project
 469    client_b
 470        .client
 471        .send(proto::LeaveProject { project_id })
 472        .unwrap();
 473    drop(project_b);
 474
 475    deterministic.run_until_parked();
 476    assert_eq!(
 477        &*project_a_events.borrow(),
 478        &[project::Event::ContactCancelledJoinRequest(user_b.clone())]
 479    );
 480}
 481
 482#[gpui::test(iterations = 3)]
 483async fn test_private_projects(
 484    deterministic: Arc<Deterministic>,
 485    cx_a: &mut TestAppContext,
 486    cx_b: &mut TestAppContext,
 487) {
 488    cx_a.foreground().forbid_parking();
 489    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
 490    let client_a = server.create_client(cx_a, "user_a").await;
 491    let client_b = server.create_client(cx_b, "user_b").await;
 492    server
 493        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
 494        .await;
 495
 496    let user_a = UserId::from_proto(client_a.user_id().unwrap());
 497
 498    let fs = FakeFs::new(cx_a.background());
 499    fs.insert_tree("/a", json!({})).await;
 500
 501    // Create a private project
 502    let project_a = cx_a.update(|cx| {
 503        Project::local(
 504            false,
 505            client_a.client.clone(),
 506            client_a.user_store.clone(),
 507            client_a.project_store.clone(),
 508            client_a.language_registry.clone(),
 509            fs.clone(),
 510            cx,
 511        )
 512    });
 513
 514    // Private projects are not registered with the server.
 515    deterministic.run_until_parked();
 516    assert!(project_a.read_with(cx_a, |project, _| project.remote_id().is_none()));
 517    assert!(client_b
 518        .user_store
 519        .read_with(cx_b, |store, _| { store.contacts()[0].projects.is_empty() }));
 520
 521    // The project is registered when it is made public.
 522    project_a.update(cx_a, |project, cx| project.set_public(true, cx));
 523    deterministic.run_until_parked();
 524    assert!(project_a.read_with(cx_a, |project, _| project.remote_id().is_some()));
 525    assert!(!client_b
 526        .user_store
 527        .read_with(cx_b, |store, _| { store.contacts()[0].projects.is_empty() }));
 528
 529    // The project is registered again when it loses and regains connection.
 530    server.disconnect_client(user_a);
 531    server.forbid_connections();
 532    cx_a.foreground().advance_clock(rpc::RECEIVE_TIMEOUT);
 533    // deterministic.run_until_parked();
 534    assert!(project_a.read_with(cx_a, |project, _| project.remote_id().is_none()));
 535    assert!(client_b
 536        .user_store
 537        .read_with(cx_b, |store, _| { store.contacts()[0].projects.is_empty() }));
 538    server.allow_connections();
 539    cx_b.foreground().advance_clock(Duration::from_secs(10));
 540    assert!(project_a.read_with(cx_a, |project, _| project.remote_id().is_some()));
 541    assert!(!client_b
 542        .user_store
 543        .read_with(cx_b, |store, _| { store.contacts()[0].projects.is_empty() }));
 544}
 545
 546#[gpui::test(iterations = 10)]
 547async fn test_propagate_saves_and_fs_changes(
 548    cx_a: &mut TestAppContext,
 549    cx_b: &mut TestAppContext,
 550    cx_c: &mut TestAppContext,
 551) {
 552    cx_a.foreground().forbid_parking();
 553    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
 554    let client_a = server.create_client(cx_a, "user_a").await;
 555    let client_b = server.create_client(cx_b, "user_b").await;
 556    let client_c = server.create_client(cx_c, "user_c").await;
 557    server
 558        .make_contacts(vec![
 559            (&client_a, cx_a),
 560            (&client_b, cx_b),
 561            (&client_c, cx_c),
 562        ])
 563        .await;
 564
 565    client_a
 566        .fs
 567        .insert_tree(
 568            "/a",
 569            json!({
 570                "file1": "",
 571                "file2": ""
 572            }),
 573        )
 574        .await;
 575    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
 576    let worktree_a = project_a.read_with(cx_a, |p, cx| p.worktrees(cx).next().unwrap());
 577
 578    // Join that worktree as clients B and C.
 579    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
 580    let project_c = client_c.build_remote_project(&project_a, cx_a, cx_c).await;
 581    let worktree_b = project_b.read_with(cx_b, |p, cx| p.worktrees(cx).next().unwrap());
 582    let worktree_c = project_c.read_with(cx_c, |p, cx| p.worktrees(cx).next().unwrap());
 583
 584    // Open and edit a buffer as both guests B and C.
 585    let buffer_b = project_b
 586        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "file1"), cx))
 587        .await
 588        .unwrap();
 589    let buffer_c = project_c
 590        .update(cx_c, |p, cx| p.open_buffer((worktree_id, "file1"), cx))
 591        .await
 592        .unwrap();
 593    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "i-am-b, ")], cx));
 594    buffer_c.update(cx_c, |buf, cx| buf.edit([(0..0, "i-am-c, ")], cx));
 595
 596    // Open and edit that buffer as the host.
 597    let buffer_a = project_a
 598        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "file1"), cx))
 599        .await
 600        .unwrap();
 601
 602    buffer_a
 603        .condition(cx_a, |buf, _| buf.text() == "i-am-c, i-am-b, ")
 604        .await;
 605    buffer_a.update(cx_a, |buf, cx| {
 606        buf.edit([(buf.len()..buf.len(), "i-am-a")], cx)
 607    });
 608
 609    // Wait for edits to propagate
 610    buffer_a
 611        .condition(cx_a, |buf, _| buf.text() == "i-am-c, i-am-b, i-am-a")
 612        .await;
 613    buffer_b
 614        .condition(cx_b, |buf, _| buf.text() == "i-am-c, i-am-b, i-am-a")
 615        .await;
 616    buffer_c
 617        .condition(cx_c, |buf, _| buf.text() == "i-am-c, i-am-b, i-am-a")
 618        .await;
 619
 620    // Edit the buffer as the host and concurrently save as guest B.
 621    let save_b = buffer_b.update(cx_b, |buf, cx| buf.save(cx));
 622    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "hi-a, ")], cx));
 623    save_b.await.unwrap();
 624    assert_eq!(
 625        client_a.fs.load("/a/file1".as_ref()).await.unwrap(),
 626        "hi-a, i-am-c, i-am-b, i-am-a"
 627    );
 628    buffer_a.read_with(cx_a, |buf, _| assert!(!buf.is_dirty()));
 629    buffer_b.read_with(cx_b, |buf, _| assert!(!buf.is_dirty()));
 630    buffer_c.condition(cx_c, |buf, _| !buf.is_dirty()).await;
 631
 632    worktree_a.flush_fs_events(cx_a).await;
 633
 634    // Make changes on host's file system, see those changes on guest worktrees.
 635    client_a
 636        .fs
 637        .rename(
 638            "/a/file1".as_ref(),
 639            "/a/file1-renamed".as_ref(),
 640            Default::default(),
 641        )
 642        .await
 643        .unwrap();
 644
 645    client_a
 646        .fs
 647        .rename("/a/file2".as_ref(), "/a/file3".as_ref(), Default::default())
 648        .await
 649        .unwrap();
 650    client_a.fs.insert_file("/a/file4", "4".into()).await;
 651
 652    worktree_a
 653        .condition(&cx_a, |tree, _| {
 654            tree.paths()
 655                .map(|p| p.to_string_lossy())
 656                .collect::<Vec<_>>()
 657                == ["file1-renamed", "file3", "file4"]
 658        })
 659        .await;
 660    worktree_b
 661        .condition(&cx_b, |tree, _| {
 662            tree.paths()
 663                .map(|p| p.to_string_lossy())
 664                .collect::<Vec<_>>()
 665                == ["file1-renamed", "file3", "file4"]
 666        })
 667        .await;
 668    worktree_c
 669        .condition(&cx_c, |tree, _| {
 670            tree.paths()
 671                .map(|p| p.to_string_lossy())
 672                .collect::<Vec<_>>()
 673                == ["file1-renamed", "file3", "file4"]
 674        })
 675        .await;
 676
 677    // Ensure buffer files are updated as well.
 678    buffer_a
 679        .condition(&cx_a, |buf, _| {
 680            buf.file().unwrap().path().to_str() == Some("file1-renamed")
 681        })
 682        .await;
 683    buffer_b
 684        .condition(&cx_b, |buf, _| {
 685            buf.file().unwrap().path().to_str() == Some("file1-renamed")
 686        })
 687        .await;
 688    buffer_c
 689        .condition(&cx_c, |buf, _| {
 690            buf.file().unwrap().path().to_str() == Some("file1-renamed")
 691        })
 692        .await;
 693}
 694
 695#[gpui::test(iterations = 10)]
 696async fn test_fs_operations(
 697    executor: Arc<Deterministic>,
 698    cx_a: &mut TestAppContext,
 699    cx_b: &mut TestAppContext,
 700) {
 701    executor.forbid_parking();
 702    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
 703    let client_a = server.create_client(cx_a, "user_a").await;
 704    let client_b = server.create_client(cx_b, "user_b").await;
 705    server
 706        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
 707        .await;
 708
 709    client_a
 710        .fs
 711        .insert_tree(
 712            "/dir",
 713            json!({
 714                "a.txt": "a-contents",
 715                "b.txt": "b-contents",
 716            }),
 717        )
 718        .await;
 719    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
 720    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
 721
 722    let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
 723    let worktree_b = project_b.read_with(cx_b, |project, cx| project.worktrees(cx).next().unwrap());
 724
 725    let entry = project_b
 726        .update(cx_b, |project, cx| {
 727            project
 728                .create_entry((worktree_id, "c.txt"), false, cx)
 729                .unwrap()
 730        })
 731        .await
 732        .unwrap();
 733    worktree_a.read_with(cx_a, |worktree, _| {
 734        assert_eq!(
 735            worktree
 736                .paths()
 737                .map(|p| p.to_string_lossy())
 738                .collect::<Vec<_>>(),
 739            ["a.txt", "b.txt", "c.txt"]
 740        );
 741    });
 742    worktree_b.read_with(cx_b, |worktree, _| {
 743        assert_eq!(
 744            worktree
 745                .paths()
 746                .map(|p| p.to_string_lossy())
 747                .collect::<Vec<_>>(),
 748            ["a.txt", "b.txt", "c.txt"]
 749        );
 750    });
 751
 752    project_b
 753        .update(cx_b, |project, cx| {
 754            project.rename_entry(entry.id, Path::new("d.txt"), cx)
 755        })
 756        .unwrap()
 757        .await
 758        .unwrap();
 759    worktree_a.read_with(cx_a, |worktree, _| {
 760        assert_eq!(
 761            worktree
 762                .paths()
 763                .map(|p| p.to_string_lossy())
 764                .collect::<Vec<_>>(),
 765            ["a.txt", "b.txt", "d.txt"]
 766        );
 767    });
 768    worktree_b.read_with(cx_b, |worktree, _| {
 769        assert_eq!(
 770            worktree
 771                .paths()
 772                .map(|p| p.to_string_lossy())
 773                .collect::<Vec<_>>(),
 774            ["a.txt", "b.txt", "d.txt"]
 775        );
 776    });
 777
 778    let dir_entry = project_b
 779        .update(cx_b, |project, cx| {
 780            project
 781                .create_entry((worktree_id, "DIR"), true, cx)
 782                .unwrap()
 783        })
 784        .await
 785        .unwrap();
 786    worktree_a.read_with(cx_a, |worktree, _| {
 787        assert_eq!(
 788            worktree
 789                .paths()
 790                .map(|p| p.to_string_lossy())
 791                .collect::<Vec<_>>(),
 792            ["DIR", "a.txt", "b.txt", "d.txt"]
 793        );
 794    });
 795    worktree_b.read_with(cx_b, |worktree, _| {
 796        assert_eq!(
 797            worktree
 798                .paths()
 799                .map(|p| p.to_string_lossy())
 800                .collect::<Vec<_>>(),
 801            ["DIR", "a.txt", "b.txt", "d.txt"]
 802        );
 803    });
 804
 805    project_b
 806        .update(cx_b, |project, cx| {
 807            project
 808                .create_entry((worktree_id, "DIR/e.txt"), false, cx)
 809                .unwrap()
 810        })
 811        .await
 812        .unwrap();
 813    project_b
 814        .update(cx_b, |project, cx| {
 815            project
 816                .create_entry((worktree_id, "DIR/SUBDIR"), true, cx)
 817                .unwrap()
 818        })
 819        .await
 820        .unwrap();
 821    project_b
 822        .update(cx_b, |project, cx| {
 823            project
 824                .create_entry((worktree_id, "DIR/SUBDIR/f.txt"), false, cx)
 825                .unwrap()
 826        })
 827        .await
 828        .unwrap();
 829    worktree_a.read_with(cx_a, |worktree, _| {
 830        assert_eq!(
 831            worktree
 832                .paths()
 833                .map(|p| p.to_string_lossy())
 834                .collect::<Vec<_>>(),
 835            [
 836                "DIR",
 837                "DIR/SUBDIR",
 838                "DIR/SUBDIR/f.txt",
 839                "DIR/e.txt",
 840                "a.txt",
 841                "b.txt",
 842                "d.txt"
 843            ]
 844        );
 845    });
 846    worktree_b.read_with(cx_b, |worktree, _| {
 847        assert_eq!(
 848            worktree
 849                .paths()
 850                .map(|p| p.to_string_lossy())
 851                .collect::<Vec<_>>(),
 852            [
 853                "DIR",
 854                "DIR/SUBDIR",
 855                "DIR/SUBDIR/f.txt",
 856                "DIR/e.txt",
 857                "a.txt",
 858                "b.txt",
 859                "d.txt"
 860            ]
 861        );
 862    });
 863
 864    project_b
 865        .update(cx_b, |project, cx| {
 866            project
 867                .copy_entry(entry.id, Path::new("f.txt"), cx)
 868                .unwrap()
 869        })
 870        .await
 871        .unwrap();
 872    worktree_a.read_with(cx_a, |worktree, _| {
 873        assert_eq!(
 874            worktree
 875                .paths()
 876                .map(|p| p.to_string_lossy())
 877                .collect::<Vec<_>>(),
 878            [
 879                "DIR",
 880                "DIR/SUBDIR",
 881                "DIR/SUBDIR/f.txt",
 882                "DIR/e.txt",
 883                "a.txt",
 884                "b.txt",
 885                "d.txt",
 886                "f.txt"
 887            ]
 888        );
 889    });
 890    worktree_b.read_with(cx_b, |worktree, _| {
 891        assert_eq!(
 892            worktree
 893                .paths()
 894                .map(|p| p.to_string_lossy())
 895                .collect::<Vec<_>>(),
 896            [
 897                "DIR",
 898                "DIR/SUBDIR",
 899                "DIR/SUBDIR/f.txt",
 900                "DIR/e.txt",
 901                "a.txt",
 902                "b.txt",
 903                "d.txt",
 904                "f.txt"
 905            ]
 906        );
 907    });
 908
 909    project_b
 910        .update(cx_b, |project, cx| {
 911            project.delete_entry(dir_entry.id, cx).unwrap()
 912        })
 913        .await
 914        .unwrap();
 915    worktree_a.read_with(cx_a, |worktree, _| {
 916        assert_eq!(
 917            worktree
 918                .paths()
 919                .map(|p| p.to_string_lossy())
 920                .collect::<Vec<_>>(),
 921            ["a.txt", "b.txt", "d.txt", "f.txt"]
 922        );
 923    });
 924    worktree_b.read_with(cx_b, |worktree, _| {
 925        assert_eq!(
 926            worktree
 927                .paths()
 928                .map(|p| p.to_string_lossy())
 929                .collect::<Vec<_>>(),
 930            ["a.txt", "b.txt", "d.txt", "f.txt"]
 931        );
 932    });
 933
 934    project_b
 935        .update(cx_b, |project, cx| {
 936            project.delete_entry(entry.id, cx).unwrap()
 937        })
 938        .await
 939        .unwrap();
 940    worktree_a.read_with(cx_a, |worktree, _| {
 941        assert_eq!(
 942            worktree
 943                .paths()
 944                .map(|p| p.to_string_lossy())
 945                .collect::<Vec<_>>(),
 946            ["a.txt", "b.txt", "f.txt"]
 947        );
 948    });
 949    worktree_b.read_with(cx_b, |worktree, _| {
 950        assert_eq!(
 951            worktree
 952                .paths()
 953                .map(|p| p.to_string_lossy())
 954                .collect::<Vec<_>>(),
 955            ["a.txt", "b.txt", "f.txt"]
 956        );
 957    });
 958}
 959
 960#[gpui::test(iterations = 10)]
 961async fn test_buffer_conflict_after_save(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
 962    cx_a.foreground().forbid_parking();
 963    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
 964    let client_a = server.create_client(cx_a, "user_a").await;
 965    let client_b = server.create_client(cx_b, "user_b").await;
 966    server
 967        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
 968        .await;
 969
 970    client_a
 971        .fs
 972        .insert_tree(
 973            "/dir",
 974            json!({
 975                "a.txt": "a-contents",
 976            }),
 977        )
 978        .await;
 979    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
 980    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
 981
 982    // Open a buffer as client B
 983    let buffer_b = project_b
 984        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
 985        .await
 986        .unwrap();
 987
 988    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "world ")], cx));
 989    buffer_b.read_with(cx_b, |buf, _| {
 990        assert!(buf.is_dirty());
 991        assert!(!buf.has_conflict());
 992    });
 993
 994    buffer_b.update(cx_b, |buf, cx| buf.save(cx)).await.unwrap();
 995    buffer_b
 996        .condition(&cx_b, |buffer_b, _| !buffer_b.is_dirty())
 997        .await;
 998    buffer_b.read_with(cx_b, |buf, _| {
 999        assert!(!buf.has_conflict());
1000    });
1001
1002    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "hello ")], cx));
1003    buffer_b.read_with(cx_b, |buf, _| {
1004        assert!(buf.is_dirty());
1005        assert!(!buf.has_conflict());
1006    });
1007}
1008
1009#[gpui::test(iterations = 10)]
1010async fn test_buffer_reloading(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
1011    cx_a.foreground().forbid_parking();
1012    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
1013    let client_a = server.create_client(cx_a, "user_a").await;
1014    let client_b = server.create_client(cx_b, "user_b").await;
1015    server
1016        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
1017        .await;
1018
1019    client_a
1020        .fs
1021        .insert_tree(
1022            "/dir",
1023            json!({
1024                "a.txt": "a-contents",
1025            }),
1026        )
1027        .await;
1028    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
1029    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
1030
1031    // Open a buffer as client B
1032    let buffer_b = project_b
1033        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
1034        .await
1035        .unwrap();
1036    buffer_b.read_with(cx_b, |buf, _| {
1037        assert!(!buf.is_dirty());
1038        assert!(!buf.has_conflict());
1039    });
1040
1041    client_a
1042        .fs
1043        .save("/dir/a.txt".as_ref(), &"new contents".into())
1044        .await
1045        .unwrap();
1046    buffer_b
1047        .condition(&cx_b, |buf, _| {
1048            buf.text() == "new contents" && !buf.is_dirty()
1049        })
1050        .await;
1051    buffer_b.read_with(cx_b, |buf, _| assert!(!buf.has_conflict()));
1052}
1053
1054#[gpui::test(iterations = 10)]
1055async fn test_editing_while_guest_opens_buffer(
1056    cx_a: &mut TestAppContext,
1057    cx_b: &mut TestAppContext,
1058) {
1059    cx_a.foreground().forbid_parking();
1060    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
1061    let client_a = server.create_client(cx_a, "user_a").await;
1062    let client_b = server.create_client(cx_b, "user_b").await;
1063    server
1064        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
1065        .await;
1066
1067    client_a
1068        .fs
1069        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
1070        .await;
1071    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
1072    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
1073
1074    // Open a buffer as client A
1075    let buffer_a = project_a
1076        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
1077        .await
1078        .unwrap();
1079
1080    // Start opening the same buffer as client B
1081    let buffer_b = cx_b
1082        .background()
1083        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)));
1084
1085    // Edit the buffer as client A while client B is still opening it.
1086    cx_b.background().simulate_random_delay().await;
1087    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "X")], cx));
1088    cx_b.background().simulate_random_delay().await;
1089    buffer_a.update(cx_a, |buf, cx| buf.edit([(1..1, "Y")], cx));
1090
1091    let text = buffer_a.read_with(cx_a, |buf, _| buf.text());
1092    let buffer_b = buffer_b.await.unwrap();
1093    buffer_b.condition(&cx_b, |buf, _| buf.text() == text).await;
1094}
1095
1096#[gpui::test(iterations = 10)]
1097async fn test_leaving_worktree_while_opening_buffer(
1098    cx_a: &mut TestAppContext,
1099    cx_b: &mut TestAppContext,
1100) {
1101    cx_a.foreground().forbid_parking();
1102    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
1103    let client_a = server.create_client(cx_a, "user_a").await;
1104    let client_b = server.create_client(cx_b, "user_b").await;
1105    server
1106        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
1107        .await;
1108
1109    client_a
1110        .fs
1111        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
1112        .await;
1113    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
1114    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
1115
1116    // See that a guest has joined as client A.
1117    project_a
1118        .condition(&cx_a, |p, _| p.collaborators().len() == 1)
1119        .await;
1120
1121    // Begin opening a buffer as client B, but leave the project before the open completes.
1122    let buffer_b = cx_b
1123        .background()
1124        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)));
1125    cx_b.update(|_| drop(project_b));
1126    drop(buffer_b);
1127
1128    // See that the guest has left.
1129    project_a
1130        .condition(&cx_a, |p, _| p.collaborators().len() == 0)
1131        .await;
1132}
1133
1134#[gpui::test(iterations = 10)]
1135async fn test_leaving_project(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
1136    cx_a.foreground().forbid_parking();
1137    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
1138    let client_a = server.create_client(cx_a, "user_a").await;
1139    let client_b = server.create_client(cx_b, "user_b").await;
1140    server
1141        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
1142        .await;
1143
1144    client_a
1145        .fs
1146        .insert_tree(
1147            "/a",
1148            json!({
1149                "a.txt": "a-contents",
1150                "b.txt": "b-contents",
1151            }),
1152        )
1153        .await;
1154    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
1155    let _project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
1156
1157    // Client A sees that a guest has joined.
1158    project_a
1159        .condition(cx_a, |p, _| p.collaborators().len() == 1)
1160        .await;
1161
1162    // Drop client B's connection and ensure client A observes client B leaving the project.
1163    client_b.disconnect(&cx_b.to_async()).unwrap();
1164    project_a
1165        .condition(cx_a, |p, _| p.collaborators().len() == 0)
1166        .await;
1167
1168    // Rejoin the project as client B
1169    let _project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
1170
1171    // Client A sees that a guest has re-joined.
1172    project_a
1173        .condition(cx_a, |p, _| p.collaborators().len() == 1)
1174        .await;
1175
1176    // Simulate connection loss for client B and ensure client A observes client B leaving the project.
1177    client_b.wait_for_current_user(cx_b).await;
1178    server.disconnect_client(client_b.current_user_id(cx_b));
1179    cx_a.foreground().advance_clock(rpc::RECEIVE_TIMEOUT);
1180    project_a
1181        .condition(cx_a, |p, _| p.collaborators().len() == 0)
1182        .await;
1183}
1184
1185#[gpui::test(iterations = 10)]
1186async fn test_collaborating_with_diagnostics(
1187    deterministic: Arc<Deterministic>,
1188    cx_a: &mut TestAppContext,
1189    cx_b: &mut TestAppContext,
1190    cx_c: &mut TestAppContext,
1191) {
1192    deterministic.forbid_parking();
1193    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
1194    let client_a = server.create_client(cx_a, "user_a").await;
1195    let client_b = server.create_client(cx_b, "user_b").await;
1196    let client_c = server.create_client(cx_c, "user_c").await;
1197    server
1198        .make_contacts(vec![
1199            (&client_a, cx_a),
1200            (&client_b, cx_b),
1201            (&client_c, cx_c),
1202        ])
1203        .await;
1204
1205    // Set up a fake language server.
1206    let mut language = Language::new(
1207        LanguageConfig {
1208            name: "Rust".into(),
1209            path_suffixes: vec!["rs".to_string()],
1210            ..Default::default()
1211        },
1212        Some(tree_sitter_rust::language()),
1213    );
1214    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default());
1215    client_a.language_registry.add(Arc::new(language));
1216
1217    // Share a project as client A
1218    client_a
1219        .fs
1220        .insert_tree(
1221            "/a",
1222            json!({
1223                "a.rs": "let one = two",
1224                "other.rs": "",
1225            }),
1226        )
1227        .await;
1228    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
1229    let project_id = project_a.update(cx_a, |p, _| p.next_remote_id()).await;
1230
1231    // Cause the language server to start.
1232    let _buffer = cx_a
1233        .background()
1234        .spawn(project_a.update(cx_a, |project, cx| {
1235            project.open_buffer(
1236                ProjectPath {
1237                    worktree_id,
1238                    path: Path::new("other.rs").into(),
1239                },
1240                cx,
1241            )
1242        }))
1243        .await
1244        .unwrap();
1245
1246    // Join the worktree as client B.
1247    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
1248
1249    // Simulate a language server reporting errors for a file.
1250    let mut fake_language_server = fake_language_servers.next().await.unwrap();
1251    fake_language_server
1252        .receive_notification::<lsp::notification::DidOpenTextDocument>()
1253        .await;
1254    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
1255        lsp::PublishDiagnosticsParams {
1256            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
1257            version: None,
1258            diagnostics: vec![lsp::Diagnostic {
1259                severity: Some(lsp::DiagnosticSeverity::ERROR),
1260                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
1261                message: "message 1".to_string(),
1262                ..Default::default()
1263            }],
1264        },
1265    );
1266
1267    // Wait for server to see the diagnostics update.
1268    deterministic.run_until_parked();
1269    {
1270        let store = server.store.read().await;
1271        let project = store.project(project_id).unwrap();
1272        let worktree = project.worktrees.get(&worktree_id.to_proto()).unwrap();
1273        assert!(!worktree.diagnostic_summaries.is_empty());
1274    }
1275
1276    // Ensure client B observes the new diagnostics.
1277    project_b.read_with(cx_b, |project, cx| {
1278        assert_eq!(
1279            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
1280            &[(
1281                ProjectPath {
1282                    worktree_id,
1283                    path: Arc::from(Path::new("a.rs")),
1284                },
1285                DiagnosticSummary {
1286                    error_count: 1,
1287                    warning_count: 0,
1288                    ..Default::default()
1289                },
1290            )]
1291        )
1292    });
1293
1294    // Join project as client C and observe the diagnostics.
1295    let project_c = client_c.build_remote_project(&project_a, cx_a, cx_c).await;
1296    project_c.read_with(cx_c, |project, cx| {
1297        assert_eq!(
1298            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
1299            &[(
1300                ProjectPath {
1301                    worktree_id,
1302                    path: Arc::from(Path::new("a.rs")),
1303                },
1304                DiagnosticSummary {
1305                    error_count: 1,
1306                    warning_count: 0,
1307                    ..Default::default()
1308                },
1309            )]
1310        )
1311    });
1312
1313    // Simulate a language server reporting more errors for a file.
1314    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
1315        lsp::PublishDiagnosticsParams {
1316            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
1317            version: None,
1318            diagnostics: vec![
1319                lsp::Diagnostic {
1320                    severity: Some(lsp::DiagnosticSeverity::ERROR),
1321                    range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
1322                    message: "message 1".to_string(),
1323                    ..Default::default()
1324                },
1325                lsp::Diagnostic {
1326                    severity: Some(lsp::DiagnosticSeverity::WARNING),
1327                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 13)),
1328                    message: "message 2".to_string(),
1329                    ..Default::default()
1330                },
1331            ],
1332        },
1333    );
1334
1335    // Clients B and C get the updated summaries
1336    deterministic.run_until_parked();
1337    project_b.read_with(cx_b, |project, cx| {
1338        assert_eq!(
1339            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
1340            [(
1341                ProjectPath {
1342                    worktree_id,
1343                    path: Arc::from(Path::new("a.rs")),
1344                },
1345                DiagnosticSummary {
1346                    error_count: 1,
1347                    warning_count: 1,
1348                    ..Default::default()
1349                },
1350            )]
1351        );
1352    });
1353    project_c.read_with(cx_c, |project, cx| {
1354        assert_eq!(
1355            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
1356            [(
1357                ProjectPath {
1358                    worktree_id,
1359                    path: Arc::from(Path::new("a.rs")),
1360                },
1361                DiagnosticSummary {
1362                    error_count: 1,
1363                    warning_count: 1,
1364                    ..Default::default()
1365                },
1366            )]
1367        );
1368    });
1369
1370    // Open the file with the errors on client B. They should be present.
1371    let buffer_b = cx_b
1372        .background()
1373        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
1374        .await
1375        .unwrap();
1376
1377    buffer_b.read_with(cx_b, |buffer, _| {
1378        assert_eq!(
1379            buffer
1380                .snapshot()
1381                .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
1382                .map(|entry| entry)
1383                .collect::<Vec<_>>(),
1384            &[
1385                DiagnosticEntry {
1386                    range: Point::new(0, 4)..Point::new(0, 7),
1387                    diagnostic: Diagnostic {
1388                        group_id: 1,
1389                        message: "message 1".to_string(),
1390                        severity: lsp::DiagnosticSeverity::ERROR,
1391                        is_primary: true,
1392                        ..Default::default()
1393                    }
1394                },
1395                DiagnosticEntry {
1396                    range: Point::new(0, 10)..Point::new(0, 13),
1397                    diagnostic: Diagnostic {
1398                        group_id: 2,
1399                        severity: lsp::DiagnosticSeverity::WARNING,
1400                        message: "message 2".to_string(),
1401                        is_primary: true,
1402                        ..Default::default()
1403                    }
1404                }
1405            ]
1406        );
1407    });
1408
1409    // Simulate a language server reporting no errors for a file.
1410    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
1411        lsp::PublishDiagnosticsParams {
1412            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
1413            version: None,
1414            diagnostics: vec![],
1415        },
1416    );
1417    deterministic.run_until_parked();
1418    project_a.read_with(cx_a, |project, cx| {
1419        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
1420    });
1421    project_b.read_with(cx_b, |project, cx| {
1422        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
1423    });
1424    project_c.read_with(cx_c, |project, cx| {
1425        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
1426    });
1427}
1428
1429#[gpui::test(iterations = 10)]
1430async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
1431    cx_a.foreground().forbid_parking();
1432    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
1433    let client_a = server.create_client(cx_a, "user_a").await;
1434    let client_b = server.create_client(cx_b, "user_b").await;
1435    server
1436        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
1437        .await;
1438
1439    // Set up a fake language server.
1440    let mut language = Language::new(
1441        LanguageConfig {
1442            name: "Rust".into(),
1443            path_suffixes: vec!["rs".to_string()],
1444            ..Default::default()
1445        },
1446        Some(tree_sitter_rust::language()),
1447    );
1448    let mut fake_language_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
1449        capabilities: lsp::ServerCapabilities {
1450            completion_provider: Some(lsp::CompletionOptions {
1451                trigger_characters: Some(vec![".".to_string()]),
1452                ..Default::default()
1453            }),
1454            ..Default::default()
1455        },
1456        ..Default::default()
1457    });
1458    client_a.language_registry.add(Arc::new(language));
1459
1460    client_a
1461        .fs
1462        .insert_tree(
1463            "/a",
1464            json!({
1465                "main.rs": "fn main() { a }",
1466                "other.rs": "",
1467            }),
1468        )
1469        .await;
1470    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
1471    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
1472
1473    // Open a file in an editor as the guest.
1474    let buffer_b = project_b
1475        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
1476        .await
1477        .unwrap();
1478    let (window_b, _) = cx_b.add_window(|_| EmptyView);
1479    let editor_b = cx_b.add_view(window_b, |cx| {
1480        Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx)
1481    });
1482
1483    let fake_language_server = fake_language_servers.next().await.unwrap();
1484    buffer_b
1485        .condition(&cx_b, |buffer, _| !buffer.completion_triggers().is_empty())
1486        .await;
1487
1488    // Type a completion trigger character as the guest.
1489    editor_b.update(cx_b, |editor, cx| {
1490        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
1491        editor.handle_input(&Input(".".into()), cx);
1492        cx.focus(&editor_b);
1493    });
1494
1495    // Receive a completion request as the host's language server.
1496    // Return some completions from the host's language server.
1497    cx_a.foreground().start_waiting();
1498    fake_language_server
1499        .handle_request::<lsp::request::Completion, _, _>(|params, _| async move {
1500            assert_eq!(
1501                params.text_document_position.text_document.uri,
1502                lsp::Url::from_file_path("/a/main.rs").unwrap(),
1503            );
1504            assert_eq!(
1505                params.text_document_position.position,
1506                lsp::Position::new(0, 14),
1507            );
1508
1509            Ok(Some(lsp::CompletionResponse::Array(vec![
1510                lsp::CompletionItem {
1511                    label: "first_method(…)".into(),
1512                    detail: Some("fn(&mut self, B) -> C".into()),
1513                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
1514                        new_text: "first_method($1)".to_string(),
1515                        range: lsp::Range::new(
1516                            lsp::Position::new(0, 14),
1517                            lsp::Position::new(0, 14),
1518                        ),
1519                    })),
1520                    insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
1521                    ..Default::default()
1522                },
1523                lsp::CompletionItem {
1524                    label: "second_method(…)".into(),
1525                    detail: Some("fn(&mut self, C) -> D<E>".into()),
1526                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
1527                        new_text: "second_method()".to_string(),
1528                        range: lsp::Range::new(
1529                            lsp::Position::new(0, 14),
1530                            lsp::Position::new(0, 14),
1531                        ),
1532                    })),
1533                    insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
1534                    ..Default::default()
1535                },
1536            ])))
1537        })
1538        .next()
1539        .await
1540        .unwrap();
1541    cx_a.foreground().finish_waiting();
1542
1543    // Open the buffer on the host.
1544    let buffer_a = project_a
1545        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
1546        .await
1547        .unwrap();
1548    buffer_a
1549        .condition(&cx_a, |buffer, _| buffer.text() == "fn main() { a. }")
1550        .await;
1551
1552    // Confirm a completion on the guest.
1553    editor_b
1554        .condition(&cx_b, |editor, _| editor.context_menu_visible())
1555        .await;
1556    editor_b.update(cx_b, |editor, cx| {
1557        editor.confirm_completion(&ConfirmCompletion { item_ix: Some(0) }, cx);
1558        assert_eq!(editor.text(cx), "fn main() { a.first_method() }");
1559    });
1560
1561    // Return a resolved completion from the host's language server.
1562    // The resolved completion has an additional text edit.
1563    fake_language_server.handle_request::<lsp::request::ResolveCompletionItem, _, _>(
1564        |params, _| async move {
1565            assert_eq!(params.label, "first_method(…)");
1566            Ok(lsp::CompletionItem {
1567                label: "first_method(…)".into(),
1568                detail: Some("fn(&mut self, B) -> C".into()),
1569                text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
1570                    new_text: "first_method($1)".to_string(),
1571                    range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
1572                })),
1573                additional_text_edits: Some(vec![lsp::TextEdit {
1574                    new_text: "use d::SomeTrait;\n".to_string(),
1575                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
1576                }]),
1577                insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
1578                ..Default::default()
1579            })
1580        },
1581    );
1582
1583    // The additional edit is applied.
1584    buffer_a
1585        .condition(&cx_a, |buffer, _| {
1586            buffer.text() == "use d::SomeTrait;\nfn main() { a.first_method() }"
1587        })
1588        .await;
1589    buffer_b
1590        .condition(&cx_b, |buffer, _| {
1591            buffer.text() == "use d::SomeTrait;\nfn main() { a.first_method() }"
1592        })
1593        .await;
1594}
1595
1596#[gpui::test(iterations = 10)]
1597async fn test_reloading_buffer_manually(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
1598    cx_a.foreground().forbid_parking();
1599    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
1600    let client_a = server.create_client(cx_a, "user_a").await;
1601    let client_b = server.create_client(cx_b, "user_b").await;
1602    server
1603        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
1604        .await;
1605
1606    client_a
1607        .fs
1608        .insert_tree("/a", json!({ "a.rs": "let one = 1;" }))
1609        .await;
1610    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
1611    let buffer_a = project_a
1612        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
1613        .await
1614        .unwrap();
1615
1616    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
1617
1618    let buffer_b = cx_b
1619        .background()
1620        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
1621        .await
1622        .unwrap();
1623    buffer_b.update(cx_b, |buffer, cx| {
1624        buffer.edit([(4..7, "six")], cx);
1625        buffer.edit([(10..11, "6")], cx);
1626        assert_eq!(buffer.text(), "let six = 6;");
1627        assert!(buffer.is_dirty());
1628        assert!(!buffer.has_conflict());
1629    });
1630    buffer_a
1631        .condition(cx_a, |buffer, _| buffer.text() == "let six = 6;")
1632        .await;
1633
1634    client_a
1635        .fs
1636        .save("/a/a.rs".as_ref(), &Rope::from("let seven = 7;"))
1637        .await
1638        .unwrap();
1639    buffer_a
1640        .condition(cx_a, |buffer, _| buffer.has_conflict())
1641        .await;
1642    buffer_b
1643        .condition(cx_b, |buffer, _| buffer.has_conflict())
1644        .await;
1645
1646    project_b
1647        .update(cx_b, |project, cx| {
1648            project.reload_buffers(HashSet::from_iter([buffer_b.clone()]), true, cx)
1649        })
1650        .await
1651        .unwrap();
1652    buffer_a.read_with(cx_a, |buffer, _| {
1653        assert_eq!(buffer.text(), "let seven = 7;");
1654        assert!(!buffer.is_dirty());
1655        assert!(!buffer.has_conflict());
1656    });
1657    buffer_b.read_with(cx_b, |buffer, _| {
1658        assert_eq!(buffer.text(), "let seven = 7;");
1659        assert!(!buffer.is_dirty());
1660        assert!(!buffer.has_conflict());
1661    });
1662
1663    buffer_a.update(cx_a, |buffer, cx| {
1664        // Undoing on the host is a no-op when the reload was initiated by the guest.
1665        buffer.undo(cx);
1666        assert_eq!(buffer.text(), "let seven = 7;");
1667        assert!(!buffer.is_dirty());
1668        assert!(!buffer.has_conflict());
1669    });
1670    buffer_b.update(cx_b, |buffer, cx| {
1671        // Undoing on the guest rolls back the buffer to before it was reloaded but the conflict gets cleared.
1672        buffer.undo(cx);
1673        assert_eq!(buffer.text(), "let six = 6;");
1674        assert!(buffer.is_dirty());
1675        assert!(!buffer.has_conflict());
1676    });
1677}
1678
1679#[gpui::test(iterations = 10)]
1680async fn test_formatting_buffer(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
1681    cx_a.foreground().forbid_parking();
1682    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
1683    let client_a = server.create_client(cx_a, "user_a").await;
1684    let client_b = server.create_client(cx_b, "user_b").await;
1685    server
1686        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
1687        .await;
1688
1689    // Set up a fake language server.
1690    let mut language = Language::new(
1691        LanguageConfig {
1692            name: "Rust".into(),
1693            path_suffixes: vec!["rs".to_string()],
1694            ..Default::default()
1695        },
1696        Some(tree_sitter_rust::language()),
1697    );
1698    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default());
1699    client_a.language_registry.add(Arc::new(language));
1700
1701    client_a
1702        .fs
1703        .insert_tree("/a", json!({ "a.rs": "let one = two" }))
1704        .await;
1705    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
1706    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
1707
1708    let buffer_b = cx_b
1709        .background()
1710        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
1711        .await
1712        .unwrap();
1713
1714    let fake_language_server = fake_language_servers.next().await.unwrap();
1715    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
1716        Ok(Some(vec![
1717            lsp::TextEdit {
1718                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 4)),
1719                new_text: "h".to_string(),
1720            },
1721            lsp::TextEdit {
1722                range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 7)),
1723                new_text: "y".to_string(),
1724            },
1725        ]))
1726    });
1727
1728    project_b
1729        .update(cx_b, |project, cx| {
1730            project.format(HashSet::from_iter([buffer_b.clone()]), true, cx)
1731        })
1732        .await
1733        .unwrap();
1734    assert_eq!(
1735        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
1736        "let honey = two"
1737    );
1738}
1739
1740#[gpui::test(iterations = 10)]
1741async fn test_definition(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
1742    cx_a.foreground().forbid_parking();
1743    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
1744    let client_a = server.create_client(cx_a, "user_a").await;
1745    let client_b = server.create_client(cx_b, "user_b").await;
1746    server
1747        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
1748        .await;
1749
1750    // Set up a fake language server.
1751    let mut language = Language::new(
1752        LanguageConfig {
1753            name: "Rust".into(),
1754            path_suffixes: vec!["rs".to_string()],
1755            ..Default::default()
1756        },
1757        Some(tree_sitter_rust::language()),
1758    );
1759    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default());
1760    client_a.language_registry.add(Arc::new(language));
1761
1762    client_a
1763        .fs
1764        .insert_tree(
1765            "/root",
1766            json!({
1767                "dir-1": {
1768                    "a.rs": "const ONE: usize = b::TWO + b::THREE;",
1769                },
1770                "dir-2": {
1771                    "b.rs": "const TWO: usize = 2;\nconst THREE: usize = 3;",
1772                }
1773            }),
1774        )
1775        .await;
1776    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
1777    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
1778
1779    // Open the file on client B.
1780    let buffer_b = cx_b
1781        .background()
1782        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
1783        .await
1784        .unwrap();
1785
1786    // Request the definition of a symbol as the guest.
1787    let fake_language_server = fake_language_servers.next().await.unwrap();
1788    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
1789        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
1790            lsp::Location::new(
1791                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
1792                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
1793            ),
1794        )))
1795    });
1796
1797    let definitions_1 = project_b
1798        .update(cx_b, |p, cx| p.definition(&buffer_b, 23, cx))
1799        .await
1800        .unwrap();
1801    cx_b.read(|cx| {
1802        assert_eq!(definitions_1.len(), 1);
1803        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
1804        let target_buffer = definitions_1[0].buffer.read(cx);
1805        assert_eq!(
1806            target_buffer.text(),
1807            "const TWO: usize = 2;\nconst THREE: usize = 3;"
1808        );
1809        assert_eq!(
1810            definitions_1[0].range.to_point(target_buffer),
1811            Point::new(0, 6)..Point::new(0, 9)
1812        );
1813    });
1814
1815    // Try getting more definitions for the same buffer, ensuring the buffer gets reused from
1816    // the previous call to `definition`.
1817    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
1818        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
1819            lsp::Location::new(
1820                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
1821                lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)),
1822            ),
1823        )))
1824    });
1825
1826    let definitions_2 = project_b
1827        .update(cx_b, |p, cx| p.definition(&buffer_b, 33, cx))
1828        .await
1829        .unwrap();
1830    cx_b.read(|cx| {
1831        assert_eq!(definitions_2.len(), 1);
1832        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
1833        let target_buffer = definitions_2[0].buffer.read(cx);
1834        assert_eq!(
1835            target_buffer.text(),
1836            "const TWO: usize = 2;\nconst THREE: usize = 3;"
1837        );
1838        assert_eq!(
1839            definitions_2[0].range.to_point(target_buffer),
1840            Point::new(1, 6)..Point::new(1, 11)
1841        );
1842    });
1843    assert_eq!(definitions_1[0].buffer, definitions_2[0].buffer);
1844}
1845
1846#[gpui::test(iterations = 10)]
1847async fn test_references(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
1848    cx_a.foreground().forbid_parking();
1849    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
1850    let client_a = server.create_client(cx_a, "user_a").await;
1851    let client_b = server.create_client(cx_b, "user_b").await;
1852    server
1853        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
1854        .await;
1855
1856    // Set up a fake language server.
1857    let mut language = Language::new(
1858        LanguageConfig {
1859            name: "Rust".into(),
1860            path_suffixes: vec!["rs".to_string()],
1861            ..Default::default()
1862        },
1863        Some(tree_sitter_rust::language()),
1864    );
1865    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default());
1866    client_a.language_registry.add(Arc::new(language));
1867
1868    client_a
1869        .fs
1870        .insert_tree(
1871            "/root",
1872            json!({
1873                "dir-1": {
1874                    "one.rs": "const ONE: usize = 1;",
1875                    "two.rs": "const TWO: usize = one::ONE + one::ONE;",
1876                },
1877                "dir-2": {
1878                    "three.rs": "const THREE: usize = two::TWO + one::ONE;",
1879                }
1880            }),
1881        )
1882        .await;
1883    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
1884    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
1885
1886    // Open the file on client B.
1887    let buffer_b = cx_b
1888        .background()
1889        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
1890        .await
1891        .unwrap();
1892
1893    // Request references to a symbol as the guest.
1894    let fake_language_server = fake_language_servers.next().await.unwrap();
1895    fake_language_server.handle_request::<lsp::request::References, _, _>(|params, _| async move {
1896        assert_eq!(
1897            params.text_document_position.text_document.uri.as_str(),
1898            "file:///root/dir-1/one.rs"
1899        );
1900        Ok(Some(vec![
1901            lsp::Location {
1902                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
1903                range: lsp::Range::new(lsp::Position::new(0, 24), lsp::Position::new(0, 27)),
1904            },
1905            lsp::Location {
1906                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
1907                range: lsp::Range::new(lsp::Position::new(0, 35), lsp::Position::new(0, 38)),
1908            },
1909            lsp::Location {
1910                uri: lsp::Url::from_file_path("/root/dir-2/three.rs").unwrap(),
1911                range: lsp::Range::new(lsp::Position::new(0, 37), lsp::Position::new(0, 40)),
1912            },
1913        ]))
1914    });
1915
1916    let references = project_b
1917        .update(cx_b, |p, cx| p.references(&buffer_b, 7, cx))
1918        .await
1919        .unwrap();
1920    cx_b.read(|cx| {
1921        assert_eq!(references.len(), 3);
1922        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
1923
1924        let two_buffer = references[0].buffer.read(cx);
1925        let three_buffer = references[2].buffer.read(cx);
1926        assert_eq!(
1927            two_buffer.file().unwrap().path().as_ref(),
1928            Path::new("two.rs")
1929        );
1930        assert_eq!(references[1].buffer, references[0].buffer);
1931        assert_eq!(
1932            three_buffer.file().unwrap().full_path(cx),
1933            Path::new("three.rs")
1934        );
1935
1936        assert_eq!(references[0].range.to_offset(&two_buffer), 24..27);
1937        assert_eq!(references[1].range.to_offset(&two_buffer), 35..38);
1938        assert_eq!(references[2].range.to_offset(&three_buffer), 37..40);
1939    });
1940}
1941
1942#[gpui::test(iterations = 10)]
1943async fn test_project_search(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
1944    cx_a.foreground().forbid_parking();
1945    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
1946    let client_a = server.create_client(cx_a, "user_a").await;
1947    let client_b = server.create_client(cx_b, "user_b").await;
1948    server
1949        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
1950        .await;
1951
1952    client_a
1953        .fs
1954        .insert_tree(
1955            "/root",
1956            json!({
1957                "dir-1": {
1958                    "a": "hello world",
1959                    "b": "goodnight moon",
1960                    "c": "a world of goo",
1961                    "d": "world champion of clown world",
1962                },
1963                "dir-2": {
1964                    "e": "disney world is fun",
1965                }
1966            }),
1967        )
1968        .await;
1969    let (project_a, _) = client_a.build_local_project("/root/dir-1", cx_a).await;
1970    let (worktree_2, _) = project_a
1971        .update(cx_a, |p, cx| {
1972            p.find_or_create_local_worktree("/root/dir-2", true, cx)
1973        })
1974        .await
1975        .unwrap();
1976    worktree_2
1977        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
1978        .await;
1979
1980    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
1981
1982    // Perform a search as the guest.
1983    let results = project_b
1984        .update(cx_b, |project, cx| {
1985            project.search(SearchQuery::text("world", false, false), cx)
1986        })
1987        .await
1988        .unwrap();
1989
1990    let mut ranges_by_path = results
1991        .into_iter()
1992        .map(|(buffer, ranges)| {
1993            buffer.read_with(cx_b, |buffer, cx| {
1994                let path = buffer.file().unwrap().full_path(cx);
1995                let offset_ranges = ranges
1996                    .into_iter()
1997                    .map(|range| range.to_offset(buffer))
1998                    .collect::<Vec<_>>();
1999                (path, offset_ranges)
2000            })
2001        })
2002        .collect::<Vec<_>>();
2003    ranges_by_path.sort_by_key(|(path, _)| path.clone());
2004
2005    assert_eq!(
2006        ranges_by_path,
2007        &[
2008            (PathBuf::from("dir-1/a"), vec![6..11]),
2009            (PathBuf::from("dir-1/c"), vec![2..7]),
2010            (PathBuf::from("dir-1/d"), vec![0..5, 24..29]),
2011            (PathBuf::from("dir-2/e"), vec![7..12]),
2012        ]
2013    );
2014}
2015
2016#[gpui::test(iterations = 10)]
2017async fn test_document_highlights(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
2018    cx_a.foreground().forbid_parking();
2019    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
2020    let client_a = server.create_client(cx_a, "user_a").await;
2021    let client_b = server.create_client(cx_b, "user_b").await;
2022    server
2023        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
2024        .await;
2025
2026    client_a
2027        .fs
2028        .insert_tree(
2029            "/root-1",
2030            json!({
2031                "main.rs": "fn double(number: i32) -> i32 { number + number }",
2032            }),
2033        )
2034        .await;
2035
2036    // Set up a fake language server.
2037    let mut language = Language::new(
2038        LanguageConfig {
2039            name: "Rust".into(),
2040            path_suffixes: vec!["rs".to_string()],
2041            ..Default::default()
2042        },
2043        Some(tree_sitter_rust::language()),
2044    );
2045    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default());
2046    client_a.language_registry.add(Arc::new(language));
2047
2048    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
2049    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
2050
2051    // Open the file on client B.
2052    let buffer_b = cx_b
2053        .background()
2054        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
2055        .await
2056        .unwrap();
2057
2058    // Request document highlights as the guest.
2059    let fake_language_server = fake_language_servers.next().await.unwrap();
2060    fake_language_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
2061        |params, _| async move {
2062            assert_eq!(
2063                params
2064                    .text_document_position_params
2065                    .text_document
2066                    .uri
2067                    .as_str(),
2068                "file:///root-1/main.rs"
2069            );
2070            assert_eq!(
2071                params.text_document_position_params.position,
2072                lsp::Position::new(0, 34)
2073            );
2074            Ok(Some(vec![
2075                lsp::DocumentHighlight {
2076                    kind: Some(lsp::DocumentHighlightKind::WRITE),
2077                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 16)),
2078                },
2079                lsp::DocumentHighlight {
2080                    kind: Some(lsp::DocumentHighlightKind::READ),
2081                    range: lsp::Range::new(lsp::Position::new(0, 32), lsp::Position::new(0, 38)),
2082                },
2083                lsp::DocumentHighlight {
2084                    kind: Some(lsp::DocumentHighlightKind::READ),
2085                    range: lsp::Range::new(lsp::Position::new(0, 41), lsp::Position::new(0, 47)),
2086                },
2087            ]))
2088        },
2089    );
2090
2091    let highlights = project_b
2092        .update(cx_b, |p, cx| p.document_highlights(&buffer_b, 34, cx))
2093        .await
2094        .unwrap();
2095    buffer_b.read_with(cx_b, |buffer, _| {
2096        let snapshot = buffer.snapshot();
2097
2098        let highlights = highlights
2099            .into_iter()
2100            .map(|highlight| (highlight.kind, highlight.range.to_offset(&snapshot)))
2101            .collect::<Vec<_>>();
2102        assert_eq!(
2103            highlights,
2104            &[
2105                (lsp::DocumentHighlightKind::WRITE, 10..16),
2106                (lsp::DocumentHighlightKind::READ, 32..38),
2107                (lsp::DocumentHighlightKind::READ, 41..47)
2108            ]
2109        )
2110    });
2111}
2112
2113#[gpui::test(iterations = 10)]
2114async fn test_project_symbols(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
2115    cx_a.foreground().forbid_parking();
2116    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
2117    let client_a = server.create_client(cx_a, "user_a").await;
2118    let client_b = server.create_client(cx_b, "user_b").await;
2119    server
2120        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
2121        .await;
2122
2123    // Set up a fake language server.
2124    let mut language = Language::new(
2125        LanguageConfig {
2126            name: "Rust".into(),
2127            path_suffixes: vec!["rs".to_string()],
2128            ..Default::default()
2129        },
2130        Some(tree_sitter_rust::language()),
2131    );
2132    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default());
2133    client_a.language_registry.add(Arc::new(language));
2134
2135    client_a
2136        .fs
2137        .insert_tree(
2138            "/code",
2139            json!({
2140                "crate-1": {
2141                    "one.rs": "const ONE: usize = 1;",
2142                },
2143                "crate-2": {
2144                    "two.rs": "const TWO: usize = 2; const THREE: usize = 3;",
2145                },
2146                "private": {
2147                    "passwords.txt": "the-password",
2148                }
2149            }),
2150        )
2151        .await;
2152    let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await;
2153    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
2154
2155    // Cause the language server to start.
2156    let _buffer = cx_b
2157        .background()
2158        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
2159        .await
2160        .unwrap();
2161
2162    let fake_language_server = fake_language_servers.next().await.unwrap();
2163    fake_language_server.handle_request::<lsp::request::WorkspaceSymbol, _, _>(|_, _| async move {
2164        #[allow(deprecated)]
2165        Ok(Some(vec![lsp::SymbolInformation {
2166            name: "TWO".into(),
2167            location: lsp::Location {
2168                uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
2169                range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
2170            },
2171            kind: lsp::SymbolKind::CONSTANT,
2172            tags: None,
2173            container_name: None,
2174            deprecated: None,
2175        }]))
2176    });
2177
2178    // Request the definition of a symbol as the guest.
2179    let symbols = project_b
2180        .update(cx_b, |p, cx| p.symbols("two", cx))
2181        .await
2182        .unwrap();
2183    assert_eq!(symbols.len(), 1);
2184    assert_eq!(symbols[0].name, "TWO");
2185
2186    // Open one of the returned symbols.
2187    let buffer_b_2 = project_b
2188        .update(cx_b, |project, cx| {
2189            project.open_buffer_for_symbol(&symbols[0], cx)
2190        })
2191        .await
2192        .unwrap();
2193    buffer_b_2.read_with(cx_b, |buffer, _| {
2194        assert_eq!(
2195            buffer.file().unwrap().path().as_ref(),
2196            Path::new("../crate-2/two.rs")
2197        );
2198    });
2199
2200    // Attempt to craft a symbol and violate host's privacy by opening an arbitrary file.
2201    let mut fake_symbol = symbols[0].clone();
2202    fake_symbol.path = Path::new("/code/secrets").into();
2203    let error = project_b
2204        .update(cx_b, |project, cx| {
2205            project.open_buffer_for_symbol(&fake_symbol, cx)
2206        })
2207        .await
2208        .unwrap_err();
2209    assert!(error.to_string().contains("invalid symbol signature"));
2210}
2211
2212#[gpui::test(iterations = 10)]
2213async fn test_open_buffer_while_getting_definition_pointing_to_it(
2214    cx_a: &mut TestAppContext,
2215    cx_b: &mut TestAppContext,
2216    mut rng: StdRng,
2217) {
2218    cx_a.foreground().forbid_parking();
2219    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
2220    let client_a = server.create_client(cx_a, "user_a").await;
2221    let client_b = server.create_client(cx_b, "user_b").await;
2222    server
2223        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
2224        .await;
2225
2226    // Set up a fake language server.
2227    let mut language = Language::new(
2228        LanguageConfig {
2229            name: "Rust".into(),
2230            path_suffixes: vec!["rs".to_string()],
2231            ..Default::default()
2232        },
2233        Some(tree_sitter_rust::language()),
2234    );
2235    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default());
2236    client_a.language_registry.add(Arc::new(language));
2237
2238    client_a
2239        .fs
2240        .insert_tree(
2241            "/root",
2242            json!({
2243                "a.rs": "const ONE: usize = b::TWO;",
2244                "b.rs": "const TWO: usize = 2",
2245            }),
2246        )
2247        .await;
2248    let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await;
2249    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
2250
2251    let buffer_b1 = cx_b
2252        .background()
2253        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
2254        .await
2255        .unwrap();
2256
2257    let fake_language_server = fake_language_servers.next().await.unwrap();
2258    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
2259        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
2260            lsp::Location::new(
2261                lsp::Url::from_file_path("/root/b.rs").unwrap(),
2262                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
2263            ),
2264        )))
2265    });
2266
2267    let definitions;
2268    let buffer_b2;
2269    if rng.gen() {
2270        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
2271        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
2272    } else {
2273        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
2274        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
2275    }
2276
2277    let buffer_b2 = buffer_b2.await.unwrap();
2278    let definitions = definitions.await.unwrap();
2279    assert_eq!(definitions.len(), 1);
2280    assert_eq!(definitions[0].buffer, buffer_b2);
2281}
2282
2283#[gpui::test(iterations = 10)]
2284async fn test_collaborating_with_code_actions(
2285    cx_a: &mut TestAppContext,
2286    cx_b: &mut TestAppContext,
2287) {
2288    cx_a.foreground().forbid_parking();
2289    cx_b.update(|cx| editor::init(cx));
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        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
2295        .await;
2296
2297    // Set up a fake language server.
2298    let mut language = Language::new(
2299        LanguageConfig {
2300            name: "Rust".into(),
2301            path_suffixes: vec!["rs".to_string()],
2302            ..Default::default()
2303        },
2304        Some(tree_sitter_rust::language()),
2305    );
2306    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default());
2307    client_a.language_registry.add(Arc::new(language));
2308
2309    client_a
2310        .fs
2311        .insert_tree(
2312            "/a",
2313            json!({
2314                "main.rs": "mod other;\nfn main() { let foo = other::foo(); }",
2315                "other.rs": "pub fn foo() -> usize { 4 }",
2316            }),
2317        )
2318        .await;
2319    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
2320
2321    // Join the project as client B.
2322    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
2323    let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(project_b.clone(), cx));
2324    let editor_b = workspace_b
2325        .update(cx_b, |workspace, cx| {
2326            workspace.open_path((worktree_id, "main.rs"), true, cx)
2327        })
2328        .await
2329        .unwrap()
2330        .downcast::<Editor>()
2331        .unwrap();
2332
2333    let mut fake_language_server = fake_language_servers.next().await.unwrap();
2334    fake_language_server
2335        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
2336            assert_eq!(
2337                params.text_document.uri,
2338                lsp::Url::from_file_path("/a/main.rs").unwrap(),
2339            );
2340            assert_eq!(params.range.start, lsp::Position::new(0, 0));
2341            assert_eq!(params.range.end, lsp::Position::new(0, 0));
2342            Ok(None)
2343        })
2344        .next()
2345        .await;
2346
2347    // Move cursor to a location that contains code actions.
2348    editor_b.update(cx_b, |editor, cx| {
2349        editor.change_selections(None, cx, |s| {
2350            s.select_ranges([Point::new(1, 31)..Point::new(1, 31)])
2351        });
2352        cx.focus(&editor_b);
2353    });
2354
2355    fake_language_server
2356        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
2357            assert_eq!(
2358                params.text_document.uri,
2359                lsp::Url::from_file_path("/a/main.rs").unwrap(),
2360            );
2361            assert_eq!(params.range.start, lsp::Position::new(1, 31));
2362            assert_eq!(params.range.end, lsp::Position::new(1, 31));
2363
2364            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
2365                lsp::CodeAction {
2366                    title: "Inline into all callers".to_string(),
2367                    edit: Some(lsp::WorkspaceEdit {
2368                        changes: Some(
2369                            [
2370                                (
2371                                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
2372                                    vec![lsp::TextEdit::new(
2373                                        lsp::Range::new(
2374                                            lsp::Position::new(1, 22),
2375                                            lsp::Position::new(1, 34),
2376                                        ),
2377                                        "4".to_string(),
2378                                    )],
2379                                ),
2380                                (
2381                                    lsp::Url::from_file_path("/a/other.rs").unwrap(),
2382                                    vec![lsp::TextEdit::new(
2383                                        lsp::Range::new(
2384                                            lsp::Position::new(0, 0),
2385                                            lsp::Position::new(0, 27),
2386                                        ),
2387                                        "".to_string(),
2388                                    )],
2389                                ),
2390                            ]
2391                            .into_iter()
2392                            .collect(),
2393                        ),
2394                        ..Default::default()
2395                    }),
2396                    data: Some(json!({
2397                        "codeActionParams": {
2398                            "range": {
2399                                "start": {"line": 1, "column": 31},
2400                                "end": {"line": 1, "column": 31},
2401                            }
2402                        }
2403                    })),
2404                    ..Default::default()
2405                },
2406            )]))
2407        })
2408        .next()
2409        .await;
2410
2411    // Toggle code actions and wait for them to display.
2412    editor_b.update(cx_b, |editor, cx| {
2413        editor.toggle_code_actions(
2414            &ToggleCodeActions {
2415                deployed_from_indicator: false,
2416            },
2417            cx,
2418        );
2419    });
2420    editor_b
2421        .condition(&cx_b, |editor, _| editor.context_menu_visible())
2422        .await;
2423
2424    fake_language_server.remove_request_handler::<lsp::request::CodeActionRequest>();
2425
2426    // Confirming the code action will trigger a resolve request.
2427    let confirm_action = workspace_b
2428        .update(cx_b, |workspace, cx| {
2429            Editor::confirm_code_action(workspace, &ConfirmCodeAction { item_ix: Some(0) }, cx)
2430        })
2431        .unwrap();
2432    fake_language_server.handle_request::<lsp::request::CodeActionResolveRequest, _, _>(
2433        |_, _| async move {
2434            Ok(lsp::CodeAction {
2435                title: "Inline into all callers".to_string(),
2436                edit: Some(lsp::WorkspaceEdit {
2437                    changes: Some(
2438                        [
2439                            (
2440                                lsp::Url::from_file_path("/a/main.rs").unwrap(),
2441                                vec![lsp::TextEdit::new(
2442                                    lsp::Range::new(
2443                                        lsp::Position::new(1, 22),
2444                                        lsp::Position::new(1, 34),
2445                                    ),
2446                                    "4".to_string(),
2447                                )],
2448                            ),
2449                            (
2450                                lsp::Url::from_file_path("/a/other.rs").unwrap(),
2451                                vec![lsp::TextEdit::new(
2452                                    lsp::Range::new(
2453                                        lsp::Position::new(0, 0),
2454                                        lsp::Position::new(0, 27),
2455                                    ),
2456                                    "".to_string(),
2457                                )],
2458                            ),
2459                        ]
2460                        .into_iter()
2461                        .collect(),
2462                    ),
2463                    ..Default::default()
2464                }),
2465                ..Default::default()
2466            })
2467        },
2468    );
2469
2470    // After the action is confirmed, an editor containing both modified files is opened.
2471    confirm_action.await.unwrap();
2472    let code_action_editor = workspace_b.read_with(cx_b, |workspace, cx| {
2473        workspace
2474            .active_item(cx)
2475            .unwrap()
2476            .downcast::<Editor>()
2477            .unwrap()
2478    });
2479    code_action_editor.update(cx_b, |editor, cx| {
2480        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
2481        editor.undo(&Undo, cx);
2482        assert_eq!(
2483            editor.text(cx),
2484            "mod other;\nfn main() { let foo = other::foo(); }\npub fn foo() -> usize { 4 }"
2485        );
2486        editor.redo(&Redo, cx);
2487        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
2488    });
2489}
2490
2491#[gpui::test(iterations = 10)]
2492async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
2493    cx_a.foreground().forbid_parking();
2494    cx_b.update(|cx| editor::init(cx));
2495    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
2496    let client_a = server.create_client(cx_a, "user_a").await;
2497    let client_b = server.create_client(cx_b, "user_b").await;
2498    server
2499        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
2500        .await;
2501
2502    // Set up a fake language server.
2503    let mut language = Language::new(
2504        LanguageConfig {
2505            name: "Rust".into(),
2506            path_suffixes: vec!["rs".to_string()],
2507            ..Default::default()
2508        },
2509        Some(tree_sitter_rust::language()),
2510    );
2511    let mut fake_language_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
2512        capabilities: lsp::ServerCapabilities {
2513            rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
2514                prepare_provider: Some(true),
2515                work_done_progress_options: Default::default(),
2516            })),
2517            ..Default::default()
2518        },
2519        ..Default::default()
2520    });
2521    client_a.language_registry.add(Arc::new(language));
2522
2523    client_a
2524        .fs
2525        .insert_tree(
2526            "/dir",
2527            json!({
2528                "one.rs": "const ONE: usize = 1;",
2529                "two.rs": "const TWO: usize = one::ONE + one::ONE;"
2530            }),
2531        )
2532        .await;
2533    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2534    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
2535
2536    let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(project_b.clone(), cx));
2537    let editor_b = workspace_b
2538        .update(cx_b, |workspace, cx| {
2539            workspace.open_path((worktree_id, "one.rs"), true, cx)
2540        })
2541        .await
2542        .unwrap()
2543        .downcast::<Editor>()
2544        .unwrap();
2545    let fake_language_server = fake_language_servers.next().await.unwrap();
2546
2547    // Move cursor to a location that can be renamed.
2548    let prepare_rename = editor_b.update(cx_b, |editor, cx| {
2549        editor.change_selections(None, cx, |s| s.select_ranges([7..7]));
2550        editor.rename(&Rename, cx).unwrap()
2551    });
2552
2553    fake_language_server
2554        .handle_request::<lsp::request::PrepareRenameRequest, _, _>(|params, _| async move {
2555            assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs");
2556            assert_eq!(params.position, lsp::Position::new(0, 7));
2557            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
2558                lsp::Position::new(0, 6),
2559                lsp::Position::new(0, 9),
2560            ))))
2561        })
2562        .next()
2563        .await
2564        .unwrap();
2565    prepare_rename.await.unwrap();
2566    editor_b.update(cx_b, |editor, cx| {
2567        let rename = editor.pending_rename().unwrap();
2568        let buffer = editor.buffer().read(cx).snapshot(cx);
2569        assert_eq!(
2570            rename.range.start.to_offset(&buffer)..rename.range.end.to_offset(&buffer),
2571            6..9
2572        );
2573        rename.editor.update(cx, |rename_editor, cx| {
2574            rename_editor.buffer().update(cx, |rename_buffer, cx| {
2575                rename_buffer.edit([(0..3, "THREE")], cx);
2576            });
2577        });
2578    });
2579
2580    let confirm_rename = workspace_b.update(cx_b, |workspace, cx| {
2581        Editor::confirm_rename(workspace, &ConfirmRename, cx).unwrap()
2582    });
2583    fake_language_server
2584        .handle_request::<lsp::request::Rename, _, _>(|params, _| async move {
2585            assert_eq!(
2586                params.text_document_position.text_document.uri.as_str(),
2587                "file:///dir/one.rs"
2588            );
2589            assert_eq!(
2590                params.text_document_position.position,
2591                lsp::Position::new(0, 6)
2592            );
2593            assert_eq!(params.new_name, "THREE");
2594            Ok(Some(lsp::WorkspaceEdit {
2595                changes: Some(
2596                    [
2597                        (
2598                            lsp::Url::from_file_path("/dir/one.rs").unwrap(),
2599                            vec![lsp::TextEdit::new(
2600                                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
2601                                "THREE".to_string(),
2602                            )],
2603                        ),
2604                        (
2605                            lsp::Url::from_file_path("/dir/two.rs").unwrap(),
2606                            vec![
2607                                lsp::TextEdit::new(
2608                                    lsp::Range::new(
2609                                        lsp::Position::new(0, 24),
2610                                        lsp::Position::new(0, 27),
2611                                    ),
2612                                    "THREE".to_string(),
2613                                ),
2614                                lsp::TextEdit::new(
2615                                    lsp::Range::new(
2616                                        lsp::Position::new(0, 35),
2617                                        lsp::Position::new(0, 38),
2618                                    ),
2619                                    "THREE".to_string(),
2620                                ),
2621                            ],
2622                        ),
2623                    ]
2624                    .into_iter()
2625                    .collect(),
2626                ),
2627                ..Default::default()
2628            }))
2629        })
2630        .next()
2631        .await
2632        .unwrap();
2633    confirm_rename.await.unwrap();
2634
2635    let rename_editor = workspace_b.read_with(cx_b, |workspace, cx| {
2636        workspace
2637            .active_item(cx)
2638            .unwrap()
2639            .downcast::<Editor>()
2640            .unwrap()
2641    });
2642    rename_editor.update(cx_b, |editor, cx| {
2643        assert_eq!(
2644            editor.text(cx),
2645            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
2646        );
2647        editor.undo(&Undo, cx);
2648        assert_eq!(
2649            editor.text(cx),
2650            "const ONE: usize = 1;\nconst TWO: usize = one::ONE + one::ONE;"
2651        );
2652        editor.redo(&Redo, cx);
2653        assert_eq!(
2654            editor.text(cx),
2655            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
2656        );
2657    });
2658
2659    // Ensure temporary rename edits cannot be undone/redone.
2660    editor_b.update(cx_b, |editor, cx| {
2661        editor.undo(&Undo, cx);
2662        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
2663        editor.undo(&Undo, cx);
2664        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
2665        editor.redo(&Redo, cx);
2666        assert_eq!(editor.text(cx), "const THREE: usize = 1;");
2667    })
2668}
2669
2670#[gpui::test(iterations = 10)]
2671async fn test_basic_chat(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
2672    cx_a.foreground().forbid_parking();
2673    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
2674    let client_a = server.create_client(cx_a, "user_a").await;
2675    let client_b = server.create_client(cx_b, "user_b").await;
2676
2677    // Create an org that includes these 2 users.
2678    let db = &server.app_state.db;
2679    let org_id = db.create_org("Test Org", "test-org").await.unwrap();
2680    db.add_org_member(org_id, client_a.current_user_id(&cx_a), false)
2681        .await
2682        .unwrap();
2683    db.add_org_member(org_id, client_b.current_user_id(&cx_b), false)
2684        .await
2685        .unwrap();
2686
2687    // Create a channel that includes all the users.
2688    let channel_id = db.create_org_channel(org_id, "test-channel").await.unwrap();
2689    db.add_channel_member(channel_id, client_a.current_user_id(&cx_a), false)
2690        .await
2691        .unwrap();
2692    db.add_channel_member(channel_id, client_b.current_user_id(&cx_b), false)
2693        .await
2694        .unwrap();
2695    db.create_channel_message(
2696        channel_id,
2697        client_b.current_user_id(&cx_b),
2698        "hello A, it's B.",
2699        OffsetDateTime::now_utc(),
2700        1,
2701    )
2702    .await
2703    .unwrap();
2704
2705    let channels_a =
2706        cx_a.add_model(|cx| ChannelList::new(client_a.user_store.clone(), client_a.clone(), cx));
2707    channels_a
2708        .condition(cx_a, |list, _| list.available_channels().is_some())
2709        .await;
2710    channels_a.read_with(cx_a, |list, _| {
2711        assert_eq!(
2712            list.available_channels().unwrap(),
2713            &[ChannelDetails {
2714                id: channel_id.to_proto(),
2715                name: "test-channel".to_string()
2716            }]
2717        )
2718    });
2719    let channel_a = channels_a.update(cx_a, |this, cx| {
2720        this.get_channel(channel_id.to_proto(), cx).unwrap()
2721    });
2722    channel_a.read_with(cx_a, |channel, _| assert!(channel.messages().is_empty()));
2723    channel_a
2724        .condition(&cx_a, |channel, _| {
2725            channel_messages(channel)
2726                == [("user_b".to_string(), "hello A, it's B.".to_string(), false)]
2727        })
2728        .await;
2729
2730    let channels_b =
2731        cx_b.add_model(|cx| ChannelList::new(client_b.user_store.clone(), client_b.clone(), cx));
2732    channels_b
2733        .condition(cx_b, |list, _| list.available_channels().is_some())
2734        .await;
2735    channels_b.read_with(cx_b, |list, _| {
2736        assert_eq!(
2737            list.available_channels().unwrap(),
2738            &[ChannelDetails {
2739                id: channel_id.to_proto(),
2740                name: "test-channel".to_string()
2741            }]
2742        )
2743    });
2744
2745    let channel_b = channels_b.update(cx_b, |this, cx| {
2746        this.get_channel(channel_id.to_proto(), cx).unwrap()
2747    });
2748    channel_b.read_with(cx_b, |channel, _| assert!(channel.messages().is_empty()));
2749    channel_b
2750        .condition(&cx_b, |channel, _| {
2751            channel_messages(channel)
2752                == [("user_b".to_string(), "hello A, it's B.".to_string(), false)]
2753        })
2754        .await;
2755
2756    channel_a
2757        .update(cx_a, |channel, cx| {
2758            channel
2759                .send_message("oh, hi B.".to_string(), cx)
2760                .unwrap()
2761                .detach();
2762            let task = channel.send_message("sup".to_string(), cx).unwrap();
2763            assert_eq!(
2764                channel_messages(channel),
2765                &[
2766                    ("user_b".to_string(), "hello A, it's B.".to_string(), false),
2767                    ("user_a".to_string(), "oh, hi B.".to_string(), true),
2768                    ("user_a".to_string(), "sup".to_string(), true)
2769                ]
2770            );
2771            task
2772        })
2773        .await
2774        .unwrap();
2775
2776    channel_b
2777        .condition(&cx_b, |channel, _| {
2778            channel_messages(channel)
2779                == [
2780                    ("user_b".to_string(), "hello A, it's B.".to_string(), false),
2781                    ("user_a".to_string(), "oh, hi B.".to_string(), false),
2782                    ("user_a".to_string(), "sup".to_string(), false),
2783                ]
2784        })
2785        .await;
2786
2787    assert_eq!(
2788        server
2789            .state()
2790            .await
2791            .channel(channel_id)
2792            .unwrap()
2793            .connection_ids
2794            .len(),
2795        2
2796    );
2797    cx_b.update(|_| drop(channel_b));
2798    server
2799        .condition(|state| state.channel(channel_id).unwrap().connection_ids.len() == 1)
2800        .await;
2801
2802    cx_a.update(|_| drop(channel_a));
2803    server
2804        .condition(|state| state.channel(channel_id).is_none())
2805        .await;
2806}
2807
2808#[gpui::test(iterations = 10)]
2809async fn test_chat_message_validation(cx_a: &mut TestAppContext) {
2810    cx_a.foreground().forbid_parking();
2811    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
2812    let client_a = server.create_client(cx_a, "user_a").await;
2813
2814    let db = &server.app_state.db;
2815    let org_id = db.create_org("Test Org", "test-org").await.unwrap();
2816    let channel_id = db.create_org_channel(org_id, "test-channel").await.unwrap();
2817    db.add_org_member(org_id, client_a.current_user_id(&cx_a), false)
2818        .await
2819        .unwrap();
2820    db.add_channel_member(channel_id, client_a.current_user_id(&cx_a), false)
2821        .await
2822        .unwrap();
2823
2824    let channels_a =
2825        cx_a.add_model(|cx| ChannelList::new(client_a.user_store.clone(), client_a.clone(), cx));
2826    channels_a
2827        .condition(cx_a, |list, _| list.available_channels().is_some())
2828        .await;
2829    let channel_a = channels_a.update(cx_a, |this, cx| {
2830        this.get_channel(channel_id.to_proto(), cx).unwrap()
2831    });
2832
2833    // Messages aren't allowed to be too long.
2834    channel_a
2835        .update(cx_a, |channel, cx| {
2836            let long_body = "this is long.\n".repeat(1024);
2837            channel.send_message(long_body, cx).unwrap()
2838        })
2839        .await
2840        .unwrap_err();
2841
2842    // Messages aren't allowed to be blank.
2843    channel_a.update(cx_a, |channel, cx| {
2844        channel.send_message(String::new(), cx).unwrap_err()
2845    });
2846
2847    // Leading and trailing whitespace are trimmed.
2848    channel_a
2849        .update(cx_a, |channel, cx| {
2850            channel
2851                .send_message("\n surrounded by whitespace  \n".to_string(), cx)
2852                .unwrap()
2853        })
2854        .await
2855        .unwrap();
2856    assert_eq!(
2857        db.get_channel_messages(channel_id, 10, None)
2858            .await
2859            .unwrap()
2860            .iter()
2861            .map(|m| &m.body)
2862            .collect::<Vec<_>>(),
2863        &["surrounded by whitespace"]
2864    );
2865}
2866
2867#[gpui::test(iterations = 10)]
2868async fn test_chat_reconnection(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
2869    cx_a.foreground().forbid_parking();
2870    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
2871    let client_a = server.create_client(cx_a, "user_a").await;
2872    let client_b = server.create_client(cx_b, "user_b").await;
2873
2874    let mut status_b = client_b.status();
2875
2876    // Create an org that includes these 2 users.
2877    let db = &server.app_state.db;
2878    let org_id = db.create_org("Test Org", "test-org").await.unwrap();
2879    db.add_org_member(org_id, client_a.current_user_id(&cx_a), false)
2880        .await
2881        .unwrap();
2882    db.add_org_member(org_id, client_b.current_user_id(&cx_b), false)
2883        .await
2884        .unwrap();
2885
2886    // Create a channel that includes all the users.
2887    let channel_id = db.create_org_channel(org_id, "test-channel").await.unwrap();
2888    db.add_channel_member(channel_id, client_a.current_user_id(&cx_a), false)
2889        .await
2890        .unwrap();
2891    db.add_channel_member(channel_id, client_b.current_user_id(&cx_b), false)
2892        .await
2893        .unwrap();
2894    db.create_channel_message(
2895        channel_id,
2896        client_b.current_user_id(&cx_b),
2897        "hello A, it's B.",
2898        OffsetDateTime::now_utc(),
2899        2,
2900    )
2901    .await
2902    .unwrap();
2903
2904    let channels_a =
2905        cx_a.add_model(|cx| ChannelList::new(client_a.user_store.clone(), client_a.clone(), cx));
2906    channels_a
2907        .condition(cx_a, |list, _| list.available_channels().is_some())
2908        .await;
2909
2910    channels_a.read_with(cx_a, |list, _| {
2911        assert_eq!(
2912            list.available_channels().unwrap(),
2913            &[ChannelDetails {
2914                id: channel_id.to_proto(),
2915                name: "test-channel".to_string()
2916            }]
2917        )
2918    });
2919    let channel_a = channels_a.update(cx_a, |this, cx| {
2920        this.get_channel(channel_id.to_proto(), cx).unwrap()
2921    });
2922    channel_a.read_with(cx_a, |channel, _| assert!(channel.messages().is_empty()));
2923    channel_a
2924        .condition(&cx_a, |channel, _| {
2925            channel_messages(channel)
2926                == [("user_b".to_string(), "hello A, it's B.".to_string(), false)]
2927        })
2928        .await;
2929
2930    let channels_b =
2931        cx_b.add_model(|cx| ChannelList::new(client_b.user_store.clone(), client_b.clone(), cx));
2932    channels_b
2933        .condition(cx_b, |list, _| list.available_channels().is_some())
2934        .await;
2935    channels_b.read_with(cx_b, |list, _| {
2936        assert_eq!(
2937            list.available_channels().unwrap(),
2938            &[ChannelDetails {
2939                id: channel_id.to_proto(),
2940                name: "test-channel".to_string()
2941            }]
2942        )
2943    });
2944
2945    let channel_b = channels_b.update(cx_b, |this, cx| {
2946        this.get_channel(channel_id.to_proto(), cx).unwrap()
2947    });
2948    channel_b.read_with(cx_b, |channel, _| assert!(channel.messages().is_empty()));
2949    channel_b
2950        .condition(&cx_b, |channel, _| {
2951            channel_messages(channel)
2952                == [("user_b".to_string(), "hello A, it's B.".to_string(), false)]
2953        })
2954        .await;
2955
2956    // Disconnect client B, ensuring we can still access its cached channel data.
2957    server.forbid_connections();
2958    server.disconnect_client(client_b.current_user_id(&cx_b));
2959    cx_b.foreground().advance_clock(rpc::RECEIVE_TIMEOUT);
2960    while !matches!(
2961        status_b.next().await,
2962        Some(client::Status::ReconnectionError { .. })
2963    ) {}
2964
2965    channels_b.read_with(cx_b, |channels, _| {
2966        assert_eq!(
2967            channels.available_channels().unwrap(),
2968            [ChannelDetails {
2969                id: channel_id.to_proto(),
2970                name: "test-channel".to_string()
2971            }]
2972        )
2973    });
2974    channel_b.read_with(cx_b, |channel, _| {
2975        assert_eq!(
2976            channel_messages(channel),
2977            [("user_b".to_string(), "hello A, it's B.".to_string(), false)]
2978        )
2979    });
2980
2981    // Send a message from client B while it is disconnected.
2982    channel_b
2983        .update(cx_b, |channel, cx| {
2984            let task = channel
2985                .send_message("can you see this?".to_string(), cx)
2986                .unwrap();
2987            assert_eq!(
2988                channel_messages(channel),
2989                &[
2990                    ("user_b".to_string(), "hello A, it's B.".to_string(), false),
2991                    ("user_b".to_string(), "can you see this?".to_string(), true)
2992                ]
2993            );
2994            task
2995        })
2996        .await
2997        .unwrap_err();
2998
2999    // Send a message from client A while B is disconnected.
3000    channel_a
3001        .update(cx_a, |channel, cx| {
3002            channel
3003                .send_message("oh, hi B.".to_string(), cx)
3004                .unwrap()
3005                .detach();
3006            let task = channel.send_message("sup".to_string(), cx).unwrap();
3007            assert_eq!(
3008                channel_messages(channel),
3009                &[
3010                    ("user_b".to_string(), "hello A, it's B.".to_string(), false),
3011                    ("user_a".to_string(), "oh, hi B.".to_string(), true),
3012                    ("user_a".to_string(), "sup".to_string(), true)
3013                ]
3014            );
3015            task
3016        })
3017        .await
3018        .unwrap();
3019
3020    // Give client B a chance to reconnect.
3021    server.allow_connections();
3022    cx_b.foreground().advance_clock(Duration::from_secs(10));
3023
3024    // Verify that B sees the new messages upon reconnection, as well as the message client B
3025    // sent while offline.
3026    channel_b
3027        .condition(&cx_b, |channel, _| {
3028            channel_messages(channel)
3029                == [
3030                    ("user_b".to_string(), "hello A, it's B.".to_string(), false),
3031                    ("user_a".to_string(), "oh, hi B.".to_string(), false),
3032                    ("user_a".to_string(), "sup".to_string(), false),
3033                    ("user_b".to_string(), "can you see this?".to_string(), false),
3034                ]
3035        })
3036        .await;
3037
3038    // Ensure client A and B can communicate normally after reconnection.
3039    channel_a
3040        .update(cx_a, |channel, cx| {
3041            channel.send_message("you online?".to_string(), cx).unwrap()
3042        })
3043        .await
3044        .unwrap();
3045    channel_b
3046        .condition(&cx_b, |channel, _| {
3047            channel_messages(channel)
3048                == [
3049                    ("user_b".to_string(), "hello A, it's B.".to_string(), false),
3050                    ("user_a".to_string(), "oh, hi B.".to_string(), false),
3051                    ("user_a".to_string(), "sup".to_string(), false),
3052                    ("user_b".to_string(), "can you see this?".to_string(), false),
3053                    ("user_a".to_string(), "you online?".to_string(), false),
3054                ]
3055        })
3056        .await;
3057
3058    channel_b
3059        .update(cx_b, |channel, cx| {
3060            channel.send_message("yep".to_string(), cx).unwrap()
3061        })
3062        .await
3063        .unwrap();
3064    channel_a
3065        .condition(&cx_a, |channel, _| {
3066            channel_messages(channel)
3067                == [
3068                    ("user_b".to_string(), "hello A, it's B.".to_string(), false),
3069                    ("user_a".to_string(), "oh, hi B.".to_string(), false),
3070                    ("user_a".to_string(), "sup".to_string(), false),
3071                    ("user_b".to_string(), "can you see this?".to_string(), false),
3072                    ("user_a".to_string(), "you online?".to_string(), false),
3073                    ("user_b".to_string(), "yep".to_string(), false),
3074                ]
3075        })
3076        .await;
3077}
3078
3079#[gpui::test(iterations = 10)]
3080async fn test_contacts(
3081    deterministic: Arc<Deterministic>,
3082    cx_a: &mut TestAppContext,
3083    cx_b: &mut TestAppContext,
3084    cx_c: &mut TestAppContext,
3085) {
3086    cx_a.foreground().forbid_parking();
3087    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
3088    let client_a = server.create_client(cx_a, "user_a").await;
3089    let client_b = server.create_client(cx_b, "user_b").await;
3090    let client_c = server.create_client(cx_c, "user_c").await;
3091    server
3092        .make_contacts(vec![
3093            (&client_a, cx_a),
3094            (&client_b, cx_b),
3095            (&client_c, cx_c),
3096        ])
3097        .await;
3098
3099    deterministic.run_until_parked();
3100    for (client, cx) in [(&client_a, &cx_a), (&client_b, &cx_b), (&client_c, &cx_c)] {
3101        client.user_store.read_with(*cx, |store, _| {
3102            assert_eq!(
3103                contacts(store),
3104                [
3105                    ("user_a", true, vec![]),
3106                    ("user_b", true, vec![]),
3107                    ("user_c", true, vec![])
3108                ],
3109                "{} has the wrong contacts",
3110                client.username
3111            )
3112        });
3113    }
3114
3115    // Share a project as client A.
3116    client_a.fs.create_dir(Path::new("/a")).await.unwrap();
3117    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
3118
3119    deterministic.run_until_parked();
3120    for (client, cx) in [(&client_a, &cx_a), (&client_b, &cx_b), (&client_c, &cx_c)] {
3121        client.user_store.read_with(*cx, |store, _| {
3122            assert_eq!(
3123                contacts(store),
3124                [
3125                    ("user_a", true, vec![("a", vec![])]),
3126                    ("user_b", true, vec![]),
3127                    ("user_c", true, vec![])
3128                ],
3129                "{} has the wrong contacts",
3130                client.username
3131            )
3132        });
3133    }
3134
3135    let _project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
3136
3137    deterministic.run_until_parked();
3138    for (client, cx) in [(&client_a, &cx_a), (&client_b, &cx_b), (&client_c, &cx_c)] {
3139        client.user_store.read_with(*cx, |store, _| {
3140            assert_eq!(
3141                contacts(store),
3142                [
3143                    ("user_a", true, vec![("a", vec!["user_b"])]),
3144                    ("user_b", true, vec![]),
3145                    ("user_c", true, vec![])
3146                ],
3147                "{} has the wrong contacts",
3148                client.username
3149            )
3150        });
3151    }
3152
3153    // Add a local project as client B
3154    client_a.fs.create_dir("/b".as_ref()).await.unwrap();
3155    let (_project_b, _) = client_b.build_local_project("/b", cx_b).await;
3156
3157    deterministic.run_until_parked();
3158    for (client, cx) in [(&client_a, &cx_a), (&client_b, &cx_b), (&client_c, &cx_c)] {
3159        client.user_store.read_with(*cx, |store, _| {
3160            assert_eq!(
3161                contacts(store),
3162                [
3163                    ("user_a", true, vec![("a", vec!["user_b"])]),
3164                    ("user_b", true, vec![("b", vec![])]),
3165                    ("user_c", true, vec![])
3166                ],
3167                "{} has the wrong contacts",
3168                client.username
3169            )
3170        });
3171    }
3172
3173    project_a
3174        .condition(&cx_a, |project, _| {
3175            project.collaborators().contains_key(&client_b.peer_id)
3176        })
3177        .await;
3178
3179    cx_a.update(move |_| drop(project_a));
3180    deterministic.run_until_parked();
3181    for (client, cx) in [(&client_a, &cx_a), (&client_b, &cx_b), (&client_c, &cx_c)] {
3182        client.user_store.read_with(*cx, |store, _| {
3183            assert_eq!(
3184                contacts(store),
3185                [
3186                    ("user_a", true, vec![]),
3187                    ("user_b", true, vec![("b", vec![])]),
3188                    ("user_c", true, vec![])
3189                ],
3190                "{} has the wrong contacts",
3191                client.username
3192            )
3193        });
3194    }
3195
3196    server.disconnect_client(client_c.current_user_id(cx_c));
3197    server.forbid_connections();
3198    deterministic.advance_clock(rpc::RECEIVE_TIMEOUT);
3199    for (client, cx) in [(&client_a, &cx_a), (&client_b, &cx_b)] {
3200        client.user_store.read_with(*cx, |store, _| {
3201            assert_eq!(
3202                contacts(store),
3203                [
3204                    ("user_a", true, vec![]),
3205                    ("user_b", true, vec![("b", vec![])]),
3206                    ("user_c", false, vec![])
3207                ],
3208                "{} has the wrong contacts",
3209                client.username
3210            )
3211        });
3212    }
3213    client_c
3214        .user_store
3215        .read_with(cx_c, |store, _| assert_eq!(contacts(store), []));
3216
3217    server.allow_connections();
3218    client_c
3219        .authenticate_and_connect(false, &cx_c.to_async())
3220        .await
3221        .unwrap();
3222
3223    deterministic.run_until_parked();
3224    for (client, cx) in [(&client_a, &cx_a), (&client_b, &cx_b), (&client_c, &cx_c)] {
3225        client.user_store.read_with(*cx, |store, _| {
3226            assert_eq!(
3227                contacts(store),
3228                [
3229                    ("user_a", true, vec![]),
3230                    ("user_b", true, vec![("b", vec![])]),
3231                    ("user_c", true, vec![])
3232                ],
3233                "{} has the wrong contacts",
3234                client.username
3235            )
3236        });
3237    }
3238
3239    fn contacts(user_store: &UserStore) -> Vec<(&str, bool, Vec<(&str, Vec<&str>)>)> {
3240        user_store
3241            .contacts()
3242            .iter()
3243            .map(|contact| {
3244                let projects = contact
3245                    .projects
3246                    .iter()
3247                    .map(|p| {
3248                        (
3249                            p.worktree_root_names[0].as_str(),
3250                            p.guests.iter().map(|p| p.github_login.as_str()).collect(),
3251                        )
3252                    })
3253                    .collect();
3254                (contact.user.github_login.as_str(), contact.online, projects)
3255            })
3256            .collect()
3257    }
3258}
3259
3260#[gpui::test(iterations = 10)]
3261async fn test_contact_requests(
3262    executor: Arc<Deterministic>,
3263    cx_a: &mut TestAppContext,
3264    cx_a2: &mut TestAppContext,
3265    cx_b: &mut TestAppContext,
3266    cx_b2: &mut TestAppContext,
3267    cx_c: &mut TestAppContext,
3268    cx_c2: &mut TestAppContext,
3269) {
3270    cx_a.foreground().forbid_parking();
3271
3272    // Connect to a server as 3 clients.
3273    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
3274    let client_a = server.create_client(cx_a, "user_a").await;
3275    let client_a2 = server.create_client(cx_a2, "user_a").await;
3276    let client_b = server.create_client(cx_b, "user_b").await;
3277    let client_b2 = server.create_client(cx_b2, "user_b").await;
3278    let client_c = server.create_client(cx_c, "user_c").await;
3279    let client_c2 = server.create_client(cx_c2, "user_c").await;
3280
3281    assert_eq!(client_a.user_id().unwrap(), client_a2.user_id().unwrap());
3282    assert_eq!(client_b.user_id().unwrap(), client_b2.user_id().unwrap());
3283    assert_eq!(client_c.user_id().unwrap(), client_c2.user_id().unwrap());
3284
3285    // User A and User C request that user B become their contact.
3286    client_a
3287        .user_store
3288        .update(cx_a, |store, cx| {
3289            store.request_contact(client_b.user_id().unwrap(), cx)
3290        })
3291        .await
3292        .unwrap();
3293    client_c
3294        .user_store
3295        .update(cx_c, |store, cx| {
3296            store.request_contact(client_b.user_id().unwrap(), cx)
3297        })
3298        .await
3299        .unwrap();
3300    executor.run_until_parked();
3301
3302    // All users see the pending request appear in all their clients.
3303    assert_eq!(
3304        client_a.summarize_contacts(&cx_a).outgoing_requests,
3305        &["user_b"]
3306    );
3307    assert_eq!(
3308        client_a2.summarize_contacts(&cx_a2).outgoing_requests,
3309        &["user_b"]
3310    );
3311    assert_eq!(
3312        client_b.summarize_contacts(&cx_b).incoming_requests,
3313        &["user_a", "user_c"]
3314    );
3315    assert_eq!(
3316        client_b2.summarize_contacts(&cx_b2).incoming_requests,
3317        &["user_a", "user_c"]
3318    );
3319    assert_eq!(
3320        client_c.summarize_contacts(&cx_c).outgoing_requests,
3321        &["user_b"]
3322    );
3323    assert_eq!(
3324        client_c2.summarize_contacts(&cx_c2).outgoing_requests,
3325        &["user_b"]
3326    );
3327
3328    // Contact requests are present upon connecting (tested here via disconnect/reconnect)
3329    disconnect_and_reconnect(&client_a, cx_a).await;
3330    disconnect_and_reconnect(&client_b, cx_b).await;
3331    disconnect_and_reconnect(&client_c, cx_c).await;
3332    executor.run_until_parked();
3333    assert_eq!(
3334        client_a.summarize_contacts(&cx_a).outgoing_requests,
3335        &["user_b"]
3336    );
3337    assert_eq!(
3338        client_b.summarize_contacts(&cx_b).incoming_requests,
3339        &["user_a", "user_c"]
3340    );
3341    assert_eq!(
3342        client_c.summarize_contacts(&cx_c).outgoing_requests,
3343        &["user_b"]
3344    );
3345
3346    // User B accepts the request from user A.
3347    client_b
3348        .user_store
3349        .update(cx_b, |store, cx| {
3350            store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
3351        })
3352        .await
3353        .unwrap();
3354
3355    executor.run_until_parked();
3356
3357    // User B sees user A as their contact now in all client, and the incoming request from them is removed.
3358    let contacts_b = client_b.summarize_contacts(&cx_b);
3359    assert_eq!(contacts_b.current, &["user_a", "user_b"]);
3360    assert_eq!(contacts_b.incoming_requests, &["user_c"]);
3361    let contacts_b2 = client_b2.summarize_contacts(&cx_b2);
3362    assert_eq!(contacts_b2.current, &["user_a", "user_b"]);
3363    assert_eq!(contacts_b2.incoming_requests, &["user_c"]);
3364
3365    // User A sees user B as their contact now in all clients, and the outgoing request to them is removed.
3366    let contacts_a = client_a.summarize_contacts(&cx_a);
3367    assert_eq!(contacts_a.current, &["user_a", "user_b"]);
3368    assert!(contacts_a.outgoing_requests.is_empty());
3369    let contacts_a2 = client_a2.summarize_contacts(&cx_a2);
3370    assert_eq!(contacts_a2.current, &["user_a", "user_b"]);
3371    assert!(contacts_a2.outgoing_requests.is_empty());
3372
3373    // Contacts are present upon connecting (tested here via disconnect/reconnect)
3374    disconnect_and_reconnect(&client_a, cx_a).await;
3375    disconnect_and_reconnect(&client_b, cx_b).await;
3376    disconnect_and_reconnect(&client_c, cx_c).await;
3377    executor.run_until_parked();
3378    assert_eq!(
3379        client_a.summarize_contacts(&cx_a).current,
3380        &["user_a", "user_b"]
3381    );
3382    assert_eq!(
3383        client_b.summarize_contacts(&cx_b).current,
3384        &["user_a", "user_b"]
3385    );
3386    assert_eq!(
3387        client_b.summarize_contacts(&cx_b).incoming_requests,
3388        &["user_c"]
3389    );
3390    assert_eq!(client_c.summarize_contacts(&cx_c).current, &["user_c"]);
3391    assert_eq!(
3392        client_c.summarize_contacts(&cx_c).outgoing_requests,
3393        &["user_b"]
3394    );
3395
3396    // User B rejects the request from user C.
3397    client_b
3398        .user_store
3399        .update(cx_b, |store, cx| {
3400            store.respond_to_contact_request(client_c.user_id().unwrap(), false, cx)
3401        })
3402        .await
3403        .unwrap();
3404
3405    executor.run_until_parked();
3406
3407    // User B doesn't see user C as their contact, and the incoming request from them is removed.
3408    let contacts_b = client_b.summarize_contacts(&cx_b);
3409    assert_eq!(contacts_b.current, &["user_a", "user_b"]);
3410    assert!(contacts_b.incoming_requests.is_empty());
3411    let contacts_b2 = client_b2.summarize_contacts(&cx_b2);
3412    assert_eq!(contacts_b2.current, &["user_a", "user_b"]);
3413    assert!(contacts_b2.incoming_requests.is_empty());
3414
3415    // User C doesn't see user B as their contact, and the outgoing request to them is removed.
3416    let contacts_c = client_c.summarize_contacts(&cx_c);
3417    assert_eq!(contacts_c.current, &["user_c"]);
3418    assert!(contacts_c.outgoing_requests.is_empty());
3419    let contacts_c2 = client_c2.summarize_contacts(&cx_c2);
3420    assert_eq!(contacts_c2.current, &["user_c"]);
3421    assert!(contacts_c2.outgoing_requests.is_empty());
3422
3423    // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect)
3424    disconnect_and_reconnect(&client_a, cx_a).await;
3425    disconnect_and_reconnect(&client_b, cx_b).await;
3426    disconnect_and_reconnect(&client_c, cx_c).await;
3427    executor.run_until_parked();
3428    assert_eq!(
3429        client_a.summarize_contacts(&cx_a).current,
3430        &["user_a", "user_b"]
3431    );
3432    assert_eq!(
3433        client_b.summarize_contacts(&cx_b).current,
3434        &["user_a", "user_b"]
3435    );
3436    assert!(client_b
3437        .summarize_contacts(&cx_b)
3438        .incoming_requests
3439        .is_empty());
3440    assert_eq!(client_c.summarize_contacts(&cx_c).current, &["user_c"]);
3441    assert!(client_c
3442        .summarize_contacts(&cx_c)
3443        .outgoing_requests
3444        .is_empty());
3445
3446    async fn disconnect_and_reconnect(client: &TestClient, cx: &mut TestAppContext) {
3447        client.disconnect(&cx.to_async()).unwrap();
3448        client.clear_contacts(cx).await;
3449        client
3450            .authenticate_and_connect(false, &cx.to_async())
3451            .await
3452            .unwrap();
3453    }
3454}
3455
3456#[gpui::test(iterations = 10)]
3457async fn test_following(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
3458    cx_a.foreground().forbid_parking();
3459    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
3460    let client_a = server.create_client(cx_a, "user_a").await;
3461    let client_b = server.create_client(cx_b, "user_b").await;
3462    server
3463        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
3464        .await;
3465    cx_a.update(editor::init);
3466    cx_b.update(editor::init);
3467
3468    client_a
3469        .fs
3470        .insert_tree(
3471            "/a",
3472            json!({
3473                "1.txt": "one",
3474                "2.txt": "two",
3475                "3.txt": "three",
3476            }),
3477        )
3478        .await;
3479    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3480
3481    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
3482
3483    // Client A opens some editors.
3484    let workspace_a = client_a.build_workspace(&project_a, cx_a);
3485    let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
3486    let editor_a1 = workspace_a
3487        .update(cx_a, |workspace, cx| {
3488            workspace.open_path((worktree_id, "1.txt"), true, cx)
3489        })
3490        .await
3491        .unwrap()
3492        .downcast::<Editor>()
3493        .unwrap();
3494    let editor_a2 = workspace_a
3495        .update(cx_a, |workspace, cx| {
3496            workspace.open_path((worktree_id, "2.txt"), true, cx)
3497        })
3498        .await
3499        .unwrap()
3500        .downcast::<Editor>()
3501        .unwrap();
3502
3503    // Client B opens an editor.
3504    let workspace_b = client_b.build_workspace(&project_b, cx_b);
3505    let editor_b1 = workspace_b
3506        .update(cx_b, |workspace, cx| {
3507            workspace.open_path((worktree_id, "1.txt"), true, cx)
3508        })
3509        .await
3510        .unwrap()
3511        .downcast::<Editor>()
3512        .unwrap();
3513
3514    let client_a_id = project_b.read_with(cx_b, |project, _| {
3515        project.collaborators().values().next().unwrap().peer_id
3516    });
3517    let client_b_id = project_a.read_with(cx_a, |project, _| {
3518        project.collaborators().values().next().unwrap().peer_id
3519    });
3520
3521    // When client B starts following client A, all visible view states are replicated to client B.
3522    editor_a1.update(cx_a, |editor, cx| {
3523        editor.change_selections(None, cx, |s| s.select_ranges([0..1]))
3524    });
3525    editor_a2.update(cx_a, |editor, cx| {
3526        editor.change_selections(None, cx, |s| s.select_ranges([2..3]))
3527    });
3528    workspace_b
3529        .update(cx_b, |workspace, cx| {
3530            workspace
3531                .toggle_follow(&ToggleFollow(client_a_id), cx)
3532                .unwrap()
3533        })
3534        .await
3535        .unwrap();
3536
3537    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
3538        workspace
3539            .active_item(cx)
3540            .unwrap()
3541            .downcast::<Editor>()
3542            .unwrap()
3543    });
3544    assert!(cx_b.read(|cx| editor_b2.is_focused(cx)));
3545    assert_eq!(
3546        editor_b2.read_with(cx_b, |editor, cx| editor.project_path(cx)),
3547        Some((worktree_id, "2.txt").into())
3548    );
3549    assert_eq!(
3550        editor_b2.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
3551        vec![2..3]
3552    );
3553    assert_eq!(
3554        editor_b1.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
3555        vec![0..1]
3556    );
3557
3558    // When client A activates a different editor, client B does so as well.
3559    workspace_a.update(cx_a, |workspace, cx| {
3560        workspace.activate_item(&editor_a1, cx)
3561    });
3562    workspace_b
3563        .condition(cx_b, |workspace, cx| {
3564            workspace.active_item(cx).unwrap().id() == editor_b1.id()
3565        })
3566        .await;
3567
3568    // When client A navigates back and forth, client B does so as well.
3569    workspace_a
3570        .update(cx_a, |workspace, cx| {
3571            workspace::Pane::go_back(workspace, None, cx)
3572        })
3573        .await;
3574    workspace_b
3575        .condition(cx_b, |workspace, cx| {
3576            workspace.active_item(cx).unwrap().id() == editor_b2.id()
3577        })
3578        .await;
3579
3580    workspace_a
3581        .update(cx_a, |workspace, cx| {
3582            workspace::Pane::go_forward(workspace, None, cx)
3583        })
3584        .await;
3585    workspace_b
3586        .condition(cx_b, |workspace, cx| {
3587            workspace.active_item(cx).unwrap().id() == editor_b1.id()
3588        })
3589        .await;
3590
3591    // Changes to client A's editor are reflected on client B.
3592    editor_a1.update(cx_a, |editor, cx| {
3593        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2]));
3594    });
3595    editor_b1
3596        .condition(cx_b, |editor, cx| {
3597            editor.selections.ranges(cx) == vec![1..1, 2..2]
3598        })
3599        .await;
3600
3601    editor_a1.update(cx_a, |editor, cx| editor.set_text("TWO", cx));
3602    editor_b1
3603        .condition(cx_b, |editor, cx| editor.text(cx) == "TWO")
3604        .await;
3605
3606    editor_a1.update(cx_a, |editor, cx| {
3607        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
3608        editor.set_scroll_position(vec2f(0., 100.), cx);
3609    });
3610    editor_b1
3611        .condition(cx_b, |editor, cx| {
3612            editor.selections.ranges(cx) == vec![3..3]
3613        })
3614        .await;
3615
3616    // After unfollowing, client B stops receiving updates from client A.
3617    workspace_b.update(cx_b, |workspace, cx| {
3618        workspace.unfollow(&workspace.active_pane().clone(), cx)
3619    });
3620    workspace_a.update(cx_a, |workspace, cx| {
3621        workspace.activate_item(&editor_a2, cx)
3622    });
3623    cx_a.foreground().run_until_parked();
3624    assert_eq!(
3625        workspace_b.read_with(cx_b, |workspace, cx| workspace
3626            .active_item(cx)
3627            .unwrap()
3628            .id()),
3629        editor_b1.id()
3630    );
3631
3632    // Client A starts following client B.
3633    workspace_a
3634        .update(cx_a, |workspace, cx| {
3635            workspace
3636                .toggle_follow(&ToggleFollow(client_b_id), cx)
3637                .unwrap()
3638        })
3639        .await
3640        .unwrap();
3641    assert_eq!(
3642        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
3643        Some(client_b_id)
3644    );
3645    assert_eq!(
3646        workspace_a.read_with(cx_a, |workspace, cx| workspace
3647            .active_item(cx)
3648            .unwrap()
3649            .id()),
3650        editor_a1.id()
3651    );
3652
3653    // Following interrupts when client B disconnects.
3654    client_b.disconnect(&cx_b.to_async()).unwrap();
3655    cx_a.foreground().run_until_parked();
3656    assert_eq!(
3657        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
3658        None
3659    );
3660}
3661
3662#[gpui::test(iterations = 10)]
3663async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
3664    cx_a.foreground().forbid_parking();
3665    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
3666    let client_a = server.create_client(cx_a, "user_a").await;
3667    let client_b = server.create_client(cx_b, "user_b").await;
3668    server
3669        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
3670        .await;
3671    cx_a.update(editor::init);
3672    cx_b.update(editor::init);
3673
3674    // Client A shares a project.
3675    client_a
3676        .fs
3677        .insert_tree(
3678            "/a",
3679            json!({
3680                "1.txt": "one",
3681                "2.txt": "two",
3682                "3.txt": "three",
3683                "4.txt": "four",
3684            }),
3685        )
3686        .await;
3687    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3688
3689    // Client B joins the project.
3690    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
3691
3692    // Client A opens some editors.
3693    let workspace_a = client_a.build_workspace(&project_a, cx_a);
3694    let pane_a1 = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
3695    let _editor_a1 = workspace_a
3696        .update(cx_a, |workspace, cx| {
3697            workspace.open_path((worktree_id, "1.txt"), true, cx)
3698        })
3699        .await
3700        .unwrap()
3701        .downcast::<Editor>()
3702        .unwrap();
3703
3704    // Client B opens an editor.
3705    let workspace_b = client_b.build_workspace(&project_b, cx_b);
3706    let pane_b1 = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
3707    let _editor_b1 = workspace_b
3708        .update(cx_b, |workspace, cx| {
3709            workspace.open_path((worktree_id, "2.txt"), true, cx)
3710        })
3711        .await
3712        .unwrap()
3713        .downcast::<Editor>()
3714        .unwrap();
3715
3716    // Clients A and B follow each other in split panes
3717    workspace_a.update(cx_a, |workspace, cx| {
3718        workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
3719        assert_ne!(*workspace.active_pane(), pane_a1);
3720    });
3721    workspace_a
3722        .update(cx_a, |workspace, cx| {
3723            let leader_id = *project_a.read(cx).collaborators().keys().next().unwrap();
3724            workspace
3725                .toggle_follow(&workspace::ToggleFollow(leader_id), cx)
3726                .unwrap()
3727        })
3728        .await
3729        .unwrap();
3730    workspace_b.update(cx_b, |workspace, cx| {
3731        workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
3732        assert_ne!(*workspace.active_pane(), pane_b1);
3733    });
3734    workspace_b
3735        .update(cx_b, |workspace, cx| {
3736            let leader_id = *project_b.read(cx).collaborators().keys().next().unwrap();
3737            workspace
3738                .toggle_follow(&workspace::ToggleFollow(leader_id), cx)
3739                .unwrap()
3740        })
3741        .await
3742        .unwrap();
3743
3744    workspace_a
3745        .update(cx_a, |workspace, cx| {
3746            workspace.activate_next_pane(cx);
3747            assert_eq!(*workspace.active_pane(), pane_a1);
3748            workspace.open_path((worktree_id, "3.txt"), true, cx)
3749        })
3750        .await
3751        .unwrap();
3752    workspace_b
3753        .update(cx_b, |workspace, cx| {
3754            workspace.activate_next_pane(cx);
3755            assert_eq!(*workspace.active_pane(), pane_b1);
3756            workspace.open_path((worktree_id, "4.txt"), true, cx)
3757        })
3758        .await
3759        .unwrap();
3760    cx_a.foreground().run_until_parked();
3761
3762    // Ensure leader updates don't change the active pane of followers
3763    workspace_a.read_with(cx_a, |workspace, _| {
3764        assert_eq!(*workspace.active_pane(), pane_a1);
3765    });
3766    workspace_b.read_with(cx_b, |workspace, _| {
3767        assert_eq!(*workspace.active_pane(), pane_b1);
3768    });
3769
3770    // Ensure peers following each other doesn't cause an infinite loop.
3771    assert_eq!(
3772        workspace_a.read_with(cx_a, |workspace, cx| workspace
3773            .active_item(cx)
3774            .unwrap()
3775            .project_path(cx)),
3776        Some((worktree_id, "3.txt").into())
3777    );
3778    workspace_a.update(cx_a, |workspace, cx| {
3779        assert_eq!(
3780            workspace.active_item(cx).unwrap().project_path(cx),
3781            Some((worktree_id, "3.txt").into())
3782        );
3783        workspace.activate_next_pane(cx);
3784        assert_eq!(
3785            workspace.active_item(cx).unwrap().project_path(cx),
3786            Some((worktree_id, "4.txt").into())
3787        );
3788    });
3789    workspace_b.update(cx_b, |workspace, cx| {
3790        assert_eq!(
3791            workspace.active_item(cx).unwrap().project_path(cx),
3792            Some((worktree_id, "4.txt").into())
3793        );
3794        workspace.activate_next_pane(cx);
3795        assert_eq!(
3796            workspace.active_item(cx).unwrap().project_path(cx),
3797            Some((worktree_id, "3.txt").into())
3798        );
3799    });
3800}
3801
3802#[gpui::test(iterations = 10)]
3803async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
3804    cx_a.foreground().forbid_parking();
3805
3806    // 2 clients connect to a server.
3807    let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
3808    let client_a = server.create_client(cx_a, "user_a").await;
3809    let client_b = server.create_client(cx_b, "user_b").await;
3810    server
3811        .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
3812        .await;
3813    cx_a.update(editor::init);
3814    cx_b.update(editor::init);
3815
3816    // Client A shares a project.
3817    client_a
3818        .fs
3819        .insert_tree(
3820            "/a",
3821            json!({
3822                "1.txt": "one",
3823                "2.txt": "two",
3824                "3.txt": "three",
3825            }),
3826        )
3827        .await;
3828    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3829    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
3830
3831    // Client A opens some editors.
3832    let workspace_a = client_a.build_workspace(&project_a, cx_a);
3833    let _editor_a1 = workspace_a
3834        .update(cx_a, |workspace, cx| {
3835            workspace.open_path((worktree_id, "1.txt"), true, cx)
3836        })
3837        .await
3838        .unwrap()
3839        .downcast::<Editor>()
3840        .unwrap();
3841
3842    // Client B starts following client A.
3843    let workspace_b = client_b.build_workspace(&project_b, cx_b);
3844    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
3845    let leader_id = project_b.read_with(cx_b, |project, _| {
3846        project.collaborators().values().next().unwrap().peer_id
3847    });
3848    workspace_b
3849        .update(cx_b, |workspace, cx| {
3850            workspace
3851                .toggle_follow(&ToggleFollow(leader_id), cx)
3852                .unwrap()
3853        })
3854        .await
3855        .unwrap();
3856    assert_eq!(
3857        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
3858        Some(leader_id)
3859    );
3860    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
3861        workspace
3862            .active_item(cx)
3863            .unwrap()
3864            .downcast::<Editor>()
3865            .unwrap()
3866    });
3867
3868    // When client B moves, it automatically stops following client A.
3869    editor_b2.update(cx_b, |editor, cx| editor.move_right(&editor::MoveRight, cx));
3870    assert_eq!(
3871        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
3872        None
3873    );
3874
3875    workspace_b
3876        .update(cx_b, |workspace, cx| {
3877            workspace
3878                .toggle_follow(&ToggleFollow(leader_id), cx)
3879                .unwrap()
3880        })
3881        .await
3882        .unwrap();
3883    assert_eq!(
3884        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
3885        Some(leader_id)
3886    );
3887
3888    // When client B edits, it automatically stops following client A.
3889    editor_b2.update(cx_b, |editor, cx| editor.insert("X", cx));
3890    assert_eq!(
3891        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
3892        None
3893    );
3894
3895    workspace_b
3896        .update(cx_b, |workspace, cx| {
3897            workspace
3898                .toggle_follow(&ToggleFollow(leader_id), cx)
3899                .unwrap()
3900        })
3901        .await
3902        .unwrap();
3903    assert_eq!(
3904        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
3905        Some(leader_id)
3906    );
3907
3908    // When client B scrolls, it automatically stops following client A.
3909    editor_b2.update(cx_b, |editor, cx| {
3910        editor.set_scroll_position(vec2f(0., 3.), cx)
3911    });
3912    assert_eq!(
3913        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
3914        None
3915    );
3916
3917    workspace_b
3918        .update(cx_b, |workspace, cx| {
3919            workspace
3920                .toggle_follow(&ToggleFollow(leader_id), cx)
3921                .unwrap()
3922        })
3923        .await
3924        .unwrap();
3925    assert_eq!(
3926        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
3927        Some(leader_id)
3928    );
3929
3930    // When client B activates a different pane, it continues following client A in the original pane.
3931    workspace_b.update(cx_b, |workspace, cx| {
3932        workspace.split_pane(pane_b.clone(), SplitDirection::Right, cx)
3933    });
3934    assert_eq!(
3935        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
3936        Some(leader_id)
3937    );
3938
3939    workspace_b.update(cx_b, |workspace, cx| workspace.activate_next_pane(cx));
3940    assert_eq!(
3941        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
3942        Some(leader_id)
3943    );
3944
3945    // When client B activates a different item in the original pane, it automatically stops following client A.
3946    workspace_b
3947        .update(cx_b, |workspace, cx| {
3948            workspace.open_path((worktree_id, "2.txt"), true, cx)
3949        })
3950        .await
3951        .unwrap();
3952    assert_eq!(
3953        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
3954        None
3955    );
3956}
3957
3958#[gpui::test(iterations = 100)]
3959async fn test_random_collaboration(
3960    cx: &mut TestAppContext,
3961    deterministic: Arc<Deterministic>,
3962    rng: StdRng,
3963) {
3964    cx.foreground().forbid_parking();
3965    let max_peers = env::var("MAX_PEERS")
3966        .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
3967        .unwrap_or(5);
3968    assert!(max_peers <= 5);
3969
3970    let max_operations = env::var("OPERATIONS")
3971        .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
3972        .unwrap_or(10);
3973
3974    let rng = Arc::new(Mutex::new(rng));
3975
3976    let guest_lang_registry = Arc::new(LanguageRegistry::test());
3977    let host_language_registry = Arc::new(LanguageRegistry::test());
3978
3979    let fs = FakeFs::new(cx.background());
3980    fs.insert_tree("/_collab", json!({"init": ""})).await;
3981
3982    let mut server = TestServer::start(cx.foreground(), cx.background()).await;
3983    let db = server.app_state.db.clone();
3984    let host_user_id = db.create_user("host", None, false).await.unwrap();
3985    for username in ["guest-1", "guest-2", "guest-3", "guest-4"] {
3986        let guest_user_id = db.create_user(username, None, false).await.unwrap();
3987        server
3988            .app_state
3989            .db
3990            .send_contact_request(guest_user_id, host_user_id)
3991            .await
3992            .unwrap();
3993        server
3994            .app_state
3995            .db
3996            .respond_to_contact_request(host_user_id, guest_user_id, true)
3997            .await
3998            .unwrap();
3999    }
4000
4001    let mut clients = Vec::new();
4002    let mut user_ids = Vec::new();
4003    let mut op_start_signals = Vec::new();
4004
4005    let mut next_entity_id = 100000;
4006    let mut host_cx = TestAppContext::new(
4007        cx.foreground_platform(),
4008        cx.platform(),
4009        deterministic.build_foreground(next_entity_id),
4010        deterministic.build_background(),
4011        cx.font_cache(),
4012        cx.leak_detector(),
4013        next_entity_id,
4014    );
4015    let host = server.create_client(&mut host_cx, "host").await;
4016    let host_project = host_cx.update(|cx| {
4017        Project::local(
4018            true,
4019            host.client.clone(),
4020            host.user_store.clone(),
4021            host.project_store.clone(),
4022            host_language_registry.clone(),
4023            fs.clone(),
4024            cx,
4025        )
4026    });
4027    let host_project_id = host_project
4028        .update(&mut host_cx, |p, _| p.next_remote_id())
4029        .await;
4030
4031    let (collab_worktree, _) = host_project
4032        .update(&mut host_cx, |project, cx| {
4033            project.find_or_create_local_worktree("/_collab", true, cx)
4034        })
4035        .await
4036        .unwrap();
4037    collab_worktree
4038        .read_with(&host_cx, |tree, _| tree.as_local().unwrap().scan_complete())
4039        .await;
4040
4041    // Set up fake language servers.
4042    let mut language = Language::new(
4043        LanguageConfig {
4044            name: "Rust".into(),
4045            path_suffixes: vec!["rs".to_string()],
4046            ..Default::default()
4047        },
4048        None,
4049    );
4050    let _fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
4051        name: "the-fake-language-server",
4052        capabilities: lsp::LanguageServer::full_capabilities(),
4053        initializer: Some(Box::new({
4054            let rng = rng.clone();
4055            let fs = fs.clone();
4056            let project = host_project.downgrade();
4057            move |fake_server: &mut FakeLanguageServer| {
4058                fake_server.handle_request::<lsp::request::Completion, _, _>(|_, _| async move {
4059                    Ok(Some(lsp::CompletionResponse::Array(vec![
4060                        lsp::CompletionItem {
4061                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
4062                                range: lsp::Range::new(
4063                                    lsp::Position::new(0, 0),
4064                                    lsp::Position::new(0, 0),
4065                                ),
4066                                new_text: "the-new-text".to_string(),
4067                            })),
4068                            ..Default::default()
4069                        },
4070                    ])))
4071                });
4072
4073                fake_server.handle_request::<lsp::request::CodeActionRequest, _, _>(
4074                    |_, _| async move {
4075                        Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
4076                            lsp::CodeAction {
4077                                title: "the-code-action".to_string(),
4078                                ..Default::default()
4079                            },
4080                        )]))
4081                    },
4082                );
4083
4084                fake_server.handle_request::<lsp::request::PrepareRenameRequest, _, _>(
4085                    |params, _| async move {
4086                        Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
4087                            params.position,
4088                            params.position,
4089                        ))))
4090                    },
4091                );
4092
4093                fake_server.handle_request::<lsp::request::GotoDefinition, _, _>({
4094                    let fs = fs.clone();
4095                    let rng = rng.clone();
4096                    move |_, _| {
4097                        let fs = fs.clone();
4098                        let rng = rng.clone();
4099                        async move {
4100                            let files = fs.files().await;
4101                            let mut rng = rng.lock();
4102                            let count = rng.gen_range::<usize, _>(1..3);
4103                            let files = (0..count)
4104                                .map(|_| files.choose(&mut *rng).unwrap())
4105                                .collect::<Vec<_>>();
4106                            log::info!("LSP: Returning definitions in files {:?}", &files);
4107                            Ok(Some(lsp::GotoDefinitionResponse::Array(
4108                                files
4109                                    .into_iter()
4110                                    .map(|file| lsp::Location {
4111                                        uri: lsp::Url::from_file_path(file).unwrap(),
4112                                        range: Default::default(),
4113                                    })
4114                                    .collect(),
4115                            )))
4116                        }
4117                    }
4118                });
4119
4120                fake_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>({
4121                    let rng = rng.clone();
4122                    let project = project.clone();
4123                    move |params, mut cx| {
4124                        let highlights = if let Some(project) = project.upgrade(&cx) {
4125                            project.update(&mut cx, |project, cx| {
4126                                let path = params
4127                                    .text_document_position_params
4128                                    .text_document
4129                                    .uri
4130                                    .to_file_path()
4131                                    .unwrap();
4132                                let (worktree, relative_path) =
4133                                    project.find_local_worktree(&path, cx)?;
4134                                let project_path =
4135                                    ProjectPath::from((worktree.read(cx).id(), relative_path));
4136                                let buffer = project.get_open_buffer(&project_path, cx)?.read(cx);
4137
4138                                let mut highlights = Vec::new();
4139                                let highlight_count = rng.lock().gen_range(1..=5);
4140                                let mut prev_end = 0;
4141                                for _ in 0..highlight_count {
4142                                    let range =
4143                                        buffer.random_byte_range(prev_end, &mut *rng.lock());
4144
4145                                    highlights.push(lsp::DocumentHighlight {
4146                                        range: range_to_lsp(range.to_point_utf16(buffer)),
4147                                        kind: Some(lsp::DocumentHighlightKind::READ),
4148                                    });
4149                                    prev_end = range.end;
4150                                }
4151                                Some(highlights)
4152                            })
4153                        } else {
4154                            None
4155                        };
4156                        async move { Ok(highlights) }
4157                    }
4158                });
4159            }
4160        })),
4161        ..Default::default()
4162    });
4163    host_language_registry.add(Arc::new(language));
4164
4165    let op_start_signal = futures::channel::mpsc::unbounded();
4166    user_ids.push(host.current_user_id(&host_cx));
4167    op_start_signals.push(op_start_signal.0);
4168    clients.push(host_cx.foreground().spawn(host.simulate_host(
4169        host_project,
4170        op_start_signal.1,
4171        rng.clone(),
4172        host_cx,
4173    )));
4174
4175    let disconnect_host_at = if rng.lock().gen_bool(0.2) {
4176        rng.lock().gen_range(0..max_operations)
4177    } else {
4178        max_operations
4179    };
4180    let mut available_guests = vec![
4181        "guest-1".to_string(),
4182        "guest-2".to_string(),
4183        "guest-3".to_string(),
4184        "guest-4".to_string(),
4185    ];
4186    let mut operations = 0;
4187    while operations < max_operations {
4188        if operations == disconnect_host_at {
4189            server.disconnect_client(user_ids[0]);
4190            cx.foreground().advance_clock(RECEIVE_TIMEOUT);
4191            drop(op_start_signals);
4192            let mut clients = futures::future::join_all(clients).await;
4193            cx.foreground().run_until_parked();
4194
4195            let (host, host_project, mut host_cx, host_err) = clients.remove(0);
4196            if let Some(host_err) = host_err {
4197                log::error!("host error - {:?}", host_err);
4198            }
4199            host_project.read_with(&host_cx, |project, _| assert!(!project.is_shared()));
4200            for (guest, guest_project, mut guest_cx, guest_err) in clients {
4201                if let Some(guest_err) = guest_err {
4202                    log::error!("{} error - {:?}", guest.username, guest_err);
4203                }
4204
4205                let contacts = server
4206                    .app_state
4207                    .db
4208                    .get_contacts(guest.current_user_id(&guest_cx))
4209                    .await
4210                    .unwrap();
4211                let contacts = server
4212                    .store
4213                    .read()
4214                    .await
4215                    .build_initial_contacts_update(contacts)
4216                    .contacts;
4217                assert!(!contacts
4218                    .iter()
4219                    .flat_map(|contact| &contact.projects)
4220                    .any(|project| project.id == host_project_id));
4221                guest_project.read_with(&guest_cx, |project, _| assert!(project.is_read_only()));
4222                guest_cx.update(|_| drop((guest, guest_project)));
4223            }
4224            host_cx.update(|_| drop((host, host_project)));
4225
4226            return;
4227        }
4228
4229        let distribution = rng.lock().gen_range(0..100);
4230        match distribution {
4231            0..=19 if !available_guests.is_empty() => {
4232                let guest_ix = rng.lock().gen_range(0..available_guests.len());
4233                let guest_username = available_guests.remove(guest_ix);
4234                log::info!("Adding new connection for {}", guest_username);
4235                next_entity_id += 100000;
4236                let mut guest_cx = TestAppContext::new(
4237                    cx.foreground_platform(),
4238                    cx.platform(),
4239                    deterministic.build_foreground(next_entity_id),
4240                    deterministic.build_background(),
4241                    cx.font_cache(),
4242                    cx.leak_detector(),
4243                    next_entity_id,
4244                );
4245                let guest = server.create_client(&mut guest_cx, &guest_username).await;
4246                let guest_project = Project::remote(
4247                    host_project_id,
4248                    guest.client.clone(),
4249                    guest.user_store.clone(),
4250                    guest.project_store.clone(),
4251                    guest_lang_registry.clone(),
4252                    FakeFs::new(cx.background()),
4253                    guest_cx.to_async(),
4254                )
4255                .await
4256                .unwrap();
4257                let op_start_signal = futures::channel::mpsc::unbounded();
4258                user_ids.push(guest.current_user_id(&guest_cx));
4259                op_start_signals.push(op_start_signal.0);
4260                clients.push(guest_cx.foreground().spawn(guest.simulate_guest(
4261                    guest_username.clone(),
4262                    guest_project,
4263                    op_start_signal.1,
4264                    rng.clone(),
4265                    guest_cx,
4266                )));
4267
4268                log::info!("Added connection for {}", guest_username);
4269                operations += 1;
4270            }
4271            20..=29 if clients.len() > 1 => {
4272                let guest_ix = rng.lock().gen_range(1..clients.len());
4273                log::info!("Removing guest {}", user_ids[guest_ix]);
4274                let removed_guest_id = user_ids.remove(guest_ix);
4275                let guest = clients.remove(guest_ix);
4276                op_start_signals.remove(guest_ix);
4277                server.forbid_connections();
4278                server.disconnect_client(removed_guest_id);
4279                cx.foreground().advance_clock(RECEIVE_TIMEOUT);
4280                let (guest, guest_project, mut guest_cx, guest_err) = guest.await;
4281                server.allow_connections();
4282
4283                if let Some(guest_err) = guest_err {
4284                    log::error!("{} error - {:?}", guest.username, guest_err);
4285                }
4286                guest_project.read_with(&guest_cx, |project, _| assert!(project.is_read_only()));
4287                for user_id in &user_ids {
4288                    let contacts = server.app_state.db.get_contacts(*user_id).await.unwrap();
4289                    let contacts = server
4290                        .store
4291                        .read()
4292                        .await
4293                        .build_initial_contacts_update(contacts)
4294                        .contacts;
4295                    for contact in contacts {
4296                        if contact.online {
4297                            assert_ne!(
4298                                contact.user_id, removed_guest_id.0 as u64,
4299                                "removed guest is still a contact of another peer"
4300                            );
4301                        }
4302                        for project in contact.projects {
4303                            for project_guest_id in project.guests {
4304                                assert_ne!(
4305                                    project_guest_id, removed_guest_id.0 as u64,
4306                                    "removed guest appears as still participating on a project"
4307                                );
4308                            }
4309                        }
4310                    }
4311                }
4312
4313                log::info!("{} removed", guest.username);
4314                available_guests.push(guest.username.clone());
4315                guest_cx.update(|_| drop((guest, guest_project)));
4316
4317                operations += 1;
4318            }
4319            _ => {
4320                while operations < max_operations && rng.lock().gen_bool(0.7) {
4321                    op_start_signals
4322                        .choose(&mut *rng.lock())
4323                        .unwrap()
4324                        .unbounded_send(())
4325                        .unwrap();
4326                    operations += 1;
4327                }
4328
4329                if rng.lock().gen_bool(0.8) {
4330                    cx.foreground().run_until_parked();
4331                }
4332            }
4333        }
4334    }
4335
4336    drop(op_start_signals);
4337    let mut clients = futures::future::join_all(clients).await;
4338    cx.foreground().run_until_parked();
4339
4340    let (host_client, host_project, mut host_cx, host_err) = clients.remove(0);
4341    if let Some(host_err) = host_err {
4342        panic!("host error - {:?}", host_err);
4343    }
4344    let host_worktree_snapshots = host_project.read_with(&host_cx, |project, cx| {
4345        project
4346            .worktrees(cx)
4347            .map(|worktree| {
4348                let snapshot = worktree.read(cx).snapshot();
4349                (snapshot.id(), snapshot)
4350            })
4351            .collect::<BTreeMap<_, _>>()
4352    });
4353
4354    host_project.read_with(&host_cx, |project, cx| project.check_invariants(cx));
4355
4356    for (guest_client, guest_project, mut guest_cx, guest_err) in clients.into_iter() {
4357        if let Some(guest_err) = guest_err {
4358            panic!("{} error - {:?}", guest_client.username, guest_err);
4359        }
4360        let worktree_snapshots = guest_project.read_with(&guest_cx, |project, cx| {
4361            project
4362                .worktrees(cx)
4363                .map(|worktree| {
4364                    let worktree = worktree.read(cx);
4365                    (worktree.id(), worktree.snapshot())
4366                })
4367                .collect::<BTreeMap<_, _>>()
4368        });
4369
4370        assert_eq!(
4371            worktree_snapshots.keys().collect::<Vec<_>>(),
4372            host_worktree_snapshots.keys().collect::<Vec<_>>(),
4373            "{} has different worktrees than the host",
4374            guest_client.username
4375        );
4376        for (id, host_snapshot) in &host_worktree_snapshots {
4377            let guest_snapshot = &worktree_snapshots[id];
4378            assert_eq!(
4379                guest_snapshot.root_name(),
4380                host_snapshot.root_name(),
4381                "{} has different root name than the host for worktree {}",
4382                guest_client.username,
4383                id
4384            );
4385            assert_eq!(
4386                guest_snapshot.entries(false).collect::<Vec<_>>(),
4387                host_snapshot.entries(false).collect::<Vec<_>>(),
4388                "{} has different snapshot than the host for worktree {}",
4389                guest_client.username,
4390                id
4391            );
4392            assert_eq!(guest_snapshot.scan_id(), host_snapshot.scan_id());
4393        }
4394
4395        guest_project.read_with(&guest_cx, |project, cx| project.check_invariants(cx));
4396
4397        for guest_buffer in &guest_client.buffers {
4398            let buffer_id = guest_buffer.read_with(&guest_cx, |buffer, _| buffer.remote_id());
4399            let host_buffer = host_project.read_with(&host_cx, |project, cx| {
4400                project.buffer_for_id(buffer_id, cx).expect(&format!(
4401                    "host does not have buffer for guest:{}, peer:{}, id:{}",
4402                    guest_client.username, guest_client.peer_id, buffer_id
4403                ))
4404            });
4405            let path =
4406                host_buffer.read_with(&host_cx, |buffer, cx| buffer.file().unwrap().full_path(cx));
4407
4408            assert_eq!(
4409                guest_buffer.read_with(&guest_cx, |buffer, _| buffer.deferred_ops_len()),
4410                0,
4411                "{}, buffer {}, path {:?} has deferred operations",
4412                guest_client.username,
4413                buffer_id,
4414                path,
4415            );
4416            assert_eq!(
4417                guest_buffer.read_with(&guest_cx, |buffer, _| buffer.text()),
4418                host_buffer.read_with(&host_cx, |buffer, _| buffer.text()),
4419                "{}, buffer {}, path {:?}, differs from the host's buffer",
4420                guest_client.username,
4421                buffer_id,
4422                path
4423            );
4424        }
4425
4426        guest_cx.update(|_| drop((guest_project, guest_client)));
4427    }
4428
4429    host_cx.update(|_| drop((host_client, host_project)));
4430}
4431
4432struct TestServer {
4433    peer: Arc<Peer>,
4434    app_state: Arc<AppState>,
4435    server: Arc<Server>,
4436    foreground: Rc<executor::Foreground>,
4437    notifications: mpsc::UnboundedReceiver<()>,
4438    connection_killers: Arc<Mutex<HashMap<UserId, Arc<AtomicBool>>>>,
4439    forbid_connections: Arc<AtomicBool>,
4440    _test_db: TestDb,
4441}
4442
4443impl TestServer {
4444    async fn start(
4445        foreground: Rc<executor::Foreground>,
4446        background: Arc<executor::Background>,
4447    ) -> Self {
4448        let test_db = TestDb::fake(background);
4449        let app_state = Self::build_app_state(&test_db).await;
4450        let peer = Peer::new();
4451        let notifications = mpsc::unbounded();
4452        let server = Server::new(app_state.clone(), Some(notifications.0));
4453        Self {
4454            peer,
4455            app_state,
4456            server,
4457            foreground,
4458            notifications: notifications.1,
4459            connection_killers: Default::default(),
4460            forbid_connections: Default::default(),
4461            _test_db: test_db,
4462        }
4463    }
4464
4465    async fn create_client(&mut self, cx: &mut TestAppContext, name: &str) -> TestClient {
4466        cx.update(|cx| {
4467            let settings = Settings::test(cx);
4468            cx.set_global(settings);
4469        });
4470
4471        let http = FakeHttpClient::with_404_response();
4472        let user_id = if let Ok(Some(user)) = self.app_state.db.get_user_by_github_login(name).await
4473        {
4474            user.id
4475        } else {
4476            self.app_state
4477                .db
4478                .create_user(name, None, false)
4479                .await
4480                .unwrap()
4481        };
4482        let client_name = name.to_string();
4483        let mut client = Client::new(http.clone());
4484        let server = self.server.clone();
4485        let db = self.app_state.db.clone();
4486        let connection_killers = self.connection_killers.clone();
4487        let forbid_connections = self.forbid_connections.clone();
4488        let (connection_id_tx, mut connection_id_rx) = mpsc::channel(16);
4489
4490        Arc::get_mut(&mut client)
4491            .unwrap()
4492            .override_authenticate(move |cx| {
4493                cx.spawn(|_| async move {
4494                    let access_token = "the-token".to_string();
4495                    Ok(Credentials {
4496                        user_id: user_id.0 as u64,
4497                        access_token,
4498                    })
4499                })
4500            })
4501            .override_establish_connection(move |credentials, cx| {
4502                assert_eq!(credentials.user_id, user_id.0 as u64);
4503                assert_eq!(credentials.access_token, "the-token");
4504
4505                let server = server.clone();
4506                let db = db.clone();
4507                let connection_killers = connection_killers.clone();
4508                let forbid_connections = forbid_connections.clone();
4509                let client_name = client_name.clone();
4510                let connection_id_tx = connection_id_tx.clone();
4511                cx.spawn(move |cx| async move {
4512                    if forbid_connections.load(SeqCst) {
4513                        Err(EstablishConnectionError::other(anyhow!(
4514                            "server is forbidding connections"
4515                        )))
4516                    } else {
4517                        let (client_conn, server_conn, killed) =
4518                            Connection::in_memory(cx.background());
4519                        connection_killers.lock().insert(user_id, killed);
4520                        let user = db.get_user_by_id(user_id).await.unwrap().unwrap();
4521                        cx.background()
4522                            .spawn(server.handle_connection(
4523                                server_conn,
4524                                client_name,
4525                                user,
4526                                Some(connection_id_tx),
4527                                cx.background(),
4528                            ))
4529                            .detach();
4530                        Ok(client_conn)
4531                    }
4532                })
4533            });
4534
4535        let fs = FakeFs::new(cx.background());
4536        let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx));
4537        let project_store = cx.add_model(|_| ProjectStore::new(project::Db::open_fake()));
4538        let app_state = Arc::new(workspace::AppState {
4539            client: client.clone(),
4540            user_store: user_store.clone(),
4541            project_store: project_store.clone(),
4542            languages: Arc::new(LanguageRegistry::new(Task::ready(()))),
4543            themes: ThemeRegistry::new((), cx.font_cache()),
4544            fs: fs.clone(),
4545            build_window_options: || Default::default(),
4546            initialize_workspace: |_, _, _| unimplemented!(),
4547        });
4548
4549        Channel::init(&client);
4550        Project::init(&client);
4551        cx.update(|cx| workspace::init(app_state.clone(), cx));
4552
4553        client
4554            .authenticate_and_connect(false, &cx.to_async())
4555            .await
4556            .unwrap();
4557        let peer_id = PeerId(connection_id_rx.next().await.unwrap().0);
4558
4559        let client = TestClient {
4560            client,
4561            peer_id,
4562            username: name.to_string(),
4563            user_store,
4564            project_store,
4565            fs,
4566            language_registry: Arc::new(LanguageRegistry::test()),
4567            buffers: Default::default(),
4568        };
4569        client.wait_for_current_user(cx).await;
4570        client
4571    }
4572
4573    fn disconnect_client(&self, user_id: UserId) {
4574        self.connection_killers
4575            .lock()
4576            .remove(&user_id)
4577            .unwrap()
4578            .store(true, SeqCst);
4579    }
4580
4581    fn forbid_connections(&self) {
4582        self.forbid_connections.store(true, SeqCst);
4583    }
4584
4585    fn allow_connections(&self) {
4586        self.forbid_connections.store(false, SeqCst);
4587    }
4588
4589    async fn make_contacts(&self, mut clients: Vec<(&TestClient, &mut TestAppContext)>) {
4590        while let Some((client_a, cx_a)) = clients.pop() {
4591            for (client_b, cx_b) in &mut clients {
4592                client_a
4593                    .user_store
4594                    .update(cx_a, |store, cx| {
4595                        store.request_contact(client_b.user_id().unwrap(), cx)
4596                    })
4597                    .await
4598                    .unwrap();
4599                cx_a.foreground().run_until_parked();
4600                client_b
4601                    .user_store
4602                    .update(*cx_b, |store, cx| {
4603                        store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
4604                    })
4605                    .await
4606                    .unwrap();
4607            }
4608        }
4609    }
4610
4611    async fn build_app_state(test_db: &TestDb) -> Arc<AppState> {
4612        Arc::new(AppState {
4613            db: test_db.db().clone(),
4614            api_token: Default::default(),
4615            invite_link_prefix: Default::default(),
4616        })
4617    }
4618
4619    async fn state<'a>(&'a self) -> RwLockReadGuard<'a, Store> {
4620        self.server.store.read().await
4621    }
4622
4623    async fn condition<F>(&mut self, mut predicate: F)
4624    where
4625        F: FnMut(&Store) -> bool,
4626    {
4627        assert!(
4628            self.foreground.parking_forbidden(),
4629            "you must call forbid_parking to use server conditions so we don't block indefinitely"
4630        );
4631        while !(predicate)(&*self.server.store.read().await) {
4632            self.foreground.start_waiting();
4633            self.notifications.next().await;
4634            self.foreground.finish_waiting();
4635        }
4636    }
4637}
4638
4639impl Deref for TestServer {
4640    type Target = Server;
4641
4642    fn deref(&self) -> &Self::Target {
4643        &self.server
4644    }
4645}
4646
4647impl Drop for TestServer {
4648    fn drop(&mut self) {
4649        self.peer.reset();
4650    }
4651}
4652
4653struct TestClient {
4654    client: Arc<Client>,
4655    username: String,
4656    pub peer_id: PeerId,
4657    pub user_store: ModelHandle<UserStore>,
4658    pub project_store: ModelHandle<ProjectStore>,
4659    language_registry: Arc<LanguageRegistry>,
4660    fs: Arc<FakeFs>,
4661    buffers: HashSet<ModelHandle<language::Buffer>>,
4662}
4663
4664impl Deref for TestClient {
4665    type Target = Arc<Client>;
4666
4667    fn deref(&self) -> &Self::Target {
4668        &self.client
4669    }
4670}
4671
4672struct ContactsSummary {
4673    pub current: Vec<String>,
4674    pub outgoing_requests: Vec<String>,
4675    pub incoming_requests: Vec<String>,
4676}
4677
4678impl TestClient {
4679    pub fn current_user_id(&self, cx: &TestAppContext) -> UserId {
4680        UserId::from_proto(
4681            self.user_store
4682                .read_with(cx, |user_store, _| user_store.current_user().unwrap().id),
4683        )
4684    }
4685
4686    async fn wait_for_current_user(&self, cx: &TestAppContext) {
4687        let mut authed_user = self
4688            .user_store
4689            .read_with(cx, |user_store, _| user_store.watch_current_user());
4690        while authed_user.next().await.unwrap().is_none() {}
4691    }
4692
4693    async fn clear_contacts(&self, cx: &mut TestAppContext) {
4694        self.user_store
4695            .update(cx, |store, _| store.clear_contacts())
4696            .await;
4697    }
4698
4699    fn summarize_contacts(&self, cx: &TestAppContext) -> ContactsSummary {
4700        self.user_store.read_with(cx, |store, _| ContactsSummary {
4701            current: store
4702                .contacts()
4703                .iter()
4704                .map(|contact| contact.user.github_login.clone())
4705                .collect(),
4706            outgoing_requests: store
4707                .outgoing_contact_requests()
4708                .iter()
4709                .map(|user| user.github_login.clone())
4710                .collect(),
4711            incoming_requests: store
4712                .incoming_contact_requests()
4713                .iter()
4714                .map(|user| user.github_login.clone())
4715                .collect(),
4716        })
4717    }
4718
4719    async fn build_local_project(
4720        &self,
4721        root_path: impl AsRef<Path>,
4722        cx: &mut TestAppContext,
4723    ) -> (ModelHandle<Project>, WorktreeId) {
4724        let project = cx.update(|cx| {
4725            Project::local(
4726                true,
4727                self.client.clone(),
4728                self.user_store.clone(),
4729                self.project_store.clone(),
4730                self.language_registry.clone(),
4731                self.fs.clone(),
4732                cx,
4733            )
4734        });
4735        let (worktree, _) = project
4736            .update(cx, |p, cx| {
4737                p.find_or_create_local_worktree(root_path, true, cx)
4738            })
4739            .await
4740            .unwrap();
4741        worktree
4742            .read_with(cx, |tree, _| tree.as_local().unwrap().scan_complete())
4743            .await;
4744        project
4745            .update(cx, |project, _| project.next_remote_id())
4746            .await;
4747        (project, worktree.read_with(cx, |tree, _| tree.id()))
4748    }
4749
4750    async fn build_remote_project(
4751        &self,
4752        host_project: &ModelHandle<Project>,
4753        host_cx: &mut TestAppContext,
4754        guest_cx: &mut TestAppContext,
4755    ) -> ModelHandle<Project> {
4756        let host_project_id = host_project
4757            .read_with(host_cx, |project, _| project.next_remote_id())
4758            .await;
4759        let guest_user_id = self.user_id().unwrap();
4760        let languages = host_project.read_with(host_cx, |project, _| project.languages().clone());
4761        let project_b = guest_cx.spawn(|cx| {
4762            Project::remote(
4763                host_project_id,
4764                self.client.clone(),
4765                self.user_store.clone(),
4766                self.project_store.clone(),
4767                languages,
4768                FakeFs::new(cx.background()),
4769                cx,
4770            )
4771        });
4772        host_cx.foreground().run_until_parked();
4773        host_project.update(host_cx, |project, cx| {
4774            project.respond_to_join_request(guest_user_id, true, cx)
4775        });
4776        let project = project_b.await.unwrap();
4777        project
4778    }
4779
4780    fn build_workspace(
4781        &self,
4782        project: &ModelHandle<Project>,
4783        cx: &mut TestAppContext,
4784    ) -> ViewHandle<Workspace> {
4785        let (window_id, _) = cx.add_window(|_| EmptyView);
4786        cx.add_view(window_id, |cx| Workspace::new(project.clone(), cx))
4787    }
4788
4789    async fn simulate_host(
4790        mut self,
4791        project: ModelHandle<Project>,
4792        op_start_signal: futures::channel::mpsc::UnboundedReceiver<()>,
4793        rng: Arc<Mutex<StdRng>>,
4794        mut cx: TestAppContext,
4795    ) -> (
4796        Self,
4797        ModelHandle<Project>,
4798        TestAppContext,
4799        Option<anyhow::Error>,
4800    ) {
4801        async fn simulate_host_internal(
4802            client: &mut TestClient,
4803            project: ModelHandle<Project>,
4804            mut op_start_signal: futures::channel::mpsc::UnboundedReceiver<()>,
4805            rng: Arc<Mutex<StdRng>>,
4806            cx: &mut TestAppContext,
4807        ) -> anyhow::Result<()> {
4808            let fs = project.read_with(cx, |project, _| project.fs().clone());
4809
4810            cx.update(|cx| {
4811                cx.subscribe(&project, move |project, event, cx| {
4812                    if let project::Event::ContactRequestedJoin(user) = event {
4813                        log::info!("Host: accepting join request from {}", user.github_login);
4814                        project.update(cx, |project, cx| {
4815                            project.respond_to_join_request(user.id, true, cx)
4816                        });
4817                    }
4818                })
4819                .detach();
4820            });
4821
4822            while op_start_signal.next().await.is_some() {
4823                let distribution = rng.lock().gen_range::<usize, _>(0..100);
4824                let files = fs.as_fake().files().await;
4825                match distribution {
4826                    0..=19 if !files.is_empty() => {
4827                        let path = files.choose(&mut *rng.lock()).unwrap();
4828                        let mut path = path.as_path();
4829                        while let Some(parent_path) = path.parent() {
4830                            path = parent_path;
4831                            if rng.lock().gen() {
4832                                break;
4833                            }
4834                        }
4835
4836                        log::info!("Host: find/create local worktree {:?}", path);
4837                        let find_or_create_worktree = project.update(cx, |project, cx| {
4838                            project.find_or_create_local_worktree(path, true, cx)
4839                        });
4840                        if rng.lock().gen() {
4841                            cx.background().spawn(find_or_create_worktree).detach();
4842                        } else {
4843                            find_or_create_worktree.await?;
4844                        }
4845                    }
4846                    20..=79 if !files.is_empty() => {
4847                        let buffer = if client.buffers.is_empty() || rng.lock().gen() {
4848                            let file = files.choose(&mut *rng.lock()).unwrap();
4849                            let (worktree, path) = project
4850                                .update(cx, |project, cx| {
4851                                    project.find_or_create_local_worktree(file.clone(), true, cx)
4852                                })
4853                                .await?;
4854                            let project_path =
4855                                worktree.read_with(cx, |worktree, _| (worktree.id(), path));
4856                            log::info!(
4857                                "Host: opening path {:?}, worktree {}, relative_path {:?}",
4858                                file,
4859                                project_path.0,
4860                                project_path.1
4861                            );
4862                            let buffer = project
4863                                .update(cx, |project, cx| project.open_buffer(project_path, cx))
4864                                .await
4865                                .unwrap();
4866                            client.buffers.insert(buffer.clone());
4867                            buffer
4868                        } else {
4869                            client
4870                                .buffers
4871                                .iter()
4872                                .choose(&mut *rng.lock())
4873                                .unwrap()
4874                                .clone()
4875                        };
4876
4877                        if rng.lock().gen_bool(0.1) {
4878                            cx.update(|cx| {
4879                                log::info!(
4880                                    "Host: dropping buffer {:?}",
4881                                    buffer.read(cx).file().unwrap().full_path(cx)
4882                                );
4883                                client.buffers.remove(&buffer);
4884                                drop(buffer);
4885                            });
4886                        } else {
4887                            buffer.update(cx, |buffer, cx| {
4888                                log::info!(
4889                                    "Host: updating buffer {:?} ({})",
4890                                    buffer.file().unwrap().full_path(cx),
4891                                    buffer.remote_id()
4892                                );
4893
4894                                if rng.lock().gen_bool(0.7) {
4895                                    buffer.randomly_edit(&mut *rng.lock(), 5, cx);
4896                                } else {
4897                                    buffer.randomly_undo_redo(&mut *rng.lock(), cx);
4898                                }
4899                            });
4900                        }
4901                    }
4902                    _ => loop {
4903                        let path_component_count = rng.lock().gen_range::<usize, _>(1..=5);
4904                        let mut path = PathBuf::new();
4905                        path.push("/");
4906                        for _ in 0..path_component_count {
4907                            let letter = rng.lock().gen_range(b'a'..=b'z');
4908                            path.push(std::str::from_utf8(&[letter]).unwrap());
4909                        }
4910                        path.set_extension("rs");
4911                        let parent_path = path.parent().unwrap();
4912
4913                        log::info!("Host: creating file {:?}", path,);
4914
4915                        if fs.create_dir(&parent_path).await.is_ok()
4916                            && fs.create_file(&path, Default::default()).await.is_ok()
4917                        {
4918                            break;
4919                        } else {
4920                            log::info!("Host: cannot create file");
4921                        }
4922                    },
4923                }
4924
4925                cx.background().simulate_random_delay().await;
4926            }
4927
4928            Ok(())
4929        }
4930
4931        let result =
4932            simulate_host_internal(&mut self, project.clone(), op_start_signal, rng, &mut cx).await;
4933        log::info!("Host done");
4934        (self, project, cx, result.err())
4935    }
4936
4937    pub async fn simulate_guest(
4938        mut self,
4939        guest_username: String,
4940        project: ModelHandle<Project>,
4941        op_start_signal: futures::channel::mpsc::UnboundedReceiver<()>,
4942        rng: Arc<Mutex<StdRng>>,
4943        mut cx: TestAppContext,
4944    ) -> (
4945        Self,
4946        ModelHandle<Project>,
4947        TestAppContext,
4948        Option<anyhow::Error>,
4949    ) {
4950        async fn simulate_guest_internal(
4951            client: &mut TestClient,
4952            guest_username: &str,
4953            project: ModelHandle<Project>,
4954            mut op_start_signal: futures::channel::mpsc::UnboundedReceiver<()>,
4955            rng: Arc<Mutex<StdRng>>,
4956            cx: &mut TestAppContext,
4957        ) -> anyhow::Result<()> {
4958            while op_start_signal.next().await.is_some() {
4959                let buffer = if client.buffers.is_empty() || rng.lock().gen() {
4960                    let worktree = if let Some(worktree) = project.read_with(cx, |project, cx| {
4961                        project
4962                            .worktrees(&cx)
4963                            .filter(|worktree| {
4964                                let worktree = worktree.read(cx);
4965                                worktree.is_visible()
4966                                    && worktree.entries(false).any(|e| e.is_file())
4967                            })
4968                            .choose(&mut *rng.lock())
4969                    }) {
4970                        worktree
4971                    } else {
4972                        cx.background().simulate_random_delay().await;
4973                        continue;
4974                    };
4975
4976                    let (worktree_root_name, project_path) =
4977                        worktree.read_with(cx, |worktree, _| {
4978                            let entry = worktree
4979                                .entries(false)
4980                                .filter(|e| e.is_file())
4981                                .choose(&mut *rng.lock())
4982                                .unwrap();
4983                            (
4984                                worktree.root_name().to_string(),
4985                                (worktree.id(), entry.path.clone()),
4986                            )
4987                        });
4988                    log::info!(
4989                        "{}: opening path {:?} in worktree {} ({})",
4990                        guest_username,
4991                        project_path.1,
4992                        project_path.0,
4993                        worktree_root_name,
4994                    );
4995                    let buffer = project
4996                        .update(cx, |project, cx| {
4997                            project.open_buffer(project_path.clone(), cx)
4998                        })
4999                        .await?;
5000                    log::info!(
5001                        "{}: opened path {:?} in worktree {} ({}) with buffer id {}",
5002                        guest_username,
5003                        project_path.1,
5004                        project_path.0,
5005                        worktree_root_name,
5006                        buffer.read_with(cx, |buffer, _| buffer.remote_id())
5007                    );
5008                    client.buffers.insert(buffer.clone());
5009                    buffer
5010                } else {
5011                    client
5012                        .buffers
5013                        .iter()
5014                        .choose(&mut *rng.lock())
5015                        .unwrap()
5016                        .clone()
5017                };
5018
5019                let choice = rng.lock().gen_range(0..100);
5020                match choice {
5021                    0..=9 => {
5022                        cx.update(|cx| {
5023                            log::info!(
5024                                "{}: dropping buffer {:?}",
5025                                guest_username,
5026                                buffer.read(cx).file().unwrap().full_path(cx)
5027                            );
5028                            client.buffers.remove(&buffer);
5029                            drop(buffer);
5030                        });
5031                    }
5032                    10..=19 => {
5033                        let completions = project.update(cx, |project, cx| {
5034                            log::info!(
5035                                "{}: requesting completions for buffer {} ({:?})",
5036                                guest_username,
5037                                buffer.read(cx).remote_id(),
5038                                buffer.read(cx).file().unwrap().full_path(cx)
5039                            );
5040                            let offset = rng.lock().gen_range(0..=buffer.read(cx).len());
5041                            project.completions(&buffer, offset, cx)
5042                        });
5043                        let completions = cx.background().spawn(async move {
5044                            completions
5045                                .await
5046                                .map_err(|err| anyhow!("completions request failed: {:?}", err))
5047                        });
5048                        if rng.lock().gen_bool(0.3) {
5049                            log::info!("{}: detaching completions request", guest_username);
5050                            cx.update(|cx| completions.detach_and_log_err(cx));
5051                        } else {
5052                            completions.await?;
5053                        }
5054                    }
5055                    20..=29 => {
5056                        let code_actions = project.update(cx, |project, cx| {
5057                            log::info!(
5058                                "{}: requesting code actions for buffer {} ({:?})",
5059                                guest_username,
5060                                buffer.read(cx).remote_id(),
5061                                buffer.read(cx).file().unwrap().full_path(cx)
5062                            );
5063                            let range = buffer.read(cx).random_byte_range(0, &mut *rng.lock());
5064                            project.code_actions(&buffer, range, cx)
5065                        });
5066                        let code_actions = cx.background().spawn(async move {
5067                            code_actions
5068                                .await
5069                                .map_err(|err| anyhow!("code actions request failed: {:?}", err))
5070                        });
5071                        if rng.lock().gen_bool(0.3) {
5072                            log::info!("{}: detaching code actions request", guest_username);
5073                            cx.update(|cx| code_actions.detach_and_log_err(cx));
5074                        } else {
5075                            code_actions.await?;
5076                        }
5077                    }
5078                    30..=39 if buffer.read_with(cx, |buffer, _| buffer.is_dirty()) => {
5079                        let (requested_version, save) = buffer.update(cx, |buffer, cx| {
5080                            log::info!(
5081                                "{}: saving buffer {} ({:?})",
5082                                guest_username,
5083                                buffer.remote_id(),
5084                                buffer.file().unwrap().full_path(cx)
5085                            );
5086                            (buffer.version(), buffer.save(cx))
5087                        });
5088                        let save = cx.background().spawn(async move {
5089                            let (saved_version, _) = save
5090                                .await
5091                                .map_err(|err| anyhow!("save request failed: {:?}", err))?;
5092                            assert!(saved_version.observed_all(&requested_version));
5093                            Ok::<_, anyhow::Error>(())
5094                        });
5095                        if rng.lock().gen_bool(0.3) {
5096                            log::info!("{}: detaching save request", guest_username);
5097                            cx.update(|cx| save.detach_and_log_err(cx));
5098                        } else {
5099                            save.await?;
5100                        }
5101                    }
5102                    40..=44 => {
5103                        let prepare_rename = project.update(cx, |project, cx| {
5104                            log::info!(
5105                                "{}: preparing rename for buffer {} ({:?})",
5106                                guest_username,
5107                                buffer.read(cx).remote_id(),
5108                                buffer.read(cx).file().unwrap().full_path(cx)
5109                            );
5110                            let offset = rng.lock().gen_range(0..=buffer.read(cx).len());
5111                            project.prepare_rename(buffer, offset, cx)
5112                        });
5113                        let prepare_rename = cx.background().spawn(async move {
5114                            prepare_rename
5115                                .await
5116                                .map_err(|err| anyhow!("prepare rename request failed: {:?}", err))
5117                        });
5118                        if rng.lock().gen_bool(0.3) {
5119                            log::info!("{}: detaching prepare rename request", guest_username);
5120                            cx.update(|cx| prepare_rename.detach_and_log_err(cx));
5121                        } else {
5122                            prepare_rename.await?;
5123                        }
5124                    }
5125                    45..=49 => {
5126                        let definitions = project.update(cx, |project, cx| {
5127                            log::info!(
5128                                "{}: requesting definitions for buffer {} ({:?})",
5129                                guest_username,
5130                                buffer.read(cx).remote_id(),
5131                                buffer.read(cx).file().unwrap().full_path(cx)
5132                            );
5133                            let offset = rng.lock().gen_range(0..=buffer.read(cx).len());
5134                            project.definition(&buffer, offset, cx)
5135                        });
5136                        let definitions = cx.background().spawn(async move {
5137                            definitions
5138                                .await
5139                                .map_err(|err| anyhow!("definitions request failed: {:?}", err))
5140                        });
5141                        if rng.lock().gen_bool(0.3) {
5142                            log::info!("{}: detaching definitions request", guest_username);
5143                            cx.update(|cx| definitions.detach_and_log_err(cx));
5144                        } else {
5145                            client
5146                                .buffers
5147                                .extend(definitions.await?.into_iter().map(|loc| loc.buffer));
5148                        }
5149                    }
5150                    50..=54 => {
5151                        let highlights = project.update(cx, |project, cx| {
5152                            log::info!(
5153                                "{}: requesting highlights for buffer {} ({:?})",
5154                                guest_username,
5155                                buffer.read(cx).remote_id(),
5156                                buffer.read(cx).file().unwrap().full_path(cx)
5157                            );
5158                            let offset = rng.lock().gen_range(0..=buffer.read(cx).len());
5159                            project.document_highlights(&buffer, offset, cx)
5160                        });
5161                        let highlights = cx.background().spawn(async move {
5162                            highlights
5163                                .await
5164                                .map_err(|err| anyhow!("highlights request failed: {:?}", err))
5165                        });
5166                        if rng.lock().gen_bool(0.3) {
5167                            log::info!("{}: detaching highlights request", guest_username);
5168                            cx.update(|cx| highlights.detach_and_log_err(cx));
5169                        } else {
5170                            highlights.await?;
5171                        }
5172                    }
5173                    55..=59 => {
5174                        let search = project.update(cx, |project, cx| {
5175                            let query = rng.lock().gen_range('a'..='z');
5176                            log::info!("{}: project-wide search {:?}", guest_username, query);
5177                            project.search(SearchQuery::text(query, false, false), cx)
5178                        });
5179                        let search = cx.background().spawn(async move {
5180                            search
5181                                .await
5182                                .map_err(|err| anyhow!("search request failed: {:?}", err))
5183                        });
5184                        if rng.lock().gen_bool(0.3) {
5185                            log::info!("{}: detaching search request", guest_username);
5186                            cx.update(|cx| search.detach_and_log_err(cx));
5187                        } else {
5188                            client.buffers.extend(search.await?.into_keys());
5189                        }
5190                    }
5191                    60..=69 => {
5192                        let worktree = project
5193                            .read_with(cx, |project, cx| {
5194                                project
5195                                    .worktrees(&cx)
5196                                    .filter(|worktree| {
5197                                        let worktree = worktree.read(cx);
5198                                        worktree.is_visible()
5199                                            && worktree.entries(false).any(|e| e.is_file())
5200                                            && worktree.root_entry().map_or(false, |e| e.is_dir())
5201                                    })
5202                                    .choose(&mut *rng.lock())
5203                            })
5204                            .unwrap();
5205                        let (worktree_id, worktree_root_name) = worktree
5206                            .read_with(cx, |worktree, _| {
5207                                (worktree.id(), worktree.root_name().to_string())
5208                            });
5209
5210                        let mut new_name = String::new();
5211                        for _ in 0..10 {
5212                            let letter = rng.lock().gen_range('a'..='z');
5213                            new_name.push(letter);
5214                        }
5215                        let mut new_path = PathBuf::new();
5216                        new_path.push(new_name);
5217                        new_path.set_extension("rs");
5218                        log::info!(
5219                            "{}: creating {:?} in worktree {} ({})",
5220                            guest_username,
5221                            new_path,
5222                            worktree_id,
5223                            worktree_root_name,
5224                        );
5225                        project
5226                            .update(cx, |project, cx| {
5227                                project.create_entry((worktree_id, new_path), false, cx)
5228                            })
5229                            .unwrap()
5230                            .await?;
5231                    }
5232                    _ => {
5233                        buffer.update(cx, |buffer, cx| {
5234                            log::info!(
5235                                "{}: updating buffer {} ({:?})",
5236                                guest_username,
5237                                buffer.remote_id(),
5238                                buffer.file().unwrap().full_path(cx)
5239                            );
5240                            if rng.lock().gen_bool(0.7) {
5241                                buffer.randomly_edit(&mut *rng.lock(), 5, cx);
5242                            } else {
5243                                buffer.randomly_undo_redo(&mut *rng.lock(), cx);
5244                            }
5245                        });
5246                    }
5247                }
5248                cx.background().simulate_random_delay().await;
5249            }
5250            Ok(())
5251        }
5252
5253        let result = simulate_guest_internal(
5254            &mut self,
5255            &guest_username,
5256            project.clone(),
5257            op_start_signal,
5258            rng,
5259            &mut cx,
5260        )
5261        .await;
5262        log::info!("{}: done", guest_username);
5263
5264        (self, project, cx, result.err())
5265    }
5266}
5267
5268impl Drop for TestClient {
5269    fn drop(&mut self) {
5270        self.client.tear_down();
5271    }
5272}
5273
5274impl Executor for Arc<gpui::executor::Background> {
5275    type Sleep = gpui::executor::Timer;
5276
5277    fn spawn_detached<F: 'static + Send + Future<Output = ()>>(&self, future: F) {
5278        self.spawn(future).detach();
5279    }
5280
5281    fn sleep(&self, duration: Duration) -> Self::Sleep {
5282        self.as_ref().timer(duration)
5283    }
5284}
5285
5286fn channel_messages(channel: &Channel) -> Vec<(String, String, bool)> {
5287    channel
5288        .messages()
5289        .cursor::<()>()
5290        .map(|m| {
5291            (
5292                m.sender.github_login.clone(),
5293                m.body.clone(),
5294                m.is_pending(),
5295            )
5296        })
5297        .collect()
5298}
5299
5300struct EmptyView;
5301
5302impl gpui::Entity for EmptyView {
5303    type Event = ();
5304}
5305
5306impl gpui::View for EmptyView {
5307    fn ui_name() -> &'static str {
5308        "empty view"
5309    }
5310
5311    fn render(&mut self, _: &mut gpui::RenderContext<Self>) -> gpui::ElementBox {
5312        gpui::Element::boxed(gpui::elements::Empty::new())
5313    }
5314}