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