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