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