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