integration_tests.rs

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