integration_tests.rs

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