integration_tests.rs

   1use crate::{
   2    db::{NewUserParams, ProjectId, 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        let store = server.store.lock().await;
2406        let project = store.project(ProjectId::from_proto(project_id)).unwrap();
2407        let worktree = project.worktrees.get(&worktree_id.to_proto()).unwrap();
2408        assert!(!worktree.diagnostic_summaries.is_empty());
2409    }
2410
2411    // Ensure client B observes the new diagnostics.
2412    project_b.read_with(cx_b, |project, cx| {
2413        assert_eq!(
2414            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
2415            &[(
2416                ProjectPath {
2417                    worktree_id,
2418                    path: Arc::from(Path::new("a.rs")),
2419                },
2420                DiagnosticSummary {
2421                    error_count: 1,
2422                    warning_count: 0,
2423                    ..Default::default()
2424                },
2425            )]
2426        )
2427    });
2428
2429    // Join project as client C and observe the diagnostics.
2430    let project_c = client_c.build_remote_project(project_id, cx_c).await;
2431    let project_c_diagnostic_summaries = Rc::new(RefCell::new(Vec::new()));
2432    project_c.update(cx_c, |_, cx| {
2433        let summaries = project_c_diagnostic_summaries.clone();
2434        cx.subscribe(&project_c, {
2435            move |p, _, event, cx| {
2436                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
2437                    *summaries.borrow_mut() = p.diagnostic_summaries(cx).collect();
2438                }
2439            }
2440        })
2441        .detach();
2442    });
2443
2444    deterministic.run_until_parked();
2445    assert_eq!(
2446        project_c_diagnostic_summaries.borrow().as_slice(),
2447        &[(
2448            ProjectPath {
2449                worktree_id,
2450                path: Arc::from(Path::new("a.rs")),
2451            },
2452            DiagnosticSummary {
2453                error_count: 1,
2454                warning_count: 0,
2455                ..Default::default()
2456            },
2457        )]
2458    );
2459
2460    // Simulate a language server reporting more errors for a file.
2461    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
2462        lsp::PublishDiagnosticsParams {
2463            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
2464            version: None,
2465            diagnostics: vec![
2466                lsp::Diagnostic {
2467                    severity: Some(lsp::DiagnosticSeverity::ERROR),
2468                    range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
2469                    message: "message 1".to_string(),
2470                    ..Default::default()
2471                },
2472                lsp::Diagnostic {
2473                    severity: Some(lsp::DiagnosticSeverity::WARNING),
2474                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 13)),
2475                    message: "message 2".to_string(),
2476                    ..Default::default()
2477                },
2478            ],
2479        },
2480    );
2481
2482    // Clients B and C get the updated summaries
2483    deterministic.run_until_parked();
2484    project_b.read_with(cx_b, |project, cx| {
2485        assert_eq!(
2486            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
2487            [(
2488                ProjectPath {
2489                    worktree_id,
2490                    path: Arc::from(Path::new("a.rs")),
2491                },
2492                DiagnosticSummary {
2493                    error_count: 1,
2494                    warning_count: 1,
2495                    ..Default::default()
2496                },
2497            )]
2498        );
2499    });
2500    project_c.read_with(cx_c, |project, cx| {
2501        assert_eq!(
2502            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
2503            [(
2504                ProjectPath {
2505                    worktree_id,
2506                    path: Arc::from(Path::new("a.rs")),
2507                },
2508                DiagnosticSummary {
2509                    error_count: 1,
2510                    warning_count: 1,
2511                    ..Default::default()
2512                },
2513            )]
2514        );
2515    });
2516
2517    // Open the file with the errors on client B. They should be present.
2518    let buffer_b = cx_b
2519        .background()
2520        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
2521        .await
2522        .unwrap();
2523
2524    buffer_b.read_with(cx_b, |buffer, _| {
2525        assert_eq!(
2526            buffer
2527                .snapshot()
2528                .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
2529                .collect::<Vec<_>>(),
2530            &[
2531                DiagnosticEntry {
2532                    range: Point::new(0, 4)..Point::new(0, 7),
2533                    diagnostic: Diagnostic {
2534                        group_id: 2,
2535                        message: "message 1".to_string(),
2536                        severity: lsp::DiagnosticSeverity::ERROR,
2537                        is_primary: true,
2538                        ..Default::default()
2539                    }
2540                },
2541                DiagnosticEntry {
2542                    range: Point::new(0, 10)..Point::new(0, 13),
2543                    diagnostic: Diagnostic {
2544                        group_id: 3,
2545                        severity: lsp::DiagnosticSeverity::WARNING,
2546                        message: "message 2".to_string(),
2547                        is_primary: true,
2548                        ..Default::default()
2549                    }
2550                }
2551            ]
2552        );
2553    });
2554
2555    // Simulate a language server reporting no errors for a file.
2556    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
2557        lsp::PublishDiagnosticsParams {
2558            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
2559            version: None,
2560            diagnostics: vec![],
2561        },
2562    );
2563    deterministic.run_until_parked();
2564    project_a.read_with(cx_a, |project, cx| {
2565        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
2566    });
2567    project_b.read_with(cx_b, |project, cx| {
2568        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
2569    });
2570    project_c.read_with(cx_c, |project, cx| {
2571        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
2572    });
2573}
2574
2575#[gpui::test(iterations = 10)]
2576async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
2577    cx_a.foreground().forbid_parking();
2578    let mut server = TestServer::start(cx_a.background()).await;
2579    let client_a = server.create_client(cx_a, "user_a").await;
2580    let client_b = server.create_client(cx_b, "user_b").await;
2581    server
2582        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2583        .await;
2584    let active_call_a = cx_a.read(ActiveCall::global);
2585
2586    // Set up a fake language server.
2587    let mut language = Language::new(
2588        LanguageConfig {
2589            name: "Rust".into(),
2590            path_suffixes: vec!["rs".to_string()],
2591            ..Default::default()
2592        },
2593        Some(tree_sitter_rust::language()),
2594    );
2595    let mut fake_language_servers = language
2596        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
2597            capabilities: lsp::ServerCapabilities {
2598                completion_provider: Some(lsp::CompletionOptions {
2599                    trigger_characters: Some(vec![".".to_string()]),
2600                    ..Default::default()
2601                }),
2602                ..Default::default()
2603            },
2604            ..Default::default()
2605        }))
2606        .await;
2607    client_a.language_registry.add(Arc::new(language));
2608
2609    client_a
2610        .fs
2611        .insert_tree(
2612            "/a",
2613            json!({
2614                "main.rs": "fn main() { a }",
2615                "other.rs": "",
2616            }),
2617        )
2618        .await;
2619    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
2620    let project_id = active_call_a
2621        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2622        .await
2623        .unwrap();
2624    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2625
2626    // Open a file in an editor as the guest.
2627    let buffer_b = project_b
2628        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
2629        .await
2630        .unwrap();
2631    let (_, window_b) = cx_b.add_window(|_| EmptyView);
2632    let editor_b = cx_b.add_view(&window_b, |cx| {
2633        Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx)
2634    });
2635
2636    let fake_language_server = fake_language_servers.next().await.unwrap();
2637    buffer_b
2638        .condition(cx_b, |buffer, _| !buffer.completion_triggers().is_empty())
2639        .await;
2640
2641    // Type a completion trigger character as the guest.
2642    editor_b.update(cx_b, |editor, cx| {
2643        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
2644        editor.handle_input(".", cx);
2645        cx.focus(&editor_b);
2646    });
2647
2648    // Receive a completion request as the host's language server.
2649    // Return some completions from the host's language server.
2650    cx_a.foreground().start_waiting();
2651    fake_language_server
2652        .handle_request::<lsp::request::Completion, _, _>(|params, _| async move {
2653            assert_eq!(
2654                params.text_document_position.text_document.uri,
2655                lsp::Url::from_file_path("/a/main.rs").unwrap(),
2656            );
2657            assert_eq!(
2658                params.text_document_position.position,
2659                lsp::Position::new(0, 14),
2660            );
2661
2662            Ok(Some(lsp::CompletionResponse::Array(vec![
2663                lsp::CompletionItem {
2664                    label: "first_method(…)".into(),
2665                    detail: Some("fn(&mut self, B) -> C".into()),
2666                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
2667                        new_text: "first_method($1)".to_string(),
2668                        range: lsp::Range::new(
2669                            lsp::Position::new(0, 14),
2670                            lsp::Position::new(0, 14),
2671                        ),
2672                    })),
2673                    insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
2674                    ..Default::default()
2675                },
2676                lsp::CompletionItem {
2677                    label: "second_method(…)".into(),
2678                    detail: Some("fn(&mut self, C) -> D<E>".into()),
2679                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
2680                        new_text: "second_method()".to_string(),
2681                        range: lsp::Range::new(
2682                            lsp::Position::new(0, 14),
2683                            lsp::Position::new(0, 14),
2684                        ),
2685                    })),
2686                    insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
2687                    ..Default::default()
2688                },
2689            ])))
2690        })
2691        .next()
2692        .await
2693        .unwrap();
2694    cx_a.foreground().finish_waiting();
2695
2696    // Open the buffer on the host.
2697    let buffer_a = project_a
2698        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
2699        .await
2700        .unwrap();
2701    buffer_a
2702        .condition(cx_a, |buffer, _| buffer.text() == "fn main() { a. }")
2703        .await;
2704
2705    // Confirm a completion on the guest.
2706    editor_b
2707        .condition(cx_b, |editor, _| editor.context_menu_visible())
2708        .await;
2709    editor_b.update(cx_b, |editor, cx| {
2710        editor.confirm_completion(&ConfirmCompletion { item_ix: Some(0) }, cx);
2711        assert_eq!(editor.text(cx), "fn main() { a.first_method() }");
2712    });
2713
2714    // Return a resolved completion from the host's language server.
2715    // The resolved completion has an additional text edit.
2716    fake_language_server.handle_request::<lsp::request::ResolveCompletionItem, _, _>(
2717        |params, _| async move {
2718            assert_eq!(params.label, "first_method(…)");
2719            Ok(lsp::CompletionItem {
2720                label: "first_method(…)".into(),
2721                detail: Some("fn(&mut self, B) -> C".into()),
2722                text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
2723                    new_text: "first_method($1)".to_string(),
2724                    range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
2725                })),
2726                additional_text_edits: Some(vec![lsp::TextEdit {
2727                    new_text: "use d::SomeTrait;\n".to_string(),
2728                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
2729                }]),
2730                insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
2731                ..Default::default()
2732            })
2733        },
2734    );
2735
2736    // The additional edit is applied.
2737    buffer_a
2738        .condition(cx_a, |buffer, _| {
2739            buffer.text() == "use d::SomeTrait;\nfn main() { a.first_method() }"
2740        })
2741        .await;
2742    buffer_b
2743        .condition(cx_b, |buffer, _| {
2744            buffer.text() == "use d::SomeTrait;\nfn main() { a.first_method() }"
2745        })
2746        .await;
2747}
2748
2749#[gpui::test(iterations = 10)]
2750async fn test_reloading_buffer_manually(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
2751    cx_a.foreground().forbid_parking();
2752    let mut server = TestServer::start(cx_a.background()).await;
2753    let client_a = server.create_client(cx_a, "user_a").await;
2754    let client_b = server.create_client(cx_b, "user_b").await;
2755    server
2756        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2757        .await;
2758    let active_call_a = cx_a.read(ActiveCall::global);
2759
2760    client_a
2761        .fs
2762        .insert_tree("/a", json!({ "a.rs": "let one = 1;" }))
2763        .await;
2764    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
2765    let buffer_a = project_a
2766        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
2767        .await
2768        .unwrap();
2769    let project_id = active_call_a
2770        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2771        .await
2772        .unwrap();
2773
2774    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2775
2776    let buffer_b = cx_b
2777        .background()
2778        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
2779        .await
2780        .unwrap();
2781    buffer_b.update(cx_b, |buffer, cx| {
2782        buffer.edit([(4..7, "six")], None, cx);
2783        buffer.edit([(10..11, "6")], None, cx);
2784        assert_eq!(buffer.text(), "let six = 6;");
2785        assert!(buffer.is_dirty());
2786        assert!(!buffer.has_conflict());
2787    });
2788    buffer_a
2789        .condition(cx_a, |buffer, _| buffer.text() == "let six = 6;")
2790        .await;
2791
2792    client_a
2793        .fs
2794        .save(
2795            "/a/a.rs".as_ref(),
2796            &Rope::from("let seven = 7;"),
2797            LineEnding::Unix,
2798        )
2799        .await
2800        .unwrap();
2801    buffer_a
2802        .condition(cx_a, |buffer, _| buffer.has_conflict())
2803        .await;
2804    buffer_b
2805        .condition(cx_b, |buffer, _| buffer.has_conflict())
2806        .await;
2807
2808    project_b
2809        .update(cx_b, |project, cx| {
2810            project.reload_buffers(HashSet::from_iter([buffer_b.clone()]), true, cx)
2811        })
2812        .await
2813        .unwrap();
2814    buffer_a.read_with(cx_a, |buffer, _| {
2815        assert_eq!(buffer.text(), "let seven = 7;");
2816        assert!(!buffer.is_dirty());
2817        assert!(!buffer.has_conflict());
2818    });
2819    buffer_b.read_with(cx_b, |buffer, _| {
2820        assert_eq!(buffer.text(), "let seven = 7;");
2821        assert!(!buffer.is_dirty());
2822        assert!(!buffer.has_conflict());
2823    });
2824
2825    buffer_a.update(cx_a, |buffer, cx| {
2826        // Undoing on the host is a no-op when the reload was initiated by the guest.
2827        buffer.undo(cx);
2828        assert_eq!(buffer.text(), "let seven = 7;");
2829        assert!(!buffer.is_dirty());
2830        assert!(!buffer.has_conflict());
2831    });
2832    buffer_b.update(cx_b, |buffer, cx| {
2833        // Undoing on the guest rolls back the buffer to before it was reloaded but the conflict gets cleared.
2834        buffer.undo(cx);
2835        assert_eq!(buffer.text(), "let six = 6;");
2836        assert!(buffer.is_dirty());
2837        assert!(!buffer.has_conflict());
2838    });
2839}
2840
2841#[gpui::test(iterations = 10)]
2842async fn test_formatting_buffer(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
2843    use project::FormatTrigger;
2844
2845    let mut server = TestServer::start(cx_a.background()).await;
2846    let client_a = server.create_client(cx_a, "user_a").await;
2847    let client_b = server.create_client(cx_b, "user_b").await;
2848    server
2849        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2850        .await;
2851    let active_call_a = cx_a.read(ActiveCall::global);
2852
2853    // Set up a fake language server.
2854    let mut language = Language::new(
2855        LanguageConfig {
2856            name: "Rust".into(),
2857            path_suffixes: vec!["rs".to_string()],
2858            ..Default::default()
2859        },
2860        Some(tree_sitter_rust::language()),
2861    );
2862    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
2863    client_a.language_registry.add(Arc::new(language));
2864
2865    // Here we insert a fake tree with a directory that exists on disk. This is needed
2866    // because later we'll invoke a command, which requires passing a working directory
2867    // that points to a valid location on disk.
2868    let directory = env::current_dir().unwrap();
2869    client_a
2870        .fs
2871        .insert_tree(&directory, json!({ "a.rs": "let one = \"two\"" }))
2872        .await;
2873    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
2874    let project_id = active_call_a
2875        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2876        .await
2877        .unwrap();
2878    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2879
2880    let buffer_b = cx_b
2881        .background()
2882        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
2883        .await
2884        .unwrap();
2885
2886    let fake_language_server = fake_language_servers.next().await.unwrap();
2887    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
2888        Ok(Some(vec![
2889            lsp::TextEdit {
2890                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 4)),
2891                new_text: "h".to_string(),
2892            },
2893            lsp::TextEdit {
2894                range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 7)),
2895                new_text: "y".to_string(),
2896            },
2897        ]))
2898    });
2899
2900    project_b
2901        .update(cx_b, |project, cx| {
2902            project.format(
2903                HashSet::from_iter([buffer_b.clone()]),
2904                true,
2905                FormatTrigger::Save,
2906                cx,
2907            )
2908        })
2909        .await
2910        .unwrap();
2911    assert_eq!(
2912        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
2913        "let honey = \"two\""
2914    );
2915
2916    // Ensure buffer can be formatted using an external command. Notice how the
2917    // host's configuration is honored as opposed to using the guest's settings.
2918    cx_a.update(|cx| {
2919        cx.update_global(|settings: &mut Settings, _| {
2920            settings.editor_defaults.formatter = Some(Formatter::External {
2921                command: "awk".to_string(),
2922                arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()],
2923            });
2924        });
2925    });
2926    project_b
2927        .update(cx_b, |project, cx| {
2928            project.format(
2929                HashSet::from_iter([buffer_b.clone()]),
2930                true,
2931                FormatTrigger::Save,
2932                cx,
2933            )
2934        })
2935        .await
2936        .unwrap();
2937    assert_eq!(
2938        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
2939        format!("let honey = \"{}/a.rs\"\n", directory.to_str().unwrap())
2940    );
2941}
2942
2943#[gpui::test(iterations = 10)]
2944async fn test_definition(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
2945    cx_a.foreground().forbid_parking();
2946    let mut server = TestServer::start(cx_a.background()).await;
2947    let client_a = server.create_client(cx_a, "user_a").await;
2948    let client_b = server.create_client(cx_b, "user_b").await;
2949    server
2950        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2951        .await;
2952    let active_call_a = cx_a.read(ActiveCall::global);
2953
2954    // Set up a fake language server.
2955    let mut language = Language::new(
2956        LanguageConfig {
2957            name: "Rust".into(),
2958            path_suffixes: vec!["rs".to_string()],
2959            ..Default::default()
2960        },
2961        Some(tree_sitter_rust::language()),
2962    );
2963    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
2964    client_a.language_registry.add(Arc::new(language));
2965
2966    client_a
2967        .fs
2968        .insert_tree(
2969            "/root",
2970            json!({
2971                "dir-1": {
2972                    "a.rs": "const ONE: usize = b::TWO + b::THREE;",
2973                },
2974                "dir-2": {
2975                    "b.rs": "const TWO: c::T2 = 2;\nconst THREE: usize = 3;",
2976                    "c.rs": "type T2 = usize;",
2977                }
2978            }),
2979        )
2980        .await;
2981    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
2982    let project_id = active_call_a
2983        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2984        .await
2985        .unwrap();
2986    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2987
2988    // Open the file on client B.
2989    let buffer_b = cx_b
2990        .background()
2991        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
2992        .await
2993        .unwrap();
2994
2995    // Request the definition of a symbol as the guest.
2996    let fake_language_server = fake_language_servers.next().await.unwrap();
2997    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
2998        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
2999            lsp::Location::new(
3000                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
3001                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
3002            ),
3003        )))
3004    });
3005
3006    let definitions_1 = project_b
3007        .update(cx_b, |p, cx| p.definition(&buffer_b, 23, cx))
3008        .await
3009        .unwrap();
3010    cx_b.read(|cx| {
3011        assert_eq!(definitions_1.len(), 1);
3012        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
3013        let target_buffer = definitions_1[0].target.buffer.read(cx);
3014        assert_eq!(
3015            target_buffer.text(),
3016            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
3017        );
3018        assert_eq!(
3019            definitions_1[0].target.range.to_point(target_buffer),
3020            Point::new(0, 6)..Point::new(0, 9)
3021        );
3022    });
3023
3024    // Try getting more definitions for the same buffer, ensuring the buffer gets reused from
3025    // the previous call to `definition`.
3026    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
3027        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
3028            lsp::Location::new(
3029                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
3030                lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)),
3031            ),
3032        )))
3033    });
3034
3035    let definitions_2 = project_b
3036        .update(cx_b, |p, cx| p.definition(&buffer_b, 33, cx))
3037        .await
3038        .unwrap();
3039    cx_b.read(|cx| {
3040        assert_eq!(definitions_2.len(), 1);
3041        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
3042        let target_buffer = definitions_2[0].target.buffer.read(cx);
3043        assert_eq!(
3044            target_buffer.text(),
3045            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
3046        );
3047        assert_eq!(
3048            definitions_2[0].target.range.to_point(target_buffer),
3049            Point::new(1, 6)..Point::new(1, 11)
3050        );
3051    });
3052    assert_eq!(
3053        definitions_1[0].target.buffer,
3054        definitions_2[0].target.buffer
3055    );
3056
3057    fake_language_server.handle_request::<lsp::request::GotoTypeDefinition, _, _>(
3058        |req, _| async move {
3059            assert_eq!(
3060                req.text_document_position_params.position,
3061                lsp::Position::new(0, 7)
3062            );
3063            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
3064                lsp::Location::new(
3065                    lsp::Url::from_file_path("/root/dir-2/c.rs").unwrap(),
3066                    lsp::Range::new(lsp::Position::new(0, 5), lsp::Position::new(0, 7)),
3067                ),
3068            )))
3069        },
3070    );
3071
3072    let type_definitions = project_b
3073        .update(cx_b, |p, cx| p.type_definition(&buffer_b, 7, cx))
3074        .await
3075        .unwrap();
3076    cx_b.read(|cx| {
3077        assert_eq!(type_definitions.len(), 1);
3078        let target_buffer = type_definitions[0].target.buffer.read(cx);
3079        assert_eq!(target_buffer.text(), "type T2 = usize;");
3080        assert_eq!(
3081            type_definitions[0].target.range.to_point(target_buffer),
3082            Point::new(0, 5)..Point::new(0, 7)
3083        );
3084    });
3085}
3086
3087#[gpui::test(iterations = 10)]
3088async fn test_references(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
3089    cx_a.foreground().forbid_parking();
3090    let mut server = TestServer::start(cx_a.background()).await;
3091    let client_a = server.create_client(cx_a, "user_a").await;
3092    let client_b = server.create_client(cx_b, "user_b").await;
3093    server
3094        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3095        .await;
3096    let active_call_a = cx_a.read(ActiveCall::global);
3097
3098    // Set up a fake language server.
3099    let mut language = Language::new(
3100        LanguageConfig {
3101            name: "Rust".into(),
3102            path_suffixes: vec!["rs".to_string()],
3103            ..Default::default()
3104        },
3105        Some(tree_sitter_rust::language()),
3106    );
3107    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
3108    client_a.language_registry.add(Arc::new(language));
3109
3110    client_a
3111        .fs
3112        .insert_tree(
3113            "/root",
3114            json!({
3115                "dir-1": {
3116                    "one.rs": "const ONE: usize = 1;",
3117                    "two.rs": "const TWO: usize = one::ONE + one::ONE;",
3118                },
3119                "dir-2": {
3120                    "three.rs": "const THREE: usize = two::TWO + one::ONE;",
3121                }
3122            }),
3123        )
3124        .await;
3125    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
3126    let project_id = active_call_a
3127        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3128        .await
3129        .unwrap();
3130    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3131
3132    // Open the file on client B.
3133    let buffer_b = cx_b
3134        .background()
3135        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
3136        .await
3137        .unwrap();
3138
3139    // Request references to a symbol as the guest.
3140    let fake_language_server = fake_language_servers.next().await.unwrap();
3141    fake_language_server.handle_request::<lsp::request::References, _, _>(|params, _| async move {
3142        assert_eq!(
3143            params.text_document_position.text_document.uri.as_str(),
3144            "file:///root/dir-1/one.rs"
3145        );
3146        Ok(Some(vec![
3147            lsp::Location {
3148                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
3149                range: lsp::Range::new(lsp::Position::new(0, 24), lsp::Position::new(0, 27)),
3150            },
3151            lsp::Location {
3152                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
3153                range: lsp::Range::new(lsp::Position::new(0, 35), lsp::Position::new(0, 38)),
3154            },
3155            lsp::Location {
3156                uri: lsp::Url::from_file_path("/root/dir-2/three.rs").unwrap(),
3157                range: lsp::Range::new(lsp::Position::new(0, 37), lsp::Position::new(0, 40)),
3158            },
3159        ]))
3160    });
3161
3162    let references = project_b
3163        .update(cx_b, |p, cx| p.references(&buffer_b, 7, cx))
3164        .await
3165        .unwrap();
3166    cx_b.read(|cx| {
3167        assert_eq!(references.len(), 3);
3168        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
3169
3170        let two_buffer = references[0].buffer.read(cx);
3171        let three_buffer = references[2].buffer.read(cx);
3172        assert_eq!(
3173            two_buffer.file().unwrap().path().as_ref(),
3174            Path::new("two.rs")
3175        );
3176        assert_eq!(references[1].buffer, references[0].buffer);
3177        assert_eq!(
3178            three_buffer.file().unwrap().full_path(cx),
3179            Path::new("/root/dir-2/three.rs")
3180        );
3181
3182        assert_eq!(references[0].range.to_offset(two_buffer), 24..27);
3183        assert_eq!(references[1].range.to_offset(two_buffer), 35..38);
3184        assert_eq!(references[2].range.to_offset(three_buffer), 37..40);
3185    });
3186}
3187
3188#[gpui::test(iterations = 10)]
3189async fn test_project_search(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
3190    cx_a.foreground().forbid_parking();
3191    let mut server = TestServer::start(cx_a.background()).await;
3192    let client_a = server.create_client(cx_a, "user_a").await;
3193    let client_b = server.create_client(cx_b, "user_b").await;
3194    server
3195        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3196        .await;
3197    let active_call_a = cx_a.read(ActiveCall::global);
3198
3199    client_a
3200        .fs
3201        .insert_tree(
3202            "/root",
3203            json!({
3204                "dir-1": {
3205                    "a": "hello world",
3206                    "b": "goodnight moon",
3207                    "c": "a world of goo",
3208                    "d": "world champion of clown world",
3209                },
3210                "dir-2": {
3211                    "e": "disney world is fun",
3212                }
3213            }),
3214        )
3215        .await;
3216    let (project_a, _) = client_a.build_local_project("/root/dir-1", cx_a).await;
3217    let (worktree_2, _) = project_a
3218        .update(cx_a, |p, cx| {
3219            p.find_or_create_local_worktree("/root/dir-2", true, cx)
3220        })
3221        .await
3222        .unwrap();
3223    worktree_2
3224        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
3225        .await;
3226    let project_id = active_call_a
3227        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3228        .await
3229        .unwrap();
3230
3231    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3232
3233    // Perform a search as the guest.
3234    let results = project_b
3235        .update(cx_b, |project, cx| {
3236            project.search(SearchQuery::text("world", false, false), cx)
3237        })
3238        .await
3239        .unwrap();
3240
3241    let mut ranges_by_path = results
3242        .into_iter()
3243        .map(|(buffer, ranges)| {
3244            buffer.read_with(cx_b, |buffer, cx| {
3245                let path = buffer.file().unwrap().full_path(cx);
3246                let offset_ranges = ranges
3247                    .into_iter()
3248                    .map(|range| range.to_offset(buffer))
3249                    .collect::<Vec<_>>();
3250                (path, offset_ranges)
3251            })
3252        })
3253        .collect::<Vec<_>>();
3254    ranges_by_path.sort_by_key(|(path, _)| path.clone());
3255
3256    assert_eq!(
3257        ranges_by_path,
3258        &[
3259            (PathBuf::from("dir-1/a"), vec![6..11]),
3260            (PathBuf::from("dir-1/c"), vec![2..7]),
3261            (PathBuf::from("dir-1/d"), vec![0..5, 24..29]),
3262            (PathBuf::from("dir-2/e"), vec![7..12]),
3263        ]
3264    );
3265}
3266
3267#[gpui::test(iterations = 10)]
3268async fn test_document_highlights(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
3269    cx_a.foreground().forbid_parking();
3270    let mut server = TestServer::start(cx_a.background()).await;
3271    let client_a = server.create_client(cx_a, "user_a").await;
3272    let client_b = server.create_client(cx_b, "user_b").await;
3273    server
3274        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3275        .await;
3276    let active_call_a = cx_a.read(ActiveCall::global);
3277
3278    client_a
3279        .fs
3280        .insert_tree(
3281            "/root-1",
3282            json!({
3283                "main.rs": "fn double(number: i32) -> i32 { number + number }",
3284            }),
3285        )
3286        .await;
3287
3288    // Set up a fake language server.
3289    let mut language = Language::new(
3290        LanguageConfig {
3291            name: "Rust".into(),
3292            path_suffixes: vec!["rs".to_string()],
3293            ..Default::default()
3294        },
3295        Some(tree_sitter_rust::language()),
3296    );
3297    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
3298    client_a.language_registry.add(Arc::new(language));
3299
3300    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
3301    let project_id = active_call_a
3302        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3303        .await
3304        .unwrap();
3305    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3306
3307    // Open the file on client B.
3308    let buffer_b = cx_b
3309        .background()
3310        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
3311        .await
3312        .unwrap();
3313
3314    // Request document highlights as the guest.
3315    let fake_language_server = fake_language_servers.next().await.unwrap();
3316    fake_language_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
3317        |params, _| async move {
3318            assert_eq!(
3319                params
3320                    .text_document_position_params
3321                    .text_document
3322                    .uri
3323                    .as_str(),
3324                "file:///root-1/main.rs"
3325            );
3326            assert_eq!(
3327                params.text_document_position_params.position,
3328                lsp::Position::new(0, 34)
3329            );
3330            Ok(Some(vec![
3331                lsp::DocumentHighlight {
3332                    kind: Some(lsp::DocumentHighlightKind::WRITE),
3333                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 16)),
3334                },
3335                lsp::DocumentHighlight {
3336                    kind: Some(lsp::DocumentHighlightKind::READ),
3337                    range: lsp::Range::new(lsp::Position::new(0, 32), lsp::Position::new(0, 38)),
3338                },
3339                lsp::DocumentHighlight {
3340                    kind: Some(lsp::DocumentHighlightKind::READ),
3341                    range: lsp::Range::new(lsp::Position::new(0, 41), lsp::Position::new(0, 47)),
3342                },
3343            ]))
3344        },
3345    );
3346
3347    let highlights = project_b
3348        .update(cx_b, |p, cx| p.document_highlights(&buffer_b, 34, cx))
3349        .await
3350        .unwrap();
3351    buffer_b.read_with(cx_b, |buffer, _| {
3352        let snapshot = buffer.snapshot();
3353
3354        let highlights = highlights
3355            .into_iter()
3356            .map(|highlight| (highlight.kind, highlight.range.to_offset(&snapshot)))
3357            .collect::<Vec<_>>();
3358        assert_eq!(
3359            highlights,
3360            &[
3361                (lsp::DocumentHighlightKind::WRITE, 10..16),
3362                (lsp::DocumentHighlightKind::READ, 32..38),
3363                (lsp::DocumentHighlightKind::READ, 41..47)
3364            ]
3365        )
3366    });
3367}
3368
3369#[gpui::test(iterations = 10)]
3370async fn test_lsp_hover(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
3371    cx_a.foreground().forbid_parking();
3372    let mut server = TestServer::start(cx_a.background()).await;
3373    let client_a = server.create_client(cx_a, "user_a").await;
3374    let client_b = server.create_client(cx_b, "user_b").await;
3375    server
3376        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3377        .await;
3378    let active_call_a = cx_a.read(ActiveCall::global);
3379
3380    client_a
3381        .fs
3382        .insert_tree(
3383            "/root-1",
3384            json!({
3385                "main.rs": "use std::collections::HashMap;",
3386            }),
3387        )
3388        .await;
3389
3390    // Set up a fake language server.
3391    let mut language = Language::new(
3392        LanguageConfig {
3393            name: "Rust".into(),
3394            path_suffixes: vec!["rs".to_string()],
3395            ..Default::default()
3396        },
3397        Some(tree_sitter_rust::language()),
3398    );
3399    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
3400    client_a.language_registry.add(Arc::new(language));
3401
3402    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
3403    let project_id = active_call_a
3404        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3405        .await
3406        .unwrap();
3407    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3408
3409    // Open the file as the guest
3410    let buffer_b = cx_b
3411        .background()
3412        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
3413        .await
3414        .unwrap();
3415
3416    // Request hover information as the guest.
3417    let fake_language_server = fake_language_servers.next().await.unwrap();
3418    fake_language_server.handle_request::<lsp::request::HoverRequest, _, _>(
3419        |params, _| async move {
3420            assert_eq!(
3421                params
3422                    .text_document_position_params
3423                    .text_document
3424                    .uri
3425                    .as_str(),
3426                "file:///root-1/main.rs"
3427            );
3428            assert_eq!(
3429                params.text_document_position_params.position,
3430                lsp::Position::new(0, 22)
3431            );
3432            Ok(Some(lsp::Hover {
3433                contents: lsp::HoverContents::Array(vec![
3434                    lsp::MarkedString::String("Test hover content.".to_string()),
3435                    lsp::MarkedString::LanguageString(lsp::LanguageString {
3436                        language: "Rust".to_string(),
3437                        value: "let foo = 42;".to_string(),
3438                    }),
3439                ]),
3440                range: Some(lsp::Range::new(
3441                    lsp::Position::new(0, 22),
3442                    lsp::Position::new(0, 29),
3443                )),
3444            }))
3445        },
3446    );
3447
3448    let hover_info = project_b
3449        .update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
3450        .await
3451        .unwrap()
3452        .unwrap();
3453    buffer_b.read_with(cx_b, |buffer, _| {
3454        let snapshot = buffer.snapshot();
3455        assert_eq!(hover_info.range.unwrap().to_offset(&snapshot), 22..29);
3456        assert_eq!(
3457            hover_info.contents,
3458            vec![
3459                project::HoverBlock {
3460                    text: "Test hover content.".to_string(),
3461                    language: None,
3462                },
3463                project::HoverBlock {
3464                    text: "let foo = 42;".to_string(),
3465                    language: Some("Rust".to_string()),
3466                }
3467            ]
3468        );
3469    });
3470}
3471
3472#[gpui::test(iterations = 10)]
3473async fn test_project_symbols(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
3474    cx_a.foreground().forbid_parking();
3475    let mut server = TestServer::start(cx_a.background()).await;
3476    let client_a = server.create_client(cx_a, "user_a").await;
3477    let client_b = server.create_client(cx_b, "user_b").await;
3478    server
3479        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3480        .await;
3481    let active_call_a = cx_a.read(ActiveCall::global);
3482
3483    // Set up a fake language server.
3484    let mut language = Language::new(
3485        LanguageConfig {
3486            name: "Rust".into(),
3487            path_suffixes: vec!["rs".to_string()],
3488            ..Default::default()
3489        },
3490        Some(tree_sitter_rust::language()),
3491    );
3492    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
3493    client_a.language_registry.add(Arc::new(language));
3494
3495    client_a
3496        .fs
3497        .insert_tree(
3498            "/code",
3499            json!({
3500                "crate-1": {
3501                    "one.rs": "const ONE: usize = 1;",
3502                },
3503                "crate-2": {
3504                    "two.rs": "const TWO: usize = 2; const THREE: usize = 3;",
3505                },
3506                "private": {
3507                    "passwords.txt": "the-password",
3508                }
3509            }),
3510        )
3511        .await;
3512    let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await;
3513    let project_id = active_call_a
3514        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3515        .await
3516        .unwrap();
3517    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3518
3519    // Cause the language server to start.
3520    let _buffer = cx_b
3521        .background()
3522        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
3523        .await
3524        .unwrap();
3525
3526    let fake_language_server = fake_language_servers.next().await.unwrap();
3527    fake_language_server.handle_request::<lsp::request::WorkspaceSymbol, _, _>(|_, _| async move {
3528        #[allow(deprecated)]
3529        Ok(Some(vec![lsp::SymbolInformation {
3530            name: "TWO".into(),
3531            location: lsp::Location {
3532                uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
3533                range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
3534            },
3535            kind: lsp::SymbolKind::CONSTANT,
3536            tags: None,
3537            container_name: None,
3538            deprecated: None,
3539        }]))
3540    });
3541
3542    // Request the definition of a symbol as the guest.
3543    let symbols = project_b
3544        .update(cx_b, |p, cx| p.symbols("two", cx))
3545        .await
3546        .unwrap();
3547    assert_eq!(symbols.len(), 1);
3548    assert_eq!(symbols[0].name, "TWO");
3549
3550    // Open one of the returned symbols.
3551    let buffer_b_2 = project_b
3552        .update(cx_b, |project, cx| {
3553            project.open_buffer_for_symbol(&symbols[0], cx)
3554        })
3555        .await
3556        .unwrap();
3557    buffer_b_2.read_with(cx_b, |buffer, _| {
3558        assert_eq!(
3559            buffer.file().unwrap().path().as_ref(),
3560            Path::new("../crate-2/two.rs")
3561        );
3562    });
3563
3564    // Attempt to craft a symbol and violate host's privacy by opening an arbitrary file.
3565    let mut fake_symbol = symbols[0].clone();
3566    fake_symbol.path.path = Path::new("/code/secrets").into();
3567    let error = project_b
3568        .update(cx_b, |project, cx| {
3569            project.open_buffer_for_symbol(&fake_symbol, cx)
3570        })
3571        .await
3572        .unwrap_err();
3573    assert!(error.to_string().contains("invalid symbol signature"));
3574}
3575
3576#[gpui::test(iterations = 10)]
3577async fn test_open_buffer_while_getting_definition_pointing_to_it(
3578    cx_a: &mut TestAppContext,
3579    cx_b: &mut TestAppContext,
3580    mut rng: StdRng,
3581) {
3582    cx_a.foreground().forbid_parking();
3583    let mut server = TestServer::start(cx_a.background()).await;
3584    let client_a = server.create_client(cx_a, "user_a").await;
3585    let client_b = server.create_client(cx_b, "user_b").await;
3586    server
3587        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3588        .await;
3589    let active_call_a = cx_a.read(ActiveCall::global);
3590
3591    // Set up a fake language server.
3592    let mut language = Language::new(
3593        LanguageConfig {
3594            name: "Rust".into(),
3595            path_suffixes: vec!["rs".to_string()],
3596            ..Default::default()
3597        },
3598        Some(tree_sitter_rust::language()),
3599    );
3600    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
3601    client_a.language_registry.add(Arc::new(language));
3602
3603    client_a
3604        .fs
3605        .insert_tree(
3606            "/root",
3607            json!({
3608                "a.rs": "const ONE: usize = b::TWO;",
3609                "b.rs": "const TWO: usize = 2",
3610            }),
3611        )
3612        .await;
3613    let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await;
3614    let project_id = active_call_a
3615        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3616        .await
3617        .unwrap();
3618    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3619
3620    let buffer_b1 = cx_b
3621        .background()
3622        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
3623        .await
3624        .unwrap();
3625
3626    let fake_language_server = fake_language_servers.next().await.unwrap();
3627    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
3628        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
3629            lsp::Location::new(
3630                lsp::Url::from_file_path("/root/b.rs").unwrap(),
3631                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
3632            ),
3633        )))
3634    });
3635
3636    let definitions;
3637    let buffer_b2;
3638    if rng.gen() {
3639        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
3640        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
3641    } else {
3642        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
3643        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
3644    }
3645
3646    let buffer_b2 = buffer_b2.await.unwrap();
3647    let definitions = definitions.await.unwrap();
3648    assert_eq!(definitions.len(), 1);
3649    assert_eq!(definitions[0].target.buffer, buffer_b2);
3650}
3651
3652#[gpui::test(iterations = 10)]
3653async fn test_collaborating_with_code_actions(
3654    cx_a: &mut TestAppContext,
3655    cx_b: &mut TestAppContext,
3656) {
3657    cx_a.foreground().forbid_parking();
3658    cx_b.update(editor::init);
3659    let mut server = TestServer::start(cx_a.background()).await;
3660    let client_a = server.create_client(cx_a, "user_a").await;
3661    let client_b = server.create_client(cx_b, "user_b").await;
3662    server
3663        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3664        .await;
3665    let active_call_a = cx_a.read(ActiveCall::global);
3666
3667    // Set up a fake language server.
3668    let mut language = Language::new(
3669        LanguageConfig {
3670            name: "Rust".into(),
3671            path_suffixes: vec!["rs".to_string()],
3672            ..Default::default()
3673        },
3674        Some(tree_sitter_rust::language()),
3675    );
3676    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
3677    client_a.language_registry.add(Arc::new(language));
3678
3679    client_a
3680        .fs
3681        .insert_tree(
3682            "/a",
3683            json!({
3684                "main.rs": "mod other;\nfn main() { let foo = other::foo(); }",
3685                "other.rs": "pub fn foo() -> usize { 4 }",
3686            }),
3687        )
3688        .await;
3689    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3690    let project_id = active_call_a
3691        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3692        .await
3693        .unwrap();
3694
3695    // Join the project as client B.
3696    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3697    let (_window_b, workspace_b) =
3698        cx_b.add_window(|cx| Workspace::new(project_b.clone(), |_, _| unimplemented!(), cx));
3699    let editor_b = workspace_b
3700        .update(cx_b, |workspace, cx| {
3701            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
3702        })
3703        .await
3704        .unwrap()
3705        .downcast::<Editor>()
3706        .unwrap();
3707
3708    let mut fake_language_server = fake_language_servers.next().await.unwrap();
3709    fake_language_server
3710        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
3711            assert_eq!(
3712                params.text_document.uri,
3713                lsp::Url::from_file_path("/a/main.rs").unwrap(),
3714            );
3715            assert_eq!(params.range.start, lsp::Position::new(0, 0));
3716            assert_eq!(params.range.end, lsp::Position::new(0, 0));
3717            Ok(None)
3718        })
3719        .next()
3720        .await;
3721
3722    // Move cursor to a location that contains code actions.
3723    editor_b.update(cx_b, |editor, cx| {
3724        editor.change_selections(None, cx, |s| {
3725            s.select_ranges([Point::new(1, 31)..Point::new(1, 31)])
3726        });
3727        cx.focus(&editor_b);
3728    });
3729
3730    fake_language_server
3731        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
3732            assert_eq!(
3733                params.text_document.uri,
3734                lsp::Url::from_file_path("/a/main.rs").unwrap(),
3735            );
3736            assert_eq!(params.range.start, lsp::Position::new(1, 31));
3737            assert_eq!(params.range.end, lsp::Position::new(1, 31));
3738
3739            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
3740                lsp::CodeAction {
3741                    title: "Inline into all callers".to_string(),
3742                    edit: Some(lsp::WorkspaceEdit {
3743                        changes: Some(
3744                            [
3745                                (
3746                                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
3747                                    vec![lsp::TextEdit::new(
3748                                        lsp::Range::new(
3749                                            lsp::Position::new(1, 22),
3750                                            lsp::Position::new(1, 34),
3751                                        ),
3752                                        "4".to_string(),
3753                                    )],
3754                                ),
3755                                (
3756                                    lsp::Url::from_file_path("/a/other.rs").unwrap(),
3757                                    vec![lsp::TextEdit::new(
3758                                        lsp::Range::new(
3759                                            lsp::Position::new(0, 0),
3760                                            lsp::Position::new(0, 27),
3761                                        ),
3762                                        "".to_string(),
3763                                    )],
3764                                ),
3765                            ]
3766                            .into_iter()
3767                            .collect(),
3768                        ),
3769                        ..Default::default()
3770                    }),
3771                    data: Some(json!({
3772                        "codeActionParams": {
3773                            "range": {
3774                                "start": {"line": 1, "column": 31},
3775                                "end": {"line": 1, "column": 31},
3776                            }
3777                        }
3778                    })),
3779                    ..Default::default()
3780                },
3781            )]))
3782        })
3783        .next()
3784        .await;
3785
3786    // Toggle code actions and wait for them to display.
3787    editor_b.update(cx_b, |editor, cx| {
3788        editor.toggle_code_actions(
3789            &ToggleCodeActions {
3790                deployed_from_indicator: false,
3791            },
3792            cx,
3793        );
3794    });
3795    editor_b
3796        .condition(cx_b, |editor, _| editor.context_menu_visible())
3797        .await;
3798
3799    fake_language_server.remove_request_handler::<lsp::request::CodeActionRequest>();
3800
3801    // Confirming the code action will trigger a resolve request.
3802    let confirm_action = workspace_b
3803        .update(cx_b, |workspace, cx| {
3804            Editor::confirm_code_action(workspace, &ConfirmCodeAction { item_ix: Some(0) }, cx)
3805        })
3806        .unwrap();
3807    fake_language_server.handle_request::<lsp::request::CodeActionResolveRequest, _, _>(
3808        |_, _| async move {
3809            Ok(lsp::CodeAction {
3810                title: "Inline into all callers".to_string(),
3811                edit: Some(lsp::WorkspaceEdit {
3812                    changes: Some(
3813                        [
3814                            (
3815                                lsp::Url::from_file_path("/a/main.rs").unwrap(),
3816                                vec![lsp::TextEdit::new(
3817                                    lsp::Range::new(
3818                                        lsp::Position::new(1, 22),
3819                                        lsp::Position::new(1, 34),
3820                                    ),
3821                                    "4".to_string(),
3822                                )],
3823                            ),
3824                            (
3825                                lsp::Url::from_file_path("/a/other.rs").unwrap(),
3826                                vec![lsp::TextEdit::new(
3827                                    lsp::Range::new(
3828                                        lsp::Position::new(0, 0),
3829                                        lsp::Position::new(0, 27),
3830                                    ),
3831                                    "".to_string(),
3832                                )],
3833                            ),
3834                        ]
3835                        .into_iter()
3836                        .collect(),
3837                    ),
3838                    ..Default::default()
3839                }),
3840                ..Default::default()
3841            })
3842        },
3843    );
3844
3845    // After the action is confirmed, an editor containing both modified files is opened.
3846    confirm_action.await.unwrap();
3847    let code_action_editor = workspace_b.read_with(cx_b, |workspace, cx| {
3848        workspace
3849            .active_item(cx)
3850            .unwrap()
3851            .downcast::<Editor>()
3852            .unwrap()
3853    });
3854    code_action_editor.update(cx_b, |editor, cx| {
3855        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
3856        editor.undo(&Undo, cx);
3857        assert_eq!(
3858            editor.text(cx),
3859            "mod other;\nfn main() { let foo = other::foo(); }\npub fn foo() -> usize { 4 }"
3860        );
3861        editor.redo(&Redo, cx);
3862        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
3863    });
3864}
3865
3866#[gpui::test(iterations = 10)]
3867async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
3868    cx_a.foreground().forbid_parking();
3869    cx_b.update(editor::init);
3870    let mut server = TestServer::start(cx_a.background()).await;
3871    let client_a = server.create_client(cx_a, "user_a").await;
3872    let client_b = server.create_client(cx_b, "user_b").await;
3873    server
3874        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3875        .await;
3876    let active_call_a = cx_a.read(ActiveCall::global);
3877
3878    // Set up a fake language server.
3879    let mut language = Language::new(
3880        LanguageConfig {
3881            name: "Rust".into(),
3882            path_suffixes: vec!["rs".to_string()],
3883            ..Default::default()
3884        },
3885        Some(tree_sitter_rust::language()),
3886    );
3887    let mut fake_language_servers = language
3888        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3889            capabilities: lsp::ServerCapabilities {
3890                rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
3891                    prepare_provider: Some(true),
3892                    work_done_progress_options: Default::default(),
3893                })),
3894                ..Default::default()
3895            },
3896            ..Default::default()
3897        }))
3898        .await;
3899    client_a.language_registry.add(Arc::new(language));
3900
3901    client_a
3902        .fs
3903        .insert_tree(
3904            "/dir",
3905            json!({
3906                "one.rs": "const ONE: usize = 1;",
3907                "two.rs": "const TWO: usize = one::ONE + one::ONE;"
3908            }),
3909        )
3910        .await;
3911    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3912    let project_id = active_call_a
3913        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3914        .await
3915        .unwrap();
3916    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3917
3918    let (_window_b, workspace_b) =
3919        cx_b.add_window(|cx| Workspace::new(project_b.clone(), |_, _| unimplemented!(), cx));
3920    let editor_b = workspace_b
3921        .update(cx_b, |workspace, cx| {
3922            workspace.open_path((worktree_id, "one.rs"), None, true, cx)
3923        })
3924        .await
3925        .unwrap()
3926        .downcast::<Editor>()
3927        .unwrap();
3928    let fake_language_server = fake_language_servers.next().await.unwrap();
3929
3930    // Move cursor to a location that can be renamed.
3931    let prepare_rename = editor_b.update(cx_b, |editor, cx| {
3932        editor.change_selections(None, cx, |s| s.select_ranges([7..7]));
3933        editor.rename(&Rename, cx).unwrap()
3934    });
3935
3936    fake_language_server
3937        .handle_request::<lsp::request::PrepareRenameRequest, _, _>(|params, _| async move {
3938            assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs");
3939            assert_eq!(params.position, lsp::Position::new(0, 7));
3940            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
3941                lsp::Position::new(0, 6),
3942                lsp::Position::new(0, 9),
3943            ))))
3944        })
3945        .next()
3946        .await
3947        .unwrap();
3948    prepare_rename.await.unwrap();
3949    editor_b.update(cx_b, |editor, cx| {
3950        let rename = editor.pending_rename().unwrap();
3951        let buffer = editor.buffer().read(cx).snapshot(cx);
3952        assert_eq!(
3953            rename.range.start.to_offset(&buffer)..rename.range.end.to_offset(&buffer),
3954            6..9
3955        );
3956        rename.editor.update(cx, |rename_editor, cx| {
3957            rename_editor.buffer().update(cx, |rename_buffer, cx| {
3958                rename_buffer.edit([(0..3, "THREE")], None, cx);
3959            });
3960        });
3961    });
3962
3963    let confirm_rename = workspace_b.update(cx_b, |workspace, cx| {
3964        Editor::confirm_rename(workspace, &ConfirmRename, cx).unwrap()
3965    });
3966    fake_language_server
3967        .handle_request::<lsp::request::Rename, _, _>(|params, _| async move {
3968            assert_eq!(
3969                params.text_document_position.text_document.uri.as_str(),
3970                "file:///dir/one.rs"
3971            );
3972            assert_eq!(
3973                params.text_document_position.position,
3974                lsp::Position::new(0, 6)
3975            );
3976            assert_eq!(params.new_name, "THREE");
3977            Ok(Some(lsp::WorkspaceEdit {
3978                changes: Some(
3979                    [
3980                        (
3981                            lsp::Url::from_file_path("/dir/one.rs").unwrap(),
3982                            vec![lsp::TextEdit::new(
3983                                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
3984                                "THREE".to_string(),
3985                            )],
3986                        ),
3987                        (
3988                            lsp::Url::from_file_path("/dir/two.rs").unwrap(),
3989                            vec![
3990                                lsp::TextEdit::new(
3991                                    lsp::Range::new(
3992                                        lsp::Position::new(0, 24),
3993                                        lsp::Position::new(0, 27),
3994                                    ),
3995                                    "THREE".to_string(),
3996                                ),
3997                                lsp::TextEdit::new(
3998                                    lsp::Range::new(
3999                                        lsp::Position::new(0, 35),
4000                                        lsp::Position::new(0, 38),
4001                                    ),
4002                                    "THREE".to_string(),
4003                                ),
4004                            ],
4005                        ),
4006                    ]
4007                    .into_iter()
4008                    .collect(),
4009                ),
4010                ..Default::default()
4011            }))
4012        })
4013        .next()
4014        .await
4015        .unwrap();
4016    confirm_rename.await.unwrap();
4017
4018    let rename_editor = workspace_b.read_with(cx_b, |workspace, cx| {
4019        workspace
4020            .active_item(cx)
4021            .unwrap()
4022            .downcast::<Editor>()
4023            .unwrap()
4024    });
4025    rename_editor.update(cx_b, |editor, cx| {
4026        assert_eq!(
4027            editor.text(cx),
4028            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
4029        );
4030        editor.undo(&Undo, cx);
4031        assert_eq!(
4032            editor.text(cx),
4033            "const ONE: usize = 1;\nconst TWO: usize = one::ONE + one::ONE;"
4034        );
4035        editor.redo(&Redo, cx);
4036        assert_eq!(
4037            editor.text(cx),
4038            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
4039        );
4040    });
4041
4042    // Ensure temporary rename edits cannot be undone/redone.
4043    editor_b.update(cx_b, |editor, cx| {
4044        editor.undo(&Undo, cx);
4045        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
4046        editor.undo(&Undo, cx);
4047        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
4048        editor.redo(&Redo, cx);
4049        assert_eq!(editor.text(cx), "const THREE: usize = 1;");
4050    })
4051}
4052
4053#[gpui::test(iterations = 10)]
4054async fn test_language_server_statuses(
4055    deterministic: Arc<Deterministic>,
4056    cx_a: &mut TestAppContext,
4057    cx_b: &mut TestAppContext,
4058) {
4059    deterministic.forbid_parking();
4060
4061    cx_b.update(editor::init);
4062    let mut server = TestServer::start(cx_a.background()).await;
4063    let client_a = server.create_client(cx_a, "user_a").await;
4064    let client_b = server.create_client(cx_b, "user_b").await;
4065    server
4066        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4067        .await;
4068    let active_call_a = cx_a.read(ActiveCall::global);
4069
4070    // Set up a fake language server.
4071    let mut language = Language::new(
4072        LanguageConfig {
4073            name: "Rust".into(),
4074            path_suffixes: vec!["rs".to_string()],
4075            ..Default::default()
4076        },
4077        Some(tree_sitter_rust::language()),
4078    );
4079    let mut fake_language_servers = language
4080        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4081            name: "the-language-server",
4082            ..Default::default()
4083        }))
4084        .await;
4085    client_a.language_registry.add(Arc::new(language));
4086
4087    client_a
4088        .fs
4089        .insert_tree(
4090            "/dir",
4091            json!({
4092                "main.rs": "const ONE: usize = 1;",
4093            }),
4094        )
4095        .await;
4096    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
4097
4098    let _buffer_a = project_a
4099        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
4100        .await
4101        .unwrap();
4102
4103    let fake_language_server = fake_language_servers.next().await.unwrap();
4104    fake_language_server.start_progress("the-token").await;
4105    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
4106        token: lsp::NumberOrString::String("the-token".to_string()),
4107        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
4108            lsp::WorkDoneProgressReport {
4109                message: Some("the-message".to_string()),
4110                ..Default::default()
4111            },
4112        )),
4113    });
4114    deterministic.run_until_parked();
4115    project_a.read_with(cx_a, |project, _| {
4116        let status = project.language_server_statuses().next().unwrap();
4117        assert_eq!(status.name, "the-language-server");
4118        assert_eq!(status.pending_work.len(), 1);
4119        assert_eq!(
4120            status.pending_work["the-token"].message.as_ref().unwrap(),
4121            "the-message"
4122        );
4123    });
4124
4125    let project_id = active_call_a
4126        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4127        .await
4128        .unwrap();
4129    deterministic.run_until_parked();
4130    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4131    project_b.read_with(cx_b, |project, _| {
4132        let status = project.language_server_statuses().next().unwrap();
4133        assert_eq!(status.name, "the-language-server");
4134    });
4135
4136    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
4137        token: lsp::NumberOrString::String("the-token".to_string()),
4138        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
4139            lsp::WorkDoneProgressReport {
4140                message: Some("the-message-2".to_string()),
4141                ..Default::default()
4142            },
4143        )),
4144    });
4145    deterministic.run_until_parked();
4146    project_a.read_with(cx_a, |project, _| {
4147        let status = project.language_server_statuses().next().unwrap();
4148        assert_eq!(status.name, "the-language-server");
4149        assert_eq!(status.pending_work.len(), 1);
4150        assert_eq!(
4151            status.pending_work["the-token"].message.as_ref().unwrap(),
4152            "the-message-2"
4153        );
4154    });
4155    project_b.read_with(cx_b, |project, _| {
4156        let status = project.language_server_statuses().next().unwrap();
4157        assert_eq!(status.name, "the-language-server");
4158        assert_eq!(status.pending_work.len(), 1);
4159        assert_eq!(
4160            status.pending_work["the-token"].message.as_ref().unwrap(),
4161            "the-message-2"
4162        );
4163    });
4164}
4165
4166#[gpui::test(iterations = 10)]
4167async fn test_contacts(
4168    deterministic: Arc<Deterministic>,
4169    cx_a: &mut TestAppContext,
4170    cx_b: &mut TestAppContext,
4171    cx_c: &mut TestAppContext,
4172) {
4173    cx_a.foreground().forbid_parking();
4174    let mut server = TestServer::start(cx_a.background()).await;
4175    let client_a = server.create_client(cx_a, "user_a").await;
4176    let client_b = server.create_client(cx_b, "user_b").await;
4177    let client_c = server.create_client(cx_c, "user_c").await;
4178    server
4179        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
4180        .await;
4181    let active_call_a = cx_a.read(ActiveCall::global);
4182    let active_call_b = cx_b.read(ActiveCall::global);
4183    let active_call_c = cx_c.read(ActiveCall::global);
4184
4185    deterministic.run_until_parked();
4186    assert_eq!(
4187        contacts(&client_a, cx_a),
4188        [
4189            ("user_b".to_string(), "online", "free"),
4190            ("user_c".to_string(), "online", "free")
4191        ]
4192    );
4193    assert_eq!(
4194        contacts(&client_b, cx_b),
4195        [
4196            ("user_a".to_string(), "online", "free"),
4197            ("user_c".to_string(), "online", "free")
4198        ]
4199    );
4200    assert_eq!(
4201        contacts(&client_c, cx_c),
4202        [
4203            ("user_a".to_string(), "online", "free"),
4204            ("user_b".to_string(), "online", "free")
4205        ]
4206    );
4207
4208    server.disconnect_client(client_c.peer_id().unwrap());
4209    server.forbid_connections();
4210    deterministic.advance_clock(rpc::RECEIVE_TIMEOUT);
4211    assert_eq!(
4212        contacts(&client_a, cx_a),
4213        [
4214            ("user_b".to_string(), "online", "free"),
4215            ("user_c".to_string(), "offline", "free")
4216        ]
4217    );
4218    assert_eq!(
4219        contacts(&client_b, cx_b),
4220        [
4221            ("user_a".to_string(), "online", "free"),
4222            ("user_c".to_string(), "offline", "free")
4223        ]
4224    );
4225    assert_eq!(contacts(&client_c, cx_c), []);
4226
4227    server.allow_connections();
4228    client_c
4229        .authenticate_and_connect(false, &cx_c.to_async())
4230        .await
4231        .unwrap();
4232
4233    deterministic.run_until_parked();
4234    assert_eq!(
4235        contacts(&client_a, cx_a),
4236        [
4237            ("user_b".to_string(), "online", "free"),
4238            ("user_c".to_string(), "online", "free")
4239        ]
4240    );
4241    assert_eq!(
4242        contacts(&client_b, cx_b),
4243        [
4244            ("user_a".to_string(), "online", "free"),
4245            ("user_c".to_string(), "online", "free")
4246        ]
4247    );
4248    assert_eq!(
4249        contacts(&client_c, cx_c),
4250        [
4251            ("user_a".to_string(), "online", "free"),
4252            ("user_b".to_string(), "online", "free")
4253        ]
4254    );
4255
4256    active_call_a
4257        .update(cx_a, |call, cx| {
4258            call.invite(client_b.user_id().unwrap(), None, cx)
4259        })
4260        .await
4261        .unwrap();
4262    deterministic.run_until_parked();
4263    assert_eq!(
4264        contacts(&client_a, cx_a),
4265        [
4266            ("user_b".to_string(), "online", "busy"),
4267            ("user_c".to_string(), "online", "free")
4268        ]
4269    );
4270    assert_eq!(
4271        contacts(&client_b, cx_b),
4272        [
4273            ("user_a".to_string(), "online", "busy"),
4274            ("user_c".to_string(), "online", "free")
4275        ]
4276    );
4277    assert_eq!(
4278        contacts(&client_c, cx_c),
4279        [
4280            ("user_a".to_string(), "online", "busy"),
4281            ("user_b".to_string(), "online", "busy")
4282        ]
4283    );
4284
4285    active_call_b.update(cx_b, |call, _| call.decline_incoming().unwrap());
4286    deterministic.run_until_parked();
4287    assert_eq!(
4288        contacts(&client_a, cx_a),
4289        [
4290            ("user_b".to_string(), "online", "free"),
4291            ("user_c".to_string(), "online", "free")
4292        ]
4293    );
4294    assert_eq!(
4295        contacts(&client_b, cx_b),
4296        [
4297            ("user_a".to_string(), "online", "free"),
4298            ("user_c".to_string(), "online", "free")
4299        ]
4300    );
4301    assert_eq!(
4302        contacts(&client_c, cx_c),
4303        [
4304            ("user_a".to_string(), "online", "free"),
4305            ("user_b".to_string(), "online", "free")
4306        ]
4307    );
4308
4309    active_call_c
4310        .update(cx_c, |call, cx| {
4311            call.invite(client_a.user_id().unwrap(), None, cx)
4312        })
4313        .await
4314        .unwrap();
4315    deterministic.run_until_parked();
4316    assert_eq!(
4317        contacts(&client_a, cx_a),
4318        [
4319            ("user_b".to_string(), "online", "free"),
4320            ("user_c".to_string(), "online", "busy")
4321        ]
4322    );
4323    assert_eq!(
4324        contacts(&client_b, cx_b),
4325        [
4326            ("user_a".to_string(), "online", "busy"),
4327            ("user_c".to_string(), "online", "busy")
4328        ]
4329    );
4330    assert_eq!(
4331        contacts(&client_c, cx_c),
4332        [
4333            ("user_a".to_string(), "online", "busy"),
4334            ("user_b".to_string(), "online", "free")
4335        ]
4336    );
4337
4338    active_call_a
4339        .update(cx_a, |call, cx| call.accept_incoming(cx))
4340        .await
4341        .unwrap();
4342    deterministic.run_until_parked();
4343    assert_eq!(
4344        contacts(&client_a, cx_a),
4345        [
4346            ("user_b".to_string(), "online", "free"),
4347            ("user_c".to_string(), "online", "busy")
4348        ]
4349    );
4350    assert_eq!(
4351        contacts(&client_b, cx_b),
4352        [
4353            ("user_a".to_string(), "online", "busy"),
4354            ("user_c".to_string(), "online", "busy")
4355        ]
4356    );
4357    assert_eq!(
4358        contacts(&client_c, cx_c),
4359        [
4360            ("user_a".to_string(), "online", "busy"),
4361            ("user_b".to_string(), "online", "free")
4362        ]
4363    );
4364
4365    active_call_a
4366        .update(cx_a, |call, cx| {
4367            call.invite(client_b.user_id().unwrap(), None, cx)
4368        })
4369        .await
4370        .unwrap();
4371    deterministic.run_until_parked();
4372    assert_eq!(
4373        contacts(&client_a, cx_a),
4374        [
4375            ("user_b".to_string(), "online", "busy"),
4376            ("user_c".to_string(), "online", "busy")
4377        ]
4378    );
4379    assert_eq!(
4380        contacts(&client_b, cx_b),
4381        [
4382            ("user_a".to_string(), "online", "busy"),
4383            ("user_c".to_string(), "online", "busy")
4384        ]
4385    );
4386    assert_eq!(
4387        contacts(&client_c, cx_c),
4388        [
4389            ("user_a".to_string(), "online", "busy"),
4390            ("user_b".to_string(), "online", "busy")
4391        ]
4392    );
4393
4394    active_call_a.update(cx_a, |call, cx| call.hang_up(cx).unwrap());
4395    deterministic.run_until_parked();
4396    assert_eq!(
4397        contacts(&client_a, cx_a),
4398        [
4399            ("user_b".to_string(), "online", "free"),
4400            ("user_c".to_string(), "online", "free")
4401        ]
4402    );
4403    assert_eq!(
4404        contacts(&client_b, cx_b),
4405        [
4406            ("user_a".to_string(), "online", "free"),
4407            ("user_c".to_string(), "online", "free")
4408        ]
4409    );
4410    assert_eq!(
4411        contacts(&client_c, cx_c),
4412        [
4413            ("user_a".to_string(), "online", "free"),
4414            ("user_b".to_string(), "online", "free")
4415        ]
4416    );
4417
4418    active_call_a
4419        .update(cx_a, |call, cx| {
4420            call.invite(client_b.user_id().unwrap(), None, cx)
4421        })
4422        .await
4423        .unwrap();
4424    deterministic.run_until_parked();
4425    assert_eq!(
4426        contacts(&client_a, cx_a),
4427        [
4428            ("user_b".to_string(), "online", "busy"),
4429            ("user_c".to_string(), "online", "free")
4430        ]
4431    );
4432    assert_eq!(
4433        contacts(&client_b, cx_b),
4434        [
4435            ("user_a".to_string(), "online", "busy"),
4436            ("user_c".to_string(), "online", "free")
4437        ]
4438    );
4439    assert_eq!(
4440        contacts(&client_c, cx_c),
4441        [
4442            ("user_a".to_string(), "online", "busy"),
4443            ("user_b".to_string(), "online", "busy")
4444        ]
4445    );
4446
4447    server.forbid_connections();
4448    server.disconnect_client(client_a.peer_id().unwrap());
4449    deterministic.advance_clock(rpc::RECEIVE_TIMEOUT);
4450    assert_eq!(contacts(&client_a, cx_a), []);
4451    assert_eq!(
4452        contacts(&client_b, cx_b),
4453        [
4454            ("user_a".to_string(), "offline", "free"),
4455            ("user_c".to_string(), "online", "free")
4456        ]
4457    );
4458    assert_eq!(
4459        contacts(&client_c, cx_c),
4460        [
4461            ("user_a".to_string(), "offline", "free"),
4462            ("user_b".to_string(), "online", "free")
4463        ]
4464    );
4465
4466    #[allow(clippy::type_complexity)]
4467    fn contacts(
4468        client: &TestClient,
4469        cx: &TestAppContext,
4470    ) -> Vec<(String, &'static str, &'static str)> {
4471        client.user_store.read_with(cx, |store, _| {
4472            store
4473                .contacts()
4474                .iter()
4475                .map(|contact| {
4476                    (
4477                        contact.user.github_login.clone(),
4478                        if contact.online { "online" } else { "offline" },
4479                        if contact.busy { "busy" } else { "free" },
4480                    )
4481                })
4482                .collect()
4483        })
4484    }
4485}
4486
4487#[gpui::test(iterations = 10)]
4488async fn test_contact_requests(
4489    executor: Arc<Deterministic>,
4490    cx_a: &mut TestAppContext,
4491    cx_a2: &mut TestAppContext,
4492    cx_b: &mut TestAppContext,
4493    cx_b2: &mut TestAppContext,
4494    cx_c: &mut TestAppContext,
4495    cx_c2: &mut TestAppContext,
4496) {
4497    cx_a.foreground().forbid_parking();
4498
4499    // Connect to a server as 3 clients.
4500    let mut server = TestServer::start(cx_a.background()).await;
4501    let client_a = server.create_client(cx_a, "user_a").await;
4502    let client_a2 = server.create_client(cx_a2, "user_a").await;
4503    let client_b = server.create_client(cx_b, "user_b").await;
4504    let client_b2 = server.create_client(cx_b2, "user_b").await;
4505    let client_c = server.create_client(cx_c, "user_c").await;
4506    let client_c2 = server.create_client(cx_c2, "user_c").await;
4507
4508    assert_eq!(client_a.user_id().unwrap(), client_a2.user_id().unwrap());
4509    assert_eq!(client_b.user_id().unwrap(), client_b2.user_id().unwrap());
4510    assert_eq!(client_c.user_id().unwrap(), client_c2.user_id().unwrap());
4511
4512    // User A and User C request that user B become their contact.
4513    client_a
4514        .user_store
4515        .update(cx_a, |store, cx| {
4516            store.request_contact(client_b.user_id().unwrap(), cx)
4517        })
4518        .await
4519        .unwrap();
4520    client_c
4521        .user_store
4522        .update(cx_c, |store, cx| {
4523            store.request_contact(client_b.user_id().unwrap(), cx)
4524        })
4525        .await
4526        .unwrap();
4527    executor.run_until_parked();
4528
4529    // All users see the pending request appear in all their clients.
4530    assert_eq!(
4531        client_a.summarize_contacts(cx_a).outgoing_requests,
4532        &["user_b"]
4533    );
4534    assert_eq!(
4535        client_a2.summarize_contacts(cx_a2).outgoing_requests,
4536        &["user_b"]
4537    );
4538    assert_eq!(
4539        client_b.summarize_contacts(cx_b).incoming_requests,
4540        &["user_a", "user_c"]
4541    );
4542    assert_eq!(
4543        client_b2.summarize_contacts(cx_b2).incoming_requests,
4544        &["user_a", "user_c"]
4545    );
4546    assert_eq!(
4547        client_c.summarize_contacts(cx_c).outgoing_requests,
4548        &["user_b"]
4549    );
4550    assert_eq!(
4551        client_c2.summarize_contacts(cx_c2).outgoing_requests,
4552        &["user_b"]
4553    );
4554
4555    // Contact requests are present upon connecting (tested here via disconnect/reconnect)
4556    disconnect_and_reconnect(&client_a, cx_a).await;
4557    disconnect_and_reconnect(&client_b, cx_b).await;
4558    disconnect_and_reconnect(&client_c, cx_c).await;
4559    executor.run_until_parked();
4560    assert_eq!(
4561        client_a.summarize_contacts(cx_a).outgoing_requests,
4562        &["user_b"]
4563    );
4564    assert_eq!(
4565        client_b.summarize_contacts(cx_b).incoming_requests,
4566        &["user_a", "user_c"]
4567    );
4568    assert_eq!(
4569        client_c.summarize_contacts(cx_c).outgoing_requests,
4570        &["user_b"]
4571    );
4572
4573    // User B accepts the request from user A.
4574    client_b
4575        .user_store
4576        .update(cx_b, |store, cx| {
4577            store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
4578        })
4579        .await
4580        .unwrap();
4581
4582    executor.run_until_parked();
4583
4584    // User B sees user A as their contact now in all client, and the incoming request from them is removed.
4585    let contacts_b = client_b.summarize_contacts(cx_b);
4586    assert_eq!(contacts_b.current, &["user_a"]);
4587    assert_eq!(contacts_b.incoming_requests, &["user_c"]);
4588    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
4589    assert_eq!(contacts_b2.current, &["user_a"]);
4590    assert_eq!(contacts_b2.incoming_requests, &["user_c"]);
4591
4592    // User A sees user B as their contact now in all clients, and the outgoing request to them is removed.
4593    let contacts_a = client_a.summarize_contacts(cx_a);
4594    assert_eq!(contacts_a.current, &["user_b"]);
4595    assert!(contacts_a.outgoing_requests.is_empty());
4596    let contacts_a2 = client_a2.summarize_contacts(cx_a2);
4597    assert_eq!(contacts_a2.current, &["user_b"]);
4598    assert!(contacts_a2.outgoing_requests.is_empty());
4599
4600    // Contacts are present upon connecting (tested here via disconnect/reconnect)
4601    disconnect_and_reconnect(&client_a, cx_a).await;
4602    disconnect_and_reconnect(&client_b, cx_b).await;
4603    disconnect_and_reconnect(&client_c, cx_c).await;
4604    executor.run_until_parked();
4605    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
4606    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
4607    assert_eq!(
4608        client_b.summarize_contacts(cx_b).incoming_requests,
4609        &["user_c"]
4610    );
4611    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
4612    assert_eq!(
4613        client_c.summarize_contacts(cx_c).outgoing_requests,
4614        &["user_b"]
4615    );
4616
4617    // User B rejects the request from user C.
4618    client_b
4619        .user_store
4620        .update(cx_b, |store, cx| {
4621            store.respond_to_contact_request(client_c.user_id().unwrap(), false, cx)
4622        })
4623        .await
4624        .unwrap();
4625
4626    executor.run_until_parked();
4627
4628    // User B doesn't see user C as their contact, and the incoming request from them is removed.
4629    let contacts_b = client_b.summarize_contacts(cx_b);
4630    assert_eq!(contacts_b.current, &["user_a"]);
4631    assert!(contacts_b.incoming_requests.is_empty());
4632    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
4633    assert_eq!(contacts_b2.current, &["user_a"]);
4634    assert!(contacts_b2.incoming_requests.is_empty());
4635
4636    // User C doesn't see user B as their contact, and the outgoing request to them is removed.
4637    let contacts_c = client_c.summarize_contacts(cx_c);
4638    assert!(contacts_c.current.is_empty());
4639    assert!(contacts_c.outgoing_requests.is_empty());
4640    let contacts_c2 = client_c2.summarize_contacts(cx_c2);
4641    assert!(contacts_c2.current.is_empty());
4642    assert!(contacts_c2.outgoing_requests.is_empty());
4643
4644    // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect)
4645    disconnect_and_reconnect(&client_a, cx_a).await;
4646    disconnect_and_reconnect(&client_b, cx_b).await;
4647    disconnect_and_reconnect(&client_c, cx_c).await;
4648    executor.run_until_parked();
4649    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
4650    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
4651    assert!(client_b
4652        .summarize_contacts(cx_b)
4653        .incoming_requests
4654        .is_empty());
4655    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
4656    assert!(client_c
4657        .summarize_contacts(cx_c)
4658        .outgoing_requests
4659        .is_empty());
4660
4661    async fn disconnect_and_reconnect(client: &TestClient, cx: &mut TestAppContext) {
4662        client.disconnect(&cx.to_async()).unwrap();
4663        client.clear_contacts(cx).await;
4664        client
4665            .authenticate_and_connect(false, &cx.to_async())
4666            .await
4667            .unwrap();
4668    }
4669}
4670
4671#[gpui::test(iterations = 10)]
4672async fn test_following(
4673    deterministic: Arc<Deterministic>,
4674    cx_a: &mut TestAppContext,
4675    cx_b: &mut TestAppContext,
4676) {
4677    cx_a.foreground().forbid_parking();
4678    cx_a.update(editor::init);
4679    cx_b.update(editor::init);
4680
4681    let mut server = TestServer::start(cx_a.background()).await;
4682    let client_a = server.create_client(cx_a, "user_a").await;
4683    let client_b = server.create_client(cx_b, "user_b").await;
4684    server
4685        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4686        .await;
4687    let active_call_a = cx_a.read(ActiveCall::global);
4688    let active_call_b = cx_b.read(ActiveCall::global);
4689
4690    client_a
4691        .fs
4692        .insert_tree(
4693            "/a",
4694            json!({
4695                "1.txt": "one",
4696                "2.txt": "two",
4697                "3.txt": "three",
4698            }),
4699        )
4700        .await;
4701    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
4702    active_call_a
4703        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
4704        .await
4705        .unwrap();
4706
4707    let project_id = active_call_a
4708        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4709        .await
4710        .unwrap();
4711    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4712    active_call_b
4713        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
4714        .await
4715        .unwrap();
4716
4717    // Client A opens some editors.
4718    let workspace_a = client_a.build_workspace(&project_a, cx_a);
4719    let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
4720    let editor_a1 = workspace_a
4721        .update(cx_a, |workspace, cx| {
4722            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
4723        })
4724        .await
4725        .unwrap()
4726        .downcast::<Editor>()
4727        .unwrap();
4728    let editor_a2 = workspace_a
4729        .update(cx_a, |workspace, cx| {
4730            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
4731        })
4732        .await
4733        .unwrap()
4734        .downcast::<Editor>()
4735        .unwrap();
4736
4737    // Client B opens an editor.
4738    let workspace_b = client_b.build_workspace(&project_b, cx_b);
4739    let editor_b1 = workspace_b
4740        .update(cx_b, |workspace, cx| {
4741            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
4742        })
4743        .await
4744        .unwrap()
4745        .downcast::<Editor>()
4746        .unwrap();
4747
4748    let client_a_id = project_b.read_with(cx_b, |project, _| {
4749        project.collaborators().values().next().unwrap().peer_id
4750    });
4751    let client_b_id = project_a.read_with(cx_a, |project, _| {
4752        project.collaborators().values().next().unwrap().peer_id
4753    });
4754
4755    // When client B starts following client A, all visible view states are replicated to client B.
4756    editor_a1.update(cx_a, |editor, cx| {
4757        editor.change_selections(None, cx, |s| s.select_ranges([0..1]))
4758    });
4759    editor_a2.update(cx_a, |editor, cx| {
4760        editor.change_selections(None, cx, |s| s.select_ranges([2..3]))
4761    });
4762    workspace_b
4763        .update(cx_b, |workspace, cx| {
4764            workspace
4765                .toggle_follow(&ToggleFollow(client_a_id), cx)
4766                .unwrap()
4767        })
4768        .await
4769        .unwrap();
4770
4771    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
4772        workspace
4773            .active_item(cx)
4774            .unwrap()
4775            .downcast::<Editor>()
4776            .unwrap()
4777    });
4778    assert!(cx_b.read(|cx| editor_b2.is_focused(cx)));
4779    assert_eq!(
4780        editor_b2.read_with(cx_b, |editor, cx| editor.project_path(cx)),
4781        Some((worktree_id, "2.txt").into())
4782    );
4783    assert_eq!(
4784        editor_b2.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
4785        vec![2..3]
4786    );
4787    assert_eq!(
4788        editor_b1.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
4789        vec![0..1]
4790    );
4791
4792    // When client A activates a different editor, client B does so as well.
4793    workspace_a.update(cx_a, |workspace, cx| {
4794        workspace.activate_item(&editor_a1, cx)
4795    });
4796    workspace_b
4797        .condition(cx_b, |workspace, cx| {
4798            workspace.active_item(cx).unwrap().id() == editor_b1.id()
4799        })
4800        .await;
4801
4802    // When client A navigates back and forth, client B does so as well.
4803    workspace_a
4804        .update(cx_a, |workspace, cx| {
4805            workspace::Pane::go_back(workspace, None, cx)
4806        })
4807        .await;
4808    workspace_b
4809        .condition(cx_b, |workspace, cx| {
4810            workspace.active_item(cx).unwrap().id() == editor_b2.id()
4811        })
4812        .await;
4813
4814    workspace_a
4815        .update(cx_a, |workspace, cx| {
4816            workspace::Pane::go_forward(workspace, None, cx)
4817        })
4818        .await;
4819    workspace_b
4820        .condition(cx_b, |workspace, cx| {
4821            workspace.active_item(cx).unwrap().id() == editor_b1.id()
4822        })
4823        .await;
4824
4825    // Changes to client A's editor are reflected on client B.
4826    editor_a1.update(cx_a, |editor, cx| {
4827        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2]));
4828    });
4829    editor_b1
4830        .condition(cx_b, |editor, cx| {
4831            editor.selections.ranges(cx) == vec![1..1, 2..2]
4832        })
4833        .await;
4834
4835    editor_a1.update(cx_a, |editor, cx| editor.set_text("TWO", cx));
4836    editor_b1
4837        .condition(cx_b, |editor, cx| editor.text(cx) == "TWO")
4838        .await;
4839
4840    editor_a1.update(cx_a, |editor, cx| {
4841        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
4842        editor.set_scroll_position(vec2f(0., 100.), cx);
4843    });
4844    editor_b1
4845        .condition(cx_b, |editor, cx| {
4846            editor.selections.ranges(cx) == vec![3..3]
4847        })
4848        .await;
4849
4850    // After unfollowing, client B stops receiving updates from client A.
4851    workspace_b.update(cx_b, |workspace, cx| {
4852        workspace.unfollow(&workspace.active_pane().clone(), cx)
4853    });
4854    workspace_a.update(cx_a, |workspace, cx| {
4855        workspace.activate_item(&editor_a2, cx)
4856    });
4857    deterministic.run_until_parked();
4858    assert_eq!(
4859        workspace_b.read_with(cx_b, |workspace, cx| workspace
4860            .active_item(cx)
4861            .unwrap()
4862            .id()),
4863        editor_b1.id()
4864    );
4865
4866    // Client A starts following client B.
4867    workspace_a
4868        .update(cx_a, |workspace, cx| {
4869            workspace
4870                .toggle_follow(&ToggleFollow(client_b_id), cx)
4871                .unwrap()
4872        })
4873        .await
4874        .unwrap();
4875    assert_eq!(
4876        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
4877        Some(client_b_id)
4878    );
4879    assert_eq!(
4880        workspace_a.read_with(cx_a, |workspace, cx| workspace
4881            .active_item(cx)
4882            .unwrap()
4883            .id()),
4884        editor_a1.id()
4885    );
4886
4887    // Client B activates an external window, which causes a new screen-sharing item to be added to the pane.
4888    let display = MacOSDisplay::new();
4889    active_call_b
4890        .update(cx_b, |call, cx| call.set_location(None, cx))
4891        .await
4892        .unwrap();
4893    active_call_b
4894        .update(cx_b, |call, cx| {
4895            call.room().unwrap().update(cx, |room, cx| {
4896                room.set_display_sources(vec![display.clone()]);
4897                room.share_screen(cx)
4898            })
4899        })
4900        .await
4901        .unwrap();
4902    deterministic.run_until_parked();
4903    let shared_screen = workspace_a.read_with(cx_a, |workspace, cx| {
4904        workspace
4905            .active_item(cx)
4906            .unwrap()
4907            .downcast::<SharedScreen>()
4908            .unwrap()
4909    });
4910
4911    // Client B activates Zed again, which causes the previous editor to become focused again.
4912    active_call_b
4913        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
4914        .await
4915        .unwrap();
4916    deterministic.run_until_parked();
4917    assert_eq!(
4918        workspace_a.read_with(cx_a, |workspace, cx| workspace
4919            .active_item(cx)
4920            .unwrap()
4921            .id()),
4922        editor_a1.id()
4923    );
4924
4925    // Client B activates an external window again, and the previously-opened screen-sharing item
4926    // gets activated.
4927    active_call_b
4928        .update(cx_b, |call, cx| call.set_location(None, cx))
4929        .await
4930        .unwrap();
4931    deterministic.run_until_parked();
4932    assert_eq!(
4933        workspace_a.read_with(cx_a, |workspace, cx| workspace
4934            .active_item(cx)
4935            .unwrap()
4936            .id()),
4937        shared_screen.id()
4938    );
4939
4940    // Following interrupts when client B disconnects.
4941    client_b.disconnect(&cx_b.to_async()).unwrap();
4942    deterministic.run_until_parked();
4943    assert_eq!(
4944        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
4945        None
4946    );
4947}
4948
4949#[gpui::test(iterations = 10)]
4950async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
4951    cx_a.foreground().forbid_parking();
4952    cx_a.update(editor::init);
4953    cx_b.update(editor::init);
4954
4955    let mut server = TestServer::start(cx_a.background()).await;
4956    let client_a = server.create_client(cx_a, "user_a").await;
4957    let client_b = server.create_client(cx_b, "user_b").await;
4958    server
4959        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4960        .await;
4961    let active_call_a = cx_a.read(ActiveCall::global);
4962    let active_call_b = cx_b.read(ActiveCall::global);
4963
4964    // Client A shares a project.
4965    client_a
4966        .fs
4967        .insert_tree(
4968            "/a",
4969            json!({
4970                "1.txt": "one",
4971                "2.txt": "two",
4972                "3.txt": "three",
4973                "4.txt": "four",
4974            }),
4975        )
4976        .await;
4977    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
4978    active_call_a
4979        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
4980        .await
4981        .unwrap();
4982    let project_id = active_call_a
4983        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4984        .await
4985        .unwrap();
4986
4987    // Client B joins the project.
4988    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4989    active_call_b
4990        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
4991        .await
4992        .unwrap();
4993
4994    // Client A opens some editors.
4995    let workspace_a = client_a.build_workspace(&project_a, cx_a);
4996    let pane_a1 = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
4997    let _editor_a1 = workspace_a
4998        .update(cx_a, |workspace, cx| {
4999            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
5000        })
5001        .await
5002        .unwrap()
5003        .downcast::<Editor>()
5004        .unwrap();
5005
5006    // Client B opens an editor.
5007    let workspace_b = client_b.build_workspace(&project_b, cx_b);
5008    let pane_b1 = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
5009    let _editor_b1 = workspace_b
5010        .update(cx_b, |workspace, cx| {
5011            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
5012        })
5013        .await
5014        .unwrap()
5015        .downcast::<Editor>()
5016        .unwrap();
5017
5018    // Clients A and B follow each other in split panes
5019    workspace_a.update(cx_a, |workspace, cx| {
5020        workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
5021        let pane_a1 = pane_a1.clone();
5022        cx.defer(move |workspace, _| {
5023            assert_ne!(*workspace.active_pane(), pane_a1);
5024        });
5025    });
5026    workspace_a
5027        .update(cx_a, |workspace, cx| {
5028            let leader_id = *project_a.read(cx).collaborators().keys().next().unwrap();
5029            workspace
5030                .toggle_follow(&workspace::ToggleFollow(leader_id), cx)
5031                .unwrap()
5032        })
5033        .await
5034        .unwrap();
5035    workspace_b.update(cx_b, |workspace, cx| {
5036        workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
5037        let pane_b1 = pane_b1.clone();
5038        cx.defer(move |workspace, _| {
5039            assert_ne!(*workspace.active_pane(), pane_b1);
5040        });
5041    });
5042    workspace_b
5043        .update(cx_b, |workspace, cx| {
5044            let leader_id = *project_b.read(cx).collaborators().keys().next().unwrap();
5045            workspace
5046                .toggle_follow(&workspace::ToggleFollow(leader_id), cx)
5047                .unwrap()
5048        })
5049        .await
5050        .unwrap();
5051
5052    workspace_a.update(cx_a, |workspace, cx| {
5053        workspace.activate_next_pane(cx);
5054    });
5055    // Wait for focus effects to be fully flushed
5056    workspace_a.update(cx_a, |workspace, _| {
5057        assert_eq!(*workspace.active_pane(), pane_a1);
5058    });
5059
5060    workspace_a
5061        .update(cx_a, |workspace, cx| {
5062            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
5063        })
5064        .await
5065        .unwrap();
5066    workspace_b.update(cx_b, |workspace, cx| {
5067        workspace.activate_next_pane(cx);
5068    });
5069
5070    workspace_b
5071        .update(cx_b, |workspace, cx| {
5072            assert_eq!(*workspace.active_pane(), pane_b1);
5073            workspace.open_path((worktree_id, "4.txt"), None, true, cx)
5074        })
5075        .await
5076        .unwrap();
5077    cx_a.foreground().run_until_parked();
5078
5079    // Ensure leader updates don't change the active pane of followers
5080    workspace_a.read_with(cx_a, |workspace, _| {
5081        assert_eq!(*workspace.active_pane(), pane_a1);
5082    });
5083    workspace_b.read_with(cx_b, |workspace, _| {
5084        assert_eq!(*workspace.active_pane(), pane_b1);
5085    });
5086
5087    // Ensure peers following each other doesn't cause an infinite loop.
5088    assert_eq!(
5089        workspace_a.read_with(cx_a, |workspace, cx| workspace
5090            .active_item(cx)
5091            .unwrap()
5092            .project_path(cx)),
5093        Some((worktree_id, "3.txt").into())
5094    );
5095    workspace_a.update(cx_a, |workspace, cx| {
5096        assert_eq!(
5097            workspace.active_item(cx).unwrap().project_path(cx),
5098            Some((worktree_id, "3.txt").into())
5099        );
5100        workspace.activate_next_pane(cx);
5101    });
5102
5103    workspace_a.update(cx_a, |workspace, cx| {
5104        assert_eq!(
5105            workspace.active_item(cx).unwrap().project_path(cx),
5106            Some((worktree_id, "4.txt").into())
5107        );
5108    });
5109
5110    workspace_b.update(cx_b, |workspace, cx| {
5111        assert_eq!(
5112            workspace.active_item(cx).unwrap().project_path(cx),
5113            Some((worktree_id, "4.txt").into())
5114        );
5115        workspace.activate_next_pane(cx);
5116    });
5117
5118    workspace_b.update(cx_b, |workspace, cx| {
5119        assert_eq!(
5120            workspace.active_item(cx).unwrap().project_path(cx),
5121            Some((worktree_id, "3.txt").into())
5122        );
5123    });
5124}
5125
5126#[gpui::test(iterations = 10)]
5127async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
5128    cx_a.foreground().forbid_parking();
5129    cx_a.update(editor::init);
5130    cx_b.update(editor::init);
5131
5132    // 2 clients connect to a server.
5133    let mut server = TestServer::start(cx_a.background()).await;
5134    let client_a = server.create_client(cx_a, "user_a").await;
5135    let client_b = server.create_client(cx_b, "user_b").await;
5136    server
5137        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5138        .await;
5139    let active_call_a = cx_a.read(ActiveCall::global);
5140    let active_call_b = cx_b.read(ActiveCall::global);
5141
5142    // Client A shares a project.
5143    client_a
5144        .fs
5145        .insert_tree(
5146            "/a",
5147            json!({
5148                "1.txt": "one",
5149                "2.txt": "two",
5150                "3.txt": "three",
5151            }),
5152        )
5153        .await;
5154    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
5155    active_call_a
5156        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
5157        .await
5158        .unwrap();
5159
5160    let project_id = active_call_a
5161        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5162        .await
5163        .unwrap();
5164    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5165    active_call_b
5166        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
5167        .await
5168        .unwrap();
5169
5170    // Client A opens some editors.
5171    let workspace_a = client_a.build_workspace(&project_a, cx_a);
5172    let _editor_a1 = workspace_a
5173        .update(cx_a, |workspace, cx| {
5174            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
5175        })
5176        .await
5177        .unwrap()
5178        .downcast::<Editor>()
5179        .unwrap();
5180
5181    // Client B starts following client A.
5182    let workspace_b = client_b.build_workspace(&project_b, cx_b);
5183    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
5184    let leader_id = project_b.read_with(cx_b, |project, _| {
5185        project.collaborators().values().next().unwrap().peer_id
5186    });
5187    workspace_b
5188        .update(cx_b, |workspace, cx| {
5189            workspace
5190                .toggle_follow(&ToggleFollow(leader_id), cx)
5191                .unwrap()
5192        })
5193        .await
5194        .unwrap();
5195    assert_eq!(
5196        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5197        Some(leader_id)
5198    );
5199    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
5200        workspace
5201            .active_item(cx)
5202            .unwrap()
5203            .downcast::<Editor>()
5204            .unwrap()
5205    });
5206
5207    // When client B moves, it automatically stops following client A.
5208    editor_b2.update(cx_b, |editor, cx| editor.move_right(&editor::MoveRight, cx));
5209    assert_eq!(
5210        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5211        None
5212    );
5213
5214    workspace_b
5215        .update(cx_b, |workspace, cx| {
5216            workspace
5217                .toggle_follow(&ToggleFollow(leader_id), cx)
5218                .unwrap()
5219        })
5220        .await
5221        .unwrap();
5222    assert_eq!(
5223        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5224        Some(leader_id)
5225    );
5226
5227    // When client B edits, it automatically stops following client A.
5228    editor_b2.update(cx_b, |editor, cx| editor.insert("X", cx));
5229    assert_eq!(
5230        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5231        None
5232    );
5233
5234    workspace_b
5235        .update(cx_b, |workspace, cx| {
5236            workspace
5237                .toggle_follow(&ToggleFollow(leader_id), cx)
5238                .unwrap()
5239        })
5240        .await
5241        .unwrap();
5242    assert_eq!(
5243        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5244        Some(leader_id)
5245    );
5246
5247    // When client B scrolls, it automatically stops following client A.
5248    editor_b2.update(cx_b, |editor, cx| {
5249        editor.set_scroll_position(vec2f(0., 3.), cx)
5250    });
5251    assert_eq!(
5252        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5253        None
5254    );
5255
5256    workspace_b
5257        .update(cx_b, |workspace, cx| {
5258            workspace
5259                .toggle_follow(&ToggleFollow(leader_id), cx)
5260                .unwrap()
5261        })
5262        .await
5263        .unwrap();
5264    assert_eq!(
5265        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5266        Some(leader_id)
5267    );
5268
5269    // When client B activates a different pane, it continues following client A in the original pane.
5270    workspace_b.update(cx_b, |workspace, cx| {
5271        workspace.split_pane(pane_b.clone(), SplitDirection::Right, cx)
5272    });
5273    assert_eq!(
5274        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5275        Some(leader_id)
5276    );
5277
5278    workspace_b.update(cx_b, |workspace, cx| workspace.activate_next_pane(cx));
5279    assert_eq!(
5280        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5281        Some(leader_id)
5282    );
5283
5284    // When client B activates a different item in the original pane, it automatically stops following client A.
5285    workspace_b
5286        .update(cx_b, |workspace, cx| {
5287            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
5288        })
5289        .await
5290        .unwrap();
5291    assert_eq!(
5292        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5293        None
5294    );
5295}
5296
5297#[gpui::test(iterations = 10)]
5298async fn test_peers_simultaneously_following_each_other(
5299    deterministic: Arc<Deterministic>,
5300    cx_a: &mut TestAppContext,
5301    cx_b: &mut TestAppContext,
5302) {
5303    deterministic.forbid_parking();
5304    cx_a.update(editor::init);
5305    cx_b.update(editor::init);
5306
5307    let mut server = TestServer::start(cx_a.background()).await;
5308    let client_a = server.create_client(cx_a, "user_a").await;
5309    let client_b = server.create_client(cx_b, "user_b").await;
5310    server
5311        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5312        .await;
5313    let active_call_a = cx_a.read(ActiveCall::global);
5314
5315    client_a.fs.insert_tree("/a", json!({})).await;
5316    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
5317    let workspace_a = client_a.build_workspace(&project_a, cx_a);
5318    let project_id = active_call_a
5319        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5320        .await
5321        .unwrap();
5322
5323    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5324    let workspace_b = client_b.build_workspace(&project_b, cx_b);
5325
5326    deterministic.run_until_parked();
5327    let client_a_id = project_b.read_with(cx_b, |project, _| {
5328        project.collaborators().values().next().unwrap().peer_id
5329    });
5330    let client_b_id = project_a.read_with(cx_a, |project, _| {
5331        project.collaborators().values().next().unwrap().peer_id
5332    });
5333
5334    let a_follow_b = workspace_a.update(cx_a, |workspace, cx| {
5335        workspace
5336            .toggle_follow(&ToggleFollow(client_b_id), cx)
5337            .unwrap()
5338    });
5339    let b_follow_a = workspace_b.update(cx_b, |workspace, cx| {
5340        workspace
5341            .toggle_follow(&ToggleFollow(client_a_id), cx)
5342            .unwrap()
5343    });
5344
5345    futures::try_join!(a_follow_b, b_follow_a).unwrap();
5346    workspace_a.read_with(cx_a, |workspace, _| {
5347        assert_eq!(
5348            workspace.leader_for_pane(workspace.active_pane()),
5349            Some(client_b_id)
5350        );
5351    });
5352    workspace_b.read_with(cx_b, |workspace, _| {
5353        assert_eq!(
5354            workspace.leader_for_pane(workspace.active_pane()),
5355            Some(client_a_id)
5356        );
5357    });
5358}
5359
5360#[gpui::test(iterations = 100)]
5361async fn test_random_collaboration(
5362    cx: &mut TestAppContext,
5363    deterministic: Arc<Deterministic>,
5364    rng: StdRng,
5365) {
5366    deterministic.forbid_parking();
5367    let rng = Arc::new(Mutex::new(rng));
5368
5369    let max_peers = env::var("MAX_PEERS")
5370        .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
5371        .unwrap_or(5);
5372
5373    let max_operations = env::var("OPERATIONS")
5374        .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
5375        .unwrap_or(10);
5376
5377    let mut server = TestServer::start(cx.background()).await;
5378    let db = server.app_state.db.clone();
5379
5380    let mut available_guests = Vec::new();
5381    for ix in 0..max_peers {
5382        let username = format!("guest-{}", ix + 1);
5383        let user_id = db
5384            .create_user(
5385                &format!("{username}@example.com"),
5386                false,
5387                NewUserParams {
5388                    github_login: username.clone(),
5389                    github_user_id: (ix + 1) as i32,
5390                    invite_count: 0,
5391                },
5392            )
5393            .await
5394            .unwrap()
5395            .user_id;
5396        available_guests.push((user_id, username));
5397    }
5398
5399    for (ix, (user_id_a, _)) in available_guests.iter().enumerate() {
5400        for (user_id_b, _) in &available_guests[ix + 1..] {
5401            server
5402                .app_state
5403                .db
5404                .send_contact_request(*user_id_a, *user_id_b)
5405                .await
5406                .unwrap();
5407            server
5408                .app_state
5409                .db
5410                .respond_to_contact_request(*user_id_b, *user_id_a, true)
5411                .await
5412                .unwrap();
5413        }
5414    }
5415
5416    let mut clients = Vec::new();
5417    let mut user_ids = Vec::new();
5418    let mut peer_ids = Vec::new();
5419    let mut op_start_signals = Vec::new();
5420    let mut next_entity_id = 100000;
5421
5422    let mut operations = 0;
5423    while operations < max_operations {
5424        let distribution = rng.lock().gen_range(0..100);
5425        match distribution {
5426            0..=19 if !available_guests.is_empty() => {
5427                let guest_ix = rng.lock().gen_range(0..available_guests.len());
5428                let (_, guest_username) = available_guests.remove(guest_ix);
5429                log::info!("Adding new connection for {}", guest_username);
5430                next_entity_id += 100000;
5431                let mut guest_cx = TestAppContext::new(
5432                    cx.foreground_platform(),
5433                    cx.platform(),
5434                    deterministic.build_foreground(next_entity_id),
5435                    deterministic.build_background(),
5436                    cx.font_cache(),
5437                    cx.leak_detector(),
5438                    next_entity_id,
5439                    cx.function_name.clone(),
5440                );
5441
5442                let op_start_signal = futures::channel::mpsc::unbounded();
5443                let guest = server.create_client(&mut guest_cx, &guest_username).await;
5444                user_ids.push(guest.current_user_id(&guest_cx));
5445                peer_ids.push(guest.peer_id().unwrap());
5446                op_start_signals.push(op_start_signal.0);
5447                clients.push(guest_cx.foreground().spawn(guest.simulate(
5448                    guest_username.clone(),
5449                    op_start_signal.1,
5450                    rng.clone(),
5451                    guest_cx,
5452                )));
5453
5454                log::info!("Added connection for {}", guest_username);
5455                operations += 1;
5456            }
5457            20..=29 if clients.len() > 1 => {
5458                let guest_ix = rng.lock().gen_range(1..clients.len());
5459                log::info!("Removing guest {}", user_ids[guest_ix]);
5460                let removed_guest_id = user_ids.remove(guest_ix);
5461                let removed_peer_id = peer_ids.remove(guest_ix);
5462                let guest = clients.remove(guest_ix);
5463                op_start_signals.remove(guest_ix);
5464                server.forbid_connections();
5465                server.disconnect_client(removed_peer_id);
5466                deterministic.advance_clock(RECEIVE_TIMEOUT);
5467                deterministic.start_waiting();
5468                log::info!("Waiting for guest {} to exit...", removed_guest_id);
5469                let (guest, mut guest_cx) = guest.await;
5470                deterministic.finish_waiting();
5471                server.allow_connections();
5472
5473                for project in &guest.remote_projects {
5474                    project.read_with(&guest_cx, |project, _| assert!(project.is_read_only()));
5475                }
5476                for user_id in &user_ids {
5477                    let contacts = server.app_state.db.get_contacts(*user_id).await.unwrap();
5478                    let contacts = server
5479                        .store
5480                        .lock()
5481                        .await
5482                        .build_initial_contacts_update(contacts)
5483                        .contacts;
5484                    for contact in contacts {
5485                        if contact.online {
5486                            assert_ne!(
5487                                contact.user_id, removed_guest_id.0 as u64,
5488                                "removed guest is still a contact of another peer"
5489                            );
5490                        }
5491                    }
5492                }
5493
5494                log::info!("{} removed", guest.username);
5495                available_guests.push((removed_guest_id, guest.username.clone()));
5496                guest_cx.update(|cx| {
5497                    cx.clear_globals();
5498                    drop(guest);
5499                });
5500
5501                operations += 1;
5502            }
5503            _ if !op_start_signals.is_empty() => {
5504                while operations < max_operations && rng.lock().gen_bool(0.7) {
5505                    op_start_signals
5506                        .choose(&mut *rng.lock())
5507                        .unwrap()
5508                        .unbounded_send(())
5509                        .unwrap();
5510                    operations += 1;
5511                }
5512
5513                if rng.lock().gen_bool(0.8) {
5514                    deterministic.run_until_parked();
5515                }
5516            }
5517            _ => {}
5518        }
5519    }
5520
5521    drop(op_start_signals);
5522    deterministic.start_waiting();
5523    let clients = futures::future::join_all(clients).await;
5524    deterministic.finish_waiting();
5525    deterministic.run_until_parked();
5526
5527    for (guest_client, guest_cx) in &clients {
5528        for guest_project in &guest_client.remote_projects {
5529            guest_project.read_with(guest_cx, |guest_project, cx| {
5530                let host_project = clients.iter().find_map(|(client, cx)| {
5531                    let project = client.local_projects.iter().find(|host_project| {
5532                        host_project.read_with(cx, |host_project, _| {
5533                            host_project.remote_id() == guest_project.remote_id()
5534                        })
5535                    })?;
5536                    Some((project, cx))
5537                });
5538
5539                if !guest_project.is_read_only() {
5540                    if let Some((host_project, host_cx)) = host_project {
5541                        let host_worktree_snapshots =
5542                            host_project.read_with(host_cx, |host_project, cx| {
5543                                host_project
5544                                    .worktrees(cx)
5545                                    .map(|worktree| {
5546                                        let worktree = worktree.read(cx);
5547                                        (worktree.id(), worktree.snapshot())
5548                                    })
5549                                    .collect::<BTreeMap<_, _>>()
5550                            });
5551                        let guest_worktree_snapshots = guest_project
5552                            .worktrees(cx)
5553                            .map(|worktree| {
5554                                let worktree = worktree.read(cx);
5555                                (worktree.id(), worktree.snapshot())
5556                            })
5557                            .collect::<BTreeMap<_, _>>();
5558
5559                        assert_eq!(
5560                            guest_worktree_snapshots.keys().collect::<Vec<_>>(),
5561                            host_worktree_snapshots.keys().collect::<Vec<_>>(),
5562                            "{} has different worktrees than the host",
5563                            guest_client.username
5564                        );
5565
5566                        for (id, host_snapshot) in &host_worktree_snapshots {
5567                            let guest_snapshot = &guest_worktree_snapshots[id];
5568                            assert_eq!(
5569                                guest_snapshot.root_name(),
5570                                host_snapshot.root_name(),
5571                                "{} has different root name than the host for worktree {}",
5572                                guest_client.username,
5573                                id
5574                            );
5575                            assert_eq!(
5576                                guest_snapshot.entries(false).collect::<Vec<_>>(),
5577                                host_snapshot.entries(false).collect::<Vec<_>>(),
5578                                "{} has different snapshot than the host for worktree {}",
5579                                guest_client.username,
5580                                id
5581                            );
5582                            assert_eq!(guest_snapshot.scan_id(), host_snapshot.scan_id());
5583                        }
5584                    }
5585                }
5586
5587                guest_project.check_invariants(cx);
5588            });
5589        }
5590
5591        for (guest_project, guest_buffers) in &guest_client.buffers {
5592            let project_id = if guest_project.read_with(guest_cx, |project, _| {
5593                project.is_local() || project.is_read_only()
5594            }) {
5595                continue;
5596            } else {
5597                guest_project
5598                    .read_with(guest_cx, |project, _| project.remote_id())
5599                    .unwrap()
5600            };
5601
5602            let host_project = clients.iter().find_map(|(client, cx)| {
5603                let project = client.local_projects.iter().find(|host_project| {
5604                    host_project.read_with(cx, |host_project, _| {
5605                        host_project.remote_id() == Some(project_id)
5606                    })
5607                })?;
5608                Some((project, cx))
5609            });
5610
5611            let (host_project, host_cx) = if let Some((host_project, host_cx)) = host_project {
5612                (host_project, host_cx)
5613            } else {
5614                continue;
5615            };
5616
5617            for guest_buffer in guest_buffers {
5618                let buffer_id = guest_buffer.read_with(guest_cx, |buffer, _| buffer.remote_id());
5619                let host_buffer = host_project.read_with(host_cx, |project, cx| {
5620                    project.buffer_for_id(buffer_id, cx).unwrap_or_else(|| {
5621                        panic!(
5622                            "host does not have buffer for guest:{}, peer:{:?}, id:{}",
5623                            guest_client.username,
5624                            guest_client.peer_id(),
5625                            buffer_id
5626                        )
5627                    })
5628                });
5629                let path = host_buffer
5630                    .read_with(host_cx, |buffer, cx| buffer.file().unwrap().full_path(cx));
5631
5632                assert_eq!(
5633                    guest_buffer.read_with(guest_cx, |buffer, _| buffer.deferred_ops_len()),
5634                    0,
5635                    "{}, buffer {}, path {:?} has deferred operations",
5636                    guest_client.username,
5637                    buffer_id,
5638                    path,
5639                );
5640                assert_eq!(
5641                    guest_buffer.read_with(guest_cx, |buffer, _| buffer.text()),
5642                    host_buffer.read_with(host_cx, |buffer, _| buffer.text()),
5643                    "{}, buffer {}, path {:?}, differs from the host's buffer",
5644                    guest_client.username,
5645                    buffer_id,
5646                    path
5647                );
5648            }
5649        }
5650    }
5651
5652    for (client, mut cx) in clients {
5653        cx.update(|cx| {
5654            cx.clear_globals();
5655            drop(client);
5656        });
5657    }
5658}
5659
5660struct TestServer {
5661    peer: Arc<Peer>,
5662    app_state: Arc<AppState>,
5663    server: Arc<Server>,
5664    connection_killers: Arc<Mutex<HashMap<PeerId, Arc<AtomicBool>>>>,
5665    forbid_connections: Arc<AtomicBool>,
5666    _test_db: TestDb,
5667    test_live_kit_server: Arc<live_kit_client::TestServer>,
5668}
5669
5670impl TestServer {
5671    async fn start(background: Arc<executor::Background>) -> Self {
5672        static NEXT_LIVE_KIT_SERVER_ID: AtomicUsize = AtomicUsize::new(0);
5673
5674        let test_db = TestDb::new(background.clone());
5675        let live_kit_server_id = NEXT_LIVE_KIT_SERVER_ID.fetch_add(1, SeqCst);
5676        let live_kit_server = live_kit_client::TestServer::create(
5677            format!("http://livekit.{}.test", live_kit_server_id),
5678            format!("devkey-{}", live_kit_server_id),
5679            format!("secret-{}", live_kit_server_id),
5680            background.clone(),
5681        )
5682        .unwrap();
5683        let app_state = Self::build_app_state(&test_db, &live_kit_server).await;
5684        let peer = Peer::new();
5685        let server = Server::new(app_state.clone());
5686        Self {
5687            peer,
5688            app_state,
5689            server,
5690            connection_killers: Default::default(),
5691            forbid_connections: Default::default(),
5692            _test_db: test_db,
5693            test_live_kit_server: live_kit_server,
5694        }
5695    }
5696
5697    async fn create_client(&mut self, cx: &mut TestAppContext, name: &str) -> TestClient {
5698        cx.update(|cx| {
5699            cx.set_global(HomeDir(Path::new("/tmp/").to_path_buf()));
5700
5701            let mut settings = Settings::test(cx);
5702            settings.projects_online_by_default = false;
5703            cx.set_global(settings);
5704        });
5705
5706        let http = FakeHttpClient::with_404_response();
5707        let user_id = if let Ok(Some(user)) = self
5708            .app_state
5709            .db
5710            .get_user_by_github_account(name, None)
5711            .await
5712        {
5713            user.id
5714        } else {
5715            self.app_state
5716                .db
5717                .create_user(
5718                    &format!("{name}@example.com"),
5719                    false,
5720                    NewUserParams {
5721                        github_login: name.into(),
5722                        github_user_id: 0,
5723                        invite_count: 0,
5724                    },
5725                )
5726                .await
5727                .expect("creating user failed")
5728                .user_id
5729        };
5730        let client_name = name.to_string();
5731        let mut client = cx.read(|cx| Client::new(http.clone(), cx));
5732        let server = self.server.clone();
5733        let db = self.app_state.db.clone();
5734        let connection_killers = self.connection_killers.clone();
5735        let forbid_connections = self.forbid_connections.clone();
5736
5737        Arc::get_mut(&mut client)
5738            .unwrap()
5739            .set_id(user_id.0 as usize)
5740            .override_authenticate(move |cx| {
5741                cx.spawn(|_| async move {
5742                    let access_token = "the-token".to_string();
5743                    Ok(Credentials {
5744                        user_id: user_id.0 as u64,
5745                        access_token,
5746                    })
5747                })
5748            })
5749            .override_establish_connection(move |credentials, cx| {
5750                assert_eq!(credentials.user_id, user_id.0 as u64);
5751                assert_eq!(credentials.access_token, "the-token");
5752
5753                let server = server.clone();
5754                let db = db.clone();
5755                let connection_killers = connection_killers.clone();
5756                let forbid_connections = forbid_connections.clone();
5757                let client_name = client_name.clone();
5758                cx.spawn(move |cx| async move {
5759                    if forbid_connections.load(SeqCst) {
5760                        Err(EstablishConnectionError::other(anyhow!(
5761                            "server is forbidding connections"
5762                        )))
5763                    } else {
5764                        let (client_conn, server_conn, killed) =
5765                            Connection::in_memory(cx.background());
5766                        let (connection_id_tx, connection_id_rx) = oneshot::channel();
5767                        let user = db
5768                            .get_user_by_id(user_id)
5769                            .await
5770                            .expect("retrieving user failed")
5771                            .unwrap();
5772                        cx.background()
5773                            .spawn(server.handle_connection(
5774                                server_conn,
5775                                client_name,
5776                                user,
5777                                Some(connection_id_tx),
5778                                cx.background(),
5779                            ))
5780                            .detach();
5781                        let connection_id = connection_id_rx.await.unwrap();
5782                        connection_killers
5783                            .lock()
5784                            .insert(PeerId(connection_id.0), killed);
5785                        Ok(client_conn)
5786                    }
5787                })
5788            });
5789
5790        let fs = FakeFs::new(cx.background());
5791        let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx));
5792        let app_state = Arc::new(workspace::AppState {
5793            client: client.clone(),
5794            user_store: user_store.clone(),
5795            languages: Arc::new(LanguageRegistry::new(Task::ready(()))),
5796            themes: ThemeRegistry::new((), cx.font_cache()),
5797            fs: fs.clone(),
5798            build_window_options: Default::default,
5799            initialize_workspace: |_, _, _| unimplemented!(),
5800            default_item_factory: |_, _| unimplemented!(),
5801        });
5802
5803        Project::init(&client);
5804        cx.update(|cx| {
5805            workspace::init(app_state.clone(), cx);
5806            call::init(client.clone(), user_store.clone(), cx);
5807        });
5808
5809        client
5810            .authenticate_and_connect(false, &cx.to_async())
5811            .await
5812            .unwrap();
5813
5814        let client = TestClient {
5815            client,
5816            username: name.to_string(),
5817            local_projects: Default::default(),
5818            remote_projects: Default::default(),
5819            next_root_dir_id: 0,
5820            user_store,
5821            fs,
5822            language_registry: Arc::new(LanguageRegistry::test()),
5823            buffers: Default::default(),
5824        };
5825        client.wait_for_current_user(cx).await;
5826        client
5827    }
5828
5829    fn disconnect_client(&self, peer_id: PeerId) {
5830        self.connection_killers
5831            .lock()
5832            .remove(&peer_id)
5833            .unwrap()
5834            .store(true, SeqCst);
5835    }
5836
5837    fn forbid_connections(&self) {
5838        self.forbid_connections.store(true, SeqCst);
5839    }
5840
5841    fn allow_connections(&self) {
5842        self.forbid_connections.store(false, SeqCst);
5843    }
5844
5845    async fn make_contacts(&self, clients: &mut [(&TestClient, &mut TestAppContext)]) {
5846        for ix in 1..clients.len() {
5847            let (left, right) = clients.split_at_mut(ix);
5848            let (client_a, cx_a) = left.last_mut().unwrap();
5849            for (client_b, cx_b) in right {
5850                client_a
5851                    .user_store
5852                    .update(*cx_a, |store, cx| {
5853                        store.request_contact(client_b.user_id().unwrap(), cx)
5854                    })
5855                    .await
5856                    .unwrap();
5857                cx_a.foreground().run_until_parked();
5858                client_b
5859                    .user_store
5860                    .update(*cx_b, |store, cx| {
5861                        store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
5862                    })
5863                    .await
5864                    .unwrap();
5865            }
5866        }
5867    }
5868
5869    async fn create_room(&self, clients: &mut [(&TestClient, &mut TestAppContext)]) {
5870        self.make_contacts(clients).await;
5871
5872        let (left, right) = clients.split_at_mut(1);
5873        let (_client_a, cx_a) = &mut left[0];
5874        let active_call_a = cx_a.read(ActiveCall::global);
5875
5876        for (client_b, cx_b) in right {
5877            let user_id_b = client_b.current_user_id(*cx_b).to_proto();
5878            active_call_a
5879                .update(*cx_a, |call, cx| call.invite(user_id_b, None, cx))
5880                .await
5881                .unwrap();
5882
5883            cx_b.foreground().run_until_parked();
5884            let active_call_b = cx_b.read(ActiveCall::global);
5885            active_call_b
5886                .update(*cx_b, |call, cx| call.accept_incoming(cx))
5887                .await
5888                .unwrap();
5889        }
5890    }
5891
5892    async fn build_app_state(
5893        test_db: &TestDb,
5894        fake_server: &live_kit_client::TestServer,
5895    ) -> Arc<AppState> {
5896        Arc::new(AppState {
5897            db: test_db.db().clone(),
5898            live_kit_client: Some(Arc::new(fake_server.create_api_client())),
5899            config: Default::default(),
5900        })
5901    }
5902}
5903
5904impl Deref for TestServer {
5905    type Target = Server;
5906
5907    fn deref(&self) -> &Self::Target {
5908        &self.server
5909    }
5910}
5911
5912impl Drop for TestServer {
5913    fn drop(&mut self) {
5914        self.peer.reset();
5915        self.test_live_kit_server.teardown().unwrap();
5916    }
5917}
5918
5919struct TestClient {
5920    client: Arc<Client>,
5921    username: String,
5922    local_projects: Vec<ModelHandle<Project>>,
5923    remote_projects: Vec<ModelHandle<Project>>,
5924    next_root_dir_id: usize,
5925    pub user_store: ModelHandle<UserStore>,
5926    language_registry: Arc<LanguageRegistry>,
5927    fs: Arc<FakeFs>,
5928    buffers: HashMap<ModelHandle<Project>, HashSet<ModelHandle<language::Buffer>>>,
5929}
5930
5931impl Deref for TestClient {
5932    type Target = Arc<Client>;
5933
5934    fn deref(&self) -> &Self::Target {
5935        &self.client
5936    }
5937}
5938
5939struct ContactsSummary {
5940    pub current: Vec<String>,
5941    pub outgoing_requests: Vec<String>,
5942    pub incoming_requests: Vec<String>,
5943}
5944
5945impl TestClient {
5946    pub fn current_user_id(&self, cx: &TestAppContext) -> UserId {
5947        UserId::from_proto(
5948            self.user_store
5949                .read_with(cx, |user_store, _| user_store.current_user().unwrap().id),
5950        )
5951    }
5952
5953    async fn wait_for_current_user(&self, cx: &TestAppContext) {
5954        let mut authed_user = self
5955            .user_store
5956            .read_with(cx, |user_store, _| user_store.watch_current_user());
5957        while authed_user.next().await.unwrap().is_none() {}
5958    }
5959
5960    async fn clear_contacts(&self, cx: &mut TestAppContext) {
5961        self.user_store
5962            .update(cx, |store, _| store.clear_contacts())
5963            .await;
5964    }
5965
5966    fn summarize_contacts(&self, cx: &TestAppContext) -> ContactsSummary {
5967        self.user_store.read_with(cx, |store, _| ContactsSummary {
5968            current: store
5969                .contacts()
5970                .iter()
5971                .map(|contact| contact.user.github_login.clone())
5972                .collect(),
5973            outgoing_requests: store
5974                .outgoing_contact_requests()
5975                .iter()
5976                .map(|user| user.github_login.clone())
5977                .collect(),
5978            incoming_requests: store
5979                .incoming_contact_requests()
5980                .iter()
5981                .map(|user| user.github_login.clone())
5982                .collect(),
5983        })
5984    }
5985
5986    async fn build_local_project(
5987        &self,
5988        root_path: impl AsRef<Path>,
5989        cx: &mut TestAppContext,
5990    ) -> (ModelHandle<Project>, WorktreeId) {
5991        let project = cx.update(|cx| {
5992            Project::local(
5993                self.client.clone(),
5994                self.user_store.clone(),
5995                self.language_registry.clone(),
5996                self.fs.clone(),
5997                cx,
5998            )
5999        });
6000        let (worktree, _) = project
6001            .update(cx, |p, cx| {
6002                p.find_or_create_local_worktree(root_path, true, cx)
6003            })
6004            .await
6005            .unwrap();
6006        worktree
6007            .read_with(cx, |tree, _| tree.as_local().unwrap().scan_complete())
6008            .await;
6009        (project, worktree.read_with(cx, |tree, _| tree.id()))
6010    }
6011
6012    async fn build_remote_project(
6013        &self,
6014        host_project_id: u64,
6015        guest_cx: &mut TestAppContext,
6016    ) -> ModelHandle<Project> {
6017        let project_b = guest_cx.spawn(|cx| {
6018            Project::remote(
6019                host_project_id,
6020                self.client.clone(),
6021                self.user_store.clone(),
6022                self.language_registry.clone(),
6023                FakeFs::new(cx.background()),
6024                cx,
6025            )
6026        });
6027        project_b.await.unwrap()
6028    }
6029
6030    fn build_workspace(
6031        &self,
6032        project: &ModelHandle<Project>,
6033        cx: &mut TestAppContext,
6034    ) -> ViewHandle<Workspace> {
6035        let (_, root_view) = cx.add_window(|_| EmptyView);
6036        cx.add_view(&root_view, |cx| {
6037            Workspace::new(project.clone(), |_, _| unimplemented!(), cx)
6038        })
6039    }
6040
6041    pub async fn simulate(
6042        mut self,
6043        username: String,
6044        mut op_start_signal: futures::channel::mpsc::UnboundedReceiver<()>,
6045        rng: Arc<Mutex<StdRng>>,
6046        mut cx: TestAppContext,
6047    ) -> (Self, TestAppContext) {
6048        async fn tick(
6049            client: &mut TestClient,
6050            username: &str,
6051            rng: Arc<Mutex<StdRng>>,
6052            cx: &mut TestAppContext,
6053        ) -> anyhow::Result<()> {
6054            let active_call = cx.read(ActiveCall::global);
6055            if active_call.read_with(cx, |call, _| call.incoming().borrow().is_some()) {
6056                if rng.lock().gen() {
6057                    log::info!("{}: accepting incoming call", username);
6058                    active_call
6059                        .update(cx, |call, cx| call.accept_incoming(cx))
6060                        .await?;
6061                } else {
6062                    log::info!("{}: declining incoming call", username);
6063                    active_call.update(cx, |call, _| call.decline_incoming())?;
6064                }
6065            } else {
6066                let available_contacts = client.user_store.read_with(cx, |user_store, _| {
6067                    user_store
6068                        .contacts()
6069                        .iter()
6070                        .filter(|contact| contact.online && !contact.busy)
6071                        .cloned()
6072                        .collect::<Vec<_>>()
6073                });
6074
6075                let distribution = rng.lock().gen_range(0..100);
6076                match distribution {
6077                    0..=29 if !available_contacts.is_empty() => {
6078                        let contact = available_contacts.choose(&mut *rng.lock()).unwrap();
6079                        log::info!("{}: inviting {}", username, contact.user.github_login);
6080                        active_call
6081                            .update(cx, |call, cx| call.invite(contact.user.id, None, cx))
6082                            .await?;
6083                    }
6084                    30..=39 if active_call.read_with(cx, |call, _| call.room().is_some()) => {
6085                        log::info!("{}: hanging up", username);
6086                        active_call.update(cx, |call, cx| call.hang_up(cx))?;
6087                    }
6088                    _ => {}
6089                }
6090            }
6091
6092            let remote_projects =
6093                if let Some(room) = active_call.read_with(cx, |call, _| call.room().cloned()) {
6094                    room.read_with(cx, |room, _| {
6095                        room.remote_participants()
6096                            .values()
6097                            .flat_map(|participant| participant.projects.clone())
6098                            .collect::<Vec<_>>()
6099                    })
6100                } else {
6101                    Default::default()
6102                };
6103            let project = if remote_projects.is_empty() || rng.lock().gen() {
6104                if client.local_projects.is_empty() || rng.lock().gen() {
6105                    let dir_paths = client.fs.directories().await;
6106                    let local_project = if dir_paths.is_empty() || rng.lock().gen() {
6107                        let root_path = format!(
6108                            "/{}-root-{}",
6109                            username,
6110                            post_inc(&mut client.next_root_dir_id)
6111                        );
6112                        let root_path = Path::new(&root_path);
6113                        client.fs.create_dir(root_path).await.unwrap();
6114                        client
6115                            .fs
6116                            .create_file(&root_path.join("main.rs"), Default::default())
6117                            .await
6118                            .unwrap();
6119                        log::info!("{}: opening local project at {:?}", username, root_path);
6120                        client.build_local_project(root_path, cx).await.0
6121                    } else {
6122                        let root_path = dir_paths.choose(&mut *rng.lock()).unwrap();
6123                        log::info!("{}: opening local project at {:?}", username, root_path);
6124                        client.build_local_project(root_path, cx).await.0
6125                    };
6126                    client.local_projects.push(local_project.clone());
6127                    local_project
6128                } else {
6129                    client
6130                        .local_projects
6131                        .choose(&mut *rng.lock())
6132                        .unwrap()
6133                        .clone()
6134                }
6135            } else {
6136                if client.remote_projects.is_empty() || rng.lock().gen() {
6137                    let remote_project_id = remote_projects.choose(&mut *rng.lock()).unwrap().id;
6138                    let remote_project = if let Some(project) =
6139                        client.remote_projects.iter().find(|project| {
6140                            project.read_with(cx, |project, _| {
6141                                project.remote_id() == Some(remote_project_id)
6142                            })
6143                        }) {
6144                        project.clone()
6145                    } else {
6146                        log::info!("{}: opening remote project {}", username, remote_project_id);
6147                        let remote_project = Project::remote(
6148                            remote_project_id,
6149                            client.client.clone(),
6150                            client.user_store.clone(),
6151                            client.language_registry.clone(),
6152                            FakeFs::new(cx.background()),
6153                            cx.to_async(),
6154                        )
6155                        .await?;
6156                        client.remote_projects.push(remote_project.clone());
6157                        remote_project
6158                    };
6159
6160                    remote_project
6161                } else {
6162                    client
6163                        .remote_projects
6164                        .choose(&mut *rng.lock())
6165                        .unwrap()
6166                        .clone()
6167                }
6168            };
6169            if let Err(error) = active_call
6170                .update(cx, |call, cx| call.share_project(project.clone(), cx))
6171                .await
6172            {
6173                log::error!("{}: error sharing project, {:?}", username, error);
6174            }
6175
6176            let buffers = client.buffers.entry(project.clone()).or_default();
6177            let buffer = if buffers.is_empty() || rng.lock().gen() {
6178                let worktree = if let Some(worktree) = project.read_with(cx, |project, cx| {
6179                    project
6180                        .worktrees(cx)
6181                        .filter(|worktree| {
6182                            let worktree = worktree.read(cx);
6183                            worktree.is_visible() && worktree.entries(false).any(|e| e.is_file())
6184                        })
6185                        .choose(&mut *rng.lock())
6186                }) {
6187                    worktree
6188                } else {
6189                    cx.background().simulate_random_delay().await;
6190                    return Ok(());
6191                };
6192
6193                let (worktree_root_name, project_path) = worktree.read_with(cx, |worktree, _| {
6194                    let entry = worktree
6195                        .entries(false)
6196                        .filter(|e| e.is_file())
6197                        .choose(&mut *rng.lock())
6198                        .unwrap();
6199                    (
6200                        worktree.root_name().to_string(),
6201                        (worktree.id(), entry.path.clone()),
6202                    )
6203                });
6204                log::info!(
6205                    "{}: opening path {:?} in worktree {} ({})",
6206                    username,
6207                    project_path.1,
6208                    project_path.0,
6209                    worktree_root_name,
6210                );
6211                let buffer = project
6212                    .update(cx, |project, cx| {
6213                        project.open_buffer(project_path.clone(), cx)
6214                    })
6215                    .await?;
6216                log::info!(
6217                    "{}: opened path {:?} in worktree {} ({}) with buffer id {}",
6218                    username,
6219                    project_path.1,
6220                    project_path.0,
6221                    worktree_root_name,
6222                    buffer.read_with(cx, |buffer, _| buffer.remote_id())
6223                );
6224                buffers.insert(buffer.clone());
6225                buffer
6226            } else {
6227                buffers.iter().choose(&mut *rng.lock()).unwrap().clone()
6228            };
6229
6230            let choice = rng.lock().gen_range(0..100);
6231            match choice {
6232                0..=9 => {
6233                    cx.update(|cx| {
6234                        log::info!(
6235                            "{}: dropping buffer {:?}",
6236                            username,
6237                            buffer.read(cx).file().unwrap().full_path(cx)
6238                        );
6239                        buffers.remove(&buffer);
6240                        drop(buffer);
6241                    });
6242                }
6243                10..=19 => {
6244                    let completions = project.update(cx, |project, cx| {
6245                        log::info!(
6246                            "{}: requesting completions for buffer {} ({:?})",
6247                            username,
6248                            buffer.read(cx).remote_id(),
6249                            buffer.read(cx).file().unwrap().full_path(cx)
6250                        );
6251                        let offset = rng.lock().gen_range(0..=buffer.read(cx).len());
6252                        project.completions(&buffer, offset, cx)
6253                    });
6254                    let completions = cx.background().spawn(async move {
6255                        completions
6256                            .await
6257                            .map_err(|err| anyhow!("completions request failed: {:?}", err))
6258                    });
6259                    if rng.lock().gen_bool(0.3) {
6260                        log::info!("{}: detaching completions request", username);
6261                        cx.update(|cx| completions.detach_and_log_err(cx));
6262                    } else {
6263                        completions.await?;
6264                    }
6265                }
6266                20..=29 => {
6267                    let code_actions = project.update(cx, |project, cx| {
6268                        log::info!(
6269                            "{}: requesting code actions for buffer {} ({:?})",
6270                            username,
6271                            buffer.read(cx).remote_id(),
6272                            buffer.read(cx).file().unwrap().full_path(cx)
6273                        );
6274                        let range = buffer.read(cx).random_byte_range(0, &mut *rng.lock());
6275                        project.code_actions(&buffer, range, cx)
6276                    });
6277                    let code_actions = cx.background().spawn(async move {
6278                        code_actions
6279                            .await
6280                            .map_err(|err| anyhow!("code actions request failed: {:?}", err))
6281                    });
6282                    if rng.lock().gen_bool(0.3) {
6283                        log::info!("{}: detaching code actions request", username);
6284                        cx.update(|cx| code_actions.detach_and_log_err(cx));
6285                    } else {
6286                        code_actions.await?;
6287                    }
6288                }
6289                30..=39 if buffer.read_with(cx, |buffer, _| buffer.is_dirty()) => {
6290                    let (requested_version, save) = buffer.update(cx, |buffer, cx| {
6291                        log::info!(
6292                            "{}: saving buffer {} ({:?})",
6293                            username,
6294                            buffer.remote_id(),
6295                            buffer.file().unwrap().full_path(cx)
6296                        );
6297                        (buffer.version(), buffer.save(cx))
6298                    });
6299                    let save = cx.background().spawn(async move {
6300                        let (saved_version, _, _) = save
6301                            .await
6302                            .map_err(|err| anyhow!("save request failed: {:?}", err))?;
6303                        assert!(saved_version.observed_all(&requested_version));
6304                        Ok::<_, anyhow::Error>(())
6305                    });
6306                    if rng.lock().gen_bool(0.3) {
6307                        log::info!("{}: detaching save request", username);
6308                        cx.update(|cx| save.detach_and_log_err(cx));
6309                    } else {
6310                        save.await?;
6311                    }
6312                }
6313                40..=44 => {
6314                    let prepare_rename = project.update(cx, |project, cx| {
6315                        log::info!(
6316                            "{}: preparing rename for buffer {} ({:?})",
6317                            username,
6318                            buffer.read(cx).remote_id(),
6319                            buffer.read(cx).file().unwrap().full_path(cx)
6320                        );
6321                        let offset = rng.lock().gen_range(0..=buffer.read(cx).len());
6322                        project.prepare_rename(buffer, offset, cx)
6323                    });
6324                    let prepare_rename = cx.background().spawn(async move {
6325                        prepare_rename
6326                            .await
6327                            .map_err(|err| anyhow!("prepare rename request failed: {:?}", err))
6328                    });
6329                    if rng.lock().gen_bool(0.3) {
6330                        log::info!("{}: detaching prepare rename request", username);
6331                        cx.update(|cx| prepare_rename.detach_and_log_err(cx));
6332                    } else {
6333                        prepare_rename.await?;
6334                    }
6335                }
6336                45..=49 => {
6337                    let definitions = project.update(cx, |project, cx| {
6338                        log::info!(
6339                            "{}: requesting definitions for buffer {} ({:?})",
6340                            username,
6341                            buffer.read(cx).remote_id(),
6342                            buffer.read(cx).file().unwrap().full_path(cx)
6343                        );
6344                        let offset = rng.lock().gen_range(0..=buffer.read(cx).len());
6345                        project.definition(&buffer, offset, cx)
6346                    });
6347                    let definitions = cx.background().spawn(async move {
6348                        definitions
6349                            .await
6350                            .map_err(|err| anyhow!("definitions request failed: {:?}", err))
6351                    });
6352                    if rng.lock().gen_bool(0.3) {
6353                        log::info!("{}: detaching definitions request", username);
6354                        cx.update(|cx| definitions.detach_and_log_err(cx));
6355                    } else {
6356                        buffers.extend(definitions.await?.into_iter().map(|loc| loc.target.buffer));
6357                    }
6358                }
6359                50..=54 => {
6360                    let highlights = project.update(cx, |project, cx| {
6361                        log::info!(
6362                            "{}: requesting highlights for buffer {} ({:?})",
6363                            username,
6364                            buffer.read(cx).remote_id(),
6365                            buffer.read(cx).file().unwrap().full_path(cx)
6366                        );
6367                        let offset = rng.lock().gen_range(0..=buffer.read(cx).len());
6368                        project.document_highlights(&buffer, offset, cx)
6369                    });
6370                    let highlights = cx.background().spawn(async move {
6371                        highlights
6372                            .await
6373                            .map_err(|err| anyhow!("highlights request failed: {:?}", err))
6374                    });
6375                    if rng.lock().gen_bool(0.3) {
6376                        log::info!("{}: detaching highlights request", username);
6377                        cx.update(|cx| highlights.detach_and_log_err(cx));
6378                    } else {
6379                        highlights.await?;
6380                    }
6381                }
6382                55..=59 => {
6383                    let search = project.update(cx, |project, cx| {
6384                        let query = rng.lock().gen_range('a'..='z');
6385                        log::info!("{}: project-wide search {:?}", username, query);
6386                        project.search(SearchQuery::text(query, false, false), cx)
6387                    });
6388                    let search = cx.background().spawn(async move {
6389                        search
6390                            .await
6391                            .map_err(|err| anyhow!("search request failed: {:?}", err))
6392                    });
6393                    if rng.lock().gen_bool(0.3) {
6394                        log::info!("{}: detaching search request", username);
6395                        cx.update(|cx| search.detach_and_log_err(cx));
6396                    } else {
6397                        buffers.extend(search.await?.into_keys());
6398                    }
6399                }
6400                60..=69 => {
6401                    let worktree = project
6402                        .read_with(cx, |project, cx| {
6403                            project
6404                                .worktrees(cx)
6405                                .filter(|worktree| {
6406                                    let worktree = worktree.read(cx);
6407                                    worktree.is_visible()
6408                                        && worktree.entries(false).any(|e| e.is_file())
6409                                        && worktree.root_entry().map_or(false, |e| e.is_dir())
6410                                })
6411                                .choose(&mut *rng.lock())
6412                        })
6413                        .unwrap();
6414                    let (worktree_id, worktree_root_name) = worktree
6415                        .read_with(cx, |worktree, _| {
6416                            (worktree.id(), worktree.root_name().to_string())
6417                        });
6418
6419                    let mut new_name = String::new();
6420                    for _ in 0..10 {
6421                        let letter = rng.lock().gen_range('a'..='z');
6422                        new_name.push(letter);
6423                    }
6424
6425                    let is_dir = rng.lock().gen::<bool>();
6426                    let mut new_path = PathBuf::new();
6427                    new_path.push(new_name);
6428                    if !is_dir {
6429                        new_path.set_extension("rs");
6430                    }
6431                    log::info!(
6432                        "{}: creating {:?} in worktree {} ({})",
6433                        username,
6434                        new_path,
6435                        worktree_id,
6436                        worktree_root_name,
6437                    );
6438                    project
6439                        .update(cx, |project, cx| {
6440                            project.create_entry((worktree_id, new_path), is_dir, cx)
6441                        })
6442                        .unwrap()
6443                        .await?;
6444                }
6445                _ => {
6446                    buffer.update(cx, |buffer, cx| {
6447                        log::info!(
6448                            "{}: updating buffer {} ({:?})",
6449                            username,
6450                            buffer.remote_id(),
6451                            buffer.file().unwrap().full_path(cx)
6452                        );
6453                        if rng.lock().gen_bool(0.7) {
6454                            buffer.randomly_edit(&mut *rng.lock(), 5, cx);
6455                        } else {
6456                            buffer.randomly_undo_redo(&mut *rng.lock(), cx);
6457                        }
6458                    });
6459                }
6460            }
6461
6462            Ok(())
6463        }
6464
6465        // Setup language server
6466        let mut language = Language::new(
6467            LanguageConfig {
6468                name: "Rust".into(),
6469                path_suffixes: vec!["rs".to_string()],
6470                ..Default::default()
6471            },
6472            None,
6473        );
6474        let _fake_language_servers = language
6475            .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
6476                name: "the-fake-language-server",
6477                capabilities: lsp::LanguageServer::full_capabilities(),
6478                initializer: Some(Box::new({
6479                    let rng = rng.clone();
6480                    let fs = self.fs.clone();
6481                    move |fake_server: &mut FakeLanguageServer| {
6482                        fake_server.handle_request::<lsp::request::Completion, _, _>(
6483                            |_, _| async move {
6484                                Ok(Some(lsp::CompletionResponse::Array(vec![
6485                                    lsp::CompletionItem {
6486                                        text_edit: Some(lsp::CompletionTextEdit::Edit(
6487                                            lsp::TextEdit {
6488                                                range: lsp::Range::new(
6489                                                    lsp::Position::new(0, 0),
6490                                                    lsp::Position::new(0, 0),
6491                                                ),
6492                                                new_text: "the-new-text".to_string(),
6493                                            },
6494                                        )),
6495                                        ..Default::default()
6496                                    },
6497                                ])))
6498                            },
6499                        );
6500
6501                        fake_server.handle_request::<lsp::request::CodeActionRequest, _, _>(
6502                            |_, _| async move {
6503                                Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
6504                                    lsp::CodeAction {
6505                                        title: "the-code-action".to_string(),
6506                                        ..Default::default()
6507                                    },
6508                                )]))
6509                            },
6510                        );
6511
6512                        fake_server.handle_request::<lsp::request::PrepareRenameRequest, _, _>(
6513                            |params, _| async move {
6514                                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
6515                                    params.position,
6516                                    params.position,
6517                                ))))
6518                            },
6519                        );
6520
6521                        fake_server.handle_request::<lsp::request::GotoDefinition, _, _>({
6522                            let fs = fs.clone();
6523                            let rng = rng.clone();
6524                            move |_, _| {
6525                                let fs = fs.clone();
6526                                let rng = rng.clone();
6527                                async move {
6528                                    let files = fs.files().await;
6529                                    let mut rng = rng.lock();
6530                                    let count = rng.gen_range::<usize, _>(1..3);
6531                                    let files = (0..count)
6532                                        .map(|_| files.choose(&mut *rng).unwrap())
6533                                        .collect::<Vec<_>>();
6534                                    log::info!("LSP: Returning definitions in files {:?}", &files);
6535                                    Ok(Some(lsp::GotoDefinitionResponse::Array(
6536                                        files
6537                                            .into_iter()
6538                                            .map(|file| lsp::Location {
6539                                                uri: lsp::Url::from_file_path(file).unwrap(),
6540                                                range: Default::default(),
6541                                            })
6542                                            .collect(),
6543                                    )))
6544                                }
6545                            }
6546                        });
6547
6548                        fake_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
6549                            {
6550                                let rng = rng.clone();
6551                                move |_, _| {
6552                                    let mut highlights = Vec::new();
6553                                    let highlight_count = rng.lock().gen_range(1..=5);
6554                                    for _ in 0..highlight_count {
6555                                        let start_row = rng.lock().gen_range(0..100);
6556                                        let start_column = rng.lock().gen_range(0..100);
6557                                        let start = PointUtf16::new(start_row, start_column);
6558                                        let end_row = rng.lock().gen_range(0..100);
6559                                        let end_column = rng.lock().gen_range(0..100);
6560                                        let end = PointUtf16::new(end_row, end_column);
6561                                        let range =
6562                                            if start > end { end..start } else { start..end };
6563                                        highlights.push(lsp::DocumentHighlight {
6564                                            range: range_to_lsp(range.clone()),
6565                                            kind: Some(lsp::DocumentHighlightKind::READ),
6566                                        });
6567                                    }
6568                                    highlights.sort_unstable_by_key(|highlight| {
6569                                        (highlight.range.start, highlight.range.end)
6570                                    });
6571                                    async move { Ok(Some(highlights)) }
6572                                }
6573                            },
6574                        );
6575                    }
6576                })),
6577                ..Default::default()
6578            }))
6579            .await;
6580        self.language_registry.add(Arc::new(language));
6581
6582        while op_start_signal.next().await.is_some() {
6583            if let Err(error) = tick(&mut self, &username, rng.clone(), &mut cx).await {
6584                log::error!("{} error: {:?}", username, error);
6585            }
6586
6587            cx.background().simulate_random_delay().await;
6588        }
6589        log::info!("{}: done", username);
6590
6591        (self, cx)
6592    }
6593}
6594
6595impl Drop for TestClient {
6596    fn drop(&mut self) {
6597        self.client.tear_down();
6598    }
6599}
6600
6601impl Executor for Arc<gpui::executor::Background> {
6602    type Sleep = gpui::executor::Timer;
6603
6604    fn spawn_detached<F: 'static + Send + Future<Output = ()>>(&self, future: F) {
6605        self.spawn(future).detach();
6606    }
6607
6608    fn sleep(&self, duration: Duration) -> Self::Sleep {
6609        self.as_ref().timer(duration)
6610    }
6611}
6612
6613#[derive(Debug, Eq, PartialEq)]
6614struct RoomParticipants {
6615    remote: Vec<String>,
6616    pending: Vec<String>,
6617}
6618
6619fn room_participants(room: &ModelHandle<Room>, cx: &mut TestAppContext) -> RoomParticipants {
6620    room.read_with(cx, |room, _| RoomParticipants {
6621        remote: room
6622            .remote_participants()
6623            .iter()
6624            .map(|(_, participant)| participant.user.github_login.clone())
6625            .collect(),
6626        pending: room
6627            .pending_participants()
6628            .iter()
6629            .map(|user| user.github_login.clone())
6630            .collect(),
6631    })
6632}