integration_tests.rs

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