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    let project_id = active_call_a
3150        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3151        .await
3152        .unwrap();
3153
3154    // As client B, join that project and observe the local settings.
3155    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3156    let worktree_b = project_b.read_with(cx_b, |project, cx| project.worktrees(cx).next().unwrap());
3157    deterministic.run_until_parked();
3158    cx_b.read(|cx| {
3159        let store = cx.global::<SettingsStore>();
3160        assert_eq!(
3161            store.local_settings(worktree_b.id()).collect::<Vec<_>>(),
3162            &[
3163                (Path::new("").into(), r#"{"tab_size":2}"#.to_string()),
3164                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3165            ]
3166        )
3167    });
3168
3169    // As client A, update a settings file. As Client B, see the changed settings.
3170    client_a
3171        .fs()
3172        .insert_file("/dir/.zed/settings.json", r#"{}"#.into())
3173        .await;
3174    deterministic.run_until_parked();
3175    cx_b.read(|cx| {
3176        let store = cx.global::<SettingsStore>();
3177        assert_eq!(
3178            store.local_settings(worktree_b.id()).collect::<Vec<_>>(),
3179            &[
3180                (Path::new("").into(), r#"{}"#.to_string()),
3181                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3182            ]
3183        )
3184    });
3185
3186    // As client A, create and remove some settings files. As client B, see the changed settings.
3187    client_a
3188        .fs()
3189        .remove_file("/dir/.zed/settings.json".as_ref(), Default::default())
3190        .await
3191        .unwrap();
3192    client_a
3193        .fs()
3194        .create_dir("/dir/b/.zed".as_ref())
3195        .await
3196        .unwrap();
3197    client_a
3198        .fs()
3199        .insert_file("/dir/b/.zed/settings.json", r#"{"tab_size": 4}"#.into())
3200        .await;
3201    deterministic.run_until_parked();
3202    cx_b.read(|cx| {
3203        let store = cx.global::<SettingsStore>();
3204        assert_eq!(
3205            store.local_settings(worktree_b.id()).collect::<Vec<_>>(),
3206            &[
3207                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3208                (Path::new("b").into(), r#"{"tab_size":4}"#.to_string()),
3209            ]
3210        )
3211    });
3212
3213    // As client B, disconnect.
3214    server.forbid_connections();
3215    server.disconnect_client(client_b.peer_id().unwrap());
3216
3217    // As client A, change and remove settings files while client B is disconnected.
3218    client_a
3219        .fs()
3220        .insert_file("/dir/a/.zed/settings.json", r#"{"hard_tabs":true}"#.into())
3221        .await;
3222    client_a
3223        .fs()
3224        .remove_file("/dir/b/.zed/settings.json".as_ref(), Default::default())
3225        .await
3226        .unwrap();
3227    deterministic.run_until_parked();
3228
3229    // As client B, reconnect and see the changed settings.
3230    server.allow_connections();
3231    deterministic.advance_clock(RECEIVE_TIMEOUT);
3232    cx_b.read(|cx| {
3233        let store = cx.global::<SettingsStore>();
3234        assert_eq!(
3235            store.local_settings(worktree_b.id()).collect::<Vec<_>>(),
3236            &[(Path::new("a").into(), r#"{"hard_tabs":true}"#.to_string()),]
3237        )
3238    });
3239}
3240
3241#[gpui::test(iterations = 10)]
3242async fn test_buffer_conflict_after_save(
3243    deterministic: Arc<Deterministic>,
3244    cx_a: &mut TestAppContext,
3245    cx_b: &mut TestAppContext,
3246) {
3247    deterministic.forbid_parking();
3248    let mut server = TestServer::start(&deterministic).await;
3249    let client_a = server.create_client(cx_a, "user_a").await;
3250    let client_b = server.create_client(cx_b, "user_b").await;
3251    server
3252        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3253        .await;
3254    let active_call_a = cx_a.read(ActiveCall::global);
3255
3256    client_a
3257        .fs()
3258        .insert_tree(
3259            "/dir",
3260            json!({
3261                "a.txt": "a-contents",
3262            }),
3263        )
3264        .await;
3265    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3266    let project_id = active_call_a
3267        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3268        .await
3269        .unwrap();
3270    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3271
3272    // Open a buffer as client B
3273    let buffer_b = project_b
3274        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3275        .await
3276        .unwrap();
3277
3278    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "world ")], None, cx));
3279    buffer_b.read_with(cx_b, |buf, _| {
3280        assert!(buf.is_dirty());
3281        assert!(!buf.has_conflict());
3282    });
3283
3284    project_b
3285        .update(cx_b, |project, cx| {
3286            project.save_buffer(buffer_b.clone(), cx)
3287        })
3288        .await
3289        .unwrap();
3290    cx_a.foreground().forbid_parking();
3291    buffer_b.read_with(cx_b, |buffer_b, _| assert!(!buffer_b.is_dirty()));
3292    buffer_b.read_with(cx_b, |buf, _| {
3293        assert!(!buf.has_conflict());
3294    });
3295
3296    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "hello ")], None, cx));
3297    buffer_b.read_with(cx_b, |buf, _| {
3298        assert!(buf.is_dirty());
3299        assert!(!buf.has_conflict());
3300    });
3301}
3302
3303#[gpui::test(iterations = 10)]
3304async fn test_buffer_reloading(
3305    deterministic: Arc<Deterministic>,
3306    cx_a: &mut TestAppContext,
3307    cx_b: &mut TestAppContext,
3308) {
3309    deterministic.forbid_parking();
3310    let mut server = TestServer::start(&deterministic).await;
3311    let client_a = server.create_client(cx_a, "user_a").await;
3312    let client_b = server.create_client(cx_b, "user_b").await;
3313    server
3314        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3315        .await;
3316    let active_call_a = cx_a.read(ActiveCall::global);
3317
3318    client_a
3319        .fs()
3320        .insert_tree(
3321            "/dir",
3322            json!({
3323                "a.txt": "a\nb\nc",
3324            }),
3325        )
3326        .await;
3327    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3328    let project_id = active_call_a
3329        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3330        .await
3331        .unwrap();
3332    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3333
3334    // Open a buffer as client B
3335    let buffer_b = project_b
3336        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3337        .await
3338        .unwrap();
3339    buffer_b.read_with(cx_b, |buf, _| {
3340        assert!(!buf.is_dirty());
3341        assert!(!buf.has_conflict());
3342        assert_eq!(buf.line_ending(), LineEnding::Unix);
3343    });
3344
3345    let new_contents = Rope::from("d\ne\nf");
3346    client_a
3347        .fs()
3348        .save("/dir/a.txt".as_ref(), &new_contents, LineEnding::Windows)
3349        .await
3350        .unwrap();
3351    cx_a.foreground().run_until_parked();
3352    buffer_b.read_with(cx_b, |buf, _| {
3353        assert_eq!(buf.text(), new_contents.to_string());
3354        assert!(!buf.is_dirty());
3355        assert!(!buf.has_conflict());
3356        assert_eq!(buf.line_ending(), LineEnding::Windows);
3357    });
3358}
3359
3360#[gpui::test(iterations = 10)]
3361async fn test_editing_while_guest_opens_buffer(
3362    deterministic: Arc<Deterministic>,
3363    cx_a: &mut TestAppContext,
3364    cx_b: &mut TestAppContext,
3365) {
3366    deterministic.forbid_parking();
3367    let mut server = TestServer::start(&deterministic).await;
3368    let client_a = server.create_client(cx_a, "user_a").await;
3369    let client_b = server.create_client(cx_b, "user_b").await;
3370    server
3371        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3372        .await;
3373    let active_call_a = cx_a.read(ActiveCall::global);
3374
3375    client_a
3376        .fs()
3377        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3378        .await;
3379    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3380    let project_id = active_call_a
3381        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3382        .await
3383        .unwrap();
3384    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3385
3386    // Open a buffer as client A
3387    let buffer_a = project_a
3388        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3389        .await
3390        .unwrap();
3391
3392    // Start opening the same buffer as client B
3393    let buffer_b = cx_b
3394        .background()
3395        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)));
3396
3397    // Edit the buffer as client A while client B is still opening it.
3398    cx_b.background().simulate_random_delay().await;
3399    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "X")], None, cx));
3400    cx_b.background().simulate_random_delay().await;
3401    buffer_a.update(cx_a, |buf, cx| buf.edit([(1..1, "Y")], None, cx));
3402
3403    let text = buffer_a.read_with(cx_a, |buf, _| buf.text());
3404    let buffer_b = buffer_b.await.unwrap();
3405    cx_a.foreground().run_until_parked();
3406    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), text));
3407}
3408
3409#[gpui::test]
3410async fn test_newline_above_or_below_does_not_move_guest_cursor(
3411    deterministic: Arc<Deterministic>,
3412    cx_a: &mut TestAppContext,
3413    cx_b: &mut TestAppContext,
3414) {
3415    deterministic.forbid_parking();
3416    let mut server = TestServer::start(&deterministic).await;
3417    let client_a = server.create_client(cx_a, "user_a").await;
3418    let client_b = server.create_client(cx_b, "user_b").await;
3419    server
3420        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3421        .await;
3422    let active_call_a = cx_a.read(ActiveCall::global);
3423
3424    client_a
3425        .fs()
3426        .insert_tree("/dir", json!({ "a.txt": "Some text\n" }))
3427        .await;
3428    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3429    let project_id = active_call_a
3430        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3431        .await
3432        .unwrap();
3433
3434    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3435
3436    // Open a buffer as client A
3437    let buffer_a = project_a
3438        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3439        .await
3440        .unwrap();
3441    let window_a = cx_a.add_window(|_| EmptyView);
3442    let editor_a = window_a.add_view(cx_a, |cx| Editor::for_buffer(buffer_a, Some(project_a), cx));
3443    let mut editor_cx_a = EditorTestContext {
3444        cx: cx_a,
3445        window: window_a.into(),
3446        editor: editor_a,
3447    };
3448
3449    // Open a buffer as client B
3450    let buffer_b = project_b
3451        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3452        .await
3453        .unwrap();
3454    let window_b = cx_b.add_window(|_| EmptyView);
3455    let editor_b = window_b.add_view(cx_b, |cx| Editor::for_buffer(buffer_b, Some(project_b), cx));
3456    let mut editor_cx_b = EditorTestContext {
3457        cx: cx_b,
3458        window: window_b.into(),
3459        editor: editor_b,
3460    };
3461
3462    // Test newline above
3463    editor_cx_a.set_selections_state(indoc! {"
3464        Some textˇ
3465    "});
3466    editor_cx_b.set_selections_state(indoc! {"
3467        Some textˇ
3468    "});
3469    editor_cx_a.update_editor(|editor, cx| editor.newline_above(&editor::NewlineAbove, cx));
3470    deterministic.run_until_parked();
3471    editor_cx_a.assert_editor_state(indoc! {"
3472        ˇ
3473        Some text
3474    "});
3475    editor_cx_b.assert_editor_state(indoc! {"
3476
3477        Some textˇ
3478    "});
3479
3480    // Test newline below
3481    editor_cx_a.set_selections_state(indoc! {"
3482
3483        Some textˇ
3484    "});
3485    editor_cx_b.set_selections_state(indoc! {"
3486
3487        Some textˇ
3488    "});
3489    editor_cx_a.update_editor(|editor, cx| editor.newline_below(&editor::NewlineBelow, cx));
3490    deterministic.run_until_parked();
3491    editor_cx_a.assert_editor_state(indoc! {"
3492
3493        Some text
3494        ˇ
3495    "});
3496    editor_cx_b.assert_editor_state(indoc! {"
3497
3498        Some textˇ
3499
3500    "});
3501}
3502
3503#[gpui::test(iterations = 10)]
3504async fn test_leaving_worktree_while_opening_buffer(
3505    deterministic: Arc<Deterministic>,
3506    cx_a: &mut TestAppContext,
3507    cx_b: &mut TestAppContext,
3508) {
3509    deterministic.forbid_parking();
3510    let mut server = TestServer::start(&deterministic).await;
3511    let client_a = server.create_client(cx_a, "user_a").await;
3512    let client_b = server.create_client(cx_b, "user_b").await;
3513    server
3514        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3515        .await;
3516    let active_call_a = cx_a.read(ActiveCall::global);
3517
3518    client_a
3519        .fs()
3520        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3521        .await;
3522    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3523    let project_id = active_call_a
3524        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3525        .await
3526        .unwrap();
3527    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3528
3529    // See that a guest has joined as client A.
3530    cx_a.foreground().run_until_parked();
3531    project_a.read_with(cx_a, |p, _| assert_eq!(p.collaborators().len(), 1));
3532
3533    // Begin opening a buffer as client B, but leave the project before the open completes.
3534    let buffer_b = cx_b
3535        .background()
3536        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)));
3537    cx_b.update(|_| drop(project_b));
3538    drop(buffer_b);
3539
3540    // See that the guest has left.
3541    cx_a.foreground().run_until_parked();
3542    project_a.read_with(cx_a, |p, _| assert!(p.collaborators().is_empty()));
3543}
3544
3545#[gpui::test(iterations = 10)]
3546async fn test_canceling_buffer_opening(
3547    deterministic: Arc<Deterministic>,
3548    cx_a: &mut TestAppContext,
3549    cx_b: &mut TestAppContext,
3550) {
3551    deterministic.forbid_parking();
3552
3553    let mut server = TestServer::start(&deterministic).await;
3554    let client_a = server.create_client(cx_a, "user_a").await;
3555    let client_b = server.create_client(cx_b, "user_b").await;
3556    server
3557        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3558        .await;
3559    let active_call_a = cx_a.read(ActiveCall::global);
3560
3561    client_a
3562        .fs()
3563        .insert_tree(
3564            "/dir",
3565            json!({
3566                "a.txt": "abc",
3567            }),
3568        )
3569        .await;
3570    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3571    let project_id = active_call_a
3572        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3573        .await
3574        .unwrap();
3575    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3576
3577    let buffer_a = project_a
3578        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3579        .await
3580        .unwrap();
3581
3582    // Open a buffer as client B but cancel after a random amount of time.
3583    let buffer_b = project_b.update(cx_b, |p, cx| {
3584        p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3585    });
3586    deterministic.simulate_random_delay().await;
3587    drop(buffer_b);
3588
3589    // Try opening the same buffer again as client B, and ensure we can
3590    // still do it despite the cancellation above.
3591    let buffer_b = project_b
3592        .update(cx_b, |p, cx| {
3593            p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3594        })
3595        .await
3596        .unwrap();
3597    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "abc"));
3598}
3599
3600#[gpui::test(iterations = 10)]
3601async fn test_leaving_project(
3602    deterministic: Arc<Deterministic>,
3603    cx_a: &mut TestAppContext,
3604    cx_b: &mut TestAppContext,
3605    cx_c: &mut TestAppContext,
3606) {
3607    deterministic.forbid_parking();
3608    let mut server = TestServer::start(&deterministic).await;
3609    let client_a = server.create_client(cx_a, "user_a").await;
3610    let client_b = server.create_client(cx_b, "user_b").await;
3611    let client_c = server.create_client(cx_c, "user_c").await;
3612    server
3613        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3614        .await;
3615    let active_call_a = cx_a.read(ActiveCall::global);
3616
3617    client_a
3618        .fs()
3619        .insert_tree(
3620            "/a",
3621            json!({
3622                "a.txt": "a-contents",
3623                "b.txt": "b-contents",
3624            }),
3625        )
3626        .await;
3627    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
3628    let project_id = active_call_a
3629        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3630        .await
3631        .unwrap();
3632    let project_b1 = client_b.build_remote_project(project_id, cx_b).await;
3633    let project_c = client_c.build_remote_project(project_id, cx_c).await;
3634
3635    // Client A sees that a guest has joined.
3636    deterministic.run_until_parked();
3637    project_a.read_with(cx_a, |project, _| {
3638        assert_eq!(project.collaborators().len(), 2);
3639    });
3640    project_b1.read_with(cx_b, |project, _| {
3641        assert_eq!(project.collaborators().len(), 2);
3642    });
3643    project_c.read_with(cx_c, |project, _| {
3644        assert_eq!(project.collaborators().len(), 2);
3645    });
3646
3647    // Client B opens a buffer.
3648    let buffer_b1 = project_b1
3649        .update(cx_b, |project, cx| {
3650            let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
3651            project.open_buffer((worktree_id, "a.txt"), cx)
3652        })
3653        .await
3654        .unwrap();
3655    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3656
3657    // Drop client B's project and ensure client A and client C observe client B leaving.
3658    cx_b.update(|_| drop(project_b1));
3659    deterministic.run_until_parked();
3660    project_a.read_with(cx_a, |project, _| {
3661        assert_eq!(project.collaborators().len(), 1);
3662    });
3663    project_c.read_with(cx_c, |project, _| {
3664        assert_eq!(project.collaborators().len(), 1);
3665    });
3666
3667    // Client B re-joins the project and can open buffers as before.
3668    let project_b2 = client_b.build_remote_project(project_id, cx_b).await;
3669    deterministic.run_until_parked();
3670    project_a.read_with(cx_a, |project, _| {
3671        assert_eq!(project.collaborators().len(), 2);
3672    });
3673    project_b2.read_with(cx_b, |project, _| {
3674        assert_eq!(project.collaborators().len(), 2);
3675    });
3676    project_c.read_with(cx_c, |project, _| {
3677        assert_eq!(project.collaborators().len(), 2);
3678    });
3679
3680    let buffer_b2 = project_b2
3681        .update(cx_b, |project, cx| {
3682            let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
3683            project.open_buffer((worktree_id, "a.txt"), cx)
3684        })
3685        .await
3686        .unwrap();
3687    buffer_b2.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3688
3689    // Drop client B's connection and ensure client A and client C observe client B leaving.
3690    client_b.disconnect(&cx_b.to_async());
3691    deterministic.advance_clock(RECONNECT_TIMEOUT);
3692    project_a.read_with(cx_a, |project, _| {
3693        assert_eq!(project.collaborators().len(), 1);
3694    });
3695    project_b2.read_with(cx_b, |project, _| {
3696        assert!(project.is_read_only());
3697    });
3698    project_c.read_with(cx_c, |project, _| {
3699        assert_eq!(project.collaborators().len(), 1);
3700    });
3701
3702    // Client B can't join the project, unless they re-join the room.
3703    cx_b.spawn(|cx| {
3704        Project::remote(
3705            project_id,
3706            client_b.app_state.client.clone(),
3707            client_b.user_store().clone(),
3708            client_b.language_registry().clone(),
3709            FakeFs::new(cx.background()),
3710            cx,
3711        )
3712    })
3713    .await
3714    .unwrap_err();
3715
3716    // Simulate connection loss for client C and ensure client A observes client C leaving the project.
3717    client_c.wait_for_current_user(cx_c).await;
3718    server.forbid_connections();
3719    server.disconnect_client(client_c.peer_id().unwrap());
3720    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
3721    deterministic.run_until_parked();
3722    project_a.read_with(cx_a, |project, _| {
3723        assert_eq!(project.collaborators().len(), 0);
3724    });
3725    project_b2.read_with(cx_b, |project, _| {
3726        assert!(project.is_read_only());
3727    });
3728    project_c.read_with(cx_c, |project, _| {
3729        assert!(project.is_read_only());
3730    });
3731}
3732
3733#[gpui::test(iterations = 10)]
3734async fn test_collaborating_with_diagnostics(
3735    deterministic: Arc<Deterministic>,
3736    cx_a: &mut TestAppContext,
3737    cx_b: &mut TestAppContext,
3738    cx_c: &mut TestAppContext,
3739) {
3740    deterministic.forbid_parking();
3741    let mut server = TestServer::start(&deterministic).await;
3742    let client_a = server.create_client(cx_a, "user_a").await;
3743    let client_b = server.create_client(cx_b, "user_b").await;
3744    let client_c = server.create_client(cx_c, "user_c").await;
3745    server
3746        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3747        .await;
3748    let active_call_a = cx_a.read(ActiveCall::global);
3749
3750    // Set up a fake language server.
3751    let mut language = Language::new(
3752        LanguageConfig {
3753            name: "Rust".into(),
3754            path_suffixes: vec!["rs".to_string()],
3755            ..Default::default()
3756        },
3757        Some(tree_sitter_rust::language()),
3758    );
3759    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
3760    client_a.language_registry().add(Arc::new(language));
3761
3762    // Share a project as client A
3763    client_a
3764        .fs()
3765        .insert_tree(
3766            "/a",
3767            json!({
3768                "a.rs": "let one = two",
3769                "other.rs": "",
3770            }),
3771        )
3772        .await;
3773    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3774
3775    // Cause the language server to start.
3776    let _buffer = project_a
3777        .update(cx_a, |project, cx| {
3778            project.open_buffer(
3779                ProjectPath {
3780                    worktree_id,
3781                    path: Path::new("other.rs").into(),
3782                },
3783                cx,
3784            )
3785        })
3786        .await
3787        .unwrap();
3788
3789    // Simulate a language server reporting errors for a file.
3790    let mut fake_language_server = fake_language_servers.next().await.unwrap();
3791    fake_language_server
3792        .receive_notification::<lsp::notification::DidOpenTextDocument>()
3793        .await;
3794    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3795        lsp::PublishDiagnosticsParams {
3796            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3797            version: None,
3798            diagnostics: vec![lsp::Diagnostic {
3799                severity: Some(lsp::DiagnosticSeverity::WARNING),
3800                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3801                message: "message 0".to_string(),
3802                ..Default::default()
3803            }],
3804        },
3805    );
3806
3807    // Client A shares the project and, simultaneously, the language server
3808    // publishes a diagnostic. This is done to ensure that the server always
3809    // observes the latest diagnostics for a worktree.
3810    let project_id = active_call_a
3811        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3812        .await
3813        .unwrap();
3814    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3815        lsp::PublishDiagnosticsParams {
3816            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3817            version: None,
3818            diagnostics: vec![lsp::Diagnostic {
3819                severity: Some(lsp::DiagnosticSeverity::ERROR),
3820                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3821                message: "message 1".to_string(),
3822                ..Default::default()
3823            }],
3824        },
3825    );
3826
3827    // Join the worktree as client B.
3828    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3829
3830    // Wait for server to see the diagnostics update.
3831    deterministic.run_until_parked();
3832
3833    // Ensure client B observes the new diagnostics.
3834    project_b.read_with(cx_b, |project, cx| {
3835        assert_eq!(
3836            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
3837            &[(
3838                ProjectPath {
3839                    worktree_id,
3840                    path: Arc::from(Path::new("a.rs")),
3841                },
3842                LanguageServerId(0),
3843                DiagnosticSummary {
3844                    error_count: 1,
3845                    warning_count: 0,
3846                    ..Default::default()
3847                },
3848            )]
3849        )
3850    });
3851
3852    // Join project as client C and observe the diagnostics.
3853    let project_c = client_c.build_remote_project(project_id, cx_c).await;
3854    let project_c_diagnostic_summaries =
3855        Rc::new(RefCell::new(project_c.read_with(cx_c, |project, cx| {
3856            project.diagnostic_summaries(cx).collect::<Vec<_>>()
3857        })));
3858    project_c.update(cx_c, |_, cx| {
3859        let summaries = project_c_diagnostic_summaries.clone();
3860        cx.subscribe(&project_c, {
3861            move |p, _, event, cx| {
3862                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
3863                    *summaries.borrow_mut() = p.diagnostic_summaries(cx).collect();
3864                }
3865            }
3866        })
3867        .detach();
3868    });
3869
3870    deterministic.run_until_parked();
3871    assert_eq!(
3872        project_c_diagnostic_summaries.borrow().as_slice(),
3873        &[(
3874            ProjectPath {
3875                worktree_id,
3876                path: Arc::from(Path::new("a.rs")),
3877            },
3878            LanguageServerId(0),
3879            DiagnosticSummary {
3880                error_count: 1,
3881                warning_count: 0,
3882                ..Default::default()
3883            },
3884        )]
3885    );
3886
3887    // Simulate a language server reporting more errors for a file.
3888    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3889        lsp::PublishDiagnosticsParams {
3890            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3891            version: None,
3892            diagnostics: vec![
3893                lsp::Diagnostic {
3894                    severity: Some(lsp::DiagnosticSeverity::ERROR),
3895                    range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3896                    message: "message 1".to_string(),
3897                    ..Default::default()
3898                },
3899                lsp::Diagnostic {
3900                    severity: Some(lsp::DiagnosticSeverity::WARNING),
3901                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 13)),
3902                    message: "message 2".to_string(),
3903                    ..Default::default()
3904                },
3905            ],
3906        },
3907    );
3908
3909    // Clients B and C get the updated summaries
3910    deterministic.run_until_parked();
3911    project_b.read_with(cx_b, |project, cx| {
3912        assert_eq!(
3913            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
3914            [(
3915                ProjectPath {
3916                    worktree_id,
3917                    path: Arc::from(Path::new("a.rs")),
3918                },
3919                LanguageServerId(0),
3920                DiagnosticSummary {
3921                    error_count: 1,
3922                    warning_count: 1,
3923                },
3924            )]
3925        );
3926    });
3927    project_c.read_with(cx_c, |project, cx| {
3928        assert_eq!(
3929            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
3930            [(
3931                ProjectPath {
3932                    worktree_id,
3933                    path: Arc::from(Path::new("a.rs")),
3934                },
3935                LanguageServerId(0),
3936                DiagnosticSummary {
3937                    error_count: 1,
3938                    warning_count: 1,
3939                },
3940            )]
3941        );
3942    });
3943
3944    // Open the file with the errors on client B. They should be present.
3945    let buffer_b = cx_b
3946        .background()
3947        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
3948        .await
3949        .unwrap();
3950
3951    buffer_b.read_with(cx_b, |buffer, _| {
3952        assert_eq!(
3953            buffer
3954                .snapshot()
3955                .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
3956                .collect::<Vec<_>>(),
3957            &[
3958                DiagnosticEntry {
3959                    range: Point::new(0, 4)..Point::new(0, 7),
3960                    diagnostic: Diagnostic {
3961                        group_id: 2,
3962                        message: "message 1".to_string(),
3963                        severity: lsp::DiagnosticSeverity::ERROR,
3964                        is_primary: true,
3965                        ..Default::default()
3966                    }
3967                },
3968                DiagnosticEntry {
3969                    range: Point::new(0, 10)..Point::new(0, 13),
3970                    diagnostic: Diagnostic {
3971                        group_id: 3,
3972                        severity: lsp::DiagnosticSeverity::WARNING,
3973                        message: "message 2".to_string(),
3974                        is_primary: true,
3975                        ..Default::default()
3976                    }
3977                }
3978            ]
3979        );
3980    });
3981
3982    // Simulate a language server reporting no errors for a file.
3983    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3984        lsp::PublishDiagnosticsParams {
3985            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3986            version: None,
3987            diagnostics: vec![],
3988        },
3989    );
3990    deterministic.run_until_parked();
3991    project_a.read_with(cx_a, |project, cx| {
3992        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
3993    });
3994    project_b.read_with(cx_b, |project, cx| {
3995        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
3996    });
3997    project_c.read_with(cx_c, |project, cx| {
3998        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
3999    });
4000}
4001
4002#[gpui::test(iterations = 10)]
4003async fn test_collaborating_with_lsp_progress_updates_and_diagnostics_ordering(
4004    deterministic: Arc<Deterministic>,
4005    cx_a: &mut TestAppContext,
4006    cx_b: &mut TestAppContext,
4007) {
4008    deterministic.forbid_parking();
4009    let mut server = TestServer::start(&deterministic).await;
4010    let client_a = server.create_client(cx_a, "user_a").await;
4011    let client_b = server.create_client(cx_b, "user_b").await;
4012    server
4013        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4014        .await;
4015
4016    // Set up a fake language server.
4017    let mut language = Language::new(
4018        LanguageConfig {
4019            name: "Rust".into(),
4020            path_suffixes: vec!["rs".to_string()],
4021            ..Default::default()
4022        },
4023        Some(tree_sitter_rust::language()),
4024    );
4025    let mut fake_language_servers = language
4026        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4027            disk_based_diagnostics_progress_token: Some("the-disk-based-token".into()),
4028            disk_based_diagnostics_sources: vec!["the-disk-based-diagnostics-source".into()],
4029            ..Default::default()
4030        }))
4031        .await;
4032    client_a.language_registry().add(Arc::new(language));
4033
4034    let file_names = &["one.rs", "two.rs", "three.rs", "four.rs", "five.rs"];
4035    client_a
4036        .fs()
4037        .insert_tree(
4038            "/test",
4039            json!({
4040                "one.rs": "const ONE: usize = 1;",
4041                "two.rs": "const TWO: usize = 2;",
4042                "three.rs": "const THREE: usize = 3;",
4043                "four.rs": "const FOUR: usize = 3;",
4044                "five.rs": "const FIVE: usize = 3;",
4045            }),
4046        )
4047        .await;
4048
4049    let (project_a, worktree_id) = client_a.build_local_project("/test", cx_a).await;
4050
4051    // Share a project as client A
4052    let active_call_a = cx_a.read(ActiveCall::global);
4053    let project_id = active_call_a
4054        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4055        .await
4056        .unwrap();
4057
4058    // Join the project as client B and open all three files.
4059    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4060    let guest_buffers = futures::future::try_join_all(file_names.iter().map(|file_name| {
4061        project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, file_name), cx))
4062    }))
4063    .await
4064    .unwrap();
4065
4066    // Simulate a language server reporting errors for a file.
4067    let fake_language_server = fake_language_servers.next().await.unwrap();
4068    fake_language_server
4069        .request::<lsp::request::WorkDoneProgressCreate>(lsp::WorkDoneProgressCreateParams {
4070            token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4071        })
4072        .await
4073        .unwrap();
4074    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
4075        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4076        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin(
4077            lsp::WorkDoneProgressBegin {
4078                title: "Progress Began".into(),
4079                ..Default::default()
4080            },
4081        )),
4082    });
4083    for file_name in file_names {
4084        fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
4085            lsp::PublishDiagnosticsParams {
4086                uri: lsp::Url::from_file_path(Path::new("/test").join(file_name)).unwrap(),
4087                version: None,
4088                diagnostics: vec![lsp::Diagnostic {
4089                    severity: Some(lsp::DiagnosticSeverity::WARNING),
4090                    source: Some("the-disk-based-diagnostics-source".into()),
4091                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
4092                    message: "message one".to_string(),
4093                    ..Default::default()
4094                }],
4095            },
4096        );
4097    }
4098    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
4099        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4100        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End(
4101            lsp::WorkDoneProgressEnd { message: None },
4102        )),
4103    });
4104
4105    // When the "disk base diagnostics finished" message is received, the buffers'
4106    // diagnostics are expected to be present.
4107    let disk_based_diagnostics_finished = Arc::new(AtomicBool::new(false));
4108    project_b.update(cx_b, {
4109        let project_b = project_b.clone();
4110        let disk_based_diagnostics_finished = disk_based_diagnostics_finished.clone();
4111        move |_, cx| {
4112            cx.subscribe(&project_b, move |_, _, event, cx| {
4113                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
4114                    disk_based_diagnostics_finished.store(true, SeqCst);
4115                    for buffer in &guest_buffers {
4116                        assert_eq!(
4117                            buffer
4118                                .read(cx)
4119                                .snapshot()
4120                                .diagnostics_in_range::<_, usize>(0..5, false)
4121                                .count(),
4122                            1,
4123                            "expected a diagnostic for buffer {:?}",
4124                            buffer.read(cx).file().unwrap().path(),
4125                        );
4126                    }
4127                }
4128            })
4129            .detach();
4130        }
4131    });
4132
4133    deterministic.run_until_parked();
4134    assert!(disk_based_diagnostics_finished.load(SeqCst));
4135}
4136
4137#[gpui::test(iterations = 10)]
4138async fn test_collaborating_with_completion(
4139    deterministic: Arc<Deterministic>,
4140    cx_a: &mut TestAppContext,
4141    cx_b: &mut TestAppContext,
4142) {
4143    deterministic.forbid_parking();
4144    let mut server = TestServer::start(&deterministic).await;
4145    let client_a = server.create_client(cx_a, "user_a").await;
4146    let client_b = server.create_client(cx_b, "user_b").await;
4147    server
4148        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4149        .await;
4150    let active_call_a = cx_a.read(ActiveCall::global);
4151
4152    // Set up a fake language server.
4153    let mut language = Language::new(
4154        LanguageConfig {
4155            name: "Rust".into(),
4156            path_suffixes: vec!["rs".to_string()],
4157            ..Default::default()
4158        },
4159        Some(tree_sitter_rust::language()),
4160    );
4161    let mut fake_language_servers = language
4162        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4163            capabilities: lsp::ServerCapabilities {
4164                completion_provider: Some(lsp::CompletionOptions {
4165                    trigger_characters: Some(vec![".".to_string()]),
4166                    resolve_provider: Some(true),
4167                    ..Default::default()
4168                }),
4169                ..Default::default()
4170            },
4171            ..Default::default()
4172        }))
4173        .await;
4174    client_a.language_registry().add(Arc::new(language));
4175
4176    client_a
4177        .fs()
4178        .insert_tree(
4179            "/a",
4180            json!({
4181                "main.rs": "fn main() { a }",
4182                "other.rs": "",
4183            }),
4184        )
4185        .await;
4186    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
4187    let project_id = active_call_a
4188        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4189        .await
4190        .unwrap();
4191    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4192
4193    // Open a file in an editor as the guest.
4194    let buffer_b = project_b
4195        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
4196        .await
4197        .unwrap();
4198    let window_b = cx_b.add_window(|_| EmptyView);
4199    let editor_b = window_b.add_view(cx_b, |cx| {
4200        Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx)
4201    });
4202
4203    let fake_language_server = fake_language_servers.next().await.unwrap();
4204    cx_a.foreground().run_until_parked();
4205    buffer_b.read_with(cx_b, |buffer, _| {
4206        assert!(!buffer.completion_triggers().is_empty())
4207    });
4208
4209    // Type a completion trigger character as the guest.
4210    editor_b.update(cx_b, |editor, cx| {
4211        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
4212        editor.handle_input(".", cx);
4213        cx.focus(&editor_b);
4214    });
4215
4216    // Receive a completion request as the host's language server.
4217    // Return some completions from the host's language server.
4218    cx_a.foreground().start_waiting();
4219    fake_language_server
4220        .handle_request::<lsp::request::Completion, _, _>(|params, _| async move {
4221            assert_eq!(
4222                params.text_document_position.text_document.uri,
4223                lsp::Url::from_file_path("/a/main.rs").unwrap(),
4224            );
4225            assert_eq!(
4226                params.text_document_position.position,
4227                lsp::Position::new(0, 14),
4228            );
4229
4230            Ok(Some(lsp::CompletionResponse::Array(vec![
4231                lsp::CompletionItem {
4232                    label: "first_method(…)".into(),
4233                    detail: Some("fn(&mut self, B) -> C".into()),
4234                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
4235                        new_text: "first_method($1)".to_string(),
4236                        range: lsp::Range::new(
4237                            lsp::Position::new(0, 14),
4238                            lsp::Position::new(0, 14),
4239                        ),
4240                    })),
4241                    insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
4242                    ..Default::default()
4243                },
4244                lsp::CompletionItem {
4245                    label: "second_method(…)".into(),
4246                    detail: Some("fn(&mut self, C) -> D<E>".into()),
4247                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
4248                        new_text: "second_method()".to_string(),
4249                        range: lsp::Range::new(
4250                            lsp::Position::new(0, 14),
4251                            lsp::Position::new(0, 14),
4252                        ),
4253                    })),
4254                    insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
4255                    ..Default::default()
4256                },
4257            ])))
4258        })
4259        .next()
4260        .await
4261        .unwrap();
4262    cx_a.foreground().finish_waiting();
4263
4264    // Open the buffer on the host.
4265    let buffer_a = project_a
4266        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
4267        .await
4268        .unwrap();
4269    cx_a.foreground().run_until_parked();
4270    buffer_a.read_with(cx_a, |buffer, _| {
4271        assert_eq!(buffer.text(), "fn main() { a. }")
4272    });
4273
4274    // Confirm a completion on the guest.
4275    editor_b.read_with(cx_b, |editor, _| assert!(editor.context_menu_visible()));
4276    editor_b.update(cx_b, |editor, cx| {
4277        editor.confirm_completion(&ConfirmCompletion { item_ix: Some(0) }, cx);
4278        assert_eq!(editor.text(cx), "fn main() { a.first_method() }");
4279    });
4280
4281    // Return a resolved completion from the host's language server.
4282    // The resolved completion has an additional text edit.
4283    fake_language_server.handle_request::<lsp::request::ResolveCompletionItem, _, _>(
4284        |params, _| async move {
4285            assert_eq!(params.label, "first_method(…)");
4286            Ok(lsp::CompletionItem {
4287                label: "first_method(…)".into(),
4288                detail: Some("fn(&mut self, B) -> C".into()),
4289                text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
4290                    new_text: "first_method($1)".to_string(),
4291                    range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
4292                })),
4293                additional_text_edits: Some(vec![lsp::TextEdit {
4294                    new_text: "use d::SomeTrait;\n".to_string(),
4295                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
4296                }]),
4297                insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
4298                ..Default::default()
4299            })
4300        },
4301    );
4302
4303    // The additional edit is applied.
4304    cx_a.foreground().run_until_parked();
4305    buffer_a.read_with(cx_a, |buffer, _| {
4306        assert_eq!(
4307            buffer.text(),
4308            "use d::SomeTrait;\nfn main() { a.first_method() }"
4309        );
4310    });
4311    buffer_b.read_with(cx_b, |buffer, _| {
4312        assert_eq!(
4313            buffer.text(),
4314            "use d::SomeTrait;\nfn main() { a.first_method() }"
4315        );
4316    });
4317}
4318
4319#[gpui::test(iterations = 10)]
4320async fn test_reloading_buffer_manually(
4321    deterministic: Arc<Deterministic>,
4322    cx_a: &mut TestAppContext,
4323    cx_b: &mut TestAppContext,
4324) {
4325    deterministic.forbid_parking();
4326    let mut server = TestServer::start(&deterministic).await;
4327    let client_a = server.create_client(cx_a, "user_a").await;
4328    let client_b = server.create_client(cx_b, "user_b").await;
4329    server
4330        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4331        .await;
4332    let active_call_a = cx_a.read(ActiveCall::global);
4333
4334    client_a
4335        .fs()
4336        .insert_tree("/a", json!({ "a.rs": "let one = 1;" }))
4337        .await;
4338    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
4339    let buffer_a = project_a
4340        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
4341        .await
4342        .unwrap();
4343    let project_id = active_call_a
4344        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4345        .await
4346        .unwrap();
4347
4348    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4349
4350    let buffer_b = cx_b
4351        .background()
4352        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4353        .await
4354        .unwrap();
4355    buffer_b.update(cx_b, |buffer, cx| {
4356        buffer.edit([(4..7, "six")], None, cx);
4357        buffer.edit([(10..11, "6")], None, cx);
4358        assert_eq!(buffer.text(), "let six = 6;");
4359        assert!(buffer.is_dirty());
4360        assert!(!buffer.has_conflict());
4361    });
4362    cx_a.foreground().run_until_parked();
4363    buffer_a.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "let six = 6;"));
4364
4365    client_a
4366        .fs()
4367        .save(
4368            "/a/a.rs".as_ref(),
4369            &Rope::from("let seven = 7;"),
4370            LineEnding::Unix,
4371        )
4372        .await
4373        .unwrap();
4374    cx_a.foreground().run_until_parked();
4375    buffer_a.read_with(cx_a, |buffer, _| assert!(buffer.has_conflict()));
4376    buffer_b.read_with(cx_b, |buffer, _| assert!(buffer.has_conflict()));
4377
4378    project_b
4379        .update(cx_b, |project, cx| {
4380            project.reload_buffers(HashSet::from_iter([buffer_b.clone()]), true, cx)
4381        })
4382        .await
4383        .unwrap();
4384    buffer_a.read_with(cx_a, |buffer, _| {
4385        assert_eq!(buffer.text(), "let seven = 7;");
4386        assert!(!buffer.is_dirty());
4387        assert!(!buffer.has_conflict());
4388    });
4389    buffer_b.read_with(cx_b, |buffer, _| {
4390        assert_eq!(buffer.text(), "let seven = 7;");
4391        assert!(!buffer.is_dirty());
4392        assert!(!buffer.has_conflict());
4393    });
4394
4395    buffer_a.update(cx_a, |buffer, cx| {
4396        // Undoing on the host is a no-op when the reload was initiated by the guest.
4397        buffer.undo(cx);
4398        assert_eq!(buffer.text(), "let seven = 7;");
4399        assert!(!buffer.is_dirty());
4400        assert!(!buffer.has_conflict());
4401    });
4402    buffer_b.update(cx_b, |buffer, cx| {
4403        // Undoing on the guest rolls back the buffer to before it was reloaded but the conflict gets cleared.
4404        buffer.undo(cx);
4405        assert_eq!(buffer.text(), "let six = 6;");
4406        assert!(buffer.is_dirty());
4407        assert!(!buffer.has_conflict());
4408    });
4409}
4410
4411#[gpui::test(iterations = 10)]
4412async fn test_formatting_buffer(
4413    deterministic: Arc<Deterministic>,
4414    cx_a: &mut TestAppContext,
4415    cx_b: &mut TestAppContext,
4416) {
4417    use project::FormatTrigger;
4418
4419    let mut server = TestServer::start(&deterministic).await;
4420    let client_a = server.create_client(cx_a, "user_a").await;
4421    let client_b = server.create_client(cx_b, "user_b").await;
4422    server
4423        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4424        .await;
4425    let active_call_a = cx_a.read(ActiveCall::global);
4426
4427    // Set up a fake language server.
4428    let mut language = Language::new(
4429        LanguageConfig {
4430            name: "Rust".into(),
4431            path_suffixes: vec!["rs".to_string()],
4432            ..Default::default()
4433        },
4434        Some(tree_sitter_rust::language()),
4435    );
4436    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4437    client_a.language_registry().add(Arc::new(language));
4438
4439    // Here we insert a fake tree with a directory that exists on disk. This is needed
4440    // because later we'll invoke a command, which requires passing a working directory
4441    // that points to a valid location on disk.
4442    let directory = env::current_dir().unwrap();
4443    client_a
4444        .fs()
4445        .insert_tree(&directory, json!({ "a.rs": "let one = \"two\"" }))
4446        .await;
4447    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
4448    let project_id = active_call_a
4449        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4450        .await
4451        .unwrap();
4452    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4453
4454    let buffer_b = cx_b
4455        .background()
4456        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4457        .await
4458        .unwrap();
4459
4460    let fake_language_server = fake_language_servers.next().await.unwrap();
4461    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
4462        Ok(Some(vec![
4463            lsp::TextEdit {
4464                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 4)),
4465                new_text: "h".to_string(),
4466            },
4467            lsp::TextEdit {
4468                range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 7)),
4469                new_text: "y".to_string(),
4470            },
4471        ]))
4472    });
4473
4474    project_b
4475        .update(cx_b, |project, cx| {
4476            project.format(
4477                HashSet::from_iter([buffer_b.clone()]),
4478                true,
4479                FormatTrigger::Save,
4480                cx,
4481            )
4482        })
4483        .await
4484        .unwrap();
4485
4486    // The edits from the LSP are applied, and a final newline is added.
4487    assert_eq!(
4488        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4489        "let honey = \"two\"\n"
4490    );
4491
4492    // Ensure buffer can be formatted using an external command. Notice how the
4493    // host's configuration is honored as opposed to using the guest's settings.
4494    cx_a.update(|cx| {
4495        cx.update_global(|store: &mut SettingsStore, cx| {
4496            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4497                file.defaults.formatter = Some(Formatter::External {
4498                    command: "awk".into(),
4499                    arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()].into(),
4500                });
4501            });
4502        });
4503    });
4504    project_b
4505        .update(cx_b, |project, cx| {
4506            project.format(
4507                HashSet::from_iter([buffer_b.clone()]),
4508                true,
4509                FormatTrigger::Save,
4510                cx,
4511            )
4512        })
4513        .await
4514        .unwrap();
4515    assert_eq!(
4516        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4517        format!("let honey = \"{}/a.rs\"\n", directory.to_str().unwrap())
4518    );
4519}
4520
4521#[gpui::test(iterations = 10)]
4522async fn test_definition(
4523    deterministic: Arc<Deterministic>,
4524    cx_a: &mut TestAppContext,
4525    cx_b: &mut TestAppContext,
4526) {
4527    deterministic.forbid_parking();
4528    let mut server = TestServer::start(&deterministic).await;
4529    let client_a = server.create_client(cx_a, "user_a").await;
4530    let client_b = server.create_client(cx_b, "user_b").await;
4531    server
4532        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4533        .await;
4534    let active_call_a = cx_a.read(ActiveCall::global);
4535
4536    // Set up a fake language server.
4537    let mut language = Language::new(
4538        LanguageConfig {
4539            name: "Rust".into(),
4540            path_suffixes: vec!["rs".to_string()],
4541            ..Default::default()
4542        },
4543        Some(tree_sitter_rust::language()),
4544    );
4545    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4546    client_a.language_registry().add(Arc::new(language));
4547
4548    client_a
4549        .fs()
4550        .insert_tree(
4551            "/root",
4552            json!({
4553                "dir-1": {
4554                    "a.rs": "const ONE: usize = b::TWO + b::THREE;",
4555                },
4556                "dir-2": {
4557                    "b.rs": "const TWO: c::T2 = 2;\nconst THREE: usize = 3;",
4558                    "c.rs": "type T2 = usize;",
4559                }
4560            }),
4561        )
4562        .await;
4563    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4564    let project_id = active_call_a
4565        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4566        .await
4567        .unwrap();
4568    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4569
4570    // Open the file on client B.
4571    let buffer_b = cx_b
4572        .background()
4573        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4574        .await
4575        .unwrap();
4576
4577    // Request the definition of a symbol as the guest.
4578    let fake_language_server = fake_language_servers.next().await.unwrap();
4579    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4580        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4581            lsp::Location::new(
4582                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4583                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4584            ),
4585        )))
4586    });
4587
4588    let definitions_1 = project_b
4589        .update(cx_b, |p, cx| p.definition(&buffer_b, 23, cx))
4590        .await
4591        .unwrap();
4592    cx_b.read(|cx| {
4593        assert_eq!(definitions_1.len(), 1);
4594        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4595        let target_buffer = definitions_1[0].target.buffer.read(cx);
4596        assert_eq!(
4597            target_buffer.text(),
4598            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4599        );
4600        assert_eq!(
4601            definitions_1[0].target.range.to_point(target_buffer),
4602            Point::new(0, 6)..Point::new(0, 9)
4603        );
4604    });
4605
4606    // Try getting more definitions for the same buffer, ensuring the buffer gets reused from
4607    // the previous call to `definition`.
4608    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4609        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4610            lsp::Location::new(
4611                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4612                lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)),
4613            ),
4614        )))
4615    });
4616
4617    let definitions_2 = project_b
4618        .update(cx_b, |p, cx| p.definition(&buffer_b, 33, cx))
4619        .await
4620        .unwrap();
4621    cx_b.read(|cx| {
4622        assert_eq!(definitions_2.len(), 1);
4623        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4624        let target_buffer = definitions_2[0].target.buffer.read(cx);
4625        assert_eq!(
4626            target_buffer.text(),
4627            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4628        );
4629        assert_eq!(
4630            definitions_2[0].target.range.to_point(target_buffer),
4631            Point::new(1, 6)..Point::new(1, 11)
4632        );
4633    });
4634    assert_eq!(
4635        definitions_1[0].target.buffer,
4636        definitions_2[0].target.buffer
4637    );
4638
4639    fake_language_server.handle_request::<lsp::request::GotoTypeDefinition, _, _>(
4640        |req, _| async move {
4641            assert_eq!(
4642                req.text_document_position_params.position,
4643                lsp::Position::new(0, 7)
4644            );
4645            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4646                lsp::Location::new(
4647                    lsp::Url::from_file_path("/root/dir-2/c.rs").unwrap(),
4648                    lsp::Range::new(lsp::Position::new(0, 5), lsp::Position::new(0, 7)),
4649                ),
4650            )))
4651        },
4652    );
4653
4654    let type_definitions = project_b
4655        .update(cx_b, |p, cx| p.type_definition(&buffer_b, 7, cx))
4656        .await
4657        .unwrap();
4658    cx_b.read(|cx| {
4659        assert_eq!(type_definitions.len(), 1);
4660        let target_buffer = type_definitions[0].target.buffer.read(cx);
4661        assert_eq!(target_buffer.text(), "type T2 = usize;");
4662        assert_eq!(
4663            type_definitions[0].target.range.to_point(target_buffer),
4664            Point::new(0, 5)..Point::new(0, 7)
4665        );
4666    });
4667}
4668
4669#[gpui::test(iterations = 10)]
4670async fn test_references(
4671    deterministic: Arc<Deterministic>,
4672    cx_a: &mut TestAppContext,
4673    cx_b: &mut TestAppContext,
4674) {
4675    deterministic.forbid_parking();
4676    let mut server = TestServer::start(&deterministic).await;
4677    let client_a = server.create_client(cx_a, "user_a").await;
4678    let client_b = server.create_client(cx_b, "user_b").await;
4679    server
4680        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4681        .await;
4682    let active_call_a = cx_a.read(ActiveCall::global);
4683
4684    // Set up a fake language server.
4685    let mut language = Language::new(
4686        LanguageConfig {
4687            name: "Rust".into(),
4688            path_suffixes: vec!["rs".to_string()],
4689            ..Default::default()
4690        },
4691        Some(tree_sitter_rust::language()),
4692    );
4693    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4694    client_a.language_registry().add(Arc::new(language));
4695
4696    client_a
4697        .fs()
4698        .insert_tree(
4699            "/root",
4700            json!({
4701                "dir-1": {
4702                    "one.rs": "const ONE: usize = 1;",
4703                    "two.rs": "const TWO: usize = one::ONE + one::ONE;",
4704                },
4705                "dir-2": {
4706                    "three.rs": "const THREE: usize = two::TWO + one::ONE;",
4707                }
4708            }),
4709        )
4710        .await;
4711    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4712    let project_id = active_call_a
4713        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4714        .await
4715        .unwrap();
4716    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4717
4718    // Open the file on client B.
4719    let buffer_b = cx_b
4720        .background()
4721        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
4722        .await
4723        .unwrap();
4724
4725    // Request references to a symbol as the guest.
4726    let fake_language_server = fake_language_servers.next().await.unwrap();
4727    fake_language_server.handle_request::<lsp::request::References, _, _>(|params, _| async move {
4728        assert_eq!(
4729            params.text_document_position.text_document.uri.as_str(),
4730            "file:///root/dir-1/one.rs"
4731        );
4732        Ok(Some(vec![
4733            lsp::Location {
4734                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4735                range: lsp::Range::new(lsp::Position::new(0, 24), lsp::Position::new(0, 27)),
4736            },
4737            lsp::Location {
4738                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4739                range: lsp::Range::new(lsp::Position::new(0, 35), lsp::Position::new(0, 38)),
4740            },
4741            lsp::Location {
4742                uri: lsp::Url::from_file_path("/root/dir-2/three.rs").unwrap(),
4743                range: lsp::Range::new(lsp::Position::new(0, 37), lsp::Position::new(0, 40)),
4744            },
4745        ]))
4746    });
4747
4748    let references = project_b
4749        .update(cx_b, |p, cx| p.references(&buffer_b, 7, cx))
4750        .await
4751        .unwrap();
4752    cx_b.read(|cx| {
4753        assert_eq!(references.len(), 3);
4754        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4755
4756        let two_buffer = references[0].buffer.read(cx);
4757        let three_buffer = references[2].buffer.read(cx);
4758        assert_eq!(
4759            two_buffer.file().unwrap().path().as_ref(),
4760            Path::new("two.rs")
4761        );
4762        assert_eq!(references[1].buffer, references[0].buffer);
4763        assert_eq!(
4764            three_buffer.file().unwrap().full_path(cx),
4765            Path::new("/root/dir-2/three.rs")
4766        );
4767
4768        assert_eq!(references[0].range.to_offset(two_buffer), 24..27);
4769        assert_eq!(references[1].range.to_offset(two_buffer), 35..38);
4770        assert_eq!(references[2].range.to_offset(three_buffer), 37..40);
4771    });
4772}
4773
4774#[gpui::test(iterations = 10)]
4775async fn test_project_search(
4776    deterministic: Arc<Deterministic>,
4777    cx_a: &mut TestAppContext,
4778    cx_b: &mut TestAppContext,
4779) {
4780    deterministic.forbid_parking();
4781    let mut server = TestServer::start(&deterministic).await;
4782    let client_a = server.create_client(cx_a, "user_a").await;
4783    let client_b = server.create_client(cx_b, "user_b").await;
4784    server
4785        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4786        .await;
4787    let active_call_a = cx_a.read(ActiveCall::global);
4788
4789    client_a
4790        .fs()
4791        .insert_tree(
4792            "/root",
4793            json!({
4794                "dir-1": {
4795                    "a": "hello world",
4796                    "b": "goodnight moon",
4797                    "c": "a world of goo",
4798                    "d": "world champion of clown world",
4799                },
4800                "dir-2": {
4801                    "e": "disney world is fun",
4802                }
4803            }),
4804        )
4805        .await;
4806    let (project_a, _) = client_a.build_local_project("/root/dir-1", cx_a).await;
4807    let (worktree_2, _) = project_a
4808        .update(cx_a, |p, cx| {
4809            p.find_or_create_local_worktree("/root/dir-2", true, cx)
4810        })
4811        .await
4812        .unwrap();
4813    worktree_2
4814        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
4815        .await;
4816    let project_id = active_call_a
4817        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4818        .await
4819        .unwrap();
4820
4821    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4822
4823    // Perform a search as the guest.
4824    let mut results = HashMap::default();
4825    let mut search_rx = project_b.update(cx_b, |project, cx| {
4826        project.search(
4827            SearchQuery::text("world", false, false, Vec::new(), Vec::new()),
4828            cx,
4829        )
4830    });
4831    while let Some((buffer, ranges)) = search_rx.next().await {
4832        results.entry(buffer).or_insert(ranges);
4833    }
4834
4835    let mut ranges_by_path = results
4836        .into_iter()
4837        .map(|(buffer, ranges)| {
4838            buffer.read_with(cx_b, |buffer, cx| {
4839                let path = buffer.file().unwrap().full_path(cx);
4840                let offset_ranges = ranges
4841                    .into_iter()
4842                    .map(|range| range.to_offset(buffer))
4843                    .collect::<Vec<_>>();
4844                (path, offset_ranges)
4845            })
4846        })
4847        .collect::<Vec<_>>();
4848    ranges_by_path.sort_by_key(|(path, _)| path.clone());
4849
4850    assert_eq!(
4851        ranges_by_path,
4852        &[
4853            (PathBuf::from("dir-1/a"), vec![6..11]),
4854            (PathBuf::from("dir-1/c"), vec![2..7]),
4855            (PathBuf::from("dir-1/d"), vec![0..5, 24..29]),
4856            (PathBuf::from("dir-2/e"), vec![7..12]),
4857        ]
4858    );
4859}
4860
4861#[gpui::test(iterations = 10)]
4862async fn test_document_highlights(
4863    deterministic: Arc<Deterministic>,
4864    cx_a: &mut TestAppContext,
4865    cx_b: &mut TestAppContext,
4866) {
4867    deterministic.forbid_parking();
4868    let mut server = TestServer::start(&deterministic).await;
4869    let client_a = server.create_client(cx_a, "user_a").await;
4870    let client_b = server.create_client(cx_b, "user_b").await;
4871    server
4872        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4873        .await;
4874    let active_call_a = cx_a.read(ActiveCall::global);
4875
4876    client_a
4877        .fs()
4878        .insert_tree(
4879            "/root-1",
4880            json!({
4881                "main.rs": "fn double(number: i32) -> i32 { number + number }",
4882            }),
4883        )
4884        .await;
4885
4886    // Set up a fake language server.
4887    let mut language = Language::new(
4888        LanguageConfig {
4889            name: "Rust".into(),
4890            path_suffixes: vec!["rs".to_string()],
4891            ..Default::default()
4892        },
4893        Some(tree_sitter_rust::language()),
4894    );
4895    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4896    client_a.language_registry().add(Arc::new(language));
4897
4898    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4899    let project_id = active_call_a
4900        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4901        .await
4902        .unwrap();
4903    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4904
4905    // Open the file on client B.
4906    let buffer_b = cx_b
4907        .background()
4908        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
4909        .await
4910        .unwrap();
4911
4912    // Request document highlights as the guest.
4913    let fake_language_server = fake_language_servers.next().await.unwrap();
4914    fake_language_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
4915        |params, _| async move {
4916            assert_eq!(
4917                params
4918                    .text_document_position_params
4919                    .text_document
4920                    .uri
4921                    .as_str(),
4922                "file:///root-1/main.rs"
4923            );
4924            assert_eq!(
4925                params.text_document_position_params.position,
4926                lsp::Position::new(0, 34)
4927            );
4928            Ok(Some(vec![
4929                lsp::DocumentHighlight {
4930                    kind: Some(lsp::DocumentHighlightKind::WRITE),
4931                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 16)),
4932                },
4933                lsp::DocumentHighlight {
4934                    kind: Some(lsp::DocumentHighlightKind::READ),
4935                    range: lsp::Range::new(lsp::Position::new(0, 32), lsp::Position::new(0, 38)),
4936                },
4937                lsp::DocumentHighlight {
4938                    kind: Some(lsp::DocumentHighlightKind::READ),
4939                    range: lsp::Range::new(lsp::Position::new(0, 41), lsp::Position::new(0, 47)),
4940                },
4941            ]))
4942        },
4943    );
4944
4945    let highlights = project_b
4946        .update(cx_b, |p, cx| p.document_highlights(&buffer_b, 34, cx))
4947        .await
4948        .unwrap();
4949    buffer_b.read_with(cx_b, |buffer, _| {
4950        let snapshot = buffer.snapshot();
4951
4952        let highlights = highlights
4953            .into_iter()
4954            .map(|highlight| (highlight.kind, highlight.range.to_offset(&snapshot)))
4955            .collect::<Vec<_>>();
4956        assert_eq!(
4957            highlights,
4958            &[
4959                (lsp::DocumentHighlightKind::WRITE, 10..16),
4960                (lsp::DocumentHighlightKind::READ, 32..38),
4961                (lsp::DocumentHighlightKind::READ, 41..47)
4962            ]
4963        )
4964    });
4965}
4966
4967#[gpui::test(iterations = 10)]
4968async fn test_lsp_hover(
4969    deterministic: Arc<Deterministic>,
4970    cx_a: &mut TestAppContext,
4971    cx_b: &mut TestAppContext,
4972) {
4973    deterministic.forbid_parking();
4974    let mut server = TestServer::start(&deterministic).await;
4975    let client_a = server.create_client(cx_a, "user_a").await;
4976    let client_b = server.create_client(cx_b, "user_b").await;
4977    server
4978        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4979        .await;
4980    let active_call_a = cx_a.read(ActiveCall::global);
4981
4982    client_a
4983        .fs()
4984        .insert_tree(
4985            "/root-1",
4986            json!({
4987                "main.rs": "use std::collections::HashMap;",
4988            }),
4989        )
4990        .await;
4991
4992    // Set up a fake language server.
4993    let mut language = Language::new(
4994        LanguageConfig {
4995            name: "Rust".into(),
4996            path_suffixes: vec!["rs".to_string()],
4997            ..Default::default()
4998        },
4999        Some(tree_sitter_rust::language()),
5000    );
5001    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5002    client_a.language_registry().add(Arc::new(language));
5003
5004    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
5005    let project_id = active_call_a
5006        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5007        .await
5008        .unwrap();
5009    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5010
5011    // Open the file as the guest
5012    let buffer_b = cx_b
5013        .background()
5014        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
5015        .await
5016        .unwrap();
5017
5018    // Request hover information as the guest.
5019    let fake_language_server = fake_language_servers.next().await.unwrap();
5020    fake_language_server.handle_request::<lsp::request::HoverRequest, _, _>(
5021        |params, _| async move {
5022            assert_eq!(
5023                params
5024                    .text_document_position_params
5025                    .text_document
5026                    .uri
5027                    .as_str(),
5028                "file:///root-1/main.rs"
5029            );
5030            assert_eq!(
5031                params.text_document_position_params.position,
5032                lsp::Position::new(0, 22)
5033            );
5034            Ok(Some(lsp::Hover {
5035                contents: lsp::HoverContents::Array(vec![
5036                    lsp::MarkedString::String("Test hover content.".to_string()),
5037                    lsp::MarkedString::LanguageString(lsp::LanguageString {
5038                        language: "Rust".to_string(),
5039                        value: "let foo = 42;".to_string(),
5040                    }),
5041                ]),
5042                range: Some(lsp::Range::new(
5043                    lsp::Position::new(0, 22),
5044                    lsp::Position::new(0, 29),
5045                )),
5046            }))
5047        },
5048    );
5049
5050    let hover_info = project_b
5051        .update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
5052        .await
5053        .unwrap()
5054        .unwrap();
5055    buffer_b.read_with(cx_b, |buffer, _| {
5056        let snapshot = buffer.snapshot();
5057        assert_eq!(hover_info.range.unwrap().to_offset(&snapshot), 22..29);
5058        assert_eq!(
5059            hover_info.contents,
5060            vec![
5061                project::HoverBlock {
5062                    text: "Test hover content.".to_string(),
5063                    kind: HoverBlockKind::Markdown,
5064                },
5065                project::HoverBlock {
5066                    text: "let foo = 42;".to_string(),
5067                    kind: HoverBlockKind::Code {
5068                        language: "Rust".to_string()
5069                    },
5070                }
5071            ]
5072        );
5073    });
5074}
5075
5076#[gpui::test(iterations = 10)]
5077async fn test_project_symbols(
5078    deterministic: Arc<Deterministic>,
5079    cx_a: &mut TestAppContext,
5080    cx_b: &mut TestAppContext,
5081) {
5082    deterministic.forbid_parking();
5083    let mut server = TestServer::start(&deterministic).await;
5084    let client_a = server.create_client(cx_a, "user_a").await;
5085    let client_b = server.create_client(cx_b, "user_b").await;
5086    server
5087        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5088        .await;
5089    let active_call_a = cx_a.read(ActiveCall::global);
5090
5091    // Set up a fake language server.
5092    let mut language = Language::new(
5093        LanguageConfig {
5094            name: "Rust".into(),
5095            path_suffixes: vec!["rs".to_string()],
5096            ..Default::default()
5097        },
5098        Some(tree_sitter_rust::language()),
5099    );
5100    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5101    client_a.language_registry().add(Arc::new(language));
5102
5103    client_a
5104        .fs()
5105        .insert_tree(
5106            "/code",
5107            json!({
5108                "crate-1": {
5109                    "one.rs": "const ONE: usize = 1;",
5110                },
5111                "crate-2": {
5112                    "two.rs": "const TWO: usize = 2; const THREE: usize = 3;",
5113                },
5114                "private": {
5115                    "passwords.txt": "the-password",
5116                }
5117            }),
5118        )
5119        .await;
5120    let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await;
5121    let project_id = active_call_a
5122        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5123        .await
5124        .unwrap();
5125    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5126
5127    // Cause the language server to start.
5128    let _buffer = cx_b
5129        .background()
5130        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
5131        .await
5132        .unwrap();
5133
5134    let fake_language_server = fake_language_servers.next().await.unwrap();
5135    fake_language_server.handle_request::<lsp::WorkspaceSymbolRequest, _, _>(|_, _| async move {
5136        Ok(Some(lsp::WorkspaceSymbolResponse::Flat(vec![
5137            #[allow(deprecated)]
5138            lsp::SymbolInformation {
5139                name: "TWO".into(),
5140                location: lsp::Location {
5141                    uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
5142                    range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5143                },
5144                kind: lsp::SymbolKind::CONSTANT,
5145                tags: None,
5146                container_name: None,
5147                deprecated: None,
5148            },
5149        ])))
5150    });
5151
5152    // Request the definition of a symbol as the guest.
5153    let symbols = project_b
5154        .update(cx_b, |p, cx| p.symbols("two", cx))
5155        .await
5156        .unwrap();
5157    assert_eq!(symbols.len(), 1);
5158    assert_eq!(symbols[0].name, "TWO");
5159
5160    // Open one of the returned symbols.
5161    let buffer_b_2 = project_b
5162        .update(cx_b, |project, cx| {
5163            project.open_buffer_for_symbol(&symbols[0], cx)
5164        })
5165        .await
5166        .unwrap();
5167    buffer_b_2.read_with(cx_b, |buffer, _| {
5168        assert_eq!(
5169            buffer.file().unwrap().path().as_ref(),
5170            Path::new("../crate-2/two.rs")
5171        );
5172    });
5173
5174    // Attempt to craft a symbol and violate host's privacy by opening an arbitrary file.
5175    let mut fake_symbol = symbols[0].clone();
5176    fake_symbol.path.path = Path::new("/code/secrets").into();
5177    let error = project_b
5178        .update(cx_b, |project, cx| {
5179            project.open_buffer_for_symbol(&fake_symbol, cx)
5180        })
5181        .await
5182        .unwrap_err();
5183    assert!(error.to_string().contains("invalid symbol signature"));
5184}
5185
5186#[gpui::test(iterations = 10)]
5187async fn test_open_buffer_while_getting_definition_pointing_to_it(
5188    deterministic: Arc<Deterministic>,
5189    cx_a: &mut TestAppContext,
5190    cx_b: &mut TestAppContext,
5191    mut rng: StdRng,
5192) {
5193    deterministic.forbid_parking();
5194    let mut server = TestServer::start(&deterministic).await;
5195    let client_a = server.create_client(cx_a, "user_a").await;
5196    let client_b = server.create_client(cx_b, "user_b").await;
5197    server
5198        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5199        .await;
5200    let active_call_a = cx_a.read(ActiveCall::global);
5201
5202    // Set up a fake language server.
5203    let mut language = Language::new(
5204        LanguageConfig {
5205            name: "Rust".into(),
5206            path_suffixes: vec!["rs".to_string()],
5207            ..Default::default()
5208        },
5209        Some(tree_sitter_rust::language()),
5210    );
5211    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5212    client_a.language_registry().add(Arc::new(language));
5213
5214    client_a
5215        .fs()
5216        .insert_tree(
5217            "/root",
5218            json!({
5219                "a.rs": "const ONE: usize = b::TWO;",
5220                "b.rs": "const TWO: usize = 2",
5221            }),
5222        )
5223        .await;
5224    let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await;
5225    let project_id = active_call_a
5226        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5227        .await
5228        .unwrap();
5229    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5230
5231    let buffer_b1 = cx_b
5232        .background()
5233        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
5234        .await
5235        .unwrap();
5236
5237    let fake_language_server = fake_language_servers.next().await.unwrap();
5238    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
5239        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
5240            lsp::Location::new(
5241                lsp::Url::from_file_path("/root/b.rs").unwrap(),
5242                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5243            ),
5244        )))
5245    });
5246
5247    let definitions;
5248    let buffer_b2;
5249    if rng.gen() {
5250        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5251        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5252    } else {
5253        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5254        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5255    }
5256
5257    let buffer_b2 = buffer_b2.await.unwrap();
5258    let definitions = definitions.await.unwrap();
5259    assert_eq!(definitions.len(), 1);
5260    assert_eq!(definitions[0].target.buffer, buffer_b2);
5261}
5262
5263#[gpui::test(iterations = 10)]
5264async fn test_collaborating_with_code_actions(
5265    deterministic: Arc<Deterministic>,
5266    cx_a: &mut TestAppContext,
5267    cx_b: &mut TestAppContext,
5268) {
5269    deterministic.forbid_parking();
5270    let mut server = TestServer::start(&deterministic).await;
5271    let client_a = server.create_client(cx_a, "user_a").await;
5272    //
5273    let client_b = server.create_client(cx_b, "user_b").await;
5274    server
5275        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5276        .await;
5277    let active_call_a = cx_a.read(ActiveCall::global);
5278
5279    cx_b.update(editor::init);
5280
5281    // Set up a fake language server.
5282    let mut language = Language::new(
5283        LanguageConfig {
5284            name: "Rust".into(),
5285            path_suffixes: vec!["rs".to_string()],
5286            ..Default::default()
5287        },
5288        Some(tree_sitter_rust::language()),
5289    );
5290    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5291    client_a.language_registry().add(Arc::new(language));
5292
5293    client_a
5294        .fs()
5295        .insert_tree(
5296            "/a",
5297            json!({
5298                "main.rs": "mod other;\nfn main() { let foo = other::foo(); }",
5299                "other.rs": "pub fn foo() -> usize { 4 }",
5300            }),
5301        )
5302        .await;
5303    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
5304    let project_id = active_call_a
5305        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5306        .await
5307        .unwrap();
5308
5309    // Join the project as client B.
5310    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5311    let window_b =
5312        cx_b.add_window(|cx| Workspace::new(0, project_b.clone(), client_b.app_state.clone(), cx));
5313    let workspace_b = window_b.root(cx_b);
5314    let editor_b = workspace_b
5315        .update(cx_b, |workspace, cx| {
5316            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
5317        })
5318        .await
5319        .unwrap()
5320        .downcast::<Editor>()
5321        .unwrap();
5322
5323    let mut fake_language_server = fake_language_servers.next().await.unwrap();
5324    let mut requests = fake_language_server
5325        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
5326            assert_eq!(
5327                params.text_document.uri,
5328                lsp::Url::from_file_path("/a/main.rs").unwrap(),
5329            );
5330            assert_eq!(params.range.start, lsp::Position::new(0, 0));
5331            assert_eq!(params.range.end, lsp::Position::new(0, 0));
5332            Ok(None)
5333        });
5334    deterministic.advance_clock(editor::CODE_ACTIONS_DEBOUNCE_TIMEOUT * 2);
5335    requests.next().await;
5336
5337    // Move cursor to a location that contains code actions.
5338    editor_b.update(cx_b, |editor, cx| {
5339        editor.change_selections(None, cx, |s| {
5340            s.select_ranges([Point::new(1, 31)..Point::new(1, 31)])
5341        });
5342        cx.focus(&editor_b);
5343    });
5344
5345    let mut requests = fake_language_server
5346        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
5347            assert_eq!(
5348                params.text_document.uri,
5349                lsp::Url::from_file_path("/a/main.rs").unwrap(),
5350            );
5351            assert_eq!(params.range.start, lsp::Position::new(1, 31));
5352            assert_eq!(params.range.end, lsp::Position::new(1, 31));
5353
5354            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
5355                lsp::CodeAction {
5356                    title: "Inline into all callers".to_string(),
5357                    edit: Some(lsp::WorkspaceEdit {
5358                        changes: Some(
5359                            [
5360                                (
5361                                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
5362                                    vec![lsp::TextEdit::new(
5363                                        lsp::Range::new(
5364                                            lsp::Position::new(1, 22),
5365                                            lsp::Position::new(1, 34),
5366                                        ),
5367                                        "4".to_string(),
5368                                    )],
5369                                ),
5370                                (
5371                                    lsp::Url::from_file_path("/a/other.rs").unwrap(),
5372                                    vec![lsp::TextEdit::new(
5373                                        lsp::Range::new(
5374                                            lsp::Position::new(0, 0),
5375                                            lsp::Position::new(0, 27),
5376                                        ),
5377                                        "".to_string(),
5378                                    )],
5379                                ),
5380                            ]
5381                            .into_iter()
5382                            .collect(),
5383                        ),
5384                        ..Default::default()
5385                    }),
5386                    data: Some(json!({
5387                        "codeActionParams": {
5388                            "range": {
5389                                "start": {"line": 1, "column": 31},
5390                                "end": {"line": 1, "column": 31},
5391                            }
5392                        }
5393                    })),
5394                    ..Default::default()
5395                },
5396            )]))
5397        });
5398    deterministic.advance_clock(editor::CODE_ACTIONS_DEBOUNCE_TIMEOUT * 2);
5399    requests.next().await;
5400
5401    // Toggle code actions and wait for them to display.
5402    editor_b.update(cx_b, |editor, cx| {
5403        editor.toggle_code_actions(
5404            &ToggleCodeActions {
5405                deployed_from_indicator: false,
5406            },
5407            cx,
5408        );
5409    });
5410    cx_a.foreground().run_until_parked();
5411    editor_b.read_with(cx_b, |editor, _| assert!(editor.context_menu_visible()));
5412
5413    fake_language_server.remove_request_handler::<lsp::request::CodeActionRequest>();
5414
5415    // Confirming the code action will trigger a resolve request.
5416    let confirm_action = workspace_b
5417        .update(cx_b, |workspace, cx| {
5418            Editor::confirm_code_action(workspace, &ConfirmCodeAction { item_ix: Some(0) }, cx)
5419        })
5420        .unwrap();
5421    fake_language_server.handle_request::<lsp::request::CodeActionResolveRequest, _, _>(
5422        |_, _| async move {
5423            Ok(lsp::CodeAction {
5424                title: "Inline into all callers".to_string(),
5425                edit: Some(lsp::WorkspaceEdit {
5426                    changes: Some(
5427                        [
5428                            (
5429                                lsp::Url::from_file_path("/a/main.rs").unwrap(),
5430                                vec![lsp::TextEdit::new(
5431                                    lsp::Range::new(
5432                                        lsp::Position::new(1, 22),
5433                                        lsp::Position::new(1, 34),
5434                                    ),
5435                                    "4".to_string(),
5436                                )],
5437                            ),
5438                            (
5439                                lsp::Url::from_file_path("/a/other.rs").unwrap(),
5440                                vec![lsp::TextEdit::new(
5441                                    lsp::Range::new(
5442                                        lsp::Position::new(0, 0),
5443                                        lsp::Position::new(0, 27),
5444                                    ),
5445                                    "".to_string(),
5446                                )],
5447                            ),
5448                        ]
5449                        .into_iter()
5450                        .collect(),
5451                    ),
5452                    ..Default::default()
5453                }),
5454                ..Default::default()
5455            })
5456        },
5457    );
5458
5459    // After the action is confirmed, an editor containing both modified files is opened.
5460    confirm_action.await.unwrap();
5461    let code_action_editor = workspace_b.read_with(cx_b, |workspace, cx| {
5462        workspace
5463            .active_item(cx)
5464            .unwrap()
5465            .downcast::<Editor>()
5466            .unwrap()
5467    });
5468    code_action_editor.update(cx_b, |editor, cx| {
5469        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
5470        editor.undo(&Undo, cx);
5471        assert_eq!(
5472            editor.text(cx),
5473            "mod other;\nfn main() { let foo = other::foo(); }\npub fn foo() -> usize { 4 }"
5474        );
5475        editor.redo(&Redo, cx);
5476        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
5477    });
5478}
5479
5480#[gpui::test(iterations = 10)]
5481async fn test_collaborating_with_renames(
5482    deterministic: Arc<Deterministic>,
5483    cx_a: &mut TestAppContext,
5484    cx_b: &mut TestAppContext,
5485) {
5486    deterministic.forbid_parking();
5487    let mut server = TestServer::start(&deterministic).await;
5488    let client_a = server.create_client(cx_a, "user_a").await;
5489    let client_b = server.create_client(cx_b, "user_b").await;
5490    server
5491        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5492        .await;
5493    let active_call_a = cx_a.read(ActiveCall::global);
5494
5495    cx_b.update(editor::init);
5496
5497    // Set up a fake language server.
5498    let mut language = Language::new(
5499        LanguageConfig {
5500            name: "Rust".into(),
5501            path_suffixes: vec!["rs".to_string()],
5502            ..Default::default()
5503        },
5504        Some(tree_sitter_rust::language()),
5505    );
5506    let mut fake_language_servers = language
5507        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
5508            capabilities: lsp::ServerCapabilities {
5509                rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
5510                    prepare_provider: Some(true),
5511                    work_done_progress_options: Default::default(),
5512                })),
5513                ..Default::default()
5514            },
5515            ..Default::default()
5516        }))
5517        .await;
5518    client_a.language_registry().add(Arc::new(language));
5519
5520    client_a
5521        .fs()
5522        .insert_tree(
5523            "/dir",
5524            json!({
5525                "one.rs": "const ONE: usize = 1;",
5526                "two.rs": "const TWO: usize = one::ONE + one::ONE;"
5527            }),
5528        )
5529        .await;
5530    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
5531    let project_id = active_call_a
5532        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5533        .await
5534        .unwrap();
5535    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5536
5537    let window_b =
5538        cx_b.add_window(|cx| Workspace::new(0, project_b.clone(), client_b.app_state.clone(), cx));
5539    let workspace_b = window_b.root(cx_b);
5540    let editor_b = workspace_b
5541        .update(cx_b, |workspace, cx| {
5542            workspace.open_path((worktree_id, "one.rs"), None, true, cx)
5543        })
5544        .await
5545        .unwrap()
5546        .downcast::<Editor>()
5547        .unwrap();
5548    let fake_language_server = fake_language_servers.next().await.unwrap();
5549
5550    // Move cursor to a location that can be renamed.
5551    let prepare_rename = editor_b.update(cx_b, |editor, cx| {
5552        editor.change_selections(None, cx, |s| s.select_ranges([7..7]));
5553        editor.rename(&Rename, cx).unwrap()
5554    });
5555
5556    fake_language_server
5557        .handle_request::<lsp::request::PrepareRenameRequest, _, _>(|params, _| async move {
5558            assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs");
5559            assert_eq!(params.position, lsp::Position::new(0, 7));
5560            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
5561                lsp::Position::new(0, 6),
5562                lsp::Position::new(0, 9),
5563            ))))
5564        })
5565        .next()
5566        .await
5567        .unwrap();
5568    prepare_rename.await.unwrap();
5569    editor_b.update(cx_b, |editor, cx| {
5570        use editor::ToOffset;
5571        let rename = editor.pending_rename().unwrap();
5572        let buffer = editor.buffer().read(cx).snapshot(cx);
5573        assert_eq!(
5574            rename.range.start.to_offset(&buffer)..rename.range.end.to_offset(&buffer),
5575            6..9
5576        );
5577        rename.editor.update(cx, |rename_editor, cx| {
5578            rename_editor.buffer().update(cx, |rename_buffer, cx| {
5579                rename_buffer.edit([(0..3, "THREE")], None, cx);
5580            });
5581        });
5582    });
5583
5584    let confirm_rename = workspace_b.update(cx_b, |workspace, cx| {
5585        Editor::confirm_rename(workspace, &ConfirmRename, cx).unwrap()
5586    });
5587    fake_language_server
5588        .handle_request::<lsp::request::Rename, _, _>(|params, _| async move {
5589            assert_eq!(
5590                params.text_document_position.text_document.uri.as_str(),
5591                "file:///dir/one.rs"
5592            );
5593            assert_eq!(
5594                params.text_document_position.position,
5595                lsp::Position::new(0, 6)
5596            );
5597            assert_eq!(params.new_name, "THREE");
5598            Ok(Some(lsp::WorkspaceEdit {
5599                changes: Some(
5600                    [
5601                        (
5602                            lsp::Url::from_file_path("/dir/one.rs").unwrap(),
5603                            vec![lsp::TextEdit::new(
5604                                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5605                                "THREE".to_string(),
5606                            )],
5607                        ),
5608                        (
5609                            lsp::Url::from_file_path("/dir/two.rs").unwrap(),
5610                            vec![
5611                                lsp::TextEdit::new(
5612                                    lsp::Range::new(
5613                                        lsp::Position::new(0, 24),
5614                                        lsp::Position::new(0, 27),
5615                                    ),
5616                                    "THREE".to_string(),
5617                                ),
5618                                lsp::TextEdit::new(
5619                                    lsp::Range::new(
5620                                        lsp::Position::new(0, 35),
5621                                        lsp::Position::new(0, 38),
5622                                    ),
5623                                    "THREE".to_string(),
5624                                ),
5625                            ],
5626                        ),
5627                    ]
5628                    .into_iter()
5629                    .collect(),
5630                ),
5631                ..Default::default()
5632            }))
5633        })
5634        .next()
5635        .await
5636        .unwrap();
5637    confirm_rename.await.unwrap();
5638
5639    let rename_editor = workspace_b.read_with(cx_b, |workspace, cx| {
5640        workspace
5641            .active_item(cx)
5642            .unwrap()
5643            .downcast::<Editor>()
5644            .unwrap()
5645    });
5646    rename_editor.update(cx_b, |editor, cx| {
5647        assert_eq!(
5648            editor.text(cx),
5649            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
5650        );
5651        editor.undo(&Undo, cx);
5652        assert_eq!(
5653            editor.text(cx),
5654            "const ONE: usize = 1;\nconst TWO: usize = one::ONE + one::ONE;"
5655        );
5656        editor.redo(&Redo, cx);
5657        assert_eq!(
5658            editor.text(cx),
5659            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
5660        );
5661    });
5662
5663    // Ensure temporary rename edits cannot be undone/redone.
5664    editor_b.update(cx_b, |editor, cx| {
5665        editor.undo(&Undo, cx);
5666        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
5667        editor.undo(&Undo, cx);
5668        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
5669        editor.redo(&Redo, cx);
5670        assert_eq!(editor.text(cx), "const THREE: usize = 1;");
5671    })
5672}
5673
5674#[gpui::test(iterations = 10)]
5675async fn test_language_server_statuses(
5676    deterministic: Arc<Deterministic>,
5677    cx_a: &mut TestAppContext,
5678    cx_b: &mut TestAppContext,
5679) {
5680    deterministic.forbid_parking();
5681    let mut server = TestServer::start(&deterministic).await;
5682    let client_a = server.create_client(cx_a, "user_a").await;
5683    let client_b = server.create_client(cx_b, "user_b").await;
5684    server
5685        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5686        .await;
5687    let active_call_a = cx_a.read(ActiveCall::global);
5688
5689    cx_b.update(editor::init);
5690
5691    // Set up a fake language server.
5692    let mut language = Language::new(
5693        LanguageConfig {
5694            name: "Rust".into(),
5695            path_suffixes: vec!["rs".to_string()],
5696            ..Default::default()
5697        },
5698        Some(tree_sitter_rust::language()),
5699    );
5700    let mut fake_language_servers = language
5701        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
5702            name: "the-language-server",
5703            ..Default::default()
5704        }))
5705        .await;
5706    client_a.language_registry().add(Arc::new(language));
5707
5708    client_a
5709        .fs()
5710        .insert_tree(
5711            "/dir",
5712            json!({
5713                "main.rs": "const ONE: usize = 1;",
5714            }),
5715        )
5716        .await;
5717    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
5718
5719    let _buffer_a = project_a
5720        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
5721        .await
5722        .unwrap();
5723
5724    let fake_language_server = fake_language_servers.next().await.unwrap();
5725    fake_language_server.start_progress("the-token").await;
5726    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
5727        token: lsp::NumberOrString::String("the-token".to_string()),
5728        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
5729            lsp::WorkDoneProgressReport {
5730                message: Some("the-message".to_string()),
5731                ..Default::default()
5732            },
5733        )),
5734    });
5735    deterministic.run_until_parked();
5736    project_a.read_with(cx_a, |project, _| {
5737        let status = project.language_server_statuses().next().unwrap();
5738        assert_eq!(status.name, "the-language-server");
5739        assert_eq!(status.pending_work.len(), 1);
5740        assert_eq!(
5741            status.pending_work["the-token"].message.as_ref().unwrap(),
5742            "the-message"
5743        );
5744    });
5745
5746    let project_id = active_call_a
5747        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5748        .await
5749        .unwrap();
5750    deterministic.run_until_parked();
5751    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5752    project_b.read_with(cx_b, |project, _| {
5753        let status = project.language_server_statuses().next().unwrap();
5754        assert_eq!(status.name, "the-language-server");
5755    });
5756
5757    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
5758        token: lsp::NumberOrString::String("the-token".to_string()),
5759        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
5760            lsp::WorkDoneProgressReport {
5761                message: Some("the-message-2".to_string()),
5762                ..Default::default()
5763            },
5764        )),
5765    });
5766    deterministic.run_until_parked();
5767    project_a.read_with(cx_a, |project, _| {
5768        let status = project.language_server_statuses().next().unwrap();
5769        assert_eq!(status.name, "the-language-server");
5770        assert_eq!(status.pending_work.len(), 1);
5771        assert_eq!(
5772            status.pending_work["the-token"].message.as_ref().unwrap(),
5773            "the-message-2"
5774        );
5775    });
5776    project_b.read_with(cx_b, |project, _| {
5777        let status = project.language_server_statuses().next().unwrap();
5778        assert_eq!(status.name, "the-language-server");
5779        assert_eq!(status.pending_work.len(), 1);
5780        assert_eq!(
5781            status.pending_work["the-token"].message.as_ref().unwrap(),
5782            "the-message-2"
5783        );
5784    });
5785}
5786
5787#[gpui::test(iterations = 10)]
5788async fn test_contacts(
5789    deterministic: Arc<Deterministic>,
5790    cx_a: &mut TestAppContext,
5791    cx_b: &mut TestAppContext,
5792    cx_c: &mut TestAppContext,
5793    cx_d: &mut TestAppContext,
5794) {
5795    deterministic.forbid_parking();
5796    let mut server = TestServer::start(&deterministic).await;
5797    let client_a = server.create_client(cx_a, "user_a").await;
5798    let client_b = server.create_client(cx_b, "user_b").await;
5799    let client_c = server.create_client(cx_c, "user_c").await;
5800    let client_d = server.create_client(cx_d, "user_d").await;
5801    server
5802        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
5803        .await;
5804    let active_call_a = cx_a.read(ActiveCall::global);
5805    let active_call_b = cx_b.read(ActiveCall::global);
5806    let active_call_c = cx_c.read(ActiveCall::global);
5807    let _active_call_d = cx_d.read(ActiveCall::global);
5808
5809    deterministic.run_until_parked();
5810    assert_eq!(
5811        contacts(&client_a, cx_a),
5812        [
5813            ("user_b".to_string(), "online", "free"),
5814            ("user_c".to_string(), "online", "free")
5815        ]
5816    );
5817    assert_eq!(
5818        contacts(&client_b, cx_b),
5819        [
5820            ("user_a".to_string(), "online", "free"),
5821            ("user_c".to_string(), "online", "free")
5822        ]
5823    );
5824    assert_eq!(
5825        contacts(&client_c, cx_c),
5826        [
5827            ("user_a".to_string(), "online", "free"),
5828            ("user_b".to_string(), "online", "free")
5829        ]
5830    );
5831    assert_eq!(contacts(&client_d, cx_d), []);
5832
5833    server.disconnect_client(client_c.peer_id().unwrap());
5834    server.forbid_connections();
5835    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5836    assert_eq!(
5837        contacts(&client_a, cx_a),
5838        [
5839            ("user_b".to_string(), "online", "free"),
5840            ("user_c".to_string(), "offline", "free")
5841        ]
5842    );
5843    assert_eq!(
5844        contacts(&client_b, cx_b),
5845        [
5846            ("user_a".to_string(), "online", "free"),
5847            ("user_c".to_string(), "offline", "free")
5848        ]
5849    );
5850    assert_eq!(contacts(&client_c, cx_c), []);
5851    assert_eq!(contacts(&client_d, cx_d), []);
5852
5853    server.allow_connections();
5854    client_c
5855        .authenticate_and_connect(false, &cx_c.to_async())
5856        .await
5857        .unwrap();
5858
5859    deterministic.run_until_parked();
5860    assert_eq!(
5861        contacts(&client_a, cx_a),
5862        [
5863            ("user_b".to_string(), "online", "free"),
5864            ("user_c".to_string(), "online", "free")
5865        ]
5866    );
5867    assert_eq!(
5868        contacts(&client_b, cx_b),
5869        [
5870            ("user_a".to_string(), "online", "free"),
5871            ("user_c".to_string(), "online", "free")
5872        ]
5873    );
5874    assert_eq!(
5875        contacts(&client_c, cx_c),
5876        [
5877            ("user_a".to_string(), "online", "free"),
5878            ("user_b".to_string(), "online", "free")
5879        ]
5880    );
5881    assert_eq!(contacts(&client_d, cx_d), []);
5882
5883    active_call_a
5884        .update(cx_a, |call, cx| {
5885            call.invite(client_b.user_id().unwrap(), None, cx)
5886        })
5887        .await
5888        .unwrap();
5889    deterministic.run_until_parked();
5890    assert_eq!(
5891        contacts(&client_a, cx_a),
5892        [
5893            ("user_b".to_string(), "online", "busy"),
5894            ("user_c".to_string(), "online", "free")
5895        ]
5896    );
5897    assert_eq!(
5898        contacts(&client_b, cx_b),
5899        [
5900            ("user_a".to_string(), "online", "busy"),
5901            ("user_c".to_string(), "online", "free")
5902        ]
5903    );
5904    assert_eq!(
5905        contacts(&client_c, cx_c),
5906        [
5907            ("user_a".to_string(), "online", "busy"),
5908            ("user_b".to_string(), "online", "busy")
5909        ]
5910    );
5911    assert_eq!(contacts(&client_d, cx_d), []);
5912
5913    // Client B and client D become contacts while client B is being called.
5914    server
5915        .make_contacts(&mut [(&client_b, cx_b), (&client_d, cx_d)])
5916        .await;
5917    deterministic.run_until_parked();
5918    assert_eq!(
5919        contacts(&client_a, cx_a),
5920        [
5921            ("user_b".to_string(), "online", "busy"),
5922            ("user_c".to_string(), "online", "free")
5923        ]
5924    );
5925    assert_eq!(
5926        contacts(&client_b, cx_b),
5927        [
5928            ("user_a".to_string(), "online", "busy"),
5929            ("user_c".to_string(), "online", "free"),
5930            ("user_d".to_string(), "online", "free"),
5931        ]
5932    );
5933    assert_eq!(
5934        contacts(&client_c, cx_c),
5935        [
5936            ("user_a".to_string(), "online", "busy"),
5937            ("user_b".to_string(), "online", "busy")
5938        ]
5939    );
5940    assert_eq!(
5941        contacts(&client_d, cx_d),
5942        [("user_b".to_string(), "online", "busy")]
5943    );
5944
5945    active_call_b.update(cx_b, |call, cx| call.decline_incoming(cx).unwrap());
5946    deterministic.run_until_parked();
5947    assert_eq!(
5948        contacts(&client_a, cx_a),
5949        [
5950            ("user_b".to_string(), "online", "free"),
5951            ("user_c".to_string(), "online", "free")
5952        ]
5953    );
5954    assert_eq!(
5955        contacts(&client_b, cx_b),
5956        [
5957            ("user_a".to_string(), "online", "free"),
5958            ("user_c".to_string(), "online", "free"),
5959            ("user_d".to_string(), "online", "free")
5960        ]
5961    );
5962    assert_eq!(
5963        contacts(&client_c, cx_c),
5964        [
5965            ("user_a".to_string(), "online", "free"),
5966            ("user_b".to_string(), "online", "free")
5967        ]
5968    );
5969    assert_eq!(
5970        contacts(&client_d, cx_d),
5971        [("user_b".to_string(), "online", "free")]
5972    );
5973
5974    active_call_c
5975        .update(cx_c, |call, cx| {
5976            call.invite(client_a.user_id().unwrap(), None, cx)
5977        })
5978        .await
5979        .unwrap();
5980    deterministic.run_until_parked();
5981    assert_eq!(
5982        contacts(&client_a, cx_a),
5983        [
5984            ("user_b".to_string(), "online", "free"),
5985            ("user_c".to_string(), "online", "busy")
5986        ]
5987    );
5988    assert_eq!(
5989        contacts(&client_b, cx_b),
5990        [
5991            ("user_a".to_string(), "online", "busy"),
5992            ("user_c".to_string(), "online", "busy"),
5993            ("user_d".to_string(), "online", "free")
5994        ]
5995    );
5996    assert_eq!(
5997        contacts(&client_c, cx_c),
5998        [
5999            ("user_a".to_string(), "online", "busy"),
6000            ("user_b".to_string(), "online", "free")
6001        ]
6002    );
6003    assert_eq!(
6004        contacts(&client_d, cx_d),
6005        [("user_b".to_string(), "online", "free")]
6006    );
6007
6008    active_call_a
6009        .update(cx_a, |call, cx| call.accept_incoming(cx))
6010        .await
6011        .unwrap();
6012    deterministic.run_until_parked();
6013    assert_eq!(
6014        contacts(&client_a, cx_a),
6015        [
6016            ("user_b".to_string(), "online", "free"),
6017            ("user_c".to_string(), "online", "busy")
6018        ]
6019    );
6020    assert_eq!(
6021        contacts(&client_b, cx_b),
6022        [
6023            ("user_a".to_string(), "online", "busy"),
6024            ("user_c".to_string(), "online", "busy"),
6025            ("user_d".to_string(), "online", "free")
6026        ]
6027    );
6028    assert_eq!(
6029        contacts(&client_c, cx_c),
6030        [
6031            ("user_a".to_string(), "online", "busy"),
6032            ("user_b".to_string(), "online", "free")
6033        ]
6034    );
6035    assert_eq!(
6036        contacts(&client_d, cx_d),
6037        [("user_b".to_string(), "online", "free")]
6038    );
6039
6040    active_call_a
6041        .update(cx_a, |call, cx| {
6042            call.invite(client_b.user_id().unwrap(), None, cx)
6043        })
6044        .await
6045        .unwrap();
6046    deterministic.run_until_parked();
6047    assert_eq!(
6048        contacts(&client_a, cx_a),
6049        [
6050            ("user_b".to_string(), "online", "busy"),
6051            ("user_c".to_string(), "online", "busy")
6052        ]
6053    );
6054    assert_eq!(
6055        contacts(&client_b, cx_b),
6056        [
6057            ("user_a".to_string(), "online", "busy"),
6058            ("user_c".to_string(), "online", "busy"),
6059            ("user_d".to_string(), "online", "free")
6060        ]
6061    );
6062    assert_eq!(
6063        contacts(&client_c, cx_c),
6064        [
6065            ("user_a".to_string(), "online", "busy"),
6066            ("user_b".to_string(), "online", "busy")
6067        ]
6068    );
6069    assert_eq!(
6070        contacts(&client_d, cx_d),
6071        [("user_b".to_string(), "online", "busy")]
6072    );
6073
6074    active_call_a
6075        .update(cx_a, |call, cx| call.hang_up(cx))
6076        .await
6077        .unwrap();
6078    deterministic.run_until_parked();
6079    assert_eq!(
6080        contacts(&client_a, cx_a),
6081        [
6082            ("user_b".to_string(), "online", "free"),
6083            ("user_c".to_string(), "online", "free")
6084        ]
6085    );
6086    assert_eq!(
6087        contacts(&client_b, cx_b),
6088        [
6089            ("user_a".to_string(), "online", "free"),
6090            ("user_c".to_string(), "online", "free"),
6091            ("user_d".to_string(), "online", "free")
6092        ]
6093    );
6094    assert_eq!(
6095        contacts(&client_c, cx_c),
6096        [
6097            ("user_a".to_string(), "online", "free"),
6098            ("user_b".to_string(), "online", "free")
6099        ]
6100    );
6101    assert_eq!(
6102        contacts(&client_d, cx_d),
6103        [("user_b".to_string(), "online", "free")]
6104    );
6105
6106    active_call_a
6107        .update(cx_a, |call, cx| {
6108            call.invite(client_b.user_id().unwrap(), None, cx)
6109        })
6110        .await
6111        .unwrap();
6112    deterministic.run_until_parked();
6113    assert_eq!(
6114        contacts(&client_a, cx_a),
6115        [
6116            ("user_b".to_string(), "online", "busy"),
6117            ("user_c".to_string(), "online", "free")
6118        ]
6119    );
6120    assert_eq!(
6121        contacts(&client_b, cx_b),
6122        [
6123            ("user_a".to_string(), "online", "busy"),
6124            ("user_c".to_string(), "online", "free"),
6125            ("user_d".to_string(), "online", "free")
6126        ]
6127    );
6128    assert_eq!(
6129        contacts(&client_c, cx_c),
6130        [
6131            ("user_a".to_string(), "online", "busy"),
6132            ("user_b".to_string(), "online", "busy")
6133        ]
6134    );
6135    assert_eq!(
6136        contacts(&client_d, cx_d),
6137        [("user_b".to_string(), "online", "busy")]
6138    );
6139
6140    server.forbid_connections();
6141    server.disconnect_client(client_a.peer_id().unwrap());
6142    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
6143    assert_eq!(contacts(&client_a, cx_a), []);
6144    assert_eq!(
6145        contacts(&client_b, cx_b),
6146        [
6147            ("user_a".to_string(), "offline", "free"),
6148            ("user_c".to_string(), "online", "free"),
6149            ("user_d".to_string(), "online", "free")
6150        ]
6151    );
6152    assert_eq!(
6153        contacts(&client_c, cx_c),
6154        [
6155            ("user_a".to_string(), "offline", "free"),
6156            ("user_b".to_string(), "online", "free")
6157        ]
6158    );
6159    assert_eq!(
6160        contacts(&client_d, cx_d),
6161        [("user_b".to_string(), "online", "free")]
6162    );
6163
6164    // Test removing a contact
6165    client_b
6166        .user_store()
6167        .update(cx_b, |store, cx| {
6168            store.remove_contact(client_c.user_id().unwrap(), cx)
6169        })
6170        .await
6171        .unwrap();
6172    deterministic.run_until_parked();
6173    assert_eq!(
6174        contacts(&client_b, cx_b),
6175        [
6176            ("user_a".to_string(), "offline", "free"),
6177            ("user_d".to_string(), "online", "free")
6178        ]
6179    );
6180    assert_eq!(
6181        contacts(&client_c, cx_c),
6182        [("user_a".to_string(), "offline", "free"),]
6183    );
6184
6185    fn contacts(
6186        client: &TestClient,
6187        cx: &TestAppContext,
6188    ) -> Vec<(String, &'static str, &'static str)> {
6189        client.user_store().read_with(cx, |store, _| {
6190            store
6191                .contacts()
6192                .iter()
6193                .map(|contact| {
6194                    (
6195                        contact.user.github_login.clone(),
6196                        if contact.online { "online" } else { "offline" },
6197                        if contact.busy { "busy" } else { "free" },
6198                    )
6199                })
6200                .collect()
6201        })
6202    }
6203}
6204
6205#[gpui::test(iterations = 10)]
6206async fn test_contact_requests(
6207    deterministic: Arc<Deterministic>,
6208    cx_a: &mut TestAppContext,
6209    cx_a2: &mut TestAppContext,
6210    cx_b: &mut TestAppContext,
6211    cx_b2: &mut TestAppContext,
6212    cx_c: &mut TestAppContext,
6213    cx_c2: &mut TestAppContext,
6214) {
6215    deterministic.forbid_parking();
6216
6217    // Connect to a server as 3 clients.
6218    let mut server = TestServer::start(&deterministic).await;
6219    let client_a = server.create_client(cx_a, "user_a").await;
6220    let client_a2 = server.create_client(cx_a2, "user_a").await;
6221    let client_b = server.create_client(cx_b, "user_b").await;
6222    let client_b2 = server.create_client(cx_b2, "user_b").await;
6223    let client_c = server.create_client(cx_c, "user_c").await;
6224    let client_c2 = server.create_client(cx_c2, "user_c").await;
6225
6226    assert_eq!(client_a.user_id().unwrap(), client_a2.user_id().unwrap());
6227    assert_eq!(client_b.user_id().unwrap(), client_b2.user_id().unwrap());
6228    assert_eq!(client_c.user_id().unwrap(), client_c2.user_id().unwrap());
6229
6230    // User A and User C request that user B become their contact.
6231    client_a
6232        .user_store()
6233        .update(cx_a, |store, cx| {
6234            store.request_contact(client_b.user_id().unwrap(), cx)
6235        })
6236        .await
6237        .unwrap();
6238    client_c
6239        .user_store()
6240        .update(cx_c, |store, cx| {
6241            store.request_contact(client_b.user_id().unwrap(), cx)
6242        })
6243        .await
6244        .unwrap();
6245    deterministic.run_until_parked();
6246
6247    // All users see the pending request appear in all their clients.
6248    assert_eq!(
6249        client_a.summarize_contacts(cx_a).outgoing_requests,
6250        &["user_b"]
6251    );
6252    assert_eq!(
6253        client_a2.summarize_contacts(cx_a2).outgoing_requests,
6254        &["user_b"]
6255    );
6256    assert_eq!(
6257        client_b.summarize_contacts(cx_b).incoming_requests,
6258        &["user_a", "user_c"]
6259    );
6260    assert_eq!(
6261        client_b2.summarize_contacts(cx_b2).incoming_requests,
6262        &["user_a", "user_c"]
6263    );
6264    assert_eq!(
6265        client_c.summarize_contacts(cx_c).outgoing_requests,
6266        &["user_b"]
6267    );
6268    assert_eq!(
6269        client_c2.summarize_contacts(cx_c2).outgoing_requests,
6270        &["user_b"]
6271    );
6272
6273    // Contact requests are present upon connecting (tested here via disconnect/reconnect)
6274    disconnect_and_reconnect(&client_a, cx_a).await;
6275    disconnect_and_reconnect(&client_b, cx_b).await;
6276    disconnect_and_reconnect(&client_c, cx_c).await;
6277    deterministic.run_until_parked();
6278    assert_eq!(
6279        client_a.summarize_contacts(cx_a).outgoing_requests,
6280        &["user_b"]
6281    );
6282    assert_eq!(
6283        client_b.summarize_contacts(cx_b).incoming_requests,
6284        &["user_a", "user_c"]
6285    );
6286    assert_eq!(
6287        client_c.summarize_contacts(cx_c).outgoing_requests,
6288        &["user_b"]
6289    );
6290
6291    // User B accepts the request from user A.
6292    client_b
6293        .user_store()
6294        .update(cx_b, |store, cx| {
6295            store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
6296        })
6297        .await
6298        .unwrap();
6299
6300    deterministic.run_until_parked();
6301
6302    // User B sees user A as their contact now in all client, and the incoming request from them is removed.
6303    let contacts_b = client_b.summarize_contacts(cx_b);
6304    assert_eq!(contacts_b.current, &["user_a"]);
6305    assert_eq!(contacts_b.incoming_requests, &["user_c"]);
6306    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
6307    assert_eq!(contacts_b2.current, &["user_a"]);
6308    assert_eq!(contacts_b2.incoming_requests, &["user_c"]);
6309
6310    // User A sees user B as their contact now in all clients, and the outgoing request to them is removed.
6311    let contacts_a = client_a.summarize_contacts(cx_a);
6312    assert_eq!(contacts_a.current, &["user_b"]);
6313    assert!(contacts_a.outgoing_requests.is_empty());
6314    let contacts_a2 = client_a2.summarize_contacts(cx_a2);
6315    assert_eq!(contacts_a2.current, &["user_b"]);
6316    assert!(contacts_a2.outgoing_requests.is_empty());
6317
6318    // Contacts are present upon connecting (tested here via disconnect/reconnect)
6319    disconnect_and_reconnect(&client_a, cx_a).await;
6320    disconnect_and_reconnect(&client_b, cx_b).await;
6321    disconnect_and_reconnect(&client_c, cx_c).await;
6322    deterministic.run_until_parked();
6323    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
6324    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
6325    assert_eq!(
6326        client_b.summarize_contacts(cx_b).incoming_requests,
6327        &["user_c"]
6328    );
6329    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
6330    assert_eq!(
6331        client_c.summarize_contacts(cx_c).outgoing_requests,
6332        &["user_b"]
6333    );
6334
6335    // User B rejects the request from user C.
6336    client_b
6337        .user_store()
6338        .update(cx_b, |store, cx| {
6339            store.respond_to_contact_request(client_c.user_id().unwrap(), false, cx)
6340        })
6341        .await
6342        .unwrap();
6343
6344    deterministic.run_until_parked();
6345
6346    // User B doesn't see user C as their contact, and the incoming request from them is removed.
6347    let contacts_b = client_b.summarize_contacts(cx_b);
6348    assert_eq!(contacts_b.current, &["user_a"]);
6349    assert!(contacts_b.incoming_requests.is_empty());
6350    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
6351    assert_eq!(contacts_b2.current, &["user_a"]);
6352    assert!(contacts_b2.incoming_requests.is_empty());
6353
6354    // User C doesn't see user B as their contact, and the outgoing request to them is removed.
6355    let contacts_c = client_c.summarize_contacts(cx_c);
6356    assert!(contacts_c.current.is_empty());
6357    assert!(contacts_c.outgoing_requests.is_empty());
6358    let contacts_c2 = client_c2.summarize_contacts(cx_c2);
6359    assert!(contacts_c2.current.is_empty());
6360    assert!(contacts_c2.outgoing_requests.is_empty());
6361
6362    // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect)
6363    disconnect_and_reconnect(&client_a, cx_a).await;
6364    disconnect_and_reconnect(&client_b, cx_b).await;
6365    disconnect_and_reconnect(&client_c, cx_c).await;
6366    deterministic.run_until_parked();
6367    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
6368    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
6369    assert!(client_b
6370        .summarize_contacts(cx_b)
6371        .incoming_requests
6372        .is_empty());
6373    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
6374    assert!(client_c
6375        .summarize_contacts(cx_c)
6376        .outgoing_requests
6377        .is_empty());
6378
6379    async fn disconnect_and_reconnect(client: &TestClient, cx: &mut TestAppContext) {
6380        client.disconnect(&cx.to_async());
6381        client.clear_contacts(cx).await;
6382        client
6383            .authenticate_and_connect(false, &cx.to_async())
6384            .await
6385            .unwrap();
6386    }
6387}
6388
6389#[gpui::test(iterations = 10)]
6390async fn test_basic_following(
6391    deterministic: Arc<Deterministic>,
6392    cx_a: &mut TestAppContext,
6393    cx_b: &mut TestAppContext,
6394    cx_c: &mut TestAppContext,
6395    cx_d: &mut TestAppContext,
6396) {
6397    deterministic.forbid_parking();
6398
6399    let mut server = TestServer::start(&deterministic).await;
6400    let client_a = server.create_client(cx_a, "user_a").await;
6401    let client_b = server.create_client(cx_b, "user_b").await;
6402    let client_c = server.create_client(cx_c, "user_c").await;
6403    let client_d = server.create_client(cx_d, "user_d").await;
6404    server
6405        .create_room(&mut [
6406            (&client_a, cx_a),
6407            (&client_b, cx_b),
6408            (&client_c, cx_c),
6409            (&client_d, cx_d),
6410        ])
6411        .await;
6412    let active_call_a = cx_a.read(ActiveCall::global);
6413    let active_call_b = cx_b.read(ActiveCall::global);
6414
6415    cx_a.update(editor::init);
6416    cx_b.update(editor::init);
6417
6418    client_a
6419        .fs()
6420        .insert_tree(
6421            "/a",
6422            json!({
6423                "1.txt": "one\none\none",
6424                "2.txt": "two\ntwo\ntwo",
6425                "3.txt": "three\nthree\nthree",
6426            }),
6427        )
6428        .await;
6429    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
6430    active_call_a
6431        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
6432        .await
6433        .unwrap();
6434
6435    let project_id = active_call_a
6436        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6437        .await
6438        .unwrap();
6439    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6440    active_call_b
6441        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6442        .await
6443        .unwrap();
6444
6445    let window_a = client_a.build_workspace(&project_a, cx_a);
6446    let workspace_a = window_a.root(cx_a);
6447    let window_b = client_b.build_workspace(&project_b, cx_b);
6448    let workspace_b = window_b.root(cx_b);
6449
6450    // Client A opens some editors.
6451    let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
6452    let editor_a1 = workspace_a
6453        .update(cx_a, |workspace, cx| {
6454            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6455        })
6456        .await
6457        .unwrap()
6458        .downcast::<Editor>()
6459        .unwrap();
6460    let editor_a2 = workspace_a
6461        .update(cx_a, |workspace, cx| {
6462            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
6463        })
6464        .await
6465        .unwrap()
6466        .downcast::<Editor>()
6467        .unwrap();
6468
6469    // Client B opens an editor.
6470    let editor_b1 = workspace_b
6471        .update(cx_b, |workspace, cx| {
6472            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6473        })
6474        .await
6475        .unwrap()
6476        .downcast::<Editor>()
6477        .unwrap();
6478
6479    let peer_id_a = client_a.peer_id().unwrap();
6480    let peer_id_b = client_b.peer_id().unwrap();
6481    let peer_id_c = client_c.peer_id().unwrap();
6482    let peer_id_d = client_d.peer_id().unwrap();
6483
6484    // Client A updates their selections in those editors
6485    editor_a1.update(cx_a, |editor, cx| {
6486        editor.handle_input("a", cx);
6487        editor.handle_input("b", cx);
6488        editor.handle_input("c", cx);
6489        editor.select_left(&Default::default(), cx);
6490        assert_eq!(editor.selections.ranges(cx), vec![3..2]);
6491    });
6492    editor_a2.update(cx_a, |editor, cx| {
6493        editor.handle_input("d", cx);
6494        editor.handle_input("e", cx);
6495        editor.select_left(&Default::default(), cx);
6496        assert_eq!(editor.selections.ranges(cx), vec![2..1]);
6497    });
6498
6499    // When client B starts following client A, all visible view states are replicated to client B.
6500    workspace_b
6501        .update(cx_b, |workspace, cx| {
6502            workspace.toggle_follow(peer_id_a, cx).unwrap()
6503        })
6504        .await
6505        .unwrap();
6506
6507    cx_c.foreground().run_until_parked();
6508    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
6509        workspace
6510            .active_item(cx)
6511            .unwrap()
6512            .downcast::<Editor>()
6513            .unwrap()
6514    });
6515    assert_eq!(
6516        cx_b.read(|cx| editor_b2.project_path(cx)),
6517        Some((worktree_id, "2.txt").into())
6518    );
6519    assert_eq!(
6520        editor_b2.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
6521        vec![2..1]
6522    );
6523    assert_eq!(
6524        editor_b1.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
6525        vec![3..2]
6526    );
6527
6528    cx_c.foreground().run_until_parked();
6529    let active_call_c = cx_c.read(ActiveCall::global);
6530    let project_c = client_c.build_remote_project(project_id, cx_c).await;
6531    let window_c = client_c.build_workspace(&project_c, cx_c);
6532    let workspace_c = window_c.root(cx_c);
6533    active_call_c
6534        .update(cx_c, |call, cx| call.set_location(Some(&project_c), cx))
6535        .await
6536        .unwrap();
6537    drop(project_c);
6538
6539    // Client C also follows client A.
6540    workspace_c
6541        .update(cx_c, |workspace, cx| {
6542            workspace.toggle_follow(peer_id_a, cx).unwrap()
6543        })
6544        .await
6545        .unwrap();
6546
6547    cx_d.foreground().run_until_parked();
6548    let active_call_d = cx_d.read(ActiveCall::global);
6549    let project_d = client_d.build_remote_project(project_id, cx_d).await;
6550    let workspace_d = client_d.build_workspace(&project_d, cx_d).root(cx_d);
6551    active_call_d
6552        .update(cx_d, |call, cx| call.set_location(Some(&project_d), cx))
6553        .await
6554        .unwrap();
6555    drop(project_d);
6556
6557    // All clients see that clients B and C are following client A.
6558    cx_c.foreground().run_until_parked();
6559    for (name, active_call, cx) in [
6560        ("A", &active_call_a, &cx_a),
6561        ("B", &active_call_b, &cx_b),
6562        ("C", &active_call_c, &cx_c),
6563        ("D", &active_call_d, &cx_d),
6564    ] {
6565        active_call.read_with(*cx, |call, cx| {
6566            let room = call.room().unwrap().read(cx);
6567            assert_eq!(
6568                room.followers_for(peer_id_a, project_id),
6569                &[peer_id_b, peer_id_c],
6570                "checking followers for A as {name}"
6571            );
6572        });
6573    }
6574
6575    // Client C unfollows client A.
6576    workspace_c.update(cx_c, |workspace, cx| {
6577        workspace.toggle_follow(peer_id_a, cx);
6578    });
6579
6580    // All clients see that clients B is following client A.
6581    cx_c.foreground().run_until_parked();
6582    for (name, active_call, cx) in [
6583        ("A", &active_call_a, &cx_a),
6584        ("B", &active_call_b, &cx_b),
6585        ("C", &active_call_c, &cx_c),
6586        ("D", &active_call_d, &cx_d),
6587    ] {
6588        active_call.read_with(*cx, |call, cx| {
6589            let room = call.room().unwrap().read(cx);
6590            assert_eq!(
6591                room.followers_for(peer_id_a, project_id),
6592                &[peer_id_b],
6593                "checking followers for A as {name}"
6594            );
6595        });
6596    }
6597
6598    // Client C re-follows client A.
6599    workspace_c.update(cx_c, |workspace, cx| {
6600        workspace.toggle_follow(peer_id_a, cx);
6601    });
6602
6603    // All clients see that clients B and C are following client A.
6604    cx_c.foreground().run_until_parked();
6605    for (name, active_call, cx) in [
6606        ("A", &active_call_a, &cx_a),
6607        ("B", &active_call_b, &cx_b),
6608        ("C", &active_call_c, &cx_c),
6609        ("D", &active_call_d, &cx_d),
6610    ] {
6611        active_call.read_with(*cx, |call, cx| {
6612            let room = call.room().unwrap().read(cx);
6613            assert_eq!(
6614                room.followers_for(peer_id_a, project_id),
6615                &[peer_id_b, peer_id_c],
6616                "checking followers for A as {name}"
6617            );
6618        });
6619    }
6620
6621    // Client D follows client C.
6622    workspace_d
6623        .update(cx_d, |workspace, cx| {
6624            workspace.toggle_follow(peer_id_c, cx).unwrap()
6625        })
6626        .await
6627        .unwrap();
6628
6629    // All clients see that D is following C
6630    cx_d.foreground().run_until_parked();
6631    for (name, active_call, cx) in [
6632        ("A", &active_call_a, &cx_a),
6633        ("B", &active_call_b, &cx_b),
6634        ("C", &active_call_c, &cx_c),
6635        ("D", &active_call_d, &cx_d),
6636    ] {
6637        active_call.read_with(*cx, |call, cx| {
6638            let room = call.room().unwrap().read(cx);
6639            assert_eq!(
6640                room.followers_for(peer_id_c, project_id),
6641                &[peer_id_d],
6642                "checking followers for C as {name}"
6643            );
6644        });
6645    }
6646
6647    // Client C closes the project.
6648    window_c.remove(cx_c);
6649    cx_c.drop_last(workspace_c);
6650
6651    // Clients A and B see that client B is following A, and client C is not present in the followers.
6652    cx_c.foreground().run_until_parked();
6653    for (name, active_call, cx) in [("A", &active_call_a, &cx_a), ("B", &active_call_b, &cx_b)] {
6654        active_call.read_with(*cx, |call, cx| {
6655            let room = call.room().unwrap().read(cx);
6656            assert_eq!(
6657                room.followers_for(peer_id_a, project_id),
6658                &[peer_id_b],
6659                "checking followers for A as {name}"
6660            );
6661        });
6662    }
6663
6664    // All clients see that no-one is following C
6665    for (name, active_call, cx) in [
6666        ("A", &active_call_a, &cx_a),
6667        ("B", &active_call_b, &cx_b),
6668        ("C", &active_call_c, &cx_c),
6669        ("D", &active_call_d, &cx_d),
6670    ] {
6671        active_call.read_with(*cx, |call, cx| {
6672            let room = call.room().unwrap().read(cx);
6673            assert_eq!(
6674                room.followers_for(peer_id_c, project_id),
6675                &[],
6676                "checking followers for C as {name}"
6677            );
6678        });
6679    }
6680
6681    // When client A activates a different editor, client B does so as well.
6682    workspace_a.update(cx_a, |workspace, cx| {
6683        workspace.activate_item(&editor_a1, cx)
6684    });
6685    deterministic.run_until_parked();
6686    workspace_b.read_with(cx_b, |workspace, cx| {
6687        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
6688    });
6689
6690    // When client A opens a multibuffer, client B does so as well.
6691    let multibuffer_a = cx_a.add_model(|cx| {
6692        let buffer_a1 = project_a.update(cx, |project, cx| {
6693            project
6694                .get_open_buffer(&(worktree_id, "1.txt").into(), cx)
6695                .unwrap()
6696        });
6697        let buffer_a2 = project_a.update(cx, |project, cx| {
6698            project
6699                .get_open_buffer(&(worktree_id, "2.txt").into(), cx)
6700                .unwrap()
6701        });
6702        let mut result = MultiBuffer::new(0);
6703        result.push_excerpts(
6704            buffer_a1,
6705            [ExcerptRange {
6706                context: 0..3,
6707                primary: None,
6708            }],
6709            cx,
6710        );
6711        result.push_excerpts(
6712            buffer_a2,
6713            [ExcerptRange {
6714                context: 4..7,
6715                primary: None,
6716            }],
6717            cx,
6718        );
6719        result
6720    });
6721    let multibuffer_editor_a = workspace_a.update(cx_a, |workspace, cx| {
6722        let editor =
6723            cx.add_view(|cx| Editor::for_multibuffer(multibuffer_a, Some(project_a.clone()), cx));
6724        workspace.add_item(Box::new(editor.clone()), cx);
6725        editor
6726    });
6727    deterministic.run_until_parked();
6728    let multibuffer_editor_b = workspace_b.read_with(cx_b, |workspace, cx| {
6729        workspace
6730            .active_item(cx)
6731            .unwrap()
6732            .downcast::<Editor>()
6733            .unwrap()
6734    });
6735    assert_eq!(
6736        multibuffer_editor_a.read_with(cx_a, |editor, cx| editor.text(cx)),
6737        multibuffer_editor_b.read_with(cx_b, |editor, cx| editor.text(cx)),
6738    );
6739
6740    // When client A navigates back and forth, client B does so as well.
6741    workspace_a
6742        .update(cx_a, |workspace, cx| {
6743            workspace.go_back(workspace.active_pane().downgrade(), cx)
6744        })
6745        .await
6746        .unwrap();
6747    deterministic.run_until_parked();
6748    workspace_b.read_with(cx_b, |workspace, cx| {
6749        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
6750    });
6751
6752    workspace_a
6753        .update(cx_a, |workspace, cx| {
6754            workspace.go_back(workspace.active_pane().downgrade(), cx)
6755        })
6756        .await
6757        .unwrap();
6758    deterministic.run_until_parked();
6759    workspace_b.read_with(cx_b, |workspace, cx| {
6760        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b2.id());
6761    });
6762
6763    workspace_a
6764        .update(cx_a, |workspace, cx| {
6765            workspace.go_forward(workspace.active_pane().downgrade(), cx)
6766        })
6767        .await
6768        .unwrap();
6769    deterministic.run_until_parked();
6770    workspace_b.read_with(cx_b, |workspace, cx| {
6771        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
6772    });
6773
6774    // Changes to client A's editor are reflected on client B.
6775    editor_a1.update(cx_a, |editor, cx| {
6776        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2]));
6777    });
6778    deterministic.run_until_parked();
6779    editor_b1.read_with(cx_b, |editor, cx| {
6780        assert_eq!(editor.selections.ranges(cx), &[1..1, 2..2]);
6781    });
6782
6783    editor_a1.update(cx_a, |editor, cx| editor.set_text("TWO", cx));
6784    deterministic.run_until_parked();
6785    editor_b1.read_with(cx_b, |editor, cx| assert_eq!(editor.text(cx), "TWO"));
6786
6787    editor_a1.update(cx_a, |editor, cx| {
6788        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
6789        editor.set_scroll_position(vec2f(0., 100.), cx);
6790    });
6791    deterministic.run_until_parked();
6792    editor_b1.read_with(cx_b, |editor, cx| {
6793        assert_eq!(editor.selections.ranges(cx), &[3..3]);
6794    });
6795
6796    // After unfollowing, client B stops receiving updates from client A.
6797    workspace_b.update(cx_b, |workspace, cx| {
6798        workspace.unfollow(&workspace.active_pane().clone(), cx)
6799    });
6800    workspace_a.update(cx_a, |workspace, cx| {
6801        workspace.activate_item(&editor_a2, cx)
6802    });
6803    deterministic.run_until_parked();
6804    assert_eq!(
6805        workspace_b.read_with(cx_b, |workspace, cx| workspace
6806            .active_item(cx)
6807            .unwrap()
6808            .id()),
6809        editor_b1.id()
6810    );
6811
6812    // Client A starts following client B.
6813    workspace_a
6814        .update(cx_a, |workspace, cx| {
6815            workspace.toggle_follow(peer_id_b, cx).unwrap()
6816        })
6817        .await
6818        .unwrap();
6819    assert_eq!(
6820        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
6821        Some(peer_id_b)
6822    );
6823    assert_eq!(
6824        workspace_a.read_with(cx_a, |workspace, cx| workspace
6825            .active_item(cx)
6826            .unwrap()
6827            .id()),
6828        editor_a1.id()
6829    );
6830
6831    // Client B activates an external window, which causes a new screen-sharing item to be added to the pane.
6832    let display = MacOSDisplay::new();
6833    active_call_b
6834        .update(cx_b, |call, cx| call.set_location(None, cx))
6835        .await
6836        .unwrap();
6837    active_call_b
6838        .update(cx_b, |call, cx| {
6839            call.room().unwrap().update(cx, |room, cx| {
6840                room.set_display_sources(vec![display.clone()]);
6841                room.share_screen(cx)
6842            })
6843        })
6844        .await
6845        .unwrap();
6846    deterministic.run_until_parked();
6847    let shared_screen = workspace_a.read_with(cx_a, |workspace, cx| {
6848        workspace
6849            .active_item(cx)
6850            .unwrap()
6851            .downcast::<SharedScreen>()
6852            .unwrap()
6853    });
6854
6855    // Client B activates Zed again, which causes the previous editor to become focused again.
6856    active_call_b
6857        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6858        .await
6859        .unwrap();
6860    deterministic.run_until_parked();
6861    workspace_a.read_with(cx_a, |workspace, cx| {
6862        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_a1.id())
6863    });
6864
6865    // Client B activates a multibuffer that was created by following client A. Client A returns to that multibuffer.
6866    workspace_b.update(cx_b, |workspace, cx| {
6867        workspace.activate_item(&multibuffer_editor_b, cx)
6868    });
6869    deterministic.run_until_parked();
6870    workspace_a.read_with(cx_a, |workspace, cx| {
6871        assert_eq!(
6872            workspace.active_item(cx).unwrap().id(),
6873            multibuffer_editor_a.id()
6874        )
6875    });
6876
6877    // Client B activates a panel, and the previously-opened screen-sharing item gets activated.
6878    let panel = window_b.add_view(cx_b, |_| TestPanel::new(DockPosition::Left));
6879    workspace_b.update(cx_b, |workspace, cx| {
6880        workspace.add_panel(panel, cx);
6881        workspace.toggle_panel_focus::<TestPanel>(cx);
6882    });
6883    deterministic.run_until_parked();
6884    assert_eq!(
6885        workspace_a.read_with(cx_a, |workspace, cx| workspace
6886            .active_item(cx)
6887            .unwrap()
6888            .id()),
6889        shared_screen.id()
6890    );
6891
6892    // Toggling the focus back to the pane causes client A to return to the multibuffer.
6893    workspace_b.update(cx_b, |workspace, cx| {
6894        workspace.toggle_panel_focus::<TestPanel>(cx);
6895    });
6896    deterministic.run_until_parked();
6897    workspace_a.read_with(cx_a, |workspace, cx| {
6898        assert_eq!(
6899            workspace.active_item(cx).unwrap().id(),
6900            multibuffer_editor_a.id()
6901        )
6902    });
6903
6904    // Client B activates an item that doesn't implement following,
6905    // so the previously-opened screen-sharing item gets activated.
6906    let unfollowable_item = window_b.add_view(cx_b, |_| TestItem::new());
6907    workspace_b.update(cx_b, |workspace, cx| {
6908        workspace.active_pane().update(cx, |pane, cx| {
6909            pane.add_item(Box::new(unfollowable_item), true, true, None, cx)
6910        })
6911    });
6912    deterministic.run_until_parked();
6913    assert_eq!(
6914        workspace_a.read_with(cx_a, |workspace, cx| workspace
6915            .active_item(cx)
6916            .unwrap()
6917            .id()),
6918        shared_screen.id()
6919    );
6920
6921    // Following interrupts when client B disconnects.
6922    client_b.disconnect(&cx_b.to_async());
6923    deterministic.advance_clock(RECONNECT_TIMEOUT);
6924    assert_eq!(
6925        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
6926        None
6927    );
6928}
6929
6930#[gpui::test(iterations = 10)]
6931async fn test_join_call_after_screen_was_shared(
6932    deterministic: Arc<Deterministic>,
6933    cx_a: &mut TestAppContext,
6934    cx_b: &mut TestAppContext,
6935) {
6936    deterministic.forbid_parking();
6937    let mut server = TestServer::start(&deterministic).await;
6938
6939    let client_a = server.create_client(cx_a, "user_a").await;
6940    let client_b = server.create_client(cx_b, "user_b").await;
6941    server
6942        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6943        .await;
6944
6945    let active_call_a = cx_a.read(ActiveCall::global);
6946    let active_call_b = cx_b.read(ActiveCall::global);
6947
6948    // Call users B and C from client A.
6949    active_call_a
6950        .update(cx_a, |call, cx| {
6951            call.invite(client_b.user_id().unwrap(), None, cx)
6952        })
6953        .await
6954        .unwrap();
6955    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
6956    deterministic.run_until_parked();
6957    assert_eq!(
6958        room_participants(&room_a, cx_a),
6959        RoomParticipants {
6960            remote: Default::default(),
6961            pending: vec!["user_b".to_string()]
6962        }
6963    );
6964
6965    // User B receives the call.
6966    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
6967    let call_b = incoming_call_b.next().await.unwrap().unwrap();
6968    assert_eq!(call_b.calling_user.github_login, "user_a");
6969
6970    // User A shares their screen
6971    let display = MacOSDisplay::new();
6972    active_call_a
6973        .update(cx_a, |call, cx| {
6974            call.room().unwrap().update(cx, |room, cx| {
6975                room.set_display_sources(vec![display.clone()]);
6976                room.share_screen(cx)
6977            })
6978        })
6979        .await
6980        .unwrap();
6981
6982    client_b.user_store().update(cx_b, |user_store, _| {
6983        user_store.clear_cache();
6984    });
6985
6986    // User B joins the room
6987    active_call_b
6988        .update(cx_b, |call, cx| call.accept_incoming(cx))
6989        .await
6990        .unwrap();
6991    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
6992    assert!(incoming_call_b.next().await.unwrap().is_none());
6993
6994    deterministic.run_until_parked();
6995    assert_eq!(
6996        room_participants(&room_a, cx_a),
6997        RoomParticipants {
6998            remote: vec!["user_b".to_string()],
6999            pending: vec![],
7000        }
7001    );
7002    assert_eq!(
7003        room_participants(&room_b, cx_b),
7004        RoomParticipants {
7005            remote: vec!["user_a".to_string()],
7006            pending: vec![],
7007        }
7008    );
7009
7010    // Ensure User B sees User A's screenshare.
7011    room_b.read_with(cx_b, |room, _| {
7012        assert_eq!(
7013            room.remote_participants()
7014                .get(&client_a.user_id().unwrap())
7015                .unwrap()
7016                .video_tracks
7017                .len(),
7018            1
7019        );
7020    });
7021}
7022
7023#[gpui::test]
7024async fn test_following_tab_order(
7025    deterministic: Arc<Deterministic>,
7026    cx_a: &mut TestAppContext,
7027    cx_b: &mut TestAppContext,
7028) {
7029    let mut server = TestServer::start(&deterministic).await;
7030    let client_a = server.create_client(cx_a, "user_a").await;
7031    let client_b = server.create_client(cx_b, "user_b").await;
7032    server
7033        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7034        .await;
7035    let active_call_a = cx_a.read(ActiveCall::global);
7036    let active_call_b = cx_b.read(ActiveCall::global);
7037
7038    cx_a.update(editor::init);
7039    cx_b.update(editor::init);
7040
7041    client_a
7042        .fs()
7043        .insert_tree(
7044            "/a",
7045            json!({
7046                "1.txt": "one",
7047                "2.txt": "two",
7048                "3.txt": "three",
7049            }),
7050        )
7051        .await;
7052    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7053    active_call_a
7054        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
7055        .await
7056        .unwrap();
7057
7058    let project_id = active_call_a
7059        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7060        .await
7061        .unwrap();
7062    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7063    active_call_b
7064        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
7065        .await
7066        .unwrap();
7067
7068    let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
7069    let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
7070
7071    let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
7072    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
7073
7074    let client_b_id = project_a.read_with(cx_a, |project, _| {
7075        project.collaborators().values().next().unwrap().peer_id
7076    });
7077
7078    //Open 1, 3 in that order on client A
7079    workspace_a
7080        .update(cx_a, |workspace, cx| {
7081            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
7082        })
7083        .await
7084        .unwrap();
7085    workspace_a
7086        .update(cx_a, |workspace, cx| {
7087            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
7088        })
7089        .await
7090        .unwrap();
7091
7092    let pane_paths = |pane: &ViewHandle<workspace::Pane>, cx: &mut TestAppContext| {
7093        pane.update(cx, |pane, cx| {
7094            pane.items()
7095                .map(|item| {
7096                    item.project_path(cx)
7097                        .unwrap()
7098                        .path
7099                        .to_str()
7100                        .unwrap()
7101                        .to_owned()
7102                })
7103                .collect::<Vec<_>>()
7104        })
7105    };
7106
7107    //Verify that the tabs opened in the order we expect
7108    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt"]);
7109
7110    //Follow client B as client A
7111    workspace_a
7112        .update(cx_a, |workspace, cx| {
7113            workspace.toggle_follow(client_b_id, cx).unwrap()
7114        })
7115        .await
7116        .unwrap();
7117
7118    //Open just 2 on client B
7119    workspace_b
7120        .update(cx_b, |workspace, cx| {
7121            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
7122        })
7123        .await
7124        .unwrap();
7125    deterministic.run_until_parked();
7126
7127    // Verify that newly opened followed file is at the end
7128    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt", "2.txt"]);
7129
7130    //Open just 1 on client B
7131    workspace_b
7132        .update(cx_b, |workspace, cx| {
7133            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
7134        })
7135        .await
7136        .unwrap();
7137    assert_eq!(&pane_paths(&pane_b, cx_b), &["2.txt", "1.txt"]);
7138    deterministic.run_until_parked();
7139
7140    // Verify that following into 1 did not reorder
7141    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt", "2.txt"]);
7142}
7143
7144#[gpui::test(iterations = 10)]
7145async fn test_peers_following_each_other(
7146    deterministic: Arc<Deterministic>,
7147    cx_a: &mut TestAppContext,
7148    cx_b: &mut TestAppContext,
7149) {
7150    deterministic.forbid_parking();
7151    let mut server = TestServer::start(&deterministic).await;
7152    let client_a = server.create_client(cx_a, "user_a").await;
7153    let client_b = server.create_client(cx_b, "user_b").await;
7154    server
7155        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7156        .await;
7157    let active_call_a = cx_a.read(ActiveCall::global);
7158    let active_call_b = cx_b.read(ActiveCall::global);
7159
7160    cx_a.update(editor::init);
7161    cx_b.update(editor::init);
7162
7163    // Client A shares a project.
7164    client_a
7165        .fs()
7166        .insert_tree(
7167            "/a",
7168            json!({
7169                "1.txt": "one",
7170                "2.txt": "two",
7171                "3.txt": "three",
7172                "4.txt": "four",
7173            }),
7174        )
7175        .await;
7176    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7177    active_call_a
7178        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
7179        .await
7180        .unwrap();
7181    let project_id = active_call_a
7182        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7183        .await
7184        .unwrap();
7185
7186    // Client B joins the project.
7187    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7188    active_call_b
7189        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
7190        .await
7191        .unwrap();
7192
7193    // Client A opens some editors.
7194    let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
7195    let pane_a1 = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
7196    let _editor_a1 = workspace_a
7197        .update(cx_a, |workspace, cx| {
7198            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
7199        })
7200        .await
7201        .unwrap()
7202        .downcast::<Editor>()
7203        .unwrap();
7204
7205    // Client B opens an editor.
7206    let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
7207    let pane_b1 = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
7208    let _editor_b1 = workspace_b
7209        .update(cx_b, |workspace, cx| {
7210            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
7211        })
7212        .await
7213        .unwrap()
7214        .downcast::<Editor>()
7215        .unwrap();
7216
7217    // Clients A and B follow each other in split panes
7218    workspace_a.update(cx_a, |workspace, cx| {
7219        workspace.split_and_clone(workspace.active_pane().clone(), SplitDirection::Right, cx);
7220    });
7221    workspace_a
7222        .update(cx_a, |workspace, cx| {
7223            assert_ne!(*workspace.active_pane(), pane_a1);
7224            let leader_id = *project_a.read(cx).collaborators().keys().next().unwrap();
7225            workspace.toggle_follow(leader_id, cx).unwrap()
7226        })
7227        .await
7228        .unwrap();
7229    workspace_b.update(cx_b, |workspace, cx| {
7230        workspace.split_and_clone(workspace.active_pane().clone(), SplitDirection::Right, cx);
7231    });
7232    workspace_b
7233        .update(cx_b, |workspace, cx| {
7234            assert_ne!(*workspace.active_pane(), pane_b1);
7235            let leader_id = *project_b.read(cx).collaborators().keys().next().unwrap();
7236            workspace.toggle_follow(leader_id, cx).unwrap()
7237        })
7238        .await
7239        .unwrap();
7240
7241    workspace_a.update(cx_a, |workspace, cx| {
7242        workspace.activate_next_pane(cx);
7243    });
7244    // Wait for focus effects to be fully flushed
7245    workspace_a.update(cx_a, |workspace, _| {
7246        assert_eq!(*workspace.active_pane(), pane_a1);
7247    });
7248
7249    workspace_a
7250        .update(cx_a, |workspace, cx| {
7251            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
7252        })
7253        .await
7254        .unwrap();
7255    workspace_b.update(cx_b, |workspace, cx| {
7256        workspace.activate_next_pane(cx);
7257    });
7258
7259    workspace_b
7260        .update(cx_b, |workspace, cx| {
7261            assert_eq!(*workspace.active_pane(), pane_b1);
7262            workspace.open_path((worktree_id, "4.txt"), None, true, cx)
7263        })
7264        .await
7265        .unwrap();
7266    cx_a.foreground().run_until_parked();
7267
7268    // Ensure leader updates don't change the active pane of followers
7269    workspace_a.read_with(cx_a, |workspace, _| {
7270        assert_eq!(*workspace.active_pane(), pane_a1);
7271    });
7272    workspace_b.read_with(cx_b, |workspace, _| {
7273        assert_eq!(*workspace.active_pane(), pane_b1);
7274    });
7275
7276    // Ensure peers following each other doesn't cause an infinite loop.
7277    assert_eq!(
7278        workspace_a.read_with(cx_a, |workspace, cx| workspace
7279            .active_item(cx)
7280            .unwrap()
7281            .project_path(cx)),
7282        Some((worktree_id, "3.txt").into())
7283    );
7284    workspace_a.update(cx_a, |workspace, cx| {
7285        assert_eq!(
7286            workspace.active_item(cx).unwrap().project_path(cx),
7287            Some((worktree_id, "3.txt").into())
7288        );
7289        workspace.activate_next_pane(cx);
7290    });
7291
7292    workspace_a.update(cx_a, |workspace, cx| {
7293        assert_eq!(
7294            workspace.active_item(cx).unwrap().project_path(cx),
7295            Some((worktree_id, "4.txt").into())
7296        );
7297    });
7298
7299    workspace_b.update(cx_b, |workspace, cx| {
7300        assert_eq!(
7301            workspace.active_item(cx).unwrap().project_path(cx),
7302            Some((worktree_id, "4.txt").into())
7303        );
7304        workspace.activate_next_pane(cx);
7305    });
7306
7307    workspace_b.update(cx_b, |workspace, cx| {
7308        assert_eq!(
7309            workspace.active_item(cx).unwrap().project_path(cx),
7310            Some((worktree_id, "3.txt").into())
7311        );
7312    });
7313}
7314
7315#[gpui::test(iterations = 10)]
7316async fn test_auto_unfollowing(
7317    deterministic: Arc<Deterministic>,
7318    cx_a: &mut TestAppContext,
7319    cx_b: &mut TestAppContext,
7320) {
7321    deterministic.forbid_parking();
7322
7323    // 2 clients connect to a server.
7324    let mut server = TestServer::start(&deterministic).await;
7325    let client_a = server.create_client(cx_a, "user_a").await;
7326    let client_b = server.create_client(cx_b, "user_b").await;
7327    server
7328        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7329        .await;
7330    let active_call_a = cx_a.read(ActiveCall::global);
7331    let active_call_b = cx_b.read(ActiveCall::global);
7332
7333    cx_a.update(editor::init);
7334    cx_b.update(editor::init);
7335
7336    // Client A shares a project.
7337    client_a
7338        .fs()
7339        .insert_tree(
7340            "/a",
7341            json!({
7342                "1.txt": "one",
7343                "2.txt": "two",
7344                "3.txt": "three",
7345            }),
7346        )
7347        .await;
7348    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7349    active_call_a
7350        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
7351        .await
7352        .unwrap();
7353
7354    let project_id = active_call_a
7355        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7356        .await
7357        .unwrap();
7358    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7359    active_call_b
7360        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
7361        .await
7362        .unwrap();
7363
7364    // Client A opens some editors.
7365    let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
7366    let _editor_a1 = workspace_a
7367        .update(cx_a, |workspace, cx| {
7368            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
7369        })
7370        .await
7371        .unwrap()
7372        .downcast::<Editor>()
7373        .unwrap();
7374
7375    // Client B starts following client A.
7376    let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
7377    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
7378    let leader_id = project_b.read_with(cx_b, |project, _| {
7379        project.collaborators().values().next().unwrap().peer_id
7380    });
7381    workspace_b
7382        .update(cx_b, |workspace, cx| {
7383            workspace.toggle_follow(leader_id, cx).unwrap()
7384        })
7385        .await
7386        .unwrap();
7387    assert_eq!(
7388        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7389        Some(leader_id)
7390    );
7391    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
7392        workspace
7393            .active_item(cx)
7394            .unwrap()
7395            .downcast::<Editor>()
7396            .unwrap()
7397    });
7398
7399    // When client B moves, it automatically stops following client A.
7400    editor_b2.update(cx_b, |editor, cx| editor.move_right(&editor::MoveRight, cx));
7401    assert_eq!(
7402        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7403        None
7404    );
7405
7406    workspace_b
7407        .update(cx_b, |workspace, cx| {
7408            workspace.toggle_follow(leader_id, cx).unwrap()
7409        })
7410        .await
7411        .unwrap();
7412    assert_eq!(
7413        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7414        Some(leader_id)
7415    );
7416
7417    // When client B edits, it automatically stops following client A.
7418    editor_b2.update(cx_b, |editor, cx| editor.insert("X", cx));
7419    assert_eq!(
7420        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7421        None
7422    );
7423
7424    workspace_b
7425        .update(cx_b, |workspace, cx| {
7426            workspace.toggle_follow(leader_id, cx).unwrap()
7427        })
7428        .await
7429        .unwrap();
7430    assert_eq!(
7431        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7432        Some(leader_id)
7433    );
7434
7435    // When client B scrolls, it automatically stops following client A.
7436    editor_b2.update(cx_b, |editor, cx| {
7437        editor.set_scroll_position(vec2f(0., 3.), cx)
7438    });
7439    assert_eq!(
7440        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7441        None
7442    );
7443
7444    workspace_b
7445        .update(cx_b, |workspace, cx| {
7446            workspace.toggle_follow(leader_id, cx).unwrap()
7447        })
7448        .await
7449        .unwrap();
7450    assert_eq!(
7451        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7452        Some(leader_id)
7453    );
7454
7455    // When client B activates a different pane, it continues following client A in the original pane.
7456    workspace_b.update(cx_b, |workspace, cx| {
7457        workspace.split_and_clone(pane_b.clone(), SplitDirection::Right, cx)
7458    });
7459    assert_eq!(
7460        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7461        Some(leader_id)
7462    );
7463
7464    workspace_b.update(cx_b, |workspace, cx| workspace.activate_next_pane(cx));
7465    assert_eq!(
7466        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7467        Some(leader_id)
7468    );
7469
7470    // When client B activates a different item in the original pane, it automatically stops following client A.
7471    workspace_b
7472        .update(cx_b, |workspace, cx| {
7473            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
7474        })
7475        .await
7476        .unwrap();
7477    assert_eq!(
7478        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7479        None
7480    );
7481}
7482
7483#[gpui::test(iterations = 10)]
7484async fn test_peers_simultaneously_following_each_other(
7485    deterministic: Arc<Deterministic>,
7486    cx_a: &mut TestAppContext,
7487    cx_b: &mut TestAppContext,
7488) {
7489    deterministic.forbid_parking();
7490
7491    let mut server = TestServer::start(&deterministic).await;
7492    let client_a = server.create_client(cx_a, "user_a").await;
7493    let client_b = server.create_client(cx_b, "user_b").await;
7494    server
7495        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7496        .await;
7497    let active_call_a = cx_a.read(ActiveCall::global);
7498
7499    cx_a.update(editor::init);
7500    cx_b.update(editor::init);
7501
7502    client_a.fs().insert_tree("/a", json!({})).await;
7503    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
7504    let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
7505    let project_id = active_call_a
7506        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7507        .await
7508        .unwrap();
7509
7510    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7511    let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
7512
7513    deterministic.run_until_parked();
7514    let client_a_id = project_b.read_with(cx_b, |project, _| {
7515        project.collaborators().values().next().unwrap().peer_id
7516    });
7517    let client_b_id = project_a.read_with(cx_a, |project, _| {
7518        project.collaborators().values().next().unwrap().peer_id
7519    });
7520
7521    let a_follow_b = workspace_a.update(cx_a, |workspace, cx| {
7522        workspace.toggle_follow(client_b_id, cx).unwrap()
7523    });
7524    let b_follow_a = workspace_b.update(cx_b, |workspace, cx| {
7525        workspace.toggle_follow(client_a_id, cx).unwrap()
7526    });
7527
7528    futures::try_join!(a_follow_b, b_follow_a).unwrap();
7529    workspace_a.read_with(cx_a, |workspace, _| {
7530        assert_eq!(
7531            workspace.leader_for_pane(workspace.active_pane()),
7532            Some(client_b_id)
7533        );
7534    });
7535    workspace_b.read_with(cx_b, |workspace, _| {
7536        assert_eq!(
7537            workspace.leader_for_pane(workspace.active_pane()),
7538            Some(client_a_id)
7539        );
7540    });
7541}
7542
7543#[gpui::test(iterations = 10)]
7544async fn test_on_input_format_from_host_to_guest(
7545    deterministic: Arc<Deterministic>,
7546    cx_a: &mut TestAppContext,
7547    cx_b: &mut TestAppContext,
7548) {
7549    deterministic.forbid_parking();
7550    let mut server = TestServer::start(&deterministic).await;
7551    let client_a = server.create_client(cx_a, "user_a").await;
7552    let client_b = server.create_client(cx_b, "user_b").await;
7553    server
7554        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7555        .await;
7556    let active_call_a = cx_a.read(ActiveCall::global);
7557
7558    // Set up a fake language server.
7559    let mut language = Language::new(
7560        LanguageConfig {
7561            name: "Rust".into(),
7562            path_suffixes: vec!["rs".to_string()],
7563            ..Default::default()
7564        },
7565        Some(tree_sitter_rust::language()),
7566    );
7567    let mut fake_language_servers = language
7568        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7569            capabilities: lsp::ServerCapabilities {
7570                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
7571                    first_trigger_character: ":".to_string(),
7572                    more_trigger_character: Some(vec![">".to_string()]),
7573                }),
7574                ..Default::default()
7575            },
7576            ..Default::default()
7577        }))
7578        .await;
7579    client_a.language_registry().add(Arc::new(language));
7580
7581    client_a
7582        .fs()
7583        .insert_tree(
7584            "/a",
7585            json!({
7586                "main.rs": "fn main() { a }",
7587                "other.rs": "// Test file",
7588            }),
7589        )
7590        .await;
7591    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7592    let project_id = active_call_a
7593        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7594        .await
7595        .unwrap();
7596    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7597
7598    // Open a file in an editor as the host.
7599    let buffer_a = project_a
7600        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
7601        .await
7602        .unwrap();
7603    let window_a = cx_a.add_window(|_| EmptyView);
7604    let editor_a = window_a.add_view(cx_a, |cx| {
7605        Editor::for_buffer(buffer_a, Some(project_a.clone()), cx)
7606    });
7607
7608    let fake_language_server = fake_language_servers.next().await.unwrap();
7609    cx_b.foreground().run_until_parked();
7610
7611    // Receive an OnTypeFormatting request as the host's language server.
7612    // Return some formattings from the host's language server.
7613    fake_language_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(
7614        |params, _| async move {
7615            assert_eq!(
7616                params.text_document_position.text_document.uri,
7617                lsp::Url::from_file_path("/a/main.rs").unwrap(),
7618            );
7619            assert_eq!(
7620                params.text_document_position.position,
7621                lsp::Position::new(0, 14),
7622            );
7623
7624            Ok(Some(vec![lsp::TextEdit {
7625                new_text: "~<".to_string(),
7626                range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
7627            }]))
7628        },
7629    );
7630
7631    // Open the buffer on the guest and see that the formattings worked
7632    let buffer_b = project_b
7633        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
7634        .await
7635        .unwrap();
7636
7637    // Type a on type formatting trigger character as the guest.
7638    editor_a.update(cx_a, |editor, cx| {
7639        cx.focus(&editor_a);
7640        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
7641        editor.handle_input(">", cx);
7642    });
7643
7644    cx_b.foreground().run_until_parked();
7645
7646    buffer_b.read_with(cx_b, |buffer, _| {
7647        assert_eq!(buffer.text(), "fn main() { a>~< }")
7648    });
7649
7650    // Undo should remove LSP edits first
7651    editor_a.update(cx_a, |editor, cx| {
7652        assert_eq!(editor.text(cx), "fn main() { a>~< }");
7653        editor.undo(&Undo, cx);
7654        assert_eq!(editor.text(cx), "fn main() { a> }");
7655    });
7656    cx_b.foreground().run_until_parked();
7657    buffer_b.read_with(cx_b, |buffer, _| {
7658        assert_eq!(buffer.text(), "fn main() { a> }")
7659    });
7660
7661    editor_a.update(cx_a, |editor, cx| {
7662        assert_eq!(editor.text(cx), "fn main() { a> }");
7663        editor.undo(&Undo, cx);
7664        assert_eq!(editor.text(cx), "fn main() { a }");
7665    });
7666    cx_b.foreground().run_until_parked();
7667    buffer_b.read_with(cx_b, |buffer, _| {
7668        assert_eq!(buffer.text(), "fn main() { a }")
7669    });
7670}
7671
7672#[gpui::test(iterations = 10)]
7673async fn test_on_input_format_from_guest_to_host(
7674    deterministic: Arc<Deterministic>,
7675    cx_a: &mut TestAppContext,
7676    cx_b: &mut TestAppContext,
7677) {
7678    deterministic.forbid_parking();
7679    let mut server = TestServer::start(&deterministic).await;
7680    let client_a = server.create_client(cx_a, "user_a").await;
7681    let client_b = server.create_client(cx_b, "user_b").await;
7682    server
7683        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7684        .await;
7685    let active_call_a = cx_a.read(ActiveCall::global);
7686
7687    // Set up a fake language server.
7688    let mut language = Language::new(
7689        LanguageConfig {
7690            name: "Rust".into(),
7691            path_suffixes: vec!["rs".to_string()],
7692            ..Default::default()
7693        },
7694        Some(tree_sitter_rust::language()),
7695    );
7696    let mut fake_language_servers = language
7697        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7698            capabilities: lsp::ServerCapabilities {
7699                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
7700                    first_trigger_character: ":".to_string(),
7701                    more_trigger_character: Some(vec![">".to_string()]),
7702                }),
7703                ..Default::default()
7704            },
7705            ..Default::default()
7706        }))
7707        .await;
7708    client_a.language_registry().add(Arc::new(language));
7709
7710    client_a
7711        .fs()
7712        .insert_tree(
7713            "/a",
7714            json!({
7715                "main.rs": "fn main() { a }",
7716                "other.rs": "// Test file",
7717            }),
7718        )
7719        .await;
7720    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7721    let project_id = active_call_a
7722        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7723        .await
7724        .unwrap();
7725    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7726
7727    // Open a file in an editor as the guest.
7728    let buffer_b = project_b
7729        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
7730        .await
7731        .unwrap();
7732    let window_b = cx_b.add_window(|_| EmptyView);
7733    let editor_b = window_b.add_view(cx_b, |cx| {
7734        Editor::for_buffer(buffer_b, Some(project_b.clone()), cx)
7735    });
7736
7737    let fake_language_server = fake_language_servers.next().await.unwrap();
7738    cx_a.foreground().run_until_parked();
7739    // Type a on type formatting trigger character as the guest.
7740    editor_b.update(cx_b, |editor, cx| {
7741        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
7742        editor.handle_input(":", cx);
7743        cx.focus(&editor_b);
7744    });
7745
7746    // Receive an OnTypeFormatting request as the host's language server.
7747    // Return some formattings from the host's language server.
7748    cx_a.foreground().start_waiting();
7749    fake_language_server
7750        .handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
7751            assert_eq!(
7752                params.text_document_position.text_document.uri,
7753                lsp::Url::from_file_path("/a/main.rs").unwrap(),
7754            );
7755            assert_eq!(
7756                params.text_document_position.position,
7757                lsp::Position::new(0, 14),
7758            );
7759
7760            Ok(Some(vec![lsp::TextEdit {
7761                new_text: "~:".to_string(),
7762                range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
7763            }]))
7764        })
7765        .next()
7766        .await
7767        .unwrap();
7768    cx_a.foreground().finish_waiting();
7769
7770    // Open the buffer on the host and see that the formattings worked
7771    let buffer_a = project_a
7772        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
7773        .await
7774        .unwrap();
7775    cx_a.foreground().run_until_parked();
7776    buffer_a.read_with(cx_a, |buffer, _| {
7777        assert_eq!(buffer.text(), "fn main() { a:~: }")
7778    });
7779
7780    // Undo should remove LSP edits first
7781    editor_b.update(cx_b, |editor, cx| {
7782        assert_eq!(editor.text(cx), "fn main() { a:~: }");
7783        editor.undo(&Undo, cx);
7784        assert_eq!(editor.text(cx), "fn main() { a: }");
7785    });
7786    cx_a.foreground().run_until_parked();
7787    buffer_a.read_with(cx_a, |buffer, _| {
7788        assert_eq!(buffer.text(), "fn main() { a: }")
7789    });
7790
7791    editor_b.update(cx_b, |editor, cx| {
7792        assert_eq!(editor.text(cx), "fn main() { a: }");
7793        editor.undo(&Undo, cx);
7794        assert_eq!(editor.text(cx), "fn main() { a }");
7795    });
7796    cx_a.foreground().run_until_parked();
7797    buffer_a.read_with(cx_a, |buffer, _| {
7798        assert_eq!(buffer.text(), "fn main() { a }")
7799    });
7800}
7801
7802#[gpui::test(iterations = 10)]
7803async fn test_mutual_editor_inlay_hint_cache_update(
7804    deterministic: Arc<Deterministic>,
7805    cx_a: &mut TestAppContext,
7806    cx_b: &mut TestAppContext,
7807) {
7808    deterministic.forbid_parking();
7809    let mut server = TestServer::start(&deterministic).await;
7810    let client_a = server.create_client(cx_a, "user_a").await;
7811    let client_b = server.create_client(cx_b, "user_b").await;
7812    server
7813        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7814        .await;
7815    let active_call_a = cx_a.read(ActiveCall::global);
7816    let active_call_b = cx_b.read(ActiveCall::global);
7817
7818    cx_a.update(editor::init);
7819    cx_b.update(editor::init);
7820
7821    cx_a.update(|cx| {
7822        cx.update_global(|store: &mut SettingsStore, cx| {
7823            store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
7824                settings.defaults.inlay_hints = Some(InlayHintSettings {
7825                    enabled: true,
7826                    show_type_hints: true,
7827                    show_parameter_hints: false,
7828                    show_other_hints: true,
7829                })
7830            });
7831        });
7832    });
7833    cx_b.update(|cx| {
7834        cx.update_global(|store: &mut SettingsStore, cx| {
7835            store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
7836                settings.defaults.inlay_hints = Some(InlayHintSettings {
7837                    enabled: true,
7838                    show_type_hints: true,
7839                    show_parameter_hints: false,
7840                    show_other_hints: true,
7841                })
7842            });
7843        });
7844    });
7845
7846    let mut language = Language::new(
7847        LanguageConfig {
7848            name: "Rust".into(),
7849            path_suffixes: vec!["rs".to_string()],
7850            ..Default::default()
7851        },
7852        Some(tree_sitter_rust::language()),
7853    );
7854    let mut fake_language_servers = language
7855        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7856            capabilities: lsp::ServerCapabilities {
7857                inlay_hint_provider: Some(lsp::OneOf::Left(true)),
7858                ..Default::default()
7859            },
7860            ..Default::default()
7861        }))
7862        .await;
7863    let language = Arc::new(language);
7864    client_a.language_registry().add(Arc::clone(&language));
7865    client_b.language_registry().add(language);
7866
7867    // Client A opens a project.
7868    client_a
7869        .fs()
7870        .insert_tree(
7871            "/a",
7872            json!({
7873                "main.rs": "fn main() { a } // and some long comment to ensure inlay hints are not trimmed out",
7874                "other.rs": "// Test file",
7875            }),
7876        )
7877        .await;
7878    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7879    active_call_a
7880        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
7881        .await
7882        .unwrap();
7883    let project_id = active_call_a
7884        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7885        .await
7886        .unwrap();
7887
7888    // Client B joins the project
7889    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7890    active_call_b
7891        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
7892        .await
7893        .unwrap();
7894
7895    let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
7896    cx_a.foreground().start_waiting();
7897
7898    // The host opens a rust file.
7899    let _buffer_a = project_a
7900        .update(cx_a, |project, cx| {
7901            project.open_local_buffer("/a/main.rs", cx)
7902        })
7903        .await
7904        .unwrap();
7905    let fake_language_server = fake_language_servers.next().await.unwrap();
7906    let editor_a = workspace_a
7907        .update(cx_a, |workspace, cx| {
7908            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
7909        })
7910        .await
7911        .unwrap()
7912        .downcast::<Editor>()
7913        .unwrap();
7914
7915    // Set up the language server to return an additional inlay hint on each request.
7916    let edits_made = Arc::new(AtomicUsize::new(0));
7917    let closure_edits_made = Arc::clone(&edits_made);
7918    fake_language_server
7919        .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
7920            let task_edits_made = Arc::clone(&closure_edits_made);
7921            async move {
7922                assert_eq!(
7923                    params.text_document.uri,
7924                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
7925                );
7926                let edits_made = task_edits_made.load(atomic::Ordering::Acquire);
7927                Ok(Some(vec![lsp::InlayHint {
7928                    position: lsp::Position::new(0, edits_made as u32),
7929                    label: lsp::InlayHintLabel::String(edits_made.to_string()),
7930                    kind: None,
7931                    text_edits: None,
7932                    tooltip: None,
7933                    padding_left: None,
7934                    padding_right: None,
7935                    data: None,
7936                }]))
7937            }
7938        })
7939        .next()
7940        .await
7941        .unwrap();
7942
7943    deterministic.run_until_parked();
7944
7945    let initial_edit = edits_made.load(atomic::Ordering::Acquire);
7946    editor_a.update(cx_a, |editor, _| {
7947        assert_eq!(
7948            vec![initial_edit.to_string()],
7949            extract_hint_labels(editor),
7950            "Host should get its first hints when opens an editor"
7951        );
7952        let inlay_cache = editor.inlay_hint_cache();
7953        assert_eq!(
7954            inlay_cache.version(),
7955            1,
7956            "Host editor update the cache version after every cache/view change",
7957        );
7958    });
7959    let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
7960    let editor_b = workspace_b
7961        .update(cx_b, |workspace, cx| {
7962            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
7963        })
7964        .await
7965        .unwrap()
7966        .downcast::<Editor>()
7967        .unwrap();
7968
7969    deterministic.run_until_parked();
7970    editor_b.update(cx_b, |editor, _| {
7971        assert_eq!(
7972            vec![initial_edit.to_string()],
7973            extract_hint_labels(editor),
7974            "Client should get its first hints when opens an editor"
7975        );
7976        let inlay_cache = editor.inlay_hint_cache();
7977        assert_eq!(
7978            inlay_cache.version(),
7979            1,
7980            "Guest editor update the cache version after every cache/view change"
7981        );
7982    });
7983
7984    let after_client_edit = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
7985    editor_b.update(cx_b, |editor, cx| {
7986        editor.change_selections(None, cx, |s| s.select_ranges([13..13].clone()));
7987        editor.handle_input(":", cx);
7988        cx.focus(&editor_b);
7989    });
7990
7991    deterministic.run_until_parked();
7992    editor_a.update(cx_a, |editor, _| {
7993        assert_eq!(
7994            vec![after_client_edit.to_string()],
7995            extract_hint_labels(editor),
7996        );
7997        let inlay_cache = editor.inlay_hint_cache();
7998        assert_eq!(inlay_cache.version(), 2);
7999    });
8000    editor_b.update(cx_b, |editor, _| {
8001        assert_eq!(
8002            vec![after_client_edit.to_string()],
8003            extract_hint_labels(editor),
8004        );
8005        let inlay_cache = editor.inlay_hint_cache();
8006        assert_eq!(inlay_cache.version(), 2);
8007    });
8008
8009    let after_host_edit = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
8010    editor_a.update(cx_a, |editor, cx| {
8011        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
8012        editor.handle_input("a change to increment both buffers' versions", cx);
8013        cx.focus(&editor_a);
8014    });
8015
8016    deterministic.run_until_parked();
8017    editor_a.update(cx_a, |editor, _| {
8018        assert_eq!(
8019            vec![after_host_edit.to_string()],
8020            extract_hint_labels(editor),
8021        );
8022        let inlay_cache = editor.inlay_hint_cache();
8023        assert_eq!(inlay_cache.version(), 3);
8024    });
8025    editor_b.update(cx_b, |editor, _| {
8026        assert_eq!(
8027            vec![after_host_edit.to_string()],
8028            extract_hint_labels(editor),
8029        );
8030        let inlay_cache = editor.inlay_hint_cache();
8031        assert_eq!(inlay_cache.version(), 3);
8032    });
8033
8034    let after_special_edit_for_refresh = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
8035    fake_language_server
8036        .request::<lsp::request::InlayHintRefreshRequest>(())
8037        .await
8038        .expect("inlay refresh request failed");
8039
8040    deterministic.run_until_parked();
8041    editor_a.update(cx_a, |editor, _| {
8042        assert_eq!(
8043            vec![after_special_edit_for_refresh.to_string()],
8044            extract_hint_labels(editor),
8045            "Host should react to /refresh LSP request"
8046        );
8047        let inlay_cache = editor.inlay_hint_cache();
8048        assert_eq!(
8049            inlay_cache.version(),
8050            4,
8051            "Host should accepted all edits and bump its cache version every time"
8052        );
8053    });
8054    editor_b.update(cx_b, |editor, _| {
8055        assert_eq!(
8056            vec![after_special_edit_for_refresh.to_string()],
8057            extract_hint_labels(editor),
8058            "Guest should get a /refresh LSP request propagated by host"
8059        );
8060        let inlay_cache = editor.inlay_hint_cache();
8061        assert_eq!(
8062            inlay_cache.version(),
8063            4,
8064            "Guest should accepted all edits and bump its cache version every time"
8065        );
8066    });
8067}
8068
8069#[gpui::test(iterations = 10)]
8070async fn test_inlay_hint_refresh_is_forwarded(
8071    deterministic: Arc<Deterministic>,
8072    cx_a: &mut TestAppContext,
8073    cx_b: &mut TestAppContext,
8074) {
8075    deterministic.forbid_parking();
8076    let mut server = TestServer::start(&deterministic).await;
8077    let client_a = server.create_client(cx_a, "user_a").await;
8078    let client_b = server.create_client(cx_b, "user_b").await;
8079    server
8080        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
8081        .await;
8082    let active_call_a = cx_a.read(ActiveCall::global);
8083    let active_call_b = cx_b.read(ActiveCall::global);
8084
8085    cx_a.update(editor::init);
8086    cx_b.update(editor::init);
8087
8088    cx_a.update(|cx| {
8089        cx.update_global(|store: &mut SettingsStore, cx| {
8090            store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
8091                settings.defaults.inlay_hints = Some(InlayHintSettings {
8092                    enabled: false,
8093                    show_type_hints: false,
8094                    show_parameter_hints: false,
8095                    show_other_hints: false,
8096                })
8097            });
8098        });
8099    });
8100    cx_b.update(|cx| {
8101        cx.update_global(|store: &mut SettingsStore, cx| {
8102            store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
8103                settings.defaults.inlay_hints = Some(InlayHintSettings {
8104                    enabled: true,
8105                    show_type_hints: true,
8106                    show_parameter_hints: true,
8107                    show_other_hints: true,
8108                })
8109            });
8110        });
8111    });
8112
8113    let mut language = Language::new(
8114        LanguageConfig {
8115            name: "Rust".into(),
8116            path_suffixes: vec!["rs".to_string()],
8117            ..Default::default()
8118        },
8119        Some(tree_sitter_rust::language()),
8120    );
8121    let mut fake_language_servers = language
8122        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
8123            capabilities: lsp::ServerCapabilities {
8124                inlay_hint_provider: Some(lsp::OneOf::Left(true)),
8125                ..Default::default()
8126            },
8127            ..Default::default()
8128        }))
8129        .await;
8130    let language = Arc::new(language);
8131    client_a.language_registry().add(Arc::clone(&language));
8132    client_b.language_registry().add(language);
8133
8134    client_a
8135        .fs()
8136        .insert_tree(
8137            "/a",
8138            json!({
8139                "main.rs": "fn main() { a } // and some long comment to ensure inlay hints are not trimmed out",
8140                "other.rs": "// Test file",
8141            }),
8142        )
8143        .await;
8144    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
8145    active_call_a
8146        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
8147        .await
8148        .unwrap();
8149    let project_id = active_call_a
8150        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
8151        .await
8152        .unwrap();
8153
8154    let project_b = client_b.build_remote_project(project_id, cx_b).await;
8155    active_call_b
8156        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
8157        .await
8158        .unwrap();
8159
8160    let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
8161    let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
8162    cx_a.foreground().start_waiting();
8163    cx_b.foreground().start_waiting();
8164
8165    let editor_a = workspace_a
8166        .update(cx_a, |workspace, cx| {
8167            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
8168        })
8169        .await
8170        .unwrap()
8171        .downcast::<Editor>()
8172        .unwrap();
8173
8174    let editor_b = workspace_b
8175        .update(cx_b, |workspace, cx| {
8176            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
8177        })
8178        .await
8179        .unwrap()
8180        .downcast::<Editor>()
8181        .unwrap();
8182
8183    let other_hints = Arc::new(AtomicBool::new(false));
8184    let fake_language_server = fake_language_servers.next().await.unwrap();
8185    let closure_other_hints = Arc::clone(&other_hints);
8186    fake_language_server
8187        .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
8188            let task_other_hints = Arc::clone(&closure_other_hints);
8189            async move {
8190                assert_eq!(
8191                    params.text_document.uri,
8192                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
8193                );
8194                let other_hints = task_other_hints.load(atomic::Ordering::Acquire);
8195                let character = if other_hints { 0 } else { 2 };
8196                let label = if other_hints {
8197                    "other hint"
8198                } else {
8199                    "initial hint"
8200                };
8201                Ok(Some(vec![lsp::InlayHint {
8202                    position: lsp::Position::new(0, character),
8203                    label: lsp::InlayHintLabel::String(label.to_string()),
8204                    kind: None,
8205                    text_edits: None,
8206                    tooltip: None,
8207                    padding_left: None,
8208                    padding_right: None,
8209                    data: None,
8210                }]))
8211            }
8212        })
8213        .next()
8214        .await
8215        .unwrap();
8216    cx_a.foreground().finish_waiting();
8217    cx_b.foreground().finish_waiting();
8218
8219    cx_a.foreground().run_until_parked();
8220    editor_a.update(cx_a, |editor, _| {
8221        assert!(
8222            extract_hint_labels(editor).is_empty(),
8223            "Host should get no hints due to them turned off"
8224        );
8225        let inlay_cache = editor.inlay_hint_cache();
8226        assert_eq!(
8227            inlay_cache.version(),
8228            0,
8229            "Turned off hints should not generate version updates"
8230        );
8231    });
8232
8233    cx_b.foreground().run_until_parked();
8234    editor_b.update(cx_b, |editor, _| {
8235        assert_eq!(
8236            vec!["initial hint".to_string()],
8237            extract_hint_labels(editor),
8238            "Client should get its first hints when opens an editor"
8239        );
8240        let inlay_cache = editor.inlay_hint_cache();
8241        assert_eq!(
8242            inlay_cache.version(),
8243            1,
8244            "Should update cache verison after first hints"
8245        );
8246    });
8247
8248    other_hints.fetch_or(true, atomic::Ordering::Release);
8249    fake_language_server
8250        .request::<lsp::request::InlayHintRefreshRequest>(())
8251        .await
8252        .expect("inlay refresh request failed");
8253    cx_a.foreground().run_until_parked();
8254    editor_a.update(cx_a, |editor, _| {
8255        assert!(
8256            extract_hint_labels(editor).is_empty(),
8257            "Host should get nop hints due to them turned off, even after the /refresh"
8258        );
8259        let inlay_cache = editor.inlay_hint_cache();
8260        assert_eq!(
8261            inlay_cache.version(),
8262            0,
8263            "Turned off hints should not generate version updates, again"
8264        );
8265    });
8266
8267    cx_b.foreground().run_until_parked();
8268    editor_b.update(cx_b, |editor, _| {
8269        assert_eq!(
8270            vec!["other hint".to_string()],
8271            extract_hint_labels(editor),
8272            "Guest should get a /refresh LSP request propagated by host despite host hints are off"
8273        );
8274        let inlay_cache = editor.inlay_hint_cache();
8275        assert_eq!(
8276            inlay_cache.version(),
8277            2,
8278            "Guest should accepted all edits and bump its cache version every time"
8279        );
8280    });
8281}
8282
8283fn extract_hint_labels(editor: &Editor) -> Vec<String> {
8284    let mut labels = Vec::new();
8285    for hint in editor.inlay_hint_cache().hints() {
8286        match hint.label {
8287            project::InlayHintLabel::String(s) => labels.push(s),
8288            _ => unreachable!(),
8289        }
8290    }
8291    labels
8292}