integration_tests.rs

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