integration_tests.rs

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