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