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) {
4192    cx_a.foreground().forbid_parking();
4193    let mut server = TestServer::start(cx_a.background()).await;
4194    let client_a = server.create_client(cx_a, "user_a").await;
4195    let client_b = server.create_client(cx_b, "user_b").await;
4196    let client_c = server.create_client(cx_c, "user_c").await;
4197    server
4198        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
4199        .await;
4200    let active_call_a = cx_a.read(ActiveCall::global);
4201    let active_call_b = cx_b.read(ActiveCall::global);
4202    let active_call_c = cx_c.read(ActiveCall::global);
4203
4204    deterministic.run_until_parked();
4205    assert_eq!(
4206        contacts(&client_a, cx_a),
4207        [
4208            ("user_b".to_string(), "online", "free"),
4209            ("user_c".to_string(), "online", "free")
4210        ]
4211    );
4212    assert_eq!(
4213        contacts(&client_b, cx_b),
4214        [
4215            ("user_a".to_string(), "online", "free"),
4216            ("user_c".to_string(), "online", "free")
4217        ]
4218    );
4219    assert_eq!(
4220        contacts(&client_c, cx_c),
4221        [
4222            ("user_a".to_string(), "online", "free"),
4223            ("user_b".to_string(), "online", "free")
4224        ]
4225    );
4226
4227    server.disconnect_client(client_c.peer_id().unwrap());
4228    server.forbid_connections();
4229    deterministic.advance_clock(rpc::RECEIVE_TIMEOUT);
4230    assert_eq!(
4231        contacts(&client_a, cx_a),
4232        [
4233            ("user_b".to_string(), "online", "free"),
4234            ("user_c".to_string(), "offline", "free")
4235        ]
4236    );
4237    assert_eq!(
4238        contacts(&client_b, cx_b),
4239        [
4240            ("user_a".to_string(), "online", "free"),
4241            ("user_c".to_string(), "offline", "free")
4242        ]
4243    );
4244    assert_eq!(contacts(&client_c, cx_c), []);
4245
4246    server.allow_connections();
4247    client_c
4248        .authenticate_and_connect(false, &cx_c.to_async())
4249        .await
4250        .unwrap();
4251
4252    deterministic.run_until_parked();
4253    assert_eq!(
4254        contacts(&client_a, cx_a),
4255        [
4256            ("user_b".to_string(), "online", "free"),
4257            ("user_c".to_string(), "online", "free")
4258        ]
4259    );
4260    assert_eq!(
4261        contacts(&client_b, cx_b),
4262        [
4263            ("user_a".to_string(), "online", "free"),
4264            ("user_c".to_string(), "online", "free")
4265        ]
4266    );
4267    assert_eq!(
4268        contacts(&client_c, cx_c),
4269        [
4270            ("user_a".to_string(), "online", "free"),
4271            ("user_b".to_string(), "online", "free")
4272        ]
4273    );
4274
4275    active_call_a
4276        .update(cx_a, |call, cx| {
4277            call.invite(client_b.user_id().unwrap(), None, cx)
4278        })
4279        .await
4280        .unwrap();
4281    deterministic.run_until_parked();
4282    assert_eq!(
4283        contacts(&client_a, cx_a),
4284        [
4285            ("user_b".to_string(), "online", "busy"),
4286            ("user_c".to_string(), "online", "free")
4287        ]
4288    );
4289    assert_eq!(
4290        contacts(&client_b, cx_b),
4291        [
4292            ("user_a".to_string(), "online", "busy"),
4293            ("user_c".to_string(), "online", "free")
4294        ]
4295    );
4296    assert_eq!(
4297        contacts(&client_c, cx_c),
4298        [
4299            ("user_a".to_string(), "online", "busy"),
4300            ("user_b".to_string(), "online", "busy")
4301        ]
4302    );
4303
4304    active_call_b.update(cx_b, |call, _| call.decline_incoming().unwrap());
4305    deterministic.run_until_parked();
4306    assert_eq!(
4307        contacts(&client_a, cx_a),
4308        [
4309            ("user_b".to_string(), "online", "free"),
4310            ("user_c".to_string(), "online", "free")
4311        ]
4312    );
4313    assert_eq!(
4314        contacts(&client_b, cx_b),
4315        [
4316            ("user_a".to_string(), "online", "free"),
4317            ("user_c".to_string(), "online", "free")
4318        ]
4319    );
4320    assert_eq!(
4321        contacts(&client_c, cx_c),
4322        [
4323            ("user_a".to_string(), "online", "free"),
4324            ("user_b".to_string(), "online", "free")
4325        ]
4326    );
4327
4328    active_call_c
4329        .update(cx_c, |call, cx| {
4330            call.invite(client_a.user_id().unwrap(), None, cx)
4331        })
4332        .await
4333        .unwrap();
4334    deterministic.run_until_parked();
4335    assert_eq!(
4336        contacts(&client_a, cx_a),
4337        [
4338            ("user_b".to_string(), "online", "free"),
4339            ("user_c".to_string(), "online", "busy")
4340        ]
4341    );
4342    assert_eq!(
4343        contacts(&client_b, cx_b),
4344        [
4345            ("user_a".to_string(), "online", "busy"),
4346            ("user_c".to_string(), "online", "busy")
4347        ]
4348    );
4349    assert_eq!(
4350        contacts(&client_c, cx_c),
4351        [
4352            ("user_a".to_string(), "online", "busy"),
4353            ("user_b".to_string(), "online", "free")
4354        ]
4355    );
4356
4357    active_call_a
4358        .update(cx_a, |call, cx| call.accept_incoming(cx))
4359        .await
4360        .unwrap();
4361    deterministic.run_until_parked();
4362    assert_eq!(
4363        contacts(&client_a, cx_a),
4364        [
4365            ("user_b".to_string(), "online", "free"),
4366            ("user_c".to_string(), "online", "busy")
4367        ]
4368    );
4369    assert_eq!(
4370        contacts(&client_b, cx_b),
4371        [
4372            ("user_a".to_string(), "online", "busy"),
4373            ("user_c".to_string(), "online", "busy")
4374        ]
4375    );
4376    assert_eq!(
4377        contacts(&client_c, cx_c),
4378        [
4379            ("user_a".to_string(), "online", "busy"),
4380            ("user_b".to_string(), "online", "free")
4381        ]
4382    );
4383
4384    active_call_a
4385        .update(cx_a, |call, cx| {
4386            call.invite(client_b.user_id().unwrap(), None, cx)
4387        })
4388        .await
4389        .unwrap();
4390    deterministic.run_until_parked();
4391    assert_eq!(
4392        contacts(&client_a, cx_a),
4393        [
4394            ("user_b".to_string(), "online", "busy"),
4395            ("user_c".to_string(), "online", "busy")
4396        ]
4397    );
4398    assert_eq!(
4399        contacts(&client_b, cx_b),
4400        [
4401            ("user_a".to_string(), "online", "busy"),
4402            ("user_c".to_string(), "online", "busy")
4403        ]
4404    );
4405    assert_eq!(
4406        contacts(&client_c, cx_c),
4407        [
4408            ("user_a".to_string(), "online", "busy"),
4409            ("user_b".to_string(), "online", "busy")
4410        ]
4411    );
4412
4413    active_call_a.update(cx_a, |call, cx| call.hang_up(cx).unwrap());
4414    deterministic.run_until_parked();
4415    assert_eq!(
4416        contacts(&client_a, cx_a),
4417        [
4418            ("user_b".to_string(), "online", "free"),
4419            ("user_c".to_string(), "online", "free")
4420        ]
4421    );
4422    assert_eq!(
4423        contacts(&client_b, cx_b),
4424        [
4425            ("user_a".to_string(), "online", "free"),
4426            ("user_c".to_string(), "online", "free")
4427        ]
4428    );
4429    assert_eq!(
4430        contacts(&client_c, cx_c),
4431        [
4432            ("user_a".to_string(), "online", "free"),
4433            ("user_b".to_string(), "online", "free")
4434        ]
4435    );
4436
4437    active_call_a
4438        .update(cx_a, |call, cx| {
4439            call.invite(client_b.user_id().unwrap(), None, cx)
4440        })
4441        .await
4442        .unwrap();
4443    deterministic.run_until_parked();
4444    assert_eq!(
4445        contacts(&client_a, cx_a),
4446        [
4447            ("user_b".to_string(), "online", "busy"),
4448            ("user_c".to_string(), "online", "free")
4449        ]
4450    );
4451    assert_eq!(
4452        contacts(&client_b, cx_b),
4453        [
4454            ("user_a".to_string(), "online", "busy"),
4455            ("user_c".to_string(), "online", "free")
4456        ]
4457    );
4458    assert_eq!(
4459        contacts(&client_c, cx_c),
4460        [
4461            ("user_a".to_string(), "online", "busy"),
4462            ("user_b".to_string(), "online", "busy")
4463        ]
4464    );
4465
4466    server.forbid_connections();
4467    server.disconnect_client(client_a.peer_id().unwrap());
4468    deterministic.advance_clock(rpc::RECEIVE_TIMEOUT);
4469    assert_eq!(contacts(&client_a, cx_a), []);
4470    assert_eq!(
4471        contacts(&client_b, cx_b),
4472        [
4473            ("user_a".to_string(), "offline", "free"),
4474            ("user_c".to_string(), "online", "free")
4475        ]
4476    );
4477    assert_eq!(
4478        contacts(&client_c, cx_c),
4479        [
4480            ("user_a".to_string(), "offline", "free"),
4481            ("user_b".to_string(), "online", "free")
4482        ]
4483    );
4484
4485    #[allow(clippy::type_complexity)]
4486    fn contacts(
4487        client: &TestClient,
4488        cx: &TestAppContext,
4489    ) -> Vec<(String, &'static str, &'static str)> {
4490        client.user_store.read_with(cx, |store, _| {
4491            store
4492                .contacts()
4493                .iter()
4494                .map(|contact| {
4495                    (
4496                        contact.user.github_login.clone(),
4497                        if contact.online { "online" } else { "offline" },
4498                        if contact.busy { "busy" } else { "free" },
4499                    )
4500                })
4501                .collect()
4502        })
4503    }
4504}
4505
4506#[gpui::test(iterations = 10)]
4507async fn test_contact_requests(
4508    executor: Arc<Deterministic>,
4509    cx_a: &mut TestAppContext,
4510    cx_a2: &mut TestAppContext,
4511    cx_b: &mut TestAppContext,
4512    cx_b2: &mut TestAppContext,
4513    cx_c: &mut TestAppContext,
4514    cx_c2: &mut TestAppContext,
4515) {
4516    cx_a.foreground().forbid_parking();
4517
4518    // Connect to a server as 3 clients.
4519    let mut server = TestServer::start(cx_a.background()).await;
4520    let client_a = server.create_client(cx_a, "user_a").await;
4521    let client_a2 = server.create_client(cx_a2, "user_a").await;
4522    let client_b = server.create_client(cx_b, "user_b").await;
4523    let client_b2 = server.create_client(cx_b2, "user_b").await;
4524    let client_c = server.create_client(cx_c, "user_c").await;
4525    let client_c2 = server.create_client(cx_c2, "user_c").await;
4526
4527    assert_eq!(client_a.user_id().unwrap(), client_a2.user_id().unwrap());
4528    assert_eq!(client_b.user_id().unwrap(), client_b2.user_id().unwrap());
4529    assert_eq!(client_c.user_id().unwrap(), client_c2.user_id().unwrap());
4530
4531    // User A and User C request that user B become their contact.
4532    client_a
4533        .user_store
4534        .update(cx_a, |store, cx| {
4535            store.request_contact(client_b.user_id().unwrap(), cx)
4536        })
4537        .await
4538        .unwrap();
4539    client_c
4540        .user_store
4541        .update(cx_c, |store, cx| {
4542            store.request_contact(client_b.user_id().unwrap(), cx)
4543        })
4544        .await
4545        .unwrap();
4546    executor.run_until_parked();
4547
4548    // All users see the pending request appear in all their clients.
4549    assert_eq!(
4550        client_a.summarize_contacts(cx_a).outgoing_requests,
4551        &["user_b"]
4552    );
4553    assert_eq!(
4554        client_a2.summarize_contacts(cx_a2).outgoing_requests,
4555        &["user_b"]
4556    );
4557    assert_eq!(
4558        client_b.summarize_contacts(cx_b).incoming_requests,
4559        &["user_a", "user_c"]
4560    );
4561    assert_eq!(
4562        client_b2.summarize_contacts(cx_b2).incoming_requests,
4563        &["user_a", "user_c"]
4564    );
4565    assert_eq!(
4566        client_c.summarize_contacts(cx_c).outgoing_requests,
4567        &["user_b"]
4568    );
4569    assert_eq!(
4570        client_c2.summarize_contacts(cx_c2).outgoing_requests,
4571        &["user_b"]
4572    );
4573
4574    // Contact requests are present upon connecting (tested here via disconnect/reconnect)
4575    disconnect_and_reconnect(&client_a, cx_a).await;
4576    disconnect_and_reconnect(&client_b, cx_b).await;
4577    disconnect_and_reconnect(&client_c, cx_c).await;
4578    executor.run_until_parked();
4579    assert_eq!(
4580        client_a.summarize_contacts(cx_a).outgoing_requests,
4581        &["user_b"]
4582    );
4583    assert_eq!(
4584        client_b.summarize_contacts(cx_b).incoming_requests,
4585        &["user_a", "user_c"]
4586    );
4587    assert_eq!(
4588        client_c.summarize_contacts(cx_c).outgoing_requests,
4589        &["user_b"]
4590    );
4591
4592    // User B accepts the request from user A.
4593    client_b
4594        .user_store
4595        .update(cx_b, |store, cx| {
4596            store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
4597        })
4598        .await
4599        .unwrap();
4600
4601    executor.run_until_parked();
4602
4603    // User B sees user A as their contact now in all client, and the incoming request from them is removed.
4604    let contacts_b = client_b.summarize_contacts(cx_b);
4605    assert_eq!(contacts_b.current, &["user_a"]);
4606    assert_eq!(contacts_b.incoming_requests, &["user_c"]);
4607    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
4608    assert_eq!(contacts_b2.current, &["user_a"]);
4609    assert_eq!(contacts_b2.incoming_requests, &["user_c"]);
4610
4611    // User A sees user B as their contact now in all clients, and the outgoing request to them is removed.
4612    let contacts_a = client_a.summarize_contacts(cx_a);
4613    assert_eq!(contacts_a.current, &["user_b"]);
4614    assert!(contacts_a.outgoing_requests.is_empty());
4615    let contacts_a2 = client_a2.summarize_contacts(cx_a2);
4616    assert_eq!(contacts_a2.current, &["user_b"]);
4617    assert!(contacts_a2.outgoing_requests.is_empty());
4618
4619    // Contacts are present upon connecting (tested here via disconnect/reconnect)
4620    disconnect_and_reconnect(&client_a, cx_a).await;
4621    disconnect_and_reconnect(&client_b, cx_b).await;
4622    disconnect_and_reconnect(&client_c, cx_c).await;
4623    executor.run_until_parked();
4624    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
4625    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
4626    assert_eq!(
4627        client_b.summarize_contacts(cx_b).incoming_requests,
4628        &["user_c"]
4629    );
4630    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
4631    assert_eq!(
4632        client_c.summarize_contacts(cx_c).outgoing_requests,
4633        &["user_b"]
4634    );
4635
4636    // User B rejects the request from user C.
4637    client_b
4638        .user_store
4639        .update(cx_b, |store, cx| {
4640            store.respond_to_contact_request(client_c.user_id().unwrap(), false, cx)
4641        })
4642        .await
4643        .unwrap();
4644
4645    executor.run_until_parked();
4646
4647    // User B doesn't see user C as their contact, and the incoming request from them is removed.
4648    let contacts_b = client_b.summarize_contacts(cx_b);
4649    assert_eq!(contacts_b.current, &["user_a"]);
4650    assert!(contacts_b.incoming_requests.is_empty());
4651    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
4652    assert_eq!(contacts_b2.current, &["user_a"]);
4653    assert!(contacts_b2.incoming_requests.is_empty());
4654
4655    // User C doesn't see user B as their contact, and the outgoing request to them is removed.
4656    let contacts_c = client_c.summarize_contacts(cx_c);
4657    assert!(contacts_c.current.is_empty());
4658    assert!(contacts_c.outgoing_requests.is_empty());
4659    let contacts_c2 = client_c2.summarize_contacts(cx_c2);
4660    assert!(contacts_c2.current.is_empty());
4661    assert!(contacts_c2.outgoing_requests.is_empty());
4662
4663    // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect)
4664    disconnect_and_reconnect(&client_a, cx_a).await;
4665    disconnect_and_reconnect(&client_b, cx_b).await;
4666    disconnect_and_reconnect(&client_c, cx_c).await;
4667    executor.run_until_parked();
4668    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
4669    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
4670    assert!(client_b
4671        .summarize_contacts(cx_b)
4672        .incoming_requests
4673        .is_empty());
4674    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
4675    assert!(client_c
4676        .summarize_contacts(cx_c)
4677        .outgoing_requests
4678        .is_empty());
4679
4680    async fn disconnect_and_reconnect(client: &TestClient, cx: &mut TestAppContext) {
4681        client.disconnect(&cx.to_async()).unwrap();
4682        client.clear_contacts(cx).await;
4683        client
4684            .authenticate_and_connect(false, &cx.to_async())
4685            .await
4686            .unwrap();
4687    }
4688}
4689
4690#[gpui::test(iterations = 10)]
4691async fn test_following(
4692    deterministic: Arc<Deterministic>,
4693    cx_a: &mut TestAppContext,
4694    cx_b: &mut TestAppContext,
4695) {
4696    cx_a.foreground().forbid_parking();
4697    cx_a.update(editor::init);
4698    cx_b.update(editor::init);
4699
4700    let mut server = TestServer::start(cx_a.background()).await;
4701    let client_a = server.create_client(cx_a, "user_a").await;
4702    let client_b = server.create_client(cx_b, "user_b").await;
4703    server
4704        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4705        .await;
4706    let active_call_a = cx_a.read(ActiveCall::global);
4707    let active_call_b = cx_b.read(ActiveCall::global);
4708
4709    client_a
4710        .fs
4711        .insert_tree(
4712            "/a",
4713            json!({
4714                "1.txt": "one",
4715                "2.txt": "two",
4716                "3.txt": "three",
4717            }),
4718        )
4719        .await;
4720    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
4721    active_call_a
4722        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
4723        .await
4724        .unwrap();
4725
4726    let project_id = active_call_a
4727        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4728        .await
4729        .unwrap();
4730    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4731    active_call_b
4732        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
4733        .await
4734        .unwrap();
4735
4736    // Client A opens some editors.
4737    let workspace_a = client_a.build_workspace(&project_a, cx_a);
4738    let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
4739    let editor_a1 = workspace_a
4740        .update(cx_a, |workspace, cx| {
4741            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
4742        })
4743        .await
4744        .unwrap()
4745        .downcast::<Editor>()
4746        .unwrap();
4747    let editor_a2 = workspace_a
4748        .update(cx_a, |workspace, cx| {
4749            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
4750        })
4751        .await
4752        .unwrap()
4753        .downcast::<Editor>()
4754        .unwrap();
4755
4756    // Client B opens an editor.
4757    let workspace_b = client_b.build_workspace(&project_b, cx_b);
4758    let editor_b1 = workspace_b
4759        .update(cx_b, |workspace, cx| {
4760            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
4761        })
4762        .await
4763        .unwrap()
4764        .downcast::<Editor>()
4765        .unwrap();
4766
4767    let client_a_id = project_b.read_with(cx_b, |project, _| {
4768        project.collaborators().values().next().unwrap().peer_id
4769    });
4770    let client_b_id = project_a.read_with(cx_a, |project, _| {
4771        project.collaborators().values().next().unwrap().peer_id
4772    });
4773
4774    // When client B starts following client A, all visible view states are replicated to client B.
4775    editor_a1.update(cx_a, |editor, cx| {
4776        editor.change_selections(None, cx, |s| s.select_ranges([0..1]))
4777    });
4778    editor_a2.update(cx_a, |editor, cx| {
4779        editor.change_selections(None, cx, |s| s.select_ranges([2..3]))
4780    });
4781    workspace_b
4782        .update(cx_b, |workspace, cx| {
4783            workspace
4784                .toggle_follow(&ToggleFollow(client_a_id), cx)
4785                .unwrap()
4786        })
4787        .await
4788        .unwrap();
4789
4790    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
4791        workspace
4792            .active_item(cx)
4793            .unwrap()
4794            .downcast::<Editor>()
4795            .unwrap()
4796    });
4797    assert!(cx_b.read(|cx| editor_b2.is_focused(cx)));
4798    assert_eq!(
4799        editor_b2.read_with(cx_b, |editor, cx| editor.project_path(cx)),
4800        Some((worktree_id, "2.txt").into())
4801    );
4802    assert_eq!(
4803        editor_b2.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
4804        vec![2..3]
4805    );
4806    assert_eq!(
4807        editor_b1.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
4808        vec![0..1]
4809    );
4810
4811    // When client A activates a different editor, client B does so as well.
4812    workspace_a.update(cx_a, |workspace, cx| {
4813        workspace.activate_item(&editor_a1, cx)
4814    });
4815    workspace_b
4816        .condition(cx_b, |workspace, cx| {
4817            workspace.active_item(cx).unwrap().id() == editor_b1.id()
4818        })
4819        .await;
4820
4821    // When client A navigates back and forth, client B does so as well.
4822    workspace_a
4823        .update(cx_a, |workspace, cx| {
4824            workspace::Pane::go_back(workspace, None, cx)
4825        })
4826        .await;
4827    workspace_b
4828        .condition(cx_b, |workspace, cx| {
4829            workspace.active_item(cx).unwrap().id() == editor_b2.id()
4830        })
4831        .await;
4832
4833    workspace_a
4834        .update(cx_a, |workspace, cx| {
4835            workspace::Pane::go_forward(workspace, None, cx)
4836        })
4837        .await;
4838    workspace_b
4839        .condition(cx_b, |workspace, cx| {
4840            workspace.active_item(cx).unwrap().id() == editor_b1.id()
4841        })
4842        .await;
4843
4844    // Changes to client A's editor are reflected on client B.
4845    editor_a1.update(cx_a, |editor, cx| {
4846        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2]));
4847    });
4848    editor_b1
4849        .condition(cx_b, |editor, cx| {
4850            editor.selections.ranges(cx) == vec![1..1, 2..2]
4851        })
4852        .await;
4853
4854    editor_a1.update(cx_a, |editor, cx| editor.set_text("TWO", cx));
4855    editor_b1
4856        .condition(cx_b, |editor, cx| editor.text(cx) == "TWO")
4857        .await;
4858
4859    editor_a1.update(cx_a, |editor, cx| {
4860        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
4861        editor.set_scroll_position(vec2f(0., 100.), cx);
4862    });
4863    editor_b1
4864        .condition(cx_b, |editor, cx| {
4865            editor.selections.ranges(cx) == vec![3..3]
4866        })
4867        .await;
4868
4869    // After unfollowing, client B stops receiving updates from client A.
4870    workspace_b.update(cx_b, |workspace, cx| {
4871        workspace.unfollow(&workspace.active_pane().clone(), cx)
4872    });
4873    workspace_a.update(cx_a, |workspace, cx| {
4874        workspace.activate_item(&editor_a2, cx)
4875    });
4876    deterministic.run_until_parked();
4877    assert_eq!(
4878        workspace_b.read_with(cx_b, |workspace, cx| workspace
4879            .active_item(cx)
4880            .unwrap()
4881            .id()),
4882        editor_b1.id()
4883    );
4884
4885    // Client A starts following client B.
4886    workspace_a
4887        .update(cx_a, |workspace, cx| {
4888            workspace
4889                .toggle_follow(&ToggleFollow(client_b_id), cx)
4890                .unwrap()
4891        })
4892        .await
4893        .unwrap();
4894    assert_eq!(
4895        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
4896        Some(client_b_id)
4897    );
4898    assert_eq!(
4899        workspace_a.read_with(cx_a, |workspace, cx| workspace
4900            .active_item(cx)
4901            .unwrap()
4902            .id()),
4903        editor_a1.id()
4904    );
4905
4906    // Client B activates an external window, which causes a new screen-sharing item to be added to the pane.
4907    let display = MacOSDisplay::new();
4908    active_call_b
4909        .update(cx_b, |call, cx| call.set_location(None, cx))
4910        .await
4911        .unwrap();
4912    active_call_b
4913        .update(cx_b, |call, cx| {
4914            call.room().unwrap().update(cx, |room, cx| {
4915                room.set_display_sources(vec![display.clone()]);
4916                room.share_screen(cx)
4917            })
4918        })
4919        .await
4920        .unwrap();
4921    deterministic.run_until_parked();
4922    let shared_screen = workspace_a.read_with(cx_a, |workspace, cx| {
4923        workspace
4924            .active_item(cx)
4925            .unwrap()
4926            .downcast::<SharedScreen>()
4927            .unwrap()
4928    });
4929
4930    // Client B activates Zed again, which causes the previous editor to become focused again.
4931    active_call_b
4932        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
4933        .await
4934        .unwrap();
4935    deterministic.run_until_parked();
4936    assert_eq!(
4937        workspace_a.read_with(cx_a, |workspace, cx| workspace
4938            .active_item(cx)
4939            .unwrap()
4940            .id()),
4941        editor_a1.id()
4942    );
4943
4944    // Client B activates an external window again, and the previously-opened screen-sharing item
4945    // gets activated.
4946    active_call_b
4947        .update(cx_b, |call, cx| call.set_location(None, cx))
4948        .await
4949        .unwrap();
4950    deterministic.run_until_parked();
4951    assert_eq!(
4952        workspace_a.read_with(cx_a, |workspace, cx| workspace
4953            .active_item(cx)
4954            .unwrap()
4955            .id()),
4956        shared_screen.id()
4957    );
4958
4959    // Following interrupts when client B disconnects.
4960    client_b.disconnect(&cx_b.to_async()).unwrap();
4961    deterministic.run_until_parked();
4962    assert_eq!(
4963        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
4964        None
4965    );
4966}
4967
4968#[gpui::test]
4969async fn test_following_tab_order(
4970    deterministic: Arc<Deterministic>,
4971    cx_a: &mut TestAppContext,
4972    cx_b: &mut TestAppContext,
4973) {
4974    cx_a.update(editor::init);
4975    cx_b.update(editor::init);
4976
4977    let mut server = TestServer::start(cx_a.background()).await;
4978    let client_a = server.create_client(cx_a, "user_a").await;
4979    let client_b = server.create_client(cx_b, "user_b").await;
4980    server
4981        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4982        .await;
4983    let active_call_a = cx_a.read(ActiveCall::global);
4984    let active_call_b = cx_b.read(ActiveCall::global);
4985
4986    client_a
4987        .fs
4988        .insert_tree(
4989            "/a",
4990            json!({
4991                "1.txt": "one",
4992                "2.txt": "two",
4993                "3.txt": "three",
4994            }),
4995        )
4996        .await;
4997    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
4998    active_call_a
4999        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
5000        .await
5001        .unwrap();
5002
5003    let project_id = active_call_a
5004        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5005        .await
5006        .unwrap();
5007    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5008    active_call_b
5009        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
5010        .await
5011        .unwrap();
5012
5013    let workspace_a = client_a.build_workspace(&project_a, cx_a);
5014    let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
5015
5016    let workspace_b = client_b.build_workspace(&project_b, cx_b);
5017    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
5018
5019    let client_b_id = project_a.read_with(cx_a, |project, _| {
5020        project.collaborators().values().next().unwrap().peer_id
5021    });
5022
5023    //Open 1, 3 in that order on client A
5024    workspace_a
5025        .update(cx_a, |workspace, cx| {
5026            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
5027        })
5028        .await
5029        .unwrap();
5030    workspace_a
5031        .update(cx_a, |workspace, cx| {
5032            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
5033        })
5034        .await
5035        .unwrap();
5036
5037    let pane_paths = |pane: &ViewHandle<workspace::Pane>, cx: &mut TestAppContext| {
5038        pane.update(cx, |pane, cx| {
5039            pane.items()
5040                .map(|item| {
5041                    item.project_path(cx)
5042                        .unwrap()
5043                        .path
5044                        .to_str()
5045                        .unwrap()
5046                        .to_owned()
5047                })
5048                .collect::<Vec<_>>()
5049        })
5050    };
5051
5052    //Verify that the tabs opened in the order we expect
5053    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt"]);
5054
5055    //Follow client B as client A
5056    workspace_a
5057        .update(cx_a, |workspace, cx| {
5058            workspace
5059                .toggle_follow(&ToggleFollow(client_b_id), cx)
5060                .unwrap()
5061        })
5062        .await
5063        .unwrap();
5064
5065    //Open just 2 on client B
5066    workspace_b
5067        .update(cx_b, |workspace, cx| {
5068            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
5069        })
5070        .await
5071        .unwrap();
5072    deterministic.run_until_parked();
5073
5074    // Verify that newly opened followed file is at the end
5075    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt", "2.txt"]);
5076
5077    //Open just 1 on client B
5078    workspace_b
5079        .update(cx_b, |workspace, cx| {
5080            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
5081        })
5082        .await
5083        .unwrap();
5084    assert_eq!(&pane_paths(&pane_b, cx_b), &["2.txt", "1.txt"]);
5085    deterministic.run_until_parked();
5086
5087    // Verify that following into 1 did not reorder
5088    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt", "2.txt"]);
5089}
5090
5091#[gpui::test(iterations = 10)]
5092async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
5093    cx_a.foreground().forbid_parking();
5094    cx_a.update(editor::init);
5095    cx_b.update(editor::init);
5096
5097    let mut server = TestServer::start(cx_a.background()).await;
5098    let client_a = server.create_client(cx_a, "user_a").await;
5099    let client_b = server.create_client(cx_b, "user_b").await;
5100    server
5101        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5102        .await;
5103    let active_call_a = cx_a.read(ActiveCall::global);
5104    let active_call_b = cx_b.read(ActiveCall::global);
5105
5106    // Client A shares a project.
5107    client_a
5108        .fs
5109        .insert_tree(
5110            "/a",
5111            json!({
5112                "1.txt": "one",
5113                "2.txt": "two",
5114                "3.txt": "three",
5115                "4.txt": "four",
5116            }),
5117        )
5118        .await;
5119    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
5120    active_call_a
5121        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
5122        .await
5123        .unwrap();
5124    let project_id = active_call_a
5125        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5126        .await
5127        .unwrap();
5128
5129    // Client B joins the project.
5130    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5131    active_call_b
5132        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
5133        .await
5134        .unwrap();
5135
5136    // Client A opens some editors.
5137    let workspace_a = client_a.build_workspace(&project_a, cx_a);
5138    let pane_a1 = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
5139    let _editor_a1 = workspace_a
5140        .update(cx_a, |workspace, cx| {
5141            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
5142        })
5143        .await
5144        .unwrap()
5145        .downcast::<Editor>()
5146        .unwrap();
5147
5148    // Client B opens an editor.
5149    let workspace_b = client_b.build_workspace(&project_b, cx_b);
5150    let pane_b1 = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
5151    let _editor_b1 = workspace_b
5152        .update(cx_b, |workspace, cx| {
5153            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
5154        })
5155        .await
5156        .unwrap()
5157        .downcast::<Editor>()
5158        .unwrap();
5159
5160    // Clients A and B follow each other in split panes
5161    workspace_a.update(cx_a, |workspace, cx| {
5162        workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
5163        let pane_a1 = pane_a1.clone();
5164        cx.defer(move |workspace, _| {
5165            assert_ne!(*workspace.active_pane(), pane_a1);
5166        });
5167    });
5168    workspace_a
5169        .update(cx_a, |workspace, cx| {
5170            let leader_id = *project_a.read(cx).collaborators().keys().next().unwrap();
5171            workspace
5172                .toggle_follow(&workspace::ToggleFollow(leader_id), cx)
5173                .unwrap()
5174        })
5175        .await
5176        .unwrap();
5177    workspace_b.update(cx_b, |workspace, cx| {
5178        workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
5179        let pane_b1 = pane_b1.clone();
5180        cx.defer(move |workspace, _| {
5181            assert_ne!(*workspace.active_pane(), pane_b1);
5182        });
5183    });
5184    workspace_b
5185        .update(cx_b, |workspace, cx| {
5186            let leader_id = *project_b.read(cx).collaborators().keys().next().unwrap();
5187            workspace
5188                .toggle_follow(&workspace::ToggleFollow(leader_id), cx)
5189                .unwrap()
5190        })
5191        .await
5192        .unwrap();
5193
5194    workspace_a.update(cx_a, |workspace, cx| {
5195        workspace.activate_next_pane(cx);
5196    });
5197    // Wait for focus effects to be fully flushed
5198    workspace_a.update(cx_a, |workspace, _| {
5199        assert_eq!(*workspace.active_pane(), pane_a1);
5200    });
5201
5202    workspace_a
5203        .update(cx_a, |workspace, cx| {
5204            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
5205        })
5206        .await
5207        .unwrap();
5208    workspace_b.update(cx_b, |workspace, cx| {
5209        workspace.activate_next_pane(cx);
5210    });
5211
5212    workspace_b
5213        .update(cx_b, |workspace, cx| {
5214            assert_eq!(*workspace.active_pane(), pane_b1);
5215            workspace.open_path((worktree_id, "4.txt"), None, true, cx)
5216        })
5217        .await
5218        .unwrap();
5219    cx_a.foreground().run_until_parked();
5220
5221    // Ensure leader updates don't change the active pane of followers
5222    workspace_a.read_with(cx_a, |workspace, _| {
5223        assert_eq!(*workspace.active_pane(), pane_a1);
5224    });
5225    workspace_b.read_with(cx_b, |workspace, _| {
5226        assert_eq!(*workspace.active_pane(), pane_b1);
5227    });
5228
5229    // Ensure peers following each other doesn't cause an infinite loop.
5230    assert_eq!(
5231        workspace_a.read_with(cx_a, |workspace, cx| workspace
5232            .active_item(cx)
5233            .unwrap()
5234            .project_path(cx)),
5235        Some((worktree_id, "3.txt").into())
5236    );
5237    workspace_a.update(cx_a, |workspace, cx| {
5238        assert_eq!(
5239            workspace.active_item(cx).unwrap().project_path(cx),
5240            Some((worktree_id, "3.txt").into())
5241        );
5242        workspace.activate_next_pane(cx);
5243    });
5244
5245    workspace_a.update(cx_a, |workspace, cx| {
5246        assert_eq!(
5247            workspace.active_item(cx).unwrap().project_path(cx),
5248            Some((worktree_id, "4.txt").into())
5249        );
5250    });
5251
5252    workspace_b.update(cx_b, |workspace, cx| {
5253        assert_eq!(
5254            workspace.active_item(cx).unwrap().project_path(cx),
5255            Some((worktree_id, "4.txt").into())
5256        );
5257        workspace.activate_next_pane(cx);
5258    });
5259
5260    workspace_b.update(cx_b, |workspace, cx| {
5261        assert_eq!(
5262            workspace.active_item(cx).unwrap().project_path(cx),
5263            Some((worktree_id, "3.txt").into())
5264        );
5265    });
5266}
5267
5268#[gpui::test(iterations = 10)]
5269async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
5270    cx_a.foreground().forbid_parking();
5271    cx_a.update(editor::init);
5272    cx_b.update(editor::init);
5273
5274    // 2 clients connect to a server.
5275    let mut server = TestServer::start(cx_a.background()).await;
5276    let client_a = server.create_client(cx_a, "user_a").await;
5277    let client_b = server.create_client(cx_b, "user_b").await;
5278    server
5279        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5280        .await;
5281    let active_call_a = cx_a.read(ActiveCall::global);
5282    let active_call_b = cx_b.read(ActiveCall::global);
5283
5284    // Client A shares a project.
5285    client_a
5286        .fs
5287        .insert_tree(
5288            "/a",
5289            json!({
5290                "1.txt": "one",
5291                "2.txt": "two",
5292                "3.txt": "three",
5293            }),
5294        )
5295        .await;
5296    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
5297    active_call_a
5298        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
5299        .await
5300        .unwrap();
5301
5302    let project_id = active_call_a
5303        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5304        .await
5305        .unwrap();
5306    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5307    active_call_b
5308        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
5309        .await
5310        .unwrap();
5311
5312    // Client A opens some editors.
5313    let workspace_a = client_a.build_workspace(&project_a, cx_a);
5314    let _editor_a1 = workspace_a
5315        .update(cx_a, |workspace, cx| {
5316            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
5317        })
5318        .await
5319        .unwrap()
5320        .downcast::<Editor>()
5321        .unwrap();
5322
5323    // Client B starts following client A.
5324    let workspace_b = client_b.build_workspace(&project_b, cx_b);
5325    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
5326    let leader_id = project_b.read_with(cx_b, |project, _| {
5327        project.collaborators().values().next().unwrap().peer_id
5328    });
5329    workspace_b
5330        .update(cx_b, |workspace, cx| {
5331            workspace
5332                .toggle_follow(&ToggleFollow(leader_id), cx)
5333                .unwrap()
5334        })
5335        .await
5336        .unwrap();
5337    assert_eq!(
5338        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5339        Some(leader_id)
5340    );
5341    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
5342        workspace
5343            .active_item(cx)
5344            .unwrap()
5345            .downcast::<Editor>()
5346            .unwrap()
5347    });
5348
5349    // When client B moves, it automatically stops following client A.
5350    editor_b2.update(cx_b, |editor, cx| editor.move_right(&editor::MoveRight, cx));
5351    assert_eq!(
5352        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5353        None
5354    );
5355
5356    workspace_b
5357        .update(cx_b, |workspace, cx| {
5358            workspace
5359                .toggle_follow(&ToggleFollow(leader_id), cx)
5360                .unwrap()
5361        })
5362        .await
5363        .unwrap();
5364    assert_eq!(
5365        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5366        Some(leader_id)
5367    );
5368
5369    // When client B edits, it automatically stops following client A.
5370    editor_b2.update(cx_b, |editor, cx| editor.insert("X", cx));
5371    assert_eq!(
5372        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5373        None
5374    );
5375
5376    workspace_b
5377        .update(cx_b, |workspace, cx| {
5378            workspace
5379                .toggle_follow(&ToggleFollow(leader_id), cx)
5380                .unwrap()
5381        })
5382        .await
5383        .unwrap();
5384    assert_eq!(
5385        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5386        Some(leader_id)
5387    );
5388
5389    // When client B scrolls, it automatically stops following client A.
5390    editor_b2.update(cx_b, |editor, cx| {
5391        editor.set_scroll_position(vec2f(0., 3.), cx)
5392    });
5393    assert_eq!(
5394        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5395        None
5396    );
5397
5398    workspace_b
5399        .update(cx_b, |workspace, cx| {
5400            workspace
5401                .toggle_follow(&ToggleFollow(leader_id), cx)
5402                .unwrap()
5403        })
5404        .await
5405        .unwrap();
5406    assert_eq!(
5407        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5408        Some(leader_id)
5409    );
5410
5411    // When client B activates a different pane, it continues following client A in the original pane.
5412    workspace_b.update(cx_b, |workspace, cx| {
5413        workspace.split_pane(pane_b.clone(), SplitDirection::Right, cx)
5414    });
5415    assert_eq!(
5416        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5417        Some(leader_id)
5418    );
5419
5420    workspace_b.update(cx_b, |workspace, cx| workspace.activate_next_pane(cx));
5421    assert_eq!(
5422        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5423        Some(leader_id)
5424    );
5425
5426    // When client B activates a different item in the original pane, it automatically stops following client A.
5427    workspace_b
5428        .update(cx_b, |workspace, cx| {
5429            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
5430        })
5431        .await
5432        .unwrap();
5433    assert_eq!(
5434        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5435        None
5436    );
5437}
5438
5439#[gpui::test(iterations = 10)]
5440async fn test_peers_simultaneously_following_each_other(
5441    deterministic: Arc<Deterministic>,
5442    cx_a: &mut TestAppContext,
5443    cx_b: &mut TestAppContext,
5444) {
5445    deterministic.forbid_parking();
5446    cx_a.update(editor::init);
5447    cx_b.update(editor::init);
5448
5449    let mut server = TestServer::start(cx_a.background()).await;
5450    let client_a = server.create_client(cx_a, "user_a").await;
5451    let client_b = server.create_client(cx_b, "user_b").await;
5452    server
5453        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5454        .await;
5455    let active_call_a = cx_a.read(ActiveCall::global);
5456
5457    client_a.fs.insert_tree("/a", json!({})).await;
5458    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
5459    let workspace_a = client_a.build_workspace(&project_a, cx_a);
5460    let project_id = active_call_a
5461        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5462        .await
5463        .unwrap();
5464
5465    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5466    let workspace_b = client_b.build_workspace(&project_b, cx_b);
5467
5468    deterministic.run_until_parked();
5469    let client_a_id = project_b.read_with(cx_b, |project, _| {
5470        project.collaborators().values().next().unwrap().peer_id
5471    });
5472    let client_b_id = project_a.read_with(cx_a, |project, _| {
5473        project.collaborators().values().next().unwrap().peer_id
5474    });
5475
5476    let a_follow_b = workspace_a.update(cx_a, |workspace, cx| {
5477        workspace
5478            .toggle_follow(&ToggleFollow(client_b_id), cx)
5479            .unwrap()
5480    });
5481    let b_follow_a = workspace_b.update(cx_b, |workspace, cx| {
5482        workspace
5483            .toggle_follow(&ToggleFollow(client_a_id), cx)
5484            .unwrap()
5485    });
5486
5487    futures::try_join!(a_follow_b, b_follow_a).unwrap();
5488    workspace_a.read_with(cx_a, |workspace, _| {
5489        assert_eq!(
5490            workspace.leader_for_pane(workspace.active_pane()),
5491            Some(client_b_id)
5492        );
5493    });
5494    workspace_b.read_with(cx_b, |workspace, _| {
5495        assert_eq!(
5496            workspace.leader_for_pane(workspace.active_pane()),
5497            Some(client_a_id)
5498        );
5499    });
5500}
5501
5502#[gpui::test(iterations = 100)]
5503async fn test_random_collaboration(
5504    cx: &mut TestAppContext,
5505    deterministic: Arc<Deterministic>,
5506    rng: StdRng,
5507) {
5508    deterministic.forbid_parking();
5509    let rng = Arc::new(Mutex::new(rng));
5510
5511    let max_peers = env::var("MAX_PEERS")
5512        .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
5513        .unwrap_or(5);
5514
5515    let max_operations = env::var("OPERATIONS")
5516        .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
5517        .unwrap_or(10);
5518
5519    let mut server = TestServer::start(cx.background()).await;
5520    let db = server.app_state.db.clone();
5521
5522    let mut available_guests = Vec::new();
5523    for ix in 0..max_peers {
5524        let username = format!("guest-{}", ix + 1);
5525        let user_id = db
5526            .create_user(
5527                &format!("{username}@example.com"),
5528                false,
5529                NewUserParams {
5530                    github_login: username.clone(),
5531                    github_user_id: (ix + 1) as i32,
5532                    invite_count: 0,
5533                },
5534            )
5535            .await
5536            .unwrap()
5537            .user_id;
5538        available_guests.push((user_id, username));
5539    }
5540
5541    for (ix, (user_id_a, _)) in available_guests.iter().enumerate() {
5542        for (user_id_b, _) in &available_guests[ix + 1..] {
5543            server
5544                .app_state
5545                .db
5546                .send_contact_request(*user_id_a, *user_id_b)
5547                .await
5548                .unwrap();
5549            server
5550                .app_state
5551                .db
5552                .respond_to_contact_request(*user_id_b, *user_id_a, true)
5553                .await
5554                .unwrap();
5555        }
5556    }
5557
5558    let mut clients = Vec::new();
5559    let mut user_ids = Vec::new();
5560    let mut peer_ids = Vec::new();
5561    let mut op_start_signals = Vec::new();
5562    let mut next_entity_id = 100000;
5563
5564    let mut operations = 0;
5565    while operations < max_operations {
5566        let distribution = rng.lock().gen_range(0..100);
5567        match distribution {
5568            0..=19 if !available_guests.is_empty() => {
5569                let guest_ix = rng.lock().gen_range(0..available_guests.len());
5570                let (_, guest_username) = available_guests.remove(guest_ix);
5571                log::info!("Adding new connection for {}", guest_username);
5572                next_entity_id += 100000;
5573                let mut guest_cx = TestAppContext::new(
5574                    cx.foreground_platform(),
5575                    cx.platform(),
5576                    deterministic.build_foreground(next_entity_id),
5577                    deterministic.build_background(),
5578                    cx.font_cache(),
5579                    cx.leak_detector(),
5580                    next_entity_id,
5581                    cx.function_name.clone(),
5582                );
5583
5584                let op_start_signal = futures::channel::mpsc::unbounded();
5585                let guest = server.create_client(&mut guest_cx, &guest_username).await;
5586                user_ids.push(guest.current_user_id(&guest_cx));
5587                peer_ids.push(guest.peer_id().unwrap());
5588                op_start_signals.push(op_start_signal.0);
5589                clients.push(guest_cx.foreground().spawn(guest.simulate(
5590                    guest_username.clone(),
5591                    op_start_signal.1,
5592                    rng.clone(),
5593                    guest_cx,
5594                )));
5595
5596                log::info!("Added connection for {}", guest_username);
5597                operations += 1;
5598            }
5599            20..=29 if clients.len() > 1 => {
5600                let guest_ix = rng.lock().gen_range(1..clients.len());
5601                log::info!("Removing guest {}", user_ids[guest_ix]);
5602                let removed_guest_id = user_ids.remove(guest_ix);
5603                let removed_peer_id = peer_ids.remove(guest_ix);
5604                let guest = clients.remove(guest_ix);
5605                op_start_signals.remove(guest_ix);
5606                server.forbid_connections();
5607                server.disconnect_client(removed_peer_id);
5608                deterministic.advance_clock(RECEIVE_TIMEOUT);
5609                deterministic.start_waiting();
5610                log::info!("Waiting for guest {} to exit...", removed_guest_id);
5611                let (guest, mut guest_cx) = guest.await;
5612                deterministic.finish_waiting();
5613                server.allow_connections();
5614
5615                for project in &guest.remote_projects {
5616                    project.read_with(&guest_cx, |project, _| assert!(project.is_read_only()));
5617                }
5618                for user_id in &user_ids {
5619                    let contacts = server.app_state.db.get_contacts(*user_id).await.unwrap();
5620                    let pool = server.connection_pool.lock().await;
5621                    for contact in contacts {
5622                        if let db::Contact::Accepted { user_id, .. } = contact {
5623                            if pool.is_user_online(user_id) {
5624                                assert_ne!(
5625                                    user_id, removed_guest_id,
5626                                    "removed guest is still a contact of another peer"
5627                                );
5628                            }
5629                        }
5630                    }
5631                }
5632
5633                log::info!("{} removed", guest.username);
5634                available_guests.push((removed_guest_id, guest.username.clone()));
5635                guest_cx.update(|cx| {
5636                    cx.clear_globals();
5637                    drop(guest);
5638                });
5639
5640                operations += 1;
5641            }
5642            _ if !op_start_signals.is_empty() => {
5643                while operations < max_operations && rng.lock().gen_bool(0.7) {
5644                    op_start_signals
5645                        .choose(&mut *rng.lock())
5646                        .unwrap()
5647                        .unbounded_send(())
5648                        .unwrap();
5649                    operations += 1;
5650                }
5651
5652                if rng.lock().gen_bool(0.8) {
5653                    deterministic.run_until_parked();
5654                }
5655            }
5656            _ => {}
5657        }
5658    }
5659
5660    drop(op_start_signals);
5661    deterministic.start_waiting();
5662    let clients = futures::future::join_all(clients).await;
5663    deterministic.finish_waiting();
5664    deterministic.run_until_parked();
5665
5666    for (guest_client, guest_cx) in &clients {
5667        for guest_project in &guest_client.remote_projects {
5668            guest_project.read_with(guest_cx, |guest_project, cx| {
5669                let host_project = clients.iter().find_map(|(client, cx)| {
5670                    let project = client.local_projects.iter().find(|host_project| {
5671                        host_project.read_with(cx, |host_project, _| {
5672                            host_project.remote_id() == guest_project.remote_id()
5673                        })
5674                    })?;
5675                    Some((project, cx))
5676                });
5677
5678                if !guest_project.is_read_only() {
5679                    if let Some((host_project, host_cx)) = host_project {
5680                        let host_worktree_snapshots =
5681                            host_project.read_with(host_cx, |host_project, cx| {
5682                                host_project
5683                                    .worktrees(cx)
5684                                    .map(|worktree| {
5685                                        let worktree = worktree.read(cx);
5686                                        (worktree.id(), worktree.snapshot())
5687                                    })
5688                                    .collect::<BTreeMap<_, _>>()
5689                            });
5690                        let guest_worktree_snapshots = guest_project
5691                            .worktrees(cx)
5692                            .map(|worktree| {
5693                                let worktree = worktree.read(cx);
5694                                (worktree.id(), worktree.snapshot())
5695                            })
5696                            .collect::<BTreeMap<_, _>>();
5697
5698                        assert_eq!(
5699                            guest_worktree_snapshots.keys().collect::<Vec<_>>(),
5700                            host_worktree_snapshots.keys().collect::<Vec<_>>(),
5701                            "{} has different worktrees than the host",
5702                            guest_client.username
5703                        );
5704
5705                        for (id, host_snapshot) in &host_worktree_snapshots {
5706                            let guest_snapshot = &guest_worktree_snapshots[id];
5707                            assert_eq!(
5708                                guest_snapshot.root_name(),
5709                                host_snapshot.root_name(),
5710                                "{} has different root name than the host for worktree {}",
5711                                guest_client.username,
5712                                id
5713                            );
5714                            assert_eq!(
5715                                guest_snapshot.abs_path(),
5716                                host_snapshot.abs_path(),
5717                                "{} has different abs path than the host for worktree {}",
5718                                guest_client.username,
5719                                id
5720                            );
5721                            assert_eq!(
5722                                guest_snapshot.entries(false).collect::<Vec<_>>(),
5723                                host_snapshot.entries(false).collect::<Vec<_>>(),
5724                                "{} has different snapshot than the host for worktree {}",
5725                                guest_client.username,
5726                                id
5727                            );
5728                            assert_eq!(guest_snapshot.scan_id(), host_snapshot.scan_id());
5729                        }
5730                    }
5731                }
5732
5733                guest_project.check_invariants(cx);
5734            });
5735        }
5736
5737        for (guest_project, guest_buffers) in &guest_client.buffers {
5738            let project_id = if guest_project.read_with(guest_cx, |project, _| {
5739                project.is_local() || project.is_read_only()
5740            }) {
5741                continue;
5742            } else {
5743                guest_project
5744                    .read_with(guest_cx, |project, _| project.remote_id())
5745                    .unwrap()
5746            };
5747
5748            let host_project = clients.iter().find_map(|(client, cx)| {
5749                let project = client.local_projects.iter().find(|host_project| {
5750                    host_project.read_with(cx, |host_project, _| {
5751                        host_project.remote_id() == Some(project_id)
5752                    })
5753                })?;
5754                Some((project, cx))
5755            });
5756
5757            let (host_project, host_cx) = if let Some((host_project, host_cx)) = host_project {
5758                (host_project, host_cx)
5759            } else {
5760                continue;
5761            };
5762
5763            for guest_buffer in guest_buffers {
5764                let buffer_id = guest_buffer.read_with(guest_cx, |buffer, _| buffer.remote_id());
5765                let host_buffer = host_project.read_with(host_cx, |project, cx| {
5766                    project.buffer_for_id(buffer_id, cx).unwrap_or_else(|| {
5767                        panic!(
5768                            "host does not have buffer for guest:{}, peer:{:?}, id:{}",
5769                            guest_client.username,
5770                            guest_client.peer_id(),
5771                            buffer_id
5772                        )
5773                    })
5774                });
5775                let path = host_buffer
5776                    .read_with(host_cx, |buffer, cx| buffer.file().unwrap().full_path(cx));
5777
5778                assert_eq!(
5779                    guest_buffer.read_with(guest_cx, |buffer, _| buffer.deferred_ops_len()),
5780                    0,
5781                    "{}, buffer {}, path {:?} has deferred operations",
5782                    guest_client.username,
5783                    buffer_id,
5784                    path,
5785                );
5786                assert_eq!(
5787                    guest_buffer.read_with(guest_cx, |buffer, _| buffer.text()),
5788                    host_buffer.read_with(host_cx, |buffer, _| buffer.text()),
5789                    "{}, buffer {}, path {:?}, differs from the host's buffer",
5790                    guest_client.username,
5791                    buffer_id,
5792                    path
5793                );
5794            }
5795        }
5796    }
5797
5798    for (client, mut cx) in clients {
5799        cx.update(|cx| {
5800            cx.clear_globals();
5801            drop(client);
5802        });
5803    }
5804}
5805
5806struct TestServer {
5807    peer: Arc<Peer>,
5808    app_state: Arc<AppState>,
5809    server: Arc<Server>,
5810    connection_killers: Arc<Mutex<HashMap<PeerId, Arc<AtomicBool>>>>,
5811    forbid_connections: Arc<AtomicBool>,
5812    _test_db: TestDb,
5813    test_live_kit_server: Arc<live_kit_client::TestServer>,
5814}
5815
5816impl TestServer {
5817    async fn start(background: Arc<executor::Background>) -> Self {
5818        static NEXT_LIVE_KIT_SERVER_ID: AtomicUsize = AtomicUsize::new(0);
5819
5820        let use_postgres = env::var("USE_POSTGRES").ok();
5821        let use_postgres = use_postgres.as_deref();
5822        let test_db = if use_postgres == Some("true") || use_postgres == Some("1") {
5823            TestDb::postgres(background.clone())
5824        } else {
5825            TestDb::sqlite(background.clone())
5826        };
5827        let live_kit_server_id = NEXT_LIVE_KIT_SERVER_ID.fetch_add(1, SeqCst);
5828        let live_kit_server = live_kit_client::TestServer::create(
5829            format!("http://livekit.{}.test", live_kit_server_id),
5830            format!("devkey-{}", live_kit_server_id),
5831            format!("secret-{}", live_kit_server_id),
5832            background.clone(),
5833        )
5834        .unwrap();
5835        let app_state = Self::build_app_state(&test_db, &live_kit_server).await;
5836        let peer = Peer::new();
5837        let server = Server::new(app_state.clone());
5838        Self {
5839            peer,
5840            app_state,
5841            server,
5842            connection_killers: Default::default(),
5843            forbid_connections: Default::default(),
5844            _test_db: test_db,
5845            test_live_kit_server: live_kit_server,
5846        }
5847    }
5848
5849    async fn create_client(&mut self, cx: &mut TestAppContext, name: &str) -> TestClient {
5850        cx.update(|cx| {
5851            cx.set_global(HomeDir(Path::new("/tmp/").to_path_buf()));
5852
5853            let mut settings = Settings::test(cx);
5854            settings.projects_online_by_default = false;
5855            cx.set_global(settings);
5856        });
5857
5858        let http = FakeHttpClient::with_404_response();
5859        let user_id = if let Ok(Some(user)) = self
5860            .app_state
5861            .db
5862            .get_user_by_github_account(name, None)
5863            .await
5864        {
5865            user.id
5866        } else {
5867            self.app_state
5868                .db
5869                .create_user(
5870                    &format!("{name}@example.com"),
5871                    false,
5872                    NewUserParams {
5873                        github_login: name.into(),
5874                        github_user_id: 0,
5875                        invite_count: 0,
5876                    },
5877                )
5878                .await
5879                .expect("creating user failed")
5880                .user_id
5881        };
5882        let client_name = name.to_string();
5883        let mut client = cx.read(|cx| Client::new(http.clone(), cx));
5884        let server = self.server.clone();
5885        let db = self.app_state.db.clone();
5886        let connection_killers = self.connection_killers.clone();
5887        let forbid_connections = self.forbid_connections.clone();
5888
5889        Arc::get_mut(&mut client)
5890            .unwrap()
5891            .set_id(user_id.0 as usize)
5892            .override_authenticate(move |cx| {
5893                cx.spawn(|_| async move {
5894                    let access_token = "the-token".to_string();
5895                    Ok(Credentials {
5896                        user_id: user_id.0 as u64,
5897                        access_token,
5898                    })
5899                })
5900            })
5901            .override_establish_connection(move |credentials, cx| {
5902                assert_eq!(credentials.user_id, user_id.0 as u64);
5903                assert_eq!(credentials.access_token, "the-token");
5904
5905                let server = server.clone();
5906                let db = db.clone();
5907                let connection_killers = connection_killers.clone();
5908                let forbid_connections = forbid_connections.clone();
5909                let client_name = client_name.clone();
5910                cx.spawn(move |cx| async move {
5911                    if forbid_connections.load(SeqCst) {
5912                        Err(EstablishConnectionError::other(anyhow!(
5913                            "server is forbidding connections"
5914                        )))
5915                    } else {
5916                        let (client_conn, server_conn, killed) =
5917                            Connection::in_memory(cx.background());
5918                        let (connection_id_tx, connection_id_rx) = oneshot::channel();
5919                        let user = db
5920                            .get_user_by_id(user_id)
5921                            .await
5922                            .expect("retrieving user failed")
5923                            .unwrap();
5924                        cx.background()
5925                            .spawn(server.handle_connection(
5926                                server_conn,
5927                                client_name,
5928                                user,
5929                                Some(connection_id_tx),
5930                                cx.background(),
5931                            ))
5932                            .detach();
5933                        let connection_id = connection_id_rx.await.unwrap();
5934                        connection_killers
5935                            .lock()
5936                            .insert(PeerId(connection_id.0), killed);
5937                        Ok(client_conn)
5938                    }
5939                })
5940            });
5941
5942        let fs = FakeFs::new(cx.background());
5943        let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx));
5944        let app_state = Arc::new(workspace::AppState {
5945            client: client.clone(),
5946            user_store: user_store.clone(),
5947            languages: Arc::new(LanguageRegistry::new(Task::ready(()))),
5948            themes: ThemeRegistry::new((), cx.font_cache()),
5949            fs: fs.clone(),
5950            build_window_options: Default::default,
5951            initialize_workspace: |_, _, _| unimplemented!(),
5952            default_item_factory: |_, _| unimplemented!(),
5953        });
5954
5955        Project::init(&client);
5956        cx.update(|cx| {
5957            workspace::init(app_state.clone(), cx);
5958            call::init(client.clone(), user_store.clone(), cx);
5959        });
5960
5961        client
5962            .authenticate_and_connect(false, &cx.to_async())
5963            .await
5964            .unwrap();
5965
5966        let client = TestClient {
5967            client,
5968            username: name.to_string(),
5969            local_projects: Default::default(),
5970            remote_projects: Default::default(),
5971            next_root_dir_id: 0,
5972            user_store,
5973            fs,
5974            language_registry: Arc::new(LanguageRegistry::test()),
5975            buffers: Default::default(),
5976        };
5977        client.wait_for_current_user(cx).await;
5978        client
5979    }
5980
5981    fn disconnect_client(&self, peer_id: PeerId) {
5982        self.connection_killers
5983            .lock()
5984            .remove(&peer_id)
5985            .unwrap()
5986            .store(true, SeqCst);
5987    }
5988
5989    fn forbid_connections(&self) {
5990        self.forbid_connections.store(true, SeqCst);
5991    }
5992
5993    fn allow_connections(&self) {
5994        self.forbid_connections.store(false, SeqCst);
5995    }
5996
5997    async fn make_contacts(&self, clients: &mut [(&TestClient, &mut TestAppContext)]) {
5998        for ix in 1..clients.len() {
5999            let (left, right) = clients.split_at_mut(ix);
6000            let (client_a, cx_a) = left.last_mut().unwrap();
6001            for (client_b, cx_b) in right {
6002                client_a
6003                    .user_store
6004                    .update(*cx_a, |store, cx| {
6005                        store.request_contact(client_b.user_id().unwrap(), cx)
6006                    })
6007                    .await
6008                    .unwrap();
6009                cx_a.foreground().run_until_parked();
6010                client_b
6011                    .user_store
6012                    .update(*cx_b, |store, cx| {
6013                        store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
6014                    })
6015                    .await
6016                    .unwrap();
6017            }
6018        }
6019    }
6020
6021    async fn create_room(&self, clients: &mut [(&TestClient, &mut TestAppContext)]) {
6022        self.make_contacts(clients).await;
6023
6024        let (left, right) = clients.split_at_mut(1);
6025        let (_client_a, cx_a) = &mut left[0];
6026        let active_call_a = cx_a.read(ActiveCall::global);
6027
6028        for (client_b, cx_b) in right {
6029            let user_id_b = client_b.current_user_id(*cx_b).to_proto();
6030            active_call_a
6031                .update(*cx_a, |call, cx| call.invite(user_id_b, None, cx))
6032                .await
6033                .unwrap();
6034
6035            cx_b.foreground().run_until_parked();
6036            let active_call_b = cx_b.read(ActiveCall::global);
6037            active_call_b
6038                .update(*cx_b, |call, cx| call.accept_incoming(cx))
6039                .await
6040                .unwrap();
6041        }
6042    }
6043
6044    async fn build_app_state(
6045        test_db: &TestDb,
6046        fake_server: &live_kit_client::TestServer,
6047    ) -> Arc<AppState> {
6048        Arc::new(AppState {
6049            db: test_db.db().clone(),
6050            live_kit_client: Some(Arc::new(fake_server.create_api_client())),
6051            config: Default::default(),
6052        })
6053    }
6054}
6055
6056impl Deref for TestServer {
6057    type Target = Server;
6058
6059    fn deref(&self) -> &Self::Target {
6060        &self.server
6061    }
6062}
6063
6064impl Drop for TestServer {
6065    fn drop(&mut self) {
6066        self.peer.reset();
6067        self.test_live_kit_server.teardown().unwrap();
6068    }
6069}
6070
6071struct TestClient {
6072    client: Arc<Client>,
6073    username: String,
6074    local_projects: Vec<ModelHandle<Project>>,
6075    remote_projects: Vec<ModelHandle<Project>>,
6076    next_root_dir_id: usize,
6077    pub user_store: ModelHandle<UserStore>,
6078    language_registry: Arc<LanguageRegistry>,
6079    fs: Arc<FakeFs>,
6080    buffers: HashMap<ModelHandle<Project>, HashSet<ModelHandle<language::Buffer>>>,
6081}
6082
6083impl Deref for TestClient {
6084    type Target = Arc<Client>;
6085
6086    fn deref(&self) -> &Self::Target {
6087        &self.client
6088    }
6089}
6090
6091struct ContactsSummary {
6092    pub current: Vec<String>,
6093    pub outgoing_requests: Vec<String>,
6094    pub incoming_requests: Vec<String>,
6095}
6096
6097impl TestClient {
6098    pub fn current_user_id(&self, cx: &TestAppContext) -> UserId {
6099        UserId::from_proto(
6100            self.user_store
6101                .read_with(cx, |user_store, _| user_store.current_user().unwrap().id),
6102        )
6103    }
6104
6105    async fn wait_for_current_user(&self, cx: &TestAppContext) {
6106        let mut authed_user = self
6107            .user_store
6108            .read_with(cx, |user_store, _| user_store.watch_current_user());
6109        while authed_user.next().await.unwrap().is_none() {}
6110    }
6111
6112    async fn clear_contacts(&self, cx: &mut TestAppContext) {
6113        self.user_store
6114            .update(cx, |store, _| store.clear_contacts())
6115            .await;
6116    }
6117
6118    fn summarize_contacts(&self, cx: &TestAppContext) -> ContactsSummary {
6119        self.user_store.read_with(cx, |store, _| ContactsSummary {
6120            current: store
6121                .contacts()
6122                .iter()
6123                .map(|contact| contact.user.github_login.clone())
6124                .collect(),
6125            outgoing_requests: store
6126                .outgoing_contact_requests()
6127                .iter()
6128                .map(|user| user.github_login.clone())
6129                .collect(),
6130            incoming_requests: store
6131                .incoming_contact_requests()
6132                .iter()
6133                .map(|user| user.github_login.clone())
6134                .collect(),
6135        })
6136    }
6137
6138    async fn build_local_project(
6139        &self,
6140        root_path: impl AsRef<Path>,
6141        cx: &mut TestAppContext,
6142    ) -> (ModelHandle<Project>, WorktreeId) {
6143        let project = cx.update(|cx| {
6144            Project::local(
6145                self.client.clone(),
6146                self.user_store.clone(),
6147                self.language_registry.clone(),
6148                self.fs.clone(),
6149                cx,
6150            )
6151        });
6152        let (worktree, _) = project
6153            .update(cx, |p, cx| {
6154                p.find_or_create_local_worktree(root_path, true, cx)
6155            })
6156            .await
6157            .unwrap();
6158        worktree
6159            .read_with(cx, |tree, _| tree.as_local().unwrap().scan_complete())
6160            .await;
6161        (project, worktree.read_with(cx, |tree, _| tree.id()))
6162    }
6163
6164    async fn build_remote_project(
6165        &self,
6166        host_project_id: u64,
6167        guest_cx: &mut TestAppContext,
6168    ) -> ModelHandle<Project> {
6169        let project_b = guest_cx.spawn(|cx| {
6170            Project::remote(
6171                host_project_id,
6172                self.client.clone(),
6173                self.user_store.clone(),
6174                self.language_registry.clone(),
6175                FakeFs::new(cx.background()),
6176                cx,
6177            )
6178        });
6179        project_b.await.unwrap()
6180    }
6181
6182    fn build_workspace(
6183        &self,
6184        project: &ModelHandle<Project>,
6185        cx: &mut TestAppContext,
6186    ) -> ViewHandle<Workspace> {
6187        let (_, root_view) = cx.add_window(|_| EmptyView);
6188        cx.add_view(&root_view, |cx| {
6189            Workspace::new(
6190                Default::default(),
6191                0,
6192                project.clone(),
6193                |_, _| unimplemented!(),
6194                cx,
6195            )
6196        })
6197    }
6198
6199    pub async fn simulate(
6200        mut self,
6201        username: String,
6202        mut op_start_signal: futures::channel::mpsc::UnboundedReceiver<()>,
6203        rng: Arc<Mutex<StdRng>>,
6204        mut cx: TestAppContext,
6205    ) -> (Self, TestAppContext) {
6206        async fn tick(
6207            client: &mut TestClient,
6208            username: &str,
6209            rng: Arc<Mutex<StdRng>>,
6210            cx: &mut TestAppContext,
6211        ) -> anyhow::Result<()> {
6212            let active_call = cx.read(ActiveCall::global);
6213            if active_call.read_with(cx, |call, _| call.incoming().borrow().is_some()) {
6214                if rng.lock().gen() {
6215                    log::info!("{}: accepting incoming call", username);
6216                    active_call
6217                        .update(cx, |call, cx| call.accept_incoming(cx))
6218                        .await?;
6219                } else {
6220                    log::info!("{}: declining incoming call", username);
6221                    active_call.update(cx, |call, _| call.decline_incoming())?;
6222                }
6223            } else {
6224                let available_contacts = client.user_store.read_with(cx, |user_store, _| {
6225                    user_store
6226                        .contacts()
6227                        .iter()
6228                        .filter(|contact| contact.online && !contact.busy)
6229                        .cloned()
6230                        .collect::<Vec<_>>()
6231                });
6232
6233                let distribution = rng.lock().gen_range(0..100);
6234                match distribution {
6235                    0..=29 if !available_contacts.is_empty() => {
6236                        let contact = available_contacts.choose(&mut *rng.lock()).unwrap();
6237                        log::info!("{}: inviting {}", username, contact.user.github_login);
6238                        active_call
6239                            .update(cx, |call, cx| call.invite(contact.user.id, None, cx))
6240                            .await?;
6241                    }
6242                    30..=39 if active_call.read_with(cx, |call, _| call.room().is_some()) => {
6243                        log::info!("{}: hanging up", username);
6244                        active_call.update(cx, |call, cx| call.hang_up(cx))?;
6245                    }
6246                    _ => {}
6247                }
6248            }
6249
6250            let remote_projects =
6251                if let Some(room) = active_call.read_with(cx, |call, _| call.room().cloned()) {
6252                    room.read_with(cx, |room, _| {
6253                        room.remote_participants()
6254                            .values()
6255                            .flat_map(|participant| participant.projects.clone())
6256                            .collect::<Vec<_>>()
6257                    })
6258                } else {
6259                    Default::default()
6260                };
6261            let project = if remote_projects.is_empty() || rng.lock().gen() {
6262                if client.local_projects.is_empty() || rng.lock().gen() {
6263                    let dir_paths = client.fs.directories().await;
6264                    let local_project = if dir_paths.is_empty() || rng.lock().gen() {
6265                        let root_path = format!(
6266                            "/{}-root-{}",
6267                            username,
6268                            post_inc(&mut client.next_root_dir_id)
6269                        );
6270                        let root_path = Path::new(&root_path);
6271                        client.fs.create_dir(root_path).await.unwrap();
6272                        client
6273                            .fs
6274                            .create_file(&root_path.join("main.rs"), Default::default())
6275                            .await
6276                            .unwrap();
6277                        log::info!("{}: opening local project at {:?}", username, root_path);
6278                        client.build_local_project(root_path, cx).await.0
6279                    } else {
6280                        let root_path = dir_paths.choose(&mut *rng.lock()).unwrap();
6281                        log::info!("{}: opening local project at {:?}", username, root_path);
6282                        client.build_local_project(root_path, cx).await.0
6283                    };
6284                    client.local_projects.push(local_project.clone());
6285                    local_project
6286                } else {
6287                    client
6288                        .local_projects
6289                        .choose(&mut *rng.lock())
6290                        .unwrap()
6291                        .clone()
6292                }
6293            } else {
6294                if client.remote_projects.is_empty() || rng.lock().gen() {
6295                    let remote_project_id = remote_projects.choose(&mut *rng.lock()).unwrap().id;
6296                    let remote_project = if let Some(project) =
6297                        client.remote_projects.iter().find(|project| {
6298                            project.read_with(cx, |project, _| {
6299                                project.remote_id() == Some(remote_project_id)
6300                            })
6301                        }) {
6302                        project.clone()
6303                    } else {
6304                        log::info!("{}: opening remote project {}", username, remote_project_id);
6305                        let remote_project = Project::remote(
6306                            remote_project_id,
6307                            client.client.clone(),
6308                            client.user_store.clone(),
6309                            client.language_registry.clone(),
6310                            FakeFs::new(cx.background()),
6311                            cx.to_async(),
6312                        )
6313                        .await?;
6314                        client.remote_projects.push(remote_project.clone());
6315                        remote_project
6316                    };
6317
6318                    remote_project
6319                } else {
6320                    client
6321                        .remote_projects
6322                        .choose(&mut *rng.lock())
6323                        .unwrap()
6324                        .clone()
6325                }
6326            };
6327            if let Err(error) = active_call
6328                .update(cx, |call, cx| call.share_project(project.clone(), cx))
6329                .await
6330            {
6331                log::error!("{}: error sharing project, {:?}", username, error);
6332            }
6333
6334            let buffers = client.buffers.entry(project.clone()).or_default();
6335            let buffer = if buffers.is_empty() || rng.lock().gen() {
6336                let worktree = if let Some(worktree) = project.read_with(cx, |project, cx| {
6337                    project
6338                        .worktrees(cx)
6339                        .filter(|worktree| {
6340                            let worktree = worktree.read(cx);
6341                            worktree.is_visible() && worktree.entries(false).any(|e| e.is_file())
6342                        })
6343                        .choose(&mut *rng.lock())
6344                }) {
6345                    worktree
6346                } else {
6347                    cx.background().simulate_random_delay().await;
6348                    return Ok(());
6349                };
6350
6351                let (worktree_root_name, project_path) = worktree.read_with(cx, |worktree, _| {
6352                    let entry = worktree
6353                        .entries(false)
6354                        .filter(|e| e.is_file())
6355                        .choose(&mut *rng.lock())
6356                        .unwrap();
6357                    (
6358                        worktree.root_name().to_string(),
6359                        (worktree.id(), entry.path.clone()),
6360                    )
6361                });
6362                log::info!(
6363                    "{}: opening path {:?} in worktree {} ({})",
6364                    username,
6365                    project_path.1,
6366                    project_path.0,
6367                    worktree_root_name,
6368                );
6369                let buffer = project
6370                    .update(cx, |project, cx| {
6371                        project.open_buffer(project_path.clone(), cx)
6372                    })
6373                    .await?;
6374                log::info!(
6375                    "{}: opened path {:?} in worktree {} ({}) with buffer id {}",
6376                    username,
6377                    project_path.1,
6378                    project_path.0,
6379                    worktree_root_name,
6380                    buffer.read_with(cx, |buffer, _| buffer.remote_id())
6381                );
6382                buffers.insert(buffer.clone());
6383                buffer
6384            } else {
6385                buffers.iter().choose(&mut *rng.lock()).unwrap().clone()
6386            };
6387
6388            let choice = rng.lock().gen_range(0..100);
6389            match choice {
6390                0..=9 => {
6391                    cx.update(|cx| {
6392                        log::info!(
6393                            "{}: dropping buffer {:?}",
6394                            username,
6395                            buffer.read(cx).file().unwrap().full_path(cx)
6396                        );
6397                        buffers.remove(&buffer);
6398                        drop(buffer);
6399                    });
6400                }
6401                10..=19 => {
6402                    let completions = project.update(cx, |project, cx| {
6403                        log::info!(
6404                            "{}: requesting completions for buffer {} ({:?})",
6405                            username,
6406                            buffer.read(cx).remote_id(),
6407                            buffer.read(cx).file().unwrap().full_path(cx)
6408                        );
6409                        let offset = rng.lock().gen_range(0..=buffer.read(cx).len());
6410                        project.completions(&buffer, offset, cx)
6411                    });
6412                    let completions = cx.background().spawn(async move {
6413                        completions
6414                            .await
6415                            .map_err(|err| anyhow!("completions request failed: {:?}", err))
6416                    });
6417                    if rng.lock().gen_bool(0.3) {
6418                        log::info!("{}: detaching completions request", username);
6419                        cx.update(|cx| completions.detach_and_log_err(cx));
6420                    } else {
6421                        completions.await?;
6422                    }
6423                }
6424                20..=29 => {
6425                    let code_actions = project.update(cx, |project, cx| {
6426                        log::info!(
6427                            "{}: requesting code actions for buffer {} ({:?})",
6428                            username,
6429                            buffer.read(cx).remote_id(),
6430                            buffer.read(cx).file().unwrap().full_path(cx)
6431                        );
6432                        let range = buffer.read(cx).random_byte_range(0, &mut *rng.lock());
6433                        project.code_actions(&buffer, range, cx)
6434                    });
6435                    let code_actions = cx.background().spawn(async move {
6436                        code_actions
6437                            .await
6438                            .map_err(|err| anyhow!("code actions request failed: {:?}", err))
6439                    });
6440                    if rng.lock().gen_bool(0.3) {
6441                        log::info!("{}: detaching code actions request", username);
6442                        cx.update(|cx| code_actions.detach_and_log_err(cx));
6443                    } else {
6444                        code_actions.await?;
6445                    }
6446                }
6447                30..=39 if buffer.read_with(cx, |buffer, _| buffer.is_dirty()) => {
6448                    let (requested_version, save) = buffer.update(cx, |buffer, cx| {
6449                        log::info!(
6450                            "{}: saving buffer {} ({:?})",
6451                            username,
6452                            buffer.remote_id(),
6453                            buffer.file().unwrap().full_path(cx)
6454                        );
6455                        (buffer.version(), buffer.save(cx))
6456                    });
6457                    let save = cx.background().spawn(async move {
6458                        let (saved_version, _, _) = save
6459                            .await
6460                            .map_err(|err| anyhow!("save request failed: {:?}", err))?;
6461                        assert!(saved_version.observed_all(&requested_version));
6462                        Ok::<_, anyhow::Error>(())
6463                    });
6464                    if rng.lock().gen_bool(0.3) {
6465                        log::info!("{}: detaching save request", username);
6466                        cx.update(|cx| save.detach_and_log_err(cx));
6467                    } else {
6468                        save.await?;
6469                    }
6470                }
6471                40..=44 => {
6472                    let prepare_rename = project.update(cx, |project, cx| {
6473                        log::info!(
6474                            "{}: preparing rename for buffer {} ({:?})",
6475                            username,
6476                            buffer.read(cx).remote_id(),
6477                            buffer.read(cx).file().unwrap().full_path(cx)
6478                        );
6479                        let offset = rng.lock().gen_range(0..=buffer.read(cx).len());
6480                        project.prepare_rename(buffer, offset, cx)
6481                    });
6482                    let prepare_rename = cx.background().spawn(async move {
6483                        prepare_rename
6484                            .await
6485                            .map_err(|err| anyhow!("prepare rename request failed: {:?}", err))
6486                    });
6487                    if rng.lock().gen_bool(0.3) {
6488                        log::info!("{}: detaching prepare rename request", username);
6489                        cx.update(|cx| prepare_rename.detach_and_log_err(cx));
6490                    } else {
6491                        prepare_rename.await?;
6492                    }
6493                }
6494                45..=49 => {
6495                    let definitions = project.update(cx, |project, cx| {
6496                        log::info!(
6497                            "{}: requesting definitions for buffer {} ({:?})",
6498                            username,
6499                            buffer.read(cx).remote_id(),
6500                            buffer.read(cx).file().unwrap().full_path(cx)
6501                        );
6502                        let offset = rng.lock().gen_range(0..=buffer.read(cx).len());
6503                        project.definition(&buffer, offset, cx)
6504                    });
6505                    let definitions = cx.background().spawn(async move {
6506                        definitions
6507                            .await
6508                            .map_err(|err| anyhow!("definitions request failed: {:?}", err))
6509                    });
6510                    if rng.lock().gen_bool(0.3) {
6511                        log::info!("{}: detaching definitions request", username);
6512                        cx.update(|cx| definitions.detach_and_log_err(cx));
6513                    } else {
6514                        buffers.extend(definitions.await?.into_iter().map(|loc| loc.target.buffer));
6515                    }
6516                }
6517                50..=54 => {
6518                    let highlights = project.update(cx, |project, cx| {
6519                        log::info!(
6520                            "{}: requesting highlights for buffer {} ({:?})",
6521                            username,
6522                            buffer.read(cx).remote_id(),
6523                            buffer.read(cx).file().unwrap().full_path(cx)
6524                        );
6525                        let offset = rng.lock().gen_range(0..=buffer.read(cx).len());
6526                        project.document_highlights(&buffer, offset, cx)
6527                    });
6528                    let highlights = cx.background().spawn(async move {
6529                        highlights
6530                            .await
6531                            .map_err(|err| anyhow!("highlights request failed: {:?}", err))
6532                    });
6533                    if rng.lock().gen_bool(0.3) {
6534                        log::info!("{}: detaching highlights request", username);
6535                        cx.update(|cx| highlights.detach_and_log_err(cx));
6536                    } else {
6537                        highlights.await?;
6538                    }
6539                }
6540                55..=59 => {
6541                    let search = project.update(cx, |project, cx| {
6542                        let query = rng.lock().gen_range('a'..='z');
6543                        log::info!("{}: project-wide search {:?}", username, query);
6544                        project.search(SearchQuery::text(query, false, false), cx)
6545                    });
6546                    let search = cx.background().spawn(async move {
6547                        search
6548                            .await
6549                            .map_err(|err| anyhow!("search request failed: {:?}", err))
6550                    });
6551                    if rng.lock().gen_bool(0.3) {
6552                        log::info!("{}: detaching search request", username);
6553                        cx.update(|cx| search.detach_and_log_err(cx));
6554                    } else {
6555                        buffers.extend(search.await?.into_keys());
6556                    }
6557                }
6558                60..=79 => {
6559                    let worktree = project
6560                        .read_with(cx, |project, cx| {
6561                            project
6562                                .worktrees(cx)
6563                                .filter(|worktree| {
6564                                    let worktree = worktree.read(cx);
6565                                    worktree.is_visible()
6566                                        && worktree.entries(false).any(|e| e.is_file())
6567                                        && worktree.root_entry().map_or(false, |e| e.is_dir())
6568                                })
6569                                .choose(&mut *rng.lock())
6570                        })
6571                        .unwrap();
6572                    let (worktree_id, worktree_root_name) = worktree
6573                        .read_with(cx, |worktree, _| {
6574                            (worktree.id(), worktree.root_name().to_string())
6575                        });
6576
6577                    let mut new_name = String::new();
6578                    for _ in 0..10 {
6579                        let letter = rng.lock().gen_range('a'..='z');
6580                        new_name.push(letter);
6581                    }
6582
6583                    let is_dir = rng.lock().gen::<bool>();
6584                    let mut new_path = PathBuf::new();
6585                    new_path.push(new_name);
6586                    if !is_dir {
6587                        new_path.set_extension("rs");
6588                    }
6589                    log::info!(
6590                        "{}: creating {:?} in worktree {} ({})",
6591                        username,
6592                        new_path,
6593                        worktree_id,
6594                        worktree_root_name,
6595                    );
6596                    project
6597                        .update(cx, |project, cx| {
6598                            project.create_entry((worktree_id, new_path), is_dir, cx)
6599                        })
6600                        .unwrap()
6601                        .await?;
6602                }
6603                _ => {
6604                    buffer.update(cx, |buffer, cx| {
6605                        log::info!(
6606                            "{}: updating buffer {} ({:?})",
6607                            username,
6608                            buffer.remote_id(),
6609                            buffer.file().unwrap().full_path(cx)
6610                        );
6611                        if rng.lock().gen_bool(0.7) {
6612                            buffer.randomly_edit(&mut *rng.lock(), 5, cx);
6613                        } else {
6614                            buffer.randomly_undo_redo(&mut *rng.lock(), cx);
6615                        }
6616                    });
6617                }
6618            }
6619
6620            Ok(())
6621        }
6622
6623        // Setup language server
6624        let mut language = Language::new(
6625            LanguageConfig {
6626                name: "Rust".into(),
6627                path_suffixes: vec!["rs".to_string()],
6628                ..Default::default()
6629            },
6630            None,
6631        );
6632        let _fake_language_servers = language
6633            .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
6634                name: "the-fake-language-server",
6635                capabilities: lsp::LanguageServer::full_capabilities(),
6636                initializer: Some(Box::new({
6637                    let rng = rng.clone();
6638                    let fs = self.fs.clone();
6639                    move |fake_server: &mut FakeLanguageServer| {
6640                        fake_server.handle_request::<lsp::request::Completion, _, _>(
6641                            |_, _| async move {
6642                                Ok(Some(lsp::CompletionResponse::Array(vec![
6643                                    lsp::CompletionItem {
6644                                        text_edit: Some(lsp::CompletionTextEdit::Edit(
6645                                            lsp::TextEdit {
6646                                                range: lsp::Range::new(
6647                                                    lsp::Position::new(0, 0),
6648                                                    lsp::Position::new(0, 0),
6649                                                ),
6650                                                new_text: "the-new-text".to_string(),
6651                                            },
6652                                        )),
6653                                        ..Default::default()
6654                                    },
6655                                ])))
6656                            },
6657                        );
6658
6659                        fake_server.handle_request::<lsp::request::CodeActionRequest, _, _>(
6660                            |_, _| async move {
6661                                Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
6662                                    lsp::CodeAction {
6663                                        title: "the-code-action".to_string(),
6664                                        ..Default::default()
6665                                    },
6666                                )]))
6667                            },
6668                        );
6669
6670                        fake_server.handle_request::<lsp::request::PrepareRenameRequest, _, _>(
6671                            |params, _| async move {
6672                                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
6673                                    params.position,
6674                                    params.position,
6675                                ))))
6676                            },
6677                        );
6678
6679                        fake_server.handle_request::<lsp::request::GotoDefinition, _, _>({
6680                            let fs = fs.clone();
6681                            let rng = rng.clone();
6682                            move |_, _| {
6683                                let fs = fs.clone();
6684                                let rng = rng.clone();
6685                                async move {
6686                                    let files = fs.files().await;
6687                                    let mut rng = rng.lock();
6688                                    let count = rng.gen_range::<usize, _>(1..3);
6689                                    let files = (0..count)
6690                                        .map(|_| files.choose(&mut *rng).unwrap())
6691                                        .collect::<Vec<_>>();
6692                                    log::info!("LSP: Returning definitions in files {:?}", &files);
6693                                    Ok(Some(lsp::GotoDefinitionResponse::Array(
6694                                        files
6695                                            .into_iter()
6696                                            .map(|file| lsp::Location {
6697                                                uri: lsp::Url::from_file_path(file).unwrap(),
6698                                                range: Default::default(),
6699                                            })
6700                                            .collect(),
6701                                    )))
6702                                }
6703                            }
6704                        });
6705
6706                        fake_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
6707                            {
6708                                let rng = rng.clone();
6709                                move |_, _| {
6710                                    let mut highlights = Vec::new();
6711                                    let highlight_count = rng.lock().gen_range(1..=5);
6712                                    for _ in 0..highlight_count {
6713                                        let start_row = rng.lock().gen_range(0..100);
6714                                        let start_column = rng.lock().gen_range(0..100);
6715                                        let start = PointUtf16::new(start_row, start_column);
6716                                        let end_row = rng.lock().gen_range(0..100);
6717                                        let end_column = rng.lock().gen_range(0..100);
6718                                        let end = PointUtf16::new(end_row, end_column);
6719                                        let range =
6720                                            if start > end { end..start } else { start..end };
6721                                        highlights.push(lsp::DocumentHighlight {
6722                                            range: range_to_lsp(range.clone()),
6723                                            kind: Some(lsp::DocumentHighlightKind::READ),
6724                                        });
6725                                    }
6726                                    highlights.sort_unstable_by_key(|highlight| {
6727                                        (highlight.range.start, highlight.range.end)
6728                                    });
6729                                    async move { Ok(Some(highlights)) }
6730                                }
6731                            },
6732                        );
6733                    }
6734                })),
6735                ..Default::default()
6736            }))
6737            .await;
6738        self.language_registry.add(Arc::new(language));
6739
6740        while op_start_signal.next().await.is_some() {
6741            if let Err(error) = tick(&mut self, &username, rng.clone(), &mut cx).await {
6742                log::error!("{} error: {:?}", username, error);
6743            }
6744
6745            cx.background().simulate_random_delay().await;
6746        }
6747        log::info!("{}: done", username);
6748
6749        (self, cx)
6750    }
6751}
6752
6753impl Drop for TestClient {
6754    fn drop(&mut self) {
6755        self.client.tear_down();
6756    }
6757}
6758
6759impl Executor for Arc<gpui::executor::Background> {
6760    type Sleep = gpui::executor::Timer;
6761
6762    fn spawn_detached<F: 'static + Send + Future<Output = ()>>(&self, future: F) {
6763        self.spawn(future).detach();
6764    }
6765
6766    fn sleep(&self, duration: Duration) -> Self::Sleep {
6767        self.as_ref().timer(duration)
6768    }
6769}
6770
6771#[derive(Debug, Eq, PartialEq)]
6772struct RoomParticipants {
6773    remote: Vec<String>,
6774    pending: Vec<String>,
6775}
6776
6777fn room_participants(room: &ModelHandle<Room>, cx: &mut TestAppContext) -> RoomParticipants {
6778    room.read_with(cx, |room, _| RoomParticipants {
6779        remote: room
6780            .remote_participants()
6781            .iter()
6782            .map(|(_, participant)| participant.user.github_login.clone())
6783            .collect(),
6784        pending: room
6785            .pending_participants()
6786            .iter()
6787            .map(|user| user.github_login.clone())
6788            .collect(),
6789    })
6790}