integration_tests.rs

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