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