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