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