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