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