integration_tests.rs

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