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