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