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