integration_tests.rs

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