integration_tests.rs

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