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