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    deterministic.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    deterministic.run_until_parked();
4795    assert_eq!(
4796        workspace_b.read_with(cx_b, |workspace, cx| workspace
4797            .active_item(cx)
4798            .unwrap()
4799            .id()),
4800        editor_b1.id()
4801    );
4802
4803    // When client A navigates back and forth, client B does so as well.
4804    workspace_a
4805        .update(cx_a, |workspace, cx| {
4806            workspace::Pane::go_back(workspace, None, cx)
4807        })
4808        .await;
4809    deterministic.run_until_parked();
4810    assert_eq!(
4811        workspace_b.read_with(cx_b, |workspace, cx| workspace
4812            .active_item(cx)
4813            .unwrap()
4814            .id()),
4815        editor_b2.id()
4816    );
4817
4818    workspace_a
4819        .update(cx_a, |workspace, cx| {
4820            workspace::Pane::go_forward(workspace, None, cx)
4821        })
4822        .await;
4823    workspace_a
4824        .update(cx_a, |workspace, cx| {
4825            workspace::Pane::go_back(workspace, None, cx)
4826        })
4827        .await;
4828    workspace_a
4829        .update(cx_a, |workspace, cx| {
4830            workspace::Pane::go_forward(workspace, None, cx)
4831        })
4832        .await;
4833    deterministic.run_until_parked();
4834    assert_eq!(
4835        workspace_b.read_with(cx_b, |workspace, cx| workspace
4836            .active_item(cx)
4837            .unwrap()
4838            .id()),
4839        editor_b1.id()
4840    );
4841
4842    // Changes to client A's editor are reflected on client B.
4843    editor_a1.update(cx_a, |editor, cx| {
4844        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2]));
4845    });
4846    deterministic.run_until_parked();
4847    assert_eq!(
4848        editor_b1.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
4849        vec![1..1, 2..2]
4850    );
4851
4852    editor_a1.update(cx_a, |editor, cx| editor.set_text("TWO", cx));
4853    deterministic.run_until_parked();
4854    assert_eq!(
4855        editor_b1.read_with(cx_b, |editor, cx| editor.text(cx)),
4856        "TWO"
4857    );
4858
4859    editor_a1.update(cx_a, |editor, cx| {
4860        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
4861        editor.set_scroll_position(vec2f(0., 100.), cx);
4862    });
4863    deterministic.run_until_parked();
4864    assert_eq!(
4865        editor_b1.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
4866        vec![3..3]
4867    );
4868
4869    // After unfollowing, client B stops receiving updates from client A.
4870    assert_eq!(
4871        workspace_b.read_with(cx_b, |workspace, cx| workspace
4872            .active_item(cx)
4873            .unwrap()
4874            .id()),
4875        editor_b1.id()
4876    );
4877    workspace_b.update(cx_b, |workspace, cx| {
4878        workspace.unfollow(&workspace.active_pane().clone(), cx)
4879    });
4880    workspace_a.update(cx_a, |workspace, cx| {
4881        workspace.activate_item(&editor_a2, cx)
4882    });
4883    deterministic.run_until_parked();
4884    assert_eq!(
4885        workspace_b.read_with(cx_b, |workspace, cx| workspace
4886            .active_item(cx)
4887            .unwrap()
4888            .id()),
4889        editor_b1.id()
4890    );
4891
4892    // Client A starts following client B.
4893    workspace_a
4894        .update(cx_a, |workspace, cx| {
4895            workspace
4896                .toggle_follow(&ToggleFollow(client_b_id), cx)
4897                .unwrap()
4898        })
4899        .await
4900        .unwrap();
4901    assert_eq!(
4902        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
4903        Some(client_b_id)
4904    );
4905    assert_eq!(
4906        workspace_a.read_with(cx_a, |workspace, cx| workspace
4907            .active_item(cx)
4908            .unwrap()
4909            .id()),
4910        editor_a1.id()
4911    );
4912
4913    // Client B activates an external window, which causes a new screen-sharing item to be added to the pane.
4914    let display = MacOSDisplay::new();
4915    active_call_b
4916        .update(cx_b, |call, cx| call.set_location(None, cx))
4917        .await
4918        .unwrap();
4919    active_call_b
4920        .update(cx_b, |call, cx| {
4921            call.room().unwrap().update(cx, |room, cx| {
4922                room.set_display_sources(vec![display.clone()]);
4923                room.share_screen(cx)
4924            })
4925        })
4926        .await
4927        .unwrap();
4928    deterministic.run_until_parked();
4929    let shared_screen = workspace_a.read_with(cx_a, |workspace, cx| {
4930        workspace
4931            .active_item(cx)
4932            .unwrap()
4933            .downcast::<SharedScreen>()
4934            .unwrap()
4935    });
4936
4937    // Client B activates Zed again, which causes the previous editor to become focused again.
4938    active_call_b
4939        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
4940        .await
4941        .unwrap();
4942    deterministic.run_until_parked();
4943    assert_eq!(
4944        workspace_a.read_with(cx_a, |workspace, cx| workspace
4945            .active_item(cx)
4946            .unwrap()
4947            .id()),
4948        editor_a1.id()
4949    );
4950
4951    // Client B activates an external window again, and the previously-opened screen-sharing item
4952    // gets activated.
4953    active_call_b
4954        .update(cx_b, |call, cx| call.set_location(None, cx))
4955        .await
4956        .unwrap();
4957    deterministic.run_until_parked();
4958    assert_eq!(
4959        workspace_a.read_with(cx_a, |workspace, cx| workspace
4960            .active_item(cx)
4961            .unwrap()
4962            .id()),
4963        shared_screen.id()
4964    );
4965
4966    // Following interrupts when client B disconnects.
4967    client_b.disconnect(&cx_b.to_async()).unwrap();
4968    deterministic.run_until_parked();
4969    assert_eq!(
4970        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
4971        None
4972    );
4973}
4974
4975#[gpui::test(iterations = 10)]
4976async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
4977    cx_a.foreground().forbid_parking();
4978    cx_a.update(editor::init);
4979    cx_b.update(editor::init);
4980
4981    let mut server = TestServer::start(cx_a.background()).await;
4982    let client_a = server.create_client(cx_a, "user_a").await;
4983    let client_b = server.create_client(cx_b, "user_b").await;
4984    server
4985        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4986        .await;
4987    let active_call_a = cx_a.read(ActiveCall::global);
4988    let active_call_b = cx_b.read(ActiveCall::global);
4989
4990    // Client A shares a project.
4991    client_a
4992        .fs
4993        .insert_tree(
4994            "/a",
4995            json!({
4996                "1.txt": "one",
4997                "2.txt": "two",
4998                "3.txt": "three",
4999                "4.txt": "four",
5000            }),
5001        )
5002        .await;
5003    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
5004    active_call_a
5005        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
5006        .await
5007        .unwrap();
5008    let project_id = active_call_a
5009        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5010        .await
5011        .unwrap();
5012
5013    // Client B joins the project.
5014    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5015    active_call_b
5016        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
5017        .await
5018        .unwrap();
5019
5020    // Client A opens some editors.
5021    let workspace_a = client_a.build_workspace(&project_a, cx_a);
5022    let pane_a1 = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
5023    let _editor_a1 = workspace_a
5024        .update(cx_a, |workspace, cx| {
5025            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
5026        })
5027        .await
5028        .unwrap()
5029        .downcast::<Editor>()
5030        .unwrap();
5031
5032    // Client B opens an editor.
5033    let workspace_b = client_b.build_workspace(&project_b, cx_b);
5034    let pane_b1 = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
5035    let _editor_b1 = workspace_b
5036        .update(cx_b, |workspace, cx| {
5037            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
5038        })
5039        .await
5040        .unwrap()
5041        .downcast::<Editor>()
5042        .unwrap();
5043
5044    // Clients A and B follow each other in split panes
5045    workspace_a.update(cx_a, |workspace, cx| {
5046        workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
5047        let pane_a1 = pane_a1.clone();
5048        cx.defer(move |workspace, _| {
5049            assert_ne!(*workspace.active_pane(), pane_a1);
5050        });
5051    });
5052    workspace_a
5053        .update(cx_a, |workspace, cx| {
5054            let leader_id = *project_a.read(cx).collaborators().keys().next().unwrap();
5055            workspace
5056                .toggle_follow(&workspace::ToggleFollow(leader_id), cx)
5057                .unwrap()
5058        })
5059        .await
5060        .unwrap();
5061    workspace_b.update(cx_b, |workspace, cx| {
5062        workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
5063        let pane_b1 = pane_b1.clone();
5064        cx.defer(move |workspace, _| {
5065            assert_ne!(*workspace.active_pane(), pane_b1);
5066        });
5067    });
5068    workspace_b
5069        .update(cx_b, |workspace, cx| {
5070            let leader_id = *project_b.read(cx).collaborators().keys().next().unwrap();
5071            workspace
5072                .toggle_follow(&workspace::ToggleFollow(leader_id), cx)
5073                .unwrap()
5074        })
5075        .await
5076        .unwrap();
5077
5078    workspace_a.update(cx_a, |workspace, cx| {
5079        workspace.activate_next_pane(cx);
5080    });
5081    // Wait for focus effects to be fully flushed
5082    workspace_a.update(cx_a, |workspace, _| {
5083        assert_eq!(*workspace.active_pane(), pane_a1);
5084    });
5085
5086    workspace_a
5087        .update(cx_a, |workspace, cx| {
5088            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
5089        })
5090        .await
5091        .unwrap();
5092    workspace_b.update(cx_b, |workspace, cx| {
5093        workspace.activate_next_pane(cx);
5094    });
5095
5096    workspace_b
5097        .update(cx_b, |workspace, cx| {
5098            assert_eq!(*workspace.active_pane(), pane_b1);
5099            workspace.open_path((worktree_id, "4.txt"), None, true, cx)
5100        })
5101        .await
5102        .unwrap();
5103    cx_a.foreground().run_until_parked();
5104
5105    // Ensure leader updates don't change the active pane of followers
5106    workspace_a.read_with(cx_a, |workspace, _| {
5107        assert_eq!(*workspace.active_pane(), pane_a1);
5108    });
5109    workspace_b.read_with(cx_b, |workspace, _| {
5110        assert_eq!(*workspace.active_pane(), pane_b1);
5111    });
5112
5113    // Ensure peers following each other doesn't cause an infinite loop.
5114    assert_eq!(
5115        workspace_a.read_with(cx_a, |workspace, cx| workspace
5116            .active_item(cx)
5117            .unwrap()
5118            .project_path(cx)),
5119        Some((worktree_id, "3.txt").into())
5120    );
5121    workspace_a.update(cx_a, |workspace, cx| {
5122        assert_eq!(
5123            workspace.active_item(cx).unwrap().project_path(cx),
5124            Some((worktree_id, "3.txt").into())
5125        );
5126        workspace.activate_next_pane(cx);
5127    });
5128
5129    workspace_a.update(cx_a, |workspace, cx| {
5130        assert_eq!(
5131            workspace.active_item(cx).unwrap().project_path(cx),
5132            Some((worktree_id, "4.txt").into())
5133        );
5134    });
5135
5136    workspace_b.update(cx_b, |workspace, cx| {
5137        assert_eq!(
5138            workspace.active_item(cx).unwrap().project_path(cx),
5139            Some((worktree_id, "4.txt").into())
5140        );
5141        workspace.activate_next_pane(cx);
5142    });
5143
5144    workspace_b.update(cx_b, |workspace, cx| {
5145        assert_eq!(
5146            workspace.active_item(cx).unwrap().project_path(cx),
5147            Some((worktree_id, "3.txt").into())
5148        );
5149    });
5150}
5151
5152#[gpui::test(iterations = 10)]
5153async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
5154    cx_a.foreground().forbid_parking();
5155    cx_a.update(editor::init);
5156    cx_b.update(editor::init);
5157
5158    // 2 clients connect to a server.
5159    let mut server = TestServer::start(cx_a.background()).await;
5160    let client_a = server.create_client(cx_a, "user_a").await;
5161    let client_b = server.create_client(cx_b, "user_b").await;
5162    server
5163        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5164        .await;
5165    let active_call_a = cx_a.read(ActiveCall::global);
5166    let active_call_b = cx_b.read(ActiveCall::global);
5167
5168    // Client A shares a project.
5169    client_a
5170        .fs
5171        .insert_tree(
5172            "/a",
5173            json!({
5174                "1.txt": "one",
5175                "2.txt": "two",
5176                "3.txt": "three",
5177            }),
5178        )
5179        .await;
5180    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
5181    active_call_a
5182        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
5183        .await
5184        .unwrap();
5185
5186    let project_id = active_call_a
5187        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5188        .await
5189        .unwrap();
5190    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5191    active_call_b
5192        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
5193        .await
5194        .unwrap();
5195
5196    // Client A opens some editors.
5197    let workspace_a = client_a.build_workspace(&project_a, cx_a);
5198    let _editor_a1 = workspace_a
5199        .update(cx_a, |workspace, cx| {
5200            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
5201        })
5202        .await
5203        .unwrap()
5204        .downcast::<Editor>()
5205        .unwrap();
5206
5207    // Client B starts following client A.
5208    let workspace_b = client_b.build_workspace(&project_b, cx_b);
5209    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
5210    let leader_id = project_b.read_with(cx_b, |project, _| {
5211        project.collaborators().values().next().unwrap().peer_id
5212    });
5213    workspace_b
5214        .update(cx_b, |workspace, cx| {
5215            workspace
5216                .toggle_follow(&ToggleFollow(leader_id), cx)
5217                .unwrap()
5218        })
5219        .await
5220        .unwrap();
5221    assert_eq!(
5222        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5223        Some(leader_id)
5224    );
5225    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
5226        workspace
5227            .active_item(cx)
5228            .unwrap()
5229            .downcast::<Editor>()
5230            .unwrap()
5231    });
5232
5233    // When client B moves, it automatically stops following client A.
5234    editor_b2.update(cx_b, |editor, cx| editor.move_right(&editor::MoveRight, cx));
5235    assert_eq!(
5236        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5237        None
5238    );
5239
5240    workspace_b
5241        .update(cx_b, |workspace, cx| {
5242            workspace
5243                .toggle_follow(&ToggleFollow(leader_id), cx)
5244                .unwrap()
5245        })
5246        .await
5247        .unwrap();
5248    assert_eq!(
5249        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5250        Some(leader_id)
5251    );
5252
5253    // When client B edits, it automatically stops following client A.
5254    editor_b2.update(cx_b, |editor, cx| editor.insert("X", cx));
5255    assert_eq!(
5256        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5257        None
5258    );
5259
5260    workspace_b
5261        .update(cx_b, |workspace, cx| {
5262            workspace
5263                .toggle_follow(&ToggleFollow(leader_id), cx)
5264                .unwrap()
5265        })
5266        .await
5267        .unwrap();
5268    assert_eq!(
5269        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5270        Some(leader_id)
5271    );
5272
5273    // When client B scrolls, it automatically stops following client A.
5274    editor_b2.update(cx_b, |editor, cx| {
5275        editor.set_scroll_position(vec2f(0., 3.), cx)
5276    });
5277    assert_eq!(
5278        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5279        None
5280    );
5281
5282    workspace_b
5283        .update(cx_b, |workspace, cx| {
5284            workspace
5285                .toggle_follow(&ToggleFollow(leader_id), cx)
5286                .unwrap()
5287        })
5288        .await
5289        .unwrap();
5290    assert_eq!(
5291        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5292        Some(leader_id)
5293    );
5294
5295    // When client B activates a different pane, it continues following client A in the original pane.
5296    workspace_b.update(cx_b, |workspace, cx| {
5297        workspace.split_pane(pane_b.clone(), SplitDirection::Right, cx)
5298    });
5299    assert_eq!(
5300        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5301        Some(leader_id)
5302    );
5303
5304    workspace_b.update(cx_b, |workspace, cx| workspace.activate_next_pane(cx));
5305    assert_eq!(
5306        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5307        Some(leader_id)
5308    );
5309
5310    // When client B activates a different item in the original pane, it automatically stops following client A.
5311    workspace_b
5312        .update(cx_b, |workspace, cx| {
5313            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
5314        })
5315        .await
5316        .unwrap();
5317    assert_eq!(
5318        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5319        None
5320    );
5321}
5322
5323#[gpui::test(iterations = 10)]
5324async fn test_peers_simultaneously_following_each_other(
5325    deterministic: Arc<Deterministic>,
5326    cx_a: &mut TestAppContext,
5327    cx_b: &mut TestAppContext,
5328) {
5329    deterministic.forbid_parking();
5330    cx_a.update(editor::init);
5331    cx_b.update(editor::init);
5332
5333    let mut server = TestServer::start(cx_a.background()).await;
5334    let client_a = server.create_client(cx_a, "user_a").await;
5335    let client_b = server.create_client(cx_b, "user_b").await;
5336    server
5337        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5338        .await;
5339    let active_call_a = cx_a.read(ActiveCall::global);
5340
5341    client_a.fs.insert_tree("/a", json!({})).await;
5342    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
5343    let workspace_a = client_a.build_workspace(&project_a, cx_a);
5344    let project_id = active_call_a
5345        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5346        .await
5347        .unwrap();
5348
5349    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5350    let workspace_b = client_b.build_workspace(&project_b, cx_b);
5351
5352    deterministic.run_until_parked();
5353    let client_a_id = project_b.read_with(cx_b, |project, _| {
5354        project.collaborators().values().next().unwrap().peer_id
5355    });
5356    let client_b_id = project_a.read_with(cx_a, |project, _| {
5357        project.collaborators().values().next().unwrap().peer_id
5358    });
5359
5360    let a_follow_b = workspace_a.update(cx_a, |workspace, cx| {
5361        workspace
5362            .toggle_follow(&ToggleFollow(client_b_id), cx)
5363            .unwrap()
5364    });
5365    let b_follow_a = workspace_b.update(cx_b, |workspace, cx| {
5366        workspace
5367            .toggle_follow(&ToggleFollow(client_a_id), cx)
5368            .unwrap()
5369    });
5370
5371    futures::try_join!(a_follow_b, b_follow_a).unwrap();
5372    workspace_a.read_with(cx_a, |workspace, _| {
5373        assert_eq!(
5374            workspace.leader_for_pane(workspace.active_pane()),
5375            Some(client_b_id)
5376        );
5377    });
5378    workspace_b.read_with(cx_b, |workspace, _| {
5379        assert_eq!(
5380            workspace.leader_for_pane(workspace.active_pane()),
5381            Some(client_a_id)
5382        );
5383    });
5384}
5385
5386#[gpui::test(iterations = 100)]
5387async fn test_random_collaboration(
5388    cx: &mut TestAppContext,
5389    deterministic: Arc<Deterministic>,
5390    rng: StdRng,
5391) {
5392    deterministic.forbid_parking();
5393    let rng = Arc::new(Mutex::new(rng));
5394
5395    let max_peers = env::var("MAX_PEERS")
5396        .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
5397        .unwrap_or(5);
5398
5399    let max_operations = env::var("OPERATIONS")
5400        .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
5401        .unwrap_or(10);
5402
5403    let mut server = TestServer::start(cx.background()).await;
5404    let db = server.app_state.db.clone();
5405
5406    let mut available_guests = Vec::new();
5407    for ix in 0..max_peers {
5408        let username = format!("guest-{}", ix + 1);
5409        let user_id = db
5410            .create_user(
5411                &format!("{username}@example.com"),
5412                false,
5413                NewUserParams {
5414                    github_login: username.clone(),
5415                    github_user_id: (ix + 1) as i32,
5416                    invite_count: 0,
5417                },
5418            )
5419            .await
5420            .unwrap()
5421            .user_id;
5422        available_guests.push((user_id, username));
5423    }
5424
5425    for (ix, (user_id_a, _)) in available_guests.iter().enumerate() {
5426        for (user_id_b, _) in &available_guests[ix + 1..] {
5427            server
5428                .app_state
5429                .db
5430                .send_contact_request(*user_id_a, *user_id_b)
5431                .await
5432                .unwrap();
5433            server
5434                .app_state
5435                .db
5436                .respond_to_contact_request(*user_id_b, *user_id_a, true)
5437                .await
5438                .unwrap();
5439        }
5440    }
5441
5442    let mut clients = Vec::new();
5443    let mut user_ids = Vec::new();
5444    let mut peer_ids = Vec::new();
5445    let mut op_start_signals = Vec::new();
5446    let mut next_entity_id = 100000;
5447
5448    let mut operations = 0;
5449    while operations < max_operations {
5450        let distribution = rng.lock().gen_range(0..100);
5451        match distribution {
5452            0..=19 if !available_guests.is_empty() => {
5453                let guest_ix = rng.lock().gen_range(0..available_guests.len());
5454                let (_, guest_username) = available_guests.remove(guest_ix);
5455                log::info!("Adding new connection for {}", guest_username);
5456                next_entity_id += 100000;
5457                let mut guest_cx = TestAppContext::new(
5458                    cx.foreground_platform(),
5459                    cx.platform(),
5460                    deterministic.build_foreground(next_entity_id),
5461                    deterministic.build_background(),
5462                    cx.font_cache(),
5463                    cx.leak_detector(),
5464                    next_entity_id,
5465                    cx.function_name.clone(),
5466                );
5467
5468                let op_start_signal = futures::channel::mpsc::unbounded();
5469                let guest = server.create_client(&mut guest_cx, &guest_username).await;
5470                user_ids.push(guest.current_user_id(&guest_cx));
5471                peer_ids.push(guest.peer_id().unwrap());
5472                op_start_signals.push(op_start_signal.0);
5473                clients.push(guest_cx.foreground().spawn(guest.simulate(
5474                    guest_username.clone(),
5475                    op_start_signal.1,
5476                    rng.clone(),
5477                    guest_cx,
5478                )));
5479
5480                log::info!("Added connection for {}", guest_username);
5481                operations += 1;
5482            }
5483            20..=29 if clients.len() > 1 => {
5484                let guest_ix = rng.lock().gen_range(1..clients.len());
5485                log::info!("Removing guest {}", user_ids[guest_ix]);
5486                let removed_guest_id = user_ids.remove(guest_ix);
5487                let removed_peer_id = peer_ids.remove(guest_ix);
5488                let guest = clients.remove(guest_ix);
5489                op_start_signals.remove(guest_ix);
5490                server.forbid_connections();
5491                server.disconnect_client(removed_peer_id);
5492                deterministic.advance_clock(RECEIVE_TIMEOUT);
5493                deterministic.start_waiting();
5494                log::info!("Waiting for guest {} to exit...", removed_guest_id);
5495                let (guest, mut guest_cx) = guest.await;
5496                deterministic.finish_waiting();
5497                server.allow_connections();
5498
5499                for project in &guest.remote_projects {
5500                    project.read_with(&guest_cx, |project, _| assert!(project.is_read_only()));
5501                }
5502                for user_id in &user_ids {
5503                    let contacts = server.app_state.db.get_contacts(*user_id).await.unwrap();
5504                    let contacts = server
5505                        .store
5506                        .lock()
5507                        .await
5508                        .build_initial_contacts_update(contacts)
5509                        .contacts;
5510                    for contact in contacts {
5511                        if contact.online {
5512                            assert_ne!(
5513                                contact.user_id, removed_guest_id.0 as u64,
5514                                "removed guest is still a contact of another peer"
5515                            );
5516                        }
5517                    }
5518                }
5519
5520                log::info!("{} removed", guest.username);
5521                available_guests.push((removed_guest_id, guest.username.clone()));
5522                guest_cx.update(|cx| {
5523                    cx.clear_globals();
5524                    drop(guest);
5525                });
5526
5527                operations += 1;
5528            }
5529            _ if !op_start_signals.is_empty() => {
5530                while operations < max_operations && rng.lock().gen_bool(0.7) {
5531                    op_start_signals
5532                        .choose(&mut *rng.lock())
5533                        .unwrap()
5534                        .unbounded_send(())
5535                        .unwrap();
5536                    operations += 1;
5537                }
5538
5539                if rng.lock().gen_bool(0.8) {
5540                    deterministic.run_until_parked();
5541                }
5542            }
5543            _ => {}
5544        }
5545    }
5546
5547    drop(op_start_signals);
5548    deterministic.start_waiting();
5549    let clients = futures::future::join_all(clients).await;
5550    deterministic.finish_waiting();
5551    deterministic.run_until_parked();
5552
5553    for (guest_client, guest_cx) in &clients {
5554        for guest_project in &guest_client.remote_projects {
5555            guest_project.read_with(guest_cx, |guest_project, cx| {
5556                let host_project = clients.iter().find_map(|(client, cx)| {
5557                    let project = client.local_projects.iter().find(|host_project| {
5558                        host_project.read_with(cx, |host_project, _| {
5559                            host_project.remote_id() == guest_project.remote_id()
5560                        })
5561                    })?;
5562                    Some((project, cx))
5563                });
5564
5565                if !guest_project.is_read_only() {
5566                    if let Some((host_project, host_cx)) = host_project {
5567                        let host_worktree_snapshots =
5568                            host_project.read_with(host_cx, |host_project, cx| {
5569                                host_project
5570                                    .worktrees(cx)
5571                                    .map(|worktree| {
5572                                        let worktree = worktree.read(cx);
5573                                        (worktree.id(), worktree.snapshot())
5574                                    })
5575                                    .collect::<BTreeMap<_, _>>()
5576                            });
5577                        let guest_worktree_snapshots = guest_project
5578                            .worktrees(cx)
5579                            .map(|worktree| {
5580                                let worktree = worktree.read(cx);
5581                                (worktree.id(), worktree.snapshot())
5582                            })
5583                            .collect::<BTreeMap<_, _>>();
5584
5585                        assert_eq!(
5586                            guest_worktree_snapshots.keys().collect::<Vec<_>>(),
5587                            host_worktree_snapshots.keys().collect::<Vec<_>>(),
5588                            "{} has different worktrees than the host",
5589                            guest_client.username
5590                        );
5591
5592                        for (id, host_snapshot) in &host_worktree_snapshots {
5593                            let guest_snapshot = &guest_worktree_snapshots[id];
5594                            assert_eq!(
5595                                guest_snapshot.root_name(),
5596                                host_snapshot.root_name(),
5597                                "{} has different root name than the host for worktree {}",
5598                                guest_client.username,
5599                                id
5600                            );
5601                            assert_eq!(
5602                                guest_snapshot.entries(false).collect::<Vec<_>>(),
5603                                host_snapshot.entries(false).collect::<Vec<_>>(),
5604                                "{} has different snapshot than the host for worktree {}",
5605                                guest_client.username,
5606                                id
5607                            );
5608                            assert_eq!(guest_snapshot.scan_id(), host_snapshot.scan_id());
5609                        }
5610                    }
5611                }
5612
5613                guest_project.check_invariants(cx);
5614            });
5615        }
5616
5617        for (guest_project, guest_buffers) in &guest_client.buffers {
5618            let project_id = if guest_project.read_with(guest_cx, |project, _| {
5619                project.is_local() || project.is_read_only()
5620            }) {
5621                continue;
5622            } else {
5623                guest_project
5624                    .read_with(guest_cx, |project, _| project.remote_id())
5625                    .unwrap()
5626            };
5627
5628            let host_project = clients.iter().find_map(|(client, cx)| {
5629                let project = client.local_projects.iter().find(|host_project| {
5630                    host_project.read_with(cx, |host_project, _| {
5631                        host_project.remote_id() == Some(project_id)
5632                    })
5633                })?;
5634                Some((project, cx))
5635            });
5636
5637            let (host_project, host_cx) = if let Some((host_project, host_cx)) = host_project {
5638                (host_project, host_cx)
5639            } else {
5640                continue;
5641            };
5642
5643            for guest_buffer in guest_buffers {
5644                let buffer_id = guest_buffer.read_with(guest_cx, |buffer, _| buffer.remote_id());
5645                let host_buffer = host_project.read_with(host_cx, |project, cx| {
5646                    project.buffer_for_id(buffer_id, cx).unwrap_or_else(|| {
5647                        panic!(
5648                            "host does not have buffer for guest:{}, peer:{:?}, id:{}",
5649                            guest_client.username,
5650                            guest_client.peer_id(),
5651                            buffer_id
5652                        )
5653                    })
5654                });
5655                let path = host_buffer
5656                    .read_with(host_cx, |buffer, cx| buffer.file().unwrap().full_path(cx));
5657
5658                assert_eq!(
5659                    guest_buffer.read_with(guest_cx, |buffer, _| buffer.deferred_ops_len()),
5660                    0,
5661                    "{}, buffer {}, path {:?} has deferred operations",
5662                    guest_client.username,
5663                    buffer_id,
5664                    path,
5665                );
5666                assert_eq!(
5667                    guest_buffer.read_with(guest_cx, |buffer, _| buffer.text()),
5668                    host_buffer.read_with(host_cx, |buffer, _| buffer.text()),
5669                    "{}, buffer {}, path {:?}, differs from the host's buffer",
5670                    guest_client.username,
5671                    buffer_id,
5672                    path
5673                );
5674            }
5675        }
5676    }
5677
5678    for (client, mut cx) in clients {
5679        cx.update(|cx| {
5680            cx.clear_globals();
5681            drop(client);
5682        });
5683    }
5684}
5685
5686struct TestServer {
5687    peer: Arc<Peer>,
5688    app_state: Arc<AppState>,
5689    server: Arc<Server>,
5690    connection_killers: Arc<Mutex<HashMap<PeerId, Arc<AtomicBool>>>>,
5691    forbid_connections: Arc<AtomicBool>,
5692    _test_db: TestDb,
5693    test_live_kit_server: Arc<live_kit_client::TestServer>,
5694}
5695
5696impl TestServer {
5697    async fn start(background: Arc<executor::Background>) -> Self {
5698        static NEXT_LIVE_KIT_SERVER_ID: AtomicUsize = AtomicUsize::new(0);
5699
5700        let test_db = TestDb::new(background.clone());
5701        let live_kit_server_id = NEXT_LIVE_KIT_SERVER_ID.fetch_add(1, SeqCst);
5702        let live_kit_server = live_kit_client::TestServer::create(
5703            format!("http://livekit.{}.test", live_kit_server_id),
5704            format!("devkey-{}", live_kit_server_id),
5705            format!("secret-{}", live_kit_server_id),
5706            background.clone(),
5707        )
5708        .unwrap();
5709        let app_state = Self::build_app_state(&test_db, &live_kit_server).await;
5710        let peer = Peer::new();
5711        let server = Server::new(app_state.clone());
5712        Self {
5713            peer,
5714            app_state,
5715            server,
5716            connection_killers: Default::default(),
5717            forbid_connections: Default::default(),
5718            _test_db: test_db,
5719            test_live_kit_server: live_kit_server,
5720        }
5721    }
5722
5723    async fn create_client(&mut self, cx: &mut TestAppContext, name: &str) -> TestClient {
5724        cx.update(|cx| {
5725            cx.set_global(HomeDir(Path::new("/tmp/").to_path_buf()));
5726
5727            let mut settings = Settings::test(cx);
5728            settings.projects_online_by_default = false;
5729            cx.set_global(settings);
5730        });
5731
5732        let http = FakeHttpClient::with_404_response();
5733        let user_id = if let Ok(Some(user)) = self
5734            .app_state
5735            .db
5736            .get_user_by_github_account(name, None)
5737            .await
5738        {
5739            user.id
5740        } else {
5741            self.app_state
5742                .db
5743                .create_user(
5744                    &format!("{name}@example.com"),
5745                    false,
5746                    NewUserParams {
5747                        github_login: name.into(),
5748                        github_user_id: 0,
5749                        invite_count: 0,
5750                    },
5751                )
5752                .await
5753                .expect("creating user failed")
5754                .user_id
5755        };
5756        let client_name = name.to_string();
5757        let mut client = cx.read(|cx| Client::new(http.clone(), cx));
5758        let server = self.server.clone();
5759        let db = self.app_state.db.clone();
5760        let connection_killers = self.connection_killers.clone();
5761        let forbid_connections = self.forbid_connections.clone();
5762
5763        Arc::get_mut(&mut client)
5764            .unwrap()
5765            .set_id(user_id.0 as usize)
5766            .override_authenticate(move |cx| {
5767                cx.spawn(|_| async move {
5768                    let access_token = "the-token".to_string();
5769                    Ok(Credentials {
5770                        user_id: user_id.0 as u64,
5771                        access_token,
5772                    })
5773                })
5774            })
5775            .override_establish_connection(move |credentials, cx| {
5776                assert_eq!(credentials.user_id, user_id.0 as u64);
5777                assert_eq!(credentials.access_token, "the-token");
5778
5779                let server = server.clone();
5780                let db = db.clone();
5781                let connection_killers = connection_killers.clone();
5782                let forbid_connections = forbid_connections.clone();
5783                let client_name = client_name.clone();
5784                cx.spawn(move |cx| async move {
5785                    if forbid_connections.load(SeqCst) {
5786                        Err(EstablishConnectionError::other(anyhow!(
5787                            "server is forbidding connections"
5788                        )))
5789                    } else {
5790                        let (client_conn, server_conn, killed) =
5791                            Connection::in_memory(cx.background());
5792                        let (connection_id_tx, connection_id_rx) = oneshot::channel();
5793                        let user = db
5794                            .get_user_by_id(user_id)
5795                            .await
5796                            .expect("retrieving user failed")
5797                            .unwrap();
5798                        cx.background()
5799                            .spawn(server.handle_connection(
5800                                server_conn,
5801                                client_name,
5802                                user,
5803                                Some(connection_id_tx),
5804                                cx.background(),
5805                            ))
5806                            .detach();
5807                        let connection_id = connection_id_rx.await.unwrap();
5808                        connection_killers
5809                            .lock()
5810                            .insert(PeerId(connection_id.0), killed);
5811                        Ok(client_conn)
5812                    }
5813                })
5814            });
5815
5816        let fs = FakeFs::new(cx.background());
5817        let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx));
5818        let app_state = Arc::new(workspace::AppState {
5819            client: client.clone(),
5820            user_store: user_store.clone(),
5821            languages: Arc::new(LanguageRegistry::new(Task::ready(()))),
5822            themes: ThemeRegistry::new((), cx.font_cache()),
5823            fs: fs.clone(),
5824            build_window_options: Default::default,
5825            initialize_workspace: |_, _, _| unimplemented!(),
5826            default_item_factory: |_, _| unimplemented!(),
5827        });
5828
5829        Project::init(&client);
5830        cx.update(|cx| {
5831            workspace::init(app_state.clone(), cx);
5832            call::init(client.clone(), user_store.clone(), cx);
5833        });
5834
5835        client
5836            .authenticate_and_connect(false, &cx.to_async())
5837            .await
5838            .unwrap();
5839
5840        let client = TestClient {
5841            client,
5842            username: name.to_string(),
5843            local_projects: Default::default(),
5844            remote_projects: Default::default(),
5845            next_root_dir_id: 0,
5846            user_store,
5847            fs,
5848            language_registry: Arc::new(LanguageRegistry::test()),
5849            buffers: Default::default(),
5850        };
5851        client.wait_for_current_user(cx).await;
5852        client
5853    }
5854
5855    fn disconnect_client(&self, peer_id: PeerId) {
5856        self.connection_killers
5857            .lock()
5858            .remove(&peer_id)
5859            .unwrap()
5860            .store(true, SeqCst);
5861    }
5862
5863    fn forbid_connections(&self) {
5864        self.forbid_connections.store(true, SeqCst);
5865    }
5866
5867    fn allow_connections(&self) {
5868        self.forbid_connections.store(false, SeqCst);
5869    }
5870
5871    async fn make_contacts(&self, clients: &mut [(&TestClient, &mut TestAppContext)]) {
5872        for ix in 1..clients.len() {
5873            let (left, right) = clients.split_at_mut(ix);
5874            let (client_a, cx_a) = left.last_mut().unwrap();
5875            for (client_b, cx_b) in right {
5876                client_a
5877                    .user_store
5878                    .update(*cx_a, |store, cx| {
5879                        store.request_contact(client_b.user_id().unwrap(), cx)
5880                    })
5881                    .await
5882                    .unwrap();
5883                cx_a.foreground().run_until_parked();
5884                client_b
5885                    .user_store
5886                    .update(*cx_b, |store, cx| {
5887                        store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
5888                    })
5889                    .await
5890                    .unwrap();
5891            }
5892        }
5893    }
5894
5895    async fn create_room(&self, clients: &mut [(&TestClient, &mut TestAppContext)]) {
5896        self.make_contacts(clients).await;
5897
5898        let (left, right) = clients.split_at_mut(1);
5899        let (_client_a, cx_a) = &mut left[0];
5900        let active_call_a = cx_a.read(ActiveCall::global);
5901
5902        for (client_b, cx_b) in right {
5903            let user_id_b = client_b.current_user_id(*cx_b).to_proto();
5904            active_call_a
5905                .update(*cx_a, |call, cx| call.invite(user_id_b, None, cx))
5906                .await
5907                .unwrap();
5908
5909            cx_b.foreground().run_until_parked();
5910            let active_call_b = cx_b.read(ActiveCall::global);
5911            active_call_b
5912                .update(*cx_b, |call, cx| call.accept_incoming(cx))
5913                .await
5914                .unwrap();
5915        }
5916    }
5917
5918    async fn build_app_state(
5919        test_db: &TestDb,
5920        fake_server: &live_kit_client::TestServer,
5921    ) -> Arc<AppState> {
5922        Arc::new(AppState {
5923            db: test_db.db().clone(),
5924            live_kit_client: Some(Arc::new(fake_server.create_api_client())),
5925            config: Default::default(),
5926        })
5927    }
5928}
5929
5930impl Deref for TestServer {
5931    type Target = Server;
5932
5933    fn deref(&self) -> &Self::Target {
5934        &self.server
5935    }
5936}
5937
5938impl Drop for TestServer {
5939    fn drop(&mut self) {
5940        self.peer.reset();
5941        self.test_live_kit_server.teardown().unwrap();
5942    }
5943}
5944
5945struct TestClient {
5946    client: Arc<Client>,
5947    username: String,
5948    local_projects: Vec<ModelHandle<Project>>,
5949    remote_projects: Vec<ModelHandle<Project>>,
5950    next_root_dir_id: usize,
5951    pub user_store: ModelHandle<UserStore>,
5952    language_registry: Arc<LanguageRegistry>,
5953    fs: Arc<FakeFs>,
5954    buffers: HashMap<ModelHandle<Project>, HashSet<ModelHandle<language::Buffer>>>,
5955}
5956
5957impl Deref for TestClient {
5958    type Target = Arc<Client>;
5959
5960    fn deref(&self) -> &Self::Target {
5961        &self.client
5962    }
5963}
5964
5965struct ContactsSummary {
5966    pub current: Vec<String>,
5967    pub outgoing_requests: Vec<String>,
5968    pub incoming_requests: Vec<String>,
5969}
5970
5971impl TestClient {
5972    pub fn current_user_id(&self, cx: &TestAppContext) -> UserId {
5973        UserId::from_proto(
5974            self.user_store
5975                .read_with(cx, |user_store, _| user_store.current_user().unwrap().id),
5976        )
5977    }
5978
5979    async fn wait_for_current_user(&self, cx: &TestAppContext) {
5980        let mut authed_user = self
5981            .user_store
5982            .read_with(cx, |user_store, _| user_store.watch_current_user());
5983        while authed_user.next().await.unwrap().is_none() {}
5984    }
5985
5986    async fn clear_contacts(&self, cx: &mut TestAppContext) {
5987        self.user_store
5988            .update(cx, |store, _| store.clear_contacts())
5989            .await;
5990    }
5991
5992    fn summarize_contacts(&self, cx: &TestAppContext) -> ContactsSummary {
5993        self.user_store.read_with(cx, |store, _| ContactsSummary {
5994            current: store
5995                .contacts()
5996                .iter()
5997                .map(|contact| contact.user.github_login.clone())
5998                .collect(),
5999            outgoing_requests: store
6000                .outgoing_contact_requests()
6001                .iter()
6002                .map(|user| user.github_login.clone())
6003                .collect(),
6004            incoming_requests: store
6005                .incoming_contact_requests()
6006                .iter()
6007                .map(|user| user.github_login.clone())
6008                .collect(),
6009        })
6010    }
6011
6012    async fn build_local_project(
6013        &self,
6014        root_path: impl AsRef<Path>,
6015        cx: &mut TestAppContext,
6016    ) -> (ModelHandle<Project>, WorktreeId) {
6017        let project = cx.update(|cx| {
6018            Project::local(
6019                self.client.clone(),
6020                self.user_store.clone(),
6021                self.language_registry.clone(),
6022                self.fs.clone(),
6023                cx,
6024            )
6025        });
6026        let (worktree, _) = project
6027            .update(cx, |p, cx| {
6028                p.find_or_create_local_worktree(root_path, true, cx)
6029            })
6030            .await
6031            .unwrap();
6032        worktree
6033            .read_with(cx, |tree, _| tree.as_local().unwrap().scan_complete())
6034            .await;
6035        (project, worktree.read_with(cx, |tree, _| tree.id()))
6036    }
6037
6038    async fn build_remote_project(
6039        &self,
6040        host_project_id: u64,
6041        guest_cx: &mut TestAppContext,
6042    ) -> ModelHandle<Project> {
6043        let project_b = guest_cx.spawn(|cx| {
6044            Project::remote(
6045                host_project_id,
6046                self.client.clone(),
6047                self.user_store.clone(),
6048                self.language_registry.clone(),
6049                FakeFs::new(cx.background()),
6050                cx,
6051            )
6052        });
6053        project_b.await.unwrap()
6054    }
6055
6056    fn build_workspace(
6057        &self,
6058        project: &ModelHandle<Project>,
6059        cx: &mut TestAppContext,
6060    ) -> ViewHandle<Workspace> {
6061        let (_, root_view) = cx.add_window(|_| EmptyView);
6062        cx.add_view(&root_view, |cx| {
6063            Workspace::new(project.clone(), |_, _| unimplemented!(), cx)
6064        })
6065    }
6066
6067    pub async fn simulate(
6068        mut self,
6069        username: String,
6070        mut op_start_signal: futures::channel::mpsc::UnboundedReceiver<()>,
6071        rng: Arc<Mutex<StdRng>>,
6072        mut cx: TestAppContext,
6073    ) -> (Self, TestAppContext) {
6074        async fn tick(
6075            client: &mut TestClient,
6076            username: &str,
6077            rng: Arc<Mutex<StdRng>>,
6078            cx: &mut TestAppContext,
6079        ) -> anyhow::Result<()> {
6080            let active_call = cx.read(ActiveCall::global);
6081            if active_call.read_with(cx, |call, _| call.incoming().borrow().is_some()) {
6082                if rng.lock().gen() {
6083                    log::info!("{}: accepting incoming call", username);
6084                    active_call
6085                        .update(cx, |call, cx| call.accept_incoming(cx))
6086                        .await?;
6087                } else {
6088                    log::info!("{}: declining incoming call", username);
6089                    active_call.update(cx, |call, _| call.decline_incoming())?;
6090                }
6091            } else {
6092                let available_contacts = client.user_store.read_with(cx, |user_store, _| {
6093                    user_store
6094                        .contacts()
6095                        .iter()
6096                        .filter(|contact| contact.online && !contact.busy)
6097                        .cloned()
6098                        .collect::<Vec<_>>()
6099                });
6100
6101                let distribution = rng.lock().gen_range(0..100);
6102                match distribution {
6103                    0..=29 if !available_contacts.is_empty() => {
6104                        let contact = available_contacts.choose(&mut *rng.lock()).unwrap();
6105                        log::info!("{}: inviting {}", username, contact.user.github_login);
6106                        active_call
6107                            .update(cx, |call, cx| call.invite(contact.user.id, None, cx))
6108                            .await?;
6109                    }
6110                    30..=39 if active_call.read_with(cx, |call, _| call.room().is_some()) => {
6111                        log::info!("{}: hanging up", username);
6112                        active_call.update(cx, |call, cx| call.hang_up(cx))?;
6113                    }
6114                    _ => {}
6115                }
6116            }
6117
6118            let remote_projects =
6119                if let Some(room) = active_call.read_with(cx, |call, _| call.room().cloned()) {
6120                    room.read_with(cx, |room, _| {
6121                        room.remote_participants()
6122                            .values()
6123                            .flat_map(|participant| participant.projects.clone())
6124                            .collect::<Vec<_>>()
6125                    })
6126                } else {
6127                    Default::default()
6128                };
6129            let project = if remote_projects.is_empty() || rng.lock().gen() {
6130                if client.local_projects.is_empty() || rng.lock().gen() {
6131                    let dir_paths = client.fs.directories().await;
6132                    let local_project = if dir_paths.is_empty() || rng.lock().gen() {
6133                        let root_path = format!(
6134                            "/{}-root-{}",
6135                            username,
6136                            post_inc(&mut client.next_root_dir_id)
6137                        );
6138                        let root_path = Path::new(&root_path);
6139                        client.fs.create_dir(root_path).await.unwrap();
6140                        client
6141                            .fs
6142                            .create_file(&root_path.join("main.rs"), Default::default())
6143                            .await
6144                            .unwrap();
6145                        log::info!("{}: opening local project at {:?}", username, root_path);
6146                        client.build_local_project(root_path, cx).await.0
6147                    } else {
6148                        let root_path = dir_paths.choose(&mut *rng.lock()).unwrap();
6149                        log::info!("{}: opening local project at {:?}", username, root_path);
6150                        client.build_local_project(root_path, cx).await.0
6151                    };
6152                    client.local_projects.push(local_project.clone());
6153                    local_project
6154                } else {
6155                    client
6156                        .local_projects
6157                        .choose(&mut *rng.lock())
6158                        .unwrap()
6159                        .clone()
6160                }
6161            } else {
6162                if client.remote_projects.is_empty() || rng.lock().gen() {
6163                    let remote_project_id = remote_projects.choose(&mut *rng.lock()).unwrap().id;
6164                    let remote_project = if let Some(project) =
6165                        client.remote_projects.iter().find(|project| {
6166                            project.read_with(cx, |project, _| {
6167                                project.remote_id() == Some(remote_project_id)
6168                            })
6169                        }) {
6170                        project.clone()
6171                    } else {
6172                        log::info!("{}: opening remote project {}", username, remote_project_id);
6173                        let remote_project = Project::remote(
6174                            remote_project_id,
6175                            client.client.clone(),
6176                            client.user_store.clone(),
6177                            client.language_registry.clone(),
6178                            FakeFs::new(cx.background()),
6179                            cx.to_async(),
6180                        )
6181                        .await?;
6182                        client.remote_projects.push(remote_project.clone());
6183                        remote_project
6184                    };
6185
6186                    remote_project
6187                } else {
6188                    client
6189                        .remote_projects
6190                        .choose(&mut *rng.lock())
6191                        .unwrap()
6192                        .clone()
6193                }
6194            };
6195            if let Err(error) = active_call
6196                .update(cx, |call, cx| call.share_project(project.clone(), cx))
6197                .await
6198            {
6199                log::error!("{}: error sharing project, {:?}", username, error);
6200            }
6201
6202            let buffers = client.buffers.entry(project.clone()).or_default();
6203            let buffer = if buffers.is_empty() || rng.lock().gen() {
6204                let worktree = if let Some(worktree) = project.read_with(cx, |project, cx| {
6205                    project
6206                        .worktrees(cx)
6207                        .filter(|worktree| {
6208                            let worktree = worktree.read(cx);
6209                            worktree.is_visible() && worktree.entries(false).any(|e| e.is_file())
6210                        })
6211                        .choose(&mut *rng.lock())
6212                }) {
6213                    worktree
6214                } else {
6215                    cx.background().simulate_random_delay().await;
6216                    return Ok(());
6217                };
6218
6219                let (worktree_root_name, project_path) = worktree.read_with(cx, |worktree, _| {
6220                    let entry = worktree
6221                        .entries(false)
6222                        .filter(|e| e.is_file())
6223                        .choose(&mut *rng.lock())
6224                        .unwrap();
6225                    (
6226                        worktree.root_name().to_string(),
6227                        (worktree.id(), entry.path.clone()),
6228                    )
6229                });
6230                log::info!(
6231                    "{}: opening path {:?} in worktree {} ({})",
6232                    username,
6233                    project_path.1,
6234                    project_path.0,
6235                    worktree_root_name,
6236                );
6237                let buffer = project
6238                    .update(cx, |project, cx| {
6239                        project.open_buffer(project_path.clone(), cx)
6240                    })
6241                    .await?;
6242                log::info!(
6243                    "{}: opened path {:?} in worktree {} ({}) with buffer id {}",
6244                    username,
6245                    project_path.1,
6246                    project_path.0,
6247                    worktree_root_name,
6248                    buffer.read_with(cx, |buffer, _| buffer.remote_id())
6249                );
6250                buffers.insert(buffer.clone());
6251                buffer
6252            } else {
6253                buffers.iter().choose(&mut *rng.lock()).unwrap().clone()
6254            };
6255
6256            let choice = rng.lock().gen_range(0..100);
6257            match choice {
6258                0..=9 => {
6259                    cx.update(|cx| {
6260                        log::info!(
6261                            "{}: dropping buffer {:?}",
6262                            username,
6263                            buffer.read(cx).file().unwrap().full_path(cx)
6264                        );
6265                        buffers.remove(&buffer);
6266                        drop(buffer);
6267                    });
6268                }
6269                10..=19 => {
6270                    let completions = project.update(cx, |project, cx| {
6271                        log::info!(
6272                            "{}: requesting completions for buffer {} ({:?})",
6273                            username,
6274                            buffer.read(cx).remote_id(),
6275                            buffer.read(cx).file().unwrap().full_path(cx)
6276                        );
6277                        let offset = rng.lock().gen_range(0..=buffer.read(cx).len());
6278                        project.completions(&buffer, offset, cx)
6279                    });
6280                    let completions = cx.background().spawn(async move {
6281                        completions
6282                            .await
6283                            .map_err(|err| anyhow!("completions request failed: {:?}", err))
6284                    });
6285                    if rng.lock().gen_bool(0.3) {
6286                        log::info!("{}: detaching completions request", username);
6287                        cx.update(|cx| completions.detach_and_log_err(cx));
6288                    } else {
6289                        completions.await?;
6290                    }
6291                }
6292                20..=29 => {
6293                    let code_actions = project.update(cx, |project, cx| {
6294                        log::info!(
6295                            "{}: requesting code actions for buffer {} ({:?})",
6296                            username,
6297                            buffer.read(cx).remote_id(),
6298                            buffer.read(cx).file().unwrap().full_path(cx)
6299                        );
6300                        let range = buffer.read(cx).random_byte_range(0, &mut *rng.lock());
6301                        project.code_actions(&buffer, range, cx)
6302                    });
6303                    let code_actions = cx.background().spawn(async move {
6304                        code_actions
6305                            .await
6306                            .map_err(|err| anyhow!("code actions request failed: {:?}", err))
6307                    });
6308                    if rng.lock().gen_bool(0.3) {
6309                        log::info!("{}: detaching code actions request", username);
6310                        cx.update(|cx| code_actions.detach_and_log_err(cx));
6311                    } else {
6312                        code_actions.await?;
6313                    }
6314                }
6315                30..=39 if buffer.read_with(cx, |buffer, _| buffer.is_dirty()) => {
6316                    let (requested_version, save) = buffer.update(cx, |buffer, cx| {
6317                        log::info!(
6318                            "{}: saving buffer {} ({:?})",
6319                            username,
6320                            buffer.remote_id(),
6321                            buffer.file().unwrap().full_path(cx)
6322                        );
6323                        (buffer.version(), buffer.save(cx))
6324                    });
6325                    let save = cx.background().spawn(async move {
6326                        let (saved_version, _, _) = save
6327                            .await
6328                            .map_err(|err| anyhow!("save request failed: {:?}", err))?;
6329                        assert!(saved_version.observed_all(&requested_version));
6330                        Ok::<_, anyhow::Error>(())
6331                    });
6332                    if rng.lock().gen_bool(0.3) {
6333                        log::info!("{}: detaching save request", username);
6334                        cx.update(|cx| save.detach_and_log_err(cx));
6335                    } else {
6336                        save.await?;
6337                    }
6338                }
6339                40..=44 => {
6340                    let prepare_rename = project.update(cx, |project, cx| {
6341                        log::info!(
6342                            "{}: preparing rename for buffer {} ({:?})",
6343                            username,
6344                            buffer.read(cx).remote_id(),
6345                            buffer.read(cx).file().unwrap().full_path(cx)
6346                        );
6347                        let offset = rng.lock().gen_range(0..=buffer.read(cx).len());
6348                        project.prepare_rename(buffer, offset, cx)
6349                    });
6350                    let prepare_rename = cx.background().spawn(async move {
6351                        prepare_rename
6352                            .await
6353                            .map_err(|err| anyhow!("prepare rename request failed: {:?}", err))
6354                    });
6355                    if rng.lock().gen_bool(0.3) {
6356                        log::info!("{}: detaching prepare rename request", username);
6357                        cx.update(|cx| prepare_rename.detach_and_log_err(cx));
6358                    } else {
6359                        prepare_rename.await?;
6360                    }
6361                }
6362                45..=49 => {
6363                    let definitions = project.update(cx, |project, cx| {
6364                        log::info!(
6365                            "{}: requesting definitions for buffer {} ({:?})",
6366                            username,
6367                            buffer.read(cx).remote_id(),
6368                            buffer.read(cx).file().unwrap().full_path(cx)
6369                        );
6370                        let offset = rng.lock().gen_range(0..=buffer.read(cx).len());
6371                        project.definition(&buffer, offset, cx)
6372                    });
6373                    let definitions = cx.background().spawn(async move {
6374                        definitions
6375                            .await
6376                            .map_err(|err| anyhow!("definitions request failed: {:?}", err))
6377                    });
6378                    if rng.lock().gen_bool(0.3) {
6379                        log::info!("{}: detaching definitions request", username);
6380                        cx.update(|cx| definitions.detach_and_log_err(cx));
6381                    } else {
6382                        buffers.extend(definitions.await?.into_iter().map(|loc| loc.target.buffer));
6383                    }
6384                }
6385                50..=54 => {
6386                    let highlights = project.update(cx, |project, cx| {
6387                        log::info!(
6388                            "{}: requesting highlights for buffer {} ({:?})",
6389                            username,
6390                            buffer.read(cx).remote_id(),
6391                            buffer.read(cx).file().unwrap().full_path(cx)
6392                        );
6393                        let offset = rng.lock().gen_range(0..=buffer.read(cx).len());
6394                        project.document_highlights(&buffer, offset, cx)
6395                    });
6396                    let highlights = cx.background().spawn(async move {
6397                        highlights
6398                            .await
6399                            .map_err(|err| anyhow!("highlights request failed: {:?}", err))
6400                    });
6401                    if rng.lock().gen_bool(0.3) {
6402                        log::info!("{}: detaching highlights request", username);
6403                        cx.update(|cx| highlights.detach_and_log_err(cx));
6404                    } else {
6405                        highlights.await?;
6406                    }
6407                }
6408                55..=59 => {
6409                    let search = project.update(cx, |project, cx| {
6410                        let query = rng.lock().gen_range('a'..='z');
6411                        log::info!("{}: project-wide search {:?}", username, query);
6412                        project.search(SearchQuery::text(query, false, false), cx)
6413                    });
6414                    let search = cx.background().spawn(async move {
6415                        search
6416                            .await
6417                            .map_err(|err| anyhow!("search request failed: {:?}", err))
6418                    });
6419                    if rng.lock().gen_bool(0.3) {
6420                        log::info!("{}: detaching search request", username);
6421                        cx.update(|cx| search.detach_and_log_err(cx));
6422                    } else {
6423                        buffers.extend(search.await?.into_keys());
6424                    }
6425                }
6426                60..=69 => {
6427                    let worktree = project
6428                        .read_with(cx, |project, cx| {
6429                            project
6430                                .worktrees(cx)
6431                                .filter(|worktree| {
6432                                    let worktree = worktree.read(cx);
6433                                    worktree.is_visible()
6434                                        && worktree.entries(false).any(|e| e.is_file())
6435                                        && worktree.root_entry().map_or(false, |e| e.is_dir())
6436                                })
6437                                .choose(&mut *rng.lock())
6438                        })
6439                        .unwrap();
6440                    let (worktree_id, worktree_root_name) = worktree
6441                        .read_with(cx, |worktree, _| {
6442                            (worktree.id(), worktree.root_name().to_string())
6443                        });
6444
6445                    let mut new_name = String::new();
6446                    for _ in 0..10 {
6447                        let letter = rng.lock().gen_range('a'..='z');
6448                        new_name.push(letter);
6449                    }
6450
6451                    let is_dir = rng.lock().gen::<bool>();
6452                    let mut new_path = PathBuf::new();
6453                    new_path.push(new_name);
6454                    if !is_dir {
6455                        new_path.set_extension("rs");
6456                    }
6457                    log::info!(
6458                        "{}: creating {:?} in worktree {} ({})",
6459                        username,
6460                        new_path,
6461                        worktree_id,
6462                        worktree_root_name,
6463                    );
6464                    project
6465                        .update(cx, |project, cx| {
6466                            project.create_entry((worktree_id, new_path), is_dir, cx)
6467                        })
6468                        .unwrap()
6469                        .await?;
6470                }
6471                _ => {
6472                    buffer.update(cx, |buffer, cx| {
6473                        log::info!(
6474                            "{}: updating buffer {} ({:?})",
6475                            username,
6476                            buffer.remote_id(),
6477                            buffer.file().unwrap().full_path(cx)
6478                        );
6479                        if rng.lock().gen_bool(0.7) {
6480                            buffer.randomly_edit(&mut *rng.lock(), 5, cx);
6481                        } else {
6482                            buffer.randomly_undo_redo(&mut *rng.lock(), cx);
6483                        }
6484                    });
6485                }
6486            }
6487
6488            Ok(())
6489        }
6490
6491        // Setup language server
6492        let mut language = Language::new(
6493            LanguageConfig {
6494                name: "Rust".into(),
6495                path_suffixes: vec!["rs".to_string()],
6496                ..Default::default()
6497            },
6498            None,
6499        );
6500        let _fake_language_servers = language
6501            .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
6502                name: "the-fake-language-server",
6503                capabilities: lsp::LanguageServer::full_capabilities(),
6504                initializer: Some(Box::new({
6505                    let rng = rng.clone();
6506                    let fs = self.fs.clone();
6507                    move |fake_server: &mut FakeLanguageServer| {
6508                        fake_server.handle_request::<lsp::request::Completion, _, _>(
6509                            |_, _| async move {
6510                                Ok(Some(lsp::CompletionResponse::Array(vec![
6511                                    lsp::CompletionItem {
6512                                        text_edit: Some(lsp::CompletionTextEdit::Edit(
6513                                            lsp::TextEdit {
6514                                                range: lsp::Range::new(
6515                                                    lsp::Position::new(0, 0),
6516                                                    lsp::Position::new(0, 0),
6517                                                ),
6518                                                new_text: "the-new-text".to_string(),
6519                                            },
6520                                        )),
6521                                        ..Default::default()
6522                                    },
6523                                ])))
6524                            },
6525                        );
6526
6527                        fake_server.handle_request::<lsp::request::CodeActionRequest, _, _>(
6528                            |_, _| async move {
6529                                Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
6530                                    lsp::CodeAction {
6531                                        title: "the-code-action".to_string(),
6532                                        ..Default::default()
6533                                    },
6534                                )]))
6535                            },
6536                        );
6537
6538                        fake_server.handle_request::<lsp::request::PrepareRenameRequest, _, _>(
6539                            |params, _| async move {
6540                                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
6541                                    params.position,
6542                                    params.position,
6543                                ))))
6544                            },
6545                        );
6546
6547                        fake_server.handle_request::<lsp::request::GotoDefinition, _, _>({
6548                            let fs = fs.clone();
6549                            let rng = rng.clone();
6550                            move |_, _| {
6551                                let fs = fs.clone();
6552                                let rng = rng.clone();
6553                                async move {
6554                                    let files = fs.files().await;
6555                                    let mut rng = rng.lock();
6556                                    let count = rng.gen_range::<usize, _>(1..3);
6557                                    let files = (0..count)
6558                                        .map(|_| files.choose(&mut *rng).unwrap())
6559                                        .collect::<Vec<_>>();
6560                                    log::info!("LSP: Returning definitions in files {:?}", &files);
6561                                    Ok(Some(lsp::GotoDefinitionResponse::Array(
6562                                        files
6563                                            .into_iter()
6564                                            .map(|file| lsp::Location {
6565                                                uri: lsp::Url::from_file_path(file).unwrap(),
6566                                                range: Default::default(),
6567                                            })
6568                                            .collect(),
6569                                    )))
6570                                }
6571                            }
6572                        });
6573
6574                        fake_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
6575                            {
6576                                let rng = rng.clone();
6577                                move |_, _| {
6578                                    let mut highlights = Vec::new();
6579                                    let highlight_count = rng.lock().gen_range(1..=5);
6580                                    for _ in 0..highlight_count {
6581                                        let start_row = rng.lock().gen_range(0..100);
6582                                        let start_column = rng.lock().gen_range(0..100);
6583                                        let start = PointUtf16::new(start_row, start_column);
6584                                        let end_row = rng.lock().gen_range(0..100);
6585                                        let end_column = rng.lock().gen_range(0..100);
6586                                        let end = PointUtf16::new(end_row, end_column);
6587                                        let range =
6588                                            if start > end { end..start } else { start..end };
6589                                        highlights.push(lsp::DocumentHighlight {
6590                                            range: range_to_lsp(range.clone()),
6591                                            kind: Some(lsp::DocumentHighlightKind::READ),
6592                                        });
6593                                    }
6594                                    highlights.sort_unstable_by_key(|highlight| {
6595                                        (highlight.range.start, highlight.range.end)
6596                                    });
6597                                    async move { Ok(Some(highlights)) }
6598                                }
6599                            },
6600                        );
6601                    }
6602                })),
6603                ..Default::default()
6604            }))
6605            .await;
6606        self.language_registry.add(Arc::new(language));
6607
6608        while op_start_signal.next().await.is_some() {
6609            if let Err(error) = tick(&mut self, &username, rng.clone(), &mut cx).await {
6610                log::error!("{} error: {:?}", username, error);
6611            }
6612
6613            cx.background().simulate_random_delay().await;
6614        }
6615        log::info!("{}: done", username);
6616
6617        (self, cx)
6618    }
6619}
6620
6621impl Drop for TestClient {
6622    fn drop(&mut self) {
6623        self.client.tear_down();
6624    }
6625}
6626
6627impl Executor for Arc<gpui::executor::Background> {
6628    type Sleep = gpui::executor::Timer;
6629
6630    fn spawn_detached<F: 'static + Send + Future<Output = ()>>(&self, future: F) {
6631        self.spawn(future).detach();
6632    }
6633
6634    fn sleep(&self, duration: Duration) -> Self::Sleep {
6635        self.as_ref().timer(duration)
6636    }
6637}
6638
6639#[derive(Debug, Eq, PartialEq)]
6640struct RoomParticipants {
6641    remote: Vec<String>,
6642    pending: Vec<String>,
6643}
6644
6645fn room_participants(room: &ModelHandle<Room>, cx: &mut TestAppContext) -> RoomParticipants {
6646    room.read_with(cx, |room, _| RoomParticipants {
6647        remote: room
6648            .remote_participants()
6649            .iter()
6650            .map(|(_, participant)| participant.user.github_login.clone())
6651            .collect(),
6652        pending: room
6653            .pending_participants()
6654            .iter()
6655            .map(|user| user.github_login.clone())
6656            .collect(),
6657    })
6658}