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