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.create_entry((worktree_id, "c.txt"), false, cx)
2985        })
2986        .await
2987        .unwrap()
2988        .unwrap();
2989    worktree_a.read_with(cx_a, |worktree, _| {
2990        assert_eq!(
2991            worktree
2992                .paths()
2993                .map(|p| p.to_string_lossy())
2994                .collect::<Vec<_>>(),
2995            ["a.txt", "b.txt", "c.txt"]
2996        );
2997    });
2998    worktree_b.read_with(cx_b, |worktree, _| {
2999        assert_eq!(
3000            worktree
3001                .paths()
3002                .map(|p| p.to_string_lossy())
3003                .collect::<Vec<_>>(),
3004            ["a.txt", "b.txt", "c.txt"]
3005        );
3006    });
3007
3008    project_b
3009        .update(cx_b, |project, cx| {
3010            project.rename_entry(entry.id, Path::new("d.txt"), cx)
3011        })
3012        .await
3013        .unwrap();
3014    worktree_a.read_with(cx_a, |worktree, _| {
3015        assert_eq!(
3016            worktree
3017                .paths()
3018                .map(|p| p.to_string_lossy())
3019                .collect::<Vec<_>>(),
3020            ["a.txt", "b.txt", "d.txt"]
3021        );
3022    });
3023    worktree_b.read_with(cx_b, |worktree, _| {
3024        assert_eq!(
3025            worktree
3026                .paths()
3027                .map(|p| p.to_string_lossy())
3028                .collect::<Vec<_>>(),
3029            ["a.txt", "b.txt", "d.txt"]
3030        );
3031    });
3032
3033    let dir_entry = project_b
3034        .update(cx_b, |project, cx| {
3035            project.create_entry((worktree_id, "DIR"), true, cx)
3036        })
3037        .await
3038        .unwrap()
3039        .unwrap();
3040    worktree_a.read_with(cx_a, |worktree, _| {
3041        assert_eq!(
3042            worktree
3043                .paths()
3044                .map(|p| p.to_string_lossy())
3045                .collect::<Vec<_>>(),
3046            ["DIR", "a.txt", "b.txt", "d.txt"]
3047        );
3048    });
3049    worktree_b.read_with(cx_b, |worktree, _| {
3050        assert_eq!(
3051            worktree
3052                .paths()
3053                .map(|p| p.to_string_lossy())
3054                .collect::<Vec<_>>(),
3055            ["DIR", "a.txt", "b.txt", "d.txt"]
3056        );
3057    });
3058
3059    project_b
3060        .update(cx_b, |project, cx| {
3061            project.create_entry((worktree_id, "DIR/e.txt"), false, cx)
3062        })
3063        .await
3064        .unwrap();
3065    project_b
3066        .update(cx_b, |project, cx| {
3067            project.create_entry((worktree_id, "DIR/SUBDIR"), true, cx)
3068        })
3069        .await
3070        .unwrap();
3071    project_b
3072        .update(cx_b, |project, cx| {
3073            project.create_entry((worktree_id, "DIR/SUBDIR/f.txt"), false, cx)
3074        })
3075        .await
3076        .unwrap();
3077    worktree_a.read_with(cx_a, |worktree, _| {
3078        assert_eq!(
3079            worktree
3080                .paths()
3081                .map(|p| p.to_string_lossy())
3082                .collect::<Vec<_>>(),
3083            [
3084                "DIR",
3085                "DIR/SUBDIR",
3086                "DIR/SUBDIR/f.txt",
3087                "DIR/e.txt",
3088                "a.txt",
3089                "b.txt",
3090                "d.txt"
3091            ]
3092        );
3093    });
3094    worktree_b.read_with(cx_b, |worktree, _| {
3095        assert_eq!(
3096            worktree
3097                .paths()
3098                .map(|p| p.to_string_lossy())
3099                .collect::<Vec<_>>(),
3100            [
3101                "DIR",
3102                "DIR/SUBDIR",
3103                "DIR/SUBDIR/f.txt",
3104                "DIR/e.txt",
3105                "a.txt",
3106                "b.txt",
3107                "d.txt"
3108            ]
3109        );
3110    });
3111
3112    project_b
3113        .update(cx_b, |project, cx| {
3114            project.copy_entry(entry.id, Path::new("f.txt"), cx)
3115        })
3116        .await
3117        .unwrap();
3118    worktree_a.read_with(cx_a, |worktree, _| {
3119        assert_eq!(
3120            worktree
3121                .paths()
3122                .map(|p| p.to_string_lossy())
3123                .collect::<Vec<_>>(),
3124            [
3125                "DIR",
3126                "DIR/SUBDIR",
3127                "DIR/SUBDIR/f.txt",
3128                "DIR/e.txt",
3129                "a.txt",
3130                "b.txt",
3131                "d.txt",
3132                "f.txt"
3133            ]
3134        );
3135    });
3136    worktree_b.read_with(cx_b, |worktree, _| {
3137        assert_eq!(
3138            worktree
3139                .paths()
3140                .map(|p| p.to_string_lossy())
3141                .collect::<Vec<_>>(),
3142            [
3143                "DIR",
3144                "DIR/SUBDIR",
3145                "DIR/SUBDIR/f.txt",
3146                "DIR/e.txt",
3147                "a.txt",
3148                "b.txt",
3149                "d.txt",
3150                "f.txt"
3151            ]
3152        );
3153    });
3154
3155    project_b
3156        .update(cx_b, |project, cx| {
3157            project.delete_entry(dir_entry.id, cx).unwrap()
3158        })
3159        .await
3160        .unwrap();
3161    deterministic.run_until_parked();
3162
3163    worktree_a.read_with(cx_a, |worktree, _| {
3164        assert_eq!(
3165            worktree
3166                .paths()
3167                .map(|p| p.to_string_lossy())
3168                .collect::<Vec<_>>(),
3169            ["a.txt", "b.txt", "d.txt", "f.txt"]
3170        );
3171    });
3172    worktree_b.read_with(cx_b, |worktree, _| {
3173        assert_eq!(
3174            worktree
3175                .paths()
3176                .map(|p| p.to_string_lossy())
3177                .collect::<Vec<_>>(),
3178            ["a.txt", "b.txt", "d.txt", "f.txt"]
3179        );
3180    });
3181
3182    project_b
3183        .update(cx_b, |project, cx| {
3184            project.delete_entry(entry.id, cx).unwrap()
3185        })
3186        .await
3187        .unwrap();
3188    worktree_a.read_with(cx_a, |worktree, _| {
3189        assert_eq!(
3190            worktree
3191                .paths()
3192                .map(|p| p.to_string_lossy())
3193                .collect::<Vec<_>>(),
3194            ["a.txt", "b.txt", "f.txt"]
3195        );
3196    });
3197    worktree_b.read_with(cx_b, |worktree, _| {
3198        assert_eq!(
3199            worktree
3200                .paths()
3201                .map(|p| p.to_string_lossy())
3202                .collect::<Vec<_>>(),
3203            ["a.txt", "b.txt", "f.txt"]
3204        );
3205    });
3206}
3207
3208#[gpui::test(iterations = 10)]
3209async fn test_local_settings(
3210    deterministic: Arc<Deterministic>,
3211    cx_a: &mut TestAppContext,
3212    cx_b: &mut TestAppContext,
3213) {
3214    deterministic.forbid_parking();
3215    let mut server = TestServer::start(&deterministic).await;
3216    let client_a = server.create_client(cx_a, "user_a").await;
3217    let client_b = server.create_client(cx_b, "user_b").await;
3218    server
3219        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3220        .await;
3221    let active_call_a = cx_a.read(ActiveCall::global);
3222
3223    // As client A, open a project that contains some local settings files
3224    client_a
3225        .fs()
3226        .insert_tree(
3227            "/dir",
3228            json!({
3229                ".zed": {
3230                    "settings.json": r#"{ "tab_size": 2 }"#
3231                },
3232                "a": {
3233                    ".zed": {
3234                        "settings.json": r#"{ "tab_size": 8 }"#
3235                    },
3236                    "a.txt": "a-contents",
3237                },
3238                "b": {
3239                    "b.txt": "b-contents",
3240                }
3241            }),
3242        )
3243        .await;
3244    let (project_a, _) = client_a.build_local_project("/dir", cx_a).await;
3245    deterministic.run_until_parked();
3246    let project_id = active_call_a
3247        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3248        .await
3249        .unwrap();
3250
3251    // As client B, join that project and observe the local settings.
3252    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3253    let worktree_b = project_b.read_with(cx_b, |project, cx| project.worktrees(cx).next().unwrap());
3254    deterministic.run_until_parked();
3255    cx_b.read(|cx| {
3256        let store = cx.global::<SettingsStore>();
3257        assert_eq!(
3258            store.local_settings(worktree_b.id()).collect::<Vec<_>>(),
3259            &[
3260                (Path::new("").into(), r#"{"tab_size":2}"#.to_string()),
3261                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3262            ]
3263        )
3264    });
3265
3266    // As client A, update a settings file. As Client B, see the changed settings.
3267    client_a
3268        .fs()
3269        .insert_file("/dir/.zed/settings.json", r#"{}"#.into())
3270        .await;
3271    deterministic.run_until_parked();
3272    cx_b.read(|cx| {
3273        let store = cx.global::<SettingsStore>();
3274        assert_eq!(
3275            store.local_settings(worktree_b.id()).collect::<Vec<_>>(),
3276            &[
3277                (Path::new("").into(), r#"{}"#.to_string()),
3278                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3279            ]
3280        )
3281    });
3282
3283    // As client A, create and remove some settings files. As client B, see the changed settings.
3284    client_a
3285        .fs()
3286        .remove_file("/dir/.zed/settings.json".as_ref(), Default::default())
3287        .await
3288        .unwrap();
3289    client_a
3290        .fs()
3291        .create_dir("/dir/b/.zed".as_ref())
3292        .await
3293        .unwrap();
3294    client_a
3295        .fs()
3296        .insert_file("/dir/b/.zed/settings.json", r#"{"tab_size": 4}"#.into())
3297        .await;
3298    deterministic.run_until_parked();
3299    cx_b.read(|cx| {
3300        let store = cx.global::<SettingsStore>();
3301        assert_eq!(
3302            store.local_settings(worktree_b.id()).collect::<Vec<_>>(),
3303            &[
3304                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3305                (Path::new("b").into(), r#"{"tab_size":4}"#.to_string()),
3306            ]
3307        )
3308    });
3309
3310    // As client B, disconnect.
3311    server.forbid_connections();
3312    server.disconnect_client(client_b.peer_id().unwrap());
3313
3314    // As client A, change and remove settings files while client B is disconnected.
3315    client_a
3316        .fs()
3317        .insert_file("/dir/a/.zed/settings.json", r#"{"hard_tabs":true}"#.into())
3318        .await;
3319    client_a
3320        .fs()
3321        .remove_file("/dir/b/.zed/settings.json".as_ref(), Default::default())
3322        .await
3323        .unwrap();
3324    deterministic.run_until_parked();
3325
3326    // As client B, reconnect and see the changed settings.
3327    server.allow_connections();
3328    deterministic.advance_clock(RECEIVE_TIMEOUT);
3329    cx_b.read(|cx| {
3330        let store = cx.global::<SettingsStore>();
3331        assert_eq!(
3332            store.local_settings(worktree_b.id()).collect::<Vec<_>>(),
3333            &[(Path::new("a").into(), r#"{"hard_tabs":true}"#.to_string()),]
3334        )
3335    });
3336}
3337
3338#[gpui::test(iterations = 10)]
3339async fn test_buffer_conflict_after_save(
3340    deterministic: Arc<Deterministic>,
3341    cx_a: &mut TestAppContext,
3342    cx_b: &mut TestAppContext,
3343) {
3344    deterministic.forbid_parking();
3345    let mut server = TestServer::start(&deterministic).await;
3346    let client_a = server.create_client(cx_a, "user_a").await;
3347    let client_b = server.create_client(cx_b, "user_b").await;
3348    server
3349        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3350        .await;
3351    let active_call_a = cx_a.read(ActiveCall::global);
3352
3353    client_a
3354        .fs()
3355        .insert_tree(
3356            "/dir",
3357            json!({
3358                "a.txt": "a-contents",
3359            }),
3360        )
3361        .await;
3362    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3363    let project_id = active_call_a
3364        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3365        .await
3366        .unwrap();
3367    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3368
3369    // Open a buffer as client B
3370    let buffer_b = project_b
3371        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3372        .await
3373        .unwrap();
3374
3375    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "world ")], None, cx));
3376    buffer_b.read_with(cx_b, |buf, _| {
3377        assert!(buf.is_dirty());
3378        assert!(!buf.has_conflict());
3379    });
3380
3381    project_b
3382        .update(cx_b, |project, cx| {
3383            project.save_buffer(buffer_b.clone(), cx)
3384        })
3385        .await
3386        .unwrap();
3387    cx_a.foreground().forbid_parking();
3388    buffer_b.read_with(cx_b, |buffer_b, _| assert!(!buffer_b.is_dirty()));
3389    buffer_b.read_with(cx_b, |buf, _| {
3390        assert!(!buf.has_conflict());
3391    });
3392
3393    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "hello ")], None, cx));
3394    buffer_b.read_with(cx_b, |buf, _| {
3395        assert!(buf.is_dirty());
3396        assert!(!buf.has_conflict());
3397    });
3398}
3399
3400#[gpui::test(iterations = 10)]
3401async fn test_buffer_reloading(
3402    deterministic: Arc<Deterministic>,
3403    cx_a: &mut TestAppContext,
3404    cx_b: &mut TestAppContext,
3405) {
3406    deterministic.forbid_parking();
3407    let mut server = TestServer::start(&deterministic).await;
3408    let client_a = server.create_client(cx_a, "user_a").await;
3409    let client_b = server.create_client(cx_b, "user_b").await;
3410    server
3411        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3412        .await;
3413    let active_call_a = cx_a.read(ActiveCall::global);
3414
3415    client_a
3416        .fs()
3417        .insert_tree(
3418            "/dir",
3419            json!({
3420                "a.txt": "a\nb\nc",
3421            }),
3422        )
3423        .await;
3424    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3425    let project_id = active_call_a
3426        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3427        .await
3428        .unwrap();
3429    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3430
3431    // Open a buffer as client B
3432    let buffer_b = project_b
3433        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3434        .await
3435        .unwrap();
3436    buffer_b.read_with(cx_b, |buf, _| {
3437        assert!(!buf.is_dirty());
3438        assert!(!buf.has_conflict());
3439        assert_eq!(buf.line_ending(), LineEnding::Unix);
3440    });
3441
3442    let new_contents = Rope::from("d\ne\nf");
3443    client_a
3444        .fs()
3445        .save("/dir/a.txt".as_ref(), &new_contents, LineEnding::Windows)
3446        .await
3447        .unwrap();
3448    cx_a.foreground().run_until_parked();
3449    buffer_b.read_with(cx_b, |buf, _| {
3450        assert_eq!(buf.text(), new_contents.to_string());
3451        assert!(!buf.is_dirty());
3452        assert!(!buf.has_conflict());
3453        assert_eq!(buf.line_ending(), LineEnding::Windows);
3454    });
3455}
3456
3457#[gpui::test(iterations = 10)]
3458async fn test_editing_while_guest_opens_buffer(
3459    deterministic: Arc<Deterministic>,
3460    cx_a: &mut TestAppContext,
3461    cx_b: &mut TestAppContext,
3462) {
3463    deterministic.forbid_parking();
3464    let mut server = TestServer::start(&deterministic).await;
3465    let client_a = server.create_client(cx_a, "user_a").await;
3466    let client_b = server.create_client(cx_b, "user_b").await;
3467    server
3468        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3469        .await;
3470    let active_call_a = cx_a.read(ActiveCall::global);
3471
3472    client_a
3473        .fs()
3474        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3475        .await;
3476    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3477    let project_id = active_call_a
3478        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3479        .await
3480        .unwrap();
3481    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3482
3483    // Open a buffer as client A
3484    let buffer_a = project_a
3485        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3486        .await
3487        .unwrap();
3488
3489    // Start opening the same buffer as client B
3490    let buffer_b = cx_b
3491        .background()
3492        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)));
3493
3494    // Edit the buffer as client A while client B is still opening it.
3495    cx_b.background().simulate_random_delay().await;
3496    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "X")], None, cx));
3497    cx_b.background().simulate_random_delay().await;
3498    buffer_a.update(cx_a, |buf, cx| buf.edit([(1..1, "Y")], None, cx));
3499
3500    let text = buffer_a.read_with(cx_a, |buf, _| buf.text());
3501    let buffer_b = buffer_b.await.unwrap();
3502    cx_a.foreground().run_until_parked();
3503    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), text));
3504}
3505
3506#[gpui::test]
3507async fn test_newline_above_or_below_does_not_move_guest_cursor(
3508    deterministic: Arc<Deterministic>,
3509    cx_a: &mut TestAppContext,
3510    cx_b: &mut TestAppContext,
3511) {
3512    deterministic.forbid_parking();
3513    let mut server = TestServer::start(&deterministic).await;
3514    let client_a = server.create_client(cx_a, "user_a").await;
3515    let client_b = server.create_client(cx_b, "user_b").await;
3516    server
3517        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3518        .await;
3519    let active_call_a = cx_a.read(ActiveCall::global);
3520
3521    client_a
3522        .fs()
3523        .insert_tree("/dir", json!({ "a.txt": "Some text\n" }))
3524        .await;
3525    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3526    let project_id = active_call_a
3527        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3528        .await
3529        .unwrap();
3530
3531    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3532
3533    // Open a buffer as client A
3534    let buffer_a = project_a
3535        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3536        .await
3537        .unwrap();
3538    let window_a = cx_a.add_window(|_| EmptyView);
3539    let editor_a = window_a.add_view(cx_a, |cx| Editor::for_buffer(buffer_a, Some(project_a), cx));
3540    let mut editor_cx_a = EditorTestContext {
3541        cx: cx_a,
3542        window: window_a.into(),
3543        editor: editor_a,
3544    };
3545
3546    // Open a buffer as client B
3547    let buffer_b = project_b
3548        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3549        .await
3550        .unwrap();
3551    let window_b = cx_b.add_window(|_| EmptyView);
3552    let editor_b = window_b.add_view(cx_b, |cx| Editor::for_buffer(buffer_b, Some(project_b), cx));
3553    let mut editor_cx_b = EditorTestContext {
3554        cx: cx_b,
3555        window: window_b.into(),
3556        editor: editor_b,
3557    };
3558
3559    // Test newline above
3560    editor_cx_a.set_selections_state(indoc! {"
3561        Some textˇ
3562    "});
3563    editor_cx_b.set_selections_state(indoc! {"
3564        Some textˇ
3565    "});
3566    editor_cx_a.update_editor(|editor, cx| editor.newline_above(&editor::NewlineAbove, cx));
3567    deterministic.run_until_parked();
3568    editor_cx_a.assert_editor_state(indoc! {"
3569        ˇ
3570        Some text
3571    "});
3572    editor_cx_b.assert_editor_state(indoc! {"
3573
3574        Some textˇ
3575    "});
3576
3577    // Test newline below
3578    editor_cx_a.set_selections_state(indoc! {"
3579
3580        Some textˇ
3581    "});
3582    editor_cx_b.set_selections_state(indoc! {"
3583
3584        Some textˇ
3585    "});
3586    editor_cx_a.update_editor(|editor, cx| editor.newline_below(&editor::NewlineBelow, cx));
3587    deterministic.run_until_parked();
3588    editor_cx_a.assert_editor_state(indoc! {"
3589
3590        Some text
3591        ˇ
3592    "});
3593    editor_cx_b.assert_editor_state(indoc! {"
3594
3595        Some textˇ
3596
3597    "});
3598}
3599
3600#[gpui::test(iterations = 10)]
3601async fn test_leaving_worktree_while_opening_buffer(
3602    deterministic: Arc<Deterministic>,
3603    cx_a: &mut TestAppContext,
3604    cx_b: &mut TestAppContext,
3605) {
3606    deterministic.forbid_parking();
3607    let mut server = TestServer::start(&deterministic).await;
3608    let client_a = server.create_client(cx_a, "user_a").await;
3609    let client_b = server.create_client(cx_b, "user_b").await;
3610    server
3611        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3612        .await;
3613    let active_call_a = cx_a.read(ActiveCall::global);
3614
3615    client_a
3616        .fs()
3617        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3618        .await;
3619    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3620    let project_id = active_call_a
3621        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3622        .await
3623        .unwrap();
3624    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3625
3626    // See that a guest has joined as client A.
3627    cx_a.foreground().run_until_parked();
3628    project_a.read_with(cx_a, |p, _| assert_eq!(p.collaborators().len(), 1));
3629
3630    // Begin opening a buffer as client B, but leave the project before the open completes.
3631    let buffer_b = cx_b
3632        .background()
3633        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)));
3634    cx_b.update(|_| drop(project_b));
3635    drop(buffer_b);
3636
3637    // See that the guest has left.
3638    cx_a.foreground().run_until_parked();
3639    project_a.read_with(cx_a, |p, _| assert!(p.collaborators().is_empty()));
3640}
3641
3642#[gpui::test(iterations = 10)]
3643async fn test_canceling_buffer_opening(
3644    deterministic: Arc<Deterministic>,
3645    cx_a: &mut TestAppContext,
3646    cx_b: &mut TestAppContext,
3647) {
3648    deterministic.forbid_parking();
3649
3650    let mut server = TestServer::start(&deterministic).await;
3651    let client_a = server.create_client(cx_a, "user_a").await;
3652    let client_b = server.create_client(cx_b, "user_b").await;
3653    server
3654        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3655        .await;
3656    let active_call_a = cx_a.read(ActiveCall::global);
3657
3658    client_a
3659        .fs()
3660        .insert_tree(
3661            "/dir",
3662            json!({
3663                "a.txt": "abc",
3664            }),
3665        )
3666        .await;
3667    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3668    let project_id = active_call_a
3669        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3670        .await
3671        .unwrap();
3672    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3673
3674    let buffer_a = project_a
3675        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3676        .await
3677        .unwrap();
3678
3679    // Open a buffer as client B but cancel after a random amount of time.
3680    let buffer_b = project_b.update(cx_b, |p, cx| {
3681        p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3682    });
3683    deterministic.simulate_random_delay().await;
3684    drop(buffer_b);
3685
3686    // Try opening the same buffer again as client B, and ensure we can
3687    // still do it despite the cancellation above.
3688    let buffer_b = project_b
3689        .update(cx_b, |p, cx| {
3690            p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3691        })
3692        .await
3693        .unwrap();
3694    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "abc"));
3695}
3696
3697#[gpui::test(iterations = 10)]
3698async fn test_leaving_project(
3699    deterministic: Arc<Deterministic>,
3700    cx_a: &mut TestAppContext,
3701    cx_b: &mut TestAppContext,
3702    cx_c: &mut TestAppContext,
3703) {
3704    deterministic.forbid_parking();
3705    let mut server = TestServer::start(&deterministic).await;
3706    let client_a = server.create_client(cx_a, "user_a").await;
3707    let client_b = server.create_client(cx_b, "user_b").await;
3708    let client_c = server.create_client(cx_c, "user_c").await;
3709    server
3710        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3711        .await;
3712    let active_call_a = cx_a.read(ActiveCall::global);
3713
3714    client_a
3715        .fs()
3716        .insert_tree(
3717            "/a",
3718            json!({
3719                "a.txt": "a-contents",
3720                "b.txt": "b-contents",
3721            }),
3722        )
3723        .await;
3724    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
3725    let project_id = active_call_a
3726        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3727        .await
3728        .unwrap();
3729    let project_b1 = client_b.build_remote_project(project_id, cx_b).await;
3730    let project_c = client_c.build_remote_project(project_id, cx_c).await;
3731
3732    // Client A sees that a guest has joined.
3733    deterministic.run_until_parked();
3734    project_a.read_with(cx_a, |project, _| {
3735        assert_eq!(project.collaborators().len(), 2);
3736    });
3737    project_b1.read_with(cx_b, |project, _| {
3738        assert_eq!(project.collaborators().len(), 2);
3739    });
3740    project_c.read_with(cx_c, |project, _| {
3741        assert_eq!(project.collaborators().len(), 2);
3742    });
3743
3744    // Client B opens a buffer.
3745    let buffer_b1 = project_b1
3746        .update(cx_b, |project, cx| {
3747            let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
3748            project.open_buffer((worktree_id, "a.txt"), cx)
3749        })
3750        .await
3751        .unwrap();
3752    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3753
3754    // Drop client B's project and ensure client A and client C observe client B leaving.
3755    cx_b.update(|_| drop(project_b1));
3756    deterministic.run_until_parked();
3757    project_a.read_with(cx_a, |project, _| {
3758        assert_eq!(project.collaborators().len(), 1);
3759    });
3760    project_c.read_with(cx_c, |project, _| {
3761        assert_eq!(project.collaborators().len(), 1);
3762    });
3763
3764    // Client B re-joins the project and can open buffers as before.
3765    let project_b2 = client_b.build_remote_project(project_id, cx_b).await;
3766    deterministic.run_until_parked();
3767    project_a.read_with(cx_a, |project, _| {
3768        assert_eq!(project.collaborators().len(), 2);
3769    });
3770    project_b2.read_with(cx_b, |project, _| {
3771        assert_eq!(project.collaborators().len(), 2);
3772    });
3773    project_c.read_with(cx_c, |project, _| {
3774        assert_eq!(project.collaborators().len(), 2);
3775    });
3776
3777    let buffer_b2 = project_b2
3778        .update(cx_b, |project, cx| {
3779            let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
3780            project.open_buffer((worktree_id, "a.txt"), cx)
3781        })
3782        .await
3783        .unwrap();
3784    buffer_b2.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3785
3786    // Drop client B's connection and ensure client A and client C observe client B leaving.
3787    client_b.disconnect(&cx_b.to_async());
3788    deterministic.advance_clock(RECONNECT_TIMEOUT);
3789    project_a.read_with(cx_a, |project, _| {
3790        assert_eq!(project.collaborators().len(), 1);
3791    });
3792    project_b2.read_with(cx_b, |project, _| {
3793        assert!(project.is_read_only());
3794    });
3795    project_c.read_with(cx_c, |project, _| {
3796        assert_eq!(project.collaborators().len(), 1);
3797    });
3798
3799    // Client B can't join the project, unless they re-join the room.
3800    cx_b.spawn(|cx| {
3801        Project::remote(
3802            project_id,
3803            client_b.app_state.client.clone(),
3804            client_b.user_store().clone(),
3805            client_b.language_registry().clone(),
3806            FakeFs::new(cx.background()),
3807            cx,
3808        )
3809    })
3810    .await
3811    .unwrap_err();
3812
3813    // Simulate connection loss for client C and ensure client A observes client C leaving the project.
3814    client_c.wait_for_current_user(cx_c).await;
3815    server.forbid_connections();
3816    server.disconnect_client(client_c.peer_id().unwrap());
3817    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
3818    deterministic.run_until_parked();
3819    project_a.read_with(cx_a, |project, _| {
3820        assert_eq!(project.collaborators().len(), 0);
3821    });
3822    project_b2.read_with(cx_b, |project, _| {
3823        assert!(project.is_read_only());
3824    });
3825    project_c.read_with(cx_c, |project, _| {
3826        assert!(project.is_read_only());
3827    });
3828}
3829
3830#[gpui::test(iterations = 10)]
3831async fn test_collaborating_with_diagnostics(
3832    deterministic: Arc<Deterministic>,
3833    cx_a: &mut TestAppContext,
3834    cx_b: &mut TestAppContext,
3835    cx_c: &mut TestAppContext,
3836) {
3837    deterministic.forbid_parking();
3838    let mut server = TestServer::start(&deterministic).await;
3839    let client_a = server.create_client(cx_a, "user_a").await;
3840    let client_b = server.create_client(cx_b, "user_b").await;
3841    let client_c = server.create_client(cx_c, "user_c").await;
3842    server
3843        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3844        .await;
3845    let active_call_a = cx_a.read(ActiveCall::global);
3846
3847    // Set up a fake language server.
3848    let mut language = Language::new(
3849        LanguageConfig {
3850            name: "Rust".into(),
3851            path_suffixes: vec!["rs".to_string()],
3852            ..Default::default()
3853        },
3854        Some(tree_sitter_rust::language()),
3855    );
3856    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
3857    client_a.language_registry().add(Arc::new(language));
3858
3859    // Share a project as client A
3860    client_a
3861        .fs()
3862        .insert_tree(
3863            "/a",
3864            json!({
3865                "a.rs": "let one = two",
3866                "other.rs": "",
3867            }),
3868        )
3869        .await;
3870    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3871
3872    // Cause the language server to start.
3873    let _buffer = project_a
3874        .update(cx_a, |project, cx| {
3875            project.open_buffer(
3876                ProjectPath {
3877                    worktree_id,
3878                    path: Path::new("other.rs").into(),
3879                },
3880                cx,
3881            )
3882        })
3883        .await
3884        .unwrap();
3885
3886    // Simulate a language server reporting errors for a file.
3887    let mut fake_language_server = fake_language_servers.next().await.unwrap();
3888    fake_language_server
3889        .receive_notification::<lsp::notification::DidOpenTextDocument>()
3890        .await;
3891    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3892        lsp::PublishDiagnosticsParams {
3893            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3894            version: None,
3895            diagnostics: vec![lsp::Diagnostic {
3896                severity: Some(lsp::DiagnosticSeverity::WARNING),
3897                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3898                message: "message 0".to_string(),
3899                ..Default::default()
3900            }],
3901        },
3902    );
3903
3904    // Client A shares the project and, simultaneously, the language server
3905    // publishes a diagnostic. This is done to ensure that the server always
3906    // observes the latest diagnostics for a worktree.
3907    let project_id = active_call_a
3908        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3909        .await
3910        .unwrap();
3911    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3912        lsp::PublishDiagnosticsParams {
3913            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3914            version: None,
3915            diagnostics: vec![lsp::Diagnostic {
3916                severity: Some(lsp::DiagnosticSeverity::ERROR),
3917                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3918                message: "message 1".to_string(),
3919                ..Default::default()
3920            }],
3921        },
3922    );
3923
3924    // Join the worktree as client B.
3925    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3926
3927    // Wait for server to see the diagnostics update.
3928    deterministic.run_until_parked();
3929
3930    // Ensure client B observes the new diagnostics.
3931    project_b.read_with(cx_b, |project, cx| {
3932        assert_eq!(
3933            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
3934            &[(
3935                ProjectPath {
3936                    worktree_id,
3937                    path: Arc::from(Path::new("a.rs")),
3938                },
3939                LanguageServerId(0),
3940                DiagnosticSummary {
3941                    error_count: 1,
3942                    warning_count: 0,
3943                    ..Default::default()
3944                },
3945            )]
3946        )
3947    });
3948
3949    // Join project as client C and observe the diagnostics.
3950    let project_c = client_c.build_remote_project(project_id, cx_c).await;
3951    let project_c_diagnostic_summaries =
3952        Rc::new(RefCell::new(project_c.read_with(cx_c, |project, cx| {
3953            project.diagnostic_summaries(false, cx).collect::<Vec<_>>()
3954        })));
3955    project_c.update(cx_c, |_, cx| {
3956        let summaries = project_c_diagnostic_summaries.clone();
3957        cx.subscribe(&project_c, {
3958            move |p, _, event, cx| {
3959                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
3960                    *summaries.borrow_mut() = p.diagnostic_summaries(false, cx).collect();
3961                }
3962            }
3963        })
3964        .detach();
3965    });
3966
3967    deterministic.run_until_parked();
3968    assert_eq!(
3969        project_c_diagnostic_summaries.borrow().as_slice(),
3970        &[(
3971            ProjectPath {
3972                worktree_id,
3973                path: Arc::from(Path::new("a.rs")),
3974            },
3975            LanguageServerId(0),
3976            DiagnosticSummary {
3977                error_count: 1,
3978                warning_count: 0,
3979                ..Default::default()
3980            },
3981        )]
3982    );
3983
3984    // Simulate a language server reporting more errors for a file.
3985    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3986        lsp::PublishDiagnosticsParams {
3987            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3988            version: None,
3989            diagnostics: vec![
3990                lsp::Diagnostic {
3991                    severity: Some(lsp::DiagnosticSeverity::ERROR),
3992                    range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3993                    message: "message 1".to_string(),
3994                    ..Default::default()
3995                },
3996                lsp::Diagnostic {
3997                    severity: Some(lsp::DiagnosticSeverity::WARNING),
3998                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 13)),
3999                    message: "message 2".to_string(),
4000                    ..Default::default()
4001                },
4002            ],
4003        },
4004    );
4005
4006    // Clients B and C get the updated summaries
4007    deterministic.run_until_parked();
4008    project_b.read_with(cx_b, |project, cx| {
4009        assert_eq!(
4010            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4011            [(
4012                ProjectPath {
4013                    worktree_id,
4014                    path: Arc::from(Path::new("a.rs")),
4015                },
4016                LanguageServerId(0),
4017                DiagnosticSummary {
4018                    error_count: 1,
4019                    warning_count: 1,
4020                },
4021            )]
4022        );
4023    });
4024    project_c.read_with(cx_c, |project, cx| {
4025        assert_eq!(
4026            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4027            [(
4028                ProjectPath {
4029                    worktree_id,
4030                    path: Arc::from(Path::new("a.rs")),
4031                },
4032                LanguageServerId(0),
4033                DiagnosticSummary {
4034                    error_count: 1,
4035                    warning_count: 1,
4036                },
4037            )]
4038        );
4039    });
4040
4041    // Open the file with the errors on client B. They should be present.
4042    let buffer_b = cx_b
4043        .background()
4044        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4045        .await
4046        .unwrap();
4047
4048    buffer_b.read_with(cx_b, |buffer, _| {
4049        assert_eq!(
4050            buffer
4051                .snapshot()
4052                .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
4053                .collect::<Vec<_>>(),
4054            &[
4055                DiagnosticEntry {
4056                    range: Point::new(0, 4)..Point::new(0, 7),
4057                    diagnostic: Diagnostic {
4058                        group_id: 2,
4059                        message: "message 1".to_string(),
4060                        severity: lsp::DiagnosticSeverity::ERROR,
4061                        is_primary: true,
4062                        ..Default::default()
4063                    }
4064                },
4065                DiagnosticEntry {
4066                    range: Point::new(0, 10)..Point::new(0, 13),
4067                    diagnostic: Diagnostic {
4068                        group_id: 3,
4069                        severity: lsp::DiagnosticSeverity::WARNING,
4070                        message: "message 2".to_string(),
4071                        is_primary: true,
4072                        ..Default::default()
4073                    }
4074                }
4075            ]
4076        );
4077    });
4078
4079    // Simulate a language server reporting no errors for a file.
4080    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
4081        lsp::PublishDiagnosticsParams {
4082            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
4083            version: None,
4084            diagnostics: vec![],
4085        },
4086    );
4087    deterministic.run_until_parked();
4088    project_a.read_with(cx_a, |project, cx| {
4089        assert_eq!(
4090            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4091            []
4092        )
4093    });
4094    project_b.read_with(cx_b, |project, cx| {
4095        assert_eq!(
4096            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4097            []
4098        )
4099    });
4100    project_c.read_with(cx_c, |project, cx| {
4101        assert_eq!(
4102            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4103            []
4104        )
4105    });
4106}
4107
4108#[gpui::test(iterations = 10)]
4109async fn test_collaborating_with_lsp_progress_updates_and_diagnostics_ordering(
4110    deterministic: Arc<Deterministic>,
4111    cx_a: &mut TestAppContext,
4112    cx_b: &mut TestAppContext,
4113) {
4114    deterministic.forbid_parking();
4115    let mut server = TestServer::start(&deterministic).await;
4116    let client_a = server.create_client(cx_a, "user_a").await;
4117    let client_b = server.create_client(cx_b, "user_b").await;
4118    server
4119        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4120        .await;
4121
4122    // Set up a fake language server.
4123    let mut language = Language::new(
4124        LanguageConfig {
4125            name: "Rust".into(),
4126            path_suffixes: vec!["rs".to_string()],
4127            ..Default::default()
4128        },
4129        Some(tree_sitter_rust::language()),
4130    );
4131    let mut fake_language_servers = language
4132        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4133            disk_based_diagnostics_progress_token: Some("the-disk-based-token".into()),
4134            disk_based_diagnostics_sources: vec!["the-disk-based-diagnostics-source".into()],
4135            ..Default::default()
4136        }))
4137        .await;
4138    client_a.language_registry().add(Arc::new(language));
4139
4140    let file_names = &["one.rs", "two.rs", "three.rs", "four.rs", "five.rs"];
4141    client_a
4142        .fs()
4143        .insert_tree(
4144            "/test",
4145            json!({
4146                "one.rs": "const ONE: usize = 1;",
4147                "two.rs": "const TWO: usize = 2;",
4148                "three.rs": "const THREE: usize = 3;",
4149                "four.rs": "const FOUR: usize = 3;",
4150                "five.rs": "const FIVE: usize = 3;",
4151            }),
4152        )
4153        .await;
4154
4155    let (project_a, worktree_id) = client_a.build_local_project("/test", cx_a).await;
4156
4157    // Share a project as client A
4158    let active_call_a = cx_a.read(ActiveCall::global);
4159    let project_id = active_call_a
4160        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4161        .await
4162        .unwrap();
4163
4164    // Join the project as client B and open all three files.
4165    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4166    let guest_buffers = futures::future::try_join_all(file_names.iter().map(|file_name| {
4167        project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, file_name), cx))
4168    }))
4169    .await
4170    .unwrap();
4171
4172    // Simulate a language server reporting errors for a file.
4173    let fake_language_server = fake_language_servers.next().await.unwrap();
4174    fake_language_server
4175        .request::<lsp::request::WorkDoneProgressCreate>(lsp::WorkDoneProgressCreateParams {
4176            token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4177        })
4178        .await
4179        .unwrap();
4180    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
4181        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4182        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin(
4183            lsp::WorkDoneProgressBegin {
4184                title: "Progress Began".into(),
4185                ..Default::default()
4186            },
4187        )),
4188    });
4189    for file_name in file_names {
4190        fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
4191            lsp::PublishDiagnosticsParams {
4192                uri: lsp::Url::from_file_path(Path::new("/test").join(file_name)).unwrap(),
4193                version: None,
4194                diagnostics: vec![lsp::Diagnostic {
4195                    severity: Some(lsp::DiagnosticSeverity::WARNING),
4196                    source: Some("the-disk-based-diagnostics-source".into()),
4197                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
4198                    message: "message one".to_string(),
4199                    ..Default::default()
4200                }],
4201            },
4202        );
4203    }
4204    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
4205        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4206        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End(
4207            lsp::WorkDoneProgressEnd { message: None },
4208        )),
4209    });
4210
4211    // When the "disk base diagnostics finished" message is received, the buffers'
4212    // diagnostics are expected to be present.
4213    let disk_based_diagnostics_finished = Arc::new(AtomicBool::new(false));
4214    project_b.update(cx_b, {
4215        let project_b = project_b.clone();
4216        let disk_based_diagnostics_finished = disk_based_diagnostics_finished.clone();
4217        move |_, cx| {
4218            cx.subscribe(&project_b, move |_, _, event, cx| {
4219                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
4220                    disk_based_diagnostics_finished.store(true, SeqCst);
4221                    for buffer in &guest_buffers {
4222                        assert_eq!(
4223                            buffer
4224                                .read(cx)
4225                                .snapshot()
4226                                .diagnostics_in_range::<_, usize>(0..5, false)
4227                                .count(),
4228                            1,
4229                            "expected a diagnostic for buffer {:?}",
4230                            buffer.read(cx).file().unwrap().path(),
4231                        );
4232                    }
4233                }
4234            })
4235            .detach();
4236        }
4237    });
4238
4239    deterministic.run_until_parked();
4240    assert!(disk_based_diagnostics_finished.load(SeqCst));
4241}
4242
4243#[gpui::test(iterations = 10)]
4244async fn test_collaborating_with_completion(
4245    deterministic: Arc<Deterministic>,
4246    cx_a: &mut TestAppContext,
4247    cx_b: &mut TestAppContext,
4248) {
4249    deterministic.forbid_parking();
4250    let mut server = TestServer::start(&deterministic).await;
4251    let client_a = server.create_client(cx_a, "user_a").await;
4252    let client_b = server.create_client(cx_b, "user_b").await;
4253    server
4254        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4255        .await;
4256    let active_call_a = cx_a.read(ActiveCall::global);
4257
4258    // Set up a fake language server.
4259    let mut language = Language::new(
4260        LanguageConfig {
4261            name: "Rust".into(),
4262            path_suffixes: vec!["rs".to_string()],
4263            ..Default::default()
4264        },
4265        Some(tree_sitter_rust::language()),
4266    );
4267    let mut fake_language_servers = language
4268        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4269            capabilities: lsp::ServerCapabilities {
4270                completion_provider: Some(lsp::CompletionOptions {
4271                    trigger_characters: Some(vec![".".to_string()]),
4272                    resolve_provider: Some(true),
4273                    ..Default::default()
4274                }),
4275                ..Default::default()
4276            },
4277            ..Default::default()
4278        }))
4279        .await;
4280    client_a.language_registry().add(Arc::new(language));
4281
4282    client_a
4283        .fs()
4284        .insert_tree(
4285            "/a",
4286            json!({
4287                "main.rs": "fn main() { a }",
4288                "other.rs": "",
4289            }),
4290        )
4291        .await;
4292    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
4293    let project_id = active_call_a
4294        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4295        .await
4296        .unwrap();
4297    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4298
4299    // Open a file in an editor as the guest.
4300    let buffer_b = project_b
4301        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
4302        .await
4303        .unwrap();
4304    let window_b = cx_b.add_window(|_| EmptyView);
4305    let editor_b = window_b.add_view(cx_b, |cx| {
4306        Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx)
4307    });
4308
4309    let fake_language_server = fake_language_servers.next().await.unwrap();
4310    cx_a.foreground().run_until_parked();
4311    buffer_b.read_with(cx_b, |buffer, _| {
4312        assert!(!buffer.completion_triggers().is_empty())
4313    });
4314
4315    // Type a completion trigger character as the guest.
4316    editor_b.update(cx_b, |editor, cx| {
4317        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
4318        editor.handle_input(".", cx);
4319        cx.focus(&editor_b);
4320    });
4321
4322    // Receive a completion request as the host's language server.
4323    // Return some completions from the host's language server.
4324    cx_a.foreground().start_waiting();
4325    fake_language_server
4326        .handle_request::<lsp::request::Completion, _, _>(|params, _| async move {
4327            assert_eq!(
4328                params.text_document_position.text_document.uri,
4329                lsp::Url::from_file_path("/a/main.rs").unwrap(),
4330            );
4331            assert_eq!(
4332                params.text_document_position.position,
4333                lsp::Position::new(0, 14),
4334            );
4335
4336            Ok(Some(lsp::CompletionResponse::Array(vec![
4337                lsp::CompletionItem {
4338                    label: "first_method(…)".into(),
4339                    detail: Some("fn(&mut self, B) -> C".into()),
4340                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
4341                        new_text: "first_method($1)".to_string(),
4342                        range: lsp::Range::new(
4343                            lsp::Position::new(0, 14),
4344                            lsp::Position::new(0, 14),
4345                        ),
4346                    })),
4347                    insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
4348                    ..Default::default()
4349                },
4350                lsp::CompletionItem {
4351                    label: "second_method(…)".into(),
4352                    detail: Some("fn(&mut self, C) -> D<E>".into()),
4353                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
4354                        new_text: "second_method()".to_string(),
4355                        range: lsp::Range::new(
4356                            lsp::Position::new(0, 14),
4357                            lsp::Position::new(0, 14),
4358                        ),
4359                    })),
4360                    insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
4361                    ..Default::default()
4362                },
4363            ])))
4364        })
4365        .next()
4366        .await
4367        .unwrap();
4368    cx_a.foreground().finish_waiting();
4369
4370    // Open the buffer on the host.
4371    let buffer_a = project_a
4372        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
4373        .await
4374        .unwrap();
4375    cx_a.foreground().run_until_parked();
4376    buffer_a.read_with(cx_a, |buffer, _| {
4377        assert_eq!(buffer.text(), "fn main() { a. }")
4378    });
4379
4380    // Confirm a completion on the guest.
4381    editor_b.read_with(cx_b, |editor, _| assert!(editor.context_menu_visible()));
4382    editor_b.update(cx_b, |editor, cx| {
4383        editor.confirm_completion(&ConfirmCompletion { item_ix: Some(0) }, cx);
4384        assert_eq!(editor.text(cx), "fn main() { a.first_method() }");
4385    });
4386
4387    // Return a resolved completion from the host's language server.
4388    // The resolved completion has an additional text edit.
4389    fake_language_server.handle_request::<lsp::request::ResolveCompletionItem, _, _>(
4390        |params, _| async move {
4391            assert_eq!(params.label, "first_method(…)");
4392            Ok(lsp::CompletionItem {
4393                label: "first_method(…)".into(),
4394                detail: Some("fn(&mut self, B) -> C".into()),
4395                text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
4396                    new_text: "first_method($1)".to_string(),
4397                    range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
4398                })),
4399                additional_text_edits: Some(vec![lsp::TextEdit {
4400                    new_text: "use d::SomeTrait;\n".to_string(),
4401                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
4402                }]),
4403                insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
4404                ..Default::default()
4405            })
4406        },
4407    );
4408
4409    // The additional edit is applied.
4410    cx_a.foreground().run_until_parked();
4411    buffer_a.read_with(cx_a, |buffer, _| {
4412        assert_eq!(
4413            buffer.text(),
4414            "use d::SomeTrait;\nfn main() { a.first_method() }"
4415        );
4416    });
4417    buffer_b.read_with(cx_b, |buffer, _| {
4418        assert_eq!(
4419            buffer.text(),
4420            "use d::SomeTrait;\nfn main() { a.first_method() }"
4421        );
4422    });
4423}
4424
4425#[gpui::test(iterations = 10)]
4426async fn test_reloading_buffer_manually(
4427    deterministic: Arc<Deterministic>,
4428    cx_a: &mut TestAppContext,
4429    cx_b: &mut TestAppContext,
4430) {
4431    deterministic.forbid_parking();
4432    let mut server = TestServer::start(&deterministic).await;
4433    let client_a = server.create_client(cx_a, "user_a").await;
4434    let client_b = server.create_client(cx_b, "user_b").await;
4435    server
4436        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4437        .await;
4438    let active_call_a = cx_a.read(ActiveCall::global);
4439
4440    client_a
4441        .fs()
4442        .insert_tree("/a", json!({ "a.rs": "let one = 1;" }))
4443        .await;
4444    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
4445    let buffer_a = project_a
4446        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
4447        .await
4448        .unwrap();
4449    let project_id = active_call_a
4450        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4451        .await
4452        .unwrap();
4453
4454    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4455
4456    let buffer_b = cx_b
4457        .background()
4458        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4459        .await
4460        .unwrap();
4461    buffer_b.update(cx_b, |buffer, cx| {
4462        buffer.edit([(4..7, "six")], None, cx);
4463        buffer.edit([(10..11, "6")], None, cx);
4464        assert_eq!(buffer.text(), "let six = 6;");
4465        assert!(buffer.is_dirty());
4466        assert!(!buffer.has_conflict());
4467    });
4468    cx_a.foreground().run_until_parked();
4469    buffer_a.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "let six = 6;"));
4470
4471    client_a
4472        .fs()
4473        .save(
4474            "/a/a.rs".as_ref(),
4475            &Rope::from("let seven = 7;"),
4476            LineEnding::Unix,
4477        )
4478        .await
4479        .unwrap();
4480    cx_a.foreground().run_until_parked();
4481    buffer_a.read_with(cx_a, |buffer, _| assert!(buffer.has_conflict()));
4482    buffer_b.read_with(cx_b, |buffer, _| assert!(buffer.has_conflict()));
4483
4484    project_b
4485        .update(cx_b, |project, cx| {
4486            project.reload_buffers(HashSet::from_iter([buffer_b.clone()]), true, cx)
4487        })
4488        .await
4489        .unwrap();
4490    buffer_a.read_with(cx_a, |buffer, _| {
4491        assert_eq!(buffer.text(), "let seven = 7;");
4492        assert!(!buffer.is_dirty());
4493        assert!(!buffer.has_conflict());
4494    });
4495    buffer_b.read_with(cx_b, |buffer, _| {
4496        assert_eq!(buffer.text(), "let seven = 7;");
4497        assert!(!buffer.is_dirty());
4498        assert!(!buffer.has_conflict());
4499    });
4500
4501    buffer_a.update(cx_a, |buffer, cx| {
4502        // Undoing on the host is a no-op when the reload was initiated by the guest.
4503        buffer.undo(cx);
4504        assert_eq!(buffer.text(), "let seven = 7;");
4505        assert!(!buffer.is_dirty());
4506        assert!(!buffer.has_conflict());
4507    });
4508    buffer_b.update(cx_b, |buffer, cx| {
4509        // Undoing on the guest rolls back the buffer to before it was reloaded but the conflict gets cleared.
4510        buffer.undo(cx);
4511        assert_eq!(buffer.text(), "let six = 6;");
4512        assert!(buffer.is_dirty());
4513        assert!(!buffer.has_conflict());
4514    });
4515}
4516
4517#[gpui::test(iterations = 10)]
4518async fn test_formatting_buffer(
4519    deterministic: Arc<Deterministic>,
4520    cx_a: &mut TestAppContext,
4521    cx_b: &mut TestAppContext,
4522) {
4523    let mut server = TestServer::start(&deterministic).await;
4524    let client_a = server.create_client(cx_a, "user_a").await;
4525    let client_b = server.create_client(cx_b, "user_b").await;
4526    server
4527        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4528        .await;
4529    let active_call_a = cx_a.read(ActiveCall::global);
4530
4531    // Set up a fake language server.
4532    let mut language = Language::new(
4533        LanguageConfig {
4534            name: "Rust".into(),
4535            path_suffixes: vec!["rs".to_string()],
4536            ..Default::default()
4537        },
4538        Some(tree_sitter_rust::language()),
4539    );
4540    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4541    client_a.language_registry().add(Arc::new(language));
4542
4543    // Here we insert a fake tree with a directory that exists on disk. This is needed
4544    // because later we'll invoke a command, which requires passing a working directory
4545    // that points to a valid location on disk.
4546    let directory = env::current_dir().unwrap();
4547    client_a
4548        .fs()
4549        .insert_tree(&directory, json!({ "a.rs": "let one = \"two\"" }))
4550        .await;
4551    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
4552    let project_id = active_call_a
4553        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4554        .await
4555        .unwrap();
4556    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4557
4558    let buffer_b = cx_b
4559        .background()
4560        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4561        .await
4562        .unwrap();
4563
4564    let fake_language_server = fake_language_servers.next().await.unwrap();
4565    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
4566        Ok(Some(vec![
4567            lsp::TextEdit {
4568                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 4)),
4569                new_text: "h".to_string(),
4570            },
4571            lsp::TextEdit {
4572                range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 7)),
4573                new_text: "y".to_string(),
4574            },
4575        ]))
4576    });
4577
4578    project_b
4579        .update(cx_b, |project, cx| {
4580            project.format(
4581                HashSet::from_iter([buffer_b.clone()]),
4582                true,
4583                FormatTrigger::Save,
4584                cx,
4585            )
4586        })
4587        .await
4588        .unwrap();
4589
4590    // The edits from the LSP are applied, and a final newline is added.
4591    assert_eq!(
4592        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4593        "let honey = \"two\"\n"
4594    );
4595
4596    // Ensure buffer can be formatted using an external command. Notice how the
4597    // host's configuration is honored as opposed to using the guest's settings.
4598    cx_a.update(|cx| {
4599        cx.update_global(|store: &mut SettingsStore, cx| {
4600            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4601                file.defaults.formatter = Some(Formatter::External {
4602                    command: "awk".into(),
4603                    arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()].into(),
4604                });
4605            });
4606        });
4607    });
4608    project_b
4609        .update(cx_b, |project, cx| {
4610            project.format(
4611                HashSet::from_iter([buffer_b.clone()]),
4612                true,
4613                FormatTrigger::Save,
4614                cx,
4615            )
4616        })
4617        .await
4618        .unwrap();
4619    assert_eq!(
4620        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4621        format!("let honey = \"{}/a.rs\"\n", directory.to_str().unwrap())
4622    );
4623}
4624
4625#[gpui::test(iterations = 10)]
4626async fn test_prettier_formatting_buffer(
4627    deterministic: Arc<Deterministic>,
4628    cx_a: &mut TestAppContext,
4629    cx_b: &mut TestAppContext,
4630) {
4631    let mut server = TestServer::start(&deterministic).await;
4632    let client_a = server.create_client(cx_a, "user_a").await;
4633    let client_b = server.create_client(cx_b, "user_b").await;
4634    server
4635        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4636        .await;
4637    let active_call_a = cx_a.read(ActiveCall::global);
4638
4639    // Set up a fake language server.
4640    let mut language = Language::new(
4641        LanguageConfig {
4642            name: "Rust".into(),
4643            path_suffixes: vec!["rs".to_string()],
4644            prettier_parser_name: Some("test_parser".to_string()),
4645            ..Default::default()
4646        },
4647        Some(tree_sitter_rust::language()),
4648    );
4649    let test_plugin = "test_plugin";
4650    let mut fake_language_servers = language
4651        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4652            prettier_plugins: vec![test_plugin],
4653            ..Default::default()
4654        }))
4655        .await;
4656    let language = Arc::new(language);
4657    client_a.language_registry().add(Arc::clone(&language));
4658
4659    // Here we insert a fake tree with a directory that exists on disk. This is needed
4660    // because later we'll invoke a command, which requires passing a working directory
4661    // that points to a valid location on disk.
4662    let directory = env::current_dir().unwrap();
4663    let buffer_text = "let one = \"two\"";
4664    client_a
4665        .fs()
4666        .insert_tree(&directory, json!({ "a.rs": buffer_text }))
4667        .await;
4668    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
4669    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
4670    let buffer_a = cx_a
4671        .background()
4672        .spawn(project_a.update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4673        .await
4674        .unwrap();
4675
4676    let project_id = active_call_a
4677        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4678        .await
4679        .unwrap();
4680    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4681    let buffer_b = cx_b
4682        .background()
4683        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4684        .await
4685        .unwrap();
4686
4687    cx_a.update(|cx| {
4688        cx.update_global(|store: &mut SettingsStore, cx| {
4689            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4690                file.defaults.formatter = Some(Formatter::Auto);
4691            });
4692        });
4693    });
4694    cx_b.update(|cx| {
4695        cx.update_global(|store: &mut SettingsStore, cx| {
4696            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4697                file.defaults.formatter = Some(Formatter::LanguageServer);
4698            });
4699        });
4700    });
4701    let fake_language_server = fake_language_servers.next().await.unwrap();
4702    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
4703        panic!(
4704            "Unexpected: prettier should be preferred since it's enabled and language supports it"
4705        )
4706    });
4707
4708    project_b
4709        .update(cx_b, |project, cx| {
4710            project.format(
4711                HashSet::from_iter([buffer_b.clone()]),
4712                true,
4713                FormatTrigger::Save,
4714                cx,
4715            )
4716        })
4717        .await
4718        .unwrap();
4719    cx_a.foreground().run_until_parked();
4720    cx_b.foreground().run_until_parked();
4721    assert_eq!(
4722        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4723        buffer_text.to_string() + "\n" + prettier_format_suffix,
4724        "Prettier formatting was not applied to client buffer after client's request"
4725    );
4726
4727    project_a
4728        .update(cx_a, |project, cx| {
4729            project.format(
4730                HashSet::from_iter([buffer_a.clone()]),
4731                true,
4732                FormatTrigger::Manual,
4733                cx,
4734            )
4735        })
4736        .await
4737        .unwrap();
4738    cx_a.foreground().run_until_parked();
4739    cx_b.foreground().run_until_parked();
4740    assert_eq!(
4741        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4742        buffer_text.to_string() + "\n" + prettier_format_suffix + "\n" + prettier_format_suffix,
4743        "Prettier formatting was not applied to client buffer after host's request"
4744    );
4745}
4746
4747#[gpui::test(iterations = 10)]
4748async fn test_definition(
4749    deterministic: Arc<Deterministic>,
4750    cx_a: &mut TestAppContext,
4751    cx_b: &mut TestAppContext,
4752) {
4753    deterministic.forbid_parking();
4754    let mut server = TestServer::start(&deterministic).await;
4755    let client_a = server.create_client(cx_a, "user_a").await;
4756    let client_b = server.create_client(cx_b, "user_b").await;
4757    server
4758        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4759        .await;
4760    let active_call_a = cx_a.read(ActiveCall::global);
4761
4762    // Set up a fake language server.
4763    let mut language = Language::new(
4764        LanguageConfig {
4765            name: "Rust".into(),
4766            path_suffixes: vec!["rs".to_string()],
4767            ..Default::default()
4768        },
4769        Some(tree_sitter_rust::language()),
4770    );
4771    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4772    client_a.language_registry().add(Arc::new(language));
4773
4774    client_a
4775        .fs()
4776        .insert_tree(
4777            "/root",
4778            json!({
4779                "dir-1": {
4780                    "a.rs": "const ONE: usize = b::TWO + b::THREE;",
4781                },
4782                "dir-2": {
4783                    "b.rs": "const TWO: c::T2 = 2;\nconst THREE: usize = 3;",
4784                    "c.rs": "type T2 = usize;",
4785                }
4786            }),
4787        )
4788        .await;
4789    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4790    let project_id = active_call_a
4791        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4792        .await
4793        .unwrap();
4794    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4795
4796    // Open the file on client B.
4797    let buffer_b = cx_b
4798        .background()
4799        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4800        .await
4801        .unwrap();
4802
4803    // Request the definition of a symbol as the guest.
4804    let fake_language_server = fake_language_servers.next().await.unwrap();
4805    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4806        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4807            lsp::Location::new(
4808                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4809                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4810            ),
4811        )))
4812    });
4813
4814    let definitions_1 = project_b
4815        .update(cx_b, |p, cx| p.definition(&buffer_b, 23, cx))
4816        .await
4817        .unwrap();
4818    cx_b.read(|cx| {
4819        assert_eq!(definitions_1.len(), 1);
4820        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4821        let target_buffer = definitions_1[0].target.buffer.read(cx);
4822        assert_eq!(
4823            target_buffer.text(),
4824            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4825        );
4826        assert_eq!(
4827            definitions_1[0].target.range.to_point(target_buffer),
4828            Point::new(0, 6)..Point::new(0, 9)
4829        );
4830    });
4831
4832    // Try getting more definitions for the same buffer, ensuring the buffer gets reused from
4833    // the previous call to `definition`.
4834    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4835        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4836            lsp::Location::new(
4837                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4838                lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)),
4839            ),
4840        )))
4841    });
4842
4843    let definitions_2 = project_b
4844        .update(cx_b, |p, cx| p.definition(&buffer_b, 33, cx))
4845        .await
4846        .unwrap();
4847    cx_b.read(|cx| {
4848        assert_eq!(definitions_2.len(), 1);
4849        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4850        let target_buffer = definitions_2[0].target.buffer.read(cx);
4851        assert_eq!(
4852            target_buffer.text(),
4853            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4854        );
4855        assert_eq!(
4856            definitions_2[0].target.range.to_point(target_buffer),
4857            Point::new(1, 6)..Point::new(1, 11)
4858        );
4859    });
4860    assert_eq!(
4861        definitions_1[0].target.buffer,
4862        definitions_2[0].target.buffer
4863    );
4864
4865    fake_language_server.handle_request::<lsp::request::GotoTypeDefinition, _, _>(
4866        |req, _| async move {
4867            assert_eq!(
4868                req.text_document_position_params.position,
4869                lsp::Position::new(0, 7)
4870            );
4871            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4872                lsp::Location::new(
4873                    lsp::Url::from_file_path("/root/dir-2/c.rs").unwrap(),
4874                    lsp::Range::new(lsp::Position::new(0, 5), lsp::Position::new(0, 7)),
4875                ),
4876            )))
4877        },
4878    );
4879
4880    let type_definitions = project_b
4881        .update(cx_b, |p, cx| p.type_definition(&buffer_b, 7, cx))
4882        .await
4883        .unwrap();
4884    cx_b.read(|cx| {
4885        assert_eq!(type_definitions.len(), 1);
4886        let target_buffer = type_definitions[0].target.buffer.read(cx);
4887        assert_eq!(target_buffer.text(), "type T2 = usize;");
4888        assert_eq!(
4889            type_definitions[0].target.range.to_point(target_buffer),
4890            Point::new(0, 5)..Point::new(0, 7)
4891        );
4892    });
4893}
4894
4895#[gpui::test(iterations = 10)]
4896async fn test_references(
4897    deterministic: Arc<Deterministic>,
4898    cx_a: &mut TestAppContext,
4899    cx_b: &mut TestAppContext,
4900) {
4901    deterministic.forbid_parking();
4902    let mut server = TestServer::start(&deterministic).await;
4903    let client_a = server.create_client(cx_a, "user_a").await;
4904    let client_b = server.create_client(cx_b, "user_b").await;
4905    server
4906        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4907        .await;
4908    let active_call_a = cx_a.read(ActiveCall::global);
4909
4910    // Set up a fake language server.
4911    let mut language = Language::new(
4912        LanguageConfig {
4913            name: "Rust".into(),
4914            path_suffixes: vec!["rs".to_string()],
4915            ..Default::default()
4916        },
4917        Some(tree_sitter_rust::language()),
4918    );
4919    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4920    client_a.language_registry().add(Arc::new(language));
4921
4922    client_a
4923        .fs()
4924        .insert_tree(
4925            "/root",
4926            json!({
4927                "dir-1": {
4928                    "one.rs": "const ONE: usize = 1;",
4929                    "two.rs": "const TWO: usize = one::ONE + one::ONE;",
4930                },
4931                "dir-2": {
4932                    "three.rs": "const THREE: usize = two::TWO + one::ONE;",
4933                }
4934            }),
4935        )
4936        .await;
4937    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4938    let project_id = active_call_a
4939        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4940        .await
4941        .unwrap();
4942    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4943
4944    // Open the file on client B.
4945    let buffer_b = cx_b
4946        .background()
4947        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
4948        .await
4949        .unwrap();
4950
4951    // Request references to a symbol as the guest.
4952    let fake_language_server = fake_language_servers.next().await.unwrap();
4953    fake_language_server.handle_request::<lsp::request::References, _, _>(|params, _| async move {
4954        assert_eq!(
4955            params.text_document_position.text_document.uri.as_str(),
4956            "file:///root/dir-1/one.rs"
4957        );
4958        Ok(Some(vec![
4959            lsp::Location {
4960                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4961                range: lsp::Range::new(lsp::Position::new(0, 24), lsp::Position::new(0, 27)),
4962            },
4963            lsp::Location {
4964                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4965                range: lsp::Range::new(lsp::Position::new(0, 35), lsp::Position::new(0, 38)),
4966            },
4967            lsp::Location {
4968                uri: lsp::Url::from_file_path("/root/dir-2/three.rs").unwrap(),
4969                range: lsp::Range::new(lsp::Position::new(0, 37), lsp::Position::new(0, 40)),
4970            },
4971        ]))
4972    });
4973
4974    let references = project_b
4975        .update(cx_b, |p, cx| p.references(&buffer_b, 7, cx))
4976        .await
4977        .unwrap();
4978    cx_b.read(|cx| {
4979        assert_eq!(references.len(), 3);
4980        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4981
4982        let two_buffer = references[0].buffer.read(cx);
4983        let three_buffer = references[2].buffer.read(cx);
4984        assert_eq!(
4985            two_buffer.file().unwrap().path().as_ref(),
4986            Path::new("two.rs")
4987        );
4988        assert_eq!(references[1].buffer, references[0].buffer);
4989        assert_eq!(
4990            three_buffer.file().unwrap().full_path(cx),
4991            Path::new("/root/dir-2/three.rs")
4992        );
4993
4994        assert_eq!(references[0].range.to_offset(two_buffer), 24..27);
4995        assert_eq!(references[1].range.to_offset(two_buffer), 35..38);
4996        assert_eq!(references[2].range.to_offset(three_buffer), 37..40);
4997    });
4998}
4999
5000#[gpui::test(iterations = 10)]
5001async fn test_project_search(
5002    deterministic: Arc<Deterministic>,
5003    cx_a: &mut TestAppContext,
5004    cx_b: &mut TestAppContext,
5005) {
5006    deterministic.forbid_parking();
5007    let mut server = TestServer::start(&deterministic).await;
5008    let client_a = server.create_client(cx_a, "user_a").await;
5009    let client_b = server.create_client(cx_b, "user_b").await;
5010    server
5011        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5012        .await;
5013    let active_call_a = cx_a.read(ActiveCall::global);
5014
5015    client_a
5016        .fs()
5017        .insert_tree(
5018            "/root",
5019            json!({
5020                "dir-1": {
5021                    "a": "hello world",
5022                    "b": "goodnight moon",
5023                    "c": "a world of goo",
5024                    "d": "world champion of clown world",
5025                },
5026                "dir-2": {
5027                    "e": "disney world is fun",
5028                }
5029            }),
5030        )
5031        .await;
5032    let (project_a, _) = client_a.build_local_project("/root/dir-1", cx_a).await;
5033    let (worktree_2, _) = project_a
5034        .update(cx_a, |p, cx| {
5035            p.find_or_create_local_worktree("/root/dir-2", true, cx)
5036        })
5037        .await
5038        .unwrap();
5039    worktree_2
5040        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
5041        .await;
5042    let project_id = active_call_a
5043        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5044        .await
5045        .unwrap();
5046
5047    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5048
5049    // Perform a search as the guest.
5050    let mut results = HashMap::default();
5051    let mut search_rx = project_b.update(cx_b, |project, cx| {
5052        project.search(
5053            SearchQuery::text("world", false, false, false, Vec::new(), Vec::new()).unwrap(),
5054            cx,
5055        )
5056    });
5057    while let Some((buffer, ranges)) = search_rx.next().await {
5058        results.entry(buffer).or_insert(ranges);
5059    }
5060
5061    let mut ranges_by_path = results
5062        .into_iter()
5063        .map(|(buffer, ranges)| {
5064            buffer.read_with(cx_b, |buffer, cx| {
5065                let path = buffer.file().unwrap().full_path(cx);
5066                let offset_ranges = ranges
5067                    .into_iter()
5068                    .map(|range| range.to_offset(buffer))
5069                    .collect::<Vec<_>>();
5070                (path, offset_ranges)
5071            })
5072        })
5073        .collect::<Vec<_>>();
5074    ranges_by_path.sort_by_key(|(path, _)| path.clone());
5075
5076    assert_eq!(
5077        ranges_by_path,
5078        &[
5079            (PathBuf::from("dir-1/a"), vec![6..11]),
5080            (PathBuf::from("dir-1/c"), vec![2..7]),
5081            (PathBuf::from("dir-1/d"), vec![0..5, 24..29]),
5082            (PathBuf::from("dir-2/e"), vec![7..12]),
5083        ]
5084    );
5085}
5086
5087#[gpui::test(iterations = 10)]
5088async fn test_document_highlights(
5089    deterministic: Arc<Deterministic>,
5090    cx_a: &mut TestAppContext,
5091    cx_b: &mut TestAppContext,
5092) {
5093    deterministic.forbid_parking();
5094    let mut server = TestServer::start(&deterministic).await;
5095    let client_a = server.create_client(cx_a, "user_a").await;
5096    let client_b = server.create_client(cx_b, "user_b").await;
5097    server
5098        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5099        .await;
5100    let active_call_a = cx_a.read(ActiveCall::global);
5101
5102    client_a
5103        .fs()
5104        .insert_tree(
5105            "/root-1",
5106            json!({
5107                "main.rs": "fn double(number: i32) -> i32 { number + number }",
5108            }),
5109        )
5110        .await;
5111
5112    // Set up a fake language server.
5113    let mut language = Language::new(
5114        LanguageConfig {
5115            name: "Rust".into(),
5116            path_suffixes: vec!["rs".to_string()],
5117            ..Default::default()
5118        },
5119        Some(tree_sitter_rust::language()),
5120    );
5121    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5122    client_a.language_registry().add(Arc::new(language));
5123
5124    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
5125    let project_id = active_call_a
5126        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5127        .await
5128        .unwrap();
5129    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5130
5131    // Open the file on client B.
5132    let buffer_b = cx_b
5133        .background()
5134        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
5135        .await
5136        .unwrap();
5137
5138    // Request document highlights as the guest.
5139    let fake_language_server = fake_language_servers.next().await.unwrap();
5140    fake_language_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
5141        |params, _| async move {
5142            assert_eq!(
5143                params
5144                    .text_document_position_params
5145                    .text_document
5146                    .uri
5147                    .as_str(),
5148                "file:///root-1/main.rs"
5149            );
5150            assert_eq!(
5151                params.text_document_position_params.position,
5152                lsp::Position::new(0, 34)
5153            );
5154            Ok(Some(vec![
5155                lsp::DocumentHighlight {
5156                    kind: Some(lsp::DocumentHighlightKind::WRITE),
5157                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 16)),
5158                },
5159                lsp::DocumentHighlight {
5160                    kind: Some(lsp::DocumentHighlightKind::READ),
5161                    range: lsp::Range::new(lsp::Position::new(0, 32), lsp::Position::new(0, 38)),
5162                },
5163                lsp::DocumentHighlight {
5164                    kind: Some(lsp::DocumentHighlightKind::READ),
5165                    range: lsp::Range::new(lsp::Position::new(0, 41), lsp::Position::new(0, 47)),
5166                },
5167            ]))
5168        },
5169    );
5170
5171    let highlights = project_b
5172        .update(cx_b, |p, cx| p.document_highlights(&buffer_b, 34, cx))
5173        .await
5174        .unwrap();
5175    buffer_b.read_with(cx_b, |buffer, _| {
5176        let snapshot = buffer.snapshot();
5177
5178        let highlights = highlights
5179            .into_iter()
5180            .map(|highlight| (highlight.kind, highlight.range.to_offset(&snapshot)))
5181            .collect::<Vec<_>>();
5182        assert_eq!(
5183            highlights,
5184            &[
5185                (lsp::DocumentHighlightKind::WRITE, 10..16),
5186                (lsp::DocumentHighlightKind::READ, 32..38),
5187                (lsp::DocumentHighlightKind::READ, 41..47)
5188            ]
5189        )
5190    });
5191}
5192
5193#[gpui::test(iterations = 10)]
5194async fn test_lsp_hover(
5195    deterministic: Arc<Deterministic>,
5196    cx_a: &mut TestAppContext,
5197    cx_b: &mut TestAppContext,
5198) {
5199    deterministic.forbid_parking();
5200    let mut server = TestServer::start(&deterministic).await;
5201    let client_a = server.create_client(cx_a, "user_a").await;
5202    let client_b = server.create_client(cx_b, "user_b").await;
5203    server
5204        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5205        .await;
5206    let active_call_a = cx_a.read(ActiveCall::global);
5207
5208    client_a
5209        .fs()
5210        .insert_tree(
5211            "/root-1",
5212            json!({
5213                "main.rs": "use std::collections::HashMap;",
5214            }),
5215        )
5216        .await;
5217
5218    // Set up a fake language server.
5219    let mut language = Language::new(
5220        LanguageConfig {
5221            name: "Rust".into(),
5222            path_suffixes: vec!["rs".to_string()],
5223            ..Default::default()
5224        },
5225        Some(tree_sitter_rust::language()),
5226    );
5227    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5228    client_a.language_registry().add(Arc::new(language));
5229
5230    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
5231    let project_id = active_call_a
5232        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5233        .await
5234        .unwrap();
5235    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5236
5237    // Open the file as the guest
5238    let buffer_b = cx_b
5239        .background()
5240        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
5241        .await
5242        .unwrap();
5243
5244    // Request hover information as the guest.
5245    let fake_language_server = fake_language_servers.next().await.unwrap();
5246    fake_language_server.handle_request::<lsp::request::HoverRequest, _, _>(
5247        |params, _| async move {
5248            assert_eq!(
5249                params
5250                    .text_document_position_params
5251                    .text_document
5252                    .uri
5253                    .as_str(),
5254                "file:///root-1/main.rs"
5255            );
5256            assert_eq!(
5257                params.text_document_position_params.position,
5258                lsp::Position::new(0, 22)
5259            );
5260            Ok(Some(lsp::Hover {
5261                contents: lsp::HoverContents::Array(vec![
5262                    lsp::MarkedString::String("Test hover content.".to_string()),
5263                    lsp::MarkedString::LanguageString(lsp::LanguageString {
5264                        language: "Rust".to_string(),
5265                        value: "let foo = 42;".to_string(),
5266                    }),
5267                ]),
5268                range: Some(lsp::Range::new(
5269                    lsp::Position::new(0, 22),
5270                    lsp::Position::new(0, 29),
5271                )),
5272            }))
5273        },
5274    );
5275
5276    let hover_info = project_b
5277        .update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
5278        .await
5279        .unwrap()
5280        .unwrap();
5281    buffer_b.read_with(cx_b, |buffer, _| {
5282        let snapshot = buffer.snapshot();
5283        assert_eq!(hover_info.range.unwrap().to_offset(&snapshot), 22..29);
5284        assert_eq!(
5285            hover_info.contents,
5286            vec![
5287                project::HoverBlock {
5288                    text: "Test hover content.".to_string(),
5289                    kind: HoverBlockKind::Markdown,
5290                },
5291                project::HoverBlock {
5292                    text: "let foo = 42;".to_string(),
5293                    kind: HoverBlockKind::Code {
5294                        language: "Rust".to_string()
5295                    },
5296                }
5297            ]
5298        );
5299    });
5300}
5301
5302#[gpui::test(iterations = 10)]
5303async fn test_project_symbols(
5304    deterministic: Arc<Deterministic>,
5305    cx_a: &mut TestAppContext,
5306    cx_b: &mut TestAppContext,
5307) {
5308    deterministic.forbid_parking();
5309    let mut server = TestServer::start(&deterministic).await;
5310    let client_a = server.create_client(cx_a, "user_a").await;
5311    let client_b = server.create_client(cx_b, "user_b").await;
5312    server
5313        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5314        .await;
5315    let active_call_a = cx_a.read(ActiveCall::global);
5316
5317    // Set up a fake language server.
5318    let mut language = Language::new(
5319        LanguageConfig {
5320            name: "Rust".into(),
5321            path_suffixes: vec!["rs".to_string()],
5322            ..Default::default()
5323        },
5324        Some(tree_sitter_rust::language()),
5325    );
5326    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5327    client_a.language_registry().add(Arc::new(language));
5328
5329    client_a
5330        .fs()
5331        .insert_tree(
5332            "/code",
5333            json!({
5334                "crate-1": {
5335                    "one.rs": "const ONE: usize = 1;",
5336                },
5337                "crate-2": {
5338                    "two.rs": "const TWO: usize = 2; const THREE: usize = 3;",
5339                },
5340                "private": {
5341                    "passwords.txt": "the-password",
5342                }
5343            }),
5344        )
5345        .await;
5346    let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await;
5347    let project_id = active_call_a
5348        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5349        .await
5350        .unwrap();
5351    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5352
5353    // Cause the language server to start.
5354    let _buffer = cx_b
5355        .background()
5356        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
5357        .await
5358        .unwrap();
5359
5360    let fake_language_server = fake_language_servers.next().await.unwrap();
5361    fake_language_server.handle_request::<lsp::WorkspaceSymbolRequest, _, _>(|_, _| async move {
5362        Ok(Some(lsp::WorkspaceSymbolResponse::Flat(vec![
5363            #[allow(deprecated)]
5364            lsp::SymbolInformation {
5365                name: "TWO".into(),
5366                location: lsp::Location {
5367                    uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
5368                    range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5369                },
5370                kind: lsp::SymbolKind::CONSTANT,
5371                tags: None,
5372                container_name: None,
5373                deprecated: None,
5374            },
5375        ])))
5376    });
5377
5378    // Request the definition of a symbol as the guest.
5379    let symbols = project_b
5380        .update(cx_b, |p, cx| p.symbols("two", cx))
5381        .await
5382        .unwrap();
5383    assert_eq!(symbols.len(), 1);
5384    assert_eq!(symbols[0].name, "TWO");
5385
5386    // Open one of the returned symbols.
5387    let buffer_b_2 = project_b
5388        .update(cx_b, |project, cx| {
5389            project.open_buffer_for_symbol(&symbols[0], cx)
5390        })
5391        .await
5392        .unwrap();
5393    buffer_b_2.read_with(cx_b, |buffer, _| {
5394        assert_eq!(
5395            buffer.file().unwrap().path().as_ref(),
5396            Path::new("../crate-2/two.rs")
5397        );
5398    });
5399
5400    // Attempt to craft a symbol and violate host's privacy by opening an arbitrary file.
5401    let mut fake_symbol = symbols[0].clone();
5402    fake_symbol.path.path = Path::new("/code/secrets").into();
5403    let error = project_b
5404        .update(cx_b, |project, cx| {
5405            project.open_buffer_for_symbol(&fake_symbol, cx)
5406        })
5407        .await
5408        .unwrap_err();
5409    assert!(error.to_string().contains("invalid symbol signature"));
5410}
5411
5412#[gpui::test(iterations = 10)]
5413async fn test_open_buffer_while_getting_definition_pointing_to_it(
5414    deterministic: Arc<Deterministic>,
5415    cx_a: &mut TestAppContext,
5416    cx_b: &mut TestAppContext,
5417    mut rng: StdRng,
5418) {
5419    deterministic.forbid_parking();
5420    let mut server = TestServer::start(&deterministic).await;
5421    let client_a = server.create_client(cx_a, "user_a").await;
5422    let client_b = server.create_client(cx_b, "user_b").await;
5423    server
5424        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5425        .await;
5426    let active_call_a = cx_a.read(ActiveCall::global);
5427
5428    // Set up a fake language server.
5429    let mut language = Language::new(
5430        LanguageConfig {
5431            name: "Rust".into(),
5432            path_suffixes: vec!["rs".to_string()],
5433            ..Default::default()
5434        },
5435        Some(tree_sitter_rust::language()),
5436    );
5437    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5438    client_a.language_registry().add(Arc::new(language));
5439
5440    client_a
5441        .fs()
5442        .insert_tree(
5443            "/root",
5444            json!({
5445                "a.rs": "const ONE: usize = b::TWO;",
5446                "b.rs": "const TWO: usize = 2",
5447            }),
5448        )
5449        .await;
5450    let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await;
5451    let project_id = active_call_a
5452        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5453        .await
5454        .unwrap();
5455    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5456
5457    let buffer_b1 = cx_b
5458        .background()
5459        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
5460        .await
5461        .unwrap();
5462
5463    let fake_language_server = fake_language_servers.next().await.unwrap();
5464    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
5465        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
5466            lsp::Location::new(
5467                lsp::Url::from_file_path("/root/b.rs").unwrap(),
5468                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5469            ),
5470        )))
5471    });
5472
5473    let definitions;
5474    let buffer_b2;
5475    if rng.gen() {
5476        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5477        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5478    } else {
5479        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5480        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5481    }
5482
5483    let buffer_b2 = buffer_b2.await.unwrap();
5484    let definitions = definitions.await.unwrap();
5485    assert_eq!(definitions.len(), 1);
5486    assert_eq!(definitions[0].target.buffer, buffer_b2);
5487}
5488
5489#[gpui::test(iterations = 10)]
5490async fn test_collaborating_with_code_actions(
5491    deterministic: Arc<Deterministic>,
5492    cx_a: &mut TestAppContext,
5493    cx_b: &mut TestAppContext,
5494) {
5495    deterministic.forbid_parking();
5496    let mut server = TestServer::start(&deterministic).await;
5497    let client_a = server.create_client(cx_a, "user_a").await;
5498    //
5499    let client_b = server.create_client(cx_b, "user_b").await;
5500    server
5501        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5502        .await;
5503    let active_call_a = cx_a.read(ActiveCall::global);
5504
5505    cx_b.update(editor::init);
5506
5507    // Set up a fake language server.
5508    let mut language = Language::new(
5509        LanguageConfig {
5510            name: "Rust".into(),
5511            path_suffixes: vec!["rs".to_string()],
5512            ..Default::default()
5513        },
5514        Some(tree_sitter_rust::language()),
5515    );
5516    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5517    client_a.language_registry().add(Arc::new(language));
5518
5519    client_a
5520        .fs()
5521        .insert_tree(
5522            "/a",
5523            json!({
5524                "main.rs": "mod other;\nfn main() { let foo = other::foo(); }",
5525                "other.rs": "pub fn foo() -> usize { 4 }",
5526            }),
5527        )
5528        .await;
5529    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
5530    let project_id = active_call_a
5531        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5532        .await
5533        .unwrap();
5534
5535    // Join the project as client B.
5536    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5537    let window_b =
5538        cx_b.add_window(|cx| Workspace::new(0, project_b.clone(), client_b.app_state.clone(), cx));
5539    let workspace_b = window_b.root(cx_b);
5540    let editor_b = workspace_b
5541        .update(cx_b, |workspace, cx| {
5542            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
5543        })
5544        .await
5545        .unwrap()
5546        .downcast::<Editor>()
5547        .unwrap();
5548
5549    let mut fake_language_server = fake_language_servers.next().await.unwrap();
5550    let mut requests = fake_language_server
5551        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
5552            assert_eq!(
5553                params.text_document.uri,
5554                lsp::Url::from_file_path("/a/main.rs").unwrap(),
5555            );
5556            assert_eq!(params.range.start, lsp::Position::new(0, 0));
5557            assert_eq!(params.range.end, lsp::Position::new(0, 0));
5558            Ok(None)
5559        });
5560    deterministic.advance_clock(editor::CODE_ACTIONS_DEBOUNCE_TIMEOUT * 2);
5561    requests.next().await;
5562
5563    // Move cursor to a location that contains code actions.
5564    editor_b.update(cx_b, |editor, cx| {
5565        editor.change_selections(None, cx, |s| {
5566            s.select_ranges([Point::new(1, 31)..Point::new(1, 31)])
5567        });
5568        cx.focus(&editor_b);
5569    });
5570
5571    let mut requests = fake_language_server
5572        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
5573            assert_eq!(
5574                params.text_document.uri,
5575                lsp::Url::from_file_path("/a/main.rs").unwrap(),
5576            );
5577            assert_eq!(params.range.start, lsp::Position::new(1, 31));
5578            assert_eq!(params.range.end, lsp::Position::new(1, 31));
5579
5580            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
5581                lsp::CodeAction {
5582                    title: "Inline into all callers".to_string(),
5583                    edit: Some(lsp::WorkspaceEdit {
5584                        changes: Some(
5585                            [
5586                                (
5587                                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
5588                                    vec![lsp::TextEdit::new(
5589                                        lsp::Range::new(
5590                                            lsp::Position::new(1, 22),
5591                                            lsp::Position::new(1, 34),
5592                                        ),
5593                                        "4".to_string(),
5594                                    )],
5595                                ),
5596                                (
5597                                    lsp::Url::from_file_path("/a/other.rs").unwrap(),
5598                                    vec![lsp::TextEdit::new(
5599                                        lsp::Range::new(
5600                                            lsp::Position::new(0, 0),
5601                                            lsp::Position::new(0, 27),
5602                                        ),
5603                                        "".to_string(),
5604                                    )],
5605                                ),
5606                            ]
5607                            .into_iter()
5608                            .collect(),
5609                        ),
5610                        ..Default::default()
5611                    }),
5612                    data: Some(json!({
5613                        "codeActionParams": {
5614                            "range": {
5615                                "start": {"line": 1, "column": 31},
5616                                "end": {"line": 1, "column": 31},
5617                            }
5618                        }
5619                    })),
5620                    ..Default::default()
5621                },
5622            )]))
5623        });
5624    deterministic.advance_clock(editor::CODE_ACTIONS_DEBOUNCE_TIMEOUT * 2);
5625    requests.next().await;
5626
5627    // Toggle code actions and wait for them to display.
5628    editor_b.update(cx_b, |editor, cx| {
5629        editor.toggle_code_actions(
5630            &ToggleCodeActions {
5631                deployed_from_indicator: false,
5632            },
5633            cx,
5634        );
5635    });
5636    cx_a.foreground().run_until_parked();
5637    editor_b.read_with(cx_b, |editor, _| assert!(editor.context_menu_visible()));
5638
5639    fake_language_server.remove_request_handler::<lsp::request::CodeActionRequest>();
5640
5641    // Confirming the code action will trigger a resolve request.
5642    let confirm_action = workspace_b
5643        .update(cx_b, |workspace, cx| {
5644            Editor::confirm_code_action(workspace, &ConfirmCodeAction { item_ix: Some(0) }, cx)
5645        })
5646        .unwrap();
5647    fake_language_server.handle_request::<lsp::request::CodeActionResolveRequest, _, _>(
5648        |_, _| async move {
5649            Ok(lsp::CodeAction {
5650                title: "Inline into all callers".to_string(),
5651                edit: Some(lsp::WorkspaceEdit {
5652                    changes: Some(
5653                        [
5654                            (
5655                                lsp::Url::from_file_path("/a/main.rs").unwrap(),
5656                                vec![lsp::TextEdit::new(
5657                                    lsp::Range::new(
5658                                        lsp::Position::new(1, 22),
5659                                        lsp::Position::new(1, 34),
5660                                    ),
5661                                    "4".to_string(),
5662                                )],
5663                            ),
5664                            (
5665                                lsp::Url::from_file_path("/a/other.rs").unwrap(),
5666                                vec![lsp::TextEdit::new(
5667                                    lsp::Range::new(
5668                                        lsp::Position::new(0, 0),
5669                                        lsp::Position::new(0, 27),
5670                                    ),
5671                                    "".to_string(),
5672                                )],
5673                            ),
5674                        ]
5675                        .into_iter()
5676                        .collect(),
5677                    ),
5678                    ..Default::default()
5679                }),
5680                ..Default::default()
5681            })
5682        },
5683    );
5684
5685    // After the action is confirmed, an editor containing both modified files is opened.
5686    confirm_action.await.unwrap();
5687    let code_action_editor = workspace_b.read_with(cx_b, |workspace, cx| {
5688        workspace
5689            .active_item(cx)
5690            .unwrap()
5691            .downcast::<Editor>()
5692            .unwrap()
5693    });
5694    code_action_editor.update(cx_b, |editor, cx| {
5695        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
5696        editor.undo(&Undo, cx);
5697        assert_eq!(
5698            editor.text(cx),
5699            "mod other;\nfn main() { let foo = other::foo(); }\npub fn foo() -> usize { 4 }"
5700        );
5701        editor.redo(&Redo, cx);
5702        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
5703    });
5704}
5705
5706#[gpui::test(iterations = 10)]
5707async fn test_collaborating_with_renames(
5708    deterministic: Arc<Deterministic>,
5709    cx_a: &mut TestAppContext,
5710    cx_b: &mut TestAppContext,
5711) {
5712    deterministic.forbid_parking();
5713    let mut server = TestServer::start(&deterministic).await;
5714    let client_a = server.create_client(cx_a, "user_a").await;
5715    let client_b = server.create_client(cx_b, "user_b").await;
5716    server
5717        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5718        .await;
5719    let active_call_a = cx_a.read(ActiveCall::global);
5720
5721    cx_b.update(editor::init);
5722
5723    // Set up a fake language server.
5724    let mut language = Language::new(
5725        LanguageConfig {
5726            name: "Rust".into(),
5727            path_suffixes: vec!["rs".to_string()],
5728            ..Default::default()
5729        },
5730        Some(tree_sitter_rust::language()),
5731    );
5732    let mut fake_language_servers = language
5733        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
5734            capabilities: lsp::ServerCapabilities {
5735                rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
5736                    prepare_provider: Some(true),
5737                    work_done_progress_options: Default::default(),
5738                })),
5739                ..Default::default()
5740            },
5741            ..Default::default()
5742        }))
5743        .await;
5744    client_a.language_registry().add(Arc::new(language));
5745
5746    client_a
5747        .fs()
5748        .insert_tree(
5749            "/dir",
5750            json!({
5751                "one.rs": "const ONE: usize = 1;",
5752                "two.rs": "const TWO: usize = one::ONE + one::ONE;"
5753            }),
5754        )
5755        .await;
5756    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
5757    let project_id = active_call_a
5758        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5759        .await
5760        .unwrap();
5761    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5762
5763    let window_b =
5764        cx_b.add_window(|cx| Workspace::new(0, project_b.clone(), client_b.app_state.clone(), cx));
5765    let workspace_b = window_b.root(cx_b);
5766    let editor_b = workspace_b
5767        .update(cx_b, |workspace, cx| {
5768            workspace.open_path((worktree_id, "one.rs"), None, true, cx)
5769        })
5770        .await
5771        .unwrap()
5772        .downcast::<Editor>()
5773        .unwrap();
5774    let fake_language_server = fake_language_servers.next().await.unwrap();
5775
5776    // Move cursor to a location that can be renamed.
5777    let prepare_rename = editor_b.update(cx_b, |editor, cx| {
5778        editor.change_selections(None, cx, |s| s.select_ranges([7..7]));
5779        editor.rename(&Rename, cx).unwrap()
5780    });
5781
5782    fake_language_server
5783        .handle_request::<lsp::request::PrepareRenameRequest, _, _>(|params, _| async move {
5784            assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs");
5785            assert_eq!(params.position, lsp::Position::new(0, 7));
5786            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
5787                lsp::Position::new(0, 6),
5788                lsp::Position::new(0, 9),
5789            ))))
5790        })
5791        .next()
5792        .await
5793        .unwrap();
5794    prepare_rename.await.unwrap();
5795    editor_b.update(cx_b, |editor, cx| {
5796        use editor::ToOffset;
5797        let rename = editor.pending_rename().unwrap();
5798        let buffer = editor.buffer().read(cx).snapshot(cx);
5799        assert_eq!(
5800            rename.range.start.to_offset(&buffer)..rename.range.end.to_offset(&buffer),
5801            6..9
5802        );
5803        rename.editor.update(cx, |rename_editor, cx| {
5804            rename_editor.buffer().update(cx, |rename_buffer, cx| {
5805                rename_buffer.edit([(0..3, "THREE")], None, cx);
5806            });
5807        });
5808    });
5809
5810    let confirm_rename = workspace_b.update(cx_b, |workspace, cx| {
5811        Editor::confirm_rename(workspace, &ConfirmRename, cx).unwrap()
5812    });
5813    fake_language_server
5814        .handle_request::<lsp::request::Rename, _, _>(|params, _| async move {
5815            assert_eq!(
5816                params.text_document_position.text_document.uri.as_str(),
5817                "file:///dir/one.rs"
5818            );
5819            assert_eq!(
5820                params.text_document_position.position,
5821                lsp::Position::new(0, 6)
5822            );
5823            assert_eq!(params.new_name, "THREE");
5824            Ok(Some(lsp::WorkspaceEdit {
5825                changes: Some(
5826                    [
5827                        (
5828                            lsp::Url::from_file_path("/dir/one.rs").unwrap(),
5829                            vec![lsp::TextEdit::new(
5830                                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5831                                "THREE".to_string(),
5832                            )],
5833                        ),
5834                        (
5835                            lsp::Url::from_file_path("/dir/two.rs").unwrap(),
5836                            vec![
5837                                lsp::TextEdit::new(
5838                                    lsp::Range::new(
5839                                        lsp::Position::new(0, 24),
5840                                        lsp::Position::new(0, 27),
5841                                    ),
5842                                    "THREE".to_string(),
5843                                ),
5844                                lsp::TextEdit::new(
5845                                    lsp::Range::new(
5846                                        lsp::Position::new(0, 35),
5847                                        lsp::Position::new(0, 38),
5848                                    ),
5849                                    "THREE".to_string(),
5850                                ),
5851                            ],
5852                        ),
5853                    ]
5854                    .into_iter()
5855                    .collect(),
5856                ),
5857                ..Default::default()
5858            }))
5859        })
5860        .next()
5861        .await
5862        .unwrap();
5863    confirm_rename.await.unwrap();
5864
5865    let rename_editor = workspace_b.read_with(cx_b, |workspace, cx| {
5866        workspace
5867            .active_item(cx)
5868            .unwrap()
5869            .downcast::<Editor>()
5870            .unwrap()
5871    });
5872    rename_editor.update(cx_b, |editor, cx| {
5873        assert_eq!(
5874            editor.text(cx),
5875            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
5876        );
5877        editor.undo(&Undo, cx);
5878        assert_eq!(
5879            editor.text(cx),
5880            "const ONE: usize = 1;\nconst TWO: usize = one::ONE + one::ONE;"
5881        );
5882        editor.redo(&Redo, cx);
5883        assert_eq!(
5884            editor.text(cx),
5885            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
5886        );
5887    });
5888
5889    // Ensure temporary rename edits cannot be undone/redone.
5890    editor_b.update(cx_b, |editor, cx| {
5891        editor.undo(&Undo, cx);
5892        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
5893        editor.undo(&Undo, cx);
5894        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
5895        editor.redo(&Redo, cx);
5896        assert_eq!(editor.text(cx), "const THREE: usize = 1;");
5897    })
5898}
5899
5900#[gpui::test(iterations = 10)]
5901async fn test_language_server_statuses(
5902    deterministic: Arc<Deterministic>,
5903    cx_a: &mut TestAppContext,
5904    cx_b: &mut TestAppContext,
5905) {
5906    deterministic.forbid_parking();
5907    let mut server = TestServer::start(&deterministic).await;
5908    let client_a = server.create_client(cx_a, "user_a").await;
5909    let client_b = server.create_client(cx_b, "user_b").await;
5910    server
5911        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5912        .await;
5913    let active_call_a = cx_a.read(ActiveCall::global);
5914
5915    cx_b.update(editor::init);
5916
5917    // Set up a fake language server.
5918    let mut language = Language::new(
5919        LanguageConfig {
5920            name: "Rust".into(),
5921            path_suffixes: vec!["rs".to_string()],
5922            ..Default::default()
5923        },
5924        Some(tree_sitter_rust::language()),
5925    );
5926    let mut fake_language_servers = language
5927        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
5928            name: "the-language-server",
5929            ..Default::default()
5930        }))
5931        .await;
5932    client_a.language_registry().add(Arc::new(language));
5933
5934    client_a
5935        .fs()
5936        .insert_tree(
5937            "/dir",
5938            json!({
5939                "main.rs": "const ONE: usize = 1;",
5940            }),
5941        )
5942        .await;
5943    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
5944
5945    let _buffer_a = project_a
5946        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
5947        .await
5948        .unwrap();
5949
5950    let fake_language_server = fake_language_servers.next().await.unwrap();
5951    fake_language_server.start_progress("the-token").await;
5952    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
5953        token: lsp::NumberOrString::String("the-token".to_string()),
5954        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
5955            lsp::WorkDoneProgressReport {
5956                message: Some("the-message".to_string()),
5957                ..Default::default()
5958            },
5959        )),
5960    });
5961    deterministic.run_until_parked();
5962    project_a.read_with(cx_a, |project, _| {
5963        let status = project.language_server_statuses().next().unwrap();
5964        assert_eq!(status.name, "the-language-server");
5965        assert_eq!(status.pending_work.len(), 1);
5966        assert_eq!(
5967            status.pending_work["the-token"].message.as_ref().unwrap(),
5968            "the-message"
5969        );
5970    });
5971
5972    let project_id = active_call_a
5973        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5974        .await
5975        .unwrap();
5976    deterministic.run_until_parked();
5977    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5978    project_b.read_with(cx_b, |project, _| {
5979        let status = project.language_server_statuses().next().unwrap();
5980        assert_eq!(status.name, "the-language-server");
5981    });
5982
5983    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
5984        token: lsp::NumberOrString::String("the-token".to_string()),
5985        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
5986            lsp::WorkDoneProgressReport {
5987                message: Some("the-message-2".to_string()),
5988                ..Default::default()
5989            },
5990        )),
5991    });
5992    deterministic.run_until_parked();
5993    project_a.read_with(cx_a, |project, _| {
5994        let status = project.language_server_statuses().next().unwrap();
5995        assert_eq!(status.name, "the-language-server");
5996        assert_eq!(status.pending_work.len(), 1);
5997        assert_eq!(
5998            status.pending_work["the-token"].message.as_ref().unwrap(),
5999            "the-message-2"
6000        );
6001    });
6002    project_b.read_with(cx_b, |project, _| {
6003        let status = project.language_server_statuses().next().unwrap();
6004        assert_eq!(status.name, "the-language-server");
6005        assert_eq!(status.pending_work.len(), 1);
6006        assert_eq!(
6007            status.pending_work["the-token"].message.as_ref().unwrap(),
6008            "the-message-2"
6009        );
6010    });
6011}
6012
6013#[gpui::test(iterations = 10)]
6014async fn test_contacts(
6015    deterministic: Arc<Deterministic>,
6016    cx_a: &mut TestAppContext,
6017    cx_b: &mut TestAppContext,
6018    cx_c: &mut TestAppContext,
6019    cx_d: &mut TestAppContext,
6020) {
6021    deterministic.forbid_parking();
6022    let mut server = TestServer::start(&deterministic).await;
6023    let client_a = server.create_client(cx_a, "user_a").await;
6024    let client_b = server.create_client(cx_b, "user_b").await;
6025    let client_c = server.create_client(cx_c, "user_c").await;
6026    let client_d = server.create_client(cx_d, "user_d").await;
6027    server
6028        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
6029        .await;
6030    let active_call_a = cx_a.read(ActiveCall::global);
6031    let active_call_b = cx_b.read(ActiveCall::global);
6032    let active_call_c = cx_c.read(ActiveCall::global);
6033    let _active_call_d = cx_d.read(ActiveCall::global);
6034
6035    deterministic.run_until_parked();
6036    assert_eq!(
6037        contacts(&client_a, cx_a),
6038        [
6039            ("user_b".to_string(), "online", "free"),
6040            ("user_c".to_string(), "online", "free")
6041        ]
6042    );
6043    assert_eq!(
6044        contacts(&client_b, cx_b),
6045        [
6046            ("user_a".to_string(), "online", "free"),
6047            ("user_c".to_string(), "online", "free")
6048        ]
6049    );
6050    assert_eq!(
6051        contacts(&client_c, cx_c),
6052        [
6053            ("user_a".to_string(), "online", "free"),
6054            ("user_b".to_string(), "online", "free")
6055        ]
6056    );
6057    assert_eq!(contacts(&client_d, cx_d), []);
6058
6059    server.disconnect_client(client_c.peer_id().unwrap());
6060    server.forbid_connections();
6061    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
6062    assert_eq!(
6063        contacts(&client_a, cx_a),
6064        [
6065            ("user_b".to_string(), "online", "free"),
6066            ("user_c".to_string(), "offline", "free")
6067        ]
6068    );
6069    assert_eq!(
6070        contacts(&client_b, cx_b),
6071        [
6072            ("user_a".to_string(), "online", "free"),
6073            ("user_c".to_string(), "offline", "free")
6074        ]
6075    );
6076    assert_eq!(contacts(&client_c, cx_c), []);
6077    assert_eq!(contacts(&client_d, cx_d), []);
6078
6079    server.allow_connections();
6080    client_c
6081        .authenticate_and_connect(false, &cx_c.to_async())
6082        .await
6083        .unwrap();
6084
6085    deterministic.run_until_parked();
6086    assert_eq!(
6087        contacts(&client_a, cx_a),
6088        [
6089            ("user_b".to_string(), "online", "free"),
6090            ("user_c".to_string(), "online", "free")
6091        ]
6092    );
6093    assert_eq!(
6094        contacts(&client_b, cx_b),
6095        [
6096            ("user_a".to_string(), "online", "free"),
6097            ("user_c".to_string(), "online", "free")
6098        ]
6099    );
6100    assert_eq!(
6101        contacts(&client_c, cx_c),
6102        [
6103            ("user_a".to_string(), "online", "free"),
6104            ("user_b".to_string(), "online", "free")
6105        ]
6106    );
6107    assert_eq!(contacts(&client_d, cx_d), []);
6108
6109    active_call_a
6110        .update(cx_a, |call, cx| {
6111            call.invite(client_b.user_id().unwrap(), None, cx)
6112        })
6113        .await
6114        .unwrap();
6115    deterministic.run_until_parked();
6116    assert_eq!(
6117        contacts(&client_a, cx_a),
6118        [
6119            ("user_b".to_string(), "online", "busy"),
6120            ("user_c".to_string(), "online", "free")
6121        ]
6122    );
6123    assert_eq!(
6124        contacts(&client_b, cx_b),
6125        [
6126            ("user_a".to_string(), "online", "busy"),
6127            ("user_c".to_string(), "online", "free")
6128        ]
6129    );
6130    assert_eq!(
6131        contacts(&client_c, cx_c),
6132        [
6133            ("user_a".to_string(), "online", "busy"),
6134            ("user_b".to_string(), "online", "busy")
6135        ]
6136    );
6137    assert_eq!(contacts(&client_d, cx_d), []);
6138
6139    // Client B and client D become contacts while client B is being called.
6140    server
6141        .make_contacts(&mut [(&client_b, cx_b), (&client_d, cx_d)])
6142        .await;
6143    deterministic.run_until_parked();
6144    assert_eq!(
6145        contacts(&client_a, cx_a),
6146        [
6147            ("user_b".to_string(), "online", "busy"),
6148            ("user_c".to_string(), "online", "free")
6149        ]
6150    );
6151    assert_eq!(
6152        contacts(&client_b, cx_b),
6153        [
6154            ("user_a".to_string(), "online", "busy"),
6155            ("user_c".to_string(), "online", "free"),
6156            ("user_d".to_string(), "online", "free"),
6157        ]
6158    );
6159    assert_eq!(
6160        contacts(&client_c, cx_c),
6161        [
6162            ("user_a".to_string(), "online", "busy"),
6163            ("user_b".to_string(), "online", "busy")
6164        ]
6165    );
6166    assert_eq!(
6167        contacts(&client_d, cx_d),
6168        [("user_b".to_string(), "online", "busy")]
6169    );
6170
6171    active_call_b.update(cx_b, |call, cx| call.decline_incoming(cx).unwrap());
6172    deterministic.run_until_parked();
6173    assert_eq!(
6174        contacts(&client_a, cx_a),
6175        [
6176            ("user_b".to_string(), "online", "free"),
6177            ("user_c".to_string(), "online", "free")
6178        ]
6179    );
6180    assert_eq!(
6181        contacts(&client_b, cx_b),
6182        [
6183            ("user_a".to_string(), "online", "free"),
6184            ("user_c".to_string(), "online", "free"),
6185            ("user_d".to_string(), "online", "free")
6186        ]
6187    );
6188    assert_eq!(
6189        contacts(&client_c, cx_c),
6190        [
6191            ("user_a".to_string(), "online", "free"),
6192            ("user_b".to_string(), "online", "free")
6193        ]
6194    );
6195    assert_eq!(
6196        contacts(&client_d, cx_d),
6197        [("user_b".to_string(), "online", "free")]
6198    );
6199
6200    active_call_c
6201        .update(cx_c, |call, cx| {
6202            call.invite(client_a.user_id().unwrap(), None, cx)
6203        })
6204        .await
6205        .unwrap();
6206    deterministic.run_until_parked();
6207    assert_eq!(
6208        contacts(&client_a, cx_a),
6209        [
6210            ("user_b".to_string(), "online", "free"),
6211            ("user_c".to_string(), "online", "busy")
6212        ]
6213    );
6214    assert_eq!(
6215        contacts(&client_b, cx_b),
6216        [
6217            ("user_a".to_string(), "online", "busy"),
6218            ("user_c".to_string(), "online", "busy"),
6219            ("user_d".to_string(), "online", "free")
6220        ]
6221    );
6222    assert_eq!(
6223        contacts(&client_c, cx_c),
6224        [
6225            ("user_a".to_string(), "online", "busy"),
6226            ("user_b".to_string(), "online", "free")
6227        ]
6228    );
6229    assert_eq!(
6230        contacts(&client_d, cx_d),
6231        [("user_b".to_string(), "online", "free")]
6232    );
6233
6234    active_call_a
6235        .update(cx_a, |call, cx| call.accept_incoming(cx))
6236        .await
6237        .unwrap();
6238    deterministic.run_until_parked();
6239    assert_eq!(
6240        contacts(&client_a, cx_a),
6241        [
6242            ("user_b".to_string(), "online", "free"),
6243            ("user_c".to_string(), "online", "busy")
6244        ]
6245    );
6246    assert_eq!(
6247        contacts(&client_b, cx_b),
6248        [
6249            ("user_a".to_string(), "online", "busy"),
6250            ("user_c".to_string(), "online", "busy"),
6251            ("user_d".to_string(), "online", "free")
6252        ]
6253    );
6254    assert_eq!(
6255        contacts(&client_c, cx_c),
6256        [
6257            ("user_a".to_string(), "online", "busy"),
6258            ("user_b".to_string(), "online", "free")
6259        ]
6260    );
6261    assert_eq!(
6262        contacts(&client_d, cx_d),
6263        [("user_b".to_string(), "online", "free")]
6264    );
6265
6266    active_call_a
6267        .update(cx_a, |call, cx| {
6268            call.invite(client_b.user_id().unwrap(), None, cx)
6269        })
6270        .await
6271        .unwrap();
6272    deterministic.run_until_parked();
6273    assert_eq!(
6274        contacts(&client_a, cx_a),
6275        [
6276            ("user_b".to_string(), "online", "busy"),
6277            ("user_c".to_string(), "online", "busy")
6278        ]
6279    );
6280    assert_eq!(
6281        contacts(&client_b, cx_b),
6282        [
6283            ("user_a".to_string(), "online", "busy"),
6284            ("user_c".to_string(), "online", "busy"),
6285            ("user_d".to_string(), "online", "free")
6286        ]
6287    );
6288    assert_eq!(
6289        contacts(&client_c, cx_c),
6290        [
6291            ("user_a".to_string(), "online", "busy"),
6292            ("user_b".to_string(), "online", "busy")
6293        ]
6294    );
6295    assert_eq!(
6296        contacts(&client_d, cx_d),
6297        [("user_b".to_string(), "online", "busy")]
6298    );
6299
6300    active_call_a
6301        .update(cx_a, |call, cx| call.hang_up(cx))
6302        .await
6303        .unwrap();
6304    deterministic.run_until_parked();
6305    assert_eq!(
6306        contacts(&client_a, cx_a),
6307        [
6308            ("user_b".to_string(), "online", "free"),
6309            ("user_c".to_string(), "online", "free")
6310        ]
6311    );
6312    assert_eq!(
6313        contacts(&client_b, cx_b),
6314        [
6315            ("user_a".to_string(), "online", "free"),
6316            ("user_c".to_string(), "online", "free"),
6317            ("user_d".to_string(), "online", "free")
6318        ]
6319    );
6320    assert_eq!(
6321        contacts(&client_c, cx_c),
6322        [
6323            ("user_a".to_string(), "online", "free"),
6324            ("user_b".to_string(), "online", "free")
6325        ]
6326    );
6327    assert_eq!(
6328        contacts(&client_d, cx_d),
6329        [("user_b".to_string(), "online", "free")]
6330    );
6331
6332    active_call_a
6333        .update(cx_a, |call, cx| {
6334            call.invite(client_b.user_id().unwrap(), None, cx)
6335        })
6336        .await
6337        .unwrap();
6338    deterministic.run_until_parked();
6339    assert_eq!(
6340        contacts(&client_a, cx_a),
6341        [
6342            ("user_b".to_string(), "online", "busy"),
6343            ("user_c".to_string(), "online", "free")
6344        ]
6345    );
6346    assert_eq!(
6347        contacts(&client_b, cx_b),
6348        [
6349            ("user_a".to_string(), "online", "busy"),
6350            ("user_c".to_string(), "online", "free"),
6351            ("user_d".to_string(), "online", "free")
6352        ]
6353    );
6354    assert_eq!(
6355        contacts(&client_c, cx_c),
6356        [
6357            ("user_a".to_string(), "online", "busy"),
6358            ("user_b".to_string(), "online", "busy")
6359        ]
6360    );
6361    assert_eq!(
6362        contacts(&client_d, cx_d),
6363        [("user_b".to_string(), "online", "busy")]
6364    );
6365
6366    server.forbid_connections();
6367    server.disconnect_client(client_a.peer_id().unwrap());
6368    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
6369    assert_eq!(contacts(&client_a, cx_a), []);
6370    assert_eq!(
6371        contacts(&client_b, cx_b),
6372        [
6373            ("user_a".to_string(), "offline", "free"),
6374            ("user_c".to_string(), "online", "free"),
6375            ("user_d".to_string(), "online", "free")
6376        ]
6377    );
6378    assert_eq!(
6379        contacts(&client_c, cx_c),
6380        [
6381            ("user_a".to_string(), "offline", "free"),
6382            ("user_b".to_string(), "online", "free")
6383        ]
6384    );
6385    assert_eq!(
6386        contacts(&client_d, cx_d),
6387        [("user_b".to_string(), "online", "free")]
6388    );
6389
6390    // Test removing a contact
6391    client_b
6392        .user_store()
6393        .update(cx_b, |store, cx| {
6394            store.remove_contact(client_c.user_id().unwrap(), cx)
6395        })
6396        .await
6397        .unwrap();
6398    deterministic.run_until_parked();
6399    assert_eq!(
6400        contacts(&client_b, cx_b),
6401        [
6402            ("user_a".to_string(), "offline", "free"),
6403            ("user_d".to_string(), "online", "free")
6404        ]
6405    );
6406    assert_eq!(
6407        contacts(&client_c, cx_c),
6408        [("user_a".to_string(), "offline", "free"),]
6409    );
6410
6411    fn contacts(
6412        client: &TestClient,
6413        cx: &TestAppContext,
6414    ) -> Vec<(String, &'static str, &'static str)> {
6415        client.user_store().read_with(cx, |store, _| {
6416            store
6417                .contacts()
6418                .iter()
6419                .map(|contact| {
6420                    (
6421                        contact.user.github_login.clone(),
6422                        if contact.online { "online" } else { "offline" },
6423                        if contact.busy { "busy" } else { "free" },
6424                    )
6425                })
6426                .collect()
6427        })
6428    }
6429}
6430
6431#[gpui::test(iterations = 10)]
6432async fn test_contact_requests(
6433    deterministic: Arc<Deterministic>,
6434    cx_a: &mut TestAppContext,
6435    cx_a2: &mut TestAppContext,
6436    cx_b: &mut TestAppContext,
6437    cx_b2: &mut TestAppContext,
6438    cx_c: &mut TestAppContext,
6439    cx_c2: &mut TestAppContext,
6440) {
6441    deterministic.forbid_parking();
6442
6443    // Connect to a server as 3 clients.
6444    let mut server = TestServer::start(&deterministic).await;
6445    let client_a = server.create_client(cx_a, "user_a").await;
6446    let client_a2 = server.create_client(cx_a2, "user_a").await;
6447    let client_b = server.create_client(cx_b, "user_b").await;
6448    let client_b2 = server.create_client(cx_b2, "user_b").await;
6449    let client_c = server.create_client(cx_c, "user_c").await;
6450    let client_c2 = server.create_client(cx_c2, "user_c").await;
6451
6452    assert_eq!(client_a.user_id().unwrap(), client_a2.user_id().unwrap());
6453    assert_eq!(client_b.user_id().unwrap(), client_b2.user_id().unwrap());
6454    assert_eq!(client_c.user_id().unwrap(), client_c2.user_id().unwrap());
6455
6456    // User A and User C request that user B become their contact.
6457    client_a
6458        .user_store()
6459        .update(cx_a, |store, cx| {
6460            store.request_contact(client_b.user_id().unwrap(), cx)
6461        })
6462        .await
6463        .unwrap();
6464    client_c
6465        .user_store()
6466        .update(cx_c, |store, cx| {
6467            store.request_contact(client_b.user_id().unwrap(), cx)
6468        })
6469        .await
6470        .unwrap();
6471    deterministic.run_until_parked();
6472
6473    // All users see the pending request appear in all their clients.
6474    assert_eq!(
6475        client_a.summarize_contacts(cx_a).outgoing_requests,
6476        &["user_b"]
6477    );
6478    assert_eq!(
6479        client_a2.summarize_contacts(cx_a2).outgoing_requests,
6480        &["user_b"]
6481    );
6482    assert_eq!(
6483        client_b.summarize_contacts(cx_b).incoming_requests,
6484        &["user_a", "user_c"]
6485    );
6486    assert_eq!(
6487        client_b2.summarize_contacts(cx_b2).incoming_requests,
6488        &["user_a", "user_c"]
6489    );
6490    assert_eq!(
6491        client_c.summarize_contacts(cx_c).outgoing_requests,
6492        &["user_b"]
6493    );
6494    assert_eq!(
6495        client_c2.summarize_contacts(cx_c2).outgoing_requests,
6496        &["user_b"]
6497    );
6498
6499    // Contact requests are present upon connecting (tested here via disconnect/reconnect)
6500    disconnect_and_reconnect(&client_a, cx_a).await;
6501    disconnect_and_reconnect(&client_b, cx_b).await;
6502    disconnect_and_reconnect(&client_c, cx_c).await;
6503    deterministic.run_until_parked();
6504    assert_eq!(
6505        client_a.summarize_contacts(cx_a).outgoing_requests,
6506        &["user_b"]
6507    );
6508    assert_eq!(
6509        client_b.summarize_contacts(cx_b).incoming_requests,
6510        &["user_a", "user_c"]
6511    );
6512    assert_eq!(
6513        client_c.summarize_contacts(cx_c).outgoing_requests,
6514        &["user_b"]
6515    );
6516
6517    // User B accepts the request from user A.
6518    client_b
6519        .user_store()
6520        .update(cx_b, |store, cx| {
6521            store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
6522        })
6523        .await
6524        .unwrap();
6525
6526    deterministic.run_until_parked();
6527
6528    // User B sees user A as their contact now in all client, and the incoming request from them is removed.
6529    let contacts_b = client_b.summarize_contacts(cx_b);
6530    assert_eq!(contacts_b.current, &["user_a"]);
6531    assert_eq!(contacts_b.incoming_requests, &["user_c"]);
6532    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
6533    assert_eq!(contacts_b2.current, &["user_a"]);
6534    assert_eq!(contacts_b2.incoming_requests, &["user_c"]);
6535
6536    // User A sees user B as their contact now in all clients, and the outgoing request to them is removed.
6537    let contacts_a = client_a.summarize_contacts(cx_a);
6538    assert_eq!(contacts_a.current, &["user_b"]);
6539    assert!(contacts_a.outgoing_requests.is_empty());
6540    let contacts_a2 = client_a2.summarize_contacts(cx_a2);
6541    assert_eq!(contacts_a2.current, &["user_b"]);
6542    assert!(contacts_a2.outgoing_requests.is_empty());
6543
6544    // Contacts are present upon connecting (tested here via disconnect/reconnect)
6545    disconnect_and_reconnect(&client_a, cx_a).await;
6546    disconnect_and_reconnect(&client_b, cx_b).await;
6547    disconnect_and_reconnect(&client_c, cx_c).await;
6548    deterministic.run_until_parked();
6549    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
6550    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
6551    assert_eq!(
6552        client_b.summarize_contacts(cx_b).incoming_requests,
6553        &["user_c"]
6554    );
6555    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
6556    assert_eq!(
6557        client_c.summarize_contacts(cx_c).outgoing_requests,
6558        &["user_b"]
6559    );
6560
6561    // User B rejects the request from user C.
6562    client_b
6563        .user_store()
6564        .update(cx_b, |store, cx| {
6565            store.respond_to_contact_request(client_c.user_id().unwrap(), false, cx)
6566        })
6567        .await
6568        .unwrap();
6569
6570    deterministic.run_until_parked();
6571
6572    // User B doesn't see user C as their contact, and the incoming request from them is removed.
6573    let contacts_b = client_b.summarize_contacts(cx_b);
6574    assert_eq!(contacts_b.current, &["user_a"]);
6575    assert!(contacts_b.incoming_requests.is_empty());
6576    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
6577    assert_eq!(contacts_b2.current, &["user_a"]);
6578    assert!(contacts_b2.incoming_requests.is_empty());
6579
6580    // User C doesn't see user B as their contact, and the outgoing request to them is removed.
6581    let contacts_c = client_c.summarize_contacts(cx_c);
6582    assert!(contacts_c.current.is_empty());
6583    assert!(contacts_c.outgoing_requests.is_empty());
6584    let contacts_c2 = client_c2.summarize_contacts(cx_c2);
6585    assert!(contacts_c2.current.is_empty());
6586    assert!(contacts_c2.outgoing_requests.is_empty());
6587
6588    // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect)
6589    disconnect_and_reconnect(&client_a, cx_a).await;
6590    disconnect_and_reconnect(&client_b, cx_b).await;
6591    disconnect_and_reconnect(&client_c, cx_c).await;
6592    deterministic.run_until_parked();
6593    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
6594    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
6595    assert!(client_b
6596        .summarize_contacts(cx_b)
6597        .incoming_requests
6598        .is_empty());
6599    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
6600    assert!(client_c
6601        .summarize_contacts(cx_c)
6602        .outgoing_requests
6603        .is_empty());
6604
6605    async fn disconnect_and_reconnect(client: &TestClient, cx: &mut TestAppContext) {
6606        client.disconnect(&cx.to_async());
6607        client.clear_contacts(cx).await;
6608        client
6609            .authenticate_and_connect(false, &cx.to_async())
6610            .await
6611            .unwrap();
6612    }
6613}
6614
6615#[gpui::test(iterations = 10)]
6616async fn test_join_call_after_screen_was_shared(
6617    deterministic: Arc<Deterministic>,
6618    cx_a: &mut TestAppContext,
6619    cx_b: &mut TestAppContext,
6620) {
6621    deterministic.forbid_parking();
6622    let mut server = TestServer::start(&deterministic).await;
6623
6624    let client_a = server.create_client(cx_a, "user_a").await;
6625    let client_b = server.create_client(cx_b, "user_b").await;
6626    server
6627        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6628        .await;
6629
6630    let active_call_a = cx_a.read(ActiveCall::global);
6631    let active_call_b = cx_b.read(ActiveCall::global);
6632
6633    // Call users B and C from client A.
6634    active_call_a
6635        .update(cx_a, |call, cx| {
6636            call.invite(client_b.user_id().unwrap(), None, cx)
6637        })
6638        .await
6639        .unwrap();
6640    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
6641    deterministic.run_until_parked();
6642    assert_eq!(
6643        room_participants(&room_a, cx_a),
6644        RoomParticipants {
6645            remote: Default::default(),
6646            pending: vec!["user_b".to_string()]
6647        }
6648    );
6649
6650    // User B receives the call.
6651    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
6652    let call_b = incoming_call_b.next().await.unwrap().unwrap();
6653    assert_eq!(call_b.calling_user.github_login, "user_a");
6654
6655    // User A shares their screen
6656    let display = MacOSDisplay::new();
6657    active_call_a
6658        .update(cx_a, |call, cx| {
6659            call.room().unwrap().update(cx, |room, cx| {
6660                room.set_display_sources(vec![display.clone()]);
6661                room.share_screen(cx)
6662            })
6663        })
6664        .await
6665        .unwrap();
6666
6667    client_b.user_store().update(cx_b, |user_store, _| {
6668        user_store.clear_cache();
6669    });
6670
6671    // User B joins the room
6672    active_call_b
6673        .update(cx_b, |call, cx| call.accept_incoming(cx))
6674        .await
6675        .unwrap();
6676    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
6677    assert!(incoming_call_b.next().await.unwrap().is_none());
6678
6679    deterministic.run_until_parked();
6680    assert_eq!(
6681        room_participants(&room_a, cx_a),
6682        RoomParticipants {
6683            remote: vec!["user_b".to_string()],
6684            pending: vec![],
6685        }
6686    );
6687    assert_eq!(
6688        room_participants(&room_b, cx_b),
6689        RoomParticipants {
6690            remote: vec!["user_a".to_string()],
6691            pending: vec![],
6692        }
6693    );
6694
6695    // Ensure User B sees User A's screenshare.
6696    room_b.read_with(cx_b, |room, _| {
6697        assert_eq!(
6698            room.remote_participants()
6699                .get(&client_a.user_id().unwrap())
6700                .unwrap()
6701                .video_tracks
6702                .len(),
6703            1
6704        );
6705    });
6706}
6707
6708#[gpui::test(iterations = 10)]
6709async fn test_on_input_format_from_host_to_guest(
6710    deterministic: Arc<Deterministic>,
6711    cx_a: &mut TestAppContext,
6712    cx_b: &mut TestAppContext,
6713) {
6714    deterministic.forbid_parking();
6715    let mut server = TestServer::start(&deterministic).await;
6716    let client_a = server.create_client(cx_a, "user_a").await;
6717    let client_b = server.create_client(cx_b, "user_b").await;
6718    server
6719        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6720        .await;
6721    let active_call_a = cx_a.read(ActiveCall::global);
6722
6723    // Set up a fake language server.
6724    let mut language = Language::new(
6725        LanguageConfig {
6726            name: "Rust".into(),
6727            path_suffixes: vec!["rs".to_string()],
6728            ..Default::default()
6729        },
6730        Some(tree_sitter_rust::language()),
6731    );
6732    let mut fake_language_servers = language
6733        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
6734            capabilities: lsp::ServerCapabilities {
6735                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
6736                    first_trigger_character: ":".to_string(),
6737                    more_trigger_character: Some(vec![">".to_string()]),
6738                }),
6739                ..Default::default()
6740            },
6741            ..Default::default()
6742        }))
6743        .await;
6744    client_a.language_registry().add(Arc::new(language));
6745
6746    client_a
6747        .fs()
6748        .insert_tree(
6749            "/a",
6750            json!({
6751                "main.rs": "fn main() { a }",
6752                "other.rs": "// Test file",
6753            }),
6754        )
6755        .await;
6756    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
6757    let project_id = active_call_a
6758        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6759        .await
6760        .unwrap();
6761    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6762
6763    // Open a file in an editor as the host.
6764    let buffer_a = project_a
6765        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
6766        .await
6767        .unwrap();
6768    let window_a = cx_a.add_window(|_| EmptyView);
6769    let editor_a = window_a.add_view(cx_a, |cx| {
6770        Editor::for_buffer(buffer_a, Some(project_a.clone()), cx)
6771    });
6772
6773    let fake_language_server = fake_language_servers.next().await.unwrap();
6774    cx_b.foreground().run_until_parked();
6775
6776    // Receive an OnTypeFormatting request as the host's language server.
6777    // Return some formattings from the host's language server.
6778    fake_language_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(
6779        |params, _| async move {
6780            assert_eq!(
6781                params.text_document_position.text_document.uri,
6782                lsp::Url::from_file_path("/a/main.rs").unwrap(),
6783            );
6784            assert_eq!(
6785                params.text_document_position.position,
6786                lsp::Position::new(0, 14),
6787            );
6788
6789            Ok(Some(vec![lsp::TextEdit {
6790                new_text: "~<".to_string(),
6791                range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
6792            }]))
6793        },
6794    );
6795
6796    // Open the buffer on the guest and see that the formattings worked
6797    let buffer_b = project_b
6798        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
6799        .await
6800        .unwrap();
6801
6802    // Type a on type formatting trigger character as the guest.
6803    editor_a.update(cx_a, |editor, cx| {
6804        cx.focus(&editor_a);
6805        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
6806        editor.handle_input(">", cx);
6807    });
6808
6809    cx_b.foreground().run_until_parked();
6810
6811    buffer_b.read_with(cx_b, |buffer, _| {
6812        assert_eq!(buffer.text(), "fn main() { a>~< }")
6813    });
6814
6815    // Undo should remove LSP edits first
6816    editor_a.update(cx_a, |editor, cx| {
6817        assert_eq!(editor.text(cx), "fn main() { a>~< }");
6818        editor.undo(&Undo, cx);
6819        assert_eq!(editor.text(cx), "fn main() { a> }");
6820    });
6821    cx_b.foreground().run_until_parked();
6822    buffer_b.read_with(cx_b, |buffer, _| {
6823        assert_eq!(buffer.text(), "fn main() { a> }")
6824    });
6825
6826    editor_a.update(cx_a, |editor, cx| {
6827        assert_eq!(editor.text(cx), "fn main() { a> }");
6828        editor.undo(&Undo, cx);
6829        assert_eq!(editor.text(cx), "fn main() { a }");
6830    });
6831    cx_b.foreground().run_until_parked();
6832    buffer_b.read_with(cx_b, |buffer, _| {
6833        assert_eq!(buffer.text(), "fn main() { a }")
6834    });
6835}
6836
6837#[gpui::test(iterations = 10)]
6838async fn test_on_input_format_from_guest_to_host(
6839    deterministic: Arc<Deterministic>,
6840    cx_a: &mut TestAppContext,
6841    cx_b: &mut TestAppContext,
6842) {
6843    deterministic.forbid_parking();
6844    let mut server = TestServer::start(&deterministic).await;
6845    let client_a = server.create_client(cx_a, "user_a").await;
6846    let client_b = server.create_client(cx_b, "user_b").await;
6847    server
6848        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6849        .await;
6850    let active_call_a = cx_a.read(ActiveCall::global);
6851
6852    // Set up a fake language server.
6853    let mut language = Language::new(
6854        LanguageConfig {
6855            name: "Rust".into(),
6856            path_suffixes: vec!["rs".to_string()],
6857            ..Default::default()
6858        },
6859        Some(tree_sitter_rust::language()),
6860    );
6861    let mut fake_language_servers = language
6862        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
6863            capabilities: lsp::ServerCapabilities {
6864                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
6865                    first_trigger_character: ":".to_string(),
6866                    more_trigger_character: Some(vec![">".to_string()]),
6867                }),
6868                ..Default::default()
6869            },
6870            ..Default::default()
6871        }))
6872        .await;
6873    client_a.language_registry().add(Arc::new(language));
6874
6875    client_a
6876        .fs()
6877        .insert_tree(
6878            "/a",
6879            json!({
6880                "main.rs": "fn main() { a }",
6881                "other.rs": "// Test file",
6882            }),
6883        )
6884        .await;
6885    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
6886    let project_id = active_call_a
6887        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6888        .await
6889        .unwrap();
6890    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6891
6892    // Open a file in an editor as the guest.
6893    let buffer_b = project_b
6894        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
6895        .await
6896        .unwrap();
6897    let window_b = cx_b.add_window(|_| EmptyView);
6898    let editor_b = window_b.add_view(cx_b, |cx| {
6899        Editor::for_buffer(buffer_b, Some(project_b.clone()), cx)
6900    });
6901
6902    let fake_language_server = fake_language_servers.next().await.unwrap();
6903    cx_a.foreground().run_until_parked();
6904    // Type a on type formatting trigger character as the guest.
6905    editor_b.update(cx_b, |editor, cx| {
6906        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
6907        editor.handle_input(":", cx);
6908        cx.focus(&editor_b);
6909    });
6910
6911    // Receive an OnTypeFormatting request as the host's language server.
6912    // Return some formattings from the host's language server.
6913    cx_a.foreground().start_waiting();
6914    fake_language_server
6915        .handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
6916            assert_eq!(
6917                params.text_document_position.text_document.uri,
6918                lsp::Url::from_file_path("/a/main.rs").unwrap(),
6919            );
6920            assert_eq!(
6921                params.text_document_position.position,
6922                lsp::Position::new(0, 14),
6923            );
6924
6925            Ok(Some(vec![lsp::TextEdit {
6926                new_text: "~:".to_string(),
6927                range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
6928            }]))
6929        })
6930        .next()
6931        .await
6932        .unwrap();
6933    cx_a.foreground().finish_waiting();
6934
6935    // Open the buffer on the host and see that the formattings worked
6936    let buffer_a = project_a
6937        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
6938        .await
6939        .unwrap();
6940    cx_a.foreground().run_until_parked();
6941    buffer_a.read_with(cx_a, |buffer, _| {
6942        assert_eq!(buffer.text(), "fn main() { a:~: }")
6943    });
6944
6945    // Undo should remove LSP edits first
6946    editor_b.update(cx_b, |editor, cx| {
6947        assert_eq!(editor.text(cx), "fn main() { a:~: }");
6948        editor.undo(&Undo, cx);
6949        assert_eq!(editor.text(cx), "fn main() { a: }");
6950    });
6951    cx_a.foreground().run_until_parked();
6952    buffer_a.read_with(cx_a, |buffer, _| {
6953        assert_eq!(buffer.text(), "fn main() { a: }")
6954    });
6955
6956    editor_b.update(cx_b, |editor, cx| {
6957        assert_eq!(editor.text(cx), "fn main() { a: }");
6958        editor.undo(&Undo, cx);
6959        assert_eq!(editor.text(cx), "fn main() { a }");
6960    });
6961    cx_a.foreground().run_until_parked();
6962    buffer_a.read_with(cx_a, |buffer, _| {
6963        assert_eq!(buffer.text(), "fn main() { a }")
6964    });
6965}
6966
6967#[gpui::test(iterations = 10)]
6968async fn test_mutual_editor_inlay_hint_cache_update(
6969    deterministic: Arc<Deterministic>,
6970    cx_a: &mut TestAppContext,
6971    cx_b: &mut TestAppContext,
6972) {
6973    deterministic.forbid_parking();
6974    let mut server = TestServer::start(&deterministic).await;
6975    let client_a = server.create_client(cx_a, "user_a").await;
6976    let client_b = server.create_client(cx_b, "user_b").await;
6977    server
6978        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6979        .await;
6980    let active_call_a = cx_a.read(ActiveCall::global);
6981    let active_call_b = cx_b.read(ActiveCall::global);
6982
6983    cx_a.update(editor::init);
6984    cx_b.update(editor::init);
6985
6986    cx_a.update(|cx| {
6987        cx.update_global(|store: &mut SettingsStore, cx| {
6988            store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
6989                settings.defaults.inlay_hints = Some(InlayHintSettings {
6990                    enabled: true,
6991                    show_type_hints: true,
6992                    show_parameter_hints: false,
6993                    show_other_hints: true,
6994                })
6995            });
6996        });
6997    });
6998    cx_b.update(|cx| {
6999        cx.update_global(|store: &mut SettingsStore, cx| {
7000            store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
7001                settings.defaults.inlay_hints = Some(InlayHintSettings {
7002                    enabled: true,
7003                    show_type_hints: true,
7004                    show_parameter_hints: false,
7005                    show_other_hints: true,
7006                })
7007            });
7008        });
7009    });
7010
7011    let mut language = Language::new(
7012        LanguageConfig {
7013            name: "Rust".into(),
7014            path_suffixes: vec!["rs".to_string()],
7015            ..Default::default()
7016        },
7017        Some(tree_sitter_rust::language()),
7018    );
7019    let mut fake_language_servers = language
7020        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7021            capabilities: lsp::ServerCapabilities {
7022                inlay_hint_provider: Some(lsp::OneOf::Left(true)),
7023                ..Default::default()
7024            },
7025            ..Default::default()
7026        }))
7027        .await;
7028    let language = Arc::new(language);
7029    client_a.language_registry().add(Arc::clone(&language));
7030    client_b.language_registry().add(language);
7031
7032    // Client A opens a project.
7033    client_a
7034        .fs()
7035        .insert_tree(
7036            "/a",
7037            json!({
7038                "main.rs": "fn main() { a } // and some long comment to ensure inlay hints are not trimmed out",
7039                "other.rs": "// Test file",
7040            }),
7041        )
7042        .await;
7043    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7044    active_call_a
7045        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
7046        .await
7047        .unwrap();
7048    let project_id = active_call_a
7049        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7050        .await
7051        .unwrap();
7052
7053    // Client B joins the project
7054    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7055    active_call_b
7056        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
7057        .await
7058        .unwrap();
7059
7060    let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
7061    cx_a.foreground().start_waiting();
7062
7063    // The host opens a rust file.
7064    let _buffer_a = project_a
7065        .update(cx_a, |project, cx| {
7066            project.open_local_buffer("/a/main.rs", cx)
7067        })
7068        .await
7069        .unwrap();
7070    let fake_language_server = fake_language_servers.next().await.unwrap();
7071    let editor_a = workspace_a
7072        .update(cx_a, |workspace, cx| {
7073            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
7074        })
7075        .await
7076        .unwrap()
7077        .downcast::<Editor>()
7078        .unwrap();
7079
7080    // Set up the language server to return an additional inlay hint on each request.
7081    let edits_made = Arc::new(AtomicUsize::new(0));
7082    let closure_edits_made = Arc::clone(&edits_made);
7083    fake_language_server
7084        .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
7085            let task_edits_made = Arc::clone(&closure_edits_made);
7086            async move {
7087                assert_eq!(
7088                    params.text_document.uri,
7089                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
7090                );
7091                let edits_made = task_edits_made.load(atomic::Ordering::Acquire);
7092                Ok(Some(vec![lsp::InlayHint {
7093                    position: lsp::Position::new(0, edits_made as u32),
7094                    label: lsp::InlayHintLabel::String(edits_made.to_string()),
7095                    kind: None,
7096                    text_edits: None,
7097                    tooltip: None,
7098                    padding_left: None,
7099                    padding_right: None,
7100                    data: None,
7101                }]))
7102            }
7103        })
7104        .next()
7105        .await
7106        .unwrap();
7107
7108    deterministic.run_until_parked();
7109
7110    let initial_edit = edits_made.load(atomic::Ordering::Acquire);
7111    editor_a.update(cx_a, |editor, _| {
7112        assert_eq!(
7113            vec![initial_edit.to_string()],
7114            extract_hint_labels(editor),
7115            "Host should get its first hints when opens an editor"
7116        );
7117        let inlay_cache = editor.inlay_hint_cache();
7118        assert_eq!(
7119            inlay_cache.version(),
7120            1,
7121            "Host editor update the cache version after every cache/view change",
7122        );
7123    });
7124    let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
7125    let editor_b = workspace_b
7126        .update(cx_b, |workspace, cx| {
7127            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
7128        })
7129        .await
7130        .unwrap()
7131        .downcast::<Editor>()
7132        .unwrap();
7133
7134    deterministic.run_until_parked();
7135    editor_b.update(cx_b, |editor, _| {
7136        assert_eq!(
7137            vec![initial_edit.to_string()],
7138            extract_hint_labels(editor),
7139            "Client should get its first hints when opens an editor"
7140        );
7141        let inlay_cache = editor.inlay_hint_cache();
7142        assert_eq!(
7143            inlay_cache.version(),
7144            1,
7145            "Guest editor update the cache version after every cache/view change"
7146        );
7147    });
7148
7149    let after_client_edit = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
7150    editor_b.update(cx_b, |editor, cx| {
7151        editor.change_selections(None, cx, |s| s.select_ranges([13..13].clone()));
7152        editor.handle_input(":", cx);
7153        cx.focus(&editor_b);
7154    });
7155
7156    deterministic.run_until_parked();
7157    editor_a.update(cx_a, |editor, _| {
7158        assert_eq!(
7159            vec![after_client_edit.to_string()],
7160            extract_hint_labels(editor),
7161        );
7162        let inlay_cache = editor.inlay_hint_cache();
7163        assert_eq!(inlay_cache.version(), 2);
7164    });
7165    editor_b.update(cx_b, |editor, _| {
7166        assert_eq!(
7167            vec![after_client_edit.to_string()],
7168            extract_hint_labels(editor),
7169        );
7170        let inlay_cache = editor.inlay_hint_cache();
7171        assert_eq!(inlay_cache.version(), 2);
7172    });
7173
7174    let after_host_edit = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
7175    editor_a.update(cx_a, |editor, cx| {
7176        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
7177        editor.handle_input("a change to increment both buffers' versions", cx);
7178        cx.focus(&editor_a);
7179    });
7180
7181    deterministic.run_until_parked();
7182    editor_a.update(cx_a, |editor, _| {
7183        assert_eq!(
7184            vec![after_host_edit.to_string()],
7185            extract_hint_labels(editor),
7186        );
7187        let inlay_cache = editor.inlay_hint_cache();
7188        assert_eq!(inlay_cache.version(), 3);
7189    });
7190    editor_b.update(cx_b, |editor, _| {
7191        assert_eq!(
7192            vec![after_host_edit.to_string()],
7193            extract_hint_labels(editor),
7194        );
7195        let inlay_cache = editor.inlay_hint_cache();
7196        assert_eq!(inlay_cache.version(), 3);
7197    });
7198
7199    let after_special_edit_for_refresh = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
7200    fake_language_server
7201        .request::<lsp::request::InlayHintRefreshRequest>(())
7202        .await
7203        .expect("inlay refresh request failed");
7204
7205    deterministic.run_until_parked();
7206    editor_a.update(cx_a, |editor, _| {
7207        assert_eq!(
7208            vec![after_special_edit_for_refresh.to_string()],
7209            extract_hint_labels(editor),
7210            "Host should react to /refresh LSP request"
7211        );
7212        let inlay_cache = editor.inlay_hint_cache();
7213        assert_eq!(
7214            inlay_cache.version(),
7215            4,
7216            "Host should accepted all edits and bump its cache version every time"
7217        );
7218    });
7219    editor_b.update(cx_b, |editor, _| {
7220        assert_eq!(
7221            vec![after_special_edit_for_refresh.to_string()],
7222            extract_hint_labels(editor),
7223            "Guest should get a /refresh LSP request propagated by host"
7224        );
7225        let inlay_cache = editor.inlay_hint_cache();
7226        assert_eq!(
7227            inlay_cache.version(),
7228            4,
7229            "Guest should accepted all edits and bump its cache version every time"
7230        );
7231    });
7232}
7233
7234#[gpui::test(iterations = 10)]
7235async fn test_inlay_hint_refresh_is_forwarded(
7236    deterministic: Arc<Deterministic>,
7237    cx_a: &mut TestAppContext,
7238    cx_b: &mut TestAppContext,
7239) {
7240    deterministic.forbid_parking();
7241    let mut server = TestServer::start(&deterministic).await;
7242    let client_a = server.create_client(cx_a, "user_a").await;
7243    let client_b = server.create_client(cx_b, "user_b").await;
7244    server
7245        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7246        .await;
7247    let active_call_a = cx_a.read(ActiveCall::global);
7248    let active_call_b = cx_b.read(ActiveCall::global);
7249
7250    cx_a.update(editor::init);
7251    cx_b.update(editor::init);
7252
7253    cx_a.update(|cx| {
7254        cx.update_global(|store: &mut SettingsStore, cx| {
7255            store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
7256                settings.defaults.inlay_hints = Some(InlayHintSettings {
7257                    enabled: false,
7258                    show_type_hints: false,
7259                    show_parameter_hints: false,
7260                    show_other_hints: false,
7261                })
7262            });
7263        });
7264    });
7265    cx_b.update(|cx| {
7266        cx.update_global(|store: &mut SettingsStore, cx| {
7267            store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
7268                settings.defaults.inlay_hints = Some(InlayHintSettings {
7269                    enabled: true,
7270                    show_type_hints: true,
7271                    show_parameter_hints: true,
7272                    show_other_hints: true,
7273                })
7274            });
7275        });
7276    });
7277
7278    let mut language = Language::new(
7279        LanguageConfig {
7280            name: "Rust".into(),
7281            path_suffixes: vec!["rs".to_string()],
7282            ..Default::default()
7283        },
7284        Some(tree_sitter_rust::language()),
7285    );
7286    let mut fake_language_servers = language
7287        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7288            capabilities: lsp::ServerCapabilities {
7289                inlay_hint_provider: Some(lsp::OneOf::Left(true)),
7290                ..Default::default()
7291            },
7292            ..Default::default()
7293        }))
7294        .await;
7295    let language = Arc::new(language);
7296    client_a.language_registry().add(Arc::clone(&language));
7297    client_b.language_registry().add(language);
7298
7299    client_a
7300        .fs()
7301        .insert_tree(
7302            "/a",
7303            json!({
7304                "main.rs": "fn main() { a } // and some long comment to ensure inlay hints are not trimmed out",
7305                "other.rs": "// Test file",
7306            }),
7307        )
7308        .await;
7309    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7310    active_call_a
7311        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
7312        .await
7313        .unwrap();
7314    let project_id = active_call_a
7315        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7316        .await
7317        .unwrap();
7318
7319    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7320    active_call_b
7321        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
7322        .await
7323        .unwrap();
7324
7325    let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
7326    let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
7327    cx_a.foreground().start_waiting();
7328    cx_b.foreground().start_waiting();
7329
7330    let editor_a = workspace_a
7331        .update(cx_a, |workspace, cx| {
7332            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
7333        })
7334        .await
7335        .unwrap()
7336        .downcast::<Editor>()
7337        .unwrap();
7338
7339    let editor_b = workspace_b
7340        .update(cx_b, |workspace, cx| {
7341            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
7342        })
7343        .await
7344        .unwrap()
7345        .downcast::<Editor>()
7346        .unwrap();
7347
7348    let other_hints = Arc::new(AtomicBool::new(false));
7349    let fake_language_server = fake_language_servers.next().await.unwrap();
7350    let closure_other_hints = Arc::clone(&other_hints);
7351    fake_language_server
7352        .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
7353            let task_other_hints = Arc::clone(&closure_other_hints);
7354            async move {
7355                assert_eq!(
7356                    params.text_document.uri,
7357                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
7358                );
7359                let other_hints = task_other_hints.load(atomic::Ordering::Acquire);
7360                let character = if other_hints { 0 } else { 2 };
7361                let label = if other_hints {
7362                    "other hint"
7363                } else {
7364                    "initial hint"
7365                };
7366                Ok(Some(vec![lsp::InlayHint {
7367                    position: lsp::Position::new(0, character),
7368                    label: lsp::InlayHintLabel::String(label.to_string()),
7369                    kind: None,
7370                    text_edits: None,
7371                    tooltip: None,
7372                    padding_left: None,
7373                    padding_right: None,
7374                    data: None,
7375                }]))
7376            }
7377        })
7378        .next()
7379        .await
7380        .unwrap();
7381    cx_a.foreground().finish_waiting();
7382    cx_b.foreground().finish_waiting();
7383
7384    cx_a.foreground().run_until_parked();
7385    editor_a.update(cx_a, |editor, _| {
7386        assert!(
7387            extract_hint_labels(editor).is_empty(),
7388            "Host should get no hints due to them turned off"
7389        );
7390        let inlay_cache = editor.inlay_hint_cache();
7391        assert_eq!(
7392            inlay_cache.version(),
7393            0,
7394            "Turned off hints should not generate version updates"
7395        );
7396    });
7397
7398    cx_b.foreground().run_until_parked();
7399    editor_b.update(cx_b, |editor, _| {
7400        assert_eq!(
7401            vec!["initial hint".to_string()],
7402            extract_hint_labels(editor),
7403            "Client should get its first hints when opens an editor"
7404        );
7405        let inlay_cache = editor.inlay_hint_cache();
7406        assert_eq!(
7407            inlay_cache.version(),
7408            1,
7409            "Should update cache verison after first hints"
7410        );
7411    });
7412
7413    other_hints.fetch_or(true, atomic::Ordering::Release);
7414    fake_language_server
7415        .request::<lsp::request::InlayHintRefreshRequest>(())
7416        .await
7417        .expect("inlay refresh request failed");
7418    cx_a.foreground().run_until_parked();
7419    editor_a.update(cx_a, |editor, _| {
7420        assert!(
7421            extract_hint_labels(editor).is_empty(),
7422            "Host should get nop hints due to them turned off, even after the /refresh"
7423        );
7424        let inlay_cache = editor.inlay_hint_cache();
7425        assert_eq!(
7426            inlay_cache.version(),
7427            0,
7428            "Turned off hints should not generate version updates, again"
7429        );
7430    });
7431
7432    cx_b.foreground().run_until_parked();
7433    editor_b.update(cx_b, |editor, _| {
7434        assert_eq!(
7435            vec!["other hint".to_string()],
7436            extract_hint_labels(editor),
7437            "Guest should get a /refresh LSP request propagated by host despite host hints are off"
7438        );
7439        let inlay_cache = editor.inlay_hint_cache();
7440        assert_eq!(
7441            inlay_cache.version(),
7442            2,
7443            "Guest should accepted all edits and bump its cache version every time"
7444        );
7445    });
7446}
7447
7448fn extract_hint_labels(editor: &Editor) -> Vec<String> {
7449    let mut labels = Vec::new();
7450    for hint in editor.inlay_hint_cache().hints() {
7451        match hint.label {
7452            project::InlayHintLabel::String(s) => labels.push(s),
7453            _ => unreachable!(),
7454        }
7455    }
7456    labels
7457}