integration_tests.rs

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