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::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 _, LineEnding, 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, 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::{AtomicBool, AtomicU32, 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 results = project_b
4825        .update(cx_b, |project, cx| {
4826            project.search(
4827                SearchQuery::text("world", false, false, Vec::new(), Vec::new()),
4828                cx,
4829            )
4830        })
4831        .await
4832        .unwrap();
4833
4834    let mut ranges_by_path = results
4835        .into_iter()
4836        .map(|(buffer, ranges)| {
4837            buffer.read_with(cx_b, |buffer, cx| {
4838                let path = buffer.file().unwrap().full_path(cx);
4839                let offset_ranges = ranges
4840                    .into_iter()
4841                    .map(|range| range.to_offset(buffer))
4842                    .collect::<Vec<_>>();
4843                (path, offset_ranges)
4844            })
4845        })
4846        .collect::<Vec<_>>();
4847    ranges_by_path.sort_by_key(|(path, _)| path.clone());
4848
4849    assert_eq!(
4850        ranges_by_path,
4851        &[
4852            (PathBuf::from("dir-1/a"), vec![6..11]),
4853            (PathBuf::from("dir-1/c"), vec![2..7]),
4854            (PathBuf::from("dir-1/d"), vec![0..5, 24..29]),
4855            (PathBuf::from("dir-2/e"), vec![7..12]),
4856        ]
4857    );
4858}
4859
4860#[gpui::test(iterations = 10)]
4861async fn test_document_highlights(
4862    deterministic: Arc<Deterministic>,
4863    cx_a: &mut TestAppContext,
4864    cx_b: &mut TestAppContext,
4865) {
4866    deterministic.forbid_parking();
4867    let mut server = TestServer::start(&deterministic).await;
4868    let client_a = server.create_client(cx_a, "user_a").await;
4869    let client_b = server.create_client(cx_b, "user_b").await;
4870    server
4871        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4872        .await;
4873    let active_call_a = cx_a.read(ActiveCall::global);
4874
4875    client_a
4876        .fs()
4877        .insert_tree(
4878            "/root-1",
4879            json!({
4880                "main.rs": "fn double(number: i32) -> i32 { number + number }",
4881            }),
4882        )
4883        .await;
4884
4885    // Set up a fake language server.
4886    let mut language = Language::new(
4887        LanguageConfig {
4888            name: "Rust".into(),
4889            path_suffixes: vec!["rs".to_string()],
4890            ..Default::default()
4891        },
4892        Some(tree_sitter_rust::language()),
4893    );
4894    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4895    client_a.language_registry().add(Arc::new(language));
4896
4897    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4898    let project_id = active_call_a
4899        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4900        .await
4901        .unwrap();
4902    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4903
4904    // Open the file on client B.
4905    let buffer_b = cx_b
4906        .background()
4907        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
4908        .await
4909        .unwrap();
4910
4911    // Request document highlights as the guest.
4912    let fake_language_server = fake_language_servers.next().await.unwrap();
4913    fake_language_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
4914        |params, _| async move {
4915            assert_eq!(
4916                params
4917                    .text_document_position_params
4918                    .text_document
4919                    .uri
4920                    .as_str(),
4921                "file:///root-1/main.rs"
4922            );
4923            assert_eq!(
4924                params.text_document_position_params.position,
4925                lsp::Position::new(0, 34)
4926            );
4927            Ok(Some(vec![
4928                lsp::DocumentHighlight {
4929                    kind: Some(lsp::DocumentHighlightKind::WRITE),
4930                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 16)),
4931                },
4932                lsp::DocumentHighlight {
4933                    kind: Some(lsp::DocumentHighlightKind::READ),
4934                    range: lsp::Range::new(lsp::Position::new(0, 32), lsp::Position::new(0, 38)),
4935                },
4936                lsp::DocumentHighlight {
4937                    kind: Some(lsp::DocumentHighlightKind::READ),
4938                    range: lsp::Range::new(lsp::Position::new(0, 41), lsp::Position::new(0, 47)),
4939                },
4940            ]))
4941        },
4942    );
4943
4944    let highlights = project_b
4945        .update(cx_b, |p, cx| p.document_highlights(&buffer_b, 34, cx))
4946        .await
4947        .unwrap();
4948    buffer_b.read_with(cx_b, |buffer, _| {
4949        let snapshot = buffer.snapshot();
4950
4951        let highlights = highlights
4952            .into_iter()
4953            .map(|highlight| (highlight.kind, highlight.range.to_offset(&snapshot)))
4954            .collect::<Vec<_>>();
4955        assert_eq!(
4956            highlights,
4957            &[
4958                (lsp::DocumentHighlightKind::WRITE, 10..16),
4959                (lsp::DocumentHighlightKind::READ, 32..38),
4960                (lsp::DocumentHighlightKind::READ, 41..47)
4961            ]
4962        )
4963    });
4964}
4965
4966#[gpui::test(iterations = 10)]
4967async fn test_lsp_hover(
4968    deterministic: Arc<Deterministic>,
4969    cx_a: &mut TestAppContext,
4970    cx_b: &mut TestAppContext,
4971) {
4972    deterministic.forbid_parking();
4973    let mut server = TestServer::start(&deterministic).await;
4974    let client_a = server.create_client(cx_a, "user_a").await;
4975    let client_b = server.create_client(cx_b, "user_b").await;
4976    server
4977        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4978        .await;
4979    let active_call_a = cx_a.read(ActiveCall::global);
4980
4981    client_a
4982        .fs()
4983        .insert_tree(
4984            "/root-1",
4985            json!({
4986                "main.rs": "use std::collections::HashMap;",
4987            }),
4988        )
4989        .await;
4990
4991    // Set up a fake language server.
4992    let mut language = Language::new(
4993        LanguageConfig {
4994            name: "Rust".into(),
4995            path_suffixes: vec!["rs".to_string()],
4996            ..Default::default()
4997        },
4998        Some(tree_sitter_rust::language()),
4999    );
5000    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5001    client_a.language_registry().add(Arc::new(language));
5002
5003    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
5004    let project_id = active_call_a
5005        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5006        .await
5007        .unwrap();
5008    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5009
5010    // Open the file as the guest
5011    let buffer_b = cx_b
5012        .background()
5013        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
5014        .await
5015        .unwrap();
5016
5017    // Request hover information as the guest.
5018    let fake_language_server = fake_language_servers.next().await.unwrap();
5019    fake_language_server.handle_request::<lsp::request::HoverRequest, _, _>(
5020        |params, _| async move {
5021            assert_eq!(
5022                params
5023                    .text_document_position_params
5024                    .text_document
5025                    .uri
5026                    .as_str(),
5027                "file:///root-1/main.rs"
5028            );
5029            assert_eq!(
5030                params.text_document_position_params.position,
5031                lsp::Position::new(0, 22)
5032            );
5033            Ok(Some(lsp::Hover {
5034                contents: lsp::HoverContents::Array(vec![
5035                    lsp::MarkedString::String("Test hover content.".to_string()),
5036                    lsp::MarkedString::LanguageString(lsp::LanguageString {
5037                        language: "Rust".to_string(),
5038                        value: "let foo = 42;".to_string(),
5039                    }),
5040                ]),
5041                range: Some(lsp::Range::new(
5042                    lsp::Position::new(0, 22),
5043                    lsp::Position::new(0, 29),
5044                )),
5045            }))
5046        },
5047    );
5048
5049    let hover_info = project_b
5050        .update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
5051        .await
5052        .unwrap()
5053        .unwrap();
5054    buffer_b.read_with(cx_b, |buffer, _| {
5055        let snapshot = buffer.snapshot();
5056        assert_eq!(hover_info.range.unwrap().to_offset(&snapshot), 22..29);
5057        assert_eq!(
5058            hover_info.contents,
5059            vec![
5060                project::HoverBlock {
5061                    text: "Test hover content.".to_string(),
5062                    kind: HoverBlockKind::Markdown,
5063                },
5064                project::HoverBlock {
5065                    text: "let foo = 42;".to_string(),
5066                    kind: HoverBlockKind::Code {
5067                        language: "Rust".to_string()
5068                    },
5069                }
5070            ]
5071        );
5072    });
5073}
5074
5075#[gpui::test(iterations = 10)]
5076async fn test_project_symbols(
5077    deterministic: Arc<Deterministic>,
5078    cx_a: &mut TestAppContext,
5079    cx_b: &mut TestAppContext,
5080) {
5081    deterministic.forbid_parking();
5082    let mut server = TestServer::start(&deterministic).await;
5083    let client_a = server.create_client(cx_a, "user_a").await;
5084    let client_b = server.create_client(cx_b, "user_b").await;
5085    server
5086        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5087        .await;
5088    let active_call_a = cx_a.read(ActiveCall::global);
5089
5090    // Set up a fake language server.
5091    let mut language = Language::new(
5092        LanguageConfig {
5093            name: "Rust".into(),
5094            path_suffixes: vec!["rs".to_string()],
5095            ..Default::default()
5096        },
5097        Some(tree_sitter_rust::language()),
5098    );
5099    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5100    client_a.language_registry().add(Arc::new(language));
5101
5102    client_a
5103        .fs()
5104        .insert_tree(
5105            "/code",
5106            json!({
5107                "crate-1": {
5108                    "one.rs": "const ONE: usize = 1;",
5109                },
5110                "crate-2": {
5111                    "two.rs": "const TWO: usize = 2; const THREE: usize = 3;",
5112                },
5113                "private": {
5114                    "passwords.txt": "the-password",
5115                }
5116            }),
5117        )
5118        .await;
5119    let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await;
5120    let project_id = active_call_a
5121        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5122        .await
5123        .unwrap();
5124    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5125
5126    // Cause the language server to start.
5127    let _buffer = cx_b
5128        .background()
5129        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
5130        .await
5131        .unwrap();
5132
5133    let fake_language_server = fake_language_servers.next().await.unwrap();
5134    fake_language_server.handle_request::<lsp::WorkspaceSymbolRequest, _, _>(|_, _| async move {
5135        Ok(Some(lsp::WorkspaceSymbolResponse::Flat(vec![
5136            #[allow(deprecated)]
5137            lsp::SymbolInformation {
5138                name: "TWO".into(),
5139                location: lsp::Location {
5140                    uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
5141                    range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5142                },
5143                kind: lsp::SymbolKind::CONSTANT,
5144                tags: None,
5145                container_name: None,
5146                deprecated: None,
5147            },
5148        ])))
5149    });
5150
5151    // Request the definition of a symbol as the guest.
5152    let symbols = project_b
5153        .update(cx_b, |p, cx| p.symbols("two", cx))
5154        .await
5155        .unwrap();
5156    assert_eq!(symbols.len(), 1);
5157    assert_eq!(symbols[0].name, "TWO");
5158
5159    // Open one of the returned symbols.
5160    let buffer_b_2 = project_b
5161        .update(cx_b, |project, cx| {
5162            project.open_buffer_for_symbol(&symbols[0], cx)
5163        })
5164        .await
5165        .unwrap();
5166    buffer_b_2.read_with(cx_b, |buffer, _| {
5167        assert_eq!(
5168            buffer.file().unwrap().path().as_ref(),
5169            Path::new("../crate-2/two.rs")
5170        );
5171    });
5172
5173    // Attempt to craft a symbol and violate host's privacy by opening an arbitrary file.
5174    let mut fake_symbol = symbols[0].clone();
5175    fake_symbol.path.path = Path::new("/code/secrets").into();
5176    let error = project_b
5177        .update(cx_b, |project, cx| {
5178            project.open_buffer_for_symbol(&fake_symbol, cx)
5179        })
5180        .await
5181        .unwrap_err();
5182    assert!(error.to_string().contains("invalid symbol signature"));
5183}
5184
5185#[gpui::test(iterations = 10)]
5186async fn test_open_buffer_while_getting_definition_pointing_to_it(
5187    deterministic: Arc<Deterministic>,
5188    cx_a: &mut TestAppContext,
5189    cx_b: &mut TestAppContext,
5190    mut rng: StdRng,
5191) {
5192    deterministic.forbid_parking();
5193    let mut server = TestServer::start(&deterministic).await;
5194    let client_a = server.create_client(cx_a, "user_a").await;
5195    let client_b = server.create_client(cx_b, "user_b").await;
5196    server
5197        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5198        .await;
5199    let active_call_a = cx_a.read(ActiveCall::global);
5200
5201    // Set up a fake language server.
5202    let mut language = Language::new(
5203        LanguageConfig {
5204            name: "Rust".into(),
5205            path_suffixes: vec!["rs".to_string()],
5206            ..Default::default()
5207        },
5208        Some(tree_sitter_rust::language()),
5209    );
5210    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5211    client_a.language_registry().add(Arc::new(language));
5212
5213    client_a
5214        .fs()
5215        .insert_tree(
5216            "/root",
5217            json!({
5218                "a.rs": "const ONE: usize = b::TWO;",
5219                "b.rs": "const TWO: usize = 2",
5220            }),
5221        )
5222        .await;
5223    let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await;
5224    let project_id = active_call_a
5225        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5226        .await
5227        .unwrap();
5228    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5229
5230    let buffer_b1 = cx_b
5231        .background()
5232        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
5233        .await
5234        .unwrap();
5235
5236    let fake_language_server = fake_language_servers.next().await.unwrap();
5237    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
5238        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
5239            lsp::Location::new(
5240                lsp::Url::from_file_path("/root/b.rs").unwrap(),
5241                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5242            ),
5243        )))
5244    });
5245
5246    let definitions;
5247    let buffer_b2;
5248    if rng.gen() {
5249        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5250        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5251    } else {
5252        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5253        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5254    }
5255
5256    let buffer_b2 = buffer_b2.await.unwrap();
5257    let definitions = definitions.await.unwrap();
5258    assert_eq!(definitions.len(), 1);
5259    assert_eq!(definitions[0].target.buffer, buffer_b2);
5260}
5261
5262#[gpui::test(iterations = 10)]
5263async fn test_collaborating_with_code_actions(
5264    deterministic: Arc<Deterministic>,
5265    cx_a: &mut TestAppContext,
5266    cx_b: &mut TestAppContext,
5267) {
5268    deterministic.forbid_parking();
5269    let mut server = TestServer::start(&deterministic).await;
5270    let client_a = server.create_client(cx_a, "user_a").await;
5271    //
5272    let client_b = server.create_client(cx_b, "user_b").await;
5273    server
5274        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5275        .await;
5276    let active_call_a = cx_a.read(ActiveCall::global);
5277
5278    cx_b.update(editor::init);
5279
5280    // Set up a fake language server.
5281    let mut language = Language::new(
5282        LanguageConfig {
5283            name: "Rust".into(),
5284            path_suffixes: vec!["rs".to_string()],
5285            ..Default::default()
5286        },
5287        Some(tree_sitter_rust::language()),
5288    );
5289    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5290    client_a.language_registry().add(Arc::new(language));
5291
5292    client_a
5293        .fs()
5294        .insert_tree(
5295            "/a",
5296            json!({
5297                "main.rs": "mod other;\nfn main() { let foo = other::foo(); }",
5298                "other.rs": "pub fn foo() -> usize { 4 }",
5299            }),
5300        )
5301        .await;
5302    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
5303    let project_id = active_call_a
5304        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5305        .await
5306        .unwrap();
5307
5308    // Join the project as client B.
5309    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5310    let window_b =
5311        cx_b.add_window(|cx| Workspace::new(0, project_b.clone(), client_b.app_state.clone(), cx));
5312    let workspace_b = window_b.root(cx_b);
5313    let editor_b = workspace_b
5314        .update(cx_b, |workspace, cx| {
5315            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
5316        })
5317        .await
5318        .unwrap()
5319        .downcast::<Editor>()
5320        .unwrap();
5321
5322    let mut fake_language_server = fake_language_servers.next().await.unwrap();
5323    let mut requests = fake_language_server
5324        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
5325            assert_eq!(
5326                params.text_document.uri,
5327                lsp::Url::from_file_path("/a/main.rs").unwrap(),
5328            );
5329            assert_eq!(params.range.start, lsp::Position::new(0, 0));
5330            assert_eq!(params.range.end, lsp::Position::new(0, 0));
5331            Ok(None)
5332        });
5333    deterministic.advance_clock(editor::CODE_ACTIONS_DEBOUNCE_TIMEOUT * 2);
5334    requests.next().await;
5335
5336    // Move cursor to a location that contains code actions.
5337    editor_b.update(cx_b, |editor, cx| {
5338        editor.change_selections(None, cx, |s| {
5339            s.select_ranges([Point::new(1, 31)..Point::new(1, 31)])
5340        });
5341        cx.focus(&editor_b);
5342    });
5343
5344    let mut requests = fake_language_server
5345        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
5346            assert_eq!(
5347                params.text_document.uri,
5348                lsp::Url::from_file_path("/a/main.rs").unwrap(),
5349            );
5350            assert_eq!(params.range.start, lsp::Position::new(1, 31));
5351            assert_eq!(params.range.end, lsp::Position::new(1, 31));
5352
5353            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
5354                lsp::CodeAction {
5355                    title: "Inline into all callers".to_string(),
5356                    edit: Some(lsp::WorkspaceEdit {
5357                        changes: Some(
5358                            [
5359                                (
5360                                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
5361                                    vec![lsp::TextEdit::new(
5362                                        lsp::Range::new(
5363                                            lsp::Position::new(1, 22),
5364                                            lsp::Position::new(1, 34),
5365                                        ),
5366                                        "4".to_string(),
5367                                    )],
5368                                ),
5369                                (
5370                                    lsp::Url::from_file_path("/a/other.rs").unwrap(),
5371                                    vec![lsp::TextEdit::new(
5372                                        lsp::Range::new(
5373                                            lsp::Position::new(0, 0),
5374                                            lsp::Position::new(0, 27),
5375                                        ),
5376                                        "".to_string(),
5377                                    )],
5378                                ),
5379                            ]
5380                            .into_iter()
5381                            .collect(),
5382                        ),
5383                        ..Default::default()
5384                    }),
5385                    data: Some(json!({
5386                        "codeActionParams": {
5387                            "range": {
5388                                "start": {"line": 1, "column": 31},
5389                                "end": {"line": 1, "column": 31},
5390                            }
5391                        }
5392                    })),
5393                    ..Default::default()
5394                },
5395            )]))
5396        });
5397    deterministic.advance_clock(editor::CODE_ACTIONS_DEBOUNCE_TIMEOUT * 2);
5398    requests.next().await;
5399
5400    // Toggle code actions and wait for them to display.
5401    editor_b.update(cx_b, |editor, cx| {
5402        editor.toggle_code_actions(
5403            &ToggleCodeActions {
5404                deployed_from_indicator: false,
5405            },
5406            cx,
5407        );
5408    });
5409    cx_a.foreground().run_until_parked();
5410    editor_b.read_with(cx_b, |editor, _| assert!(editor.context_menu_visible()));
5411
5412    fake_language_server.remove_request_handler::<lsp::request::CodeActionRequest>();
5413
5414    // Confirming the code action will trigger a resolve request.
5415    let confirm_action = workspace_b
5416        .update(cx_b, |workspace, cx| {
5417            Editor::confirm_code_action(workspace, &ConfirmCodeAction { item_ix: Some(0) }, cx)
5418        })
5419        .unwrap();
5420    fake_language_server.handle_request::<lsp::request::CodeActionResolveRequest, _, _>(
5421        |_, _| async move {
5422            Ok(lsp::CodeAction {
5423                title: "Inline into all callers".to_string(),
5424                edit: Some(lsp::WorkspaceEdit {
5425                    changes: Some(
5426                        [
5427                            (
5428                                lsp::Url::from_file_path("/a/main.rs").unwrap(),
5429                                vec![lsp::TextEdit::new(
5430                                    lsp::Range::new(
5431                                        lsp::Position::new(1, 22),
5432                                        lsp::Position::new(1, 34),
5433                                    ),
5434                                    "4".to_string(),
5435                                )],
5436                            ),
5437                            (
5438                                lsp::Url::from_file_path("/a/other.rs").unwrap(),
5439                                vec![lsp::TextEdit::new(
5440                                    lsp::Range::new(
5441                                        lsp::Position::new(0, 0),
5442                                        lsp::Position::new(0, 27),
5443                                    ),
5444                                    "".to_string(),
5445                                )],
5446                            ),
5447                        ]
5448                        .into_iter()
5449                        .collect(),
5450                    ),
5451                    ..Default::default()
5452                }),
5453                ..Default::default()
5454            })
5455        },
5456    );
5457
5458    // After the action is confirmed, an editor containing both modified files is opened.
5459    confirm_action.await.unwrap();
5460    let code_action_editor = workspace_b.read_with(cx_b, |workspace, cx| {
5461        workspace
5462            .active_item(cx)
5463            .unwrap()
5464            .downcast::<Editor>()
5465            .unwrap()
5466    });
5467    code_action_editor.update(cx_b, |editor, cx| {
5468        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
5469        editor.undo(&Undo, cx);
5470        assert_eq!(
5471            editor.text(cx),
5472            "mod other;\nfn main() { let foo = other::foo(); }\npub fn foo() -> usize { 4 }"
5473        );
5474        editor.redo(&Redo, cx);
5475        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
5476    });
5477}
5478
5479#[gpui::test(iterations = 10)]
5480async fn test_collaborating_with_renames(
5481    deterministic: Arc<Deterministic>,
5482    cx_a: &mut TestAppContext,
5483    cx_b: &mut TestAppContext,
5484) {
5485    deterministic.forbid_parking();
5486    let mut server = TestServer::start(&deterministic).await;
5487    let client_a = server.create_client(cx_a, "user_a").await;
5488    let client_b = server.create_client(cx_b, "user_b").await;
5489    server
5490        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5491        .await;
5492    let active_call_a = cx_a.read(ActiveCall::global);
5493
5494    cx_b.update(editor::init);
5495
5496    // Set up a fake language server.
5497    let mut language = Language::new(
5498        LanguageConfig {
5499            name: "Rust".into(),
5500            path_suffixes: vec!["rs".to_string()],
5501            ..Default::default()
5502        },
5503        Some(tree_sitter_rust::language()),
5504    );
5505    let mut fake_language_servers = language
5506        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
5507            capabilities: lsp::ServerCapabilities {
5508                rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
5509                    prepare_provider: Some(true),
5510                    work_done_progress_options: Default::default(),
5511                })),
5512                ..Default::default()
5513            },
5514            ..Default::default()
5515        }))
5516        .await;
5517    client_a.language_registry().add(Arc::new(language));
5518
5519    client_a
5520        .fs()
5521        .insert_tree(
5522            "/dir",
5523            json!({
5524                "one.rs": "const ONE: usize = 1;",
5525                "two.rs": "const TWO: usize = one::ONE + one::ONE;"
5526            }),
5527        )
5528        .await;
5529    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
5530    let project_id = active_call_a
5531        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5532        .await
5533        .unwrap();
5534    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5535
5536    let window_b =
5537        cx_b.add_window(|cx| Workspace::new(0, project_b.clone(), client_b.app_state.clone(), cx));
5538    let workspace_b = window_b.root(cx_b);
5539    let editor_b = workspace_b
5540        .update(cx_b, |workspace, cx| {
5541            workspace.open_path((worktree_id, "one.rs"), None, true, cx)
5542        })
5543        .await
5544        .unwrap()
5545        .downcast::<Editor>()
5546        .unwrap();
5547    let fake_language_server = fake_language_servers.next().await.unwrap();
5548
5549    // Move cursor to a location that can be renamed.
5550    let prepare_rename = editor_b.update(cx_b, |editor, cx| {
5551        editor.change_selections(None, cx, |s| s.select_ranges([7..7]));
5552        editor.rename(&Rename, cx).unwrap()
5553    });
5554
5555    fake_language_server
5556        .handle_request::<lsp::request::PrepareRenameRequest, _, _>(|params, _| async move {
5557            assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs");
5558            assert_eq!(params.position, lsp::Position::new(0, 7));
5559            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
5560                lsp::Position::new(0, 6),
5561                lsp::Position::new(0, 9),
5562            ))))
5563        })
5564        .next()
5565        .await
5566        .unwrap();
5567    prepare_rename.await.unwrap();
5568    editor_b.update(cx_b, |editor, cx| {
5569        use editor::ToOffset;
5570        let rename = editor.pending_rename().unwrap();
5571        let buffer = editor.buffer().read(cx).snapshot(cx);
5572        assert_eq!(
5573            rename.range.start.to_offset(&buffer)..rename.range.end.to_offset(&buffer),
5574            6..9
5575        );
5576        rename.editor.update(cx, |rename_editor, cx| {
5577            rename_editor.buffer().update(cx, |rename_buffer, cx| {
5578                rename_buffer.edit([(0..3, "THREE")], None, cx);
5579            });
5580        });
5581    });
5582
5583    let confirm_rename = workspace_b.update(cx_b, |workspace, cx| {
5584        Editor::confirm_rename(workspace, &ConfirmRename, cx).unwrap()
5585    });
5586    fake_language_server
5587        .handle_request::<lsp::request::Rename, _, _>(|params, _| async move {
5588            assert_eq!(
5589                params.text_document_position.text_document.uri.as_str(),
5590                "file:///dir/one.rs"
5591            );
5592            assert_eq!(
5593                params.text_document_position.position,
5594                lsp::Position::new(0, 6)
5595            );
5596            assert_eq!(params.new_name, "THREE");
5597            Ok(Some(lsp::WorkspaceEdit {
5598                changes: Some(
5599                    [
5600                        (
5601                            lsp::Url::from_file_path("/dir/one.rs").unwrap(),
5602                            vec![lsp::TextEdit::new(
5603                                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5604                                "THREE".to_string(),
5605                            )],
5606                        ),
5607                        (
5608                            lsp::Url::from_file_path("/dir/two.rs").unwrap(),
5609                            vec![
5610                                lsp::TextEdit::new(
5611                                    lsp::Range::new(
5612                                        lsp::Position::new(0, 24),
5613                                        lsp::Position::new(0, 27),
5614                                    ),
5615                                    "THREE".to_string(),
5616                                ),
5617                                lsp::TextEdit::new(
5618                                    lsp::Range::new(
5619                                        lsp::Position::new(0, 35),
5620                                        lsp::Position::new(0, 38),
5621                                    ),
5622                                    "THREE".to_string(),
5623                                ),
5624                            ],
5625                        ),
5626                    ]
5627                    .into_iter()
5628                    .collect(),
5629                ),
5630                ..Default::default()
5631            }))
5632        })
5633        .next()
5634        .await
5635        .unwrap();
5636    confirm_rename.await.unwrap();
5637
5638    let rename_editor = workspace_b.read_with(cx_b, |workspace, cx| {
5639        workspace
5640            .active_item(cx)
5641            .unwrap()
5642            .downcast::<Editor>()
5643            .unwrap()
5644    });
5645    rename_editor.update(cx_b, |editor, cx| {
5646        assert_eq!(
5647            editor.text(cx),
5648            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
5649        );
5650        editor.undo(&Undo, cx);
5651        assert_eq!(
5652            editor.text(cx),
5653            "const ONE: usize = 1;\nconst TWO: usize = one::ONE + one::ONE;"
5654        );
5655        editor.redo(&Redo, cx);
5656        assert_eq!(
5657            editor.text(cx),
5658            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
5659        );
5660    });
5661
5662    // Ensure temporary rename edits cannot be undone/redone.
5663    editor_b.update(cx_b, |editor, cx| {
5664        editor.undo(&Undo, cx);
5665        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
5666        editor.undo(&Undo, cx);
5667        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
5668        editor.redo(&Redo, cx);
5669        assert_eq!(editor.text(cx), "const THREE: usize = 1;");
5670    })
5671}
5672
5673#[gpui::test(iterations = 10)]
5674async fn test_language_server_statuses(
5675    deterministic: Arc<Deterministic>,
5676    cx_a: &mut TestAppContext,
5677    cx_b: &mut TestAppContext,
5678) {
5679    deterministic.forbid_parking();
5680    let mut server = TestServer::start(&deterministic).await;
5681    let client_a = server.create_client(cx_a, "user_a").await;
5682    let client_b = server.create_client(cx_b, "user_b").await;
5683    server
5684        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5685        .await;
5686    let active_call_a = cx_a.read(ActiveCall::global);
5687
5688    cx_b.update(editor::init);
5689
5690    // Set up a fake language server.
5691    let mut language = Language::new(
5692        LanguageConfig {
5693            name: "Rust".into(),
5694            path_suffixes: vec!["rs".to_string()],
5695            ..Default::default()
5696        },
5697        Some(tree_sitter_rust::language()),
5698    );
5699    let mut fake_language_servers = language
5700        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
5701            name: "the-language-server",
5702            ..Default::default()
5703        }))
5704        .await;
5705    client_a.language_registry().add(Arc::new(language));
5706
5707    client_a
5708        .fs()
5709        .insert_tree(
5710            "/dir",
5711            json!({
5712                "main.rs": "const ONE: usize = 1;",
5713            }),
5714        )
5715        .await;
5716    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
5717
5718    let _buffer_a = project_a
5719        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
5720        .await
5721        .unwrap();
5722
5723    let fake_language_server = fake_language_servers.next().await.unwrap();
5724    fake_language_server.start_progress("the-token").await;
5725    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
5726        token: lsp::NumberOrString::String("the-token".to_string()),
5727        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
5728            lsp::WorkDoneProgressReport {
5729                message: Some("the-message".to_string()),
5730                ..Default::default()
5731            },
5732        )),
5733    });
5734    deterministic.run_until_parked();
5735    project_a.read_with(cx_a, |project, _| {
5736        let status = project.language_server_statuses().next().unwrap();
5737        assert_eq!(status.name, "the-language-server");
5738        assert_eq!(status.pending_work.len(), 1);
5739        assert_eq!(
5740            status.pending_work["the-token"].message.as_ref().unwrap(),
5741            "the-message"
5742        );
5743    });
5744
5745    let project_id = active_call_a
5746        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5747        .await
5748        .unwrap();
5749    deterministic.run_until_parked();
5750    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5751    project_b.read_with(cx_b, |project, _| {
5752        let status = project.language_server_statuses().next().unwrap();
5753        assert_eq!(status.name, "the-language-server");
5754    });
5755
5756    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
5757        token: lsp::NumberOrString::String("the-token".to_string()),
5758        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
5759            lsp::WorkDoneProgressReport {
5760                message: Some("the-message-2".to_string()),
5761                ..Default::default()
5762            },
5763        )),
5764    });
5765    deterministic.run_until_parked();
5766    project_a.read_with(cx_a, |project, _| {
5767        let status = project.language_server_statuses().next().unwrap();
5768        assert_eq!(status.name, "the-language-server");
5769        assert_eq!(status.pending_work.len(), 1);
5770        assert_eq!(
5771            status.pending_work["the-token"].message.as_ref().unwrap(),
5772            "the-message-2"
5773        );
5774    });
5775    project_b.read_with(cx_b, |project, _| {
5776        let status = project.language_server_statuses().next().unwrap();
5777        assert_eq!(status.name, "the-language-server");
5778        assert_eq!(status.pending_work.len(), 1);
5779        assert_eq!(
5780            status.pending_work["the-token"].message.as_ref().unwrap(),
5781            "the-message-2"
5782        );
5783    });
5784}
5785
5786#[gpui::test(iterations = 10)]
5787async fn test_contacts(
5788    deterministic: Arc<Deterministic>,
5789    cx_a: &mut TestAppContext,
5790    cx_b: &mut TestAppContext,
5791    cx_c: &mut TestAppContext,
5792    cx_d: &mut TestAppContext,
5793) {
5794    deterministic.forbid_parking();
5795    let mut server = TestServer::start(&deterministic).await;
5796    let client_a = server.create_client(cx_a, "user_a").await;
5797    let client_b = server.create_client(cx_b, "user_b").await;
5798    let client_c = server.create_client(cx_c, "user_c").await;
5799    let client_d = server.create_client(cx_d, "user_d").await;
5800    server
5801        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
5802        .await;
5803    let active_call_a = cx_a.read(ActiveCall::global);
5804    let active_call_b = cx_b.read(ActiveCall::global);
5805    let active_call_c = cx_c.read(ActiveCall::global);
5806    let _active_call_d = cx_d.read(ActiveCall::global);
5807
5808    deterministic.run_until_parked();
5809    assert_eq!(
5810        contacts(&client_a, cx_a),
5811        [
5812            ("user_b".to_string(), "online", "free"),
5813            ("user_c".to_string(), "online", "free")
5814        ]
5815    );
5816    assert_eq!(
5817        contacts(&client_b, cx_b),
5818        [
5819            ("user_a".to_string(), "online", "free"),
5820            ("user_c".to_string(), "online", "free")
5821        ]
5822    );
5823    assert_eq!(
5824        contacts(&client_c, cx_c),
5825        [
5826            ("user_a".to_string(), "online", "free"),
5827            ("user_b".to_string(), "online", "free")
5828        ]
5829    );
5830    assert_eq!(contacts(&client_d, cx_d), []);
5831
5832    server.disconnect_client(client_c.peer_id().unwrap());
5833    server.forbid_connections();
5834    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5835    assert_eq!(
5836        contacts(&client_a, cx_a),
5837        [
5838            ("user_b".to_string(), "online", "free"),
5839            ("user_c".to_string(), "offline", "free")
5840        ]
5841    );
5842    assert_eq!(
5843        contacts(&client_b, cx_b),
5844        [
5845            ("user_a".to_string(), "online", "free"),
5846            ("user_c".to_string(), "offline", "free")
5847        ]
5848    );
5849    assert_eq!(contacts(&client_c, cx_c), []);
5850    assert_eq!(contacts(&client_d, cx_d), []);
5851
5852    server.allow_connections();
5853    client_c
5854        .authenticate_and_connect(false, &cx_c.to_async())
5855        .await
5856        .unwrap();
5857
5858    deterministic.run_until_parked();
5859    assert_eq!(
5860        contacts(&client_a, cx_a),
5861        [
5862            ("user_b".to_string(), "online", "free"),
5863            ("user_c".to_string(), "online", "free")
5864        ]
5865    );
5866    assert_eq!(
5867        contacts(&client_b, cx_b),
5868        [
5869            ("user_a".to_string(), "online", "free"),
5870            ("user_c".to_string(), "online", "free")
5871        ]
5872    );
5873    assert_eq!(
5874        contacts(&client_c, cx_c),
5875        [
5876            ("user_a".to_string(), "online", "free"),
5877            ("user_b".to_string(), "online", "free")
5878        ]
5879    );
5880    assert_eq!(contacts(&client_d, cx_d), []);
5881
5882    active_call_a
5883        .update(cx_a, |call, cx| {
5884            call.invite(client_b.user_id().unwrap(), None, cx)
5885        })
5886        .await
5887        .unwrap();
5888    deterministic.run_until_parked();
5889    assert_eq!(
5890        contacts(&client_a, cx_a),
5891        [
5892            ("user_b".to_string(), "online", "busy"),
5893            ("user_c".to_string(), "online", "free")
5894        ]
5895    );
5896    assert_eq!(
5897        contacts(&client_b, cx_b),
5898        [
5899            ("user_a".to_string(), "online", "busy"),
5900            ("user_c".to_string(), "online", "free")
5901        ]
5902    );
5903    assert_eq!(
5904        contacts(&client_c, cx_c),
5905        [
5906            ("user_a".to_string(), "online", "busy"),
5907            ("user_b".to_string(), "online", "busy")
5908        ]
5909    );
5910    assert_eq!(contacts(&client_d, cx_d), []);
5911
5912    // Client B and client D become contacts while client B is being called.
5913    server
5914        .make_contacts(&mut [(&client_b, cx_b), (&client_d, cx_d)])
5915        .await;
5916    deterministic.run_until_parked();
5917    assert_eq!(
5918        contacts(&client_a, cx_a),
5919        [
5920            ("user_b".to_string(), "online", "busy"),
5921            ("user_c".to_string(), "online", "free")
5922        ]
5923    );
5924    assert_eq!(
5925        contacts(&client_b, cx_b),
5926        [
5927            ("user_a".to_string(), "online", "busy"),
5928            ("user_c".to_string(), "online", "free"),
5929            ("user_d".to_string(), "online", "free"),
5930        ]
5931    );
5932    assert_eq!(
5933        contacts(&client_c, cx_c),
5934        [
5935            ("user_a".to_string(), "online", "busy"),
5936            ("user_b".to_string(), "online", "busy")
5937        ]
5938    );
5939    assert_eq!(
5940        contacts(&client_d, cx_d),
5941        [("user_b".to_string(), "online", "busy")]
5942    );
5943
5944    active_call_b.update(cx_b, |call, cx| call.decline_incoming(cx).unwrap());
5945    deterministic.run_until_parked();
5946    assert_eq!(
5947        contacts(&client_a, cx_a),
5948        [
5949            ("user_b".to_string(), "online", "free"),
5950            ("user_c".to_string(), "online", "free")
5951        ]
5952    );
5953    assert_eq!(
5954        contacts(&client_b, cx_b),
5955        [
5956            ("user_a".to_string(), "online", "free"),
5957            ("user_c".to_string(), "online", "free"),
5958            ("user_d".to_string(), "online", "free")
5959        ]
5960    );
5961    assert_eq!(
5962        contacts(&client_c, cx_c),
5963        [
5964            ("user_a".to_string(), "online", "free"),
5965            ("user_b".to_string(), "online", "free")
5966        ]
5967    );
5968    assert_eq!(
5969        contacts(&client_d, cx_d),
5970        [("user_b".to_string(), "online", "free")]
5971    );
5972
5973    active_call_c
5974        .update(cx_c, |call, cx| {
5975            call.invite(client_a.user_id().unwrap(), None, cx)
5976        })
5977        .await
5978        .unwrap();
5979    deterministic.run_until_parked();
5980    assert_eq!(
5981        contacts(&client_a, cx_a),
5982        [
5983            ("user_b".to_string(), "online", "free"),
5984            ("user_c".to_string(), "online", "busy")
5985        ]
5986    );
5987    assert_eq!(
5988        contacts(&client_b, cx_b),
5989        [
5990            ("user_a".to_string(), "online", "busy"),
5991            ("user_c".to_string(), "online", "busy"),
5992            ("user_d".to_string(), "online", "free")
5993        ]
5994    );
5995    assert_eq!(
5996        contacts(&client_c, cx_c),
5997        [
5998            ("user_a".to_string(), "online", "busy"),
5999            ("user_b".to_string(), "online", "free")
6000        ]
6001    );
6002    assert_eq!(
6003        contacts(&client_d, cx_d),
6004        [("user_b".to_string(), "online", "free")]
6005    );
6006
6007    active_call_a
6008        .update(cx_a, |call, cx| call.accept_incoming(cx))
6009        .await
6010        .unwrap();
6011    deterministic.run_until_parked();
6012    assert_eq!(
6013        contacts(&client_a, cx_a),
6014        [
6015            ("user_b".to_string(), "online", "free"),
6016            ("user_c".to_string(), "online", "busy")
6017        ]
6018    );
6019    assert_eq!(
6020        contacts(&client_b, cx_b),
6021        [
6022            ("user_a".to_string(), "online", "busy"),
6023            ("user_c".to_string(), "online", "busy"),
6024            ("user_d".to_string(), "online", "free")
6025        ]
6026    );
6027    assert_eq!(
6028        contacts(&client_c, cx_c),
6029        [
6030            ("user_a".to_string(), "online", "busy"),
6031            ("user_b".to_string(), "online", "free")
6032        ]
6033    );
6034    assert_eq!(
6035        contacts(&client_d, cx_d),
6036        [("user_b".to_string(), "online", "free")]
6037    );
6038
6039    active_call_a
6040        .update(cx_a, |call, cx| {
6041            call.invite(client_b.user_id().unwrap(), None, cx)
6042        })
6043        .await
6044        .unwrap();
6045    deterministic.run_until_parked();
6046    assert_eq!(
6047        contacts(&client_a, cx_a),
6048        [
6049            ("user_b".to_string(), "online", "busy"),
6050            ("user_c".to_string(), "online", "busy")
6051        ]
6052    );
6053    assert_eq!(
6054        contacts(&client_b, cx_b),
6055        [
6056            ("user_a".to_string(), "online", "busy"),
6057            ("user_c".to_string(), "online", "busy"),
6058            ("user_d".to_string(), "online", "free")
6059        ]
6060    );
6061    assert_eq!(
6062        contacts(&client_c, cx_c),
6063        [
6064            ("user_a".to_string(), "online", "busy"),
6065            ("user_b".to_string(), "online", "busy")
6066        ]
6067    );
6068    assert_eq!(
6069        contacts(&client_d, cx_d),
6070        [("user_b".to_string(), "online", "busy")]
6071    );
6072
6073    active_call_a
6074        .update(cx_a, |call, cx| call.hang_up(cx))
6075        .await
6076        .unwrap();
6077    deterministic.run_until_parked();
6078    assert_eq!(
6079        contacts(&client_a, cx_a),
6080        [
6081            ("user_b".to_string(), "online", "free"),
6082            ("user_c".to_string(), "online", "free")
6083        ]
6084    );
6085    assert_eq!(
6086        contacts(&client_b, cx_b),
6087        [
6088            ("user_a".to_string(), "online", "free"),
6089            ("user_c".to_string(), "online", "free"),
6090            ("user_d".to_string(), "online", "free")
6091        ]
6092    );
6093    assert_eq!(
6094        contacts(&client_c, cx_c),
6095        [
6096            ("user_a".to_string(), "online", "free"),
6097            ("user_b".to_string(), "online", "free")
6098        ]
6099    );
6100    assert_eq!(
6101        contacts(&client_d, cx_d),
6102        [("user_b".to_string(), "online", "free")]
6103    );
6104
6105    active_call_a
6106        .update(cx_a, |call, cx| {
6107            call.invite(client_b.user_id().unwrap(), None, cx)
6108        })
6109        .await
6110        .unwrap();
6111    deterministic.run_until_parked();
6112    assert_eq!(
6113        contacts(&client_a, cx_a),
6114        [
6115            ("user_b".to_string(), "online", "busy"),
6116            ("user_c".to_string(), "online", "free")
6117        ]
6118    );
6119    assert_eq!(
6120        contacts(&client_b, cx_b),
6121        [
6122            ("user_a".to_string(), "online", "busy"),
6123            ("user_c".to_string(), "online", "free"),
6124            ("user_d".to_string(), "online", "free")
6125        ]
6126    );
6127    assert_eq!(
6128        contacts(&client_c, cx_c),
6129        [
6130            ("user_a".to_string(), "online", "busy"),
6131            ("user_b".to_string(), "online", "busy")
6132        ]
6133    );
6134    assert_eq!(
6135        contacts(&client_d, cx_d),
6136        [("user_b".to_string(), "online", "busy")]
6137    );
6138
6139    server.forbid_connections();
6140    server.disconnect_client(client_a.peer_id().unwrap());
6141    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
6142    assert_eq!(contacts(&client_a, cx_a), []);
6143    assert_eq!(
6144        contacts(&client_b, cx_b),
6145        [
6146            ("user_a".to_string(), "offline", "free"),
6147            ("user_c".to_string(), "online", "free"),
6148            ("user_d".to_string(), "online", "free")
6149        ]
6150    );
6151    assert_eq!(
6152        contacts(&client_c, cx_c),
6153        [
6154            ("user_a".to_string(), "offline", "free"),
6155            ("user_b".to_string(), "online", "free")
6156        ]
6157    );
6158    assert_eq!(
6159        contacts(&client_d, cx_d),
6160        [("user_b".to_string(), "online", "free")]
6161    );
6162
6163    // Test removing a contact
6164    client_b
6165        .user_store()
6166        .update(cx_b, |store, cx| {
6167            store.remove_contact(client_c.user_id().unwrap(), cx)
6168        })
6169        .await
6170        .unwrap();
6171    deterministic.run_until_parked();
6172    assert_eq!(
6173        contacts(&client_b, cx_b),
6174        [
6175            ("user_a".to_string(), "offline", "free"),
6176            ("user_d".to_string(), "online", "free")
6177        ]
6178    );
6179    assert_eq!(
6180        contacts(&client_c, cx_c),
6181        [("user_a".to_string(), "offline", "free"),]
6182    );
6183
6184    fn contacts(
6185        client: &TestClient,
6186        cx: &TestAppContext,
6187    ) -> Vec<(String, &'static str, &'static str)> {
6188        client.user_store().read_with(cx, |store, _| {
6189            store
6190                .contacts()
6191                .iter()
6192                .map(|contact| {
6193                    (
6194                        contact.user.github_login.clone(),
6195                        if contact.online { "online" } else { "offline" },
6196                        if contact.busy { "busy" } else { "free" },
6197                    )
6198                })
6199                .collect()
6200        })
6201    }
6202}
6203
6204#[gpui::test(iterations = 10)]
6205async fn test_contact_requests(
6206    deterministic: Arc<Deterministic>,
6207    cx_a: &mut TestAppContext,
6208    cx_a2: &mut TestAppContext,
6209    cx_b: &mut TestAppContext,
6210    cx_b2: &mut TestAppContext,
6211    cx_c: &mut TestAppContext,
6212    cx_c2: &mut TestAppContext,
6213) {
6214    deterministic.forbid_parking();
6215
6216    // Connect to a server as 3 clients.
6217    let mut server = TestServer::start(&deterministic).await;
6218    let client_a = server.create_client(cx_a, "user_a").await;
6219    let client_a2 = server.create_client(cx_a2, "user_a").await;
6220    let client_b = server.create_client(cx_b, "user_b").await;
6221    let client_b2 = server.create_client(cx_b2, "user_b").await;
6222    let client_c = server.create_client(cx_c, "user_c").await;
6223    let client_c2 = server.create_client(cx_c2, "user_c").await;
6224
6225    assert_eq!(client_a.user_id().unwrap(), client_a2.user_id().unwrap());
6226    assert_eq!(client_b.user_id().unwrap(), client_b2.user_id().unwrap());
6227    assert_eq!(client_c.user_id().unwrap(), client_c2.user_id().unwrap());
6228
6229    // User A and User C request that user B become their contact.
6230    client_a
6231        .user_store()
6232        .update(cx_a, |store, cx| {
6233            store.request_contact(client_b.user_id().unwrap(), cx)
6234        })
6235        .await
6236        .unwrap();
6237    client_c
6238        .user_store()
6239        .update(cx_c, |store, cx| {
6240            store.request_contact(client_b.user_id().unwrap(), cx)
6241        })
6242        .await
6243        .unwrap();
6244    deterministic.run_until_parked();
6245
6246    // All users see the pending request appear in all their clients.
6247    assert_eq!(
6248        client_a.summarize_contacts(cx_a).outgoing_requests,
6249        &["user_b"]
6250    );
6251    assert_eq!(
6252        client_a2.summarize_contacts(cx_a2).outgoing_requests,
6253        &["user_b"]
6254    );
6255    assert_eq!(
6256        client_b.summarize_contacts(cx_b).incoming_requests,
6257        &["user_a", "user_c"]
6258    );
6259    assert_eq!(
6260        client_b2.summarize_contacts(cx_b2).incoming_requests,
6261        &["user_a", "user_c"]
6262    );
6263    assert_eq!(
6264        client_c.summarize_contacts(cx_c).outgoing_requests,
6265        &["user_b"]
6266    );
6267    assert_eq!(
6268        client_c2.summarize_contacts(cx_c2).outgoing_requests,
6269        &["user_b"]
6270    );
6271
6272    // Contact requests are present upon connecting (tested here via disconnect/reconnect)
6273    disconnect_and_reconnect(&client_a, cx_a).await;
6274    disconnect_and_reconnect(&client_b, cx_b).await;
6275    disconnect_and_reconnect(&client_c, cx_c).await;
6276    deterministic.run_until_parked();
6277    assert_eq!(
6278        client_a.summarize_contacts(cx_a).outgoing_requests,
6279        &["user_b"]
6280    );
6281    assert_eq!(
6282        client_b.summarize_contacts(cx_b).incoming_requests,
6283        &["user_a", "user_c"]
6284    );
6285    assert_eq!(
6286        client_c.summarize_contacts(cx_c).outgoing_requests,
6287        &["user_b"]
6288    );
6289
6290    // User B accepts the request from user A.
6291    client_b
6292        .user_store()
6293        .update(cx_b, |store, cx| {
6294            store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
6295        })
6296        .await
6297        .unwrap();
6298
6299    deterministic.run_until_parked();
6300
6301    // User B sees user A as their contact now in all client, and the incoming request from them is removed.
6302    let contacts_b = client_b.summarize_contacts(cx_b);
6303    assert_eq!(contacts_b.current, &["user_a"]);
6304    assert_eq!(contacts_b.incoming_requests, &["user_c"]);
6305    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
6306    assert_eq!(contacts_b2.current, &["user_a"]);
6307    assert_eq!(contacts_b2.incoming_requests, &["user_c"]);
6308
6309    // User A sees user B as their contact now in all clients, and the outgoing request to them is removed.
6310    let contacts_a = client_a.summarize_contacts(cx_a);
6311    assert_eq!(contacts_a.current, &["user_b"]);
6312    assert!(contacts_a.outgoing_requests.is_empty());
6313    let contacts_a2 = client_a2.summarize_contacts(cx_a2);
6314    assert_eq!(contacts_a2.current, &["user_b"]);
6315    assert!(contacts_a2.outgoing_requests.is_empty());
6316
6317    // Contacts are present upon connecting (tested here via disconnect/reconnect)
6318    disconnect_and_reconnect(&client_a, cx_a).await;
6319    disconnect_and_reconnect(&client_b, cx_b).await;
6320    disconnect_and_reconnect(&client_c, cx_c).await;
6321    deterministic.run_until_parked();
6322    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
6323    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
6324    assert_eq!(
6325        client_b.summarize_contacts(cx_b).incoming_requests,
6326        &["user_c"]
6327    );
6328    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
6329    assert_eq!(
6330        client_c.summarize_contacts(cx_c).outgoing_requests,
6331        &["user_b"]
6332    );
6333
6334    // User B rejects the request from user C.
6335    client_b
6336        .user_store()
6337        .update(cx_b, |store, cx| {
6338            store.respond_to_contact_request(client_c.user_id().unwrap(), false, cx)
6339        })
6340        .await
6341        .unwrap();
6342
6343    deterministic.run_until_parked();
6344
6345    // User B doesn't see user C as their contact, and the incoming request from them is removed.
6346    let contacts_b = client_b.summarize_contacts(cx_b);
6347    assert_eq!(contacts_b.current, &["user_a"]);
6348    assert!(contacts_b.incoming_requests.is_empty());
6349    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
6350    assert_eq!(contacts_b2.current, &["user_a"]);
6351    assert!(contacts_b2.incoming_requests.is_empty());
6352
6353    // User C doesn't see user B as their contact, and the outgoing request to them is removed.
6354    let contacts_c = client_c.summarize_contacts(cx_c);
6355    assert!(contacts_c.current.is_empty());
6356    assert!(contacts_c.outgoing_requests.is_empty());
6357    let contacts_c2 = client_c2.summarize_contacts(cx_c2);
6358    assert!(contacts_c2.current.is_empty());
6359    assert!(contacts_c2.outgoing_requests.is_empty());
6360
6361    // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect)
6362    disconnect_and_reconnect(&client_a, cx_a).await;
6363    disconnect_and_reconnect(&client_b, cx_b).await;
6364    disconnect_and_reconnect(&client_c, cx_c).await;
6365    deterministic.run_until_parked();
6366    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
6367    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
6368    assert!(client_b
6369        .summarize_contacts(cx_b)
6370        .incoming_requests
6371        .is_empty());
6372    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
6373    assert!(client_c
6374        .summarize_contacts(cx_c)
6375        .outgoing_requests
6376        .is_empty());
6377
6378    async fn disconnect_and_reconnect(client: &TestClient, cx: &mut TestAppContext) {
6379        client.disconnect(&cx.to_async());
6380        client.clear_contacts(cx).await;
6381        client
6382            .authenticate_and_connect(false, &cx.to_async())
6383            .await
6384            .unwrap();
6385    }
6386}
6387
6388#[gpui::test(iterations = 10)]
6389async fn test_basic_following(
6390    deterministic: Arc<Deterministic>,
6391    cx_a: &mut TestAppContext,
6392    cx_b: &mut TestAppContext,
6393    cx_c: &mut TestAppContext,
6394    cx_d: &mut TestAppContext,
6395) {
6396    deterministic.forbid_parking();
6397
6398    let mut server = TestServer::start(&deterministic).await;
6399    let client_a = server.create_client(cx_a, "user_a").await;
6400    let client_b = server.create_client(cx_b, "user_b").await;
6401    let client_c = server.create_client(cx_c, "user_c").await;
6402    let client_d = server.create_client(cx_d, "user_d").await;
6403    server
6404        .create_room(&mut [
6405            (&client_a, cx_a),
6406            (&client_b, cx_b),
6407            (&client_c, cx_c),
6408            (&client_d, cx_d),
6409        ])
6410        .await;
6411    let active_call_a = cx_a.read(ActiveCall::global);
6412    let active_call_b = cx_b.read(ActiveCall::global);
6413
6414    cx_a.update(editor::init);
6415    cx_b.update(editor::init);
6416
6417    client_a
6418        .fs()
6419        .insert_tree(
6420            "/a",
6421            json!({
6422                "1.txt": "one\none\none",
6423                "2.txt": "two\ntwo\ntwo",
6424                "3.txt": "three\nthree\nthree",
6425            }),
6426        )
6427        .await;
6428    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
6429    active_call_a
6430        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
6431        .await
6432        .unwrap();
6433
6434    let project_id = active_call_a
6435        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6436        .await
6437        .unwrap();
6438    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6439    active_call_b
6440        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6441        .await
6442        .unwrap();
6443
6444    let window_a = client_a.build_workspace(&project_a, cx_a);
6445    let workspace_a = window_a.root(cx_a);
6446    let window_b = client_b.build_workspace(&project_b, cx_b);
6447    let workspace_b = window_b.root(cx_b);
6448
6449    // Client A opens some editors.
6450    let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
6451    let editor_a1 = workspace_a
6452        .update(cx_a, |workspace, cx| {
6453            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6454        })
6455        .await
6456        .unwrap()
6457        .downcast::<Editor>()
6458        .unwrap();
6459    let editor_a2 = workspace_a
6460        .update(cx_a, |workspace, cx| {
6461            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
6462        })
6463        .await
6464        .unwrap()
6465        .downcast::<Editor>()
6466        .unwrap();
6467
6468    // Client B opens an editor.
6469    let editor_b1 = workspace_b
6470        .update(cx_b, |workspace, cx| {
6471            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6472        })
6473        .await
6474        .unwrap()
6475        .downcast::<Editor>()
6476        .unwrap();
6477
6478    let peer_id_a = client_a.peer_id().unwrap();
6479    let peer_id_b = client_b.peer_id().unwrap();
6480    let peer_id_c = client_c.peer_id().unwrap();
6481    let peer_id_d = client_d.peer_id().unwrap();
6482
6483    // Client A updates their selections in those editors
6484    editor_a1.update(cx_a, |editor, cx| {
6485        editor.handle_input("a", cx);
6486        editor.handle_input("b", cx);
6487        editor.handle_input("c", cx);
6488        editor.select_left(&Default::default(), cx);
6489        assert_eq!(editor.selections.ranges(cx), vec![3..2]);
6490    });
6491    editor_a2.update(cx_a, |editor, cx| {
6492        editor.handle_input("d", cx);
6493        editor.handle_input("e", cx);
6494        editor.select_left(&Default::default(), cx);
6495        assert_eq!(editor.selections.ranges(cx), vec![2..1]);
6496    });
6497
6498    // When client B starts following client A, all visible view states are replicated to client B.
6499    workspace_b
6500        .update(cx_b, |workspace, cx| {
6501            workspace.toggle_follow(peer_id_a, cx).unwrap()
6502        })
6503        .await
6504        .unwrap();
6505
6506    cx_c.foreground().run_until_parked();
6507    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
6508        workspace
6509            .active_item(cx)
6510            .unwrap()
6511            .downcast::<Editor>()
6512            .unwrap()
6513    });
6514    assert_eq!(
6515        cx_b.read(|cx| editor_b2.project_path(cx)),
6516        Some((worktree_id, "2.txt").into())
6517    );
6518    assert_eq!(
6519        editor_b2.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
6520        vec![2..1]
6521    );
6522    assert_eq!(
6523        editor_b1.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
6524        vec![3..2]
6525    );
6526
6527    cx_c.foreground().run_until_parked();
6528    let active_call_c = cx_c.read(ActiveCall::global);
6529    let project_c = client_c.build_remote_project(project_id, cx_c).await;
6530    let window_c = client_c.build_workspace(&project_c, cx_c);
6531    let workspace_c = window_c.root(cx_c);
6532    active_call_c
6533        .update(cx_c, |call, cx| call.set_location(Some(&project_c), cx))
6534        .await
6535        .unwrap();
6536    drop(project_c);
6537
6538    // Client C also follows client A.
6539    workspace_c
6540        .update(cx_c, |workspace, cx| {
6541            workspace.toggle_follow(peer_id_a, cx).unwrap()
6542        })
6543        .await
6544        .unwrap();
6545
6546    cx_d.foreground().run_until_parked();
6547    let active_call_d = cx_d.read(ActiveCall::global);
6548    let project_d = client_d.build_remote_project(project_id, cx_d).await;
6549    let workspace_d = client_d.build_workspace(&project_d, cx_d).root(cx_d);
6550    active_call_d
6551        .update(cx_d, |call, cx| call.set_location(Some(&project_d), cx))
6552        .await
6553        .unwrap();
6554    drop(project_d);
6555
6556    // All clients see that clients B and C are following client A.
6557    cx_c.foreground().run_until_parked();
6558    for (name, active_call, cx) in [
6559        ("A", &active_call_a, &cx_a),
6560        ("B", &active_call_b, &cx_b),
6561        ("C", &active_call_c, &cx_c),
6562        ("D", &active_call_d, &cx_d),
6563    ] {
6564        active_call.read_with(*cx, |call, cx| {
6565            let room = call.room().unwrap().read(cx);
6566            assert_eq!(
6567                room.followers_for(peer_id_a, project_id),
6568                &[peer_id_b, peer_id_c],
6569                "checking followers for A as {name}"
6570            );
6571        });
6572    }
6573
6574    // Client C unfollows client A.
6575    workspace_c.update(cx_c, |workspace, cx| {
6576        workspace.toggle_follow(peer_id_a, cx);
6577    });
6578
6579    // All clients see that clients B is following client A.
6580    cx_c.foreground().run_until_parked();
6581    for (name, active_call, cx) in [
6582        ("A", &active_call_a, &cx_a),
6583        ("B", &active_call_b, &cx_b),
6584        ("C", &active_call_c, &cx_c),
6585        ("D", &active_call_d, &cx_d),
6586    ] {
6587        active_call.read_with(*cx, |call, cx| {
6588            let room = call.room().unwrap().read(cx);
6589            assert_eq!(
6590                room.followers_for(peer_id_a, project_id),
6591                &[peer_id_b],
6592                "checking followers for A as {name}"
6593            );
6594        });
6595    }
6596
6597    // Client C re-follows client A.
6598    workspace_c.update(cx_c, |workspace, cx| {
6599        workspace.toggle_follow(peer_id_a, cx);
6600    });
6601
6602    // All clients see that clients B and C are following client A.
6603    cx_c.foreground().run_until_parked();
6604    for (name, active_call, cx) in [
6605        ("A", &active_call_a, &cx_a),
6606        ("B", &active_call_b, &cx_b),
6607        ("C", &active_call_c, &cx_c),
6608        ("D", &active_call_d, &cx_d),
6609    ] {
6610        active_call.read_with(*cx, |call, cx| {
6611            let room = call.room().unwrap().read(cx);
6612            assert_eq!(
6613                room.followers_for(peer_id_a, project_id),
6614                &[peer_id_b, peer_id_c],
6615                "checking followers for A as {name}"
6616            );
6617        });
6618    }
6619
6620    // Client D follows client C.
6621    workspace_d
6622        .update(cx_d, |workspace, cx| {
6623            workspace.toggle_follow(peer_id_c, cx).unwrap()
6624        })
6625        .await
6626        .unwrap();
6627
6628    // All clients see that D is following C
6629    cx_d.foreground().run_until_parked();
6630    for (name, active_call, cx) in [
6631        ("A", &active_call_a, &cx_a),
6632        ("B", &active_call_b, &cx_b),
6633        ("C", &active_call_c, &cx_c),
6634        ("D", &active_call_d, &cx_d),
6635    ] {
6636        active_call.read_with(*cx, |call, cx| {
6637            let room = call.room().unwrap().read(cx);
6638            assert_eq!(
6639                room.followers_for(peer_id_c, project_id),
6640                &[peer_id_d],
6641                "checking followers for C as {name}"
6642            );
6643        });
6644    }
6645
6646    // Client C closes the project.
6647    window_c.remove(cx_c);
6648    cx_c.drop_last(workspace_c);
6649
6650    // Clients A and B see that client B is following A, and client C is not present in the followers.
6651    cx_c.foreground().run_until_parked();
6652    for (name, active_call, cx) in [("A", &active_call_a, &cx_a), ("B", &active_call_b, &cx_b)] {
6653        active_call.read_with(*cx, |call, cx| {
6654            let room = call.room().unwrap().read(cx);
6655            assert_eq!(
6656                room.followers_for(peer_id_a, project_id),
6657                &[peer_id_b],
6658                "checking followers for A as {name}"
6659            );
6660        });
6661    }
6662
6663    // All clients see that no-one is following C
6664    for (name, active_call, cx) in [
6665        ("A", &active_call_a, &cx_a),
6666        ("B", &active_call_b, &cx_b),
6667        ("C", &active_call_c, &cx_c),
6668        ("D", &active_call_d, &cx_d),
6669    ] {
6670        active_call.read_with(*cx, |call, cx| {
6671            let room = call.room().unwrap().read(cx);
6672            assert_eq!(
6673                room.followers_for(peer_id_c, project_id),
6674                &[],
6675                "checking followers for C as {name}"
6676            );
6677        });
6678    }
6679
6680    // When client A activates a different editor, client B does so as well.
6681    workspace_a.update(cx_a, |workspace, cx| {
6682        workspace.activate_item(&editor_a1, cx)
6683    });
6684    deterministic.run_until_parked();
6685    workspace_b.read_with(cx_b, |workspace, cx| {
6686        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
6687    });
6688
6689    // When client A opens a multibuffer, client B does so as well.
6690    let multibuffer_a = cx_a.add_model(|cx| {
6691        let buffer_a1 = project_a.update(cx, |project, cx| {
6692            project
6693                .get_open_buffer(&(worktree_id, "1.txt").into(), cx)
6694                .unwrap()
6695        });
6696        let buffer_a2 = project_a.update(cx, |project, cx| {
6697            project
6698                .get_open_buffer(&(worktree_id, "2.txt").into(), cx)
6699                .unwrap()
6700        });
6701        let mut result = MultiBuffer::new(0);
6702        result.push_excerpts(
6703            buffer_a1,
6704            [ExcerptRange {
6705                context: 0..3,
6706                primary: None,
6707            }],
6708            cx,
6709        );
6710        result.push_excerpts(
6711            buffer_a2,
6712            [ExcerptRange {
6713                context: 4..7,
6714                primary: None,
6715            }],
6716            cx,
6717        );
6718        result
6719    });
6720    let multibuffer_editor_a = workspace_a.update(cx_a, |workspace, cx| {
6721        let editor =
6722            cx.add_view(|cx| Editor::for_multibuffer(multibuffer_a, Some(project_a.clone()), cx));
6723        workspace.add_item(Box::new(editor.clone()), cx);
6724        editor
6725    });
6726    deterministic.run_until_parked();
6727    let multibuffer_editor_b = workspace_b.read_with(cx_b, |workspace, cx| {
6728        workspace
6729            .active_item(cx)
6730            .unwrap()
6731            .downcast::<Editor>()
6732            .unwrap()
6733    });
6734    assert_eq!(
6735        multibuffer_editor_a.read_with(cx_a, |editor, cx| editor.text(cx)),
6736        multibuffer_editor_b.read_with(cx_b, |editor, cx| editor.text(cx)),
6737    );
6738
6739    // When client A navigates back and forth, client B does so as well.
6740    workspace_a
6741        .update(cx_a, |workspace, cx| {
6742            workspace.go_back(workspace.active_pane().downgrade(), cx)
6743        })
6744        .await
6745        .unwrap();
6746    deterministic.run_until_parked();
6747    workspace_b.read_with(cx_b, |workspace, cx| {
6748        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
6749    });
6750
6751    workspace_a
6752        .update(cx_a, |workspace, cx| {
6753            workspace.go_back(workspace.active_pane().downgrade(), cx)
6754        })
6755        .await
6756        .unwrap();
6757    deterministic.run_until_parked();
6758    workspace_b.read_with(cx_b, |workspace, cx| {
6759        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b2.id());
6760    });
6761
6762    workspace_a
6763        .update(cx_a, |workspace, cx| {
6764            workspace.go_forward(workspace.active_pane().downgrade(), cx)
6765        })
6766        .await
6767        .unwrap();
6768    deterministic.run_until_parked();
6769    workspace_b.read_with(cx_b, |workspace, cx| {
6770        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
6771    });
6772
6773    // Changes to client A's editor are reflected on client B.
6774    editor_a1.update(cx_a, |editor, cx| {
6775        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2]));
6776    });
6777    deterministic.run_until_parked();
6778    editor_b1.read_with(cx_b, |editor, cx| {
6779        assert_eq!(editor.selections.ranges(cx), &[1..1, 2..2]);
6780    });
6781
6782    editor_a1.update(cx_a, |editor, cx| editor.set_text("TWO", cx));
6783    deterministic.run_until_parked();
6784    editor_b1.read_with(cx_b, |editor, cx| assert_eq!(editor.text(cx), "TWO"));
6785
6786    editor_a1.update(cx_a, |editor, cx| {
6787        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
6788        editor.set_scroll_position(vec2f(0., 100.), cx);
6789    });
6790    deterministic.run_until_parked();
6791    editor_b1.read_with(cx_b, |editor, cx| {
6792        assert_eq!(editor.selections.ranges(cx), &[3..3]);
6793    });
6794
6795    // After unfollowing, client B stops receiving updates from client A.
6796    workspace_b.update(cx_b, |workspace, cx| {
6797        workspace.unfollow(&workspace.active_pane().clone(), cx)
6798    });
6799    workspace_a.update(cx_a, |workspace, cx| {
6800        workspace.activate_item(&editor_a2, cx)
6801    });
6802    deterministic.run_until_parked();
6803    assert_eq!(
6804        workspace_b.read_with(cx_b, |workspace, cx| workspace
6805            .active_item(cx)
6806            .unwrap()
6807            .id()),
6808        editor_b1.id()
6809    );
6810
6811    // Client A starts following client B.
6812    workspace_a
6813        .update(cx_a, |workspace, cx| {
6814            workspace.toggle_follow(peer_id_b, cx).unwrap()
6815        })
6816        .await
6817        .unwrap();
6818    assert_eq!(
6819        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
6820        Some(peer_id_b)
6821    );
6822    assert_eq!(
6823        workspace_a.read_with(cx_a, |workspace, cx| workspace
6824            .active_item(cx)
6825            .unwrap()
6826            .id()),
6827        editor_a1.id()
6828    );
6829
6830    // Client B activates an external window, which causes a new screen-sharing item to be added to the pane.
6831    let display = MacOSDisplay::new();
6832    active_call_b
6833        .update(cx_b, |call, cx| call.set_location(None, cx))
6834        .await
6835        .unwrap();
6836    active_call_b
6837        .update(cx_b, |call, cx| {
6838            call.room().unwrap().update(cx, |room, cx| {
6839                room.set_display_sources(vec![display.clone()]);
6840                room.share_screen(cx)
6841            })
6842        })
6843        .await
6844        .unwrap();
6845    deterministic.run_until_parked();
6846    let shared_screen = workspace_a.read_with(cx_a, |workspace, cx| {
6847        workspace
6848            .active_item(cx)
6849            .unwrap()
6850            .downcast::<SharedScreen>()
6851            .unwrap()
6852    });
6853
6854    // Client B activates Zed again, which causes the previous editor to become focused again.
6855    active_call_b
6856        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6857        .await
6858        .unwrap();
6859    deterministic.run_until_parked();
6860    workspace_a.read_with(cx_a, |workspace, cx| {
6861        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_a1.id())
6862    });
6863
6864    // Client B activates a multibuffer that was created by following client A. Client A returns to that multibuffer.
6865    workspace_b.update(cx_b, |workspace, cx| {
6866        workspace.activate_item(&multibuffer_editor_b, cx)
6867    });
6868    deterministic.run_until_parked();
6869    workspace_a.read_with(cx_a, |workspace, cx| {
6870        assert_eq!(
6871            workspace.active_item(cx).unwrap().id(),
6872            multibuffer_editor_a.id()
6873        )
6874    });
6875
6876    // Client B activates a panel, and the previously-opened screen-sharing item gets activated.
6877    let panel = window_b.add_view(cx_b, |_| TestPanel::new(DockPosition::Left));
6878    workspace_b.update(cx_b, |workspace, cx| {
6879        workspace.add_panel(panel, cx);
6880        workspace.toggle_panel_focus::<TestPanel>(cx);
6881    });
6882    deterministic.run_until_parked();
6883    assert_eq!(
6884        workspace_a.read_with(cx_a, |workspace, cx| workspace
6885            .active_item(cx)
6886            .unwrap()
6887            .id()),
6888        shared_screen.id()
6889    );
6890
6891    // Toggling the focus back to the pane causes client A to return to the multibuffer.
6892    workspace_b.update(cx_b, |workspace, cx| {
6893        workspace.toggle_panel_focus::<TestPanel>(cx);
6894    });
6895    deterministic.run_until_parked();
6896    workspace_a.read_with(cx_a, |workspace, cx| {
6897        assert_eq!(
6898            workspace.active_item(cx).unwrap().id(),
6899            multibuffer_editor_a.id()
6900        )
6901    });
6902
6903    // Client B activates an item that doesn't implement following,
6904    // so the previously-opened screen-sharing item gets activated.
6905    let unfollowable_item = window_b.add_view(cx_b, |_| TestItem::new());
6906    workspace_b.update(cx_b, |workspace, cx| {
6907        workspace.active_pane().update(cx, |pane, cx| {
6908            pane.add_item(Box::new(unfollowable_item), true, true, None, cx)
6909        })
6910    });
6911    deterministic.run_until_parked();
6912    assert_eq!(
6913        workspace_a.read_with(cx_a, |workspace, cx| workspace
6914            .active_item(cx)
6915            .unwrap()
6916            .id()),
6917        shared_screen.id()
6918    );
6919
6920    // Following interrupts when client B disconnects.
6921    client_b.disconnect(&cx_b.to_async());
6922    deterministic.advance_clock(RECONNECT_TIMEOUT);
6923    assert_eq!(
6924        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
6925        None
6926    );
6927}
6928
6929#[gpui::test(iterations = 10)]
6930async fn test_join_call_after_screen_was_shared(
6931    deterministic: Arc<Deterministic>,
6932    cx_a: &mut TestAppContext,
6933    cx_b: &mut TestAppContext,
6934) {
6935    deterministic.forbid_parking();
6936    let mut server = TestServer::start(&deterministic).await;
6937
6938    let client_a = server.create_client(cx_a, "user_a").await;
6939    let client_b = server.create_client(cx_b, "user_b").await;
6940    server
6941        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6942        .await;
6943
6944    let active_call_a = cx_a.read(ActiveCall::global);
6945    let active_call_b = cx_b.read(ActiveCall::global);
6946
6947    // Call users B and C from client A.
6948    active_call_a
6949        .update(cx_a, |call, cx| {
6950            call.invite(client_b.user_id().unwrap(), None, cx)
6951        })
6952        .await
6953        .unwrap();
6954    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
6955    deterministic.run_until_parked();
6956    assert_eq!(
6957        room_participants(&room_a, cx_a),
6958        RoomParticipants {
6959            remote: Default::default(),
6960            pending: vec!["user_b".to_string()]
6961        }
6962    );
6963
6964    // User B receives the call.
6965    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
6966    let call_b = incoming_call_b.next().await.unwrap().unwrap();
6967    assert_eq!(call_b.calling_user.github_login, "user_a");
6968
6969    // User A shares their screen
6970    let display = MacOSDisplay::new();
6971    active_call_a
6972        .update(cx_a, |call, cx| {
6973            call.room().unwrap().update(cx, |room, cx| {
6974                room.set_display_sources(vec![display.clone()]);
6975                room.share_screen(cx)
6976            })
6977        })
6978        .await
6979        .unwrap();
6980
6981    client_b.user_store().update(cx_b, |user_store, _| {
6982        user_store.clear_cache();
6983    });
6984
6985    // User B joins the room
6986    active_call_b
6987        .update(cx_b, |call, cx| call.accept_incoming(cx))
6988        .await
6989        .unwrap();
6990    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
6991    assert!(incoming_call_b.next().await.unwrap().is_none());
6992
6993    deterministic.run_until_parked();
6994    assert_eq!(
6995        room_participants(&room_a, cx_a),
6996        RoomParticipants {
6997            remote: vec!["user_b".to_string()],
6998            pending: vec![],
6999        }
7000    );
7001    assert_eq!(
7002        room_participants(&room_b, cx_b),
7003        RoomParticipants {
7004            remote: vec!["user_a".to_string()],
7005            pending: vec![],
7006        }
7007    );
7008
7009    // Ensure User B sees User A's screenshare.
7010    room_b.read_with(cx_b, |room, _| {
7011        assert_eq!(
7012            room.remote_participants()
7013                .get(&client_a.user_id().unwrap())
7014                .unwrap()
7015                .video_tracks
7016                .len(),
7017            1
7018        );
7019    });
7020}
7021
7022#[gpui::test]
7023async fn test_following_tab_order(
7024    deterministic: Arc<Deterministic>,
7025    cx_a: &mut TestAppContext,
7026    cx_b: &mut TestAppContext,
7027) {
7028    let mut server = TestServer::start(&deterministic).await;
7029    let client_a = server.create_client(cx_a, "user_a").await;
7030    let client_b = server.create_client(cx_b, "user_b").await;
7031    server
7032        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7033        .await;
7034    let active_call_a = cx_a.read(ActiveCall::global);
7035    let active_call_b = cx_b.read(ActiveCall::global);
7036
7037    cx_a.update(editor::init);
7038    cx_b.update(editor::init);
7039
7040    client_a
7041        .fs()
7042        .insert_tree(
7043            "/a",
7044            json!({
7045                "1.txt": "one",
7046                "2.txt": "two",
7047                "3.txt": "three",
7048            }),
7049        )
7050        .await;
7051    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7052    active_call_a
7053        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
7054        .await
7055        .unwrap();
7056
7057    let project_id = active_call_a
7058        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7059        .await
7060        .unwrap();
7061    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7062    active_call_b
7063        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
7064        .await
7065        .unwrap();
7066
7067    let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
7068    let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
7069
7070    let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
7071    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
7072
7073    let client_b_id = project_a.read_with(cx_a, |project, _| {
7074        project.collaborators().values().next().unwrap().peer_id
7075    });
7076
7077    //Open 1, 3 in that order on client A
7078    workspace_a
7079        .update(cx_a, |workspace, cx| {
7080            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
7081        })
7082        .await
7083        .unwrap();
7084    workspace_a
7085        .update(cx_a, |workspace, cx| {
7086            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
7087        })
7088        .await
7089        .unwrap();
7090
7091    let pane_paths = |pane: &ViewHandle<workspace::Pane>, cx: &mut TestAppContext| {
7092        pane.update(cx, |pane, cx| {
7093            pane.items()
7094                .map(|item| {
7095                    item.project_path(cx)
7096                        .unwrap()
7097                        .path
7098                        .to_str()
7099                        .unwrap()
7100                        .to_owned()
7101                })
7102                .collect::<Vec<_>>()
7103        })
7104    };
7105
7106    //Verify that the tabs opened in the order we expect
7107    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt"]);
7108
7109    //Follow client B as client A
7110    workspace_a
7111        .update(cx_a, |workspace, cx| {
7112            workspace.toggle_follow(client_b_id, cx).unwrap()
7113        })
7114        .await
7115        .unwrap();
7116
7117    //Open just 2 on client B
7118    workspace_b
7119        .update(cx_b, |workspace, cx| {
7120            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
7121        })
7122        .await
7123        .unwrap();
7124    deterministic.run_until_parked();
7125
7126    // Verify that newly opened followed file is at the end
7127    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt", "2.txt"]);
7128
7129    //Open just 1 on client B
7130    workspace_b
7131        .update(cx_b, |workspace, cx| {
7132            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
7133        })
7134        .await
7135        .unwrap();
7136    assert_eq!(&pane_paths(&pane_b, cx_b), &["2.txt", "1.txt"]);
7137    deterministic.run_until_parked();
7138
7139    // Verify that following into 1 did not reorder
7140    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt", "2.txt"]);
7141}
7142
7143#[gpui::test(iterations = 10)]
7144async fn test_peers_following_each_other(
7145    deterministic: Arc<Deterministic>,
7146    cx_a: &mut TestAppContext,
7147    cx_b: &mut TestAppContext,
7148) {
7149    deterministic.forbid_parking();
7150    let mut server = TestServer::start(&deterministic).await;
7151    let client_a = server.create_client(cx_a, "user_a").await;
7152    let client_b = server.create_client(cx_b, "user_b").await;
7153    server
7154        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7155        .await;
7156    let active_call_a = cx_a.read(ActiveCall::global);
7157    let active_call_b = cx_b.read(ActiveCall::global);
7158
7159    cx_a.update(editor::init);
7160    cx_b.update(editor::init);
7161
7162    // Client A shares a project.
7163    client_a
7164        .fs()
7165        .insert_tree(
7166            "/a",
7167            json!({
7168                "1.txt": "one",
7169                "2.txt": "two",
7170                "3.txt": "three",
7171                "4.txt": "four",
7172            }),
7173        )
7174        .await;
7175    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7176    active_call_a
7177        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
7178        .await
7179        .unwrap();
7180    let project_id = active_call_a
7181        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7182        .await
7183        .unwrap();
7184
7185    // Client B joins the project.
7186    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7187    active_call_b
7188        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
7189        .await
7190        .unwrap();
7191
7192    // Client A opens some editors.
7193    let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
7194    let pane_a1 = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
7195    let _editor_a1 = workspace_a
7196        .update(cx_a, |workspace, cx| {
7197            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
7198        })
7199        .await
7200        .unwrap()
7201        .downcast::<Editor>()
7202        .unwrap();
7203
7204    // Client B opens an editor.
7205    let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
7206    let pane_b1 = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
7207    let _editor_b1 = workspace_b
7208        .update(cx_b, |workspace, cx| {
7209            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
7210        })
7211        .await
7212        .unwrap()
7213        .downcast::<Editor>()
7214        .unwrap();
7215
7216    // Clients A and B follow each other in split panes
7217    workspace_a.update(cx_a, |workspace, cx| {
7218        workspace.split_and_clone(workspace.active_pane().clone(), SplitDirection::Right, cx);
7219    });
7220    workspace_a
7221        .update(cx_a, |workspace, cx| {
7222            assert_ne!(*workspace.active_pane(), pane_a1);
7223            let leader_id = *project_a.read(cx).collaborators().keys().next().unwrap();
7224            workspace.toggle_follow(leader_id, cx).unwrap()
7225        })
7226        .await
7227        .unwrap();
7228    workspace_b.update(cx_b, |workspace, cx| {
7229        workspace.split_and_clone(workspace.active_pane().clone(), SplitDirection::Right, cx);
7230    });
7231    workspace_b
7232        .update(cx_b, |workspace, cx| {
7233            assert_ne!(*workspace.active_pane(), pane_b1);
7234            let leader_id = *project_b.read(cx).collaborators().keys().next().unwrap();
7235            workspace.toggle_follow(leader_id, cx).unwrap()
7236        })
7237        .await
7238        .unwrap();
7239
7240    workspace_a.update(cx_a, |workspace, cx| {
7241        workspace.activate_next_pane(cx);
7242    });
7243    // Wait for focus effects to be fully flushed
7244    workspace_a.update(cx_a, |workspace, _| {
7245        assert_eq!(*workspace.active_pane(), pane_a1);
7246    });
7247
7248    workspace_a
7249        .update(cx_a, |workspace, cx| {
7250            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
7251        })
7252        .await
7253        .unwrap();
7254    workspace_b.update(cx_b, |workspace, cx| {
7255        workspace.activate_next_pane(cx);
7256    });
7257
7258    workspace_b
7259        .update(cx_b, |workspace, cx| {
7260            assert_eq!(*workspace.active_pane(), pane_b1);
7261            workspace.open_path((worktree_id, "4.txt"), None, true, cx)
7262        })
7263        .await
7264        .unwrap();
7265    cx_a.foreground().run_until_parked();
7266
7267    // Ensure leader updates don't change the active pane of followers
7268    workspace_a.read_with(cx_a, |workspace, _| {
7269        assert_eq!(*workspace.active_pane(), pane_a1);
7270    });
7271    workspace_b.read_with(cx_b, |workspace, _| {
7272        assert_eq!(*workspace.active_pane(), pane_b1);
7273    });
7274
7275    // Ensure peers following each other doesn't cause an infinite loop.
7276    assert_eq!(
7277        workspace_a.read_with(cx_a, |workspace, cx| workspace
7278            .active_item(cx)
7279            .unwrap()
7280            .project_path(cx)),
7281        Some((worktree_id, "3.txt").into())
7282    );
7283    workspace_a.update(cx_a, |workspace, cx| {
7284        assert_eq!(
7285            workspace.active_item(cx).unwrap().project_path(cx),
7286            Some((worktree_id, "3.txt").into())
7287        );
7288        workspace.activate_next_pane(cx);
7289    });
7290
7291    workspace_a.update(cx_a, |workspace, cx| {
7292        assert_eq!(
7293            workspace.active_item(cx).unwrap().project_path(cx),
7294            Some((worktree_id, "4.txt").into())
7295        );
7296    });
7297
7298    workspace_b.update(cx_b, |workspace, cx| {
7299        assert_eq!(
7300            workspace.active_item(cx).unwrap().project_path(cx),
7301            Some((worktree_id, "4.txt").into())
7302        );
7303        workspace.activate_next_pane(cx);
7304    });
7305
7306    workspace_b.update(cx_b, |workspace, cx| {
7307        assert_eq!(
7308            workspace.active_item(cx).unwrap().project_path(cx),
7309            Some((worktree_id, "3.txt").into())
7310        );
7311    });
7312}
7313
7314#[gpui::test(iterations = 10)]
7315async fn test_auto_unfollowing(
7316    deterministic: Arc<Deterministic>,
7317    cx_a: &mut TestAppContext,
7318    cx_b: &mut TestAppContext,
7319) {
7320    deterministic.forbid_parking();
7321
7322    // 2 clients connect to a server.
7323    let mut server = TestServer::start(&deterministic).await;
7324    let client_a = server.create_client(cx_a, "user_a").await;
7325    let client_b = server.create_client(cx_b, "user_b").await;
7326    server
7327        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7328        .await;
7329    let active_call_a = cx_a.read(ActiveCall::global);
7330    let active_call_b = cx_b.read(ActiveCall::global);
7331
7332    cx_a.update(editor::init);
7333    cx_b.update(editor::init);
7334
7335    // Client A shares a project.
7336    client_a
7337        .fs()
7338        .insert_tree(
7339            "/a",
7340            json!({
7341                "1.txt": "one",
7342                "2.txt": "two",
7343                "3.txt": "three",
7344            }),
7345        )
7346        .await;
7347    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7348    active_call_a
7349        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
7350        .await
7351        .unwrap();
7352
7353    let project_id = active_call_a
7354        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7355        .await
7356        .unwrap();
7357    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7358    active_call_b
7359        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
7360        .await
7361        .unwrap();
7362
7363    // Client A opens some editors.
7364    let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
7365    let _editor_a1 = workspace_a
7366        .update(cx_a, |workspace, cx| {
7367            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
7368        })
7369        .await
7370        .unwrap()
7371        .downcast::<Editor>()
7372        .unwrap();
7373
7374    // Client B starts following client A.
7375    let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
7376    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
7377    let leader_id = project_b.read_with(cx_b, |project, _| {
7378        project.collaborators().values().next().unwrap().peer_id
7379    });
7380    workspace_b
7381        .update(cx_b, |workspace, cx| {
7382            workspace.toggle_follow(leader_id, cx).unwrap()
7383        })
7384        .await
7385        .unwrap();
7386    assert_eq!(
7387        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7388        Some(leader_id)
7389    );
7390    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
7391        workspace
7392            .active_item(cx)
7393            .unwrap()
7394            .downcast::<Editor>()
7395            .unwrap()
7396    });
7397
7398    // When client B moves, it automatically stops following client A.
7399    editor_b2.update(cx_b, |editor, cx| editor.move_right(&editor::MoveRight, cx));
7400    assert_eq!(
7401        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7402        None
7403    );
7404
7405    workspace_b
7406        .update(cx_b, |workspace, cx| {
7407            workspace.toggle_follow(leader_id, cx).unwrap()
7408        })
7409        .await
7410        .unwrap();
7411    assert_eq!(
7412        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7413        Some(leader_id)
7414    );
7415
7416    // When client B edits, it automatically stops following client A.
7417    editor_b2.update(cx_b, |editor, cx| editor.insert("X", cx));
7418    assert_eq!(
7419        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7420        None
7421    );
7422
7423    workspace_b
7424        .update(cx_b, |workspace, cx| {
7425            workspace.toggle_follow(leader_id, cx).unwrap()
7426        })
7427        .await
7428        .unwrap();
7429    assert_eq!(
7430        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7431        Some(leader_id)
7432    );
7433
7434    // When client B scrolls, it automatically stops following client A.
7435    editor_b2.update(cx_b, |editor, cx| {
7436        editor.set_scroll_position(vec2f(0., 3.), cx)
7437    });
7438    assert_eq!(
7439        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7440        None
7441    );
7442
7443    workspace_b
7444        .update(cx_b, |workspace, cx| {
7445            workspace.toggle_follow(leader_id, cx).unwrap()
7446        })
7447        .await
7448        .unwrap();
7449    assert_eq!(
7450        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7451        Some(leader_id)
7452    );
7453
7454    // When client B activates a different pane, it continues following client A in the original pane.
7455    workspace_b.update(cx_b, |workspace, cx| {
7456        workspace.split_and_clone(pane_b.clone(), SplitDirection::Right, cx)
7457    });
7458    assert_eq!(
7459        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7460        Some(leader_id)
7461    );
7462
7463    workspace_b.update(cx_b, |workspace, cx| workspace.activate_next_pane(cx));
7464    assert_eq!(
7465        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7466        Some(leader_id)
7467    );
7468
7469    // When client B activates a different item in the original pane, it automatically stops following client A.
7470    workspace_b
7471        .update(cx_b, |workspace, cx| {
7472            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
7473        })
7474        .await
7475        .unwrap();
7476    assert_eq!(
7477        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7478        None
7479    );
7480}
7481
7482#[gpui::test(iterations = 10)]
7483async fn test_peers_simultaneously_following_each_other(
7484    deterministic: Arc<Deterministic>,
7485    cx_a: &mut TestAppContext,
7486    cx_b: &mut TestAppContext,
7487) {
7488    deterministic.forbid_parking();
7489
7490    let mut server = TestServer::start(&deterministic).await;
7491    let client_a = server.create_client(cx_a, "user_a").await;
7492    let client_b = server.create_client(cx_b, "user_b").await;
7493    server
7494        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7495        .await;
7496    let active_call_a = cx_a.read(ActiveCall::global);
7497
7498    cx_a.update(editor::init);
7499    cx_b.update(editor::init);
7500
7501    client_a.fs().insert_tree("/a", json!({})).await;
7502    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
7503    let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
7504    let project_id = active_call_a
7505        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7506        .await
7507        .unwrap();
7508
7509    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7510    let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
7511
7512    deterministic.run_until_parked();
7513    let client_a_id = project_b.read_with(cx_b, |project, _| {
7514        project.collaborators().values().next().unwrap().peer_id
7515    });
7516    let client_b_id = project_a.read_with(cx_a, |project, _| {
7517        project.collaborators().values().next().unwrap().peer_id
7518    });
7519
7520    let a_follow_b = workspace_a.update(cx_a, |workspace, cx| {
7521        workspace.toggle_follow(client_b_id, cx).unwrap()
7522    });
7523    let b_follow_a = workspace_b.update(cx_b, |workspace, cx| {
7524        workspace.toggle_follow(client_a_id, cx).unwrap()
7525    });
7526
7527    futures::try_join!(a_follow_b, b_follow_a).unwrap();
7528    workspace_a.read_with(cx_a, |workspace, _| {
7529        assert_eq!(
7530            workspace.leader_for_pane(workspace.active_pane()),
7531            Some(client_b_id)
7532        );
7533    });
7534    workspace_b.read_with(cx_b, |workspace, _| {
7535        assert_eq!(
7536            workspace.leader_for_pane(workspace.active_pane()),
7537            Some(client_a_id)
7538        );
7539    });
7540}
7541
7542#[gpui::test(iterations = 10)]
7543async fn test_on_input_format_from_host_to_guest(
7544    deterministic: Arc<Deterministic>,
7545    cx_a: &mut TestAppContext,
7546    cx_b: &mut TestAppContext,
7547) {
7548    deterministic.forbid_parking();
7549    let mut server = TestServer::start(&deterministic).await;
7550    let client_a = server.create_client(cx_a, "user_a").await;
7551    let client_b = server.create_client(cx_b, "user_b").await;
7552    server
7553        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7554        .await;
7555    let active_call_a = cx_a.read(ActiveCall::global);
7556
7557    // Set up a fake language server.
7558    let mut language = Language::new(
7559        LanguageConfig {
7560            name: "Rust".into(),
7561            path_suffixes: vec!["rs".to_string()],
7562            ..Default::default()
7563        },
7564        Some(tree_sitter_rust::language()),
7565    );
7566    let mut fake_language_servers = language
7567        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7568            capabilities: lsp::ServerCapabilities {
7569                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
7570                    first_trigger_character: ":".to_string(),
7571                    more_trigger_character: Some(vec![">".to_string()]),
7572                }),
7573                ..Default::default()
7574            },
7575            ..Default::default()
7576        }))
7577        .await;
7578    client_a.language_registry().add(Arc::new(language));
7579
7580    client_a
7581        .fs()
7582        .insert_tree(
7583            "/a",
7584            json!({
7585                "main.rs": "fn main() { a }",
7586                "other.rs": "// Test file",
7587            }),
7588        )
7589        .await;
7590    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7591    let project_id = active_call_a
7592        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7593        .await
7594        .unwrap();
7595    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7596
7597    // Open a file in an editor as the host.
7598    let buffer_a = project_a
7599        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
7600        .await
7601        .unwrap();
7602    let window_a = cx_a.add_window(|_| EmptyView);
7603    let editor_a = window_a.add_view(cx_a, |cx| {
7604        Editor::for_buffer(buffer_a, Some(project_a.clone()), cx)
7605    });
7606
7607    let fake_language_server = fake_language_servers.next().await.unwrap();
7608    cx_b.foreground().run_until_parked();
7609
7610    // Receive an OnTypeFormatting request as the host's language server.
7611    // Return some formattings from the host's language server.
7612    fake_language_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(
7613        |params, _| async move {
7614            assert_eq!(
7615                params.text_document_position.text_document.uri,
7616                lsp::Url::from_file_path("/a/main.rs").unwrap(),
7617            );
7618            assert_eq!(
7619                params.text_document_position.position,
7620                lsp::Position::new(0, 14),
7621            );
7622
7623            Ok(Some(vec![lsp::TextEdit {
7624                new_text: "~<".to_string(),
7625                range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
7626            }]))
7627        },
7628    );
7629
7630    // Open the buffer on the guest and see that the formattings worked
7631    let buffer_b = project_b
7632        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
7633        .await
7634        .unwrap();
7635
7636    // Type a on type formatting trigger character as the guest.
7637    editor_a.update(cx_a, |editor, cx| {
7638        cx.focus(&editor_a);
7639        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
7640        editor.handle_input(">", cx);
7641    });
7642
7643    cx_b.foreground().run_until_parked();
7644
7645    buffer_b.read_with(cx_b, |buffer, _| {
7646        assert_eq!(buffer.text(), "fn main() { a>~< }")
7647    });
7648
7649    // Undo should remove LSP edits first
7650    editor_a.update(cx_a, |editor, cx| {
7651        assert_eq!(editor.text(cx), "fn main() { a>~< }");
7652        editor.undo(&Undo, cx);
7653        assert_eq!(editor.text(cx), "fn main() { a> }");
7654    });
7655    cx_b.foreground().run_until_parked();
7656    buffer_b.read_with(cx_b, |buffer, _| {
7657        assert_eq!(buffer.text(), "fn main() { a> }")
7658    });
7659
7660    editor_a.update(cx_a, |editor, cx| {
7661        assert_eq!(editor.text(cx), "fn main() { a> }");
7662        editor.undo(&Undo, cx);
7663        assert_eq!(editor.text(cx), "fn main() { a }");
7664    });
7665    cx_b.foreground().run_until_parked();
7666    buffer_b.read_with(cx_b, |buffer, _| {
7667        assert_eq!(buffer.text(), "fn main() { a }")
7668    });
7669}
7670
7671#[gpui::test(iterations = 10)]
7672async fn test_on_input_format_from_guest_to_host(
7673    deterministic: Arc<Deterministic>,
7674    cx_a: &mut TestAppContext,
7675    cx_b: &mut TestAppContext,
7676) {
7677    deterministic.forbid_parking();
7678    let mut server = TestServer::start(&deterministic).await;
7679    let client_a = server.create_client(cx_a, "user_a").await;
7680    let client_b = server.create_client(cx_b, "user_b").await;
7681    server
7682        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7683        .await;
7684    let active_call_a = cx_a.read(ActiveCall::global);
7685
7686    // Set up a fake language server.
7687    let mut language = Language::new(
7688        LanguageConfig {
7689            name: "Rust".into(),
7690            path_suffixes: vec!["rs".to_string()],
7691            ..Default::default()
7692        },
7693        Some(tree_sitter_rust::language()),
7694    );
7695    let mut fake_language_servers = language
7696        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7697            capabilities: lsp::ServerCapabilities {
7698                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
7699                    first_trigger_character: ":".to_string(),
7700                    more_trigger_character: Some(vec![">".to_string()]),
7701                }),
7702                ..Default::default()
7703            },
7704            ..Default::default()
7705        }))
7706        .await;
7707    client_a.language_registry().add(Arc::new(language));
7708
7709    client_a
7710        .fs()
7711        .insert_tree(
7712            "/a",
7713            json!({
7714                "main.rs": "fn main() { a }",
7715                "other.rs": "// Test file",
7716            }),
7717        )
7718        .await;
7719    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7720    let project_id = active_call_a
7721        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7722        .await
7723        .unwrap();
7724    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7725
7726    // Open a file in an editor as the guest.
7727    let buffer_b = project_b
7728        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
7729        .await
7730        .unwrap();
7731    let window_b = cx_b.add_window(|_| EmptyView);
7732    let editor_b = window_b.add_view(cx_b, |cx| {
7733        Editor::for_buffer(buffer_b, Some(project_b.clone()), cx)
7734    });
7735
7736    let fake_language_server = fake_language_servers.next().await.unwrap();
7737    cx_a.foreground().run_until_parked();
7738    // Type a on type formatting trigger character as the guest.
7739    editor_b.update(cx_b, |editor, cx| {
7740        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
7741        editor.handle_input(":", cx);
7742        cx.focus(&editor_b);
7743    });
7744
7745    // Receive an OnTypeFormatting request as the host's language server.
7746    // Return some formattings from the host's language server.
7747    cx_a.foreground().start_waiting();
7748    fake_language_server
7749        .handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
7750            assert_eq!(
7751                params.text_document_position.text_document.uri,
7752                lsp::Url::from_file_path("/a/main.rs").unwrap(),
7753            );
7754            assert_eq!(
7755                params.text_document_position.position,
7756                lsp::Position::new(0, 14),
7757            );
7758
7759            Ok(Some(vec![lsp::TextEdit {
7760                new_text: "~:".to_string(),
7761                range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
7762            }]))
7763        })
7764        .next()
7765        .await
7766        .unwrap();
7767    cx_a.foreground().finish_waiting();
7768
7769    // Open the buffer on the host and see that the formattings worked
7770    let buffer_a = project_a
7771        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
7772        .await
7773        .unwrap();
7774    cx_a.foreground().run_until_parked();
7775    buffer_a.read_with(cx_a, |buffer, _| {
7776        assert_eq!(buffer.text(), "fn main() { a:~: }")
7777    });
7778
7779    // Undo should remove LSP edits first
7780    editor_b.update(cx_b, |editor, cx| {
7781        assert_eq!(editor.text(cx), "fn main() { a:~: }");
7782        editor.undo(&Undo, cx);
7783        assert_eq!(editor.text(cx), "fn main() { a: }");
7784    });
7785    cx_a.foreground().run_until_parked();
7786    buffer_a.read_with(cx_a, |buffer, _| {
7787        assert_eq!(buffer.text(), "fn main() { a: }")
7788    });
7789
7790    editor_b.update(cx_b, |editor, cx| {
7791        assert_eq!(editor.text(cx), "fn main() { a: }");
7792        editor.undo(&Undo, cx);
7793        assert_eq!(editor.text(cx), "fn main() { a }");
7794    });
7795    cx_a.foreground().run_until_parked();
7796    buffer_a.read_with(cx_a, |buffer, _| {
7797        assert_eq!(buffer.text(), "fn main() { a }")
7798    });
7799}
7800
7801#[allow(unused)]
7802async fn test_mutual_editor_inlay_hint_cache_update(
7803    deterministic: Arc<Deterministic>,
7804    cx_a: &mut TestAppContext,
7805    cx_b: &mut TestAppContext,
7806) {
7807    deterministic.forbid_parking();
7808    let mut server = TestServer::start(&deterministic).await;
7809    let client_a = server.create_client(cx_a, "user_a").await;
7810    let client_b = server.create_client(cx_b, "user_b").await;
7811    server
7812        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7813        .await;
7814    let active_call_a = cx_a.read(ActiveCall::global);
7815    let active_call_b = cx_b.read(ActiveCall::global);
7816
7817    cx_a.update(editor::init);
7818    cx_b.update(editor::init);
7819
7820    cx_a.update(|cx| {
7821        cx.update_global(|store: &mut SettingsStore, cx| {
7822            store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
7823                settings.defaults.inlay_hints = Some(InlayHintSettings {
7824                    enabled: true,
7825                    show_type_hints: true,
7826                    show_parameter_hints: false,
7827                    show_other_hints: true,
7828                })
7829            });
7830        });
7831    });
7832    cx_b.update(|cx| {
7833        cx.update_global(|store: &mut SettingsStore, cx| {
7834            store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
7835                settings.defaults.inlay_hints = Some(InlayHintSettings {
7836                    enabled: true,
7837                    show_type_hints: true,
7838                    show_parameter_hints: false,
7839                    show_other_hints: true,
7840                })
7841            });
7842        });
7843    });
7844
7845    let mut language = Language::new(
7846        LanguageConfig {
7847            name: "Rust".into(),
7848            path_suffixes: vec!["rs".to_string()],
7849            ..Default::default()
7850        },
7851        Some(tree_sitter_rust::language()),
7852    );
7853    let mut fake_language_servers = language
7854        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7855            capabilities: lsp::ServerCapabilities {
7856                inlay_hint_provider: Some(lsp::OneOf::Left(true)),
7857                ..Default::default()
7858            },
7859            ..Default::default()
7860        }))
7861        .await;
7862    let language = Arc::new(language);
7863    client_a.language_registry().add(Arc::clone(&language));
7864    client_b.language_registry().add(language);
7865
7866    // Client A opens a project.
7867    client_a
7868        .fs()
7869        .insert_tree(
7870            "/a",
7871            json!({
7872                "main.rs": "fn main() { a } // and some long comment to ensure inlay hints are not trimmed out",
7873                "other.rs": "// Test file",
7874            }),
7875        )
7876        .await;
7877    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7878    active_call_a
7879        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
7880        .await
7881        .unwrap();
7882    let project_id = active_call_a
7883        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7884        .await
7885        .unwrap();
7886
7887    // Client B joins the project
7888    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7889    active_call_b
7890        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
7891        .await
7892        .unwrap();
7893
7894    let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
7895    cx_a.foreground().start_waiting();
7896
7897    // The host opens a rust file.
7898    let _buffer_a = project_a
7899        .update(cx_a, |project, cx| {
7900            project.open_local_buffer("/a/main.rs", cx)
7901        })
7902        .await
7903        .unwrap();
7904    let fake_language_server = fake_language_servers.next().await.unwrap();
7905    let editor_a = workspace_a
7906        .update(cx_a, |workspace, cx| {
7907            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
7908        })
7909        .await
7910        .unwrap()
7911        .downcast::<Editor>()
7912        .unwrap();
7913
7914    // Set up the language server to return an additional inlay hint on each request.
7915    let next_call_id = Arc::new(AtomicU32::new(0));
7916    fake_language_server
7917        .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
7918            let task_next_call_id = Arc::clone(&next_call_id);
7919            async move {
7920                assert_eq!(
7921                    params.text_document.uri,
7922                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
7923                );
7924                let call_count = task_next_call_id.fetch_add(1, SeqCst);
7925                Ok(Some(
7926                    (0..=call_count)
7927                        .map(|ix| lsp::InlayHint {
7928                            position: lsp::Position::new(0, ix),
7929                            label: lsp::InlayHintLabel::String(ix.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                        .collect(),
7938                ))
7939            }
7940        })
7941        .next()
7942        .await
7943        .unwrap();
7944
7945    deterministic.run_until_parked();
7946
7947    let mut edits_made = 1;
7948    editor_a.update(cx_a, |editor, _| {
7949        assert_eq!(
7950            vec!["0".to_string()],
7951            extract_hint_labels(editor),
7952            "Host should get its first hints when opens an editor"
7953        );
7954        let inlay_cache = editor.inlay_hint_cache();
7955        assert_eq!(
7956            inlay_cache.version(),
7957            edits_made,
7958            "Host editor update the cache version after every cache/view change",
7959        );
7960    });
7961    let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
7962    let editor_b = workspace_b
7963        .update(cx_b, |workspace, cx| {
7964            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
7965        })
7966        .await
7967        .unwrap()
7968        .downcast::<Editor>()
7969        .unwrap();
7970
7971    deterministic.run_until_parked();
7972    editor_b.update(cx_b, |editor, _| {
7973        assert_eq!(
7974            vec!["0".to_string(), "1".to_string()],
7975            extract_hint_labels(editor),
7976            "Client should get its first hints when opens an editor"
7977        );
7978        let inlay_cache = editor.inlay_hint_cache();
7979        assert_eq!(
7980            inlay_cache.version(),
7981            edits_made,
7982            "Guest editor update the cache version after every cache/view change"
7983        );
7984    });
7985
7986    editor_b.update(cx_b, |editor, cx| {
7987        editor.change_selections(None, cx, |s| s.select_ranges([13..13].clone()));
7988        editor.handle_input(":", cx);
7989        cx.focus(&editor_b);
7990        edits_made += 1;
7991    });
7992
7993    deterministic.run_until_parked();
7994    editor_a.update(cx_a, |editor, _| {
7995        assert_eq!(
7996            vec![
7997                "0".to_string(),
7998                "1".to_string(),
7999                "2".to_string(),
8000                "3".to_string()
8001            ],
8002            extract_hint_labels(editor),
8003            "Guest should get hints the 1st edit and 2nd LSP query"
8004        );
8005        let inlay_cache = editor.inlay_hint_cache();
8006        assert_eq!(inlay_cache.version(), edits_made);
8007    });
8008    editor_b.update(cx_b, |editor, _| {
8009        assert_eq!(
8010            vec!["0".to_string(), "1".to_string(), "2".to_string(),],
8011            extract_hint_labels(editor),
8012            "Guest should get hints the 1st edit and 2nd LSP query"
8013        );
8014        let inlay_cache = editor.inlay_hint_cache();
8015        assert_eq!(inlay_cache.version(), edits_made);
8016    });
8017
8018    editor_a.update(cx_a, |editor, cx| {
8019        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
8020        editor.handle_input("a change to increment both buffers' versions", cx);
8021        cx.focus(&editor_a);
8022        edits_made += 1;
8023    });
8024
8025    deterministic.run_until_parked();
8026    editor_a.update(cx_a, |editor, _| {
8027        assert_eq!(
8028            vec![
8029                "0".to_string(),
8030                "1".to_string(),
8031                "2".to_string(),
8032                "3".to_string(),
8033                "4".to_string()
8034            ],
8035            extract_hint_labels(editor),
8036            "Host should get hints from 3rd edit, 5th LSP query: \
80374th query was made by guest (but not applied) due to cache invalidation logic"
8038        );
8039        let inlay_cache = editor.inlay_hint_cache();
8040        assert_eq!(inlay_cache.version(), edits_made);
8041    });
8042    editor_b.update(cx_b, |editor, _| {
8043        assert_eq!(
8044            vec![
8045                "0".to_string(),
8046                "1".to_string(),
8047                "2".to_string(),
8048                "3".to_string(),
8049                "4".to_string(),
8050                "5".to_string(),
8051            ],
8052            extract_hint_labels(editor),
8053            "Guest should get hints from 3rd edit, 6th LSP query"
8054        );
8055        let inlay_cache = editor.inlay_hint_cache();
8056        assert_eq!(inlay_cache.version(), edits_made);
8057    });
8058
8059    fake_language_server
8060        .request::<lsp::request::InlayHintRefreshRequest>(())
8061        .await
8062        .expect("inlay refresh request failed");
8063    edits_made += 1;
8064
8065    deterministic.run_until_parked();
8066    editor_a.update(cx_a, |editor, _| {
8067        assert_eq!(
8068            vec![
8069                "0".to_string(),
8070                "1".to_string(),
8071                "2".to_string(),
8072                "3".to_string(),
8073                "4".to_string(),
8074                "5".to_string(),
8075                "6".to_string(),
8076            ],
8077            extract_hint_labels(editor),
8078            "Host should react to /refresh LSP request and get new hints from 7th LSP query"
8079        );
8080        let inlay_cache = editor.inlay_hint_cache();
8081        assert_eq!(
8082            inlay_cache.version(),
8083            edits_made,
8084            "Host should accepted all edits and bump its cache version every time"
8085        );
8086    });
8087    editor_b.update(cx_b, |editor, _| {
8088        assert_eq!(
8089            vec![
8090                "0".to_string(),
8091                "1".to_string(),
8092                "2".to_string(),
8093                "3".to_string(),
8094                "4".to_string(),
8095                "5".to_string(),
8096                "6".to_string(),
8097                "7".to_string(),
8098            ],
8099            extract_hint_labels(editor),
8100            "Guest should get a /refresh LSP request propagated by host and get new hints from 8th LSP query"
8101        );
8102        let inlay_cache = editor.inlay_hint_cache();
8103        assert_eq!(
8104            inlay_cache.version(),
8105            edits_made,
8106            "Guest should accepted all edits and bump its cache version every time"
8107        );
8108    });
8109}
8110
8111#[gpui::test]
8112async fn test_inlay_hint_refresh_is_forwarded(
8113    deterministic: Arc<Deterministic>,
8114    cx_a: &mut TestAppContext,
8115    cx_b: &mut TestAppContext,
8116) {
8117    deterministic.forbid_parking();
8118    let mut server = TestServer::start(&deterministic).await;
8119    let client_a = server.create_client(cx_a, "user_a").await;
8120    let client_b = server.create_client(cx_b, "user_b").await;
8121    server
8122        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
8123        .await;
8124    let active_call_a = cx_a.read(ActiveCall::global);
8125    let active_call_b = cx_b.read(ActiveCall::global);
8126
8127    cx_a.update(editor::init);
8128    cx_b.update(editor::init);
8129
8130    cx_a.update(|cx| {
8131        cx.update_global(|store: &mut SettingsStore, cx| {
8132            store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
8133                settings.defaults.inlay_hints = Some(InlayHintSettings {
8134                    enabled: false,
8135                    show_type_hints: false,
8136                    show_parameter_hints: false,
8137                    show_other_hints: false,
8138                })
8139            });
8140        });
8141    });
8142    cx_b.update(|cx| {
8143        cx.update_global(|store: &mut SettingsStore, cx| {
8144            store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
8145                settings.defaults.inlay_hints = Some(InlayHintSettings {
8146                    enabled: true,
8147                    show_type_hints: true,
8148                    show_parameter_hints: true,
8149                    show_other_hints: true,
8150                })
8151            });
8152        });
8153    });
8154
8155    let mut language = Language::new(
8156        LanguageConfig {
8157            name: "Rust".into(),
8158            path_suffixes: vec!["rs".to_string()],
8159            ..Default::default()
8160        },
8161        Some(tree_sitter_rust::language()),
8162    );
8163    let mut fake_language_servers = language
8164        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
8165            capabilities: lsp::ServerCapabilities {
8166                inlay_hint_provider: Some(lsp::OneOf::Left(true)),
8167                ..Default::default()
8168            },
8169            ..Default::default()
8170        }))
8171        .await;
8172    let language = Arc::new(language);
8173    client_a.language_registry().add(Arc::clone(&language));
8174    client_b.language_registry().add(language);
8175
8176    client_a
8177        .fs()
8178        .insert_tree(
8179            "/a",
8180            json!({
8181                "main.rs": "fn main() { a } // and some long comment to ensure inlay hints are not trimmed out",
8182                "other.rs": "// Test file",
8183            }),
8184        )
8185        .await;
8186    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
8187    active_call_a
8188        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
8189        .await
8190        .unwrap();
8191    let project_id = active_call_a
8192        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
8193        .await
8194        .unwrap();
8195
8196    let project_b = client_b.build_remote_project(project_id, cx_b).await;
8197    active_call_b
8198        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
8199        .await
8200        .unwrap();
8201
8202    let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
8203    let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
8204    cx_a.foreground().start_waiting();
8205    cx_b.foreground().start_waiting();
8206
8207    let editor_a = workspace_a
8208        .update(cx_a, |workspace, cx| {
8209            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
8210        })
8211        .await
8212        .unwrap()
8213        .downcast::<Editor>()
8214        .unwrap();
8215
8216    let editor_b = workspace_b
8217        .update(cx_b, |workspace, cx| {
8218            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
8219        })
8220        .await
8221        .unwrap()
8222        .downcast::<Editor>()
8223        .unwrap();
8224
8225    let fake_language_server = fake_language_servers.next().await.unwrap();
8226    let next_call_id = Arc::new(AtomicU32::new(0));
8227    fake_language_server
8228        .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
8229            let task_next_call_id = Arc::clone(&next_call_id);
8230            async move {
8231                assert_eq!(
8232                    params.text_document.uri,
8233                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
8234                );
8235                let mut current_call_id = Arc::clone(&task_next_call_id).fetch_add(1, SeqCst);
8236                let mut new_hints = Vec::with_capacity(current_call_id as usize);
8237                loop {
8238                    new_hints.push(lsp::InlayHint {
8239                        position: lsp::Position::new(0, current_call_id),
8240                        label: lsp::InlayHintLabel::String(current_call_id.to_string()),
8241                        kind: None,
8242                        text_edits: None,
8243                        tooltip: None,
8244                        padding_left: None,
8245                        padding_right: None,
8246                        data: None,
8247                    });
8248                    if current_call_id == 0 {
8249                        break;
8250                    }
8251                    current_call_id -= 1;
8252                }
8253                Ok(Some(new_hints))
8254            }
8255        })
8256        .next()
8257        .await
8258        .unwrap();
8259    cx_a.foreground().finish_waiting();
8260    cx_b.foreground().finish_waiting();
8261
8262    cx_a.foreground().run_until_parked();
8263    editor_a.update(cx_a, |editor, _| {
8264        assert!(
8265            extract_hint_labels(editor).is_empty(),
8266            "Host should get no hints due to them turned off"
8267        );
8268        let inlay_cache = editor.inlay_hint_cache();
8269        assert_eq!(
8270            inlay_cache.version(),
8271            0,
8272            "Host should not increment its cache version due to no changes",
8273        );
8274    });
8275
8276    let mut edits_made = 1;
8277    cx_b.foreground().run_until_parked();
8278    editor_b.update(cx_b, |editor, _| {
8279        assert_eq!(
8280            vec!["0".to_string()],
8281            extract_hint_labels(editor),
8282            "Client should get its first hints when opens an editor"
8283        );
8284        let inlay_cache = editor.inlay_hint_cache();
8285        assert_eq!(
8286            inlay_cache.version(),
8287            edits_made,
8288            "Guest editor update the cache version after every cache/view change"
8289        );
8290    });
8291
8292    fake_language_server
8293        .request::<lsp::request::InlayHintRefreshRequest>(())
8294        .await
8295        .expect("inlay refresh request failed");
8296    cx_a.foreground().run_until_parked();
8297    editor_a.update(cx_a, |editor, _| {
8298        assert!(
8299            extract_hint_labels(editor).is_empty(),
8300            "Host should get nop hints due to them turned off, even after the /refresh"
8301        );
8302        let inlay_cache = editor.inlay_hint_cache();
8303        assert_eq!(
8304            inlay_cache.version(),
8305            0,
8306            "Host should not increment its cache version due to no changes",
8307        );
8308    });
8309
8310    edits_made += 1;
8311    cx_b.foreground().run_until_parked();
8312    editor_b.update(cx_b, |editor, _| {
8313        assert_eq!(
8314            vec!["0".to_string(), "1".to_string(),],
8315            extract_hint_labels(editor),
8316            "Guest should get a /refresh LSP request propagated by host despite host hints are off"
8317        );
8318        let inlay_cache = editor.inlay_hint_cache();
8319        assert_eq!(
8320            inlay_cache.version(),
8321            edits_made,
8322            "Guest should accepted all edits and bump its cache version every time"
8323        );
8324    });
8325}
8326
8327fn extract_hint_labels(editor: &Editor) -> Vec<String> {
8328    let mut labels = Vec::new();
8329    for hint in editor.inlay_hint_cache().hints() {
8330        match hint.label {
8331            project::InlayHintLabel::String(s) => labels.push(s),
8332            _ => unreachable!(),
8333        }
8334    }
8335    labels
8336}