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