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