integration_tests.rs

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