integration_tests.rs

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