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