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