integration_tests.rs

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