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