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