integration_tests.rs

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