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