integration_tests.rs

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