integration_tests.rs

   1use crate::{
   2    rpc::{CLEANUP_TIMEOUT, RECONNECT_TIMEOUT},
   3    tests::{room_participants, RoomParticipants, TestClient, TestServer},
   4};
   5use call::{room, ActiveCall, ParticipantLocation, Room};
   6use client::{User, RECEIVE_TIMEOUT};
   7use collections::{HashMap, HashSet};
   8use editor::{
   9    test::editor_test_context::EditorTestContext, ConfirmCodeAction, ConfirmCompletion,
  10    ConfirmRename, Editor, ExcerptRange, MultiBuffer, Redo, Rename, ToggleCodeActions, Undo,
  11};
  12use fs::{repository::GitFileStatus, FakeFs, Fs as _, RemoveOptions};
  13use futures::StreamExt as _;
  14use gpui::{
  15    executor::Deterministic, geometry::vector::vec2f, test::EmptyView, AppContext, ModelHandle,
  16    TestAppContext, ViewHandle,
  17};
  18use indoc::indoc;
  19use language::{
  20    language_settings::{AllLanguageSettings, Formatter, InlayHintSettings},
  21    tree_sitter_rust, Anchor, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language,
  22    LanguageConfig, LineEnding, OffsetRangeExt, Point, Rope,
  23};
  24use live_kit_client::MacOSDisplay;
  25use lsp::LanguageServerId;
  26use project::{search::SearchQuery, DiagnosticSummary, HoverBlockKind, Project, ProjectPath};
  27use rand::prelude::*;
  28use serde_json::json;
  29use settings::SettingsStore;
  30use std::{
  31    cell::{Cell, RefCell},
  32    env, future, mem,
  33    path::{Path, PathBuf},
  34    rc::Rc,
  35    sync::{
  36        atomic::{self, AtomicBool, AtomicUsize, Ordering::SeqCst},
  37        Arc,
  38    },
  39};
  40use unindent::Unindent as _;
  41use workspace::{
  42    dock::{test::TestPanel, DockPosition},
  43    item::{test::TestItem, ItemHandle as _},
  44    shared_screen::SharedScreen,
  45    SplitDirection, Workspace,
  46};
  47
  48#[ctor::ctor]
  49fn init_logger() {
  50    if std::env::var("RUST_LOG").is_ok() {
  51        env_logger::init();
  52    }
  53}
  54
  55#[gpui::test(iterations = 10)]
  56async fn test_basic_calls(
  57    deterministic: Arc<Deterministic>,
  58    cx_a: &mut TestAppContext,
  59    cx_b: &mut TestAppContext,
  60    cx_b2: &mut TestAppContext,
  61    cx_c: &mut TestAppContext,
  62) {
  63    deterministic.forbid_parking();
  64    let mut server = TestServer::start(&deterministic).await;
  65
  66    let client_a = server.create_client(cx_a, "user_a").await;
  67    let client_b = server.create_client(cx_b, "user_b").await;
  68    let client_c = server.create_client(cx_c, "user_c").await;
  69    server
  70        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
  71        .await;
  72
  73    let active_call_a = cx_a.read(ActiveCall::global);
  74    let active_call_b = cx_b.read(ActiveCall::global);
  75    let active_call_c = cx_c.read(ActiveCall::global);
  76
  77    // Call user B from client A.
  78    active_call_a
  79        .update(cx_a, |call, cx| {
  80            call.invite(client_b.user_id().unwrap(), None, cx)
  81        })
  82        .await
  83        .unwrap();
  84    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
  85    deterministic.run_until_parked();
  86    assert_eq!(
  87        room_participants(&room_a, cx_a),
  88        RoomParticipants {
  89            remote: Default::default(),
  90            pending: vec!["user_b".to_string()]
  91        }
  92    );
  93
  94    // User B receives the call.
  95    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
  96    let call_b = incoming_call_b.next().await.unwrap().unwrap();
  97    assert_eq!(call_b.calling_user.github_login, "user_a");
  98
  99    // User B connects via another client and also receives a ring on the newly-connected client.
 100    let _client_b2 = server.create_client(cx_b2, "user_b").await;
 101    let active_call_b2 = cx_b2.read(ActiveCall::global);
 102    let mut incoming_call_b2 = active_call_b2.read_with(cx_b2, |call, _| call.incoming());
 103    deterministic.run_until_parked();
 104    let call_b2 = incoming_call_b2.next().await.unwrap().unwrap();
 105    assert_eq!(call_b2.calling_user.github_login, "user_a");
 106
 107    // User B joins the room using the first client.
 108    active_call_b
 109        .update(cx_b, |call, cx| call.accept_incoming(cx))
 110        .await
 111        .unwrap();
 112    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 113    assert!(incoming_call_b.next().await.unwrap().is_none());
 114
 115    deterministic.run_until_parked();
 116    assert_eq!(
 117        room_participants(&room_a, cx_a),
 118        RoomParticipants {
 119            remote: vec!["user_b".to_string()],
 120            pending: Default::default()
 121        }
 122    );
 123    assert_eq!(
 124        room_participants(&room_b, cx_b),
 125        RoomParticipants {
 126            remote: vec!["user_a".to_string()],
 127            pending: Default::default()
 128        }
 129    );
 130
 131    // Call user C from client B.
 132    let mut incoming_call_c = active_call_c.read_with(cx_c, |call, _| call.incoming());
 133    active_call_b
 134        .update(cx_b, |call, cx| {
 135            call.invite(client_c.user_id().unwrap(), None, cx)
 136        })
 137        .await
 138        .unwrap();
 139
 140    deterministic.run_until_parked();
 141    assert_eq!(
 142        room_participants(&room_a, cx_a),
 143        RoomParticipants {
 144            remote: vec!["user_b".to_string()],
 145            pending: vec!["user_c".to_string()]
 146        }
 147    );
 148    assert_eq!(
 149        room_participants(&room_b, cx_b),
 150        RoomParticipants {
 151            remote: vec!["user_a".to_string()],
 152            pending: vec!["user_c".to_string()]
 153        }
 154    );
 155
 156    // User C receives the call, but declines it.
 157    let call_c = incoming_call_c.next().await.unwrap().unwrap();
 158    assert_eq!(call_c.calling_user.github_login, "user_b");
 159    active_call_c.update(cx_c, |call, cx| call.decline_incoming(cx).unwrap());
 160    assert!(incoming_call_c.next().await.unwrap().is_none());
 161
 162    deterministic.run_until_parked();
 163    assert_eq!(
 164        room_participants(&room_a, cx_a),
 165        RoomParticipants {
 166            remote: vec!["user_b".to_string()],
 167            pending: Default::default()
 168        }
 169    );
 170    assert_eq!(
 171        room_participants(&room_b, cx_b),
 172        RoomParticipants {
 173            remote: vec!["user_a".to_string()],
 174            pending: Default::default()
 175        }
 176    );
 177
 178    // Call user C again from user A.
 179    active_call_a
 180        .update(cx_a, |call, cx| {
 181            call.invite(client_c.user_id().unwrap(), None, cx)
 182        })
 183        .await
 184        .unwrap();
 185
 186    deterministic.run_until_parked();
 187    assert_eq!(
 188        room_participants(&room_a, cx_a),
 189        RoomParticipants {
 190            remote: vec!["user_b".to_string()],
 191            pending: vec!["user_c".to_string()]
 192        }
 193    );
 194    assert_eq!(
 195        room_participants(&room_b, cx_b),
 196        RoomParticipants {
 197            remote: vec!["user_a".to_string()],
 198            pending: vec!["user_c".to_string()]
 199        }
 200    );
 201
 202    // User C accepts the call.
 203    let call_c = incoming_call_c.next().await.unwrap().unwrap();
 204    assert_eq!(call_c.calling_user.github_login, "user_a");
 205    active_call_c
 206        .update(cx_c, |call, cx| call.accept_incoming(cx))
 207        .await
 208        .unwrap();
 209    assert!(incoming_call_c.next().await.unwrap().is_none());
 210    let room_c = active_call_c.read_with(cx_c, |call, _| call.room().unwrap().clone());
 211
 212    deterministic.run_until_parked();
 213    assert_eq!(
 214        room_participants(&room_a, cx_a),
 215        RoomParticipants {
 216            remote: vec!["user_b".to_string(), "user_c".to_string()],
 217            pending: Default::default()
 218        }
 219    );
 220    assert_eq!(
 221        room_participants(&room_b, cx_b),
 222        RoomParticipants {
 223            remote: vec!["user_a".to_string(), "user_c".to_string()],
 224            pending: Default::default()
 225        }
 226    );
 227    assert_eq!(
 228        room_participants(&room_c, cx_c),
 229        RoomParticipants {
 230            remote: vec!["user_a".to_string(), "user_b".to_string()],
 231            pending: Default::default()
 232        }
 233    );
 234
 235    // User A shares their screen
 236    let display = MacOSDisplay::new();
 237    let events_b = active_call_events(cx_b);
 238    let events_c = active_call_events(cx_c);
 239    active_call_a
 240        .update(cx_a, |call, cx| {
 241            call.room().unwrap().update(cx, |room, cx| {
 242                room.set_display_sources(vec![display.clone()]);
 243                room.share_screen(cx)
 244            })
 245        })
 246        .await
 247        .unwrap();
 248
 249    deterministic.run_until_parked();
 250
 251    // User B observes the remote screen sharing track.
 252    assert_eq!(events_b.borrow().len(), 1);
 253    let event_b = events_b.borrow().first().unwrap().clone();
 254    if let call::room::Event::RemoteVideoTracksChanged { participant_id } = event_b {
 255        assert_eq!(participant_id, client_a.peer_id().unwrap());
 256        room_b.read_with(cx_b, |room, _| {
 257            assert_eq!(
 258                room.remote_participants()[&client_a.user_id().unwrap()]
 259                    .video_tracks
 260                    .len(),
 261                1
 262            );
 263        });
 264    } else {
 265        panic!("unexpected event")
 266    }
 267
 268    // User C observes the remote screen sharing track.
 269    assert_eq!(events_c.borrow().len(), 1);
 270    let event_c = events_c.borrow().first().unwrap().clone();
 271    if let call::room::Event::RemoteVideoTracksChanged { participant_id } = event_c {
 272        assert_eq!(participant_id, client_a.peer_id().unwrap());
 273        room_c.read_with(cx_c, |room, _| {
 274            assert_eq!(
 275                room.remote_participants()[&client_a.user_id().unwrap()]
 276                    .video_tracks
 277                    .len(),
 278                1
 279            );
 280        });
 281    } else {
 282        panic!("unexpected event")
 283    }
 284
 285    // User A leaves the room.
 286    active_call_a
 287        .update(cx_a, |call, cx| {
 288            let hang_up = call.hang_up(cx);
 289            assert!(call.room().is_none());
 290            hang_up
 291        })
 292        .await
 293        .unwrap();
 294    deterministic.run_until_parked();
 295    assert_eq!(
 296        room_participants(&room_a, cx_a),
 297        RoomParticipants {
 298            remote: Default::default(),
 299            pending: Default::default()
 300        }
 301    );
 302    assert_eq!(
 303        room_participants(&room_b, cx_b),
 304        RoomParticipants {
 305            remote: vec!["user_c".to_string()],
 306            pending: Default::default()
 307        }
 308    );
 309    assert_eq!(
 310        room_participants(&room_c, cx_c),
 311        RoomParticipants {
 312            remote: vec!["user_b".to_string()],
 313            pending: Default::default()
 314        }
 315    );
 316
 317    // User B gets disconnected from the LiveKit server, which causes them
 318    // to automatically leave the room. User C leaves the room as well because
 319    // nobody else is in there.
 320    server
 321        .test_live_kit_server
 322        .disconnect_client(client_b.user_id().unwrap().to_string())
 323        .await;
 324    deterministic.run_until_parked();
 325    active_call_b.read_with(cx_b, |call, _| assert!(call.room().is_none()));
 326    active_call_c.read_with(cx_c, |call, _| assert!(call.room().is_none()));
 327    assert_eq!(
 328        room_participants(&room_a, cx_a),
 329        RoomParticipants {
 330            remote: Default::default(),
 331            pending: Default::default()
 332        }
 333    );
 334    assert_eq!(
 335        room_participants(&room_b, cx_b),
 336        RoomParticipants {
 337            remote: Default::default(),
 338            pending: Default::default()
 339        }
 340    );
 341    assert_eq!(
 342        room_participants(&room_c, cx_c),
 343        RoomParticipants {
 344            remote: Default::default(),
 345            pending: Default::default()
 346        }
 347    );
 348}
 349
 350#[gpui::test(iterations = 10)]
 351async fn test_calling_multiple_users_simultaneously(
 352    deterministic: Arc<Deterministic>,
 353    cx_a: &mut TestAppContext,
 354    cx_b: &mut TestAppContext,
 355    cx_c: &mut TestAppContext,
 356    cx_d: &mut TestAppContext,
 357) {
 358    deterministic.forbid_parking();
 359    let mut server = TestServer::start(&deterministic).await;
 360
 361    let client_a = server.create_client(cx_a, "user_a").await;
 362    let client_b = server.create_client(cx_b, "user_b").await;
 363    let client_c = server.create_client(cx_c, "user_c").await;
 364    let client_d = server.create_client(cx_d, "user_d").await;
 365    server
 366        .make_contacts(&mut [
 367            (&client_a, cx_a),
 368            (&client_b, cx_b),
 369            (&client_c, cx_c),
 370            (&client_d, cx_d),
 371        ])
 372        .await;
 373
 374    let active_call_a = cx_a.read(ActiveCall::global);
 375    let active_call_b = cx_b.read(ActiveCall::global);
 376    let active_call_c = cx_c.read(ActiveCall::global);
 377    let active_call_d = cx_d.read(ActiveCall::global);
 378
 379    // Simultaneously call user B and user C from client A.
 380    let b_invite = active_call_a.update(cx_a, |call, cx| {
 381        call.invite(client_b.user_id().unwrap(), None, cx)
 382    });
 383    let c_invite = active_call_a.update(cx_a, |call, cx| {
 384        call.invite(client_c.user_id().unwrap(), None, cx)
 385    });
 386    b_invite.await.unwrap();
 387    c_invite.await.unwrap();
 388
 389    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 390    deterministic.run_until_parked();
 391    assert_eq!(
 392        room_participants(&room_a, cx_a),
 393        RoomParticipants {
 394            remote: Default::default(),
 395            pending: vec!["user_b".to_string(), "user_c".to_string()]
 396        }
 397    );
 398
 399    // Call client D from client A.
 400    active_call_a
 401        .update(cx_a, |call, cx| {
 402            call.invite(client_d.user_id().unwrap(), None, cx)
 403        })
 404        .await
 405        .unwrap();
 406    deterministic.run_until_parked();
 407    assert_eq!(
 408        room_participants(&room_a, cx_a),
 409        RoomParticipants {
 410            remote: Default::default(),
 411            pending: vec![
 412                "user_b".to_string(),
 413                "user_c".to_string(),
 414                "user_d".to_string()
 415            ]
 416        }
 417    );
 418
 419    // Accept the call on all clients simultaneously.
 420    let accept_b = active_call_b.update(cx_b, |call, cx| call.accept_incoming(cx));
 421    let accept_c = active_call_c.update(cx_c, |call, cx| call.accept_incoming(cx));
 422    let accept_d = active_call_d.update(cx_d, |call, cx| call.accept_incoming(cx));
 423    accept_b.await.unwrap();
 424    accept_c.await.unwrap();
 425    accept_d.await.unwrap();
 426
 427    deterministic.run_until_parked();
 428
 429    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 430    let room_c = active_call_c.read_with(cx_c, |call, _| call.room().unwrap().clone());
 431    let room_d = active_call_d.read_with(cx_d, |call, _| call.room().unwrap().clone());
 432    assert_eq!(
 433        room_participants(&room_a, cx_a),
 434        RoomParticipants {
 435            remote: vec![
 436                "user_b".to_string(),
 437                "user_c".to_string(),
 438                "user_d".to_string(),
 439            ],
 440            pending: Default::default()
 441        }
 442    );
 443    assert_eq!(
 444        room_participants(&room_b, cx_b),
 445        RoomParticipants {
 446            remote: vec![
 447                "user_a".to_string(),
 448                "user_c".to_string(),
 449                "user_d".to_string(),
 450            ],
 451            pending: Default::default()
 452        }
 453    );
 454    assert_eq!(
 455        room_participants(&room_c, cx_c),
 456        RoomParticipants {
 457            remote: vec![
 458                "user_a".to_string(),
 459                "user_b".to_string(),
 460                "user_d".to_string(),
 461            ],
 462            pending: Default::default()
 463        }
 464    );
 465    assert_eq!(
 466        room_participants(&room_d, cx_d),
 467        RoomParticipants {
 468            remote: vec![
 469                "user_a".to_string(),
 470                "user_b".to_string(),
 471                "user_c".to_string(),
 472            ],
 473            pending: Default::default()
 474        }
 475    );
 476}
 477
 478#[gpui::test(iterations = 10)]
 479async fn test_room_uniqueness(
 480    deterministic: Arc<Deterministic>,
 481    cx_a: &mut TestAppContext,
 482    cx_a2: &mut TestAppContext,
 483    cx_b: &mut TestAppContext,
 484    cx_b2: &mut TestAppContext,
 485    cx_c: &mut TestAppContext,
 486) {
 487    deterministic.forbid_parking();
 488    let mut server = TestServer::start(&deterministic).await;
 489    let client_a = server.create_client(cx_a, "user_a").await;
 490    let _client_a2 = server.create_client(cx_a2, "user_a").await;
 491    let client_b = server.create_client(cx_b, "user_b").await;
 492    let _client_b2 = server.create_client(cx_b2, "user_b").await;
 493    let client_c = server.create_client(cx_c, "user_c").await;
 494    server
 495        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
 496        .await;
 497
 498    let active_call_a = cx_a.read(ActiveCall::global);
 499    let active_call_a2 = cx_a2.read(ActiveCall::global);
 500    let active_call_b = cx_b.read(ActiveCall::global);
 501    let active_call_b2 = cx_b2.read(ActiveCall::global);
 502    let active_call_c = cx_c.read(ActiveCall::global);
 503
 504    // Call user B from client A.
 505    active_call_a
 506        .update(cx_a, |call, cx| {
 507            call.invite(client_b.user_id().unwrap(), None, cx)
 508        })
 509        .await
 510        .unwrap();
 511
 512    // Ensure a new room can't be created given user A just created one.
 513    active_call_a2
 514        .update(cx_a2, |call, cx| {
 515            call.invite(client_c.user_id().unwrap(), None, cx)
 516        })
 517        .await
 518        .unwrap_err();
 519    active_call_a2.read_with(cx_a2, |call, _| assert!(call.room().is_none()));
 520
 521    // User B receives the call from user A.
 522    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
 523    let call_b1 = incoming_call_b.next().await.unwrap().unwrap();
 524    assert_eq!(call_b1.calling_user.github_login, "user_a");
 525
 526    // Ensure calling users A and B from client C fails.
 527    active_call_c
 528        .update(cx_c, |call, cx| {
 529            call.invite(client_a.user_id().unwrap(), None, cx)
 530        })
 531        .await
 532        .unwrap_err();
 533    active_call_c
 534        .update(cx_c, |call, cx| {
 535            call.invite(client_b.user_id().unwrap(), None, cx)
 536        })
 537        .await
 538        .unwrap_err();
 539
 540    // Ensure User B can't create a room while they still have an incoming call.
 541    active_call_b2
 542        .update(cx_b2, |call, cx| {
 543            call.invite(client_c.user_id().unwrap(), None, cx)
 544        })
 545        .await
 546        .unwrap_err();
 547    active_call_b2.read_with(cx_b2, |call, _| assert!(call.room().is_none()));
 548
 549    // User B joins the room and calling them after they've joined still fails.
 550    active_call_b
 551        .update(cx_b, |call, cx| call.accept_incoming(cx))
 552        .await
 553        .unwrap();
 554    active_call_c
 555        .update(cx_c, |call, cx| {
 556            call.invite(client_b.user_id().unwrap(), None, cx)
 557        })
 558        .await
 559        .unwrap_err();
 560
 561    // Ensure User B can't create a room while they belong to another room.
 562    active_call_b2
 563        .update(cx_b2, |call, cx| {
 564            call.invite(client_c.user_id().unwrap(), None, cx)
 565        })
 566        .await
 567        .unwrap_err();
 568    active_call_b2.read_with(cx_b2, |call, _| assert!(call.room().is_none()));
 569
 570    // Client C can successfully call client B after client B leaves the room.
 571    active_call_b
 572        .update(cx_b, |call, cx| call.hang_up(cx))
 573        .await
 574        .unwrap();
 575    deterministic.run_until_parked();
 576    active_call_c
 577        .update(cx_c, |call, cx| {
 578            call.invite(client_b.user_id().unwrap(), None, cx)
 579        })
 580        .await
 581        .unwrap();
 582    deterministic.run_until_parked();
 583    let call_b2 = incoming_call_b.next().await.unwrap().unwrap();
 584    assert_eq!(call_b2.calling_user.github_login, "user_c");
 585}
 586
 587#[gpui::test(iterations = 10)]
 588async fn test_client_disconnecting_from_room(
 589    deterministic: Arc<Deterministic>,
 590    cx_a: &mut TestAppContext,
 591    cx_b: &mut TestAppContext,
 592) {
 593    deterministic.forbid_parking();
 594    let mut server = TestServer::start(&deterministic).await;
 595    let client_a = server.create_client(cx_a, "user_a").await;
 596    let client_b = server.create_client(cx_b, "user_b").await;
 597    server
 598        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
 599        .await;
 600
 601    let active_call_a = cx_a.read(ActiveCall::global);
 602    let active_call_b = cx_b.read(ActiveCall::global);
 603
 604    // Call user B from client A.
 605    active_call_a
 606        .update(cx_a, |call, cx| {
 607            call.invite(client_b.user_id().unwrap(), None, cx)
 608        })
 609        .await
 610        .unwrap();
 611    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 612
 613    // User B receives the call and joins the room.
 614    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
 615    incoming_call_b.next().await.unwrap().unwrap();
 616    active_call_b
 617        .update(cx_b, |call, cx| call.accept_incoming(cx))
 618        .await
 619        .unwrap();
 620    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 621    deterministic.run_until_parked();
 622    assert_eq!(
 623        room_participants(&room_a, cx_a),
 624        RoomParticipants {
 625            remote: vec!["user_b".to_string()],
 626            pending: Default::default()
 627        }
 628    );
 629    assert_eq!(
 630        room_participants(&room_b, cx_b),
 631        RoomParticipants {
 632            remote: vec!["user_a".to_string()],
 633            pending: Default::default()
 634        }
 635    );
 636
 637    // User A automatically reconnects to the room upon disconnection.
 638    server.disconnect_client(client_a.peer_id().unwrap());
 639    deterministic.advance_clock(RECEIVE_TIMEOUT);
 640    deterministic.run_until_parked();
 641    assert_eq!(
 642        room_participants(&room_a, cx_a),
 643        RoomParticipants {
 644            remote: vec!["user_b".to_string()],
 645            pending: Default::default()
 646        }
 647    );
 648    assert_eq!(
 649        room_participants(&room_b, cx_b),
 650        RoomParticipants {
 651            remote: vec!["user_a".to_string()],
 652            pending: Default::default()
 653        }
 654    );
 655
 656    // When user A disconnects, both client A and B clear their room on the active call.
 657    server.forbid_connections();
 658    server.disconnect_client(client_a.peer_id().unwrap());
 659    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
 660    active_call_a.read_with(cx_a, |call, _| assert!(call.room().is_none()));
 661    active_call_b.read_with(cx_b, |call, _| assert!(call.room().is_none()));
 662    assert_eq!(
 663        room_participants(&room_a, cx_a),
 664        RoomParticipants {
 665            remote: Default::default(),
 666            pending: Default::default()
 667        }
 668    );
 669    assert_eq!(
 670        room_participants(&room_b, cx_b),
 671        RoomParticipants {
 672            remote: Default::default(),
 673            pending: Default::default()
 674        }
 675    );
 676
 677    // Allow user A to reconnect to the server.
 678    server.allow_connections();
 679    deterministic.advance_clock(RECEIVE_TIMEOUT);
 680
 681    // Call user B again from client A.
 682    active_call_a
 683        .update(cx_a, |call, cx| {
 684            call.invite(client_b.user_id().unwrap(), None, cx)
 685        })
 686        .await
 687        .unwrap();
 688    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 689
 690    // User B receives the call and joins the room.
 691    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
 692    incoming_call_b.next().await.unwrap().unwrap();
 693    active_call_b
 694        .update(cx_b, |call, cx| call.accept_incoming(cx))
 695        .await
 696        .unwrap();
 697    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 698    deterministic.run_until_parked();
 699    assert_eq!(
 700        room_participants(&room_a, cx_a),
 701        RoomParticipants {
 702            remote: vec!["user_b".to_string()],
 703            pending: Default::default()
 704        }
 705    );
 706    assert_eq!(
 707        room_participants(&room_b, cx_b),
 708        RoomParticipants {
 709            remote: vec!["user_a".to_string()],
 710            pending: Default::default()
 711        }
 712    );
 713
 714    // User B gets disconnected from the LiveKit server, which causes it
 715    // to automatically leave the room.
 716    server
 717        .test_live_kit_server
 718        .disconnect_client(client_b.user_id().unwrap().to_string())
 719        .await;
 720    deterministic.run_until_parked();
 721    active_call_a.update(cx_a, |call, _| assert!(call.room().is_none()));
 722    active_call_b.update(cx_b, |call, _| assert!(call.room().is_none()));
 723    assert_eq!(
 724        room_participants(&room_a, cx_a),
 725        RoomParticipants {
 726            remote: Default::default(),
 727            pending: Default::default()
 728        }
 729    );
 730    assert_eq!(
 731        room_participants(&room_b, cx_b),
 732        RoomParticipants {
 733            remote: Default::default(),
 734            pending: Default::default()
 735        }
 736    );
 737}
 738
 739#[gpui::test(iterations = 10)]
 740async fn test_server_restarts(
 741    deterministic: Arc<Deterministic>,
 742    cx_a: &mut TestAppContext,
 743    cx_b: &mut TestAppContext,
 744    cx_c: &mut TestAppContext,
 745    cx_d: &mut TestAppContext,
 746) {
 747    deterministic.forbid_parking();
 748    let mut server = TestServer::start(&deterministic).await;
 749    let client_a = server.create_client(cx_a, "user_a").await;
 750    client_a
 751        .fs()
 752        .insert_tree("/a", json!({ "a.txt": "a-contents" }))
 753        .await;
 754
 755    // Invite client B to collaborate on a project
 756    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
 757
 758    let client_b = server.create_client(cx_b, "user_b").await;
 759    let client_c = server.create_client(cx_c, "user_c").await;
 760    let client_d = server.create_client(cx_d, "user_d").await;
 761    server
 762        .make_contacts(&mut [
 763            (&client_a, cx_a),
 764            (&client_b, cx_b),
 765            (&client_c, cx_c),
 766            (&client_d, cx_d),
 767        ])
 768        .await;
 769
 770    let active_call_a = cx_a.read(ActiveCall::global);
 771    let active_call_b = cx_b.read(ActiveCall::global);
 772    let active_call_c = cx_c.read(ActiveCall::global);
 773    let active_call_d = cx_d.read(ActiveCall::global);
 774
 775    // User A calls users B, C, and D.
 776    active_call_a
 777        .update(cx_a, |call, cx| {
 778            call.invite(client_b.user_id().unwrap(), Some(project_a.clone()), cx)
 779        })
 780        .await
 781        .unwrap();
 782    active_call_a
 783        .update(cx_a, |call, cx| {
 784            call.invite(client_c.user_id().unwrap(), Some(project_a.clone()), cx)
 785        })
 786        .await
 787        .unwrap();
 788    active_call_a
 789        .update(cx_a, |call, cx| {
 790            call.invite(client_d.user_id().unwrap(), Some(project_a.clone()), cx)
 791        })
 792        .await
 793        .unwrap();
 794    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 795
 796    // User B receives the call and joins the room.
 797    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
 798    assert!(incoming_call_b.next().await.unwrap().is_some());
 799    active_call_b
 800        .update(cx_b, |call, cx| call.accept_incoming(cx))
 801        .await
 802        .unwrap();
 803    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 804
 805    // User C receives the call and joins the room.
 806    let mut incoming_call_c = active_call_c.read_with(cx_c, |call, _| call.incoming());
 807    assert!(incoming_call_c.next().await.unwrap().is_some());
 808    active_call_c
 809        .update(cx_c, |call, cx| call.accept_incoming(cx))
 810        .await
 811        .unwrap();
 812    let room_c = active_call_c.read_with(cx_c, |call, _| call.room().unwrap().clone());
 813
 814    // User D receives the call but doesn't join the room yet.
 815    let mut incoming_call_d = active_call_d.read_with(cx_d, |call, _| call.incoming());
 816    assert!(incoming_call_d.next().await.unwrap().is_some());
 817
 818    deterministic.run_until_parked();
 819    assert_eq!(
 820        room_participants(&room_a, cx_a),
 821        RoomParticipants {
 822            remote: vec!["user_b".to_string(), "user_c".to_string()],
 823            pending: vec!["user_d".to_string()]
 824        }
 825    );
 826    assert_eq!(
 827        room_participants(&room_b, cx_b),
 828        RoomParticipants {
 829            remote: vec!["user_a".to_string(), "user_c".to_string()],
 830            pending: vec!["user_d".to_string()]
 831        }
 832    );
 833    assert_eq!(
 834        room_participants(&room_c, cx_c),
 835        RoomParticipants {
 836            remote: vec!["user_a".to_string(), "user_b".to_string()],
 837            pending: vec!["user_d".to_string()]
 838        }
 839    );
 840
 841    // The server is torn down.
 842    server.reset().await;
 843
 844    // Users A and B reconnect to the call. User C has troubles reconnecting, so it leaves the room.
 845    client_c.override_establish_connection(|_, cx| cx.spawn(|_| future::pending()));
 846    deterministic.advance_clock(RECONNECT_TIMEOUT);
 847    assert_eq!(
 848        room_participants(&room_a, cx_a),
 849        RoomParticipants {
 850            remote: vec!["user_b".to_string(), "user_c".to_string()],
 851            pending: vec!["user_d".to_string()]
 852        }
 853    );
 854    assert_eq!(
 855        room_participants(&room_b, cx_b),
 856        RoomParticipants {
 857            remote: vec!["user_a".to_string(), "user_c".to_string()],
 858            pending: vec!["user_d".to_string()]
 859        }
 860    );
 861    assert_eq!(
 862        room_participants(&room_c, cx_c),
 863        RoomParticipants {
 864            remote: vec![],
 865            pending: vec![]
 866        }
 867    );
 868
 869    // User D is notified again of the incoming call and accepts it.
 870    assert!(incoming_call_d.next().await.unwrap().is_some());
 871    active_call_d
 872        .update(cx_d, |call, cx| call.accept_incoming(cx))
 873        .await
 874        .unwrap();
 875    deterministic.run_until_parked();
 876    let room_d = active_call_d.read_with(cx_d, |call, _| call.room().unwrap().clone());
 877    assert_eq!(
 878        room_participants(&room_a, cx_a),
 879        RoomParticipants {
 880            remote: vec![
 881                "user_b".to_string(),
 882                "user_c".to_string(),
 883                "user_d".to_string(),
 884            ],
 885            pending: vec![]
 886        }
 887    );
 888    assert_eq!(
 889        room_participants(&room_b, cx_b),
 890        RoomParticipants {
 891            remote: vec![
 892                "user_a".to_string(),
 893                "user_c".to_string(),
 894                "user_d".to_string(),
 895            ],
 896            pending: vec![]
 897        }
 898    );
 899    assert_eq!(
 900        room_participants(&room_c, cx_c),
 901        RoomParticipants {
 902            remote: vec![],
 903            pending: vec![]
 904        }
 905    );
 906    assert_eq!(
 907        room_participants(&room_d, cx_d),
 908        RoomParticipants {
 909            remote: vec![
 910                "user_a".to_string(),
 911                "user_b".to_string(),
 912                "user_c".to_string(),
 913            ],
 914            pending: vec![]
 915        }
 916    );
 917
 918    // The server finishes restarting, cleaning up stale connections.
 919    server.start().await.unwrap();
 920    deterministic.advance_clock(CLEANUP_TIMEOUT);
 921    assert_eq!(
 922        room_participants(&room_a, cx_a),
 923        RoomParticipants {
 924            remote: vec!["user_b".to_string(), "user_d".to_string()],
 925            pending: vec![]
 926        }
 927    );
 928    assert_eq!(
 929        room_participants(&room_b, cx_b),
 930        RoomParticipants {
 931            remote: vec!["user_a".to_string(), "user_d".to_string()],
 932            pending: vec![]
 933        }
 934    );
 935    assert_eq!(
 936        room_participants(&room_c, cx_c),
 937        RoomParticipants {
 938            remote: vec![],
 939            pending: vec![]
 940        }
 941    );
 942    assert_eq!(
 943        room_participants(&room_d, cx_d),
 944        RoomParticipants {
 945            remote: vec!["user_a".to_string(), "user_b".to_string()],
 946            pending: vec![]
 947        }
 948    );
 949
 950    // User D hangs up.
 951    active_call_d
 952        .update(cx_d, |call, cx| call.hang_up(cx))
 953        .await
 954        .unwrap();
 955    deterministic.run_until_parked();
 956    assert_eq!(
 957        room_participants(&room_a, cx_a),
 958        RoomParticipants {
 959            remote: vec!["user_b".to_string()],
 960            pending: vec![]
 961        }
 962    );
 963    assert_eq!(
 964        room_participants(&room_b, cx_b),
 965        RoomParticipants {
 966            remote: vec!["user_a".to_string()],
 967            pending: vec![]
 968        }
 969    );
 970    assert_eq!(
 971        room_participants(&room_c, cx_c),
 972        RoomParticipants {
 973            remote: vec![],
 974            pending: vec![]
 975        }
 976    );
 977    assert_eq!(
 978        room_participants(&room_d, cx_d),
 979        RoomParticipants {
 980            remote: vec![],
 981            pending: vec![]
 982        }
 983    );
 984
 985    // User B calls user D again.
 986    active_call_b
 987        .update(cx_b, |call, cx| {
 988            call.invite(client_d.user_id().unwrap(), None, cx)
 989        })
 990        .await
 991        .unwrap();
 992
 993    // User D receives the call but doesn't join the room yet.
 994    let mut incoming_call_d = active_call_d.read_with(cx_d, |call, _| call.incoming());
 995    assert!(incoming_call_d.next().await.unwrap().is_some());
 996    deterministic.run_until_parked();
 997    assert_eq!(
 998        room_participants(&room_a, cx_a),
 999        RoomParticipants {
1000            remote: vec!["user_b".to_string()],
1001            pending: vec!["user_d".to_string()]
1002        }
1003    );
1004    assert_eq!(
1005        room_participants(&room_b, cx_b),
1006        RoomParticipants {
1007            remote: vec!["user_a".to_string()],
1008            pending: vec!["user_d".to_string()]
1009        }
1010    );
1011
1012    // The server is torn down.
1013    server.reset().await;
1014
1015    // Users A and B have troubles reconnecting, so they leave the room.
1016    client_a.override_establish_connection(|_, cx| cx.spawn(|_| future::pending()));
1017    client_b.override_establish_connection(|_, cx| cx.spawn(|_| future::pending()));
1018    client_c.override_establish_connection(|_, cx| cx.spawn(|_| future::pending()));
1019    deterministic.advance_clock(RECONNECT_TIMEOUT);
1020    assert_eq!(
1021        room_participants(&room_a, cx_a),
1022        RoomParticipants {
1023            remote: vec![],
1024            pending: vec![]
1025        }
1026    );
1027    assert_eq!(
1028        room_participants(&room_b, cx_b),
1029        RoomParticipants {
1030            remote: vec![],
1031            pending: vec![]
1032        }
1033    );
1034
1035    // User D is notified again of the incoming call but doesn't accept it.
1036    assert!(incoming_call_d.next().await.unwrap().is_some());
1037
1038    // The server finishes restarting, cleaning up stale connections and canceling the
1039    // call to user D because the room has become empty.
1040    server.start().await.unwrap();
1041    deterministic.advance_clock(CLEANUP_TIMEOUT);
1042    assert!(incoming_call_d.next().await.unwrap().is_none());
1043}
1044
1045#[gpui::test(iterations = 10)]
1046async fn test_calls_on_multiple_connections(
1047    deterministic: Arc<Deterministic>,
1048    cx_a: &mut TestAppContext,
1049    cx_b1: &mut TestAppContext,
1050    cx_b2: &mut TestAppContext,
1051) {
1052    deterministic.forbid_parking();
1053    let mut server = TestServer::start(&deterministic).await;
1054    let client_a = server.create_client(cx_a, "user_a").await;
1055    let client_b1 = server.create_client(cx_b1, "user_b").await;
1056    let client_b2 = server.create_client(cx_b2, "user_b").await;
1057    server
1058        .make_contacts(&mut [(&client_a, cx_a), (&client_b1, cx_b1)])
1059        .await;
1060
1061    let active_call_a = cx_a.read(ActiveCall::global);
1062    let active_call_b1 = cx_b1.read(ActiveCall::global);
1063    let active_call_b2 = cx_b2.read(ActiveCall::global);
1064    let mut incoming_call_b1 = active_call_b1.read_with(cx_b1, |call, _| call.incoming());
1065    let mut incoming_call_b2 = active_call_b2.read_with(cx_b2, |call, _| call.incoming());
1066    assert!(incoming_call_b1.next().await.unwrap().is_none());
1067    assert!(incoming_call_b2.next().await.unwrap().is_none());
1068
1069    // Call user B from client A, ensuring both clients for user B ring.
1070    active_call_a
1071        .update(cx_a, |call, cx| {
1072            call.invite(client_b1.user_id().unwrap(), None, cx)
1073        })
1074        .await
1075        .unwrap();
1076    deterministic.run_until_parked();
1077    assert!(incoming_call_b1.next().await.unwrap().is_some());
1078    assert!(incoming_call_b2.next().await.unwrap().is_some());
1079
1080    // User B declines the call on one of the two connections, causing both connections
1081    // to stop ringing.
1082    active_call_b2.update(cx_b2, |call, cx| call.decline_incoming(cx).unwrap());
1083    deterministic.run_until_parked();
1084    assert!(incoming_call_b1.next().await.unwrap().is_none());
1085    assert!(incoming_call_b2.next().await.unwrap().is_none());
1086
1087    // Call user B again from client A.
1088    active_call_a
1089        .update(cx_a, |call, cx| {
1090            call.invite(client_b1.user_id().unwrap(), None, cx)
1091        })
1092        .await
1093        .unwrap();
1094    deterministic.run_until_parked();
1095    assert!(incoming_call_b1.next().await.unwrap().is_some());
1096    assert!(incoming_call_b2.next().await.unwrap().is_some());
1097
1098    // User B accepts the call on one of the two connections, causing both connections
1099    // to stop ringing.
1100    active_call_b2
1101        .update(cx_b2, |call, cx| call.accept_incoming(cx))
1102        .await
1103        .unwrap();
1104    deterministic.run_until_parked();
1105    assert!(incoming_call_b1.next().await.unwrap().is_none());
1106    assert!(incoming_call_b2.next().await.unwrap().is_none());
1107
1108    // User B disconnects the client that is not on the call. Everything should be fine.
1109    client_b1.disconnect(&cx_b1.to_async());
1110    deterministic.advance_clock(RECEIVE_TIMEOUT);
1111    client_b1
1112        .authenticate_and_connect(false, &cx_b1.to_async())
1113        .await
1114        .unwrap();
1115
1116    // User B hangs up, and user A calls them again.
1117    active_call_b2
1118        .update(cx_b2, |call, cx| call.hang_up(cx))
1119        .await
1120        .unwrap();
1121    deterministic.run_until_parked();
1122    active_call_a
1123        .update(cx_a, |call, cx| {
1124            call.invite(client_b1.user_id().unwrap(), None, cx)
1125        })
1126        .await
1127        .unwrap();
1128    deterministic.run_until_parked();
1129    assert!(incoming_call_b1.next().await.unwrap().is_some());
1130    assert!(incoming_call_b2.next().await.unwrap().is_some());
1131
1132    // User A cancels the call, causing both connections to stop ringing.
1133    active_call_a
1134        .update(cx_a, |call, cx| {
1135            call.cancel_invite(client_b1.user_id().unwrap(), cx)
1136        })
1137        .await
1138        .unwrap();
1139    deterministic.run_until_parked();
1140    assert!(incoming_call_b1.next().await.unwrap().is_none());
1141    assert!(incoming_call_b2.next().await.unwrap().is_none());
1142
1143    // User A calls user B again.
1144    active_call_a
1145        .update(cx_a, |call, cx| {
1146            call.invite(client_b1.user_id().unwrap(), None, cx)
1147        })
1148        .await
1149        .unwrap();
1150    deterministic.run_until_parked();
1151    assert!(incoming_call_b1.next().await.unwrap().is_some());
1152    assert!(incoming_call_b2.next().await.unwrap().is_some());
1153
1154    // User A hangs up, causing both connections to stop ringing.
1155    active_call_a
1156        .update(cx_a, |call, cx| call.hang_up(cx))
1157        .await
1158        .unwrap();
1159    deterministic.run_until_parked();
1160    assert!(incoming_call_b1.next().await.unwrap().is_none());
1161    assert!(incoming_call_b2.next().await.unwrap().is_none());
1162
1163    // User A calls user B again.
1164    active_call_a
1165        .update(cx_a, |call, cx| {
1166            call.invite(client_b1.user_id().unwrap(), None, cx)
1167        })
1168        .await
1169        .unwrap();
1170    deterministic.run_until_parked();
1171    assert!(incoming_call_b1.next().await.unwrap().is_some());
1172    assert!(incoming_call_b2.next().await.unwrap().is_some());
1173
1174    // User A disconnects, causing both connections to stop ringing.
1175    server.forbid_connections();
1176    server.disconnect_client(client_a.peer_id().unwrap());
1177    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
1178    assert!(incoming_call_b1.next().await.unwrap().is_none());
1179    assert!(incoming_call_b2.next().await.unwrap().is_none());
1180
1181    // User A reconnects automatically, then calls user B again.
1182    server.allow_connections();
1183    deterministic.advance_clock(RECEIVE_TIMEOUT);
1184    active_call_a
1185        .update(cx_a, |call, cx| {
1186            call.invite(client_b1.user_id().unwrap(), None, cx)
1187        })
1188        .await
1189        .unwrap();
1190    deterministic.run_until_parked();
1191    assert!(incoming_call_b1.next().await.unwrap().is_some());
1192    assert!(incoming_call_b2.next().await.unwrap().is_some());
1193
1194    // User B disconnects all clients, causing user A to no longer see a pending call for them.
1195    server.forbid_connections();
1196    server.disconnect_client(client_b1.peer_id().unwrap());
1197    server.disconnect_client(client_b2.peer_id().unwrap());
1198    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
1199    active_call_a.read_with(cx_a, |call, _| assert!(call.room().is_none()));
1200}
1201
1202#[gpui::test(iterations = 10)]
1203async fn test_share_project(
1204    deterministic: Arc<Deterministic>,
1205    cx_a: &mut TestAppContext,
1206    cx_b: &mut TestAppContext,
1207    cx_c: &mut TestAppContext,
1208) {
1209    deterministic.forbid_parking();
1210    let window_b = cx_b.add_window(|_| EmptyView);
1211    let mut server = TestServer::start(&deterministic).await;
1212    let client_a = server.create_client(cx_a, "user_a").await;
1213    let client_b = server.create_client(cx_b, "user_b").await;
1214    let client_c = server.create_client(cx_c, "user_c").await;
1215    server
1216        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
1217        .await;
1218    let active_call_a = cx_a.read(ActiveCall::global);
1219    let active_call_b = cx_b.read(ActiveCall::global);
1220    let active_call_c = cx_c.read(ActiveCall::global);
1221
1222    client_a
1223        .fs()
1224        .insert_tree(
1225            "/a",
1226            json!({
1227                ".gitignore": "ignored-dir",
1228                "a.txt": "a-contents",
1229                "b.txt": "b-contents",
1230                "ignored-dir": {
1231                    "c.txt": "",
1232                    "d.txt": "",
1233                }
1234            }),
1235        )
1236        .await;
1237
1238    // Invite client B to collaborate on a project
1239    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
1240    active_call_a
1241        .update(cx_a, |call, cx| {
1242            call.invite(client_b.user_id().unwrap(), Some(project_a.clone()), cx)
1243        })
1244        .await
1245        .unwrap();
1246
1247    // Join that project as client B
1248    let incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
1249    deterministic.run_until_parked();
1250    let call = incoming_call_b.borrow().clone().unwrap();
1251    assert_eq!(call.calling_user.github_login, "user_a");
1252    let initial_project = call.initial_project.unwrap();
1253    active_call_b
1254        .update(cx_b, |call, cx| call.accept_incoming(cx))
1255        .await
1256        .unwrap();
1257    let client_b_peer_id = client_b.peer_id().unwrap();
1258    let project_b = client_b
1259        .build_remote_project(initial_project.id, cx_b)
1260        .await;
1261    let replica_id_b = project_b.read_with(cx_b, |project, _| project.replica_id());
1262
1263    deterministic.run_until_parked();
1264    project_a.read_with(cx_a, |project, _| {
1265        let client_b_collaborator = project.collaborators().get(&client_b_peer_id).unwrap();
1266        assert_eq!(client_b_collaborator.replica_id, replica_id_b);
1267    });
1268    project_b.read_with(cx_b, |project, cx| {
1269        let worktree = project.worktrees(cx).next().unwrap().read(cx);
1270        assert_eq!(
1271            worktree.paths().map(AsRef::as_ref).collect::<Vec<_>>(),
1272            [
1273                Path::new(".gitignore"),
1274                Path::new("a.txt"),
1275                Path::new("b.txt"),
1276                Path::new("ignored-dir"),
1277            ]
1278        );
1279    });
1280
1281    project_b
1282        .update(cx_b, |project, cx| {
1283            let worktree = project.worktrees(cx).next().unwrap();
1284            let entry = worktree.read(cx).entry_for_path("ignored-dir").unwrap();
1285            project.expand_entry(worktree_id, entry.id, cx).unwrap()
1286        })
1287        .await
1288        .unwrap();
1289    project_b.read_with(cx_b, |project, cx| {
1290        let worktree = project.worktrees(cx).next().unwrap().read(cx);
1291        assert_eq!(
1292            worktree.paths().map(AsRef::as_ref).collect::<Vec<_>>(),
1293            [
1294                Path::new(".gitignore"),
1295                Path::new("a.txt"),
1296                Path::new("b.txt"),
1297                Path::new("ignored-dir"),
1298                Path::new("ignored-dir/c.txt"),
1299                Path::new("ignored-dir/d.txt"),
1300            ]
1301        );
1302    });
1303
1304    // Open the same file as client B and client A.
1305    let buffer_b = project_b
1306        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.txt"), cx))
1307        .await
1308        .unwrap();
1309    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "b-contents"));
1310    project_a.read_with(cx_a, |project, cx| {
1311        assert!(project.has_open_buffer((worktree_id, "b.txt"), cx))
1312    });
1313    let buffer_a = project_a
1314        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "b.txt"), cx))
1315        .await
1316        .unwrap();
1317
1318    let editor_b = window_b.add_view(cx_b, |cx| Editor::for_buffer(buffer_b, None, cx));
1319
1320    // Client A sees client B's selection
1321    deterministic.run_until_parked();
1322    buffer_a.read_with(cx_a, |buffer, _| {
1323        buffer
1324            .snapshot()
1325            .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
1326            .count()
1327            == 1
1328    });
1329
1330    // Edit the buffer as client B and see that edit as client A.
1331    editor_b.update(cx_b, |editor, cx| editor.handle_input("ok, ", cx));
1332    deterministic.run_until_parked();
1333    buffer_a.read_with(cx_a, |buffer, _| {
1334        assert_eq!(buffer.text(), "ok, b-contents")
1335    });
1336
1337    // Client B can invite client C on a project shared by client A.
1338    active_call_b
1339        .update(cx_b, |call, cx| {
1340            call.invite(client_c.user_id().unwrap(), Some(project_b.clone()), cx)
1341        })
1342        .await
1343        .unwrap();
1344
1345    let incoming_call_c = active_call_c.read_with(cx_c, |call, _| call.incoming());
1346    deterministic.run_until_parked();
1347    let call = incoming_call_c.borrow().clone().unwrap();
1348    assert_eq!(call.calling_user.github_login, "user_b");
1349    let initial_project = call.initial_project.unwrap();
1350    active_call_c
1351        .update(cx_c, |call, cx| call.accept_incoming(cx))
1352        .await
1353        .unwrap();
1354    let _project_c = client_c
1355        .build_remote_project(initial_project.id, cx_c)
1356        .await;
1357
1358    // Client B closes the editor, and client A sees client B's selections removed.
1359    cx_b.update(move |_| drop(editor_b));
1360    deterministic.run_until_parked();
1361    buffer_a.read_with(cx_a, |buffer, _| {
1362        buffer
1363            .snapshot()
1364            .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
1365            .count()
1366            == 0
1367    });
1368}
1369
1370#[gpui::test(iterations = 10)]
1371async fn test_unshare_project(
1372    deterministic: Arc<Deterministic>,
1373    cx_a: &mut TestAppContext,
1374    cx_b: &mut TestAppContext,
1375    cx_c: &mut TestAppContext,
1376) {
1377    deterministic.forbid_parking();
1378    let mut server = TestServer::start(&deterministic).await;
1379    let client_a = server.create_client(cx_a, "user_a").await;
1380    let client_b = server.create_client(cx_b, "user_b").await;
1381    let client_c = server.create_client(cx_c, "user_c").await;
1382    server
1383        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
1384        .await;
1385
1386    let active_call_a = cx_a.read(ActiveCall::global);
1387    let active_call_b = cx_b.read(ActiveCall::global);
1388
1389    client_a
1390        .fs()
1391        .insert_tree(
1392            "/a",
1393            json!({
1394                "a.txt": "a-contents",
1395                "b.txt": "b-contents",
1396            }),
1397        )
1398        .await;
1399
1400    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
1401    let project_id = active_call_a
1402        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
1403        .await
1404        .unwrap();
1405    let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
1406    let project_b = client_b.build_remote_project(project_id, cx_b).await;
1407    deterministic.run_until_parked();
1408    assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
1409
1410    project_b
1411        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
1412        .await
1413        .unwrap();
1414
1415    // When client B leaves the room, the project becomes read-only.
1416    active_call_b
1417        .update(cx_b, |call, cx| call.hang_up(cx))
1418        .await
1419        .unwrap();
1420    deterministic.run_until_parked();
1421    assert!(project_b.read_with(cx_b, |project, _| project.is_read_only()));
1422
1423    // Client C opens the project.
1424    let project_c = client_c.build_remote_project(project_id, cx_c).await;
1425
1426    // When client A unshares the project, client C's project becomes read-only.
1427    project_a
1428        .update(cx_a, |project, cx| project.unshare(cx))
1429        .unwrap();
1430    deterministic.run_until_parked();
1431    assert!(worktree_a.read_with(cx_a, |tree, _| !tree.as_local().unwrap().is_shared()));
1432    assert!(project_c.read_with(cx_c, |project, _| project.is_read_only()));
1433
1434    // Client C can open the project again after client A re-shares.
1435    let project_id = active_call_a
1436        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
1437        .await
1438        .unwrap();
1439    let project_c2 = client_c.build_remote_project(project_id, cx_c).await;
1440    deterministic.run_until_parked();
1441    assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
1442    project_c2
1443        .update(cx_c, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
1444        .await
1445        .unwrap();
1446
1447    // When client A (the host) leaves the room, the project gets unshared and guests are notified.
1448    active_call_a
1449        .update(cx_a, |call, cx| call.hang_up(cx))
1450        .await
1451        .unwrap();
1452    deterministic.run_until_parked();
1453    project_a.read_with(cx_a, |project, _| assert!(!project.is_shared()));
1454    project_c2.read_with(cx_c, |project, _| {
1455        assert!(project.is_read_only());
1456        assert!(project.collaborators().is_empty());
1457    });
1458}
1459
1460#[gpui::test(iterations = 10)]
1461async fn test_host_disconnect(
1462    deterministic: Arc<Deterministic>,
1463    cx_a: &mut TestAppContext,
1464    cx_b: &mut TestAppContext,
1465    cx_c: &mut TestAppContext,
1466) {
1467    deterministic.forbid_parking();
1468    let mut server = TestServer::start(&deterministic).await;
1469    let client_a = server.create_client(cx_a, "user_a").await;
1470    let client_b = server.create_client(cx_b, "user_b").await;
1471    let client_c = server.create_client(cx_c, "user_c").await;
1472    server
1473        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
1474        .await;
1475
1476    cx_b.update(editor::init);
1477
1478    client_a
1479        .fs()
1480        .insert_tree(
1481            "/a",
1482            json!({
1483                "a.txt": "a-contents",
1484                "b.txt": "b-contents",
1485            }),
1486        )
1487        .await;
1488
1489    let active_call_a = cx_a.read(ActiveCall::global);
1490    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
1491    let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
1492    let project_id = active_call_a
1493        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
1494        .await
1495        .unwrap();
1496
1497    let project_b = client_b.build_remote_project(project_id, cx_b).await;
1498    deterministic.run_until_parked();
1499    assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
1500
1501    let window_b =
1502        cx_b.add_window(|cx| Workspace::new(0, project_b.clone(), client_b.app_state.clone(), cx));
1503    let workspace_b = window_b.root(cx_b);
1504    let editor_b = workspace_b
1505        .update(cx_b, |workspace, cx| {
1506            workspace.open_path((worktree_id, "b.txt"), None, true, cx)
1507        })
1508        .await
1509        .unwrap()
1510        .downcast::<Editor>()
1511        .unwrap();
1512    assert!(window_b.read_with(cx_b, |cx| editor_b.is_focused(cx)));
1513    editor_b.update(cx_b, |editor, cx| editor.insert("X", cx));
1514    assert!(window_b.is_edited(cx_b));
1515
1516    // Drop client A's connection. Collaborators should disappear and the project should not be shown as shared.
1517    server.forbid_connections();
1518    server.disconnect_client(client_a.peer_id().unwrap());
1519    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
1520    project_a.read_with(cx_a, |project, _| project.collaborators().is_empty());
1521    project_a.read_with(cx_a, |project, _| assert!(!project.is_shared()));
1522    project_b.read_with(cx_b, |project, _| project.is_read_only());
1523    assert!(worktree_a.read_with(cx_a, |tree, _| !tree.as_local().unwrap().is_shared()));
1524
1525    // Ensure client B's edited state is reset and that the whole window is blurred.
1526    window_b.read_with(cx_b, |cx| {
1527        assert_eq!(cx.focused_view_id(), None);
1528    });
1529    assert!(!window_b.is_edited(cx_b));
1530
1531    // Ensure client B is not prompted to save edits when closing window after disconnecting.
1532    let can_close = workspace_b
1533        .update(cx_b, |workspace, cx| workspace.prepare_to_close(true, cx))
1534        .await
1535        .unwrap();
1536    assert!(can_close);
1537
1538    // Allow client A to reconnect to the server.
1539    server.allow_connections();
1540    deterministic.advance_clock(RECEIVE_TIMEOUT);
1541
1542    // Client B calls client A again after they reconnected.
1543    let active_call_b = cx_b.read(ActiveCall::global);
1544    active_call_b
1545        .update(cx_b, |call, cx| {
1546            call.invite(client_a.user_id().unwrap(), None, cx)
1547        })
1548        .await
1549        .unwrap();
1550    deterministic.run_until_parked();
1551    active_call_a
1552        .update(cx_a, |call, cx| call.accept_incoming(cx))
1553        .await
1554        .unwrap();
1555
1556    active_call_a
1557        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
1558        .await
1559        .unwrap();
1560
1561    // Drop client A's connection again. We should still unshare it successfully.
1562    server.forbid_connections();
1563    server.disconnect_client(client_a.peer_id().unwrap());
1564    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
1565    project_a.read_with(cx_a, |project, _| assert!(!project.is_shared()));
1566}
1567
1568#[gpui::test(iterations = 10)]
1569async fn test_project_reconnect(
1570    deterministic: Arc<Deterministic>,
1571    cx_a: &mut TestAppContext,
1572    cx_b: &mut TestAppContext,
1573) {
1574    deterministic.forbid_parking();
1575    let mut server = TestServer::start(&deterministic).await;
1576    let client_a = server.create_client(cx_a, "user_a").await;
1577    let client_b = server.create_client(cx_b, "user_b").await;
1578    server
1579        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
1580        .await;
1581
1582    cx_b.update(editor::init);
1583
1584    client_a
1585        .fs()
1586        .insert_tree(
1587            "/root-1",
1588            json!({
1589                "dir1": {
1590                    "a.txt": "a",
1591                    "b.txt": "b",
1592                    "subdir1": {
1593                        "c.txt": "c",
1594                        "d.txt": "d",
1595                        "e.txt": "e",
1596                    }
1597                },
1598                "dir2": {
1599                    "v.txt": "v",
1600                },
1601                "dir3": {
1602                    "w.txt": "w",
1603                    "x.txt": "x",
1604                    "y.txt": "y",
1605                },
1606                "dir4": {
1607                    "z.txt": "z",
1608                },
1609            }),
1610        )
1611        .await;
1612    client_a
1613        .fs()
1614        .insert_tree(
1615            "/root-2",
1616            json!({
1617                "2.txt": "2",
1618            }),
1619        )
1620        .await;
1621    client_a
1622        .fs()
1623        .insert_tree(
1624            "/root-3",
1625            json!({
1626                "3.txt": "3",
1627            }),
1628        )
1629        .await;
1630
1631    let active_call_a = cx_a.read(ActiveCall::global);
1632    let (project_a1, _) = client_a.build_local_project("/root-1/dir1", cx_a).await;
1633    let (project_a2, _) = client_a.build_local_project("/root-2", cx_a).await;
1634    let (project_a3, _) = client_a.build_local_project("/root-3", cx_a).await;
1635    let worktree_a1 =
1636        project_a1.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
1637    let project1_id = active_call_a
1638        .update(cx_a, |call, cx| call.share_project(project_a1.clone(), cx))
1639        .await
1640        .unwrap();
1641    let project2_id = active_call_a
1642        .update(cx_a, |call, cx| call.share_project(project_a2.clone(), cx))
1643        .await
1644        .unwrap();
1645    let project3_id = active_call_a
1646        .update(cx_a, |call, cx| call.share_project(project_a3.clone(), cx))
1647        .await
1648        .unwrap();
1649
1650    let project_b1 = client_b.build_remote_project(project1_id, cx_b).await;
1651    let project_b2 = client_b.build_remote_project(project2_id, cx_b).await;
1652    let project_b3 = client_b.build_remote_project(project3_id, cx_b).await;
1653    deterministic.run_until_parked();
1654
1655    let worktree1_id = worktree_a1.read_with(cx_a, |worktree, _| {
1656        assert!(worktree.as_local().unwrap().is_shared());
1657        worktree.id()
1658    });
1659    let (worktree_a2, _) = project_a1
1660        .update(cx_a, |p, cx| {
1661            p.find_or_create_local_worktree("/root-1/dir2", true, cx)
1662        })
1663        .await
1664        .unwrap();
1665    deterministic.run_until_parked();
1666    let worktree2_id = worktree_a2.read_with(cx_a, |tree, _| {
1667        assert!(tree.as_local().unwrap().is_shared());
1668        tree.id()
1669    });
1670    deterministic.run_until_parked();
1671    project_b1.read_with(cx_b, |project, cx| {
1672        assert!(project.worktree_for_id(worktree2_id, cx).is_some())
1673    });
1674
1675    let buffer_a1 = project_a1
1676        .update(cx_a, |p, cx| p.open_buffer((worktree1_id, "a.txt"), cx))
1677        .await
1678        .unwrap();
1679    let buffer_b1 = project_b1
1680        .update(cx_b, |p, cx| p.open_buffer((worktree1_id, "a.txt"), cx))
1681        .await
1682        .unwrap();
1683
1684    // Drop client A's connection.
1685    server.forbid_connections();
1686    server.disconnect_client(client_a.peer_id().unwrap());
1687    deterministic.advance_clock(RECEIVE_TIMEOUT);
1688    project_a1.read_with(cx_a, |project, _| {
1689        assert!(project.is_shared());
1690        assert_eq!(project.collaborators().len(), 1);
1691    });
1692    project_b1.read_with(cx_b, |project, _| {
1693        assert!(!project.is_read_only());
1694        assert_eq!(project.collaborators().len(), 1);
1695    });
1696    worktree_a1.read_with(cx_a, |tree, _| {
1697        assert!(tree.as_local().unwrap().is_shared())
1698    });
1699
1700    // While client A is disconnected, add and remove files from client A's project.
1701    client_a
1702        .fs()
1703        .insert_tree(
1704            "/root-1/dir1/subdir2",
1705            json!({
1706                "f.txt": "f-contents",
1707                "g.txt": "g-contents",
1708                "h.txt": "h-contents",
1709                "i.txt": "i-contents",
1710            }),
1711        )
1712        .await;
1713    client_a
1714        .fs()
1715        .remove_dir(
1716            "/root-1/dir1/subdir1".as_ref(),
1717            RemoveOptions {
1718                recursive: true,
1719                ..Default::default()
1720            },
1721        )
1722        .await
1723        .unwrap();
1724
1725    // While client A is disconnected, add and remove worktrees from client A's project.
1726    project_a1.update(cx_a, |project, cx| {
1727        project.remove_worktree(worktree2_id, cx)
1728    });
1729    let (worktree_a3, _) = project_a1
1730        .update(cx_a, |p, cx| {
1731            p.find_or_create_local_worktree("/root-1/dir3", true, cx)
1732        })
1733        .await
1734        .unwrap();
1735    worktree_a3
1736        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
1737        .await;
1738    let worktree3_id = worktree_a3.read_with(cx_a, |tree, _| {
1739        assert!(!tree.as_local().unwrap().is_shared());
1740        tree.id()
1741    });
1742    deterministic.run_until_parked();
1743
1744    // While client A is disconnected, close project 2
1745    cx_a.update(|_| drop(project_a2));
1746
1747    // While client A is disconnected, mutate a buffer on both the host and the guest.
1748    buffer_a1.update(cx_a, |buf, cx| buf.edit([(0..0, "W")], None, cx));
1749    buffer_b1.update(cx_b, |buf, cx| buf.edit([(1..1, "Z")], None, cx));
1750    deterministic.run_until_parked();
1751
1752    // Client A reconnects. Their project is re-shared, and client B re-joins it.
1753    server.allow_connections();
1754    client_a
1755        .authenticate_and_connect(false, &cx_a.to_async())
1756        .await
1757        .unwrap();
1758    deterministic.run_until_parked();
1759    project_a1.read_with(cx_a, |project, cx| {
1760        assert!(project.is_shared());
1761        assert!(worktree_a1.read(cx).as_local().unwrap().is_shared());
1762        assert_eq!(
1763            worktree_a1
1764                .read(cx)
1765                .snapshot()
1766                .paths()
1767                .map(|p| p.to_str().unwrap())
1768                .collect::<Vec<_>>(),
1769            vec![
1770                "a.txt",
1771                "b.txt",
1772                "subdir2",
1773                "subdir2/f.txt",
1774                "subdir2/g.txt",
1775                "subdir2/h.txt",
1776                "subdir2/i.txt"
1777            ]
1778        );
1779        assert!(worktree_a3.read(cx).as_local().unwrap().is_shared());
1780        assert_eq!(
1781            worktree_a3
1782                .read(cx)
1783                .snapshot()
1784                .paths()
1785                .map(|p| p.to_str().unwrap())
1786                .collect::<Vec<_>>(),
1787            vec!["w.txt", "x.txt", "y.txt"]
1788        );
1789    });
1790    project_b1.read_with(cx_b, |project, cx| {
1791        assert!(!project.is_read_only());
1792        assert_eq!(
1793            project
1794                .worktree_for_id(worktree1_id, cx)
1795                .unwrap()
1796                .read(cx)
1797                .snapshot()
1798                .paths()
1799                .map(|p| p.to_str().unwrap())
1800                .collect::<Vec<_>>(),
1801            vec![
1802                "a.txt",
1803                "b.txt",
1804                "subdir2",
1805                "subdir2/f.txt",
1806                "subdir2/g.txt",
1807                "subdir2/h.txt",
1808                "subdir2/i.txt"
1809            ]
1810        );
1811        assert!(project.worktree_for_id(worktree2_id, cx).is_none());
1812        assert_eq!(
1813            project
1814                .worktree_for_id(worktree3_id, cx)
1815                .unwrap()
1816                .read(cx)
1817                .snapshot()
1818                .paths()
1819                .map(|p| p.to_str().unwrap())
1820                .collect::<Vec<_>>(),
1821            vec!["w.txt", "x.txt", "y.txt"]
1822        );
1823    });
1824    project_b2.read_with(cx_b, |project, _| assert!(project.is_read_only()));
1825    project_b3.read_with(cx_b, |project, _| assert!(!project.is_read_only()));
1826    buffer_a1.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "WaZ"));
1827    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "WaZ"));
1828
1829    // Drop client B's connection.
1830    server.forbid_connections();
1831    server.disconnect_client(client_b.peer_id().unwrap());
1832    deterministic.advance_clock(RECEIVE_TIMEOUT);
1833
1834    // While client B is disconnected, add and remove files from client A's project
1835    client_a
1836        .fs()
1837        .insert_file("/root-1/dir1/subdir2/j.txt", "j-contents".into())
1838        .await;
1839    client_a
1840        .fs()
1841        .remove_file("/root-1/dir1/subdir2/i.txt".as_ref(), Default::default())
1842        .await
1843        .unwrap();
1844
1845    // While client B is disconnected, add and remove worktrees from client A's project.
1846    let (worktree_a4, _) = project_a1
1847        .update(cx_a, |p, cx| {
1848            p.find_or_create_local_worktree("/root-1/dir4", true, cx)
1849        })
1850        .await
1851        .unwrap();
1852    deterministic.run_until_parked();
1853    let worktree4_id = worktree_a4.read_with(cx_a, |tree, _| {
1854        assert!(tree.as_local().unwrap().is_shared());
1855        tree.id()
1856    });
1857    project_a1.update(cx_a, |project, cx| {
1858        project.remove_worktree(worktree3_id, cx)
1859    });
1860    deterministic.run_until_parked();
1861
1862    // While client B is disconnected, mutate a buffer on both the host and the guest.
1863    buffer_a1.update(cx_a, |buf, cx| buf.edit([(1..1, "X")], None, cx));
1864    buffer_b1.update(cx_b, |buf, cx| buf.edit([(2..2, "Y")], None, cx));
1865    deterministic.run_until_parked();
1866
1867    // While disconnected, close project 3
1868    cx_a.update(|_| drop(project_a3));
1869
1870    // Client B reconnects. They re-join the room and the remaining shared project.
1871    server.allow_connections();
1872    client_b
1873        .authenticate_and_connect(false, &cx_b.to_async())
1874        .await
1875        .unwrap();
1876    deterministic.run_until_parked();
1877    project_b1.read_with(cx_b, |project, cx| {
1878        assert!(!project.is_read_only());
1879        assert_eq!(
1880            project
1881                .worktree_for_id(worktree1_id, cx)
1882                .unwrap()
1883                .read(cx)
1884                .snapshot()
1885                .paths()
1886                .map(|p| p.to_str().unwrap())
1887                .collect::<Vec<_>>(),
1888            vec![
1889                "a.txt",
1890                "b.txt",
1891                "subdir2",
1892                "subdir2/f.txt",
1893                "subdir2/g.txt",
1894                "subdir2/h.txt",
1895                "subdir2/j.txt"
1896            ]
1897        );
1898        assert!(project.worktree_for_id(worktree2_id, cx).is_none());
1899        assert_eq!(
1900            project
1901                .worktree_for_id(worktree4_id, cx)
1902                .unwrap()
1903                .read(cx)
1904                .snapshot()
1905                .paths()
1906                .map(|p| p.to_str().unwrap())
1907                .collect::<Vec<_>>(),
1908            vec!["z.txt"]
1909        );
1910    });
1911    project_b3.read_with(cx_b, |project, _| assert!(project.is_read_only()));
1912    buffer_a1.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "WXaYZ"));
1913    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "WXaYZ"));
1914}
1915
1916#[gpui::test(iterations = 10)]
1917async fn test_active_call_events(
1918    deterministic: Arc<Deterministic>,
1919    cx_a: &mut TestAppContext,
1920    cx_b: &mut TestAppContext,
1921) {
1922    deterministic.forbid_parking();
1923    let mut server = TestServer::start(&deterministic).await;
1924    let client_a = server.create_client(cx_a, "user_a").await;
1925    let client_b = server.create_client(cx_b, "user_b").await;
1926    client_a.fs().insert_tree("/a", json!({})).await;
1927    client_b.fs().insert_tree("/b", json!({})).await;
1928
1929    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
1930    let (project_b, _) = client_b.build_local_project("/b", cx_b).await;
1931
1932    server
1933        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
1934        .await;
1935    let active_call_a = cx_a.read(ActiveCall::global);
1936    let active_call_b = cx_b.read(ActiveCall::global);
1937
1938    let events_a = active_call_events(cx_a);
1939    let events_b = active_call_events(cx_b);
1940
1941    let project_a_id = active_call_a
1942        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
1943        .await
1944        .unwrap();
1945    deterministic.run_until_parked();
1946    assert_eq!(mem::take(&mut *events_a.borrow_mut()), vec![]);
1947    assert_eq!(
1948        mem::take(&mut *events_b.borrow_mut()),
1949        vec![room::Event::RemoteProjectShared {
1950            owner: Arc::new(User {
1951                id: client_a.user_id().unwrap(),
1952                github_login: "user_a".to_string(),
1953                avatar: None,
1954            }),
1955            project_id: project_a_id,
1956            worktree_root_names: vec!["a".to_string()],
1957        }]
1958    );
1959
1960    let project_b_id = active_call_b
1961        .update(cx_b, |call, cx| call.share_project(project_b.clone(), cx))
1962        .await
1963        .unwrap();
1964    deterministic.run_until_parked();
1965    assert_eq!(
1966        mem::take(&mut *events_a.borrow_mut()),
1967        vec![room::Event::RemoteProjectShared {
1968            owner: Arc::new(User {
1969                id: client_b.user_id().unwrap(),
1970                github_login: "user_b".to_string(),
1971                avatar: None,
1972            }),
1973            project_id: project_b_id,
1974            worktree_root_names: vec!["b".to_string()]
1975        }]
1976    );
1977    assert_eq!(mem::take(&mut *events_b.borrow_mut()), vec![]);
1978
1979    // Sharing a project twice is idempotent.
1980    let project_b_id_2 = active_call_b
1981        .update(cx_b, |call, cx| call.share_project(project_b.clone(), cx))
1982        .await
1983        .unwrap();
1984    assert_eq!(project_b_id_2, project_b_id);
1985    deterministic.run_until_parked();
1986    assert_eq!(mem::take(&mut *events_a.borrow_mut()), vec![]);
1987    assert_eq!(mem::take(&mut *events_b.borrow_mut()), vec![]);
1988}
1989
1990fn active_call_events(cx: &mut TestAppContext) -> Rc<RefCell<Vec<room::Event>>> {
1991    let events = Rc::new(RefCell::new(Vec::new()));
1992    let active_call = cx.read(ActiveCall::global);
1993    cx.update({
1994        let events = events.clone();
1995        |cx| {
1996            cx.subscribe(&active_call, move |_, event, _| {
1997                events.borrow_mut().push(event.clone())
1998            })
1999            .detach()
2000        }
2001    });
2002    events
2003}
2004
2005#[gpui::test(iterations = 10)]
2006async fn test_room_location(
2007    deterministic: Arc<Deterministic>,
2008    cx_a: &mut TestAppContext,
2009    cx_b: &mut TestAppContext,
2010) {
2011    deterministic.forbid_parking();
2012    let mut server = TestServer::start(&deterministic).await;
2013    let client_a = server.create_client(cx_a, "user_a").await;
2014    let client_b = server.create_client(cx_b, "user_b").await;
2015    client_a.fs().insert_tree("/a", json!({})).await;
2016    client_b.fs().insert_tree("/b", json!({})).await;
2017
2018    let active_call_a = cx_a.read(ActiveCall::global);
2019    let active_call_b = cx_b.read(ActiveCall::global);
2020
2021    let a_notified = Rc::new(Cell::new(false));
2022    cx_a.update({
2023        let notified = a_notified.clone();
2024        |cx| {
2025            cx.observe(&active_call_a, move |_, _| notified.set(true))
2026                .detach()
2027        }
2028    });
2029
2030    let b_notified = Rc::new(Cell::new(false));
2031    cx_b.update({
2032        let b_notified = b_notified.clone();
2033        |cx| {
2034            cx.observe(&active_call_b, move |_, _| b_notified.set(true))
2035                .detach()
2036        }
2037    });
2038
2039    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
2040    active_call_a
2041        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
2042        .await
2043        .unwrap();
2044    let (project_b, _) = client_b.build_local_project("/b", cx_b).await;
2045
2046    server
2047        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2048        .await;
2049    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
2050    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
2051    deterministic.run_until_parked();
2052    assert!(a_notified.take());
2053    assert_eq!(
2054        participant_locations(&room_a, cx_a),
2055        vec![("user_b".to_string(), ParticipantLocation::External)]
2056    );
2057    assert!(b_notified.take());
2058    assert_eq!(
2059        participant_locations(&room_b, cx_b),
2060        vec![("user_a".to_string(), ParticipantLocation::UnsharedProject)]
2061    );
2062
2063    let project_a_id = active_call_a
2064        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2065        .await
2066        .unwrap();
2067    deterministic.run_until_parked();
2068    assert!(a_notified.take());
2069    assert_eq!(
2070        participant_locations(&room_a, cx_a),
2071        vec![("user_b".to_string(), ParticipantLocation::External)]
2072    );
2073    assert!(b_notified.take());
2074    assert_eq!(
2075        participant_locations(&room_b, cx_b),
2076        vec![(
2077            "user_a".to_string(),
2078            ParticipantLocation::SharedProject {
2079                project_id: project_a_id
2080            }
2081        )]
2082    );
2083
2084    let project_b_id = active_call_b
2085        .update(cx_b, |call, cx| call.share_project(project_b.clone(), cx))
2086        .await
2087        .unwrap();
2088    deterministic.run_until_parked();
2089    assert!(a_notified.take());
2090    assert_eq!(
2091        participant_locations(&room_a, cx_a),
2092        vec![("user_b".to_string(), ParticipantLocation::External)]
2093    );
2094    assert!(b_notified.take());
2095    assert_eq!(
2096        participant_locations(&room_b, cx_b),
2097        vec![(
2098            "user_a".to_string(),
2099            ParticipantLocation::SharedProject {
2100                project_id: project_a_id
2101            }
2102        )]
2103    );
2104
2105    active_call_b
2106        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
2107        .await
2108        .unwrap();
2109    deterministic.run_until_parked();
2110    assert!(a_notified.take());
2111    assert_eq!(
2112        participant_locations(&room_a, cx_a),
2113        vec![(
2114            "user_b".to_string(),
2115            ParticipantLocation::SharedProject {
2116                project_id: project_b_id
2117            }
2118        )]
2119    );
2120    assert!(b_notified.take());
2121    assert_eq!(
2122        participant_locations(&room_b, cx_b),
2123        vec![(
2124            "user_a".to_string(),
2125            ParticipantLocation::SharedProject {
2126                project_id: project_a_id
2127            }
2128        )]
2129    );
2130
2131    active_call_b
2132        .update(cx_b, |call, cx| call.set_location(None, cx))
2133        .await
2134        .unwrap();
2135    deterministic.run_until_parked();
2136    assert!(a_notified.take());
2137    assert_eq!(
2138        participant_locations(&room_a, cx_a),
2139        vec![("user_b".to_string(), ParticipantLocation::External)]
2140    );
2141    assert!(b_notified.take());
2142    assert_eq!(
2143        participant_locations(&room_b, cx_b),
2144        vec![(
2145            "user_a".to_string(),
2146            ParticipantLocation::SharedProject {
2147                project_id: project_a_id
2148            }
2149        )]
2150    );
2151
2152    fn participant_locations(
2153        room: &ModelHandle<Room>,
2154        cx: &TestAppContext,
2155    ) -> Vec<(String, ParticipantLocation)> {
2156        room.read_with(cx, |room, _| {
2157            room.remote_participants()
2158                .values()
2159                .map(|participant| {
2160                    (
2161                        participant.user.github_login.to_string(),
2162                        participant.location,
2163                    )
2164                })
2165                .collect()
2166        })
2167    }
2168}
2169
2170#[gpui::test(iterations = 10)]
2171async fn test_propagate_saves_and_fs_changes(
2172    deterministic: Arc<Deterministic>,
2173    cx_a: &mut TestAppContext,
2174    cx_b: &mut TestAppContext,
2175    cx_c: &mut TestAppContext,
2176) {
2177    deterministic.forbid_parking();
2178    let mut server = TestServer::start(&deterministic).await;
2179    let client_a = server.create_client(cx_a, "user_a").await;
2180    let client_b = server.create_client(cx_b, "user_b").await;
2181    let client_c = server.create_client(cx_c, "user_c").await;
2182
2183    server
2184        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
2185        .await;
2186    let active_call_a = cx_a.read(ActiveCall::global);
2187
2188    let rust = Arc::new(Language::new(
2189        LanguageConfig {
2190            name: "Rust".into(),
2191            path_suffixes: vec!["rs".to_string()],
2192            ..Default::default()
2193        },
2194        Some(tree_sitter_rust::language()),
2195    ));
2196    let javascript = Arc::new(Language::new(
2197        LanguageConfig {
2198            name: "JavaScript".into(),
2199            path_suffixes: vec!["js".to_string()],
2200            ..Default::default()
2201        },
2202        Some(tree_sitter_rust::language()),
2203    ));
2204    for client in [&client_a, &client_b, &client_c] {
2205        client.language_registry().add(rust.clone());
2206        client.language_registry().add(javascript.clone());
2207    }
2208
2209    client_a
2210        .fs()
2211        .insert_tree(
2212            "/a",
2213            json!({
2214                "file1.rs": "",
2215                "file2": ""
2216            }),
2217        )
2218        .await;
2219    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
2220    let worktree_a = project_a.read_with(cx_a, |p, cx| p.worktrees(cx).next().unwrap());
2221    let project_id = active_call_a
2222        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2223        .await
2224        .unwrap();
2225
2226    // Join that worktree as clients B and C.
2227    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2228    let project_c = client_c.build_remote_project(project_id, cx_c).await;
2229    let worktree_b = project_b.read_with(cx_b, |p, cx| p.worktrees(cx).next().unwrap());
2230    let worktree_c = project_c.read_with(cx_c, |p, cx| p.worktrees(cx).next().unwrap());
2231
2232    // Open and edit a buffer as both guests B and C.
2233    let buffer_b = project_b
2234        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "file1.rs"), cx))
2235        .await
2236        .unwrap();
2237    let buffer_c = project_c
2238        .update(cx_c, |p, cx| p.open_buffer((worktree_id, "file1.rs"), cx))
2239        .await
2240        .unwrap();
2241    buffer_b.read_with(cx_b, |buffer, _| {
2242        assert_eq!(&*buffer.language().unwrap().name(), "Rust");
2243    });
2244    buffer_c.read_with(cx_c, |buffer, _| {
2245        assert_eq!(&*buffer.language().unwrap().name(), "Rust");
2246    });
2247    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "i-am-b, ")], None, cx));
2248    buffer_c.update(cx_c, |buf, cx| buf.edit([(0..0, "i-am-c, ")], None, cx));
2249
2250    // Open and edit that buffer as the host.
2251    let buffer_a = project_a
2252        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "file1.rs"), cx))
2253        .await
2254        .unwrap();
2255
2256    deterministic.run_until_parked();
2257    buffer_a.read_with(cx_a, |buf, _| assert_eq!(buf.text(), "i-am-c, i-am-b, "));
2258    buffer_a.update(cx_a, |buf, cx| {
2259        buf.edit([(buf.len()..buf.len(), "i-am-a")], None, cx)
2260    });
2261
2262    deterministic.run_until_parked();
2263    buffer_a.read_with(cx_a, |buf, _| {
2264        assert_eq!(buf.text(), "i-am-c, i-am-b, i-am-a");
2265    });
2266    buffer_b.read_with(cx_b, |buf, _| {
2267        assert_eq!(buf.text(), "i-am-c, i-am-b, i-am-a");
2268    });
2269    buffer_c.read_with(cx_c, |buf, _| {
2270        assert_eq!(buf.text(), "i-am-c, i-am-b, i-am-a");
2271    });
2272
2273    // Edit the buffer as the host and concurrently save as guest B.
2274    let save_b = project_b.update(cx_b, |project, cx| {
2275        project.save_buffer(buffer_b.clone(), cx)
2276    });
2277    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "hi-a, ")], None, cx));
2278    save_b.await.unwrap();
2279    assert_eq!(
2280        client_a.fs().load("/a/file1.rs".as_ref()).await.unwrap(),
2281        "hi-a, i-am-c, i-am-b, i-am-a"
2282    );
2283
2284    deterministic.run_until_parked();
2285    buffer_a.read_with(cx_a, |buf, _| assert!(!buf.is_dirty()));
2286    buffer_b.read_with(cx_b, |buf, _| assert!(!buf.is_dirty()));
2287    buffer_c.read_with(cx_c, |buf, _| assert!(!buf.is_dirty()));
2288
2289    // Make changes on host's file system, see those changes on guest worktrees.
2290    client_a
2291        .fs()
2292        .rename(
2293            "/a/file1.rs".as_ref(),
2294            "/a/file1.js".as_ref(),
2295            Default::default(),
2296        )
2297        .await
2298        .unwrap();
2299    client_a
2300        .fs()
2301        .rename("/a/file2".as_ref(), "/a/file3".as_ref(), Default::default())
2302        .await
2303        .unwrap();
2304    client_a.fs().insert_file("/a/file4", "4".into()).await;
2305    deterministic.run_until_parked();
2306
2307    worktree_a.read_with(cx_a, |tree, _| {
2308        assert_eq!(
2309            tree.paths()
2310                .map(|p| p.to_string_lossy())
2311                .collect::<Vec<_>>(),
2312            ["file1.js", "file3", "file4"]
2313        )
2314    });
2315    worktree_b.read_with(cx_b, |tree, _| {
2316        assert_eq!(
2317            tree.paths()
2318                .map(|p| p.to_string_lossy())
2319                .collect::<Vec<_>>(),
2320            ["file1.js", "file3", "file4"]
2321        )
2322    });
2323    worktree_c.read_with(cx_c, |tree, _| {
2324        assert_eq!(
2325            tree.paths()
2326                .map(|p| p.to_string_lossy())
2327                .collect::<Vec<_>>(),
2328            ["file1.js", "file3", "file4"]
2329        )
2330    });
2331
2332    // Ensure buffer files are updated as well.
2333    buffer_a.read_with(cx_a, |buffer, _| {
2334        assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
2335        assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
2336    });
2337    buffer_b.read_with(cx_b, |buffer, _| {
2338        assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
2339        assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
2340    });
2341    buffer_c.read_with(cx_c, |buffer, _| {
2342        assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
2343        assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
2344    });
2345
2346    let new_buffer_a = project_a
2347        .update(cx_a, |p, cx| p.create_buffer("", None, cx))
2348        .unwrap();
2349    let new_buffer_id = new_buffer_a.read_with(cx_a, |buffer, _| buffer.remote_id());
2350    let new_buffer_b = project_b
2351        .update(cx_b, |p, cx| p.open_buffer_by_id(new_buffer_id, cx))
2352        .await
2353        .unwrap();
2354    new_buffer_b.read_with(cx_b, |buffer, _| {
2355        assert!(buffer.file().is_none());
2356    });
2357
2358    new_buffer_a.update(cx_a, |buffer, cx| {
2359        buffer.edit([(0..0, "ok")], None, cx);
2360    });
2361    project_a
2362        .update(cx_a, |project, cx| {
2363            project.save_buffer_as(new_buffer_a.clone(), "/a/file3.rs".into(), cx)
2364        })
2365        .await
2366        .unwrap();
2367
2368    deterministic.run_until_parked();
2369    new_buffer_b.read_with(cx_b, |buffer_b, _| {
2370        assert_eq!(
2371            buffer_b.file().unwrap().path().as_ref(),
2372            Path::new("file3.rs")
2373        );
2374
2375        new_buffer_a.read_with(cx_a, |buffer_a, _| {
2376            assert_eq!(buffer_b.saved_mtime(), buffer_a.saved_mtime());
2377            assert_eq!(buffer_b.saved_version(), buffer_a.saved_version());
2378        });
2379    });
2380}
2381
2382#[gpui::test(iterations = 10)]
2383async fn test_git_diff_base_change(
2384    deterministic: Arc<Deterministic>,
2385    cx_a: &mut TestAppContext,
2386    cx_b: &mut TestAppContext,
2387) {
2388    deterministic.forbid_parking();
2389    let mut server = TestServer::start(&deterministic).await;
2390    let client_a = server.create_client(cx_a, "user_a").await;
2391    let client_b = server.create_client(cx_b, "user_b").await;
2392    server
2393        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2394        .await;
2395    let active_call_a = cx_a.read(ActiveCall::global);
2396
2397    client_a
2398        .fs()
2399        .insert_tree(
2400            "/dir",
2401            json!({
2402            ".git": {},
2403            "sub": {
2404                ".git": {},
2405                "b.txt": "
2406                    one
2407                    two
2408                    three
2409                ".unindent(),
2410            },
2411            "a.txt": "
2412                    one
2413                    two
2414                    three
2415                ".unindent(),
2416            }),
2417        )
2418        .await;
2419
2420    let (project_local, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2421    let project_id = active_call_a
2422        .update(cx_a, |call, cx| {
2423            call.share_project(project_local.clone(), cx)
2424        })
2425        .await
2426        .unwrap();
2427
2428    let project_remote = client_b.build_remote_project(project_id, cx_b).await;
2429
2430    let diff_base = "
2431        one
2432        three
2433    "
2434    .unindent();
2435
2436    let new_diff_base = "
2437        one
2438        two
2439    "
2440    .unindent();
2441
2442    client_a.fs().set_index_for_repo(
2443        Path::new("/dir/.git"),
2444        &[(Path::new("a.txt"), diff_base.clone())],
2445    );
2446
2447    // Create the buffer
2448    let buffer_local_a = project_local
2449        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2450        .await
2451        .unwrap();
2452
2453    // Wait for it to catch up to the new diff
2454    deterministic.run_until_parked();
2455
2456    // Smoke test diffing
2457    buffer_local_a.read_with(cx_a, |buffer, _| {
2458        assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
2459        git::diff::assert_hunks(
2460            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2461            &buffer,
2462            &diff_base,
2463            &[(1..2, "", "two\n")],
2464        );
2465    });
2466
2467    // Create remote buffer
2468    let buffer_remote_a = project_remote
2469        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2470        .await
2471        .unwrap();
2472
2473    // Wait remote buffer to catch up to the new diff
2474    deterministic.run_until_parked();
2475
2476    // Smoke test diffing
2477    buffer_remote_a.read_with(cx_b, |buffer, _| {
2478        assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
2479        git::diff::assert_hunks(
2480            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2481            &buffer,
2482            &diff_base,
2483            &[(1..2, "", "two\n")],
2484        );
2485    });
2486
2487    client_a.fs().set_index_for_repo(
2488        Path::new("/dir/.git"),
2489        &[(Path::new("a.txt"), new_diff_base.clone())],
2490    );
2491
2492    // Wait for buffer_local_a to receive it
2493    deterministic.run_until_parked();
2494
2495    // Smoke test new diffing
2496    buffer_local_a.read_with(cx_a, |buffer, _| {
2497        assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
2498
2499        git::diff::assert_hunks(
2500            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2501            &buffer,
2502            &diff_base,
2503            &[(2..3, "", "three\n")],
2504        );
2505    });
2506
2507    // Smoke test B
2508    buffer_remote_a.read_with(cx_b, |buffer, _| {
2509        assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
2510        git::diff::assert_hunks(
2511            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2512            &buffer,
2513            &diff_base,
2514            &[(2..3, "", "three\n")],
2515        );
2516    });
2517
2518    //Nested git dir
2519
2520    let diff_base = "
2521        one
2522        three
2523    "
2524    .unindent();
2525
2526    let new_diff_base = "
2527        one
2528        two
2529    "
2530    .unindent();
2531
2532    client_a.fs().set_index_for_repo(
2533        Path::new("/dir/sub/.git"),
2534        &[(Path::new("b.txt"), diff_base.clone())],
2535    );
2536
2537    // Create the buffer
2538    let buffer_local_b = project_local
2539        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "sub/b.txt"), cx))
2540        .await
2541        .unwrap();
2542
2543    // Wait for it to catch up to the new diff
2544    deterministic.run_until_parked();
2545
2546    // Smoke test diffing
2547    buffer_local_b.read_with(cx_a, |buffer, _| {
2548        assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
2549        git::diff::assert_hunks(
2550            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2551            &buffer,
2552            &diff_base,
2553            &[(1..2, "", "two\n")],
2554        );
2555    });
2556
2557    // Create remote buffer
2558    let buffer_remote_b = project_remote
2559        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "sub/b.txt"), cx))
2560        .await
2561        .unwrap();
2562
2563    // Wait remote buffer to catch up to the new diff
2564    deterministic.run_until_parked();
2565
2566    // Smoke test diffing
2567    buffer_remote_b.read_with(cx_b, |buffer, _| {
2568        assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
2569        git::diff::assert_hunks(
2570            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2571            &buffer,
2572            &diff_base,
2573            &[(1..2, "", "two\n")],
2574        );
2575    });
2576
2577    client_a.fs().set_index_for_repo(
2578        Path::new("/dir/sub/.git"),
2579        &[(Path::new("b.txt"), new_diff_base.clone())],
2580    );
2581
2582    // Wait for buffer_local_b to receive it
2583    deterministic.run_until_parked();
2584
2585    // Smoke test new diffing
2586    buffer_local_b.read_with(cx_a, |buffer, _| {
2587        assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
2588        println!("{:?}", buffer.as_rope().to_string());
2589        println!("{:?}", buffer.diff_base());
2590        println!(
2591            "{:?}",
2592            buffer
2593                .snapshot()
2594                .git_diff_hunks_in_row_range(0..4)
2595                .collect::<Vec<_>>()
2596        );
2597
2598        git::diff::assert_hunks(
2599            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2600            &buffer,
2601            &diff_base,
2602            &[(2..3, "", "three\n")],
2603        );
2604    });
2605
2606    // Smoke test B
2607    buffer_remote_b.read_with(cx_b, |buffer, _| {
2608        assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
2609        git::diff::assert_hunks(
2610            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2611            &buffer,
2612            &diff_base,
2613            &[(2..3, "", "three\n")],
2614        );
2615    });
2616}
2617
2618#[gpui::test]
2619async fn test_git_branch_name(
2620    deterministic: Arc<Deterministic>,
2621    cx_a: &mut TestAppContext,
2622    cx_b: &mut TestAppContext,
2623    cx_c: &mut TestAppContext,
2624) {
2625    deterministic.forbid_parking();
2626    let mut server = TestServer::start(&deterministic).await;
2627    let client_a = server.create_client(cx_a, "user_a").await;
2628    let client_b = server.create_client(cx_b, "user_b").await;
2629    let client_c = server.create_client(cx_c, "user_c").await;
2630    server
2631        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
2632        .await;
2633    let active_call_a = cx_a.read(ActiveCall::global);
2634
2635    client_a
2636        .fs()
2637        .insert_tree(
2638            "/dir",
2639            json!({
2640            ".git": {},
2641            }),
2642        )
2643        .await;
2644
2645    let (project_local, _worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2646    let project_id = active_call_a
2647        .update(cx_a, |call, cx| {
2648            call.share_project(project_local.clone(), cx)
2649        })
2650        .await
2651        .unwrap();
2652
2653    let project_remote = client_b.build_remote_project(project_id, cx_b).await;
2654    client_a
2655        .fs()
2656        .set_branch_name(Path::new("/dir/.git"), Some("branch-1"));
2657
2658    // Wait for it to catch up to the new branch
2659    deterministic.run_until_parked();
2660
2661    #[track_caller]
2662    fn assert_branch(branch_name: Option<impl Into<String>>, project: &Project, cx: &AppContext) {
2663        let branch_name = branch_name.map(Into::into);
2664        let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
2665        assert_eq!(worktrees.len(), 1);
2666        let worktree = worktrees[0].clone();
2667        let root_entry = worktree.read(cx).snapshot().root_git_entry().unwrap();
2668        assert_eq!(root_entry.branch(), branch_name.map(Into::into));
2669    }
2670
2671    // Smoke test branch reading
2672    project_local.read_with(cx_a, |project, cx| {
2673        assert_branch(Some("branch-1"), project, cx)
2674    });
2675    project_remote.read_with(cx_b, |project, cx| {
2676        assert_branch(Some("branch-1"), project, cx)
2677    });
2678
2679    client_a
2680        .fs()
2681        .set_branch_name(Path::new("/dir/.git"), Some("branch-2"));
2682
2683    // Wait for buffer_local_a to receive it
2684    deterministic.run_until_parked();
2685
2686    // Smoke test branch reading
2687    project_local.read_with(cx_a, |project, cx| {
2688        assert_branch(Some("branch-2"), project, cx)
2689    });
2690    project_remote.read_with(cx_b, |project, cx| {
2691        assert_branch(Some("branch-2"), project, cx)
2692    });
2693
2694    let project_remote_c = client_c.build_remote_project(project_id, cx_c).await;
2695    deterministic.run_until_parked();
2696    project_remote_c.read_with(cx_c, |project, cx| {
2697        assert_branch(Some("branch-2"), project, cx)
2698    });
2699}
2700
2701#[gpui::test]
2702async fn test_git_status_sync(
2703    deterministic: Arc<Deterministic>,
2704    cx_a: &mut TestAppContext,
2705    cx_b: &mut TestAppContext,
2706    cx_c: &mut TestAppContext,
2707) {
2708    deterministic.forbid_parking();
2709    let mut server = TestServer::start(&deterministic).await;
2710    let client_a = server.create_client(cx_a, "user_a").await;
2711    let client_b = server.create_client(cx_b, "user_b").await;
2712    let client_c = server.create_client(cx_c, "user_c").await;
2713    server
2714        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
2715        .await;
2716    let active_call_a = cx_a.read(ActiveCall::global);
2717
2718    client_a
2719        .fs()
2720        .insert_tree(
2721            "/dir",
2722            json!({
2723            ".git": {},
2724            "a.txt": "a",
2725            "b.txt": "b",
2726            }),
2727        )
2728        .await;
2729
2730    const A_TXT: &'static str = "a.txt";
2731    const B_TXT: &'static str = "b.txt";
2732
2733    client_a.fs().set_status_for_repo_via_git_operation(
2734        Path::new("/dir/.git"),
2735        &[
2736            (&Path::new(A_TXT), GitFileStatus::Added),
2737            (&Path::new(B_TXT), GitFileStatus::Added),
2738        ],
2739    );
2740
2741    let (project_local, _worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2742    let project_id = active_call_a
2743        .update(cx_a, |call, cx| {
2744            call.share_project(project_local.clone(), cx)
2745        })
2746        .await
2747        .unwrap();
2748
2749    let project_remote = client_b.build_remote_project(project_id, cx_b).await;
2750
2751    // Wait for it to catch up to the new status
2752    deterministic.run_until_parked();
2753
2754    #[track_caller]
2755    fn assert_status(
2756        file: &impl AsRef<Path>,
2757        status: Option<GitFileStatus>,
2758        project: &Project,
2759        cx: &AppContext,
2760    ) {
2761        let file = file.as_ref();
2762        let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
2763        assert_eq!(worktrees.len(), 1);
2764        let worktree = worktrees[0].clone();
2765        let snapshot = worktree.read(cx).snapshot();
2766        assert_eq!(snapshot.status_for_file(file), status);
2767    }
2768
2769    // Smoke test status reading
2770    project_local.read_with(cx_a, |project, cx| {
2771        assert_status(&Path::new(A_TXT), Some(GitFileStatus::Added), project, cx);
2772        assert_status(&Path::new(B_TXT), Some(GitFileStatus::Added), project, cx);
2773    });
2774    project_remote.read_with(cx_b, |project, cx| {
2775        assert_status(&Path::new(A_TXT), Some(GitFileStatus::Added), project, cx);
2776        assert_status(&Path::new(B_TXT), Some(GitFileStatus::Added), project, cx);
2777    });
2778
2779    client_a.fs().set_status_for_repo_via_working_copy_change(
2780        Path::new("/dir/.git"),
2781        &[
2782            (&Path::new(A_TXT), GitFileStatus::Modified),
2783            (&Path::new(B_TXT), GitFileStatus::Modified),
2784        ],
2785    );
2786
2787    // Wait for buffer_local_a to receive it
2788    deterministic.run_until_parked();
2789
2790    // Smoke test status reading
2791    project_local.read_with(cx_a, |project, cx| {
2792        assert_status(
2793            &Path::new(A_TXT),
2794            Some(GitFileStatus::Modified),
2795            project,
2796            cx,
2797        );
2798        assert_status(
2799            &Path::new(B_TXT),
2800            Some(GitFileStatus::Modified),
2801            project,
2802            cx,
2803        );
2804    });
2805    project_remote.read_with(cx_b, |project, cx| {
2806        assert_status(
2807            &Path::new(A_TXT),
2808            Some(GitFileStatus::Modified),
2809            project,
2810            cx,
2811        );
2812        assert_status(
2813            &Path::new(B_TXT),
2814            Some(GitFileStatus::Modified),
2815            project,
2816            cx,
2817        );
2818    });
2819
2820    // And synchronization while joining
2821    let project_remote_c = client_c.build_remote_project(project_id, cx_c).await;
2822    deterministic.run_until_parked();
2823
2824    project_remote_c.read_with(cx_c, |project, cx| {
2825        assert_status(
2826            &Path::new(A_TXT),
2827            Some(GitFileStatus::Modified),
2828            project,
2829            cx,
2830        );
2831        assert_status(
2832            &Path::new(B_TXT),
2833            Some(GitFileStatus::Modified),
2834            project,
2835            cx,
2836        );
2837    });
2838}
2839
2840#[gpui::test(iterations = 10)]
2841async fn test_fs_operations(
2842    deterministic: Arc<Deterministic>,
2843    cx_a: &mut TestAppContext,
2844    cx_b: &mut TestAppContext,
2845) {
2846    deterministic.forbid_parking();
2847    let mut server = TestServer::start(&deterministic).await;
2848    let client_a = server.create_client(cx_a, "user_a").await;
2849    let client_b = server.create_client(cx_b, "user_b").await;
2850    server
2851        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2852        .await;
2853    let active_call_a = cx_a.read(ActiveCall::global);
2854
2855    client_a
2856        .fs()
2857        .insert_tree(
2858            "/dir",
2859            json!({
2860                "a.txt": "a-contents",
2861                "b.txt": "b-contents",
2862            }),
2863        )
2864        .await;
2865    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2866    let project_id = active_call_a
2867        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2868        .await
2869        .unwrap();
2870    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2871
2872    let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
2873    let worktree_b = project_b.read_with(cx_b, |project, cx| project.worktrees(cx).next().unwrap());
2874
2875    let entry = project_b
2876        .update(cx_b, |project, cx| {
2877            project
2878                .create_entry((worktree_id, "c.txt"), false, cx)
2879                .unwrap()
2880        })
2881        .await
2882        .unwrap();
2883    worktree_a.read_with(cx_a, |worktree, _| {
2884        assert_eq!(
2885            worktree
2886                .paths()
2887                .map(|p| p.to_string_lossy())
2888                .collect::<Vec<_>>(),
2889            ["a.txt", "b.txt", "c.txt"]
2890        );
2891    });
2892    worktree_b.read_with(cx_b, |worktree, _| {
2893        assert_eq!(
2894            worktree
2895                .paths()
2896                .map(|p| p.to_string_lossy())
2897                .collect::<Vec<_>>(),
2898            ["a.txt", "b.txt", "c.txt"]
2899        );
2900    });
2901
2902    project_b
2903        .update(cx_b, |project, cx| {
2904            project.rename_entry(entry.id, Path::new("d.txt"), cx)
2905        })
2906        .unwrap()
2907        .await
2908        .unwrap();
2909    worktree_a.read_with(cx_a, |worktree, _| {
2910        assert_eq!(
2911            worktree
2912                .paths()
2913                .map(|p| p.to_string_lossy())
2914                .collect::<Vec<_>>(),
2915            ["a.txt", "b.txt", "d.txt"]
2916        );
2917    });
2918    worktree_b.read_with(cx_b, |worktree, _| {
2919        assert_eq!(
2920            worktree
2921                .paths()
2922                .map(|p| p.to_string_lossy())
2923                .collect::<Vec<_>>(),
2924            ["a.txt", "b.txt", "d.txt"]
2925        );
2926    });
2927
2928    let dir_entry = project_b
2929        .update(cx_b, |project, cx| {
2930            project
2931                .create_entry((worktree_id, "DIR"), true, cx)
2932                .unwrap()
2933        })
2934        .await
2935        .unwrap();
2936    worktree_a.read_with(cx_a, |worktree, _| {
2937        assert_eq!(
2938            worktree
2939                .paths()
2940                .map(|p| p.to_string_lossy())
2941                .collect::<Vec<_>>(),
2942            ["DIR", "a.txt", "b.txt", "d.txt"]
2943        );
2944    });
2945    worktree_b.read_with(cx_b, |worktree, _| {
2946        assert_eq!(
2947            worktree
2948                .paths()
2949                .map(|p| p.to_string_lossy())
2950                .collect::<Vec<_>>(),
2951            ["DIR", "a.txt", "b.txt", "d.txt"]
2952        );
2953    });
2954
2955    project_b
2956        .update(cx_b, |project, cx| {
2957            project
2958                .create_entry((worktree_id, "DIR/e.txt"), false, cx)
2959                .unwrap()
2960        })
2961        .await
2962        .unwrap();
2963    project_b
2964        .update(cx_b, |project, cx| {
2965            project
2966                .create_entry((worktree_id, "DIR/SUBDIR"), true, cx)
2967                .unwrap()
2968        })
2969        .await
2970        .unwrap();
2971    project_b
2972        .update(cx_b, |project, cx| {
2973            project
2974                .create_entry((worktree_id, "DIR/SUBDIR/f.txt"), false, cx)
2975                .unwrap()
2976        })
2977        .await
2978        .unwrap();
2979    worktree_a.read_with(cx_a, |worktree, _| {
2980        assert_eq!(
2981            worktree
2982                .paths()
2983                .map(|p| p.to_string_lossy())
2984                .collect::<Vec<_>>(),
2985            [
2986                "DIR",
2987                "DIR/SUBDIR",
2988                "DIR/SUBDIR/f.txt",
2989                "DIR/e.txt",
2990                "a.txt",
2991                "b.txt",
2992                "d.txt"
2993            ]
2994        );
2995    });
2996    worktree_b.read_with(cx_b, |worktree, _| {
2997        assert_eq!(
2998            worktree
2999                .paths()
3000                .map(|p| p.to_string_lossy())
3001                .collect::<Vec<_>>(),
3002            [
3003                "DIR",
3004                "DIR/SUBDIR",
3005                "DIR/SUBDIR/f.txt",
3006                "DIR/e.txt",
3007                "a.txt",
3008                "b.txt",
3009                "d.txt"
3010            ]
3011        );
3012    });
3013
3014    project_b
3015        .update(cx_b, |project, cx| {
3016            project
3017                .copy_entry(entry.id, Path::new("f.txt"), cx)
3018                .unwrap()
3019        })
3020        .await
3021        .unwrap();
3022    worktree_a.read_with(cx_a, |worktree, _| {
3023        assert_eq!(
3024            worktree
3025                .paths()
3026                .map(|p| p.to_string_lossy())
3027                .collect::<Vec<_>>(),
3028            [
3029                "DIR",
3030                "DIR/SUBDIR",
3031                "DIR/SUBDIR/f.txt",
3032                "DIR/e.txt",
3033                "a.txt",
3034                "b.txt",
3035                "d.txt",
3036                "f.txt"
3037            ]
3038        );
3039    });
3040    worktree_b.read_with(cx_b, |worktree, _| {
3041        assert_eq!(
3042            worktree
3043                .paths()
3044                .map(|p| p.to_string_lossy())
3045                .collect::<Vec<_>>(),
3046            [
3047                "DIR",
3048                "DIR/SUBDIR",
3049                "DIR/SUBDIR/f.txt",
3050                "DIR/e.txt",
3051                "a.txt",
3052                "b.txt",
3053                "d.txt",
3054                "f.txt"
3055            ]
3056        );
3057    });
3058
3059    project_b
3060        .update(cx_b, |project, cx| {
3061            project.delete_entry(dir_entry.id, cx).unwrap()
3062        })
3063        .await
3064        .unwrap();
3065    deterministic.run_until_parked();
3066
3067    worktree_a.read_with(cx_a, |worktree, _| {
3068        assert_eq!(
3069            worktree
3070                .paths()
3071                .map(|p| p.to_string_lossy())
3072                .collect::<Vec<_>>(),
3073            ["a.txt", "b.txt", "d.txt", "f.txt"]
3074        );
3075    });
3076    worktree_b.read_with(cx_b, |worktree, _| {
3077        assert_eq!(
3078            worktree
3079                .paths()
3080                .map(|p| p.to_string_lossy())
3081                .collect::<Vec<_>>(),
3082            ["a.txt", "b.txt", "d.txt", "f.txt"]
3083        );
3084    });
3085
3086    project_b
3087        .update(cx_b, |project, cx| {
3088            project.delete_entry(entry.id, cx).unwrap()
3089        })
3090        .await
3091        .unwrap();
3092    worktree_a.read_with(cx_a, |worktree, _| {
3093        assert_eq!(
3094            worktree
3095                .paths()
3096                .map(|p| p.to_string_lossy())
3097                .collect::<Vec<_>>(),
3098            ["a.txt", "b.txt", "f.txt"]
3099        );
3100    });
3101    worktree_b.read_with(cx_b, |worktree, _| {
3102        assert_eq!(
3103            worktree
3104                .paths()
3105                .map(|p| p.to_string_lossy())
3106                .collect::<Vec<_>>(),
3107            ["a.txt", "b.txt", "f.txt"]
3108        );
3109    });
3110}
3111
3112#[gpui::test(iterations = 10)]
3113async fn test_local_settings(
3114    deterministic: Arc<Deterministic>,
3115    cx_a: &mut TestAppContext,
3116    cx_b: &mut TestAppContext,
3117) {
3118    deterministic.forbid_parking();
3119    let mut server = TestServer::start(&deterministic).await;
3120    let client_a = server.create_client(cx_a, "user_a").await;
3121    let client_b = server.create_client(cx_b, "user_b").await;
3122    server
3123        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3124        .await;
3125    let active_call_a = cx_a.read(ActiveCall::global);
3126
3127    // As client A, open a project that contains some local settings files
3128    client_a
3129        .fs()
3130        .insert_tree(
3131            "/dir",
3132            json!({
3133                ".zed": {
3134                    "settings.json": r#"{ "tab_size": 2 }"#
3135                },
3136                "a": {
3137                    ".zed": {
3138                        "settings.json": r#"{ "tab_size": 8 }"#
3139                    },
3140                    "a.txt": "a-contents",
3141                },
3142                "b": {
3143                    "b.txt": "b-contents",
3144                }
3145            }),
3146        )
3147        .await;
3148    let (project_a, _) = client_a.build_local_project("/dir", cx_a).await;
3149    deterministic.run_until_parked();
3150    let project_id = active_call_a
3151        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3152        .await
3153        .unwrap();
3154
3155    // As client B, join that project and observe the local settings.
3156    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3157    let worktree_b = project_b.read_with(cx_b, |project, cx| project.worktrees(cx).next().unwrap());
3158    deterministic.run_until_parked();
3159    cx_b.read(|cx| {
3160        let store = cx.global::<SettingsStore>();
3161        assert_eq!(
3162            store.local_settings(worktree_b.id()).collect::<Vec<_>>(),
3163            &[
3164                (Path::new("").into(), r#"{"tab_size":2}"#.to_string()),
3165                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3166            ]
3167        )
3168    });
3169
3170    // As client A, update a settings file. As Client B, see the changed settings.
3171    client_a
3172        .fs()
3173        .insert_file("/dir/.zed/settings.json", r#"{}"#.into())
3174        .await;
3175    deterministic.run_until_parked();
3176    cx_b.read(|cx| {
3177        let store = cx.global::<SettingsStore>();
3178        assert_eq!(
3179            store.local_settings(worktree_b.id()).collect::<Vec<_>>(),
3180            &[
3181                (Path::new("").into(), r#"{}"#.to_string()),
3182                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3183            ]
3184        )
3185    });
3186
3187    // As client A, create and remove some settings files. As client B, see the changed settings.
3188    client_a
3189        .fs()
3190        .remove_file("/dir/.zed/settings.json".as_ref(), Default::default())
3191        .await
3192        .unwrap();
3193    client_a
3194        .fs()
3195        .create_dir("/dir/b/.zed".as_ref())
3196        .await
3197        .unwrap();
3198    client_a
3199        .fs()
3200        .insert_file("/dir/b/.zed/settings.json", r#"{"tab_size": 4}"#.into())
3201        .await;
3202    deterministic.run_until_parked();
3203    cx_b.read(|cx| {
3204        let store = cx.global::<SettingsStore>();
3205        assert_eq!(
3206            store.local_settings(worktree_b.id()).collect::<Vec<_>>(),
3207            &[
3208                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3209                (Path::new("b").into(), r#"{"tab_size":4}"#.to_string()),
3210            ]
3211        )
3212    });
3213
3214    // As client B, disconnect.
3215    server.forbid_connections();
3216    server.disconnect_client(client_b.peer_id().unwrap());
3217
3218    // As client A, change and remove settings files while client B is disconnected.
3219    client_a
3220        .fs()
3221        .insert_file("/dir/a/.zed/settings.json", r#"{"hard_tabs":true}"#.into())
3222        .await;
3223    client_a
3224        .fs()
3225        .remove_file("/dir/b/.zed/settings.json".as_ref(), Default::default())
3226        .await
3227        .unwrap();
3228    deterministic.run_until_parked();
3229
3230    // As client B, reconnect and see the changed settings.
3231    server.allow_connections();
3232    deterministic.advance_clock(RECEIVE_TIMEOUT);
3233    cx_b.read(|cx| {
3234        let store = cx.global::<SettingsStore>();
3235        assert_eq!(
3236            store.local_settings(worktree_b.id()).collect::<Vec<_>>(),
3237            &[(Path::new("a").into(), r#"{"hard_tabs":true}"#.to_string()),]
3238        )
3239    });
3240}
3241
3242#[gpui::test(iterations = 10)]
3243async fn test_buffer_conflict_after_save(
3244    deterministic: Arc<Deterministic>,
3245    cx_a: &mut TestAppContext,
3246    cx_b: &mut TestAppContext,
3247) {
3248    deterministic.forbid_parking();
3249    let mut server = TestServer::start(&deterministic).await;
3250    let client_a = server.create_client(cx_a, "user_a").await;
3251    let client_b = server.create_client(cx_b, "user_b").await;
3252    server
3253        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3254        .await;
3255    let active_call_a = cx_a.read(ActiveCall::global);
3256
3257    client_a
3258        .fs()
3259        .insert_tree(
3260            "/dir",
3261            json!({
3262                "a.txt": "a-contents",
3263            }),
3264        )
3265        .await;
3266    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3267    let project_id = active_call_a
3268        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3269        .await
3270        .unwrap();
3271    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3272
3273    // Open a buffer as client B
3274    let buffer_b = project_b
3275        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3276        .await
3277        .unwrap();
3278
3279    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "world ")], None, cx));
3280    buffer_b.read_with(cx_b, |buf, _| {
3281        assert!(buf.is_dirty());
3282        assert!(!buf.has_conflict());
3283    });
3284
3285    project_b
3286        .update(cx_b, |project, cx| {
3287            project.save_buffer(buffer_b.clone(), cx)
3288        })
3289        .await
3290        .unwrap();
3291    cx_a.foreground().forbid_parking();
3292    buffer_b.read_with(cx_b, |buffer_b, _| assert!(!buffer_b.is_dirty()));
3293    buffer_b.read_with(cx_b, |buf, _| {
3294        assert!(!buf.has_conflict());
3295    });
3296
3297    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "hello ")], None, cx));
3298    buffer_b.read_with(cx_b, |buf, _| {
3299        assert!(buf.is_dirty());
3300        assert!(!buf.has_conflict());
3301    });
3302}
3303
3304#[gpui::test(iterations = 10)]
3305async fn test_buffer_reloading(
3306    deterministic: Arc<Deterministic>,
3307    cx_a: &mut TestAppContext,
3308    cx_b: &mut TestAppContext,
3309) {
3310    deterministic.forbid_parking();
3311    let mut server = TestServer::start(&deterministic).await;
3312    let client_a = server.create_client(cx_a, "user_a").await;
3313    let client_b = server.create_client(cx_b, "user_b").await;
3314    server
3315        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3316        .await;
3317    let active_call_a = cx_a.read(ActiveCall::global);
3318
3319    client_a
3320        .fs()
3321        .insert_tree(
3322            "/dir",
3323            json!({
3324                "a.txt": "a\nb\nc",
3325            }),
3326        )
3327        .await;
3328    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3329    let project_id = active_call_a
3330        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3331        .await
3332        .unwrap();
3333    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3334
3335    // Open a buffer as client B
3336    let buffer_b = project_b
3337        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3338        .await
3339        .unwrap();
3340    buffer_b.read_with(cx_b, |buf, _| {
3341        assert!(!buf.is_dirty());
3342        assert!(!buf.has_conflict());
3343        assert_eq!(buf.line_ending(), LineEnding::Unix);
3344    });
3345
3346    let new_contents = Rope::from("d\ne\nf");
3347    client_a
3348        .fs()
3349        .save("/dir/a.txt".as_ref(), &new_contents, LineEnding::Windows)
3350        .await
3351        .unwrap();
3352    cx_a.foreground().run_until_parked();
3353    buffer_b.read_with(cx_b, |buf, _| {
3354        assert_eq!(buf.text(), new_contents.to_string());
3355        assert!(!buf.is_dirty());
3356        assert!(!buf.has_conflict());
3357        assert_eq!(buf.line_ending(), LineEnding::Windows);
3358    });
3359}
3360
3361#[gpui::test(iterations = 10)]
3362async fn test_editing_while_guest_opens_buffer(
3363    deterministic: Arc<Deterministic>,
3364    cx_a: &mut TestAppContext,
3365    cx_b: &mut TestAppContext,
3366) {
3367    deterministic.forbid_parking();
3368    let mut server = TestServer::start(&deterministic).await;
3369    let client_a = server.create_client(cx_a, "user_a").await;
3370    let client_b = server.create_client(cx_b, "user_b").await;
3371    server
3372        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3373        .await;
3374    let active_call_a = cx_a.read(ActiveCall::global);
3375
3376    client_a
3377        .fs()
3378        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3379        .await;
3380    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3381    let project_id = active_call_a
3382        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3383        .await
3384        .unwrap();
3385    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3386
3387    // Open a buffer as client A
3388    let buffer_a = project_a
3389        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3390        .await
3391        .unwrap();
3392
3393    // Start opening the same buffer as client B
3394    let buffer_b = cx_b
3395        .background()
3396        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)));
3397
3398    // Edit the buffer as client A while client B is still opening it.
3399    cx_b.background().simulate_random_delay().await;
3400    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "X")], None, cx));
3401    cx_b.background().simulate_random_delay().await;
3402    buffer_a.update(cx_a, |buf, cx| buf.edit([(1..1, "Y")], None, cx));
3403
3404    let text = buffer_a.read_with(cx_a, |buf, _| buf.text());
3405    let buffer_b = buffer_b.await.unwrap();
3406    cx_a.foreground().run_until_parked();
3407    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), text));
3408}
3409
3410#[gpui::test]
3411async fn test_newline_above_or_below_does_not_move_guest_cursor(
3412    deterministic: Arc<Deterministic>,
3413    cx_a: &mut TestAppContext,
3414    cx_b: &mut TestAppContext,
3415) {
3416    deterministic.forbid_parking();
3417    let mut server = TestServer::start(&deterministic).await;
3418    let client_a = server.create_client(cx_a, "user_a").await;
3419    let client_b = server.create_client(cx_b, "user_b").await;
3420    server
3421        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3422        .await;
3423    let active_call_a = cx_a.read(ActiveCall::global);
3424
3425    client_a
3426        .fs()
3427        .insert_tree("/dir", json!({ "a.txt": "Some text\n" }))
3428        .await;
3429    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3430    let project_id = active_call_a
3431        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3432        .await
3433        .unwrap();
3434
3435    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3436
3437    // Open a buffer as client A
3438    let buffer_a = project_a
3439        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3440        .await
3441        .unwrap();
3442    let window_a = cx_a.add_window(|_| EmptyView);
3443    let editor_a = window_a.add_view(cx_a, |cx| Editor::for_buffer(buffer_a, Some(project_a), cx));
3444    let mut editor_cx_a = EditorTestContext {
3445        cx: cx_a,
3446        window: window_a.into(),
3447        editor: editor_a,
3448    };
3449
3450    // Open a buffer as client B
3451    let buffer_b = project_b
3452        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3453        .await
3454        .unwrap();
3455    let window_b = cx_b.add_window(|_| EmptyView);
3456    let editor_b = window_b.add_view(cx_b, |cx| Editor::for_buffer(buffer_b, Some(project_b), cx));
3457    let mut editor_cx_b = EditorTestContext {
3458        cx: cx_b,
3459        window: window_b.into(),
3460        editor: editor_b,
3461    };
3462
3463    // Test newline above
3464    editor_cx_a.set_selections_state(indoc! {"
3465        Some textˇ
3466    "});
3467    editor_cx_b.set_selections_state(indoc! {"
3468        Some textˇ
3469    "});
3470    editor_cx_a.update_editor(|editor, cx| editor.newline_above(&editor::NewlineAbove, cx));
3471    deterministic.run_until_parked();
3472    editor_cx_a.assert_editor_state(indoc! {"
3473        ˇ
3474        Some text
3475    "});
3476    editor_cx_b.assert_editor_state(indoc! {"
3477
3478        Some textˇ
3479    "});
3480
3481    // Test newline below
3482    editor_cx_a.set_selections_state(indoc! {"
3483
3484        Some textˇ
3485    "});
3486    editor_cx_b.set_selections_state(indoc! {"
3487
3488        Some textˇ
3489    "});
3490    editor_cx_a.update_editor(|editor, cx| editor.newline_below(&editor::NewlineBelow, cx));
3491    deterministic.run_until_parked();
3492    editor_cx_a.assert_editor_state(indoc! {"
3493
3494        Some text
3495        ˇ
3496    "});
3497    editor_cx_b.assert_editor_state(indoc! {"
3498
3499        Some textˇ
3500
3501    "});
3502}
3503
3504#[gpui::test(iterations = 10)]
3505async fn test_leaving_worktree_while_opening_buffer(
3506    deterministic: Arc<Deterministic>,
3507    cx_a: &mut TestAppContext,
3508    cx_b: &mut TestAppContext,
3509) {
3510    deterministic.forbid_parking();
3511    let mut server = TestServer::start(&deterministic).await;
3512    let client_a = server.create_client(cx_a, "user_a").await;
3513    let client_b = server.create_client(cx_b, "user_b").await;
3514    server
3515        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3516        .await;
3517    let active_call_a = cx_a.read(ActiveCall::global);
3518
3519    client_a
3520        .fs()
3521        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3522        .await;
3523    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3524    let project_id = active_call_a
3525        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3526        .await
3527        .unwrap();
3528    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3529
3530    // See that a guest has joined as client A.
3531    cx_a.foreground().run_until_parked();
3532    project_a.read_with(cx_a, |p, _| assert_eq!(p.collaborators().len(), 1));
3533
3534    // Begin opening a buffer as client B, but leave the project before the open completes.
3535    let buffer_b = cx_b
3536        .background()
3537        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)));
3538    cx_b.update(|_| drop(project_b));
3539    drop(buffer_b);
3540
3541    // See that the guest has left.
3542    cx_a.foreground().run_until_parked();
3543    project_a.read_with(cx_a, |p, _| assert!(p.collaborators().is_empty()));
3544}
3545
3546#[gpui::test(iterations = 10)]
3547async fn test_canceling_buffer_opening(
3548    deterministic: Arc<Deterministic>,
3549    cx_a: &mut TestAppContext,
3550    cx_b: &mut TestAppContext,
3551) {
3552    deterministic.forbid_parking();
3553
3554    let mut server = TestServer::start(&deterministic).await;
3555    let client_a = server.create_client(cx_a, "user_a").await;
3556    let client_b = server.create_client(cx_b, "user_b").await;
3557    server
3558        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3559        .await;
3560    let active_call_a = cx_a.read(ActiveCall::global);
3561
3562    client_a
3563        .fs()
3564        .insert_tree(
3565            "/dir",
3566            json!({
3567                "a.txt": "abc",
3568            }),
3569        )
3570        .await;
3571    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3572    let project_id = active_call_a
3573        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3574        .await
3575        .unwrap();
3576    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3577
3578    let buffer_a = project_a
3579        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3580        .await
3581        .unwrap();
3582
3583    // Open a buffer as client B but cancel after a random amount of time.
3584    let buffer_b = project_b.update(cx_b, |p, cx| {
3585        p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3586    });
3587    deterministic.simulate_random_delay().await;
3588    drop(buffer_b);
3589
3590    // Try opening the same buffer again as client B, and ensure we can
3591    // still do it despite the cancellation above.
3592    let buffer_b = project_b
3593        .update(cx_b, |p, cx| {
3594            p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3595        })
3596        .await
3597        .unwrap();
3598    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "abc"));
3599}
3600
3601#[gpui::test(iterations = 10)]
3602async fn test_leaving_project(
3603    deterministic: Arc<Deterministic>,
3604    cx_a: &mut TestAppContext,
3605    cx_b: &mut TestAppContext,
3606    cx_c: &mut TestAppContext,
3607) {
3608    deterministic.forbid_parking();
3609    let mut server = TestServer::start(&deterministic).await;
3610    let client_a = server.create_client(cx_a, "user_a").await;
3611    let client_b = server.create_client(cx_b, "user_b").await;
3612    let client_c = server.create_client(cx_c, "user_c").await;
3613    server
3614        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3615        .await;
3616    let active_call_a = cx_a.read(ActiveCall::global);
3617
3618    client_a
3619        .fs()
3620        .insert_tree(
3621            "/a",
3622            json!({
3623                "a.txt": "a-contents",
3624                "b.txt": "b-contents",
3625            }),
3626        )
3627        .await;
3628    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
3629    let project_id = active_call_a
3630        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3631        .await
3632        .unwrap();
3633    let project_b1 = client_b.build_remote_project(project_id, cx_b).await;
3634    let project_c = client_c.build_remote_project(project_id, cx_c).await;
3635
3636    // Client A sees that a guest has joined.
3637    deterministic.run_until_parked();
3638    project_a.read_with(cx_a, |project, _| {
3639        assert_eq!(project.collaborators().len(), 2);
3640    });
3641    project_b1.read_with(cx_b, |project, _| {
3642        assert_eq!(project.collaborators().len(), 2);
3643    });
3644    project_c.read_with(cx_c, |project, _| {
3645        assert_eq!(project.collaborators().len(), 2);
3646    });
3647
3648    // Client B opens a buffer.
3649    let buffer_b1 = project_b1
3650        .update(cx_b, |project, cx| {
3651            let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
3652            project.open_buffer((worktree_id, "a.txt"), cx)
3653        })
3654        .await
3655        .unwrap();
3656    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3657
3658    // Drop client B's project and ensure client A and client C observe client B leaving.
3659    cx_b.update(|_| drop(project_b1));
3660    deterministic.run_until_parked();
3661    project_a.read_with(cx_a, |project, _| {
3662        assert_eq!(project.collaborators().len(), 1);
3663    });
3664    project_c.read_with(cx_c, |project, _| {
3665        assert_eq!(project.collaborators().len(), 1);
3666    });
3667
3668    // Client B re-joins the project and can open buffers as before.
3669    let project_b2 = client_b.build_remote_project(project_id, cx_b).await;
3670    deterministic.run_until_parked();
3671    project_a.read_with(cx_a, |project, _| {
3672        assert_eq!(project.collaborators().len(), 2);
3673    });
3674    project_b2.read_with(cx_b, |project, _| {
3675        assert_eq!(project.collaborators().len(), 2);
3676    });
3677    project_c.read_with(cx_c, |project, _| {
3678        assert_eq!(project.collaborators().len(), 2);
3679    });
3680
3681    let buffer_b2 = project_b2
3682        .update(cx_b, |project, cx| {
3683            let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
3684            project.open_buffer((worktree_id, "a.txt"), cx)
3685        })
3686        .await
3687        .unwrap();
3688    buffer_b2.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3689
3690    // Drop client B's connection and ensure client A and client C observe client B leaving.
3691    client_b.disconnect(&cx_b.to_async());
3692    deterministic.advance_clock(RECONNECT_TIMEOUT);
3693    project_a.read_with(cx_a, |project, _| {
3694        assert_eq!(project.collaborators().len(), 1);
3695    });
3696    project_b2.read_with(cx_b, |project, _| {
3697        assert!(project.is_read_only());
3698    });
3699    project_c.read_with(cx_c, |project, _| {
3700        assert_eq!(project.collaborators().len(), 1);
3701    });
3702
3703    // Client B can't join the project, unless they re-join the room.
3704    cx_b.spawn(|cx| {
3705        Project::remote(
3706            project_id,
3707            client_b.app_state.client.clone(),
3708            client_b.user_store().clone(),
3709            client_b.language_registry().clone(),
3710            FakeFs::new(cx.background()),
3711            cx,
3712        )
3713    })
3714    .await
3715    .unwrap_err();
3716
3717    // Simulate connection loss for client C and ensure client A observes client C leaving the project.
3718    client_c.wait_for_current_user(cx_c).await;
3719    server.forbid_connections();
3720    server.disconnect_client(client_c.peer_id().unwrap());
3721    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
3722    deterministic.run_until_parked();
3723    project_a.read_with(cx_a, |project, _| {
3724        assert_eq!(project.collaborators().len(), 0);
3725    });
3726    project_b2.read_with(cx_b, |project, _| {
3727        assert!(project.is_read_only());
3728    });
3729    project_c.read_with(cx_c, |project, _| {
3730        assert!(project.is_read_only());
3731    });
3732}
3733
3734#[gpui::test(iterations = 10)]
3735async fn test_collaborating_with_diagnostics(
3736    deterministic: Arc<Deterministic>,
3737    cx_a: &mut TestAppContext,
3738    cx_b: &mut TestAppContext,
3739    cx_c: &mut TestAppContext,
3740) {
3741    deterministic.forbid_parking();
3742    let mut server = TestServer::start(&deterministic).await;
3743    let client_a = server.create_client(cx_a, "user_a").await;
3744    let client_b = server.create_client(cx_b, "user_b").await;
3745    let client_c = server.create_client(cx_c, "user_c").await;
3746    server
3747        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3748        .await;
3749    let active_call_a = cx_a.read(ActiveCall::global);
3750
3751    // Set up a fake language server.
3752    let mut language = Language::new(
3753        LanguageConfig {
3754            name: "Rust".into(),
3755            path_suffixes: vec!["rs".to_string()],
3756            ..Default::default()
3757        },
3758        Some(tree_sitter_rust::language()),
3759    );
3760    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
3761    client_a.language_registry().add(Arc::new(language));
3762
3763    // Share a project as client A
3764    client_a
3765        .fs()
3766        .insert_tree(
3767            "/a",
3768            json!({
3769                "a.rs": "let one = two",
3770                "other.rs": "",
3771            }),
3772        )
3773        .await;
3774    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3775
3776    // Cause the language server to start.
3777    let _buffer = project_a
3778        .update(cx_a, |project, cx| {
3779            project.open_buffer(
3780                ProjectPath {
3781                    worktree_id,
3782                    path: Path::new("other.rs").into(),
3783                },
3784                cx,
3785            )
3786        })
3787        .await
3788        .unwrap();
3789
3790    // Simulate a language server reporting errors for a file.
3791    let mut fake_language_server = fake_language_servers.next().await.unwrap();
3792    fake_language_server
3793        .receive_notification::<lsp::notification::DidOpenTextDocument>()
3794        .await;
3795    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3796        lsp::PublishDiagnosticsParams {
3797            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3798            version: None,
3799            diagnostics: vec![lsp::Diagnostic {
3800                severity: Some(lsp::DiagnosticSeverity::WARNING),
3801                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3802                message: "message 0".to_string(),
3803                ..Default::default()
3804            }],
3805        },
3806    );
3807
3808    // Client A shares the project and, simultaneously, the language server
3809    // publishes a diagnostic. This is done to ensure that the server always
3810    // observes the latest diagnostics for a worktree.
3811    let project_id = active_call_a
3812        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3813        .await
3814        .unwrap();
3815    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3816        lsp::PublishDiagnosticsParams {
3817            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3818            version: None,
3819            diagnostics: vec![lsp::Diagnostic {
3820                severity: Some(lsp::DiagnosticSeverity::ERROR),
3821                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3822                message: "message 1".to_string(),
3823                ..Default::default()
3824            }],
3825        },
3826    );
3827
3828    // Join the worktree as client B.
3829    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3830
3831    // Wait for server to see the diagnostics update.
3832    deterministic.run_until_parked();
3833
3834    // Ensure client B observes the new diagnostics.
3835    project_b.read_with(cx_b, |project, cx| {
3836        assert_eq!(
3837            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
3838            &[(
3839                ProjectPath {
3840                    worktree_id,
3841                    path: Arc::from(Path::new("a.rs")),
3842                },
3843                LanguageServerId(0),
3844                DiagnosticSummary {
3845                    error_count: 1,
3846                    warning_count: 0,
3847                    ..Default::default()
3848                },
3849            )]
3850        )
3851    });
3852
3853    // Join project as client C and observe the diagnostics.
3854    let project_c = client_c.build_remote_project(project_id, cx_c).await;
3855    let project_c_diagnostic_summaries =
3856        Rc::new(RefCell::new(project_c.read_with(cx_c, |project, cx| {
3857            project.diagnostic_summaries(cx).collect::<Vec<_>>()
3858        })));
3859    project_c.update(cx_c, |_, cx| {
3860        let summaries = project_c_diagnostic_summaries.clone();
3861        cx.subscribe(&project_c, {
3862            move |p, _, event, cx| {
3863                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
3864                    *summaries.borrow_mut() = p.diagnostic_summaries(cx).collect();
3865                }
3866            }
3867        })
3868        .detach();
3869    });
3870
3871    deterministic.run_until_parked();
3872    assert_eq!(
3873        project_c_diagnostic_summaries.borrow().as_slice(),
3874        &[(
3875            ProjectPath {
3876                worktree_id,
3877                path: Arc::from(Path::new("a.rs")),
3878            },
3879            LanguageServerId(0),
3880            DiagnosticSummary {
3881                error_count: 1,
3882                warning_count: 0,
3883                ..Default::default()
3884            },
3885        )]
3886    );
3887
3888    // Simulate a language server reporting more errors for a file.
3889    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3890        lsp::PublishDiagnosticsParams {
3891            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3892            version: None,
3893            diagnostics: vec![
3894                lsp::Diagnostic {
3895                    severity: Some(lsp::DiagnosticSeverity::ERROR),
3896                    range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3897                    message: "message 1".to_string(),
3898                    ..Default::default()
3899                },
3900                lsp::Diagnostic {
3901                    severity: Some(lsp::DiagnosticSeverity::WARNING),
3902                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 13)),
3903                    message: "message 2".to_string(),
3904                    ..Default::default()
3905                },
3906            ],
3907        },
3908    );
3909
3910    // Clients B and C get the updated summaries
3911    deterministic.run_until_parked();
3912    project_b.read_with(cx_b, |project, cx| {
3913        assert_eq!(
3914            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
3915            [(
3916                ProjectPath {
3917                    worktree_id,
3918                    path: Arc::from(Path::new("a.rs")),
3919                },
3920                LanguageServerId(0),
3921                DiagnosticSummary {
3922                    error_count: 1,
3923                    warning_count: 1,
3924                },
3925            )]
3926        );
3927    });
3928    project_c.read_with(cx_c, |project, cx| {
3929        assert_eq!(
3930            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
3931            [(
3932                ProjectPath {
3933                    worktree_id,
3934                    path: Arc::from(Path::new("a.rs")),
3935                },
3936                LanguageServerId(0),
3937                DiagnosticSummary {
3938                    error_count: 1,
3939                    warning_count: 1,
3940                },
3941            )]
3942        );
3943    });
3944
3945    // Open the file with the errors on client B. They should be present.
3946    let buffer_b = cx_b
3947        .background()
3948        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
3949        .await
3950        .unwrap();
3951
3952    buffer_b.read_with(cx_b, |buffer, _| {
3953        assert_eq!(
3954            buffer
3955                .snapshot()
3956                .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
3957                .collect::<Vec<_>>(),
3958            &[
3959                DiagnosticEntry {
3960                    range: Point::new(0, 4)..Point::new(0, 7),
3961                    diagnostic: Diagnostic {
3962                        group_id: 2,
3963                        message: "message 1".to_string(),
3964                        severity: lsp::DiagnosticSeverity::ERROR,
3965                        is_primary: true,
3966                        ..Default::default()
3967                    }
3968                },
3969                DiagnosticEntry {
3970                    range: Point::new(0, 10)..Point::new(0, 13),
3971                    diagnostic: Diagnostic {
3972                        group_id: 3,
3973                        severity: lsp::DiagnosticSeverity::WARNING,
3974                        message: "message 2".to_string(),
3975                        is_primary: true,
3976                        ..Default::default()
3977                    }
3978                }
3979            ]
3980        );
3981    });
3982
3983    // Simulate a language server reporting no errors for a file.
3984    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3985        lsp::PublishDiagnosticsParams {
3986            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3987            version: None,
3988            diagnostics: vec![],
3989        },
3990    );
3991    deterministic.run_until_parked();
3992    project_a.read_with(cx_a, |project, cx| {
3993        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
3994    });
3995    project_b.read_with(cx_b, |project, cx| {
3996        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
3997    });
3998    project_c.read_with(cx_c, |project, cx| {
3999        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
4000    });
4001}
4002
4003#[gpui::test(iterations = 10)]
4004async fn test_collaborating_with_lsp_progress_updates_and_diagnostics_ordering(
4005    deterministic: Arc<Deterministic>,
4006    cx_a: &mut TestAppContext,
4007    cx_b: &mut TestAppContext,
4008) {
4009    deterministic.forbid_parking();
4010    let mut server = TestServer::start(&deterministic).await;
4011    let client_a = server.create_client(cx_a, "user_a").await;
4012    let client_b = server.create_client(cx_b, "user_b").await;
4013    server
4014        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4015        .await;
4016
4017    // Set up a fake language server.
4018    let mut language = Language::new(
4019        LanguageConfig {
4020            name: "Rust".into(),
4021            path_suffixes: vec!["rs".to_string()],
4022            ..Default::default()
4023        },
4024        Some(tree_sitter_rust::language()),
4025    );
4026    let mut fake_language_servers = language
4027        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4028            disk_based_diagnostics_progress_token: Some("the-disk-based-token".into()),
4029            disk_based_diagnostics_sources: vec!["the-disk-based-diagnostics-source".into()],
4030            ..Default::default()
4031        }))
4032        .await;
4033    client_a.language_registry().add(Arc::new(language));
4034
4035    let file_names = &["one.rs", "two.rs", "three.rs", "four.rs", "five.rs"];
4036    client_a
4037        .fs()
4038        .insert_tree(
4039            "/test",
4040            json!({
4041                "one.rs": "const ONE: usize = 1;",
4042                "two.rs": "const TWO: usize = 2;",
4043                "three.rs": "const THREE: usize = 3;",
4044                "four.rs": "const FOUR: usize = 3;",
4045                "five.rs": "const FIVE: usize = 3;",
4046            }),
4047        )
4048        .await;
4049
4050    let (project_a, worktree_id) = client_a.build_local_project("/test", cx_a).await;
4051
4052    // Share a project as client A
4053    let active_call_a = cx_a.read(ActiveCall::global);
4054    let project_id = active_call_a
4055        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4056        .await
4057        .unwrap();
4058
4059    // Join the project as client B and open all three files.
4060    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4061    let guest_buffers = futures::future::try_join_all(file_names.iter().map(|file_name| {
4062        project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, file_name), cx))
4063    }))
4064    .await
4065    .unwrap();
4066
4067    // Simulate a language server reporting errors for a file.
4068    let fake_language_server = fake_language_servers.next().await.unwrap();
4069    fake_language_server
4070        .request::<lsp::request::WorkDoneProgressCreate>(lsp::WorkDoneProgressCreateParams {
4071            token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4072        })
4073        .await
4074        .unwrap();
4075    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
4076        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4077        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin(
4078            lsp::WorkDoneProgressBegin {
4079                title: "Progress Began".into(),
4080                ..Default::default()
4081            },
4082        )),
4083    });
4084    for file_name in file_names {
4085        fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
4086            lsp::PublishDiagnosticsParams {
4087                uri: lsp::Url::from_file_path(Path::new("/test").join(file_name)).unwrap(),
4088                version: None,
4089                diagnostics: vec![lsp::Diagnostic {
4090                    severity: Some(lsp::DiagnosticSeverity::WARNING),
4091                    source: Some("the-disk-based-diagnostics-source".into()),
4092                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
4093                    message: "message one".to_string(),
4094                    ..Default::default()
4095                }],
4096            },
4097        );
4098    }
4099    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
4100        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4101        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End(
4102            lsp::WorkDoneProgressEnd { message: None },
4103        )),
4104    });
4105
4106    // When the "disk base diagnostics finished" message is received, the buffers'
4107    // diagnostics are expected to be present.
4108    let disk_based_diagnostics_finished = Arc::new(AtomicBool::new(false));
4109    project_b.update(cx_b, {
4110        let project_b = project_b.clone();
4111        let disk_based_diagnostics_finished = disk_based_diagnostics_finished.clone();
4112        move |_, cx| {
4113            cx.subscribe(&project_b, move |_, _, event, cx| {
4114                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
4115                    disk_based_diagnostics_finished.store(true, SeqCst);
4116                    for buffer in &guest_buffers {
4117                        assert_eq!(
4118                            buffer
4119                                .read(cx)
4120                                .snapshot()
4121                                .diagnostics_in_range::<_, usize>(0..5, false)
4122                                .count(),
4123                            1,
4124                            "expected a diagnostic for buffer {:?}",
4125                            buffer.read(cx).file().unwrap().path(),
4126                        );
4127                    }
4128                }
4129            })
4130            .detach();
4131        }
4132    });
4133
4134    deterministic.run_until_parked();
4135    assert!(disk_based_diagnostics_finished.load(SeqCst));
4136}
4137
4138#[gpui::test(iterations = 10)]
4139async fn test_collaborating_with_completion(
4140    deterministic: Arc<Deterministic>,
4141    cx_a: &mut TestAppContext,
4142    cx_b: &mut TestAppContext,
4143) {
4144    deterministic.forbid_parking();
4145    let mut server = TestServer::start(&deterministic).await;
4146    let client_a = server.create_client(cx_a, "user_a").await;
4147    let client_b = server.create_client(cx_b, "user_b").await;
4148    server
4149        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4150        .await;
4151    let active_call_a = cx_a.read(ActiveCall::global);
4152
4153    // Set up a fake language server.
4154    let mut language = Language::new(
4155        LanguageConfig {
4156            name: "Rust".into(),
4157            path_suffixes: vec!["rs".to_string()],
4158            ..Default::default()
4159        },
4160        Some(tree_sitter_rust::language()),
4161    );
4162    let mut fake_language_servers = language
4163        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4164            capabilities: lsp::ServerCapabilities {
4165                completion_provider: Some(lsp::CompletionOptions {
4166                    trigger_characters: Some(vec![".".to_string()]),
4167                    resolve_provider: Some(true),
4168                    ..Default::default()
4169                }),
4170                ..Default::default()
4171            },
4172            ..Default::default()
4173        }))
4174        .await;
4175    client_a.language_registry().add(Arc::new(language));
4176
4177    client_a
4178        .fs()
4179        .insert_tree(
4180            "/a",
4181            json!({
4182                "main.rs": "fn main() { a }",
4183                "other.rs": "",
4184            }),
4185        )
4186        .await;
4187    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
4188    let project_id = active_call_a
4189        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4190        .await
4191        .unwrap();
4192    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4193
4194    // Open a file in an editor as the guest.
4195    let buffer_b = project_b
4196        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
4197        .await
4198        .unwrap();
4199    let window_b = cx_b.add_window(|_| EmptyView);
4200    let editor_b = window_b.add_view(cx_b, |cx| {
4201        Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx)
4202    });
4203
4204    let fake_language_server = fake_language_servers.next().await.unwrap();
4205    cx_a.foreground().run_until_parked();
4206    buffer_b.read_with(cx_b, |buffer, _| {
4207        assert!(!buffer.completion_triggers().is_empty())
4208    });
4209
4210    // Type a completion trigger character as the guest.
4211    editor_b.update(cx_b, |editor, cx| {
4212        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
4213        editor.handle_input(".", cx);
4214        cx.focus(&editor_b);
4215    });
4216
4217    // Receive a completion request as the host's language server.
4218    // Return some completions from the host's language server.
4219    cx_a.foreground().start_waiting();
4220    fake_language_server
4221        .handle_request::<lsp::request::Completion, _, _>(|params, _| async move {
4222            assert_eq!(
4223                params.text_document_position.text_document.uri,
4224                lsp::Url::from_file_path("/a/main.rs").unwrap(),
4225            );
4226            assert_eq!(
4227                params.text_document_position.position,
4228                lsp::Position::new(0, 14),
4229            );
4230
4231            Ok(Some(lsp::CompletionResponse::Array(vec![
4232                lsp::CompletionItem {
4233                    label: "first_method(…)".into(),
4234                    detail: Some("fn(&mut self, B) -> C".into()),
4235                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
4236                        new_text: "first_method($1)".to_string(),
4237                        range: lsp::Range::new(
4238                            lsp::Position::new(0, 14),
4239                            lsp::Position::new(0, 14),
4240                        ),
4241                    })),
4242                    insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
4243                    ..Default::default()
4244                },
4245                lsp::CompletionItem {
4246                    label: "second_method(…)".into(),
4247                    detail: Some("fn(&mut self, C) -> D<E>".into()),
4248                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
4249                        new_text: "second_method()".to_string(),
4250                        range: lsp::Range::new(
4251                            lsp::Position::new(0, 14),
4252                            lsp::Position::new(0, 14),
4253                        ),
4254                    })),
4255                    insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
4256                    ..Default::default()
4257                },
4258            ])))
4259        })
4260        .next()
4261        .await
4262        .unwrap();
4263    cx_a.foreground().finish_waiting();
4264
4265    // Open the buffer on the host.
4266    let buffer_a = project_a
4267        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
4268        .await
4269        .unwrap();
4270    cx_a.foreground().run_until_parked();
4271    buffer_a.read_with(cx_a, |buffer, _| {
4272        assert_eq!(buffer.text(), "fn main() { a. }")
4273    });
4274
4275    // Confirm a completion on the guest.
4276    editor_b.read_with(cx_b, |editor, _| assert!(editor.context_menu_visible()));
4277    editor_b.update(cx_b, |editor, cx| {
4278        editor.confirm_completion(&ConfirmCompletion { item_ix: Some(0) }, cx);
4279        assert_eq!(editor.text(cx), "fn main() { a.first_method() }");
4280    });
4281
4282    // Return a resolved completion from the host's language server.
4283    // The resolved completion has an additional text edit.
4284    fake_language_server.handle_request::<lsp::request::ResolveCompletionItem, _, _>(
4285        |params, _| async move {
4286            assert_eq!(params.label, "first_method(…)");
4287            Ok(lsp::CompletionItem {
4288                label: "first_method(…)".into(),
4289                detail: Some("fn(&mut self, B) -> C".into()),
4290                text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
4291                    new_text: "first_method($1)".to_string(),
4292                    range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
4293                })),
4294                additional_text_edits: Some(vec![lsp::TextEdit {
4295                    new_text: "use d::SomeTrait;\n".to_string(),
4296                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
4297                }]),
4298                insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
4299                ..Default::default()
4300            })
4301        },
4302    );
4303
4304    // The additional edit is applied.
4305    cx_a.foreground().run_until_parked();
4306    buffer_a.read_with(cx_a, |buffer, _| {
4307        assert_eq!(
4308            buffer.text(),
4309            "use d::SomeTrait;\nfn main() { a.first_method() }"
4310        );
4311    });
4312    buffer_b.read_with(cx_b, |buffer, _| {
4313        assert_eq!(
4314            buffer.text(),
4315            "use d::SomeTrait;\nfn main() { a.first_method() }"
4316        );
4317    });
4318}
4319
4320#[gpui::test(iterations = 10)]
4321async fn test_reloading_buffer_manually(
4322    deterministic: Arc<Deterministic>,
4323    cx_a: &mut TestAppContext,
4324    cx_b: &mut TestAppContext,
4325) {
4326    deterministic.forbid_parking();
4327    let mut server = TestServer::start(&deterministic).await;
4328    let client_a = server.create_client(cx_a, "user_a").await;
4329    let client_b = server.create_client(cx_b, "user_b").await;
4330    server
4331        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4332        .await;
4333    let active_call_a = cx_a.read(ActiveCall::global);
4334
4335    client_a
4336        .fs()
4337        .insert_tree("/a", json!({ "a.rs": "let one = 1;" }))
4338        .await;
4339    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
4340    let buffer_a = project_a
4341        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
4342        .await
4343        .unwrap();
4344    let project_id = active_call_a
4345        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4346        .await
4347        .unwrap();
4348
4349    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4350
4351    let buffer_b = cx_b
4352        .background()
4353        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4354        .await
4355        .unwrap();
4356    buffer_b.update(cx_b, |buffer, cx| {
4357        buffer.edit([(4..7, "six")], None, cx);
4358        buffer.edit([(10..11, "6")], None, cx);
4359        assert_eq!(buffer.text(), "let six = 6;");
4360        assert!(buffer.is_dirty());
4361        assert!(!buffer.has_conflict());
4362    });
4363    cx_a.foreground().run_until_parked();
4364    buffer_a.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "let six = 6;"));
4365
4366    client_a
4367        .fs()
4368        .save(
4369            "/a/a.rs".as_ref(),
4370            &Rope::from("let seven = 7;"),
4371            LineEnding::Unix,
4372        )
4373        .await
4374        .unwrap();
4375    cx_a.foreground().run_until_parked();
4376    buffer_a.read_with(cx_a, |buffer, _| assert!(buffer.has_conflict()));
4377    buffer_b.read_with(cx_b, |buffer, _| assert!(buffer.has_conflict()));
4378
4379    project_b
4380        .update(cx_b, |project, cx| {
4381            project.reload_buffers(HashSet::from_iter([buffer_b.clone()]), true, cx)
4382        })
4383        .await
4384        .unwrap();
4385    buffer_a.read_with(cx_a, |buffer, _| {
4386        assert_eq!(buffer.text(), "let seven = 7;");
4387        assert!(!buffer.is_dirty());
4388        assert!(!buffer.has_conflict());
4389    });
4390    buffer_b.read_with(cx_b, |buffer, _| {
4391        assert_eq!(buffer.text(), "let seven = 7;");
4392        assert!(!buffer.is_dirty());
4393        assert!(!buffer.has_conflict());
4394    });
4395
4396    buffer_a.update(cx_a, |buffer, cx| {
4397        // Undoing on the host is a no-op when the reload was initiated by the guest.
4398        buffer.undo(cx);
4399        assert_eq!(buffer.text(), "let seven = 7;");
4400        assert!(!buffer.is_dirty());
4401        assert!(!buffer.has_conflict());
4402    });
4403    buffer_b.update(cx_b, |buffer, cx| {
4404        // Undoing on the guest rolls back the buffer to before it was reloaded but the conflict gets cleared.
4405        buffer.undo(cx);
4406        assert_eq!(buffer.text(), "let six = 6;");
4407        assert!(buffer.is_dirty());
4408        assert!(!buffer.has_conflict());
4409    });
4410}
4411
4412#[gpui::test(iterations = 10)]
4413async fn test_formatting_buffer(
4414    deterministic: Arc<Deterministic>,
4415    cx_a: &mut TestAppContext,
4416    cx_b: &mut TestAppContext,
4417) {
4418    use project::FormatTrigger;
4419
4420    let mut server = TestServer::start(&deterministic).await;
4421    let client_a = server.create_client(cx_a, "user_a").await;
4422    let client_b = server.create_client(cx_b, "user_b").await;
4423    server
4424        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4425        .await;
4426    let active_call_a = cx_a.read(ActiveCall::global);
4427
4428    // Set up a fake language server.
4429    let mut language = Language::new(
4430        LanguageConfig {
4431            name: "Rust".into(),
4432            path_suffixes: vec!["rs".to_string()],
4433            ..Default::default()
4434        },
4435        Some(tree_sitter_rust::language()),
4436    );
4437    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4438    client_a.language_registry().add(Arc::new(language));
4439
4440    // Here we insert a fake tree with a directory that exists on disk. This is needed
4441    // because later we'll invoke a command, which requires passing a working directory
4442    // that points to a valid location on disk.
4443    let directory = env::current_dir().unwrap();
4444    client_a
4445        .fs()
4446        .insert_tree(&directory, json!({ "a.rs": "let one = \"two\"" }))
4447        .await;
4448    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
4449    let project_id = active_call_a
4450        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4451        .await
4452        .unwrap();
4453    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4454
4455    let buffer_b = cx_b
4456        .background()
4457        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4458        .await
4459        .unwrap();
4460
4461    let fake_language_server = fake_language_servers.next().await.unwrap();
4462    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
4463        Ok(Some(vec![
4464            lsp::TextEdit {
4465                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 4)),
4466                new_text: "h".to_string(),
4467            },
4468            lsp::TextEdit {
4469                range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 7)),
4470                new_text: "y".to_string(),
4471            },
4472        ]))
4473    });
4474
4475    project_b
4476        .update(cx_b, |project, cx| {
4477            project.format(
4478                HashSet::from_iter([buffer_b.clone()]),
4479                true,
4480                FormatTrigger::Save,
4481                cx,
4482            )
4483        })
4484        .await
4485        .unwrap();
4486
4487    // The edits from the LSP are applied, and a final newline is added.
4488    assert_eq!(
4489        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4490        "let honey = \"two\"\n"
4491    );
4492
4493    // Ensure buffer can be formatted using an external command. Notice how the
4494    // host's configuration is honored as opposed to using the guest's settings.
4495    cx_a.update(|cx| {
4496        cx.update_global(|store: &mut SettingsStore, cx| {
4497            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4498                file.defaults.formatter = Some(Formatter::External {
4499                    command: "awk".into(),
4500                    arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()].into(),
4501                });
4502            });
4503        });
4504    });
4505    project_b
4506        .update(cx_b, |project, cx| {
4507            project.format(
4508                HashSet::from_iter([buffer_b.clone()]),
4509                true,
4510                FormatTrigger::Save,
4511                cx,
4512            )
4513        })
4514        .await
4515        .unwrap();
4516    assert_eq!(
4517        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4518        format!("let honey = \"{}/a.rs\"\n", directory.to_str().unwrap())
4519    );
4520}
4521
4522#[gpui::test(iterations = 10)]
4523async fn test_definition(
4524    deterministic: Arc<Deterministic>,
4525    cx_a: &mut TestAppContext,
4526    cx_b: &mut TestAppContext,
4527) {
4528    deterministic.forbid_parking();
4529    let mut server = TestServer::start(&deterministic).await;
4530    let client_a = server.create_client(cx_a, "user_a").await;
4531    let client_b = server.create_client(cx_b, "user_b").await;
4532    server
4533        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4534        .await;
4535    let active_call_a = cx_a.read(ActiveCall::global);
4536
4537    // Set up a fake language server.
4538    let mut language = Language::new(
4539        LanguageConfig {
4540            name: "Rust".into(),
4541            path_suffixes: vec!["rs".to_string()],
4542            ..Default::default()
4543        },
4544        Some(tree_sitter_rust::language()),
4545    );
4546    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4547    client_a.language_registry().add(Arc::new(language));
4548
4549    client_a
4550        .fs()
4551        .insert_tree(
4552            "/root",
4553            json!({
4554                "dir-1": {
4555                    "a.rs": "const ONE: usize = b::TWO + b::THREE;",
4556                },
4557                "dir-2": {
4558                    "b.rs": "const TWO: c::T2 = 2;\nconst THREE: usize = 3;",
4559                    "c.rs": "type T2 = usize;",
4560                }
4561            }),
4562        )
4563        .await;
4564    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4565    let project_id = active_call_a
4566        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4567        .await
4568        .unwrap();
4569    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4570
4571    // Open the file on client B.
4572    let buffer_b = cx_b
4573        .background()
4574        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4575        .await
4576        .unwrap();
4577
4578    // Request the definition of a symbol as the guest.
4579    let fake_language_server = fake_language_servers.next().await.unwrap();
4580    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4581        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4582            lsp::Location::new(
4583                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4584                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4585            ),
4586        )))
4587    });
4588
4589    let definitions_1 = project_b
4590        .update(cx_b, |p, cx| p.definition(&buffer_b, 23, cx))
4591        .await
4592        .unwrap();
4593    cx_b.read(|cx| {
4594        assert_eq!(definitions_1.len(), 1);
4595        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4596        let target_buffer = definitions_1[0].target.buffer.read(cx);
4597        assert_eq!(
4598            target_buffer.text(),
4599            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4600        );
4601        assert_eq!(
4602            definitions_1[0].target.range.to_point(target_buffer),
4603            Point::new(0, 6)..Point::new(0, 9)
4604        );
4605    });
4606
4607    // Try getting more definitions for the same buffer, ensuring the buffer gets reused from
4608    // the previous call to `definition`.
4609    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4610        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4611            lsp::Location::new(
4612                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4613                lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)),
4614            ),
4615        )))
4616    });
4617
4618    let definitions_2 = project_b
4619        .update(cx_b, |p, cx| p.definition(&buffer_b, 33, cx))
4620        .await
4621        .unwrap();
4622    cx_b.read(|cx| {
4623        assert_eq!(definitions_2.len(), 1);
4624        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4625        let target_buffer = definitions_2[0].target.buffer.read(cx);
4626        assert_eq!(
4627            target_buffer.text(),
4628            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4629        );
4630        assert_eq!(
4631            definitions_2[0].target.range.to_point(target_buffer),
4632            Point::new(1, 6)..Point::new(1, 11)
4633        );
4634    });
4635    assert_eq!(
4636        definitions_1[0].target.buffer,
4637        definitions_2[0].target.buffer
4638    );
4639
4640    fake_language_server.handle_request::<lsp::request::GotoTypeDefinition, _, _>(
4641        |req, _| async move {
4642            assert_eq!(
4643                req.text_document_position_params.position,
4644                lsp::Position::new(0, 7)
4645            );
4646            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4647                lsp::Location::new(
4648                    lsp::Url::from_file_path("/root/dir-2/c.rs").unwrap(),
4649                    lsp::Range::new(lsp::Position::new(0, 5), lsp::Position::new(0, 7)),
4650                ),
4651            )))
4652        },
4653    );
4654
4655    let type_definitions = project_b
4656        .update(cx_b, |p, cx| p.type_definition(&buffer_b, 7, cx))
4657        .await
4658        .unwrap();
4659    cx_b.read(|cx| {
4660        assert_eq!(type_definitions.len(), 1);
4661        let target_buffer = type_definitions[0].target.buffer.read(cx);
4662        assert_eq!(target_buffer.text(), "type T2 = usize;");
4663        assert_eq!(
4664            type_definitions[0].target.range.to_point(target_buffer),
4665            Point::new(0, 5)..Point::new(0, 7)
4666        );
4667    });
4668}
4669
4670#[gpui::test(iterations = 10)]
4671async fn test_references(
4672    deterministic: Arc<Deterministic>,
4673    cx_a: &mut TestAppContext,
4674    cx_b: &mut TestAppContext,
4675) {
4676    deterministic.forbid_parking();
4677    let mut server = TestServer::start(&deterministic).await;
4678    let client_a = server.create_client(cx_a, "user_a").await;
4679    let client_b = server.create_client(cx_b, "user_b").await;
4680    server
4681        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4682        .await;
4683    let active_call_a = cx_a.read(ActiveCall::global);
4684
4685    // Set up a fake language server.
4686    let mut language = Language::new(
4687        LanguageConfig {
4688            name: "Rust".into(),
4689            path_suffixes: vec!["rs".to_string()],
4690            ..Default::default()
4691        },
4692        Some(tree_sitter_rust::language()),
4693    );
4694    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4695    client_a.language_registry().add(Arc::new(language));
4696
4697    client_a
4698        .fs()
4699        .insert_tree(
4700            "/root",
4701            json!({
4702                "dir-1": {
4703                    "one.rs": "const ONE: usize = 1;",
4704                    "two.rs": "const TWO: usize = one::ONE + one::ONE;",
4705                },
4706                "dir-2": {
4707                    "three.rs": "const THREE: usize = two::TWO + one::ONE;",
4708                }
4709            }),
4710        )
4711        .await;
4712    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4713    let project_id = active_call_a
4714        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4715        .await
4716        .unwrap();
4717    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4718
4719    // Open the file on client B.
4720    let buffer_b = cx_b
4721        .background()
4722        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
4723        .await
4724        .unwrap();
4725
4726    // Request references to a symbol as the guest.
4727    let fake_language_server = fake_language_servers.next().await.unwrap();
4728    fake_language_server.handle_request::<lsp::request::References, _, _>(|params, _| async move {
4729        assert_eq!(
4730            params.text_document_position.text_document.uri.as_str(),
4731            "file:///root/dir-1/one.rs"
4732        );
4733        Ok(Some(vec![
4734            lsp::Location {
4735                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4736                range: lsp::Range::new(lsp::Position::new(0, 24), lsp::Position::new(0, 27)),
4737            },
4738            lsp::Location {
4739                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4740                range: lsp::Range::new(lsp::Position::new(0, 35), lsp::Position::new(0, 38)),
4741            },
4742            lsp::Location {
4743                uri: lsp::Url::from_file_path("/root/dir-2/three.rs").unwrap(),
4744                range: lsp::Range::new(lsp::Position::new(0, 37), lsp::Position::new(0, 40)),
4745            },
4746        ]))
4747    });
4748
4749    let references = project_b
4750        .update(cx_b, |p, cx| p.references(&buffer_b, 7, cx))
4751        .await
4752        .unwrap();
4753    cx_b.read(|cx| {
4754        assert_eq!(references.len(), 3);
4755        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4756
4757        let two_buffer = references[0].buffer.read(cx);
4758        let three_buffer = references[2].buffer.read(cx);
4759        assert_eq!(
4760            two_buffer.file().unwrap().path().as_ref(),
4761            Path::new("two.rs")
4762        );
4763        assert_eq!(references[1].buffer, references[0].buffer);
4764        assert_eq!(
4765            three_buffer.file().unwrap().full_path(cx),
4766            Path::new("/root/dir-2/three.rs")
4767        );
4768
4769        assert_eq!(references[0].range.to_offset(two_buffer), 24..27);
4770        assert_eq!(references[1].range.to_offset(two_buffer), 35..38);
4771        assert_eq!(references[2].range.to_offset(three_buffer), 37..40);
4772    });
4773}
4774
4775#[gpui::test(iterations = 10)]
4776async fn test_project_search(
4777    deterministic: Arc<Deterministic>,
4778    cx_a: &mut TestAppContext,
4779    cx_b: &mut TestAppContext,
4780) {
4781    deterministic.forbid_parking();
4782    let mut server = TestServer::start(&deterministic).await;
4783    let client_a = server.create_client(cx_a, "user_a").await;
4784    let client_b = server.create_client(cx_b, "user_b").await;
4785    server
4786        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4787        .await;
4788    let active_call_a = cx_a.read(ActiveCall::global);
4789
4790    client_a
4791        .fs()
4792        .insert_tree(
4793            "/root",
4794            json!({
4795                "dir-1": {
4796                    "a": "hello world",
4797                    "b": "goodnight moon",
4798                    "c": "a world of goo",
4799                    "d": "world champion of clown world",
4800                },
4801                "dir-2": {
4802                    "e": "disney world is fun",
4803                }
4804            }),
4805        )
4806        .await;
4807    let (project_a, _) = client_a.build_local_project("/root/dir-1", cx_a).await;
4808    let (worktree_2, _) = project_a
4809        .update(cx_a, |p, cx| {
4810            p.find_or_create_local_worktree("/root/dir-2", true, cx)
4811        })
4812        .await
4813        .unwrap();
4814    worktree_2
4815        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
4816        .await;
4817    let project_id = active_call_a
4818        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4819        .await
4820        .unwrap();
4821
4822    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4823
4824    // Perform a search as the guest.
4825    let mut results = HashMap::default();
4826    let mut search_rx = project_b.update(cx_b, |project, cx| {
4827        project.search(
4828            SearchQuery::text("world", false, false, Vec::new(), Vec::new()).unwrap(),
4829            cx,
4830        )
4831    });
4832    while let Some((buffer, ranges)) = search_rx.next().await {
4833        results.entry(buffer).or_insert(ranges);
4834    }
4835
4836    let mut ranges_by_path = results
4837        .into_iter()
4838        .map(|(buffer, ranges)| {
4839            buffer.read_with(cx_b, |buffer, cx| {
4840                let path = buffer.file().unwrap().full_path(cx);
4841                let offset_ranges = ranges
4842                    .into_iter()
4843                    .map(|range| range.to_offset(buffer))
4844                    .collect::<Vec<_>>();
4845                (path, offset_ranges)
4846            })
4847        })
4848        .collect::<Vec<_>>();
4849    ranges_by_path.sort_by_key(|(path, _)| path.clone());
4850
4851    assert_eq!(
4852        ranges_by_path,
4853        &[
4854            (PathBuf::from("dir-1/a"), vec![6..11]),
4855            (PathBuf::from("dir-1/c"), vec![2..7]),
4856            (PathBuf::from("dir-1/d"), vec![0..5, 24..29]),
4857            (PathBuf::from("dir-2/e"), vec![7..12]),
4858        ]
4859    );
4860}
4861
4862#[gpui::test(iterations = 10)]
4863async fn test_document_highlights(
4864    deterministic: Arc<Deterministic>,
4865    cx_a: &mut TestAppContext,
4866    cx_b: &mut TestAppContext,
4867) {
4868    deterministic.forbid_parking();
4869    let mut server = TestServer::start(&deterministic).await;
4870    let client_a = server.create_client(cx_a, "user_a").await;
4871    let client_b = server.create_client(cx_b, "user_b").await;
4872    server
4873        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4874        .await;
4875    let active_call_a = cx_a.read(ActiveCall::global);
4876
4877    client_a
4878        .fs()
4879        .insert_tree(
4880            "/root-1",
4881            json!({
4882                "main.rs": "fn double(number: i32) -> i32 { number + number }",
4883            }),
4884        )
4885        .await;
4886
4887    // Set up a fake language server.
4888    let mut language = Language::new(
4889        LanguageConfig {
4890            name: "Rust".into(),
4891            path_suffixes: vec!["rs".to_string()],
4892            ..Default::default()
4893        },
4894        Some(tree_sitter_rust::language()),
4895    );
4896    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4897    client_a.language_registry().add(Arc::new(language));
4898
4899    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4900    let project_id = active_call_a
4901        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4902        .await
4903        .unwrap();
4904    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4905
4906    // Open the file on client B.
4907    let buffer_b = cx_b
4908        .background()
4909        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
4910        .await
4911        .unwrap();
4912
4913    // Request document highlights as the guest.
4914    let fake_language_server = fake_language_servers.next().await.unwrap();
4915    fake_language_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
4916        |params, _| async move {
4917            assert_eq!(
4918                params
4919                    .text_document_position_params
4920                    .text_document
4921                    .uri
4922                    .as_str(),
4923                "file:///root-1/main.rs"
4924            );
4925            assert_eq!(
4926                params.text_document_position_params.position,
4927                lsp::Position::new(0, 34)
4928            );
4929            Ok(Some(vec![
4930                lsp::DocumentHighlight {
4931                    kind: Some(lsp::DocumentHighlightKind::WRITE),
4932                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 16)),
4933                },
4934                lsp::DocumentHighlight {
4935                    kind: Some(lsp::DocumentHighlightKind::READ),
4936                    range: lsp::Range::new(lsp::Position::new(0, 32), lsp::Position::new(0, 38)),
4937                },
4938                lsp::DocumentHighlight {
4939                    kind: Some(lsp::DocumentHighlightKind::READ),
4940                    range: lsp::Range::new(lsp::Position::new(0, 41), lsp::Position::new(0, 47)),
4941                },
4942            ]))
4943        },
4944    );
4945
4946    let highlights = project_b
4947        .update(cx_b, |p, cx| p.document_highlights(&buffer_b, 34, cx))
4948        .await
4949        .unwrap();
4950    buffer_b.read_with(cx_b, |buffer, _| {
4951        let snapshot = buffer.snapshot();
4952
4953        let highlights = highlights
4954            .into_iter()
4955            .map(|highlight| (highlight.kind, highlight.range.to_offset(&snapshot)))
4956            .collect::<Vec<_>>();
4957        assert_eq!(
4958            highlights,
4959            &[
4960                (lsp::DocumentHighlightKind::WRITE, 10..16),
4961                (lsp::DocumentHighlightKind::READ, 32..38),
4962                (lsp::DocumentHighlightKind::READ, 41..47)
4963            ]
4964        )
4965    });
4966}
4967
4968#[gpui::test(iterations = 10)]
4969async fn test_lsp_hover(
4970    deterministic: Arc<Deterministic>,
4971    cx_a: &mut TestAppContext,
4972    cx_b: &mut TestAppContext,
4973) {
4974    deterministic.forbid_parking();
4975    let mut server = TestServer::start(&deterministic).await;
4976    let client_a = server.create_client(cx_a, "user_a").await;
4977    let client_b = server.create_client(cx_b, "user_b").await;
4978    server
4979        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4980        .await;
4981    let active_call_a = cx_a.read(ActiveCall::global);
4982
4983    client_a
4984        .fs()
4985        .insert_tree(
4986            "/root-1",
4987            json!({
4988                "main.rs": "use std::collections::HashMap;",
4989            }),
4990        )
4991        .await;
4992
4993    // Set up a fake language server.
4994    let mut language = Language::new(
4995        LanguageConfig {
4996            name: "Rust".into(),
4997            path_suffixes: vec!["rs".to_string()],
4998            ..Default::default()
4999        },
5000        Some(tree_sitter_rust::language()),
5001    );
5002    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5003    client_a.language_registry().add(Arc::new(language));
5004
5005    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
5006    let project_id = active_call_a
5007        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5008        .await
5009        .unwrap();
5010    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5011
5012    // Open the file as the guest
5013    let buffer_b = cx_b
5014        .background()
5015        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
5016        .await
5017        .unwrap();
5018
5019    // Request hover information as the guest.
5020    let fake_language_server = fake_language_servers.next().await.unwrap();
5021    fake_language_server.handle_request::<lsp::request::HoverRequest, _, _>(
5022        |params, _| async move {
5023            assert_eq!(
5024                params
5025                    .text_document_position_params
5026                    .text_document
5027                    .uri
5028                    .as_str(),
5029                "file:///root-1/main.rs"
5030            );
5031            assert_eq!(
5032                params.text_document_position_params.position,
5033                lsp::Position::new(0, 22)
5034            );
5035            Ok(Some(lsp::Hover {
5036                contents: lsp::HoverContents::Array(vec![
5037                    lsp::MarkedString::String("Test hover content.".to_string()),
5038                    lsp::MarkedString::LanguageString(lsp::LanguageString {
5039                        language: "Rust".to_string(),
5040                        value: "let foo = 42;".to_string(),
5041                    }),
5042                ]),
5043                range: Some(lsp::Range::new(
5044                    lsp::Position::new(0, 22),
5045                    lsp::Position::new(0, 29),
5046                )),
5047            }))
5048        },
5049    );
5050
5051    let hover_info = project_b
5052        .update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
5053        .await
5054        .unwrap()
5055        .unwrap();
5056    buffer_b.read_with(cx_b, |buffer, _| {
5057        let snapshot = buffer.snapshot();
5058        assert_eq!(hover_info.range.unwrap().to_offset(&snapshot), 22..29);
5059        assert_eq!(
5060            hover_info.contents,
5061            vec![
5062                project::HoverBlock {
5063                    text: "Test hover content.".to_string(),
5064                    kind: HoverBlockKind::Markdown,
5065                },
5066                project::HoverBlock {
5067                    text: "let foo = 42;".to_string(),
5068                    kind: HoverBlockKind::Code {
5069                        language: "Rust".to_string()
5070                    },
5071                }
5072            ]
5073        );
5074    });
5075}
5076
5077#[gpui::test(iterations = 10)]
5078async fn test_project_symbols(
5079    deterministic: Arc<Deterministic>,
5080    cx_a: &mut TestAppContext,
5081    cx_b: &mut TestAppContext,
5082) {
5083    deterministic.forbid_parking();
5084    let mut server = TestServer::start(&deterministic).await;
5085    let client_a = server.create_client(cx_a, "user_a").await;
5086    let client_b = server.create_client(cx_b, "user_b").await;
5087    server
5088        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5089        .await;
5090    let active_call_a = cx_a.read(ActiveCall::global);
5091
5092    // Set up a fake language server.
5093    let mut language = Language::new(
5094        LanguageConfig {
5095            name: "Rust".into(),
5096            path_suffixes: vec!["rs".to_string()],
5097            ..Default::default()
5098        },
5099        Some(tree_sitter_rust::language()),
5100    );
5101    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5102    client_a.language_registry().add(Arc::new(language));
5103
5104    client_a
5105        .fs()
5106        .insert_tree(
5107            "/code",
5108            json!({
5109                "crate-1": {
5110                    "one.rs": "const ONE: usize = 1;",
5111                },
5112                "crate-2": {
5113                    "two.rs": "const TWO: usize = 2; const THREE: usize = 3;",
5114                },
5115                "private": {
5116                    "passwords.txt": "the-password",
5117                }
5118            }),
5119        )
5120        .await;
5121    let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await;
5122    let project_id = active_call_a
5123        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5124        .await
5125        .unwrap();
5126    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5127
5128    // Cause the language server to start.
5129    let _buffer = cx_b
5130        .background()
5131        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
5132        .await
5133        .unwrap();
5134
5135    let fake_language_server = fake_language_servers.next().await.unwrap();
5136    fake_language_server.handle_request::<lsp::WorkspaceSymbolRequest, _, _>(|_, _| async move {
5137        Ok(Some(lsp::WorkspaceSymbolResponse::Flat(vec![
5138            #[allow(deprecated)]
5139            lsp::SymbolInformation {
5140                name: "TWO".into(),
5141                location: lsp::Location {
5142                    uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
5143                    range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5144                },
5145                kind: lsp::SymbolKind::CONSTANT,
5146                tags: None,
5147                container_name: None,
5148                deprecated: None,
5149            },
5150        ])))
5151    });
5152
5153    // Request the definition of a symbol as the guest.
5154    let symbols = project_b
5155        .update(cx_b, |p, cx| p.symbols("two", cx))
5156        .await
5157        .unwrap();
5158    assert_eq!(symbols.len(), 1);
5159    assert_eq!(symbols[0].name, "TWO");
5160
5161    // Open one of the returned symbols.
5162    let buffer_b_2 = project_b
5163        .update(cx_b, |project, cx| {
5164            project.open_buffer_for_symbol(&symbols[0], cx)
5165        })
5166        .await
5167        .unwrap();
5168    buffer_b_2.read_with(cx_b, |buffer, _| {
5169        assert_eq!(
5170            buffer.file().unwrap().path().as_ref(),
5171            Path::new("../crate-2/two.rs")
5172        );
5173    });
5174
5175    // Attempt to craft a symbol and violate host's privacy by opening an arbitrary file.
5176    let mut fake_symbol = symbols[0].clone();
5177    fake_symbol.path.path = Path::new("/code/secrets").into();
5178    let error = project_b
5179        .update(cx_b, |project, cx| {
5180            project.open_buffer_for_symbol(&fake_symbol, cx)
5181        })
5182        .await
5183        .unwrap_err();
5184    assert!(error.to_string().contains("invalid symbol signature"));
5185}
5186
5187#[gpui::test(iterations = 10)]
5188async fn test_open_buffer_while_getting_definition_pointing_to_it(
5189    deterministic: Arc<Deterministic>,
5190    cx_a: &mut TestAppContext,
5191    cx_b: &mut TestAppContext,
5192    mut rng: StdRng,
5193) {
5194    deterministic.forbid_parking();
5195    let mut server = TestServer::start(&deterministic).await;
5196    let client_a = server.create_client(cx_a, "user_a").await;
5197    let client_b = server.create_client(cx_b, "user_b").await;
5198    server
5199        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5200        .await;
5201    let active_call_a = cx_a.read(ActiveCall::global);
5202
5203    // Set up a fake language server.
5204    let mut language = Language::new(
5205        LanguageConfig {
5206            name: "Rust".into(),
5207            path_suffixes: vec!["rs".to_string()],
5208            ..Default::default()
5209        },
5210        Some(tree_sitter_rust::language()),
5211    );
5212    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5213    client_a.language_registry().add(Arc::new(language));
5214
5215    client_a
5216        .fs()
5217        .insert_tree(
5218            "/root",
5219            json!({
5220                "a.rs": "const ONE: usize = b::TWO;",
5221                "b.rs": "const TWO: usize = 2",
5222            }),
5223        )
5224        .await;
5225    let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await;
5226    let project_id = active_call_a
5227        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5228        .await
5229        .unwrap();
5230    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5231
5232    let buffer_b1 = cx_b
5233        .background()
5234        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
5235        .await
5236        .unwrap();
5237
5238    let fake_language_server = fake_language_servers.next().await.unwrap();
5239    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
5240        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
5241            lsp::Location::new(
5242                lsp::Url::from_file_path("/root/b.rs").unwrap(),
5243                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5244            ),
5245        )))
5246    });
5247
5248    let definitions;
5249    let buffer_b2;
5250    if rng.gen() {
5251        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5252        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5253    } else {
5254        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5255        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5256    }
5257
5258    let buffer_b2 = buffer_b2.await.unwrap();
5259    let definitions = definitions.await.unwrap();
5260    assert_eq!(definitions.len(), 1);
5261    assert_eq!(definitions[0].target.buffer, buffer_b2);
5262}
5263
5264#[gpui::test(iterations = 10)]
5265async fn test_collaborating_with_code_actions(
5266    deterministic: Arc<Deterministic>,
5267    cx_a: &mut TestAppContext,
5268    cx_b: &mut TestAppContext,
5269) {
5270    deterministic.forbid_parking();
5271    let mut server = TestServer::start(&deterministic).await;
5272    let client_a = server.create_client(cx_a, "user_a").await;
5273    //
5274    let client_b = server.create_client(cx_b, "user_b").await;
5275    server
5276        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5277        .await;
5278    let active_call_a = cx_a.read(ActiveCall::global);
5279
5280    cx_b.update(editor::init);
5281
5282    // Set up a fake language server.
5283    let mut language = Language::new(
5284        LanguageConfig {
5285            name: "Rust".into(),
5286            path_suffixes: vec!["rs".to_string()],
5287            ..Default::default()
5288        },
5289        Some(tree_sitter_rust::language()),
5290    );
5291    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5292    client_a.language_registry().add(Arc::new(language));
5293
5294    client_a
5295        .fs()
5296        .insert_tree(
5297            "/a",
5298            json!({
5299                "main.rs": "mod other;\nfn main() { let foo = other::foo(); }",
5300                "other.rs": "pub fn foo() -> usize { 4 }",
5301            }),
5302        )
5303        .await;
5304    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
5305    let project_id = active_call_a
5306        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5307        .await
5308        .unwrap();
5309
5310    // Join the project as client B.
5311    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5312    let window_b =
5313        cx_b.add_window(|cx| Workspace::new(0, project_b.clone(), client_b.app_state.clone(), cx));
5314    let workspace_b = window_b.root(cx_b);
5315    let editor_b = workspace_b
5316        .update(cx_b, |workspace, cx| {
5317            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
5318        })
5319        .await
5320        .unwrap()
5321        .downcast::<Editor>()
5322        .unwrap();
5323
5324    let mut fake_language_server = fake_language_servers.next().await.unwrap();
5325    let mut requests = fake_language_server
5326        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
5327            assert_eq!(
5328                params.text_document.uri,
5329                lsp::Url::from_file_path("/a/main.rs").unwrap(),
5330            );
5331            assert_eq!(params.range.start, lsp::Position::new(0, 0));
5332            assert_eq!(params.range.end, lsp::Position::new(0, 0));
5333            Ok(None)
5334        });
5335    deterministic.advance_clock(editor::CODE_ACTIONS_DEBOUNCE_TIMEOUT * 2);
5336    requests.next().await;
5337
5338    // Move cursor to a location that contains code actions.
5339    editor_b.update(cx_b, |editor, cx| {
5340        editor.change_selections(None, cx, |s| {
5341            s.select_ranges([Point::new(1, 31)..Point::new(1, 31)])
5342        });
5343        cx.focus(&editor_b);
5344    });
5345
5346    let mut requests = fake_language_server
5347        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
5348            assert_eq!(
5349                params.text_document.uri,
5350                lsp::Url::from_file_path("/a/main.rs").unwrap(),
5351            );
5352            assert_eq!(params.range.start, lsp::Position::new(1, 31));
5353            assert_eq!(params.range.end, lsp::Position::new(1, 31));
5354
5355            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
5356                lsp::CodeAction {
5357                    title: "Inline into all callers".to_string(),
5358                    edit: Some(lsp::WorkspaceEdit {
5359                        changes: Some(
5360                            [
5361                                (
5362                                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
5363                                    vec![lsp::TextEdit::new(
5364                                        lsp::Range::new(
5365                                            lsp::Position::new(1, 22),
5366                                            lsp::Position::new(1, 34),
5367                                        ),
5368                                        "4".to_string(),
5369                                    )],
5370                                ),
5371                                (
5372                                    lsp::Url::from_file_path("/a/other.rs").unwrap(),
5373                                    vec![lsp::TextEdit::new(
5374                                        lsp::Range::new(
5375                                            lsp::Position::new(0, 0),
5376                                            lsp::Position::new(0, 27),
5377                                        ),
5378                                        "".to_string(),
5379                                    )],
5380                                ),
5381                            ]
5382                            .into_iter()
5383                            .collect(),
5384                        ),
5385                        ..Default::default()
5386                    }),
5387                    data: Some(json!({
5388                        "codeActionParams": {
5389                            "range": {
5390                                "start": {"line": 1, "column": 31},
5391                                "end": {"line": 1, "column": 31},
5392                            }
5393                        }
5394                    })),
5395                    ..Default::default()
5396                },
5397            )]))
5398        });
5399    deterministic.advance_clock(editor::CODE_ACTIONS_DEBOUNCE_TIMEOUT * 2);
5400    requests.next().await;
5401
5402    // Toggle code actions and wait for them to display.
5403    editor_b.update(cx_b, |editor, cx| {
5404        editor.toggle_code_actions(
5405            &ToggleCodeActions {
5406                deployed_from_indicator: false,
5407            },
5408            cx,
5409        );
5410    });
5411    cx_a.foreground().run_until_parked();
5412    editor_b.read_with(cx_b, |editor, _| assert!(editor.context_menu_visible()));
5413
5414    fake_language_server.remove_request_handler::<lsp::request::CodeActionRequest>();
5415
5416    // Confirming the code action will trigger a resolve request.
5417    let confirm_action = workspace_b
5418        .update(cx_b, |workspace, cx| {
5419            Editor::confirm_code_action(workspace, &ConfirmCodeAction { item_ix: Some(0) }, cx)
5420        })
5421        .unwrap();
5422    fake_language_server.handle_request::<lsp::request::CodeActionResolveRequest, _, _>(
5423        |_, _| async move {
5424            Ok(lsp::CodeAction {
5425                title: "Inline into all callers".to_string(),
5426                edit: Some(lsp::WorkspaceEdit {
5427                    changes: Some(
5428                        [
5429                            (
5430                                lsp::Url::from_file_path("/a/main.rs").unwrap(),
5431                                vec![lsp::TextEdit::new(
5432                                    lsp::Range::new(
5433                                        lsp::Position::new(1, 22),
5434                                        lsp::Position::new(1, 34),
5435                                    ),
5436                                    "4".to_string(),
5437                                )],
5438                            ),
5439                            (
5440                                lsp::Url::from_file_path("/a/other.rs").unwrap(),
5441                                vec![lsp::TextEdit::new(
5442                                    lsp::Range::new(
5443                                        lsp::Position::new(0, 0),
5444                                        lsp::Position::new(0, 27),
5445                                    ),
5446                                    "".to_string(),
5447                                )],
5448                            ),
5449                        ]
5450                        .into_iter()
5451                        .collect(),
5452                    ),
5453                    ..Default::default()
5454                }),
5455                ..Default::default()
5456            })
5457        },
5458    );
5459
5460    // After the action is confirmed, an editor containing both modified files is opened.
5461    confirm_action.await.unwrap();
5462    let code_action_editor = workspace_b.read_with(cx_b, |workspace, cx| {
5463        workspace
5464            .active_item(cx)
5465            .unwrap()
5466            .downcast::<Editor>()
5467            .unwrap()
5468    });
5469    code_action_editor.update(cx_b, |editor, cx| {
5470        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
5471        editor.undo(&Undo, cx);
5472        assert_eq!(
5473            editor.text(cx),
5474            "mod other;\nfn main() { let foo = other::foo(); }\npub fn foo() -> usize { 4 }"
5475        );
5476        editor.redo(&Redo, cx);
5477        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
5478    });
5479}
5480
5481#[gpui::test(iterations = 10)]
5482async fn test_collaborating_with_renames(
5483    deterministic: Arc<Deterministic>,
5484    cx_a: &mut TestAppContext,
5485    cx_b: &mut TestAppContext,
5486) {
5487    deterministic.forbid_parking();
5488    let mut server = TestServer::start(&deterministic).await;
5489    let client_a = server.create_client(cx_a, "user_a").await;
5490    let client_b = server.create_client(cx_b, "user_b").await;
5491    server
5492        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5493        .await;
5494    let active_call_a = cx_a.read(ActiveCall::global);
5495
5496    cx_b.update(editor::init);
5497
5498    // Set up a fake language server.
5499    let mut language = Language::new(
5500        LanguageConfig {
5501            name: "Rust".into(),
5502            path_suffixes: vec!["rs".to_string()],
5503            ..Default::default()
5504        },
5505        Some(tree_sitter_rust::language()),
5506    );
5507    let mut fake_language_servers = language
5508        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
5509            capabilities: lsp::ServerCapabilities {
5510                rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
5511                    prepare_provider: Some(true),
5512                    work_done_progress_options: Default::default(),
5513                })),
5514                ..Default::default()
5515            },
5516            ..Default::default()
5517        }))
5518        .await;
5519    client_a.language_registry().add(Arc::new(language));
5520
5521    client_a
5522        .fs()
5523        .insert_tree(
5524            "/dir",
5525            json!({
5526                "one.rs": "const ONE: usize = 1;",
5527                "two.rs": "const TWO: usize = one::ONE + one::ONE;"
5528            }),
5529        )
5530        .await;
5531    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
5532    let project_id = active_call_a
5533        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5534        .await
5535        .unwrap();
5536    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5537
5538    let window_b =
5539        cx_b.add_window(|cx| Workspace::new(0, project_b.clone(), client_b.app_state.clone(), cx));
5540    let workspace_b = window_b.root(cx_b);
5541    let editor_b = workspace_b
5542        .update(cx_b, |workspace, cx| {
5543            workspace.open_path((worktree_id, "one.rs"), None, true, cx)
5544        })
5545        .await
5546        .unwrap()
5547        .downcast::<Editor>()
5548        .unwrap();
5549    let fake_language_server = fake_language_servers.next().await.unwrap();
5550
5551    // Move cursor to a location that can be renamed.
5552    let prepare_rename = editor_b.update(cx_b, |editor, cx| {
5553        editor.change_selections(None, cx, |s| s.select_ranges([7..7]));
5554        editor.rename(&Rename, cx).unwrap()
5555    });
5556
5557    fake_language_server
5558        .handle_request::<lsp::request::PrepareRenameRequest, _, _>(|params, _| async move {
5559            assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs");
5560            assert_eq!(params.position, lsp::Position::new(0, 7));
5561            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
5562                lsp::Position::new(0, 6),
5563                lsp::Position::new(0, 9),
5564            ))))
5565        })
5566        .next()
5567        .await
5568        .unwrap();
5569    prepare_rename.await.unwrap();
5570    editor_b.update(cx_b, |editor, cx| {
5571        use editor::ToOffset;
5572        let rename = editor.pending_rename().unwrap();
5573        let buffer = editor.buffer().read(cx).snapshot(cx);
5574        assert_eq!(
5575            rename.range.start.to_offset(&buffer)..rename.range.end.to_offset(&buffer),
5576            6..9
5577        );
5578        rename.editor.update(cx, |rename_editor, cx| {
5579            rename_editor.buffer().update(cx, |rename_buffer, cx| {
5580                rename_buffer.edit([(0..3, "THREE")], None, cx);
5581            });
5582        });
5583    });
5584
5585    let confirm_rename = workspace_b.update(cx_b, |workspace, cx| {
5586        Editor::confirm_rename(workspace, &ConfirmRename, cx).unwrap()
5587    });
5588    fake_language_server
5589        .handle_request::<lsp::request::Rename, _, _>(|params, _| async move {
5590            assert_eq!(
5591                params.text_document_position.text_document.uri.as_str(),
5592                "file:///dir/one.rs"
5593            );
5594            assert_eq!(
5595                params.text_document_position.position,
5596                lsp::Position::new(0, 6)
5597            );
5598            assert_eq!(params.new_name, "THREE");
5599            Ok(Some(lsp::WorkspaceEdit {
5600                changes: Some(
5601                    [
5602                        (
5603                            lsp::Url::from_file_path("/dir/one.rs").unwrap(),
5604                            vec![lsp::TextEdit::new(
5605                                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5606                                "THREE".to_string(),
5607                            )],
5608                        ),
5609                        (
5610                            lsp::Url::from_file_path("/dir/two.rs").unwrap(),
5611                            vec![
5612                                lsp::TextEdit::new(
5613                                    lsp::Range::new(
5614                                        lsp::Position::new(0, 24),
5615                                        lsp::Position::new(0, 27),
5616                                    ),
5617                                    "THREE".to_string(),
5618                                ),
5619                                lsp::TextEdit::new(
5620                                    lsp::Range::new(
5621                                        lsp::Position::new(0, 35),
5622                                        lsp::Position::new(0, 38),
5623                                    ),
5624                                    "THREE".to_string(),
5625                                ),
5626                            ],
5627                        ),
5628                    ]
5629                    .into_iter()
5630                    .collect(),
5631                ),
5632                ..Default::default()
5633            }))
5634        })
5635        .next()
5636        .await
5637        .unwrap();
5638    confirm_rename.await.unwrap();
5639
5640    let rename_editor = workspace_b.read_with(cx_b, |workspace, cx| {
5641        workspace
5642            .active_item(cx)
5643            .unwrap()
5644            .downcast::<Editor>()
5645            .unwrap()
5646    });
5647    rename_editor.update(cx_b, |editor, cx| {
5648        assert_eq!(
5649            editor.text(cx),
5650            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
5651        );
5652        editor.undo(&Undo, cx);
5653        assert_eq!(
5654            editor.text(cx),
5655            "const ONE: usize = 1;\nconst TWO: usize = one::ONE + one::ONE;"
5656        );
5657        editor.redo(&Redo, cx);
5658        assert_eq!(
5659            editor.text(cx),
5660            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
5661        );
5662    });
5663
5664    // Ensure temporary rename edits cannot be undone/redone.
5665    editor_b.update(cx_b, |editor, cx| {
5666        editor.undo(&Undo, cx);
5667        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
5668        editor.undo(&Undo, cx);
5669        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
5670        editor.redo(&Redo, cx);
5671        assert_eq!(editor.text(cx), "const THREE: usize = 1;");
5672    })
5673}
5674
5675#[gpui::test(iterations = 10)]
5676async fn test_language_server_statuses(
5677    deterministic: Arc<Deterministic>,
5678    cx_a: &mut TestAppContext,
5679    cx_b: &mut TestAppContext,
5680) {
5681    deterministic.forbid_parking();
5682    let mut server = TestServer::start(&deterministic).await;
5683    let client_a = server.create_client(cx_a, "user_a").await;
5684    let client_b = server.create_client(cx_b, "user_b").await;
5685    server
5686        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5687        .await;
5688    let active_call_a = cx_a.read(ActiveCall::global);
5689
5690    cx_b.update(editor::init);
5691
5692    // Set up a fake language server.
5693    let mut language = Language::new(
5694        LanguageConfig {
5695            name: "Rust".into(),
5696            path_suffixes: vec!["rs".to_string()],
5697            ..Default::default()
5698        },
5699        Some(tree_sitter_rust::language()),
5700    );
5701    let mut fake_language_servers = language
5702        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
5703            name: "the-language-server",
5704            ..Default::default()
5705        }))
5706        .await;
5707    client_a.language_registry().add(Arc::new(language));
5708
5709    client_a
5710        .fs()
5711        .insert_tree(
5712            "/dir",
5713            json!({
5714                "main.rs": "const ONE: usize = 1;",
5715            }),
5716        )
5717        .await;
5718    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
5719
5720    let _buffer_a = project_a
5721        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
5722        .await
5723        .unwrap();
5724
5725    let fake_language_server = fake_language_servers.next().await.unwrap();
5726    fake_language_server.start_progress("the-token").await;
5727    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
5728        token: lsp::NumberOrString::String("the-token".to_string()),
5729        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
5730            lsp::WorkDoneProgressReport {
5731                message: Some("the-message".to_string()),
5732                ..Default::default()
5733            },
5734        )),
5735    });
5736    deterministic.run_until_parked();
5737    project_a.read_with(cx_a, |project, _| {
5738        let status = project.language_server_statuses().next().unwrap();
5739        assert_eq!(status.name, "the-language-server");
5740        assert_eq!(status.pending_work.len(), 1);
5741        assert_eq!(
5742            status.pending_work["the-token"].message.as_ref().unwrap(),
5743            "the-message"
5744        );
5745    });
5746
5747    let project_id = active_call_a
5748        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5749        .await
5750        .unwrap();
5751    deterministic.run_until_parked();
5752    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5753    project_b.read_with(cx_b, |project, _| {
5754        let status = project.language_server_statuses().next().unwrap();
5755        assert_eq!(status.name, "the-language-server");
5756    });
5757
5758    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
5759        token: lsp::NumberOrString::String("the-token".to_string()),
5760        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
5761            lsp::WorkDoneProgressReport {
5762                message: Some("the-message-2".to_string()),
5763                ..Default::default()
5764            },
5765        )),
5766    });
5767    deterministic.run_until_parked();
5768    project_a.read_with(cx_a, |project, _| {
5769        let status = project.language_server_statuses().next().unwrap();
5770        assert_eq!(status.name, "the-language-server");
5771        assert_eq!(status.pending_work.len(), 1);
5772        assert_eq!(
5773            status.pending_work["the-token"].message.as_ref().unwrap(),
5774            "the-message-2"
5775        );
5776    });
5777    project_b.read_with(cx_b, |project, _| {
5778        let status = project.language_server_statuses().next().unwrap();
5779        assert_eq!(status.name, "the-language-server");
5780        assert_eq!(status.pending_work.len(), 1);
5781        assert_eq!(
5782            status.pending_work["the-token"].message.as_ref().unwrap(),
5783            "the-message-2"
5784        );
5785    });
5786}
5787
5788#[gpui::test(iterations = 10)]
5789async fn test_contacts(
5790    deterministic: Arc<Deterministic>,
5791    cx_a: &mut TestAppContext,
5792    cx_b: &mut TestAppContext,
5793    cx_c: &mut TestAppContext,
5794    cx_d: &mut TestAppContext,
5795) {
5796    deterministic.forbid_parking();
5797    let mut server = TestServer::start(&deterministic).await;
5798    let client_a = server.create_client(cx_a, "user_a").await;
5799    let client_b = server.create_client(cx_b, "user_b").await;
5800    let client_c = server.create_client(cx_c, "user_c").await;
5801    let client_d = server.create_client(cx_d, "user_d").await;
5802    server
5803        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
5804        .await;
5805    let active_call_a = cx_a.read(ActiveCall::global);
5806    let active_call_b = cx_b.read(ActiveCall::global);
5807    let active_call_c = cx_c.read(ActiveCall::global);
5808    let _active_call_d = cx_d.read(ActiveCall::global);
5809
5810    deterministic.run_until_parked();
5811    assert_eq!(
5812        contacts(&client_a, cx_a),
5813        [
5814            ("user_b".to_string(), "online", "free"),
5815            ("user_c".to_string(), "online", "free")
5816        ]
5817    );
5818    assert_eq!(
5819        contacts(&client_b, cx_b),
5820        [
5821            ("user_a".to_string(), "online", "free"),
5822            ("user_c".to_string(), "online", "free")
5823        ]
5824    );
5825    assert_eq!(
5826        contacts(&client_c, cx_c),
5827        [
5828            ("user_a".to_string(), "online", "free"),
5829            ("user_b".to_string(), "online", "free")
5830        ]
5831    );
5832    assert_eq!(contacts(&client_d, cx_d), []);
5833
5834    server.disconnect_client(client_c.peer_id().unwrap());
5835    server.forbid_connections();
5836    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5837    assert_eq!(
5838        contacts(&client_a, cx_a),
5839        [
5840            ("user_b".to_string(), "online", "free"),
5841            ("user_c".to_string(), "offline", "free")
5842        ]
5843    );
5844    assert_eq!(
5845        contacts(&client_b, cx_b),
5846        [
5847            ("user_a".to_string(), "online", "free"),
5848            ("user_c".to_string(), "offline", "free")
5849        ]
5850    );
5851    assert_eq!(contacts(&client_c, cx_c), []);
5852    assert_eq!(contacts(&client_d, cx_d), []);
5853
5854    server.allow_connections();
5855    client_c
5856        .authenticate_and_connect(false, &cx_c.to_async())
5857        .await
5858        .unwrap();
5859
5860    deterministic.run_until_parked();
5861    assert_eq!(
5862        contacts(&client_a, cx_a),
5863        [
5864            ("user_b".to_string(), "online", "free"),
5865            ("user_c".to_string(), "online", "free")
5866        ]
5867    );
5868    assert_eq!(
5869        contacts(&client_b, cx_b),
5870        [
5871            ("user_a".to_string(), "online", "free"),
5872            ("user_c".to_string(), "online", "free")
5873        ]
5874    );
5875    assert_eq!(
5876        contacts(&client_c, cx_c),
5877        [
5878            ("user_a".to_string(), "online", "free"),
5879            ("user_b".to_string(), "online", "free")
5880        ]
5881    );
5882    assert_eq!(contacts(&client_d, cx_d), []);
5883
5884    active_call_a
5885        .update(cx_a, |call, cx| {
5886            call.invite(client_b.user_id().unwrap(), None, cx)
5887        })
5888        .await
5889        .unwrap();
5890    deterministic.run_until_parked();
5891    assert_eq!(
5892        contacts(&client_a, cx_a),
5893        [
5894            ("user_b".to_string(), "online", "busy"),
5895            ("user_c".to_string(), "online", "free")
5896        ]
5897    );
5898    assert_eq!(
5899        contacts(&client_b, cx_b),
5900        [
5901            ("user_a".to_string(), "online", "busy"),
5902            ("user_c".to_string(), "online", "free")
5903        ]
5904    );
5905    assert_eq!(
5906        contacts(&client_c, cx_c),
5907        [
5908            ("user_a".to_string(), "online", "busy"),
5909            ("user_b".to_string(), "online", "busy")
5910        ]
5911    );
5912    assert_eq!(contacts(&client_d, cx_d), []);
5913
5914    // Client B and client D become contacts while client B is being called.
5915    server
5916        .make_contacts(&mut [(&client_b, cx_b), (&client_d, cx_d)])
5917        .await;
5918    deterministic.run_until_parked();
5919    assert_eq!(
5920        contacts(&client_a, cx_a),
5921        [
5922            ("user_b".to_string(), "online", "busy"),
5923            ("user_c".to_string(), "online", "free")
5924        ]
5925    );
5926    assert_eq!(
5927        contacts(&client_b, cx_b),
5928        [
5929            ("user_a".to_string(), "online", "busy"),
5930            ("user_c".to_string(), "online", "free"),
5931            ("user_d".to_string(), "online", "free"),
5932        ]
5933    );
5934    assert_eq!(
5935        contacts(&client_c, cx_c),
5936        [
5937            ("user_a".to_string(), "online", "busy"),
5938            ("user_b".to_string(), "online", "busy")
5939        ]
5940    );
5941    assert_eq!(
5942        contacts(&client_d, cx_d),
5943        [("user_b".to_string(), "online", "busy")]
5944    );
5945
5946    active_call_b.update(cx_b, |call, cx| call.decline_incoming(cx).unwrap());
5947    deterministic.run_until_parked();
5948    assert_eq!(
5949        contacts(&client_a, cx_a),
5950        [
5951            ("user_b".to_string(), "online", "free"),
5952            ("user_c".to_string(), "online", "free")
5953        ]
5954    );
5955    assert_eq!(
5956        contacts(&client_b, cx_b),
5957        [
5958            ("user_a".to_string(), "online", "free"),
5959            ("user_c".to_string(), "online", "free"),
5960            ("user_d".to_string(), "online", "free")
5961        ]
5962    );
5963    assert_eq!(
5964        contacts(&client_c, cx_c),
5965        [
5966            ("user_a".to_string(), "online", "free"),
5967            ("user_b".to_string(), "online", "free")
5968        ]
5969    );
5970    assert_eq!(
5971        contacts(&client_d, cx_d),
5972        [("user_b".to_string(), "online", "free")]
5973    );
5974
5975    active_call_c
5976        .update(cx_c, |call, cx| {
5977            call.invite(client_a.user_id().unwrap(), None, cx)
5978        })
5979        .await
5980        .unwrap();
5981    deterministic.run_until_parked();
5982    assert_eq!(
5983        contacts(&client_a, cx_a),
5984        [
5985            ("user_b".to_string(), "online", "free"),
5986            ("user_c".to_string(), "online", "busy")
5987        ]
5988    );
5989    assert_eq!(
5990        contacts(&client_b, cx_b),
5991        [
5992            ("user_a".to_string(), "online", "busy"),
5993            ("user_c".to_string(), "online", "busy"),
5994            ("user_d".to_string(), "online", "free")
5995        ]
5996    );
5997    assert_eq!(
5998        contacts(&client_c, cx_c),
5999        [
6000            ("user_a".to_string(), "online", "busy"),
6001            ("user_b".to_string(), "online", "free")
6002        ]
6003    );
6004    assert_eq!(
6005        contacts(&client_d, cx_d),
6006        [("user_b".to_string(), "online", "free")]
6007    );
6008
6009    active_call_a
6010        .update(cx_a, |call, cx| call.accept_incoming(cx))
6011        .await
6012        .unwrap();
6013    deterministic.run_until_parked();
6014    assert_eq!(
6015        contacts(&client_a, cx_a),
6016        [
6017            ("user_b".to_string(), "online", "free"),
6018            ("user_c".to_string(), "online", "busy")
6019        ]
6020    );
6021    assert_eq!(
6022        contacts(&client_b, cx_b),
6023        [
6024            ("user_a".to_string(), "online", "busy"),
6025            ("user_c".to_string(), "online", "busy"),
6026            ("user_d".to_string(), "online", "free")
6027        ]
6028    );
6029    assert_eq!(
6030        contacts(&client_c, cx_c),
6031        [
6032            ("user_a".to_string(), "online", "busy"),
6033            ("user_b".to_string(), "online", "free")
6034        ]
6035    );
6036    assert_eq!(
6037        contacts(&client_d, cx_d),
6038        [("user_b".to_string(), "online", "free")]
6039    );
6040
6041    active_call_a
6042        .update(cx_a, |call, cx| {
6043            call.invite(client_b.user_id().unwrap(), None, cx)
6044        })
6045        .await
6046        .unwrap();
6047    deterministic.run_until_parked();
6048    assert_eq!(
6049        contacts(&client_a, cx_a),
6050        [
6051            ("user_b".to_string(), "online", "busy"),
6052            ("user_c".to_string(), "online", "busy")
6053        ]
6054    );
6055    assert_eq!(
6056        contacts(&client_b, cx_b),
6057        [
6058            ("user_a".to_string(), "online", "busy"),
6059            ("user_c".to_string(), "online", "busy"),
6060            ("user_d".to_string(), "online", "free")
6061        ]
6062    );
6063    assert_eq!(
6064        contacts(&client_c, cx_c),
6065        [
6066            ("user_a".to_string(), "online", "busy"),
6067            ("user_b".to_string(), "online", "busy")
6068        ]
6069    );
6070    assert_eq!(
6071        contacts(&client_d, cx_d),
6072        [("user_b".to_string(), "online", "busy")]
6073    );
6074
6075    active_call_a
6076        .update(cx_a, |call, cx| call.hang_up(cx))
6077        .await
6078        .unwrap();
6079    deterministic.run_until_parked();
6080    assert_eq!(
6081        contacts(&client_a, cx_a),
6082        [
6083            ("user_b".to_string(), "online", "free"),
6084            ("user_c".to_string(), "online", "free")
6085        ]
6086    );
6087    assert_eq!(
6088        contacts(&client_b, cx_b),
6089        [
6090            ("user_a".to_string(), "online", "free"),
6091            ("user_c".to_string(), "online", "free"),
6092            ("user_d".to_string(), "online", "free")
6093        ]
6094    );
6095    assert_eq!(
6096        contacts(&client_c, cx_c),
6097        [
6098            ("user_a".to_string(), "online", "free"),
6099            ("user_b".to_string(), "online", "free")
6100        ]
6101    );
6102    assert_eq!(
6103        contacts(&client_d, cx_d),
6104        [("user_b".to_string(), "online", "free")]
6105    );
6106
6107    active_call_a
6108        .update(cx_a, |call, cx| {
6109            call.invite(client_b.user_id().unwrap(), None, cx)
6110        })
6111        .await
6112        .unwrap();
6113    deterministic.run_until_parked();
6114    assert_eq!(
6115        contacts(&client_a, cx_a),
6116        [
6117            ("user_b".to_string(), "online", "busy"),
6118            ("user_c".to_string(), "online", "free")
6119        ]
6120    );
6121    assert_eq!(
6122        contacts(&client_b, cx_b),
6123        [
6124            ("user_a".to_string(), "online", "busy"),
6125            ("user_c".to_string(), "online", "free"),
6126            ("user_d".to_string(), "online", "free")
6127        ]
6128    );
6129    assert_eq!(
6130        contacts(&client_c, cx_c),
6131        [
6132            ("user_a".to_string(), "online", "busy"),
6133            ("user_b".to_string(), "online", "busy")
6134        ]
6135    );
6136    assert_eq!(
6137        contacts(&client_d, cx_d),
6138        [("user_b".to_string(), "online", "busy")]
6139    );
6140
6141    server.forbid_connections();
6142    server.disconnect_client(client_a.peer_id().unwrap());
6143    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
6144    assert_eq!(contacts(&client_a, cx_a), []);
6145    assert_eq!(
6146        contacts(&client_b, cx_b),
6147        [
6148            ("user_a".to_string(), "offline", "free"),
6149            ("user_c".to_string(), "online", "free"),
6150            ("user_d".to_string(), "online", "free")
6151        ]
6152    );
6153    assert_eq!(
6154        contacts(&client_c, cx_c),
6155        [
6156            ("user_a".to_string(), "offline", "free"),
6157            ("user_b".to_string(), "online", "free")
6158        ]
6159    );
6160    assert_eq!(
6161        contacts(&client_d, cx_d),
6162        [("user_b".to_string(), "online", "free")]
6163    );
6164
6165    // Test removing a contact
6166    client_b
6167        .user_store()
6168        .update(cx_b, |store, cx| {
6169            store.remove_contact(client_c.user_id().unwrap(), cx)
6170        })
6171        .await
6172        .unwrap();
6173    deterministic.run_until_parked();
6174    assert_eq!(
6175        contacts(&client_b, cx_b),
6176        [
6177            ("user_a".to_string(), "offline", "free"),
6178            ("user_d".to_string(), "online", "free")
6179        ]
6180    );
6181    assert_eq!(
6182        contacts(&client_c, cx_c),
6183        [("user_a".to_string(), "offline", "free"),]
6184    );
6185
6186    fn contacts(
6187        client: &TestClient,
6188        cx: &TestAppContext,
6189    ) -> Vec<(String, &'static str, &'static str)> {
6190        client.user_store().read_with(cx, |store, _| {
6191            store
6192                .contacts()
6193                .iter()
6194                .map(|contact| {
6195                    (
6196                        contact.user.github_login.clone(),
6197                        if contact.online { "online" } else { "offline" },
6198                        if contact.busy { "busy" } else { "free" },
6199                    )
6200                })
6201                .collect()
6202        })
6203    }
6204}
6205
6206#[gpui::test(iterations = 10)]
6207async fn test_contact_requests(
6208    deterministic: Arc<Deterministic>,
6209    cx_a: &mut TestAppContext,
6210    cx_a2: &mut TestAppContext,
6211    cx_b: &mut TestAppContext,
6212    cx_b2: &mut TestAppContext,
6213    cx_c: &mut TestAppContext,
6214    cx_c2: &mut TestAppContext,
6215) {
6216    deterministic.forbid_parking();
6217
6218    // Connect to a server as 3 clients.
6219    let mut server = TestServer::start(&deterministic).await;
6220    let client_a = server.create_client(cx_a, "user_a").await;
6221    let client_a2 = server.create_client(cx_a2, "user_a").await;
6222    let client_b = server.create_client(cx_b, "user_b").await;
6223    let client_b2 = server.create_client(cx_b2, "user_b").await;
6224    let client_c = server.create_client(cx_c, "user_c").await;
6225    let client_c2 = server.create_client(cx_c2, "user_c").await;
6226
6227    assert_eq!(client_a.user_id().unwrap(), client_a2.user_id().unwrap());
6228    assert_eq!(client_b.user_id().unwrap(), client_b2.user_id().unwrap());
6229    assert_eq!(client_c.user_id().unwrap(), client_c2.user_id().unwrap());
6230
6231    // User A and User C request that user B become their contact.
6232    client_a
6233        .user_store()
6234        .update(cx_a, |store, cx| {
6235            store.request_contact(client_b.user_id().unwrap(), cx)
6236        })
6237        .await
6238        .unwrap();
6239    client_c
6240        .user_store()
6241        .update(cx_c, |store, cx| {
6242            store.request_contact(client_b.user_id().unwrap(), cx)
6243        })
6244        .await
6245        .unwrap();
6246    deterministic.run_until_parked();
6247
6248    // All users see the pending request appear in all their clients.
6249    assert_eq!(
6250        client_a.summarize_contacts(cx_a).outgoing_requests,
6251        &["user_b"]
6252    );
6253    assert_eq!(
6254        client_a2.summarize_contacts(cx_a2).outgoing_requests,
6255        &["user_b"]
6256    );
6257    assert_eq!(
6258        client_b.summarize_contacts(cx_b).incoming_requests,
6259        &["user_a", "user_c"]
6260    );
6261    assert_eq!(
6262        client_b2.summarize_contacts(cx_b2).incoming_requests,
6263        &["user_a", "user_c"]
6264    );
6265    assert_eq!(
6266        client_c.summarize_contacts(cx_c).outgoing_requests,
6267        &["user_b"]
6268    );
6269    assert_eq!(
6270        client_c2.summarize_contacts(cx_c2).outgoing_requests,
6271        &["user_b"]
6272    );
6273
6274    // Contact requests are present upon connecting (tested here via disconnect/reconnect)
6275    disconnect_and_reconnect(&client_a, cx_a).await;
6276    disconnect_and_reconnect(&client_b, cx_b).await;
6277    disconnect_and_reconnect(&client_c, cx_c).await;
6278    deterministic.run_until_parked();
6279    assert_eq!(
6280        client_a.summarize_contacts(cx_a).outgoing_requests,
6281        &["user_b"]
6282    );
6283    assert_eq!(
6284        client_b.summarize_contacts(cx_b).incoming_requests,
6285        &["user_a", "user_c"]
6286    );
6287    assert_eq!(
6288        client_c.summarize_contacts(cx_c).outgoing_requests,
6289        &["user_b"]
6290    );
6291
6292    // User B accepts the request from user A.
6293    client_b
6294        .user_store()
6295        .update(cx_b, |store, cx| {
6296            store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
6297        })
6298        .await
6299        .unwrap();
6300
6301    deterministic.run_until_parked();
6302
6303    // User B sees user A as their contact now in all client, and the incoming request from them is removed.
6304    let contacts_b = client_b.summarize_contacts(cx_b);
6305    assert_eq!(contacts_b.current, &["user_a"]);
6306    assert_eq!(contacts_b.incoming_requests, &["user_c"]);
6307    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
6308    assert_eq!(contacts_b2.current, &["user_a"]);
6309    assert_eq!(contacts_b2.incoming_requests, &["user_c"]);
6310
6311    // User A sees user B as their contact now in all clients, and the outgoing request to them is removed.
6312    let contacts_a = client_a.summarize_contacts(cx_a);
6313    assert_eq!(contacts_a.current, &["user_b"]);
6314    assert!(contacts_a.outgoing_requests.is_empty());
6315    let contacts_a2 = client_a2.summarize_contacts(cx_a2);
6316    assert_eq!(contacts_a2.current, &["user_b"]);
6317    assert!(contacts_a2.outgoing_requests.is_empty());
6318
6319    // Contacts are present upon connecting (tested here via disconnect/reconnect)
6320    disconnect_and_reconnect(&client_a, cx_a).await;
6321    disconnect_and_reconnect(&client_b, cx_b).await;
6322    disconnect_and_reconnect(&client_c, cx_c).await;
6323    deterministic.run_until_parked();
6324    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
6325    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
6326    assert_eq!(
6327        client_b.summarize_contacts(cx_b).incoming_requests,
6328        &["user_c"]
6329    );
6330    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
6331    assert_eq!(
6332        client_c.summarize_contacts(cx_c).outgoing_requests,
6333        &["user_b"]
6334    );
6335
6336    // User B rejects the request from user C.
6337    client_b
6338        .user_store()
6339        .update(cx_b, |store, cx| {
6340            store.respond_to_contact_request(client_c.user_id().unwrap(), false, cx)
6341        })
6342        .await
6343        .unwrap();
6344
6345    deterministic.run_until_parked();
6346
6347    // User B doesn't see user C as their contact, and the incoming request from them is removed.
6348    let contacts_b = client_b.summarize_contacts(cx_b);
6349    assert_eq!(contacts_b.current, &["user_a"]);
6350    assert!(contacts_b.incoming_requests.is_empty());
6351    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
6352    assert_eq!(contacts_b2.current, &["user_a"]);
6353    assert!(contacts_b2.incoming_requests.is_empty());
6354
6355    // User C doesn't see user B as their contact, and the outgoing request to them is removed.
6356    let contacts_c = client_c.summarize_contacts(cx_c);
6357    assert!(contacts_c.current.is_empty());
6358    assert!(contacts_c.outgoing_requests.is_empty());
6359    let contacts_c2 = client_c2.summarize_contacts(cx_c2);
6360    assert!(contacts_c2.current.is_empty());
6361    assert!(contacts_c2.outgoing_requests.is_empty());
6362
6363    // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect)
6364    disconnect_and_reconnect(&client_a, cx_a).await;
6365    disconnect_and_reconnect(&client_b, cx_b).await;
6366    disconnect_and_reconnect(&client_c, cx_c).await;
6367    deterministic.run_until_parked();
6368    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
6369    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
6370    assert!(client_b
6371        .summarize_contacts(cx_b)
6372        .incoming_requests
6373        .is_empty());
6374    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
6375    assert!(client_c
6376        .summarize_contacts(cx_c)
6377        .outgoing_requests
6378        .is_empty());
6379
6380    async fn disconnect_and_reconnect(client: &TestClient, cx: &mut TestAppContext) {
6381        client.disconnect(&cx.to_async());
6382        client.clear_contacts(cx).await;
6383        client
6384            .authenticate_and_connect(false, &cx.to_async())
6385            .await
6386            .unwrap();
6387    }
6388}
6389
6390#[gpui::test(iterations = 10)]
6391async fn test_basic_following(
6392    deterministic: Arc<Deterministic>,
6393    cx_a: &mut TestAppContext,
6394    cx_b: &mut TestAppContext,
6395    cx_c: &mut TestAppContext,
6396    cx_d: &mut TestAppContext,
6397) {
6398    deterministic.forbid_parking();
6399
6400    let mut server = TestServer::start(&deterministic).await;
6401    let client_a = server.create_client(cx_a, "user_a").await;
6402    let client_b = server.create_client(cx_b, "user_b").await;
6403    let client_c = server.create_client(cx_c, "user_c").await;
6404    let client_d = server.create_client(cx_d, "user_d").await;
6405    server
6406        .create_room(&mut [
6407            (&client_a, cx_a),
6408            (&client_b, cx_b),
6409            (&client_c, cx_c),
6410            (&client_d, cx_d),
6411        ])
6412        .await;
6413    let active_call_a = cx_a.read(ActiveCall::global);
6414    let active_call_b = cx_b.read(ActiveCall::global);
6415
6416    cx_a.update(editor::init);
6417    cx_b.update(editor::init);
6418
6419    client_a
6420        .fs()
6421        .insert_tree(
6422            "/a",
6423            json!({
6424                "1.txt": "one\none\none",
6425                "2.txt": "two\ntwo\ntwo",
6426                "3.txt": "three\nthree\nthree",
6427            }),
6428        )
6429        .await;
6430    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
6431    active_call_a
6432        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
6433        .await
6434        .unwrap();
6435
6436    let project_id = active_call_a
6437        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6438        .await
6439        .unwrap();
6440    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6441    active_call_b
6442        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6443        .await
6444        .unwrap();
6445
6446    let window_a = client_a.build_workspace(&project_a, cx_a);
6447    let workspace_a = window_a.root(cx_a);
6448    let window_b = client_b.build_workspace(&project_b, cx_b);
6449    let workspace_b = window_b.root(cx_b);
6450
6451    // Client A opens some editors.
6452    let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
6453    let editor_a1 = workspace_a
6454        .update(cx_a, |workspace, cx| {
6455            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6456        })
6457        .await
6458        .unwrap()
6459        .downcast::<Editor>()
6460        .unwrap();
6461    let editor_a2 = workspace_a
6462        .update(cx_a, |workspace, cx| {
6463            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
6464        })
6465        .await
6466        .unwrap()
6467        .downcast::<Editor>()
6468        .unwrap();
6469
6470    // Client B opens an editor.
6471    let editor_b1 = workspace_b
6472        .update(cx_b, |workspace, cx| {
6473            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6474        })
6475        .await
6476        .unwrap()
6477        .downcast::<Editor>()
6478        .unwrap();
6479
6480    let peer_id_a = client_a.peer_id().unwrap();
6481    let peer_id_b = client_b.peer_id().unwrap();
6482    let peer_id_c = client_c.peer_id().unwrap();
6483    let peer_id_d = client_d.peer_id().unwrap();
6484
6485    // Client A updates their selections in those editors
6486    editor_a1.update(cx_a, |editor, cx| {
6487        editor.handle_input("a", cx);
6488        editor.handle_input("b", cx);
6489        editor.handle_input("c", cx);
6490        editor.select_left(&Default::default(), cx);
6491        assert_eq!(editor.selections.ranges(cx), vec![3..2]);
6492    });
6493    editor_a2.update(cx_a, |editor, cx| {
6494        editor.handle_input("d", cx);
6495        editor.handle_input("e", cx);
6496        editor.select_left(&Default::default(), cx);
6497        assert_eq!(editor.selections.ranges(cx), vec![2..1]);
6498    });
6499
6500    // When client B starts following client A, all visible view states are replicated to client B.
6501    workspace_b
6502        .update(cx_b, |workspace, cx| {
6503            workspace.toggle_follow(peer_id_a, cx).unwrap()
6504        })
6505        .await
6506        .unwrap();
6507
6508    cx_c.foreground().run_until_parked();
6509    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
6510        workspace
6511            .active_item(cx)
6512            .unwrap()
6513            .downcast::<Editor>()
6514            .unwrap()
6515    });
6516    assert_eq!(
6517        cx_b.read(|cx| editor_b2.project_path(cx)),
6518        Some((worktree_id, "2.txt").into())
6519    );
6520    assert_eq!(
6521        editor_b2.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
6522        vec![2..1]
6523    );
6524    assert_eq!(
6525        editor_b1.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
6526        vec![3..2]
6527    );
6528
6529    cx_c.foreground().run_until_parked();
6530    let active_call_c = cx_c.read(ActiveCall::global);
6531    let project_c = client_c.build_remote_project(project_id, cx_c).await;
6532    let window_c = client_c.build_workspace(&project_c, cx_c);
6533    let workspace_c = window_c.root(cx_c);
6534    active_call_c
6535        .update(cx_c, |call, cx| call.set_location(Some(&project_c), cx))
6536        .await
6537        .unwrap();
6538    drop(project_c);
6539
6540    // Client C also follows client A.
6541    workspace_c
6542        .update(cx_c, |workspace, cx| {
6543            workspace.toggle_follow(peer_id_a, cx).unwrap()
6544        })
6545        .await
6546        .unwrap();
6547
6548    cx_d.foreground().run_until_parked();
6549    let active_call_d = cx_d.read(ActiveCall::global);
6550    let project_d = client_d.build_remote_project(project_id, cx_d).await;
6551    let workspace_d = client_d.build_workspace(&project_d, cx_d).root(cx_d);
6552    active_call_d
6553        .update(cx_d, |call, cx| call.set_location(Some(&project_d), cx))
6554        .await
6555        .unwrap();
6556    drop(project_d);
6557
6558    // All clients see that clients B and C are following client A.
6559    cx_c.foreground().run_until_parked();
6560    for (name, active_call, cx) in [
6561        ("A", &active_call_a, &cx_a),
6562        ("B", &active_call_b, &cx_b),
6563        ("C", &active_call_c, &cx_c),
6564        ("D", &active_call_d, &cx_d),
6565    ] {
6566        active_call.read_with(*cx, |call, cx| {
6567            let room = call.room().unwrap().read(cx);
6568            assert_eq!(
6569                room.followers_for(peer_id_a, project_id),
6570                &[peer_id_b, peer_id_c],
6571                "checking followers for A as {name}"
6572            );
6573        });
6574    }
6575
6576    // Client C unfollows client A.
6577    workspace_c.update(cx_c, |workspace, cx| {
6578        workspace.toggle_follow(peer_id_a, cx);
6579    });
6580
6581    // All clients see that clients B is following client A.
6582    cx_c.foreground().run_until_parked();
6583    for (name, active_call, cx) in [
6584        ("A", &active_call_a, &cx_a),
6585        ("B", &active_call_b, &cx_b),
6586        ("C", &active_call_c, &cx_c),
6587        ("D", &active_call_d, &cx_d),
6588    ] {
6589        active_call.read_with(*cx, |call, cx| {
6590            let room = call.room().unwrap().read(cx);
6591            assert_eq!(
6592                room.followers_for(peer_id_a, project_id),
6593                &[peer_id_b],
6594                "checking followers for A as {name}"
6595            );
6596        });
6597    }
6598
6599    // Client C re-follows client A.
6600    workspace_c.update(cx_c, |workspace, cx| {
6601        workspace.toggle_follow(peer_id_a, cx);
6602    });
6603
6604    // All clients see that clients B and C are following client A.
6605    cx_c.foreground().run_until_parked();
6606    for (name, active_call, cx) in [
6607        ("A", &active_call_a, &cx_a),
6608        ("B", &active_call_b, &cx_b),
6609        ("C", &active_call_c, &cx_c),
6610        ("D", &active_call_d, &cx_d),
6611    ] {
6612        active_call.read_with(*cx, |call, cx| {
6613            let room = call.room().unwrap().read(cx);
6614            assert_eq!(
6615                room.followers_for(peer_id_a, project_id),
6616                &[peer_id_b, peer_id_c],
6617                "checking followers for A as {name}"
6618            );
6619        });
6620    }
6621
6622    // Client D follows client C.
6623    workspace_d
6624        .update(cx_d, |workspace, cx| {
6625            workspace.toggle_follow(peer_id_c, cx).unwrap()
6626        })
6627        .await
6628        .unwrap();
6629
6630    // All clients see that D is following C
6631    cx_d.foreground().run_until_parked();
6632    for (name, active_call, cx) in [
6633        ("A", &active_call_a, &cx_a),
6634        ("B", &active_call_b, &cx_b),
6635        ("C", &active_call_c, &cx_c),
6636        ("D", &active_call_d, &cx_d),
6637    ] {
6638        active_call.read_with(*cx, |call, cx| {
6639            let room = call.room().unwrap().read(cx);
6640            assert_eq!(
6641                room.followers_for(peer_id_c, project_id),
6642                &[peer_id_d],
6643                "checking followers for C as {name}"
6644            );
6645        });
6646    }
6647
6648    // Client C closes the project.
6649    window_c.remove(cx_c);
6650    cx_c.drop_last(workspace_c);
6651
6652    // Clients A and B see that client B is following A, and client C is not present in the followers.
6653    cx_c.foreground().run_until_parked();
6654    for (name, active_call, cx) in [("A", &active_call_a, &cx_a), ("B", &active_call_b, &cx_b)] {
6655        active_call.read_with(*cx, |call, cx| {
6656            let room = call.room().unwrap().read(cx);
6657            assert_eq!(
6658                room.followers_for(peer_id_a, project_id),
6659                &[peer_id_b],
6660                "checking followers for A as {name}"
6661            );
6662        });
6663    }
6664
6665    // All clients see that no-one is following C
6666    for (name, active_call, cx) in [
6667        ("A", &active_call_a, &cx_a),
6668        ("B", &active_call_b, &cx_b),
6669        ("C", &active_call_c, &cx_c),
6670        ("D", &active_call_d, &cx_d),
6671    ] {
6672        active_call.read_with(*cx, |call, cx| {
6673            let room = call.room().unwrap().read(cx);
6674            assert_eq!(
6675                room.followers_for(peer_id_c, project_id),
6676                &[],
6677                "checking followers for C as {name}"
6678            );
6679        });
6680    }
6681
6682    // When client A activates a different editor, client B does so as well.
6683    workspace_a.update(cx_a, |workspace, cx| {
6684        workspace.activate_item(&editor_a1, cx)
6685    });
6686    deterministic.run_until_parked();
6687    workspace_b.read_with(cx_b, |workspace, cx| {
6688        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
6689    });
6690
6691    // When client A opens a multibuffer, client B does so as well.
6692    let multibuffer_a = cx_a.add_model(|cx| {
6693        let buffer_a1 = project_a.update(cx, |project, cx| {
6694            project
6695                .get_open_buffer(&(worktree_id, "1.txt").into(), cx)
6696                .unwrap()
6697        });
6698        let buffer_a2 = project_a.update(cx, |project, cx| {
6699            project
6700                .get_open_buffer(&(worktree_id, "2.txt").into(), cx)
6701                .unwrap()
6702        });
6703        let mut result = MultiBuffer::new(0);
6704        result.push_excerpts(
6705            buffer_a1,
6706            [ExcerptRange {
6707                context: 0..3,
6708                primary: None,
6709            }],
6710            cx,
6711        );
6712        result.push_excerpts(
6713            buffer_a2,
6714            [ExcerptRange {
6715                context: 4..7,
6716                primary: None,
6717            }],
6718            cx,
6719        );
6720        result
6721    });
6722    let multibuffer_editor_a = workspace_a.update(cx_a, |workspace, cx| {
6723        let editor =
6724            cx.add_view(|cx| Editor::for_multibuffer(multibuffer_a, Some(project_a.clone()), cx));
6725        workspace.add_item(Box::new(editor.clone()), cx);
6726        editor
6727    });
6728    deterministic.run_until_parked();
6729    let multibuffer_editor_b = workspace_b.read_with(cx_b, |workspace, cx| {
6730        workspace
6731            .active_item(cx)
6732            .unwrap()
6733            .downcast::<Editor>()
6734            .unwrap()
6735    });
6736    assert_eq!(
6737        multibuffer_editor_a.read_with(cx_a, |editor, cx| editor.text(cx)),
6738        multibuffer_editor_b.read_with(cx_b, |editor, cx| editor.text(cx)),
6739    );
6740
6741    // When client A navigates back and forth, client B does so as well.
6742    workspace_a
6743        .update(cx_a, |workspace, cx| {
6744            workspace.go_back(workspace.active_pane().downgrade(), cx)
6745        })
6746        .await
6747        .unwrap();
6748    deterministic.run_until_parked();
6749    workspace_b.read_with(cx_b, |workspace, cx| {
6750        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
6751    });
6752
6753    workspace_a
6754        .update(cx_a, |workspace, cx| {
6755            workspace.go_back(workspace.active_pane().downgrade(), cx)
6756        })
6757        .await
6758        .unwrap();
6759    deterministic.run_until_parked();
6760    workspace_b.read_with(cx_b, |workspace, cx| {
6761        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b2.id());
6762    });
6763
6764    workspace_a
6765        .update(cx_a, |workspace, cx| {
6766            workspace.go_forward(workspace.active_pane().downgrade(), cx)
6767        })
6768        .await
6769        .unwrap();
6770    deterministic.run_until_parked();
6771    workspace_b.read_with(cx_b, |workspace, cx| {
6772        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
6773    });
6774
6775    // Changes to client A's editor are reflected on client B.
6776    editor_a1.update(cx_a, |editor, cx| {
6777        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2]));
6778    });
6779    deterministic.run_until_parked();
6780    editor_b1.read_with(cx_b, |editor, cx| {
6781        assert_eq!(editor.selections.ranges(cx), &[1..1, 2..2]);
6782    });
6783
6784    editor_a1.update(cx_a, |editor, cx| editor.set_text("TWO", cx));
6785    deterministic.run_until_parked();
6786    editor_b1.read_with(cx_b, |editor, cx| assert_eq!(editor.text(cx), "TWO"));
6787
6788    editor_a1.update(cx_a, |editor, cx| {
6789        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
6790        editor.set_scroll_position(vec2f(0., 100.), cx);
6791    });
6792    deterministic.run_until_parked();
6793    editor_b1.read_with(cx_b, |editor, cx| {
6794        assert_eq!(editor.selections.ranges(cx), &[3..3]);
6795    });
6796
6797    // After unfollowing, client B stops receiving updates from client A.
6798    workspace_b.update(cx_b, |workspace, cx| {
6799        workspace.unfollow(&workspace.active_pane().clone(), cx)
6800    });
6801    workspace_a.update(cx_a, |workspace, cx| {
6802        workspace.activate_item(&editor_a2, cx)
6803    });
6804    deterministic.run_until_parked();
6805    assert_eq!(
6806        workspace_b.read_with(cx_b, |workspace, cx| workspace
6807            .active_item(cx)
6808            .unwrap()
6809            .id()),
6810        editor_b1.id()
6811    );
6812
6813    // Client A starts following client B.
6814    workspace_a
6815        .update(cx_a, |workspace, cx| {
6816            workspace.toggle_follow(peer_id_b, cx).unwrap()
6817        })
6818        .await
6819        .unwrap();
6820    assert_eq!(
6821        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
6822        Some(peer_id_b)
6823    );
6824    assert_eq!(
6825        workspace_a.read_with(cx_a, |workspace, cx| workspace
6826            .active_item(cx)
6827            .unwrap()
6828            .id()),
6829        editor_a1.id()
6830    );
6831
6832    // Client B activates an external window, which causes a new screen-sharing item to be added to the pane.
6833    let display = MacOSDisplay::new();
6834    active_call_b
6835        .update(cx_b, |call, cx| call.set_location(None, cx))
6836        .await
6837        .unwrap();
6838    active_call_b
6839        .update(cx_b, |call, cx| {
6840            call.room().unwrap().update(cx, |room, cx| {
6841                room.set_display_sources(vec![display.clone()]);
6842                room.share_screen(cx)
6843            })
6844        })
6845        .await
6846        .unwrap();
6847    deterministic.run_until_parked();
6848    let shared_screen = workspace_a.read_with(cx_a, |workspace, cx| {
6849        workspace
6850            .active_item(cx)
6851            .unwrap()
6852            .downcast::<SharedScreen>()
6853            .unwrap()
6854    });
6855
6856    // Client B activates Zed again, which causes the previous editor to become focused again.
6857    active_call_b
6858        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6859        .await
6860        .unwrap();
6861    deterministic.run_until_parked();
6862    workspace_a.read_with(cx_a, |workspace, cx| {
6863        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_a1.id())
6864    });
6865
6866    // Client B activates a multibuffer that was created by following client A. Client A returns to that multibuffer.
6867    workspace_b.update(cx_b, |workspace, cx| {
6868        workspace.activate_item(&multibuffer_editor_b, cx)
6869    });
6870    deterministic.run_until_parked();
6871    workspace_a.read_with(cx_a, |workspace, cx| {
6872        assert_eq!(
6873            workspace.active_item(cx).unwrap().id(),
6874            multibuffer_editor_a.id()
6875        )
6876    });
6877
6878    // Client B activates a panel, and the previously-opened screen-sharing item gets activated.
6879    let panel = window_b.add_view(cx_b, |_| TestPanel::new(DockPosition::Left));
6880    workspace_b.update(cx_b, |workspace, cx| {
6881        workspace.add_panel(panel, cx);
6882        workspace.toggle_panel_focus::<TestPanel>(cx);
6883    });
6884    deterministic.run_until_parked();
6885    assert_eq!(
6886        workspace_a.read_with(cx_a, |workspace, cx| workspace
6887            .active_item(cx)
6888            .unwrap()
6889            .id()),
6890        shared_screen.id()
6891    );
6892
6893    // Toggling the focus back to the pane causes client A to return to the multibuffer.
6894    workspace_b.update(cx_b, |workspace, cx| {
6895        workspace.toggle_panel_focus::<TestPanel>(cx);
6896    });
6897    deterministic.run_until_parked();
6898    workspace_a.read_with(cx_a, |workspace, cx| {
6899        assert_eq!(
6900            workspace.active_item(cx).unwrap().id(),
6901            multibuffer_editor_a.id()
6902        )
6903    });
6904
6905    // Client B activates an item that doesn't implement following,
6906    // so the previously-opened screen-sharing item gets activated.
6907    let unfollowable_item = window_b.add_view(cx_b, |_| TestItem::new());
6908    workspace_b.update(cx_b, |workspace, cx| {
6909        workspace.active_pane().update(cx, |pane, cx| {
6910            pane.add_item(Box::new(unfollowable_item), true, true, None, cx)
6911        })
6912    });
6913    deterministic.run_until_parked();
6914    assert_eq!(
6915        workspace_a.read_with(cx_a, |workspace, cx| workspace
6916            .active_item(cx)
6917            .unwrap()
6918            .id()),
6919        shared_screen.id()
6920    );
6921
6922    // Following interrupts when client B disconnects.
6923    client_b.disconnect(&cx_b.to_async());
6924    deterministic.advance_clock(RECONNECT_TIMEOUT);
6925    assert_eq!(
6926        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
6927        None
6928    );
6929}
6930
6931#[gpui::test(iterations = 10)]
6932async fn test_join_call_after_screen_was_shared(
6933    deterministic: Arc<Deterministic>,
6934    cx_a: &mut TestAppContext,
6935    cx_b: &mut TestAppContext,
6936) {
6937    deterministic.forbid_parking();
6938    let mut server = TestServer::start(&deterministic).await;
6939
6940    let client_a = server.create_client(cx_a, "user_a").await;
6941    let client_b = server.create_client(cx_b, "user_b").await;
6942    server
6943        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6944        .await;
6945
6946    let active_call_a = cx_a.read(ActiveCall::global);
6947    let active_call_b = cx_b.read(ActiveCall::global);
6948
6949    // Call users B and C from client A.
6950    active_call_a
6951        .update(cx_a, |call, cx| {
6952            call.invite(client_b.user_id().unwrap(), None, cx)
6953        })
6954        .await
6955        .unwrap();
6956    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
6957    deterministic.run_until_parked();
6958    assert_eq!(
6959        room_participants(&room_a, cx_a),
6960        RoomParticipants {
6961            remote: Default::default(),
6962            pending: vec!["user_b".to_string()]
6963        }
6964    );
6965
6966    // User B receives the call.
6967    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
6968    let call_b = incoming_call_b.next().await.unwrap().unwrap();
6969    assert_eq!(call_b.calling_user.github_login, "user_a");
6970
6971    // User A shares their screen
6972    let display = MacOSDisplay::new();
6973    active_call_a
6974        .update(cx_a, |call, cx| {
6975            call.room().unwrap().update(cx, |room, cx| {
6976                room.set_display_sources(vec![display.clone()]);
6977                room.share_screen(cx)
6978            })
6979        })
6980        .await
6981        .unwrap();
6982
6983    client_b.user_store().update(cx_b, |user_store, _| {
6984        user_store.clear_cache();
6985    });
6986
6987    // User B joins the room
6988    active_call_b
6989        .update(cx_b, |call, cx| call.accept_incoming(cx))
6990        .await
6991        .unwrap();
6992    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
6993    assert!(incoming_call_b.next().await.unwrap().is_none());
6994
6995    deterministic.run_until_parked();
6996    assert_eq!(
6997        room_participants(&room_a, cx_a),
6998        RoomParticipants {
6999            remote: vec!["user_b".to_string()],
7000            pending: vec![],
7001        }
7002    );
7003    assert_eq!(
7004        room_participants(&room_b, cx_b),
7005        RoomParticipants {
7006            remote: vec!["user_a".to_string()],
7007            pending: vec![],
7008        }
7009    );
7010
7011    // Ensure User B sees User A's screenshare.
7012    room_b.read_with(cx_b, |room, _| {
7013        assert_eq!(
7014            room.remote_participants()
7015                .get(&client_a.user_id().unwrap())
7016                .unwrap()
7017                .video_tracks
7018                .len(),
7019            1
7020        );
7021    });
7022}
7023
7024#[gpui::test]
7025async fn test_following_tab_order(
7026    deterministic: Arc<Deterministic>,
7027    cx_a: &mut TestAppContext,
7028    cx_b: &mut TestAppContext,
7029) {
7030    let mut server = TestServer::start(&deterministic).await;
7031    let client_a = server.create_client(cx_a, "user_a").await;
7032    let client_b = server.create_client(cx_b, "user_b").await;
7033    server
7034        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7035        .await;
7036    let active_call_a = cx_a.read(ActiveCall::global);
7037    let active_call_b = cx_b.read(ActiveCall::global);
7038
7039    cx_a.update(editor::init);
7040    cx_b.update(editor::init);
7041
7042    client_a
7043        .fs()
7044        .insert_tree(
7045            "/a",
7046            json!({
7047                "1.txt": "one",
7048                "2.txt": "two",
7049                "3.txt": "three",
7050            }),
7051        )
7052        .await;
7053    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7054    active_call_a
7055        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
7056        .await
7057        .unwrap();
7058
7059    let project_id = active_call_a
7060        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7061        .await
7062        .unwrap();
7063    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7064    active_call_b
7065        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
7066        .await
7067        .unwrap();
7068
7069    let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
7070    let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
7071
7072    let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
7073    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
7074
7075    let client_b_id = project_a.read_with(cx_a, |project, _| {
7076        project.collaborators().values().next().unwrap().peer_id
7077    });
7078
7079    //Open 1, 3 in that order on client A
7080    workspace_a
7081        .update(cx_a, |workspace, cx| {
7082            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
7083        })
7084        .await
7085        .unwrap();
7086    workspace_a
7087        .update(cx_a, |workspace, cx| {
7088            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
7089        })
7090        .await
7091        .unwrap();
7092
7093    let pane_paths = |pane: &ViewHandle<workspace::Pane>, cx: &mut TestAppContext| {
7094        pane.update(cx, |pane, cx| {
7095            pane.items()
7096                .map(|item| {
7097                    item.project_path(cx)
7098                        .unwrap()
7099                        .path
7100                        .to_str()
7101                        .unwrap()
7102                        .to_owned()
7103                })
7104                .collect::<Vec<_>>()
7105        })
7106    };
7107
7108    //Verify that the tabs opened in the order we expect
7109    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt"]);
7110
7111    //Follow client B as client A
7112    workspace_a
7113        .update(cx_a, |workspace, cx| {
7114            workspace.toggle_follow(client_b_id, cx).unwrap()
7115        })
7116        .await
7117        .unwrap();
7118
7119    //Open just 2 on client B
7120    workspace_b
7121        .update(cx_b, |workspace, cx| {
7122            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
7123        })
7124        .await
7125        .unwrap();
7126    deterministic.run_until_parked();
7127
7128    // Verify that newly opened followed file is at the end
7129    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt", "2.txt"]);
7130
7131    //Open just 1 on client B
7132    workspace_b
7133        .update(cx_b, |workspace, cx| {
7134            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
7135        })
7136        .await
7137        .unwrap();
7138    assert_eq!(&pane_paths(&pane_b, cx_b), &["2.txt", "1.txt"]);
7139    deterministic.run_until_parked();
7140
7141    // Verify that following into 1 did not reorder
7142    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt", "2.txt"]);
7143}
7144
7145#[gpui::test(iterations = 10)]
7146async fn test_peers_following_each_other(
7147    deterministic: Arc<Deterministic>,
7148    cx_a: &mut TestAppContext,
7149    cx_b: &mut TestAppContext,
7150) {
7151    deterministic.forbid_parking();
7152    let mut server = TestServer::start(&deterministic).await;
7153    let client_a = server.create_client(cx_a, "user_a").await;
7154    let client_b = server.create_client(cx_b, "user_b").await;
7155    server
7156        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7157        .await;
7158    let active_call_a = cx_a.read(ActiveCall::global);
7159    let active_call_b = cx_b.read(ActiveCall::global);
7160
7161    cx_a.update(editor::init);
7162    cx_b.update(editor::init);
7163
7164    // Client A shares a project.
7165    client_a
7166        .fs()
7167        .insert_tree(
7168            "/a",
7169            json!({
7170                "1.txt": "one",
7171                "2.txt": "two",
7172                "3.txt": "three",
7173                "4.txt": "four",
7174            }),
7175        )
7176        .await;
7177    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7178    active_call_a
7179        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
7180        .await
7181        .unwrap();
7182    let project_id = active_call_a
7183        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7184        .await
7185        .unwrap();
7186
7187    // Client B joins the project.
7188    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7189    active_call_b
7190        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
7191        .await
7192        .unwrap();
7193
7194    // Client A opens some editors.
7195    let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
7196    let pane_a1 = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
7197    let _editor_a1 = workspace_a
7198        .update(cx_a, |workspace, cx| {
7199            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
7200        })
7201        .await
7202        .unwrap()
7203        .downcast::<Editor>()
7204        .unwrap();
7205
7206    // Client B opens an editor.
7207    let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
7208    let pane_b1 = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
7209    let _editor_b1 = workspace_b
7210        .update(cx_b, |workspace, cx| {
7211            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
7212        })
7213        .await
7214        .unwrap()
7215        .downcast::<Editor>()
7216        .unwrap();
7217
7218    // Clients A and B follow each other in split panes
7219    workspace_a.update(cx_a, |workspace, cx| {
7220        workspace.split_and_clone(workspace.active_pane().clone(), SplitDirection::Right, cx);
7221    });
7222    workspace_a
7223        .update(cx_a, |workspace, cx| {
7224            assert_ne!(*workspace.active_pane(), pane_a1);
7225            let leader_id = *project_a.read(cx).collaborators().keys().next().unwrap();
7226            workspace.toggle_follow(leader_id, cx).unwrap()
7227        })
7228        .await
7229        .unwrap();
7230    workspace_b.update(cx_b, |workspace, cx| {
7231        workspace.split_and_clone(workspace.active_pane().clone(), SplitDirection::Right, cx);
7232    });
7233    workspace_b
7234        .update(cx_b, |workspace, cx| {
7235            assert_ne!(*workspace.active_pane(), pane_b1);
7236            let leader_id = *project_b.read(cx).collaborators().keys().next().unwrap();
7237            workspace.toggle_follow(leader_id, cx).unwrap()
7238        })
7239        .await
7240        .unwrap();
7241
7242    workspace_a.update(cx_a, |workspace, cx| {
7243        workspace.activate_next_pane(cx);
7244    });
7245    // Wait for focus effects to be fully flushed
7246    workspace_a.update(cx_a, |workspace, _| {
7247        assert_eq!(*workspace.active_pane(), pane_a1);
7248    });
7249
7250    workspace_a
7251        .update(cx_a, |workspace, cx| {
7252            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
7253        })
7254        .await
7255        .unwrap();
7256    workspace_b.update(cx_b, |workspace, cx| {
7257        workspace.activate_next_pane(cx);
7258    });
7259
7260    workspace_b
7261        .update(cx_b, |workspace, cx| {
7262            assert_eq!(*workspace.active_pane(), pane_b1);
7263            workspace.open_path((worktree_id, "4.txt"), None, true, cx)
7264        })
7265        .await
7266        .unwrap();
7267    cx_a.foreground().run_until_parked();
7268
7269    // Ensure leader updates don't change the active pane of followers
7270    workspace_a.read_with(cx_a, |workspace, _| {
7271        assert_eq!(*workspace.active_pane(), pane_a1);
7272    });
7273    workspace_b.read_with(cx_b, |workspace, _| {
7274        assert_eq!(*workspace.active_pane(), pane_b1);
7275    });
7276
7277    // Ensure peers following each other doesn't cause an infinite loop.
7278    assert_eq!(
7279        workspace_a.read_with(cx_a, |workspace, cx| workspace
7280            .active_item(cx)
7281            .unwrap()
7282            .project_path(cx)),
7283        Some((worktree_id, "3.txt").into())
7284    );
7285    workspace_a.update(cx_a, |workspace, cx| {
7286        assert_eq!(
7287            workspace.active_item(cx).unwrap().project_path(cx),
7288            Some((worktree_id, "3.txt").into())
7289        );
7290        workspace.activate_next_pane(cx);
7291    });
7292
7293    workspace_a.update(cx_a, |workspace, cx| {
7294        assert_eq!(
7295            workspace.active_item(cx).unwrap().project_path(cx),
7296            Some((worktree_id, "4.txt").into())
7297        );
7298    });
7299
7300    workspace_b.update(cx_b, |workspace, cx| {
7301        assert_eq!(
7302            workspace.active_item(cx).unwrap().project_path(cx),
7303            Some((worktree_id, "4.txt").into())
7304        );
7305        workspace.activate_next_pane(cx);
7306    });
7307
7308    workspace_b.update(cx_b, |workspace, cx| {
7309        assert_eq!(
7310            workspace.active_item(cx).unwrap().project_path(cx),
7311            Some((worktree_id, "3.txt").into())
7312        );
7313    });
7314}
7315
7316#[gpui::test(iterations = 10)]
7317async fn test_auto_unfollowing(
7318    deterministic: Arc<Deterministic>,
7319    cx_a: &mut TestAppContext,
7320    cx_b: &mut TestAppContext,
7321) {
7322    deterministic.forbid_parking();
7323
7324    // 2 clients connect to a server.
7325    let mut server = TestServer::start(&deterministic).await;
7326    let client_a = server.create_client(cx_a, "user_a").await;
7327    let client_b = server.create_client(cx_b, "user_b").await;
7328    server
7329        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7330        .await;
7331    let active_call_a = cx_a.read(ActiveCall::global);
7332    let active_call_b = cx_b.read(ActiveCall::global);
7333
7334    cx_a.update(editor::init);
7335    cx_b.update(editor::init);
7336
7337    // Client A shares a project.
7338    client_a
7339        .fs()
7340        .insert_tree(
7341            "/a",
7342            json!({
7343                "1.txt": "one",
7344                "2.txt": "two",
7345                "3.txt": "three",
7346            }),
7347        )
7348        .await;
7349    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7350    active_call_a
7351        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
7352        .await
7353        .unwrap();
7354
7355    let project_id = active_call_a
7356        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7357        .await
7358        .unwrap();
7359    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7360    active_call_b
7361        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
7362        .await
7363        .unwrap();
7364
7365    // Client A opens some editors.
7366    let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
7367    let _editor_a1 = workspace_a
7368        .update(cx_a, |workspace, cx| {
7369            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
7370        })
7371        .await
7372        .unwrap()
7373        .downcast::<Editor>()
7374        .unwrap();
7375
7376    // Client B starts following client A.
7377    let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
7378    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
7379    let leader_id = project_b.read_with(cx_b, |project, _| {
7380        project.collaborators().values().next().unwrap().peer_id
7381    });
7382    workspace_b
7383        .update(cx_b, |workspace, cx| {
7384            workspace.toggle_follow(leader_id, cx).unwrap()
7385        })
7386        .await
7387        .unwrap();
7388    assert_eq!(
7389        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7390        Some(leader_id)
7391    );
7392    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
7393        workspace
7394            .active_item(cx)
7395            .unwrap()
7396            .downcast::<Editor>()
7397            .unwrap()
7398    });
7399
7400    // When client B moves, it automatically stops following client A.
7401    editor_b2.update(cx_b, |editor, cx| editor.move_right(&editor::MoveRight, cx));
7402    assert_eq!(
7403        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7404        None
7405    );
7406
7407    workspace_b
7408        .update(cx_b, |workspace, cx| {
7409            workspace.toggle_follow(leader_id, cx).unwrap()
7410        })
7411        .await
7412        .unwrap();
7413    assert_eq!(
7414        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7415        Some(leader_id)
7416    );
7417
7418    // When client B edits, it automatically stops following client A.
7419    editor_b2.update(cx_b, |editor, cx| editor.insert("X", cx));
7420    assert_eq!(
7421        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7422        None
7423    );
7424
7425    workspace_b
7426        .update(cx_b, |workspace, cx| {
7427            workspace.toggle_follow(leader_id, cx).unwrap()
7428        })
7429        .await
7430        .unwrap();
7431    assert_eq!(
7432        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7433        Some(leader_id)
7434    );
7435
7436    // When client B scrolls, it automatically stops following client A.
7437    editor_b2.update(cx_b, |editor, cx| {
7438        editor.set_scroll_position(vec2f(0., 3.), cx)
7439    });
7440    assert_eq!(
7441        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7442        None
7443    );
7444
7445    workspace_b
7446        .update(cx_b, |workspace, cx| {
7447            workspace.toggle_follow(leader_id, cx).unwrap()
7448        })
7449        .await
7450        .unwrap();
7451    assert_eq!(
7452        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7453        Some(leader_id)
7454    );
7455
7456    // When client B activates a different pane, it continues following client A in the original pane.
7457    workspace_b.update(cx_b, |workspace, cx| {
7458        workspace.split_and_clone(pane_b.clone(), SplitDirection::Right, cx)
7459    });
7460    assert_eq!(
7461        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7462        Some(leader_id)
7463    );
7464
7465    workspace_b.update(cx_b, |workspace, cx| workspace.activate_next_pane(cx));
7466    assert_eq!(
7467        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7468        Some(leader_id)
7469    );
7470
7471    // When client B activates a different item in the original pane, it automatically stops following client A.
7472    workspace_b
7473        .update(cx_b, |workspace, cx| {
7474            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
7475        })
7476        .await
7477        .unwrap();
7478    assert_eq!(
7479        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7480        None
7481    );
7482}
7483
7484#[gpui::test(iterations = 10)]
7485async fn test_peers_simultaneously_following_each_other(
7486    deterministic: Arc<Deterministic>,
7487    cx_a: &mut TestAppContext,
7488    cx_b: &mut TestAppContext,
7489) {
7490    deterministic.forbid_parking();
7491
7492    let mut server = TestServer::start(&deterministic).await;
7493    let client_a = server.create_client(cx_a, "user_a").await;
7494    let client_b = server.create_client(cx_b, "user_b").await;
7495    server
7496        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7497        .await;
7498    let active_call_a = cx_a.read(ActiveCall::global);
7499
7500    cx_a.update(editor::init);
7501    cx_b.update(editor::init);
7502
7503    client_a.fs().insert_tree("/a", json!({})).await;
7504    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
7505    let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
7506    let project_id = active_call_a
7507        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7508        .await
7509        .unwrap();
7510
7511    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7512    let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
7513
7514    deterministic.run_until_parked();
7515    let client_a_id = project_b.read_with(cx_b, |project, _| {
7516        project.collaborators().values().next().unwrap().peer_id
7517    });
7518    let client_b_id = project_a.read_with(cx_a, |project, _| {
7519        project.collaborators().values().next().unwrap().peer_id
7520    });
7521
7522    let a_follow_b = workspace_a.update(cx_a, |workspace, cx| {
7523        workspace.toggle_follow(client_b_id, cx).unwrap()
7524    });
7525    let b_follow_a = workspace_b.update(cx_b, |workspace, cx| {
7526        workspace.toggle_follow(client_a_id, cx).unwrap()
7527    });
7528
7529    futures::try_join!(a_follow_b, b_follow_a).unwrap();
7530    workspace_a.read_with(cx_a, |workspace, _| {
7531        assert_eq!(
7532            workspace.leader_for_pane(workspace.active_pane()),
7533            Some(client_b_id)
7534        );
7535    });
7536    workspace_b.read_with(cx_b, |workspace, _| {
7537        assert_eq!(
7538            workspace.leader_for_pane(workspace.active_pane()),
7539            Some(client_a_id)
7540        );
7541    });
7542}
7543
7544#[gpui::test(iterations = 10)]
7545async fn test_on_input_format_from_host_to_guest(
7546    deterministic: Arc<Deterministic>,
7547    cx_a: &mut TestAppContext,
7548    cx_b: &mut TestAppContext,
7549) {
7550    deterministic.forbid_parking();
7551    let mut server = TestServer::start(&deterministic).await;
7552    let client_a = server.create_client(cx_a, "user_a").await;
7553    let client_b = server.create_client(cx_b, "user_b").await;
7554    server
7555        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7556        .await;
7557    let active_call_a = cx_a.read(ActiveCall::global);
7558
7559    // Set up a fake language server.
7560    let mut language = Language::new(
7561        LanguageConfig {
7562            name: "Rust".into(),
7563            path_suffixes: vec!["rs".to_string()],
7564            ..Default::default()
7565        },
7566        Some(tree_sitter_rust::language()),
7567    );
7568    let mut fake_language_servers = language
7569        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7570            capabilities: lsp::ServerCapabilities {
7571                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
7572                    first_trigger_character: ":".to_string(),
7573                    more_trigger_character: Some(vec![">".to_string()]),
7574                }),
7575                ..Default::default()
7576            },
7577            ..Default::default()
7578        }))
7579        .await;
7580    client_a.language_registry().add(Arc::new(language));
7581
7582    client_a
7583        .fs()
7584        .insert_tree(
7585            "/a",
7586            json!({
7587                "main.rs": "fn main() { a }",
7588                "other.rs": "// Test file",
7589            }),
7590        )
7591        .await;
7592    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7593    let project_id = active_call_a
7594        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7595        .await
7596        .unwrap();
7597    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7598
7599    // Open a file in an editor as the host.
7600    let buffer_a = project_a
7601        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
7602        .await
7603        .unwrap();
7604    let window_a = cx_a.add_window(|_| EmptyView);
7605    let editor_a = window_a.add_view(cx_a, |cx| {
7606        Editor::for_buffer(buffer_a, Some(project_a.clone()), cx)
7607    });
7608
7609    let fake_language_server = fake_language_servers.next().await.unwrap();
7610    cx_b.foreground().run_until_parked();
7611
7612    // Receive an OnTypeFormatting request as the host's language server.
7613    // Return some formattings from the host's language server.
7614    fake_language_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(
7615        |params, _| async move {
7616            assert_eq!(
7617                params.text_document_position.text_document.uri,
7618                lsp::Url::from_file_path("/a/main.rs").unwrap(),
7619            );
7620            assert_eq!(
7621                params.text_document_position.position,
7622                lsp::Position::new(0, 14),
7623            );
7624
7625            Ok(Some(vec![lsp::TextEdit {
7626                new_text: "~<".to_string(),
7627                range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
7628            }]))
7629        },
7630    );
7631
7632    // Open the buffer on the guest and see that the formattings worked
7633    let buffer_b = project_b
7634        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
7635        .await
7636        .unwrap();
7637
7638    // Type a on type formatting trigger character as the guest.
7639    editor_a.update(cx_a, |editor, cx| {
7640        cx.focus(&editor_a);
7641        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
7642        editor.handle_input(">", cx);
7643    });
7644
7645    cx_b.foreground().run_until_parked();
7646
7647    buffer_b.read_with(cx_b, |buffer, _| {
7648        assert_eq!(buffer.text(), "fn main() { a>~< }")
7649    });
7650
7651    // Undo should remove LSP edits first
7652    editor_a.update(cx_a, |editor, cx| {
7653        assert_eq!(editor.text(cx), "fn main() { a>~< }");
7654        editor.undo(&Undo, cx);
7655        assert_eq!(editor.text(cx), "fn main() { a> }");
7656    });
7657    cx_b.foreground().run_until_parked();
7658    buffer_b.read_with(cx_b, |buffer, _| {
7659        assert_eq!(buffer.text(), "fn main() { a> }")
7660    });
7661
7662    editor_a.update(cx_a, |editor, cx| {
7663        assert_eq!(editor.text(cx), "fn main() { a> }");
7664        editor.undo(&Undo, cx);
7665        assert_eq!(editor.text(cx), "fn main() { a }");
7666    });
7667    cx_b.foreground().run_until_parked();
7668    buffer_b.read_with(cx_b, |buffer, _| {
7669        assert_eq!(buffer.text(), "fn main() { a }")
7670    });
7671}
7672
7673#[gpui::test(iterations = 10)]
7674async fn test_on_input_format_from_guest_to_host(
7675    deterministic: Arc<Deterministic>,
7676    cx_a: &mut TestAppContext,
7677    cx_b: &mut TestAppContext,
7678) {
7679    deterministic.forbid_parking();
7680    let mut server = TestServer::start(&deterministic).await;
7681    let client_a = server.create_client(cx_a, "user_a").await;
7682    let client_b = server.create_client(cx_b, "user_b").await;
7683    server
7684        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7685        .await;
7686    let active_call_a = cx_a.read(ActiveCall::global);
7687
7688    // Set up a fake language server.
7689    let mut language = Language::new(
7690        LanguageConfig {
7691            name: "Rust".into(),
7692            path_suffixes: vec!["rs".to_string()],
7693            ..Default::default()
7694        },
7695        Some(tree_sitter_rust::language()),
7696    );
7697    let mut fake_language_servers = language
7698        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7699            capabilities: lsp::ServerCapabilities {
7700                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
7701                    first_trigger_character: ":".to_string(),
7702                    more_trigger_character: Some(vec![">".to_string()]),
7703                }),
7704                ..Default::default()
7705            },
7706            ..Default::default()
7707        }))
7708        .await;
7709    client_a.language_registry().add(Arc::new(language));
7710
7711    client_a
7712        .fs()
7713        .insert_tree(
7714            "/a",
7715            json!({
7716                "main.rs": "fn main() { a }",
7717                "other.rs": "// Test file",
7718            }),
7719        )
7720        .await;
7721    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7722    let project_id = active_call_a
7723        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7724        .await
7725        .unwrap();
7726    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7727
7728    // Open a file in an editor as the guest.
7729    let buffer_b = project_b
7730        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
7731        .await
7732        .unwrap();
7733    let window_b = cx_b.add_window(|_| EmptyView);
7734    let editor_b = window_b.add_view(cx_b, |cx| {
7735        Editor::for_buffer(buffer_b, Some(project_b.clone()), cx)
7736    });
7737
7738    let fake_language_server = fake_language_servers.next().await.unwrap();
7739    cx_a.foreground().run_until_parked();
7740    // Type a on type formatting trigger character as the guest.
7741    editor_b.update(cx_b, |editor, cx| {
7742        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
7743        editor.handle_input(":", cx);
7744        cx.focus(&editor_b);
7745    });
7746
7747    // Receive an OnTypeFormatting request as the host's language server.
7748    // Return some formattings from the host's language server.
7749    cx_a.foreground().start_waiting();
7750    fake_language_server
7751        .handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
7752            assert_eq!(
7753                params.text_document_position.text_document.uri,
7754                lsp::Url::from_file_path("/a/main.rs").unwrap(),
7755            );
7756            assert_eq!(
7757                params.text_document_position.position,
7758                lsp::Position::new(0, 14),
7759            );
7760
7761            Ok(Some(vec![lsp::TextEdit {
7762                new_text: "~:".to_string(),
7763                range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
7764            }]))
7765        })
7766        .next()
7767        .await
7768        .unwrap();
7769    cx_a.foreground().finish_waiting();
7770
7771    // Open the buffer on the host and see that the formattings worked
7772    let buffer_a = project_a
7773        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
7774        .await
7775        .unwrap();
7776    cx_a.foreground().run_until_parked();
7777    buffer_a.read_with(cx_a, |buffer, _| {
7778        assert_eq!(buffer.text(), "fn main() { a:~: }")
7779    });
7780
7781    // Undo should remove LSP edits first
7782    editor_b.update(cx_b, |editor, cx| {
7783        assert_eq!(editor.text(cx), "fn main() { a:~: }");
7784        editor.undo(&Undo, cx);
7785        assert_eq!(editor.text(cx), "fn main() { a: }");
7786    });
7787    cx_a.foreground().run_until_parked();
7788    buffer_a.read_with(cx_a, |buffer, _| {
7789        assert_eq!(buffer.text(), "fn main() { a: }")
7790    });
7791
7792    editor_b.update(cx_b, |editor, cx| {
7793        assert_eq!(editor.text(cx), "fn main() { a: }");
7794        editor.undo(&Undo, cx);
7795        assert_eq!(editor.text(cx), "fn main() { a }");
7796    });
7797    cx_a.foreground().run_until_parked();
7798    buffer_a.read_with(cx_a, |buffer, _| {
7799        assert_eq!(buffer.text(), "fn main() { a }")
7800    });
7801}
7802
7803#[gpui::test(iterations = 10)]
7804async fn test_mutual_editor_inlay_hint_cache_update(
7805    deterministic: Arc<Deterministic>,
7806    cx_a: &mut TestAppContext,
7807    cx_b: &mut TestAppContext,
7808) {
7809    deterministic.forbid_parking();
7810    let mut server = TestServer::start(&deterministic).await;
7811    let client_a = server.create_client(cx_a, "user_a").await;
7812    let client_b = server.create_client(cx_b, "user_b").await;
7813    server
7814        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7815        .await;
7816    let active_call_a = cx_a.read(ActiveCall::global);
7817    let active_call_b = cx_b.read(ActiveCall::global);
7818
7819    cx_a.update(editor::init);
7820    cx_b.update(editor::init);
7821
7822    cx_a.update(|cx| {
7823        cx.update_global(|store: &mut SettingsStore, cx| {
7824            store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
7825                settings.defaults.inlay_hints = Some(InlayHintSettings {
7826                    enabled: true,
7827                    show_type_hints: true,
7828                    show_parameter_hints: false,
7829                    show_other_hints: true,
7830                })
7831            });
7832        });
7833    });
7834    cx_b.update(|cx| {
7835        cx.update_global(|store: &mut SettingsStore, cx| {
7836            store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
7837                settings.defaults.inlay_hints = Some(InlayHintSettings {
7838                    enabled: true,
7839                    show_type_hints: true,
7840                    show_parameter_hints: false,
7841                    show_other_hints: true,
7842                })
7843            });
7844        });
7845    });
7846
7847    let mut language = Language::new(
7848        LanguageConfig {
7849            name: "Rust".into(),
7850            path_suffixes: vec!["rs".to_string()],
7851            ..Default::default()
7852        },
7853        Some(tree_sitter_rust::language()),
7854    );
7855    let mut fake_language_servers = language
7856        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7857            capabilities: lsp::ServerCapabilities {
7858                inlay_hint_provider: Some(lsp::OneOf::Left(true)),
7859                ..Default::default()
7860            },
7861            ..Default::default()
7862        }))
7863        .await;
7864    let language = Arc::new(language);
7865    client_a.language_registry().add(Arc::clone(&language));
7866    client_b.language_registry().add(language);
7867
7868    // Client A opens a project.
7869    client_a
7870        .fs()
7871        .insert_tree(
7872            "/a",
7873            json!({
7874                "main.rs": "fn main() { a } // and some long comment to ensure inlay hints are not trimmed out",
7875                "other.rs": "// Test file",
7876            }),
7877        )
7878        .await;
7879    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7880    active_call_a
7881        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
7882        .await
7883        .unwrap();
7884    let project_id = active_call_a
7885        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7886        .await
7887        .unwrap();
7888
7889    // Client B joins the project
7890    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7891    active_call_b
7892        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
7893        .await
7894        .unwrap();
7895
7896    let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
7897    cx_a.foreground().start_waiting();
7898
7899    // The host opens a rust file.
7900    let _buffer_a = project_a
7901        .update(cx_a, |project, cx| {
7902            project.open_local_buffer("/a/main.rs", cx)
7903        })
7904        .await
7905        .unwrap();
7906    let fake_language_server = fake_language_servers.next().await.unwrap();
7907    let editor_a = workspace_a
7908        .update(cx_a, |workspace, cx| {
7909            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
7910        })
7911        .await
7912        .unwrap()
7913        .downcast::<Editor>()
7914        .unwrap();
7915
7916    // Set up the language server to return an additional inlay hint on each request.
7917    let edits_made = Arc::new(AtomicUsize::new(0));
7918    let closure_edits_made = Arc::clone(&edits_made);
7919    fake_language_server
7920        .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
7921            let task_edits_made = Arc::clone(&closure_edits_made);
7922            async move {
7923                assert_eq!(
7924                    params.text_document.uri,
7925                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
7926                );
7927                let edits_made = task_edits_made.load(atomic::Ordering::Acquire);
7928                Ok(Some(vec![lsp::InlayHint {
7929                    position: lsp::Position::new(0, edits_made as u32),
7930                    label: lsp::InlayHintLabel::String(edits_made.to_string()),
7931                    kind: None,
7932                    text_edits: None,
7933                    tooltip: None,
7934                    padding_left: None,
7935                    padding_right: None,
7936                    data: None,
7937                }]))
7938            }
7939        })
7940        .next()
7941        .await
7942        .unwrap();
7943
7944    deterministic.run_until_parked();
7945
7946    let initial_edit = edits_made.load(atomic::Ordering::Acquire);
7947    editor_a.update(cx_a, |editor, _| {
7948        assert_eq!(
7949            vec![initial_edit.to_string()],
7950            extract_hint_labels(editor),
7951            "Host should get its first hints when opens an editor"
7952        );
7953        let inlay_cache = editor.inlay_hint_cache();
7954        assert_eq!(
7955            inlay_cache.version(),
7956            1,
7957            "Host editor update the cache version after every cache/view change",
7958        );
7959    });
7960    let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
7961    let editor_b = workspace_b
7962        .update(cx_b, |workspace, cx| {
7963            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
7964        })
7965        .await
7966        .unwrap()
7967        .downcast::<Editor>()
7968        .unwrap();
7969
7970    deterministic.run_until_parked();
7971    editor_b.update(cx_b, |editor, _| {
7972        assert_eq!(
7973            vec![initial_edit.to_string()],
7974            extract_hint_labels(editor),
7975            "Client should get its first hints when opens an editor"
7976        );
7977        let inlay_cache = editor.inlay_hint_cache();
7978        assert_eq!(
7979            inlay_cache.version(),
7980            1,
7981            "Guest editor update the cache version after every cache/view change"
7982        );
7983    });
7984
7985    let after_client_edit = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
7986    editor_b.update(cx_b, |editor, cx| {
7987        editor.change_selections(None, cx, |s| s.select_ranges([13..13].clone()));
7988        editor.handle_input(":", cx);
7989        cx.focus(&editor_b);
7990    });
7991
7992    deterministic.run_until_parked();
7993    editor_a.update(cx_a, |editor, _| {
7994        assert_eq!(
7995            vec![after_client_edit.to_string()],
7996            extract_hint_labels(editor),
7997        );
7998        let inlay_cache = editor.inlay_hint_cache();
7999        assert_eq!(inlay_cache.version(), 2);
8000    });
8001    editor_b.update(cx_b, |editor, _| {
8002        assert_eq!(
8003            vec![after_client_edit.to_string()],
8004            extract_hint_labels(editor),
8005        );
8006        let inlay_cache = editor.inlay_hint_cache();
8007        assert_eq!(inlay_cache.version(), 2);
8008    });
8009
8010    let after_host_edit = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
8011    editor_a.update(cx_a, |editor, cx| {
8012        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
8013        editor.handle_input("a change to increment both buffers' versions", cx);
8014        cx.focus(&editor_a);
8015    });
8016
8017    deterministic.run_until_parked();
8018    editor_a.update(cx_a, |editor, _| {
8019        assert_eq!(
8020            vec![after_host_edit.to_string()],
8021            extract_hint_labels(editor),
8022        );
8023        let inlay_cache = editor.inlay_hint_cache();
8024        assert_eq!(inlay_cache.version(), 3);
8025    });
8026    editor_b.update(cx_b, |editor, _| {
8027        assert_eq!(
8028            vec![after_host_edit.to_string()],
8029            extract_hint_labels(editor),
8030        );
8031        let inlay_cache = editor.inlay_hint_cache();
8032        assert_eq!(inlay_cache.version(), 3);
8033    });
8034
8035    let after_special_edit_for_refresh = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
8036    fake_language_server
8037        .request::<lsp::request::InlayHintRefreshRequest>(())
8038        .await
8039        .expect("inlay refresh request failed");
8040
8041    deterministic.run_until_parked();
8042    editor_a.update(cx_a, |editor, _| {
8043        assert_eq!(
8044            vec![after_special_edit_for_refresh.to_string()],
8045            extract_hint_labels(editor),
8046            "Host should react to /refresh LSP request"
8047        );
8048        let inlay_cache = editor.inlay_hint_cache();
8049        assert_eq!(
8050            inlay_cache.version(),
8051            4,
8052            "Host should accepted all edits and bump its cache version every time"
8053        );
8054    });
8055    editor_b.update(cx_b, |editor, _| {
8056        assert_eq!(
8057            vec![after_special_edit_for_refresh.to_string()],
8058            extract_hint_labels(editor),
8059            "Guest should get a /refresh LSP request propagated by host"
8060        );
8061        let inlay_cache = editor.inlay_hint_cache();
8062        assert_eq!(
8063            inlay_cache.version(),
8064            4,
8065            "Guest should accepted all edits and bump its cache version every time"
8066        );
8067    });
8068}
8069
8070#[gpui::test(iterations = 10)]
8071async fn test_inlay_hint_refresh_is_forwarded(
8072    deterministic: Arc<Deterministic>,
8073    cx_a: &mut TestAppContext,
8074    cx_b: &mut TestAppContext,
8075) {
8076    deterministic.forbid_parking();
8077    let mut server = TestServer::start(&deterministic).await;
8078    let client_a = server.create_client(cx_a, "user_a").await;
8079    let client_b = server.create_client(cx_b, "user_b").await;
8080    server
8081        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
8082        .await;
8083    let active_call_a = cx_a.read(ActiveCall::global);
8084    let active_call_b = cx_b.read(ActiveCall::global);
8085
8086    cx_a.update(editor::init);
8087    cx_b.update(editor::init);
8088
8089    cx_a.update(|cx| {
8090        cx.update_global(|store: &mut SettingsStore, cx| {
8091            store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
8092                settings.defaults.inlay_hints = Some(InlayHintSettings {
8093                    enabled: false,
8094                    show_type_hints: false,
8095                    show_parameter_hints: false,
8096                    show_other_hints: false,
8097                })
8098            });
8099        });
8100    });
8101    cx_b.update(|cx| {
8102        cx.update_global(|store: &mut SettingsStore, cx| {
8103            store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
8104                settings.defaults.inlay_hints = Some(InlayHintSettings {
8105                    enabled: true,
8106                    show_type_hints: true,
8107                    show_parameter_hints: true,
8108                    show_other_hints: true,
8109                })
8110            });
8111        });
8112    });
8113
8114    let mut language = Language::new(
8115        LanguageConfig {
8116            name: "Rust".into(),
8117            path_suffixes: vec!["rs".to_string()],
8118            ..Default::default()
8119        },
8120        Some(tree_sitter_rust::language()),
8121    );
8122    let mut fake_language_servers = language
8123        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
8124            capabilities: lsp::ServerCapabilities {
8125                inlay_hint_provider: Some(lsp::OneOf::Left(true)),
8126                ..Default::default()
8127            },
8128            ..Default::default()
8129        }))
8130        .await;
8131    let language = Arc::new(language);
8132    client_a.language_registry().add(Arc::clone(&language));
8133    client_b.language_registry().add(language);
8134
8135    client_a
8136        .fs()
8137        .insert_tree(
8138            "/a",
8139            json!({
8140                "main.rs": "fn main() { a } // and some long comment to ensure inlay hints are not trimmed out",
8141                "other.rs": "// Test file",
8142            }),
8143        )
8144        .await;
8145    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
8146    active_call_a
8147        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
8148        .await
8149        .unwrap();
8150    let project_id = active_call_a
8151        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
8152        .await
8153        .unwrap();
8154
8155    let project_b = client_b.build_remote_project(project_id, cx_b).await;
8156    active_call_b
8157        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
8158        .await
8159        .unwrap();
8160
8161    let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
8162    let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
8163    cx_a.foreground().start_waiting();
8164    cx_b.foreground().start_waiting();
8165
8166    let editor_a = workspace_a
8167        .update(cx_a, |workspace, cx| {
8168            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
8169        })
8170        .await
8171        .unwrap()
8172        .downcast::<Editor>()
8173        .unwrap();
8174
8175    let editor_b = workspace_b
8176        .update(cx_b, |workspace, cx| {
8177            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
8178        })
8179        .await
8180        .unwrap()
8181        .downcast::<Editor>()
8182        .unwrap();
8183
8184    let other_hints = Arc::new(AtomicBool::new(false));
8185    let fake_language_server = fake_language_servers.next().await.unwrap();
8186    let closure_other_hints = Arc::clone(&other_hints);
8187    fake_language_server
8188        .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
8189            let task_other_hints = Arc::clone(&closure_other_hints);
8190            async move {
8191                assert_eq!(
8192                    params.text_document.uri,
8193                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
8194                );
8195                let other_hints = task_other_hints.load(atomic::Ordering::Acquire);
8196                let character = if other_hints { 0 } else { 2 };
8197                let label = if other_hints {
8198                    "other hint"
8199                } else {
8200                    "initial hint"
8201                };
8202                Ok(Some(vec![lsp::InlayHint {
8203                    position: lsp::Position::new(0, character),
8204                    label: lsp::InlayHintLabel::String(label.to_string()),
8205                    kind: None,
8206                    text_edits: None,
8207                    tooltip: None,
8208                    padding_left: None,
8209                    padding_right: None,
8210                    data: None,
8211                }]))
8212            }
8213        })
8214        .next()
8215        .await
8216        .unwrap();
8217    cx_a.foreground().finish_waiting();
8218    cx_b.foreground().finish_waiting();
8219
8220    cx_a.foreground().run_until_parked();
8221    editor_a.update(cx_a, |editor, _| {
8222        assert!(
8223            extract_hint_labels(editor).is_empty(),
8224            "Host should get no hints due to them turned off"
8225        );
8226        let inlay_cache = editor.inlay_hint_cache();
8227        assert_eq!(
8228            inlay_cache.version(),
8229            0,
8230            "Turned off hints should not generate version updates"
8231        );
8232    });
8233
8234    cx_b.foreground().run_until_parked();
8235    editor_b.update(cx_b, |editor, _| {
8236        assert_eq!(
8237            vec!["initial hint".to_string()],
8238            extract_hint_labels(editor),
8239            "Client should get its first hints when opens an editor"
8240        );
8241        let inlay_cache = editor.inlay_hint_cache();
8242        assert_eq!(
8243            inlay_cache.version(),
8244            1,
8245            "Should update cache verison after first hints"
8246        );
8247    });
8248
8249    other_hints.fetch_or(true, atomic::Ordering::Release);
8250    fake_language_server
8251        .request::<lsp::request::InlayHintRefreshRequest>(())
8252        .await
8253        .expect("inlay refresh request failed");
8254    cx_a.foreground().run_until_parked();
8255    editor_a.update(cx_a, |editor, _| {
8256        assert!(
8257            extract_hint_labels(editor).is_empty(),
8258            "Host should get nop hints due to them turned off, even after the /refresh"
8259        );
8260        let inlay_cache = editor.inlay_hint_cache();
8261        assert_eq!(
8262            inlay_cache.version(),
8263            0,
8264            "Turned off hints should not generate version updates, again"
8265        );
8266    });
8267
8268    cx_b.foreground().run_until_parked();
8269    editor_b.update(cx_b, |editor, _| {
8270        assert_eq!(
8271            vec!["other hint".to_string()],
8272            extract_hint_labels(editor),
8273            "Guest should get a /refresh LSP request propagated by host despite host hints are off"
8274        );
8275        let inlay_cache = editor.inlay_hint_cache();
8276        assert_eq!(
8277            inlay_cache.version(),
8278            2,
8279            "Guest should accepted all edits and bump its cache version every time"
8280        );
8281    });
8282}
8283
8284fn extract_hint_labels(editor: &Editor) -> Vec<String> {
8285    let mut labels = Vec::new();
8286    for hint in editor.inlay_hint_cache().hints() {
8287        match hint.label {
8288            project::InlayHintLabel::String(s) => labels.push(s),
8289            _ => unreachable!(),
8290        }
8291    }
8292    labels
8293}