integration_tests.rs

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