integration_tests.rs

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