integration_tests.rs

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