integration_tests.rs

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