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