integration_tests.rs

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