integration_tests.rs

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