integration_tests.rs

   1use crate::{
   2    rpc::{CLEANUP_TIMEOUT, RECONNECT_TIMEOUT},
   3    tests::{
   4        channel_id, following_tests::join_channel, room_participants, rust_lang, RoomParticipants,
   5        TestClient, TestServer,
   6    },
   7};
   8use anyhow::{anyhow, Result};
   9use assistant_context_editor::ContextStore;
  10use assistant_slash_command::SlashCommandWorkingSet;
  11use 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                name: None,
1844                email: None,
1845            }),
1846            project_id: project_a_id,
1847            worktree_root_names: vec!["a".to_string()],
1848        }]
1849    );
1850
1851    let project_b_id = active_call_b
1852        .update(cx_b, |call, cx| call.share_project(project_b.clone(), cx))
1853        .await
1854        .unwrap();
1855    executor.run_until_parked();
1856    assert_eq!(
1857        mem::take(&mut *events_a.borrow_mut()),
1858        vec![room::Event::RemoteProjectShared {
1859            owner: Arc::new(User {
1860                id: client_b.user_id().unwrap(),
1861                github_login: "user_b".to_string(),
1862                avatar_uri: "avatar_b".into(),
1863                name: None,
1864                email: None,
1865            }),
1866            project_id: project_b_id,
1867            worktree_root_names: vec!["b".to_string()]
1868        }]
1869    );
1870    assert_eq!(mem::take(&mut *events_b.borrow_mut()), vec![]);
1871
1872    // Sharing a project twice is idempotent.
1873    let project_b_id_2 = active_call_b
1874        .update(cx_b, |call, cx| call.share_project(project_b.clone(), cx))
1875        .await
1876        .unwrap();
1877    assert_eq!(project_b_id_2, project_b_id);
1878    executor.run_until_parked();
1879    assert_eq!(mem::take(&mut *events_a.borrow_mut()), vec![]);
1880    assert_eq!(mem::take(&mut *events_b.borrow_mut()), vec![]);
1881
1882    // Unsharing a project should dispatch the RemoteProjectUnshared event.
1883    active_call_a
1884        .update(cx_a, |call, cx| call.hang_up(cx))
1885        .await
1886        .unwrap();
1887    executor.run_until_parked();
1888
1889    assert_eq!(
1890        mem::take(&mut *events_a.borrow_mut()),
1891        vec![room::Event::RoomLeft { channel_id: None }]
1892    );
1893    assert_eq!(
1894        mem::take(&mut *events_b.borrow_mut()),
1895        vec![room::Event::RemoteProjectUnshared {
1896            project_id: project_a_id,
1897        }]
1898    );
1899}
1900
1901fn active_call_events(cx: &mut TestAppContext) -> Rc<RefCell<Vec<room::Event>>> {
1902    let events = Rc::new(RefCell::new(Vec::new()));
1903    let active_call = cx.read(ActiveCall::global);
1904    cx.update({
1905        let events = events.clone();
1906        |cx| {
1907            cx.subscribe(&active_call, move |_, event, _| {
1908                events.borrow_mut().push(event.clone())
1909            })
1910            .detach()
1911        }
1912    });
1913    events
1914}
1915
1916#[gpui::test]
1917async fn test_mute_deafen(
1918    executor: BackgroundExecutor,
1919    cx_a: &mut TestAppContext,
1920    cx_b: &mut TestAppContext,
1921    cx_c: &mut TestAppContext,
1922) {
1923    let mut server = TestServer::start(executor.clone()).await;
1924    let client_a = server.create_client(cx_a, "user_a").await;
1925    let client_b = server.create_client(cx_b, "user_b").await;
1926    let client_c = server.create_client(cx_c, "user_c").await;
1927
1928    server
1929        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
1930        .await;
1931
1932    let active_call_a = cx_a.read(ActiveCall::global);
1933    let active_call_b = cx_b.read(ActiveCall::global);
1934    let active_call_c = cx_c.read(ActiveCall::global);
1935
1936    // User A calls user B, B answers.
1937    active_call_a
1938        .update(cx_a, |call, cx| {
1939            call.invite(client_b.user_id().unwrap(), None, cx)
1940        })
1941        .await
1942        .unwrap();
1943    executor.run_until_parked();
1944    active_call_b
1945        .update(cx_b, |call, cx| call.accept_incoming(cx))
1946        .await
1947        .unwrap();
1948    executor.run_until_parked();
1949
1950    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
1951    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
1952
1953    room_a.read_with(cx_a, |room, _| assert!(!room.is_muted()));
1954    room_b.read_with(cx_b, |room, _| assert!(!room.is_muted()));
1955
1956    // Users A and B are both unmuted.
1957    assert_eq!(
1958        participant_audio_state(&room_a, cx_a),
1959        &[ParticipantAudioState {
1960            user_id: client_b.user_id().unwrap(),
1961            is_muted: false,
1962            audio_tracks_playing: vec![true],
1963        }]
1964    );
1965    assert_eq!(
1966        participant_audio_state(&room_b, cx_b),
1967        &[ParticipantAudioState {
1968            user_id: client_a.user_id().unwrap(),
1969            is_muted: false,
1970            audio_tracks_playing: vec![true],
1971        }]
1972    );
1973
1974    // User A mutes
1975    room_a.update(cx_a, |room, cx| room.toggle_mute(cx));
1976    executor.run_until_parked();
1977
1978    // User A hears user B, but B doesn't hear A.
1979    room_a.read_with(cx_a, |room, _| assert!(room.is_muted()));
1980    room_b.read_with(cx_b, |room, _| assert!(!room.is_muted()));
1981    assert_eq!(
1982        participant_audio_state(&room_a, cx_a),
1983        &[ParticipantAudioState {
1984            user_id: client_b.user_id().unwrap(),
1985            is_muted: false,
1986            audio_tracks_playing: vec![true],
1987        }]
1988    );
1989    assert_eq!(
1990        participant_audio_state(&room_b, cx_b),
1991        &[ParticipantAudioState {
1992            user_id: client_a.user_id().unwrap(),
1993            is_muted: true,
1994            audio_tracks_playing: vec![true],
1995        }]
1996    );
1997
1998    // User A deafens
1999    room_a.update(cx_a, |room, cx| room.toggle_deafen(cx));
2000    executor.run_until_parked();
2001
2002    // User A does not hear user B.
2003    room_a.read_with(cx_a, |room, _| assert!(room.is_muted()));
2004    room_b.read_with(cx_b, |room, _| assert!(!room.is_muted()));
2005    assert_eq!(
2006        participant_audio_state(&room_a, cx_a),
2007        &[ParticipantAudioState {
2008            user_id: client_b.user_id().unwrap(),
2009            is_muted: false,
2010            audio_tracks_playing: vec![false],
2011        }]
2012    );
2013    assert_eq!(
2014        participant_audio_state(&room_b, cx_b),
2015        &[ParticipantAudioState {
2016            user_id: client_a.user_id().unwrap(),
2017            is_muted: true,
2018            audio_tracks_playing: vec![true],
2019        }]
2020    );
2021
2022    // User B calls user C, C joins.
2023    active_call_b
2024        .update(cx_b, |call, cx| {
2025            call.invite(client_c.user_id().unwrap(), None, cx)
2026        })
2027        .await
2028        .unwrap();
2029    executor.run_until_parked();
2030    active_call_c
2031        .update(cx_c, |call, cx| call.accept_incoming(cx))
2032        .await
2033        .unwrap();
2034    executor.run_until_parked();
2035
2036    // User A does not hear users B or C.
2037    assert_eq!(
2038        participant_audio_state(&room_a, cx_a),
2039        &[
2040            ParticipantAudioState {
2041                user_id: client_b.user_id().unwrap(),
2042                is_muted: false,
2043                audio_tracks_playing: vec![false],
2044            },
2045            ParticipantAudioState {
2046                user_id: client_c.user_id().unwrap(),
2047                is_muted: false,
2048                audio_tracks_playing: vec![false],
2049            }
2050        ]
2051    );
2052    assert_eq!(
2053        participant_audio_state(&room_b, cx_b),
2054        &[
2055            ParticipantAudioState {
2056                user_id: client_a.user_id().unwrap(),
2057                is_muted: true,
2058                audio_tracks_playing: vec![true],
2059            },
2060            ParticipantAudioState {
2061                user_id: client_c.user_id().unwrap(),
2062                is_muted: false,
2063                audio_tracks_playing: vec![true],
2064            }
2065        ]
2066    );
2067
2068    #[derive(PartialEq, Eq, Debug)]
2069    struct ParticipantAudioState {
2070        user_id: u64,
2071        is_muted: bool,
2072        audio_tracks_playing: Vec<bool>,
2073    }
2074
2075    fn participant_audio_state(
2076        room: &Model<Room>,
2077        cx: &TestAppContext,
2078    ) -> Vec<ParticipantAudioState> {
2079        room.read_with(cx, |room, _| {
2080            room.remote_participants()
2081                .iter()
2082                .map(|(user_id, participant)| ParticipantAudioState {
2083                    user_id: *user_id,
2084                    is_muted: participant.muted,
2085                    audio_tracks_playing: participant
2086                        .audio_tracks
2087                        .values()
2088                        .map({
2089                            #[cfg(target_os = "macos")]
2090                            {
2091                                |track| track.is_playing()
2092                            }
2093
2094                            #[cfg(not(target_os = "macos"))]
2095                            {
2096                                |(track, _)| track.rtc_track().enabled()
2097                            }
2098                        })
2099                        .collect(),
2100                })
2101                .collect::<Vec<_>>()
2102        })
2103    }
2104}
2105
2106#[gpui::test(iterations = 10)]
2107async fn test_room_location(
2108    executor: BackgroundExecutor,
2109    cx_a: &mut TestAppContext,
2110    cx_b: &mut TestAppContext,
2111) {
2112    let mut server = TestServer::start(executor.clone()).await;
2113    let client_a = server.create_client(cx_a, "user_a").await;
2114    let client_b = server.create_client(cx_b, "user_b").await;
2115    client_a.fs().insert_tree("/a", json!({})).await;
2116    client_b.fs().insert_tree("/b", json!({})).await;
2117
2118    let active_call_a = cx_a.read(ActiveCall::global);
2119    let active_call_b = cx_b.read(ActiveCall::global);
2120
2121    let a_notified = Rc::new(Cell::new(false));
2122    cx_a.update({
2123        let notified = a_notified.clone();
2124        |cx| {
2125            cx.observe(&active_call_a, move |_, _| notified.set(true))
2126                .detach()
2127        }
2128    });
2129
2130    let b_notified = Rc::new(Cell::new(false));
2131    cx_b.update({
2132        let b_notified = b_notified.clone();
2133        |cx| {
2134            cx.observe(&active_call_b, move |_, _| b_notified.set(true))
2135                .detach()
2136        }
2137    });
2138
2139    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
2140    active_call_a
2141        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
2142        .await
2143        .unwrap();
2144    let (project_b, _) = client_b.build_local_project("/b", cx_b).await;
2145
2146    server
2147        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2148        .await;
2149
2150    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
2151
2152    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
2153    executor.run_until_parked();
2154    assert!(a_notified.take());
2155    assert_eq!(
2156        participant_locations(&room_a, cx_a),
2157        vec![("user_b".to_string(), ParticipantLocation::External)]
2158    );
2159    assert!(b_notified.take());
2160    assert_eq!(
2161        participant_locations(&room_b, cx_b),
2162        vec![("user_a".to_string(), ParticipantLocation::UnsharedProject)]
2163    );
2164
2165    let project_a_id = active_call_a
2166        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2167        .await
2168        .unwrap();
2169    executor.run_until_parked();
2170    assert!(a_notified.take());
2171    assert_eq!(
2172        participant_locations(&room_a, cx_a),
2173        vec![("user_b".to_string(), ParticipantLocation::External)]
2174    );
2175    assert!(b_notified.take());
2176    assert_eq!(
2177        participant_locations(&room_b, cx_b),
2178        vec![(
2179            "user_a".to_string(),
2180            ParticipantLocation::SharedProject {
2181                project_id: project_a_id
2182            }
2183        )]
2184    );
2185
2186    let project_b_id = active_call_b
2187        .update(cx_b, |call, cx| call.share_project(project_b.clone(), cx))
2188        .await
2189        .unwrap();
2190    executor.run_until_parked();
2191    assert!(a_notified.take());
2192    assert_eq!(
2193        participant_locations(&room_a, cx_a),
2194        vec![("user_b".to_string(), ParticipantLocation::External)]
2195    );
2196    assert!(b_notified.take());
2197    assert_eq!(
2198        participant_locations(&room_b, cx_b),
2199        vec![(
2200            "user_a".to_string(),
2201            ParticipantLocation::SharedProject {
2202                project_id: project_a_id
2203            }
2204        )]
2205    );
2206
2207    active_call_b
2208        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
2209        .await
2210        .unwrap();
2211    executor.run_until_parked();
2212    assert!(a_notified.take());
2213    assert_eq!(
2214        participant_locations(&room_a, cx_a),
2215        vec![(
2216            "user_b".to_string(),
2217            ParticipantLocation::SharedProject {
2218                project_id: project_b_id
2219            }
2220        )]
2221    );
2222    assert!(b_notified.take());
2223    assert_eq!(
2224        participant_locations(&room_b, cx_b),
2225        vec![(
2226            "user_a".to_string(),
2227            ParticipantLocation::SharedProject {
2228                project_id: project_a_id
2229            }
2230        )]
2231    );
2232
2233    active_call_b
2234        .update(cx_b, |call, cx| call.set_location(None, cx))
2235        .await
2236        .unwrap();
2237    executor.run_until_parked();
2238    assert!(a_notified.take());
2239    assert_eq!(
2240        participant_locations(&room_a, cx_a),
2241        vec![("user_b".to_string(), ParticipantLocation::External)]
2242    );
2243    assert!(b_notified.take());
2244    assert_eq!(
2245        participant_locations(&room_b, cx_b),
2246        vec![(
2247            "user_a".to_string(),
2248            ParticipantLocation::SharedProject {
2249                project_id: project_a_id
2250            }
2251        )]
2252    );
2253
2254    fn participant_locations(
2255        room: &Model<Room>,
2256        cx: &TestAppContext,
2257    ) -> Vec<(String, ParticipantLocation)> {
2258        room.read_with(cx, |room, _| {
2259            room.remote_participants()
2260                .values()
2261                .map(|participant| {
2262                    (
2263                        participant.user.github_login.to_string(),
2264                        participant.location,
2265                    )
2266                })
2267                .collect()
2268        })
2269    }
2270}
2271
2272#[gpui::test(iterations = 10)]
2273async fn test_propagate_saves_and_fs_changes(
2274    executor: BackgroundExecutor,
2275    cx_a: &mut TestAppContext,
2276    cx_b: &mut TestAppContext,
2277    cx_c: &mut TestAppContext,
2278) {
2279    let mut server = TestServer::start(executor.clone()).await;
2280    let client_a = server.create_client(cx_a, "user_a").await;
2281    let client_b = server.create_client(cx_b, "user_b").await;
2282    let client_c = server.create_client(cx_c, "user_c").await;
2283
2284    server
2285        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
2286        .await;
2287    let active_call_a = cx_a.read(ActiveCall::global);
2288
2289    let rust = Arc::new(Language::new(
2290        LanguageConfig {
2291            name: "Rust".into(),
2292            matcher: LanguageMatcher {
2293                path_suffixes: vec!["rs".to_string()],
2294                ..Default::default()
2295            },
2296            ..Default::default()
2297        },
2298        Some(tree_sitter_rust::LANGUAGE.into()),
2299    ));
2300    let javascript = Arc::new(Language::new(
2301        LanguageConfig {
2302            name: "JavaScript".into(),
2303            matcher: LanguageMatcher {
2304                path_suffixes: vec!["js".to_string()],
2305                ..Default::default()
2306            },
2307            ..Default::default()
2308        },
2309        Some(tree_sitter_rust::LANGUAGE.into()),
2310    ));
2311    for client in [&client_a, &client_b, &client_c] {
2312        client.language_registry().add(rust.clone());
2313        client.language_registry().add(javascript.clone());
2314    }
2315
2316    client_a
2317        .fs()
2318        .insert_tree(
2319            "/a",
2320            json!({
2321                "file1.rs": "",
2322                "file2": ""
2323            }),
2324        )
2325        .await;
2326    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
2327
2328    let worktree_a = project_a.read_with(cx_a, |p, cx| p.worktrees(cx).next().unwrap());
2329    let project_id = active_call_a
2330        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2331        .await
2332        .unwrap();
2333
2334    // Join that worktree as clients B and C.
2335    let project_b = client_b.join_remote_project(project_id, cx_b).await;
2336    let project_c = client_c.join_remote_project(project_id, cx_c).await;
2337
2338    let worktree_b = project_b.read_with(cx_b, |p, cx| p.worktrees(cx).next().unwrap());
2339
2340    let worktree_c = project_c.read_with(cx_c, |p, cx| p.worktrees(cx).next().unwrap());
2341
2342    // Open and edit a buffer as both guests B and C.
2343    let buffer_b = project_b
2344        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "file1.rs"), cx))
2345        .await
2346        .unwrap();
2347    let buffer_c = project_c
2348        .update(cx_c, |p, cx| p.open_buffer((worktree_id, "file1.rs"), cx))
2349        .await
2350        .unwrap();
2351
2352    buffer_b.read_with(cx_b, |buffer, _| {
2353        assert_eq!(buffer.language().unwrap().name(), "Rust".into());
2354    });
2355
2356    buffer_c.read_with(cx_c, |buffer, _| {
2357        assert_eq!(buffer.language().unwrap().name(), "Rust".into());
2358    });
2359    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "i-am-b, ")], None, cx));
2360    buffer_c.update(cx_c, |buf, cx| buf.edit([(0..0, "i-am-c, ")], None, cx));
2361
2362    // Open and edit that buffer as the host.
2363    let buffer_a = project_a
2364        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "file1.rs"), cx))
2365        .await
2366        .unwrap();
2367
2368    executor.run_until_parked();
2369
2370    buffer_a.read_with(cx_a, |buf, _| assert_eq!(buf.text(), "i-am-c, i-am-b, "));
2371    buffer_a.update(cx_a, |buf, cx| {
2372        buf.edit([(buf.len()..buf.len(), "i-am-a")], None, cx)
2373    });
2374
2375    executor.run_until_parked();
2376
2377    buffer_a.read_with(cx_a, |buf, _| {
2378        assert_eq!(buf.text(), "i-am-c, i-am-b, i-am-a");
2379    });
2380
2381    buffer_b.read_with(cx_b, |buf, _| {
2382        assert_eq!(buf.text(), "i-am-c, i-am-b, i-am-a");
2383    });
2384
2385    buffer_c.read_with(cx_c, |buf, _| {
2386        assert_eq!(buf.text(), "i-am-c, i-am-b, i-am-a");
2387    });
2388
2389    // Edit the buffer as the host and concurrently save as guest B.
2390    let save_b = project_b.update(cx_b, |project, cx| {
2391        project.save_buffer(buffer_b.clone(), cx)
2392    });
2393    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "hi-a, ")], None, cx));
2394    save_b.await.unwrap();
2395    assert_eq!(
2396        client_a.fs().load("/a/file1.rs".as_ref()).await.unwrap(),
2397        "hi-a, i-am-c, i-am-b, i-am-a"
2398    );
2399
2400    executor.run_until_parked();
2401
2402    buffer_a.read_with(cx_a, |buf, _| assert!(!buf.is_dirty()));
2403
2404    buffer_b.read_with(cx_b, |buf, _| assert!(!buf.is_dirty()));
2405
2406    buffer_c.read_with(cx_c, |buf, _| assert!(!buf.is_dirty()));
2407
2408    // Make changes on host's file system, see those changes on guest worktrees.
2409    client_a
2410        .fs()
2411        .rename(
2412            "/a/file1.rs".as_ref(),
2413            "/a/file1.js".as_ref(),
2414            Default::default(),
2415        )
2416        .await
2417        .unwrap();
2418    client_a
2419        .fs()
2420        .rename("/a/file2".as_ref(), "/a/file3".as_ref(), Default::default())
2421        .await
2422        .unwrap();
2423    client_a.fs().insert_file("/a/file4", "4".into()).await;
2424    executor.run_until_parked();
2425
2426    worktree_a.read_with(cx_a, |tree, _| {
2427        assert_eq!(
2428            tree.paths()
2429                .map(|p| p.to_string_lossy())
2430                .collect::<Vec<_>>(),
2431            ["file1.js", "file3", "file4"]
2432        )
2433    });
2434
2435    worktree_b.read_with(cx_b, |tree, _| {
2436        assert_eq!(
2437            tree.paths()
2438                .map(|p| p.to_string_lossy())
2439                .collect::<Vec<_>>(),
2440            ["file1.js", "file3", "file4"]
2441        )
2442    });
2443
2444    worktree_c.read_with(cx_c, |tree, _| {
2445        assert_eq!(
2446            tree.paths()
2447                .map(|p| p.to_string_lossy())
2448                .collect::<Vec<_>>(),
2449            ["file1.js", "file3", "file4"]
2450        )
2451    });
2452
2453    // Ensure buffer files are updated as well.
2454
2455    buffer_a.read_with(cx_a, |buffer, _| {
2456        assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
2457        assert_eq!(buffer.language().unwrap().name(), "JavaScript".into());
2458    });
2459
2460    buffer_b.read_with(cx_b, |buffer, _| {
2461        assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
2462        assert_eq!(buffer.language().unwrap().name(), "JavaScript".into());
2463    });
2464
2465    buffer_c.read_with(cx_c, |buffer, _| {
2466        assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
2467        assert_eq!(buffer.language().unwrap().name(), "JavaScript".into());
2468    });
2469
2470    let new_buffer_a = project_a
2471        .update(cx_a, |p, cx| p.create_buffer(cx))
2472        .await
2473        .unwrap();
2474
2475    let new_buffer_id = new_buffer_a.read_with(cx_a, |buffer, _| buffer.remote_id());
2476    let new_buffer_b = project_b
2477        .update(cx_b, |p, cx| p.open_buffer_by_id(new_buffer_id, cx))
2478        .await
2479        .unwrap();
2480
2481    new_buffer_b.read_with(cx_b, |buffer, _| {
2482        assert!(buffer.file().is_none());
2483    });
2484
2485    new_buffer_a.update(cx_a, |buffer, cx| {
2486        buffer.edit([(0..0, "ok")], None, cx);
2487    });
2488    project_a
2489        .update(cx_a, |project, cx| {
2490            let path = ProjectPath {
2491                path: Arc::from(Path::new("file3.rs")),
2492                worktree_id: worktree_a.read(cx).id(),
2493            };
2494
2495            project.save_buffer_as(new_buffer_a.clone(), path, cx)
2496        })
2497        .await
2498        .unwrap();
2499
2500    executor.run_until_parked();
2501
2502    new_buffer_b.read_with(cx_b, |buffer_b, _| {
2503        assert_eq!(
2504            buffer_b.file().unwrap().path().as_ref(),
2505            Path::new("file3.rs")
2506        );
2507
2508        new_buffer_a.read_with(cx_a, |buffer_a, _| {
2509            assert_eq!(buffer_b.saved_mtime(), buffer_a.saved_mtime());
2510            assert_eq!(buffer_b.saved_version(), buffer_a.saved_version());
2511        });
2512    });
2513}
2514
2515#[gpui::test(iterations = 10)]
2516async fn test_git_diff_base_change(
2517    executor: BackgroundExecutor,
2518    cx_a: &mut TestAppContext,
2519    cx_b: &mut TestAppContext,
2520) {
2521    let mut server = TestServer::start(executor.clone()).await;
2522    let client_a = server.create_client(cx_a, "user_a").await;
2523    let client_b = server.create_client(cx_b, "user_b").await;
2524    server
2525        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2526        .await;
2527    let active_call_a = cx_a.read(ActiveCall::global);
2528
2529    client_a
2530        .fs()
2531        .insert_tree(
2532            "/dir",
2533            json!({
2534            ".git": {},
2535            "sub": {
2536                ".git": {},
2537                "b.txt": "
2538                    one
2539                    two
2540                    three
2541                ".unindent(),
2542            },
2543            "a.txt": "
2544                    one
2545                    two
2546                    three
2547                ".unindent(),
2548            }),
2549        )
2550        .await;
2551
2552    let (project_local, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2553    let project_id = active_call_a
2554        .update(cx_a, |call, cx| {
2555            call.share_project(project_local.clone(), cx)
2556        })
2557        .await
2558        .unwrap();
2559
2560    let project_remote = client_b.join_remote_project(project_id, cx_b).await;
2561
2562    let diff_base = "
2563        one
2564        three
2565    "
2566    .unindent();
2567
2568    let new_diff_base = "
2569        one
2570        two
2571    "
2572    .unindent();
2573
2574    client_a.fs().set_index_for_repo(
2575        Path::new("/dir/.git"),
2576        &[(Path::new("a.txt"), diff_base.clone())],
2577    );
2578
2579    // Create the buffer
2580    let buffer_local_a = project_local
2581        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2582        .await
2583        .unwrap();
2584    let change_set_local_a = project_local
2585        .update(cx_a, |p, cx| {
2586            p.open_unstaged_changes(buffer_local_a.clone(), cx)
2587        })
2588        .await
2589        .unwrap();
2590
2591    // Wait for it to catch up to the new diff
2592    executor.run_until_parked();
2593    change_set_local_a.read_with(cx_a, |change_set, cx| {
2594        let buffer = buffer_local_a.read(cx);
2595        assert_eq!(
2596            change_set.base_text_string(cx).as_deref(),
2597            Some(diff_base.as_str())
2598        );
2599        git::diff::assert_hunks(
2600            change_set.diff_to_buffer.hunks_in_row_range(0..4, buffer),
2601            buffer,
2602            &diff_base,
2603            &[(1..2, "", "two\n")],
2604        );
2605    });
2606
2607    // Create remote buffer
2608    let buffer_remote_a = project_remote
2609        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2610        .await
2611        .unwrap();
2612    let change_set_remote_a = project_remote
2613        .update(cx_b, |p, cx| {
2614            p.open_unstaged_changes(buffer_remote_a.clone(), cx)
2615        })
2616        .await
2617        .unwrap();
2618
2619    // Wait remote buffer to catch up to the new diff
2620    executor.run_until_parked();
2621    change_set_remote_a.read_with(cx_b, |change_set, cx| {
2622        let buffer = buffer_remote_a.read(cx);
2623        assert_eq!(
2624            change_set.base_text_string(cx).as_deref(),
2625            Some(diff_base.as_str())
2626        );
2627        git::diff::assert_hunks(
2628            change_set.diff_to_buffer.hunks_in_row_range(0..4, buffer),
2629            buffer,
2630            &diff_base,
2631            &[(1..2, "", "two\n")],
2632        );
2633    });
2634
2635    // Update the staged text of the open buffer
2636    client_a.fs().set_index_for_repo(
2637        Path::new("/dir/.git"),
2638        &[(Path::new("a.txt"), new_diff_base.clone())],
2639    );
2640
2641    // Wait for buffer_local_a to receive it
2642    executor.run_until_parked();
2643    change_set_local_a.read_with(cx_a, |change_set, cx| {
2644        let buffer = buffer_local_a.read(cx);
2645        assert_eq!(
2646            change_set.base_text_string(cx).as_deref(),
2647            Some(new_diff_base.as_str())
2648        );
2649        git::diff::assert_hunks(
2650            change_set.diff_to_buffer.hunks_in_row_range(0..4, buffer),
2651            buffer,
2652            &new_diff_base,
2653            &[(2..3, "", "three\n")],
2654        );
2655    });
2656
2657    change_set_remote_a.read_with(cx_b, |change_set, cx| {
2658        let buffer = buffer_remote_a.read(cx);
2659        assert_eq!(
2660            change_set.base_text_string(cx).as_deref(),
2661            Some(new_diff_base.as_str())
2662        );
2663        git::diff::assert_hunks(
2664            change_set.diff_to_buffer.hunks_in_row_range(0..4, buffer),
2665            buffer,
2666            &new_diff_base,
2667            &[(2..3, "", "three\n")],
2668        );
2669    });
2670
2671    // Nested git dir
2672    let diff_base = "
2673        one
2674        three
2675    "
2676    .unindent();
2677
2678    let new_diff_base = "
2679        one
2680        two
2681    "
2682    .unindent();
2683
2684    client_a.fs().set_index_for_repo(
2685        Path::new("/dir/sub/.git"),
2686        &[(Path::new("b.txt"), diff_base.clone())],
2687    );
2688
2689    // Create the buffer
2690    let buffer_local_b = project_local
2691        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "sub/b.txt"), cx))
2692        .await
2693        .unwrap();
2694    let change_set_local_b = project_local
2695        .update(cx_a, |p, cx| {
2696            p.open_unstaged_changes(buffer_local_b.clone(), cx)
2697        })
2698        .await
2699        .unwrap();
2700
2701    // Wait for it to catch up to the new diff
2702    executor.run_until_parked();
2703    change_set_local_b.read_with(cx_a, |change_set, cx| {
2704        let buffer = buffer_local_b.read(cx);
2705        assert_eq!(
2706            change_set.base_text_string(cx).as_deref(),
2707            Some(diff_base.as_str())
2708        );
2709        git::diff::assert_hunks(
2710            change_set.diff_to_buffer.hunks_in_row_range(0..4, buffer),
2711            buffer,
2712            &diff_base,
2713            &[(1..2, "", "two\n")],
2714        );
2715    });
2716
2717    // Create remote buffer
2718    let buffer_remote_b = project_remote
2719        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "sub/b.txt"), cx))
2720        .await
2721        .unwrap();
2722    let change_set_remote_b = project_remote
2723        .update(cx_b, |p, cx| {
2724            p.open_unstaged_changes(buffer_remote_b.clone(), cx)
2725        })
2726        .await
2727        .unwrap();
2728
2729    executor.run_until_parked();
2730    change_set_remote_b.read_with(cx_b, |change_set, cx| {
2731        let buffer = buffer_remote_b.read(cx);
2732        assert_eq!(
2733            change_set.base_text_string(cx).as_deref(),
2734            Some(diff_base.as_str())
2735        );
2736        git::diff::assert_hunks(
2737            change_set.diff_to_buffer.hunks_in_row_range(0..4, buffer),
2738            buffer,
2739            &diff_base,
2740            &[(1..2, "", "two\n")],
2741        );
2742    });
2743
2744    // Update the staged text
2745    client_a.fs().set_index_for_repo(
2746        Path::new("/dir/sub/.git"),
2747        &[(Path::new("b.txt"), new_diff_base.clone())],
2748    );
2749
2750    // Wait for buffer_local_b to receive it
2751    executor.run_until_parked();
2752    change_set_local_b.read_with(cx_a, |change_set, cx| {
2753        let buffer = buffer_local_b.read(cx);
2754        assert_eq!(
2755            change_set.base_text_string(cx).as_deref(),
2756            Some(new_diff_base.as_str())
2757        );
2758        git::diff::assert_hunks(
2759            change_set.diff_to_buffer.hunks_in_row_range(0..4, buffer),
2760            buffer,
2761            &new_diff_base,
2762            &[(2..3, "", "three\n")],
2763        );
2764    });
2765
2766    change_set_remote_b.read_with(cx_b, |change_set, cx| {
2767        let buffer = buffer_remote_b.read(cx);
2768        assert_eq!(
2769            change_set.base_text_string(cx).as_deref(),
2770            Some(new_diff_base.as_str())
2771        );
2772        git::diff::assert_hunks(
2773            change_set.diff_to_buffer.hunks_in_row_range(0..4, buffer),
2774            buffer,
2775            &new_diff_base,
2776            &[(2..3, "", "three\n")],
2777        );
2778    });
2779}
2780
2781#[gpui::test]
2782async fn test_git_branch_name(
2783    executor: BackgroundExecutor,
2784    cx_a: &mut TestAppContext,
2785    cx_b: &mut TestAppContext,
2786    cx_c: &mut TestAppContext,
2787) {
2788    let mut server = TestServer::start(executor.clone()).await;
2789    let client_a = server.create_client(cx_a, "user_a").await;
2790    let client_b = server.create_client(cx_b, "user_b").await;
2791    let client_c = server.create_client(cx_c, "user_c").await;
2792    server
2793        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
2794        .await;
2795    let active_call_a = cx_a.read(ActiveCall::global);
2796
2797    client_a
2798        .fs()
2799        .insert_tree(
2800            "/dir",
2801            json!({
2802            ".git": {},
2803            }),
2804        )
2805        .await;
2806
2807    let (project_local, _worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2808    let project_id = active_call_a
2809        .update(cx_a, |call, cx| {
2810            call.share_project(project_local.clone(), cx)
2811        })
2812        .await
2813        .unwrap();
2814
2815    let project_remote = client_b.join_remote_project(project_id, cx_b).await;
2816    client_a
2817        .fs()
2818        .set_branch_name(Path::new("/dir/.git"), Some("branch-1"));
2819
2820    // Wait for it to catch up to the new branch
2821    executor.run_until_parked();
2822
2823    #[track_caller]
2824    fn assert_branch(branch_name: Option<impl Into<String>>, project: &Project, cx: &AppContext) {
2825        let branch_name = branch_name.map(Into::into);
2826        let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
2827        assert_eq!(worktrees.len(), 1);
2828        let worktree = worktrees[0].clone();
2829        let root_entry = worktree.read(cx).snapshot().root_git_entry().unwrap();
2830        assert_eq!(root_entry.branch(), branch_name.map(Into::into));
2831    }
2832
2833    // Smoke test branch reading
2834
2835    project_local.read_with(cx_a, |project, cx| {
2836        assert_branch(Some("branch-1"), project, cx)
2837    });
2838
2839    project_remote.read_with(cx_b, |project, cx| {
2840        assert_branch(Some("branch-1"), project, cx)
2841    });
2842
2843    client_a
2844        .fs()
2845        .set_branch_name(Path::new("/dir/.git"), Some("branch-2"));
2846
2847    // Wait for buffer_local_a to receive it
2848    executor.run_until_parked();
2849
2850    // Smoke test branch reading
2851
2852    project_local.read_with(cx_a, |project, cx| {
2853        assert_branch(Some("branch-2"), project, cx)
2854    });
2855
2856    project_remote.read_with(cx_b, |project, cx| {
2857        assert_branch(Some("branch-2"), project, cx)
2858    });
2859
2860    let project_remote_c = client_c.join_remote_project(project_id, cx_c).await;
2861    executor.run_until_parked();
2862
2863    project_remote_c.read_with(cx_c, |project, cx| {
2864        assert_branch(Some("branch-2"), project, cx)
2865    });
2866}
2867
2868#[gpui::test]
2869async fn test_git_status_sync(
2870    executor: BackgroundExecutor,
2871    cx_a: &mut TestAppContext,
2872    cx_b: &mut TestAppContext,
2873    cx_c: &mut TestAppContext,
2874) {
2875    let mut server = TestServer::start(executor.clone()).await;
2876    let client_a = server.create_client(cx_a, "user_a").await;
2877    let client_b = server.create_client(cx_b, "user_b").await;
2878    let client_c = server.create_client(cx_c, "user_c").await;
2879    server
2880        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
2881        .await;
2882    let active_call_a = cx_a.read(ActiveCall::global);
2883
2884    client_a
2885        .fs()
2886        .insert_tree(
2887            "/dir",
2888            json!({
2889            ".git": {},
2890            "a.txt": "a",
2891            "b.txt": "b",
2892            }),
2893        )
2894        .await;
2895
2896    const A_TXT: &str = "a.txt";
2897    const B_TXT: &str = "b.txt";
2898
2899    const A_STATUS_START: FileStatus = FileStatus::Tracked(TrackedStatus {
2900        index_status: StatusCode::Added,
2901        worktree_status: StatusCode::Modified,
2902    });
2903    const B_STATUS_START: FileStatus = FileStatus::Unmerged(UnmergedStatus {
2904        first_head: UnmergedStatusCode::Updated,
2905        second_head: UnmergedStatusCode::Deleted,
2906    });
2907
2908    client_a.fs().set_status_for_repo_via_git_operation(
2909        Path::new("/dir/.git"),
2910        &[
2911            (Path::new(A_TXT), A_STATUS_START),
2912            (Path::new(B_TXT), B_STATUS_START),
2913        ],
2914    );
2915
2916    let (project_local, _worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2917    let project_id = active_call_a
2918        .update(cx_a, |call, cx| {
2919            call.share_project(project_local.clone(), cx)
2920        })
2921        .await
2922        .unwrap();
2923
2924    let project_remote = client_b.join_remote_project(project_id, cx_b).await;
2925
2926    // Wait for it to catch up to the new status
2927    executor.run_until_parked();
2928
2929    #[track_caller]
2930    fn assert_status(
2931        file: &impl AsRef<Path>,
2932        status: Option<FileStatus>,
2933        project: &Project,
2934        cx: &AppContext,
2935    ) {
2936        let file = file.as_ref();
2937        let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
2938        assert_eq!(worktrees.len(), 1);
2939        let worktree = worktrees[0].clone();
2940        let snapshot = worktree.read(cx).snapshot();
2941        assert_eq!(snapshot.status_for_file(file), status);
2942    }
2943
2944    project_local.read_with(cx_a, |project, cx| {
2945        assert_status(&Path::new(A_TXT), Some(A_STATUS_START), project, cx);
2946        assert_status(&Path::new(B_TXT), Some(B_STATUS_START), project, cx);
2947    });
2948
2949    project_remote.read_with(cx_b, |project, cx| {
2950        assert_status(&Path::new(A_TXT), Some(A_STATUS_START), project, cx);
2951        assert_status(&Path::new(B_TXT), Some(B_STATUS_START), project, cx);
2952    });
2953
2954    const A_STATUS_END: FileStatus = FileStatus::Tracked(TrackedStatus {
2955        index_status: StatusCode::Added,
2956        worktree_status: StatusCode::Unmodified,
2957    });
2958    const B_STATUS_END: FileStatus = FileStatus::Tracked(TrackedStatus {
2959        index_status: StatusCode::Deleted,
2960        worktree_status: StatusCode::Unmodified,
2961    });
2962
2963    client_a.fs().set_status_for_repo_via_working_copy_change(
2964        Path::new("/dir/.git"),
2965        &[
2966            (Path::new(A_TXT), A_STATUS_END),
2967            (Path::new(B_TXT), B_STATUS_END),
2968        ],
2969    );
2970
2971    // Wait for buffer_local_a to receive it
2972    executor.run_until_parked();
2973
2974    // Smoke test status reading
2975
2976    project_local.read_with(cx_a, |project, cx| {
2977        assert_status(&Path::new(A_TXT), Some(A_STATUS_END), project, cx);
2978        assert_status(&Path::new(B_TXT), Some(B_STATUS_END), project, cx);
2979    });
2980
2981    project_remote.read_with(cx_b, |project, cx| {
2982        assert_status(&Path::new(A_TXT), Some(A_STATUS_END), project, cx);
2983        assert_status(&Path::new(B_TXT), Some(B_STATUS_END), project, cx);
2984    });
2985
2986    // And synchronization while joining
2987    let project_remote_c = client_c.join_remote_project(project_id, cx_c).await;
2988    executor.run_until_parked();
2989
2990    project_remote_c.read_with(cx_c, |project, cx| {
2991        assert_status(&Path::new(A_TXT), Some(A_STATUS_END), project, cx);
2992        assert_status(&Path::new(B_TXT), Some(B_STATUS_END), project, cx);
2993    });
2994}
2995
2996#[gpui::test(iterations = 10)]
2997async fn test_fs_operations(
2998    executor: BackgroundExecutor,
2999    cx_a: &mut TestAppContext,
3000    cx_b: &mut TestAppContext,
3001) {
3002    let mut server = TestServer::start(executor.clone()).await;
3003    let client_a = server.create_client(cx_a, "user_a").await;
3004    let client_b = server.create_client(cx_b, "user_b").await;
3005    server
3006        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3007        .await;
3008    let active_call_a = cx_a.read(ActiveCall::global);
3009
3010    client_a
3011        .fs()
3012        .insert_tree(
3013            "/dir",
3014            json!({
3015                "a.txt": "a-contents",
3016                "b.txt": "b-contents",
3017            }),
3018        )
3019        .await;
3020    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3021    let project_id = active_call_a
3022        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3023        .await
3024        .unwrap();
3025    let project_b = client_b.join_remote_project(project_id, cx_b).await;
3026
3027    let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
3028    let worktree_b = project_b.read_with(cx_b, |project, cx| project.worktrees(cx).next().unwrap());
3029
3030    let entry = project_b
3031        .update(cx_b, |project, cx| {
3032            project.create_entry((worktree_id, "c.txt"), false, cx)
3033        })
3034        .await
3035        .unwrap()
3036        .to_included()
3037        .unwrap();
3038
3039    worktree_a.read_with(cx_a, |worktree, _| {
3040        assert_eq!(
3041            worktree
3042                .paths()
3043                .map(|p| p.to_string_lossy())
3044                .collect::<Vec<_>>(),
3045            ["a.txt", "b.txt", "c.txt"]
3046        );
3047    });
3048
3049    worktree_b.read_with(cx_b, |worktree, _| {
3050        assert_eq!(
3051            worktree
3052                .paths()
3053                .map(|p| p.to_string_lossy())
3054                .collect::<Vec<_>>(),
3055            ["a.txt", "b.txt", "c.txt"]
3056        );
3057    });
3058
3059    project_b
3060        .update(cx_b, |project, cx| {
3061            project.rename_entry(entry.id, Path::new("d.txt"), cx)
3062        })
3063        .await
3064        .unwrap()
3065        .to_included()
3066        .unwrap();
3067
3068    worktree_a.read_with(cx_a, |worktree, _| {
3069        assert_eq!(
3070            worktree
3071                .paths()
3072                .map(|p| p.to_string_lossy())
3073                .collect::<Vec<_>>(),
3074            ["a.txt", "b.txt", "d.txt"]
3075        );
3076    });
3077
3078    worktree_b.read_with(cx_b, |worktree, _| {
3079        assert_eq!(
3080            worktree
3081                .paths()
3082                .map(|p| p.to_string_lossy())
3083                .collect::<Vec<_>>(),
3084            ["a.txt", "b.txt", "d.txt"]
3085        );
3086    });
3087
3088    let dir_entry = project_b
3089        .update(cx_b, |project, cx| {
3090            project.create_entry((worktree_id, "DIR"), true, cx)
3091        })
3092        .await
3093        .unwrap()
3094        .to_included()
3095        .unwrap();
3096
3097    worktree_a.read_with(cx_a, |worktree, _| {
3098        assert_eq!(
3099            worktree
3100                .paths()
3101                .map(|p| p.to_string_lossy())
3102                .collect::<Vec<_>>(),
3103            ["DIR", "a.txt", "b.txt", "d.txt"]
3104        );
3105    });
3106
3107    worktree_b.read_with(cx_b, |worktree, _| {
3108        assert_eq!(
3109            worktree
3110                .paths()
3111                .map(|p| p.to_string_lossy())
3112                .collect::<Vec<_>>(),
3113            ["DIR", "a.txt", "b.txt", "d.txt"]
3114        );
3115    });
3116
3117    project_b
3118        .update(cx_b, |project, cx| {
3119            project.create_entry((worktree_id, "DIR/e.txt"), false, cx)
3120        })
3121        .await
3122        .unwrap()
3123        .to_included()
3124        .unwrap();
3125
3126    project_b
3127        .update(cx_b, |project, cx| {
3128            project.create_entry((worktree_id, "DIR/SUBDIR"), true, cx)
3129        })
3130        .await
3131        .unwrap()
3132        .to_included()
3133        .unwrap();
3134
3135    project_b
3136        .update(cx_b, |project, cx| {
3137            project.create_entry((worktree_id, "DIR/SUBDIR/f.txt"), false, cx)
3138        })
3139        .await
3140        .unwrap()
3141        .to_included()
3142        .unwrap();
3143
3144    worktree_a.read_with(cx_a, |worktree, _| {
3145        assert_eq!(
3146            worktree
3147                .paths()
3148                .map(|p| p.to_string_lossy())
3149                .collect::<Vec<_>>(),
3150            [
3151                "DIR",
3152                "DIR/SUBDIR",
3153                "DIR/SUBDIR/f.txt",
3154                "DIR/e.txt",
3155                "a.txt",
3156                "b.txt",
3157                "d.txt"
3158            ]
3159        );
3160    });
3161
3162    worktree_b.read_with(cx_b, |worktree, _| {
3163        assert_eq!(
3164            worktree
3165                .paths()
3166                .map(|p| p.to_string_lossy())
3167                .collect::<Vec<_>>(),
3168            [
3169                "DIR",
3170                "DIR/SUBDIR",
3171                "DIR/SUBDIR/f.txt",
3172                "DIR/e.txt",
3173                "a.txt",
3174                "b.txt",
3175                "d.txt"
3176            ]
3177        );
3178    });
3179
3180    project_b
3181        .update(cx_b, |project, cx| {
3182            project.copy_entry(entry.id, None, Path::new("f.txt"), cx)
3183        })
3184        .await
3185        .unwrap()
3186        .unwrap();
3187
3188    worktree_a.read_with(cx_a, |worktree, _| {
3189        assert_eq!(
3190            worktree
3191                .paths()
3192                .map(|p| p.to_string_lossy())
3193                .collect::<Vec<_>>(),
3194            [
3195                "DIR",
3196                "DIR/SUBDIR",
3197                "DIR/SUBDIR/f.txt",
3198                "DIR/e.txt",
3199                "a.txt",
3200                "b.txt",
3201                "d.txt",
3202                "f.txt"
3203            ]
3204        );
3205    });
3206
3207    worktree_b.read_with(cx_b, |worktree, _| {
3208        assert_eq!(
3209            worktree
3210                .paths()
3211                .map(|p| p.to_string_lossy())
3212                .collect::<Vec<_>>(),
3213            [
3214                "DIR",
3215                "DIR/SUBDIR",
3216                "DIR/SUBDIR/f.txt",
3217                "DIR/e.txt",
3218                "a.txt",
3219                "b.txt",
3220                "d.txt",
3221                "f.txt"
3222            ]
3223        );
3224    });
3225
3226    project_b
3227        .update(cx_b, |project, cx| {
3228            project.delete_entry(dir_entry.id, false, cx).unwrap()
3229        })
3230        .await
3231        .unwrap();
3232    executor.run_until_parked();
3233
3234    worktree_a.read_with(cx_a, |worktree, _| {
3235        assert_eq!(
3236            worktree
3237                .paths()
3238                .map(|p| p.to_string_lossy())
3239                .collect::<Vec<_>>(),
3240            ["a.txt", "b.txt", "d.txt", "f.txt"]
3241        );
3242    });
3243
3244    worktree_b.read_with(cx_b, |worktree, _| {
3245        assert_eq!(
3246            worktree
3247                .paths()
3248                .map(|p| p.to_string_lossy())
3249                .collect::<Vec<_>>(),
3250            ["a.txt", "b.txt", "d.txt", "f.txt"]
3251        );
3252    });
3253
3254    project_b
3255        .update(cx_b, |project, cx| {
3256            project.delete_entry(entry.id, false, cx).unwrap()
3257        })
3258        .await
3259        .unwrap();
3260
3261    worktree_a.read_with(cx_a, |worktree, _| {
3262        assert_eq!(
3263            worktree
3264                .paths()
3265                .map(|p| p.to_string_lossy())
3266                .collect::<Vec<_>>(),
3267            ["a.txt", "b.txt", "f.txt"]
3268        );
3269    });
3270
3271    worktree_b.read_with(cx_b, |worktree, _| {
3272        assert_eq!(
3273            worktree
3274                .paths()
3275                .map(|p| p.to_string_lossy())
3276                .collect::<Vec<_>>(),
3277            ["a.txt", "b.txt", "f.txt"]
3278        );
3279    });
3280}
3281
3282#[gpui::test(iterations = 10)]
3283async fn test_local_settings(
3284    executor: BackgroundExecutor,
3285    cx_a: &mut TestAppContext,
3286    cx_b: &mut TestAppContext,
3287) {
3288    let mut server = TestServer::start(executor.clone()).await;
3289    let client_a = server.create_client(cx_a, "user_a").await;
3290    let client_b = server.create_client(cx_b, "user_b").await;
3291    server
3292        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3293        .await;
3294    let active_call_a = cx_a.read(ActiveCall::global);
3295
3296    // As client A, open a project that contains some local settings files
3297    client_a
3298        .fs()
3299        .insert_tree(
3300            "/dir",
3301            json!({
3302                ".zed": {
3303                    "settings.json": r#"{ "tab_size": 2 }"#
3304                },
3305                "a": {
3306                    ".zed": {
3307                        "settings.json": r#"{ "tab_size": 8 }"#
3308                    },
3309                    "a.txt": "a-contents",
3310                },
3311                "b": {
3312                    "b.txt": "b-contents",
3313                }
3314            }),
3315        )
3316        .await;
3317    let (project_a, _) = client_a.build_local_project("/dir", cx_a).await;
3318    executor.run_until_parked();
3319    let project_id = active_call_a
3320        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3321        .await
3322        .unwrap();
3323    executor.run_until_parked();
3324
3325    // As client B, join that project and observe the local settings.
3326    let project_b = client_b.join_remote_project(project_id, cx_b).await;
3327
3328    let worktree_b = project_b.read_with(cx_b, |project, cx| project.worktrees(cx).next().unwrap());
3329    executor.run_until_parked();
3330    cx_b.read(|cx| {
3331        let store = cx.global::<SettingsStore>();
3332        assert_eq!(
3333            store
3334                .local_settings(worktree_b.read(cx).id())
3335                .collect::<Vec<_>>(),
3336            &[
3337                (Path::new("").into(), r#"{"tab_size":2}"#.to_string()),
3338                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3339            ]
3340        )
3341    });
3342
3343    // As client A, update a settings file. As Client B, see the changed settings.
3344    client_a
3345        .fs()
3346        .insert_file("/dir/.zed/settings.json", r#"{}"#.into())
3347        .await;
3348    executor.run_until_parked();
3349    cx_b.read(|cx| {
3350        let store = cx.global::<SettingsStore>();
3351        assert_eq!(
3352            store
3353                .local_settings(worktree_b.read(cx).id())
3354                .collect::<Vec<_>>(),
3355            &[
3356                (Path::new("").into(), r#"{}"#.to_string()),
3357                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3358            ]
3359        )
3360    });
3361
3362    // As client A, create and remove some settings files. As client B, see the changed settings.
3363    client_a
3364        .fs()
3365        .remove_file("/dir/.zed/settings.json".as_ref(), Default::default())
3366        .await
3367        .unwrap();
3368    client_a
3369        .fs()
3370        .create_dir("/dir/b/.zed".as_ref())
3371        .await
3372        .unwrap();
3373    client_a
3374        .fs()
3375        .insert_file("/dir/b/.zed/settings.json", r#"{"tab_size": 4}"#.into())
3376        .await;
3377    executor.run_until_parked();
3378    cx_b.read(|cx| {
3379        let store = cx.global::<SettingsStore>();
3380        assert_eq!(
3381            store
3382                .local_settings(worktree_b.read(cx).id())
3383                .collect::<Vec<_>>(),
3384            &[
3385                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3386                (Path::new("b").into(), r#"{"tab_size":4}"#.to_string()),
3387            ]
3388        )
3389    });
3390
3391    // As client B, disconnect.
3392    server.forbid_connections();
3393    server.disconnect_client(client_b.peer_id().unwrap());
3394
3395    // As client A, change and remove settings files while client B is disconnected.
3396    client_a
3397        .fs()
3398        .insert_file("/dir/a/.zed/settings.json", r#"{"hard_tabs":true}"#.into())
3399        .await;
3400    client_a
3401        .fs()
3402        .remove_file("/dir/b/.zed/settings.json".as_ref(), Default::default())
3403        .await
3404        .unwrap();
3405    executor.run_until_parked();
3406
3407    // As client B, reconnect and see the changed settings.
3408    server.allow_connections();
3409    executor.advance_clock(RECEIVE_TIMEOUT);
3410    cx_b.read(|cx| {
3411        let store = cx.global::<SettingsStore>();
3412        assert_eq!(
3413            store
3414                .local_settings(worktree_b.read(cx).id())
3415                .collect::<Vec<_>>(),
3416            &[(Path::new("a").into(), r#"{"hard_tabs":true}"#.to_string()),]
3417        )
3418    });
3419}
3420
3421#[gpui::test(iterations = 10)]
3422async fn test_buffer_conflict_after_save(
3423    executor: BackgroundExecutor,
3424    cx_a: &mut TestAppContext,
3425    cx_b: &mut TestAppContext,
3426) {
3427    let mut server = TestServer::start(executor.clone()).await;
3428    let client_a = server.create_client(cx_a, "user_a").await;
3429    let client_b = server.create_client(cx_b, "user_b").await;
3430    server
3431        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3432        .await;
3433    let active_call_a = cx_a.read(ActiveCall::global);
3434
3435    client_a
3436        .fs()
3437        .insert_tree(
3438            "/dir",
3439            json!({
3440                "a.txt": "a-contents",
3441            }),
3442        )
3443        .await;
3444    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3445    let project_id = active_call_a
3446        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3447        .await
3448        .unwrap();
3449    let project_b = client_b.join_remote_project(project_id, cx_b).await;
3450
3451    // Open a buffer as client B
3452    let buffer_b = project_b
3453        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3454        .await
3455        .unwrap();
3456
3457    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "world ")], None, cx));
3458
3459    buffer_b.read_with(cx_b, |buf, _| {
3460        assert!(buf.is_dirty());
3461        assert!(!buf.has_conflict());
3462    });
3463
3464    project_b
3465        .update(cx_b, |project, cx| {
3466            project.save_buffer(buffer_b.clone(), cx)
3467        })
3468        .await
3469        .unwrap();
3470
3471    buffer_b.read_with(cx_b, |buffer_b, _| assert!(!buffer_b.is_dirty()));
3472
3473    buffer_b.read_with(cx_b, |buf, _| {
3474        assert!(!buf.has_conflict());
3475    });
3476
3477    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "hello ")], None, cx));
3478
3479    buffer_b.read_with(cx_b, |buf, _| {
3480        assert!(buf.is_dirty());
3481        assert!(!buf.has_conflict());
3482    });
3483}
3484
3485#[gpui::test(iterations = 10)]
3486async fn test_buffer_reloading(
3487    executor: BackgroundExecutor,
3488    cx_a: &mut TestAppContext,
3489    cx_b: &mut TestAppContext,
3490) {
3491    let mut server = TestServer::start(executor.clone()).await;
3492    let client_a = server.create_client(cx_a, "user_a").await;
3493    let client_b = server.create_client(cx_b, "user_b").await;
3494    server
3495        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3496        .await;
3497    let active_call_a = cx_a.read(ActiveCall::global);
3498
3499    client_a
3500        .fs()
3501        .insert_tree(
3502            "/dir",
3503            json!({
3504                "a.txt": "a\nb\nc",
3505            }),
3506        )
3507        .await;
3508    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3509    let project_id = active_call_a
3510        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3511        .await
3512        .unwrap();
3513    let project_b = client_b.join_remote_project(project_id, cx_b).await;
3514
3515    // Open a buffer as client B
3516    let buffer_b = project_b
3517        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3518        .await
3519        .unwrap();
3520
3521    buffer_b.read_with(cx_b, |buf, _| {
3522        assert!(!buf.is_dirty());
3523        assert!(!buf.has_conflict());
3524        assert_eq!(buf.line_ending(), LineEnding::Unix);
3525    });
3526
3527    let new_contents = Rope::from("d\ne\nf");
3528    client_a
3529        .fs()
3530        .save("/dir/a.txt".as_ref(), &new_contents, LineEnding::Windows)
3531        .await
3532        .unwrap();
3533
3534    executor.run_until_parked();
3535
3536    buffer_b.read_with(cx_b, |buf, _| {
3537        assert_eq!(buf.text(), new_contents.to_string());
3538        assert!(!buf.is_dirty());
3539        assert!(!buf.has_conflict());
3540        assert_eq!(buf.line_ending(), LineEnding::Windows);
3541    });
3542}
3543
3544#[gpui::test(iterations = 10)]
3545async fn test_editing_while_guest_opens_buffer(
3546    executor: BackgroundExecutor,
3547    cx_a: &mut TestAppContext,
3548    cx_b: &mut TestAppContext,
3549) {
3550    let mut server = TestServer::start(executor.clone()).await;
3551    let client_a = server.create_client(cx_a, "user_a").await;
3552    let client_b = server.create_client(cx_b, "user_b").await;
3553    server
3554        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3555        .await;
3556    let active_call_a = cx_a.read(ActiveCall::global);
3557
3558    client_a
3559        .fs()
3560        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3561        .await;
3562    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3563    let project_id = active_call_a
3564        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3565        .await
3566        .unwrap();
3567    let project_b = client_b.join_remote_project(project_id, cx_b).await;
3568
3569    // Open a buffer as client A
3570    let buffer_a = project_a
3571        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3572        .await
3573        .unwrap();
3574
3575    // Start opening the same buffer as client B
3576    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx));
3577    let buffer_b = cx_b.executor().spawn(open_buffer);
3578
3579    // Edit the buffer as client A while client B is still opening it.
3580    cx_b.executor().simulate_random_delay().await;
3581    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "X")], None, cx));
3582    cx_b.executor().simulate_random_delay().await;
3583    buffer_a.update(cx_a, |buf, cx| buf.edit([(1..1, "Y")], None, cx));
3584
3585    let text = buffer_a.read_with(cx_a, |buf, _| buf.text());
3586    let buffer_b = buffer_b.await.unwrap();
3587    executor.run_until_parked();
3588
3589    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), text));
3590}
3591
3592#[gpui::test(iterations = 10)]
3593async fn test_leaving_worktree_while_opening_buffer(
3594    executor: BackgroundExecutor,
3595    cx_a: &mut TestAppContext,
3596    cx_b: &mut TestAppContext,
3597) {
3598    let mut server = TestServer::start(executor.clone()).await;
3599    let client_a = server.create_client(cx_a, "user_a").await;
3600    let client_b = server.create_client(cx_b, "user_b").await;
3601    server
3602        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3603        .await;
3604    let active_call_a = cx_a.read(ActiveCall::global);
3605
3606    client_a
3607        .fs()
3608        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3609        .await;
3610    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3611    let project_id = active_call_a
3612        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3613        .await
3614        .unwrap();
3615    let project_b = client_b.join_remote_project(project_id, cx_b).await;
3616
3617    // See that a guest has joined as client A.
3618    executor.run_until_parked();
3619
3620    project_a.read_with(cx_a, |p, _| assert_eq!(p.collaborators().len(), 1));
3621
3622    // Begin opening a buffer as client B, but leave the project before the open completes.
3623    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx));
3624    let buffer_b = cx_b.executor().spawn(open_buffer);
3625    cx_b.update(|_| drop(project_b));
3626    drop(buffer_b);
3627
3628    // See that the guest has left.
3629    executor.run_until_parked();
3630
3631    project_a.read_with(cx_a, |p, _| assert!(p.collaborators().is_empty()));
3632}
3633
3634#[gpui::test(iterations = 10)]
3635async fn test_canceling_buffer_opening(
3636    executor: BackgroundExecutor,
3637    cx_a: &mut TestAppContext,
3638    cx_b: &mut TestAppContext,
3639) {
3640    let mut server = TestServer::start(executor.clone()).await;
3641    let client_a = server.create_client(cx_a, "user_a").await;
3642    let client_b = server.create_client(cx_b, "user_b").await;
3643    server
3644        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3645        .await;
3646    let active_call_a = cx_a.read(ActiveCall::global);
3647
3648    client_a
3649        .fs()
3650        .insert_tree(
3651            "/dir",
3652            json!({
3653                "a.txt": "abc",
3654            }),
3655        )
3656        .await;
3657    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3658    let project_id = active_call_a
3659        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3660        .await
3661        .unwrap();
3662    let project_b = client_b.join_remote_project(project_id, cx_b).await;
3663
3664    let buffer_a = project_a
3665        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3666        .await
3667        .unwrap();
3668
3669    // Open a buffer as client B but cancel after a random amount of time.
3670    let buffer_b = project_b.update(cx_b, |p, cx| {
3671        p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3672    });
3673    executor.simulate_random_delay().await;
3674    drop(buffer_b);
3675
3676    // Try opening the same buffer again as client B, and ensure we can
3677    // still do it despite the cancellation above.
3678    let buffer_b = project_b
3679        .update(cx_b, |p, cx| {
3680            p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3681        })
3682        .await
3683        .unwrap();
3684
3685    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "abc"));
3686}
3687
3688#[gpui::test(iterations = 10)]
3689async fn test_leaving_project(
3690    executor: BackgroundExecutor,
3691    cx_a: &mut TestAppContext,
3692    cx_b: &mut TestAppContext,
3693    cx_c: &mut TestAppContext,
3694) {
3695    let mut server = TestServer::start(executor.clone()).await;
3696    let client_a = server.create_client(cx_a, "user_a").await;
3697    let client_b = server.create_client(cx_b, "user_b").await;
3698    let client_c = server.create_client(cx_c, "user_c").await;
3699    server
3700        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3701        .await;
3702    let active_call_a = cx_a.read(ActiveCall::global);
3703
3704    client_a
3705        .fs()
3706        .insert_tree(
3707            "/a",
3708            json!({
3709                "a.txt": "a-contents",
3710                "b.txt": "b-contents",
3711            }),
3712        )
3713        .await;
3714    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
3715    let project_id = active_call_a
3716        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3717        .await
3718        .unwrap();
3719    let project_b1 = client_b.join_remote_project(project_id, cx_b).await;
3720    let project_c = client_c.join_remote_project(project_id, cx_c).await;
3721
3722    // Client A sees that a guest has joined.
3723    executor.run_until_parked();
3724
3725    project_a.read_with(cx_a, |project, _| {
3726        assert_eq!(project.collaborators().len(), 2);
3727    });
3728
3729    project_b1.read_with(cx_b, |project, _| {
3730        assert_eq!(project.collaborators().len(), 2);
3731    });
3732
3733    project_c.read_with(cx_c, |project, _| {
3734        assert_eq!(project.collaborators().len(), 2);
3735    });
3736
3737    // Client B opens a buffer.
3738    let buffer_b1 = project_b1
3739        .update(cx_b, |project, cx| {
3740            let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
3741            project.open_buffer((worktree_id, "a.txt"), cx)
3742        })
3743        .await
3744        .unwrap();
3745
3746    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3747
3748    // Drop client B's project and ensure client A and client C observe client B leaving.
3749    cx_b.update(|_| drop(project_b1));
3750    executor.run_until_parked();
3751
3752    project_a.read_with(cx_a, |project, _| {
3753        assert_eq!(project.collaborators().len(), 1);
3754    });
3755
3756    project_c.read_with(cx_c, |project, _| {
3757        assert_eq!(project.collaborators().len(), 1);
3758    });
3759
3760    // Client B re-joins the project and can open buffers as before.
3761    let project_b2 = client_b.join_remote_project(project_id, cx_b).await;
3762    executor.run_until_parked();
3763
3764    project_a.read_with(cx_a, |project, _| {
3765        assert_eq!(project.collaborators().len(), 2);
3766    });
3767
3768    project_b2.read_with(cx_b, |project, _| {
3769        assert_eq!(project.collaborators().len(), 2);
3770    });
3771
3772    project_c.read_with(cx_c, |project, _| {
3773        assert_eq!(project.collaborators().len(), 2);
3774    });
3775
3776    let buffer_b2 = project_b2
3777        .update(cx_b, |project, cx| {
3778            let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
3779            project.open_buffer((worktree_id, "a.txt"), cx)
3780        })
3781        .await
3782        .unwrap();
3783
3784    buffer_b2.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3785
3786    project_a.read_with(cx_a, |project, _| {
3787        assert_eq!(project.collaborators().len(), 2);
3788    });
3789
3790    // Drop client B's connection and ensure client A and client C observe client B leaving.
3791    client_b.disconnect(&cx_b.to_async());
3792    executor.advance_clock(RECONNECT_TIMEOUT);
3793
3794    project_a.read_with(cx_a, |project, _| {
3795        assert_eq!(project.collaborators().len(), 1);
3796    });
3797
3798    project_b2.read_with(cx_b, |project, cx| {
3799        assert!(project.is_disconnected(cx));
3800    });
3801
3802    project_c.read_with(cx_c, |project, _| {
3803        assert_eq!(project.collaborators().len(), 1);
3804    });
3805
3806    // Client B can't join the project, unless they re-join the room.
3807    cx_b.spawn(|cx| {
3808        Project::in_room(
3809            project_id,
3810            client_b.app_state.client.clone(),
3811            client_b.user_store().clone(),
3812            client_b.language_registry().clone(),
3813            FakeFs::new(cx.background_executor().clone()),
3814            cx,
3815        )
3816    })
3817    .await
3818    .unwrap_err();
3819
3820    // Simulate connection loss for client C and ensure client A observes client C leaving the project.
3821    client_c.wait_for_current_user(cx_c).await;
3822    server.forbid_connections();
3823    server.disconnect_client(client_c.peer_id().unwrap());
3824    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
3825    executor.run_until_parked();
3826
3827    project_a.read_with(cx_a, |project, _| {
3828        assert_eq!(project.collaborators().len(), 0);
3829    });
3830
3831    project_b2.read_with(cx_b, |project, cx| {
3832        assert!(project.is_disconnected(cx));
3833    });
3834
3835    project_c.read_with(cx_c, |project, cx| {
3836        assert!(project.is_disconnected(cx));
3837    });
3838}
3839
3840#[gpui::test(iterations = 10)]
3841async fn test_collaborating_with_diagnostics(
3842    executor: BackgroundExecutor,
3843    cx_a: &mut TestAppContext,
3844    cx_b: &mut TestAppContext,
3845    cx_c: &mut TestAppContext,
3846) {
3847    let mut server = TestServer::start(executor.clone()).await;
3848    let client_a = server.create_client(cx_a, "user_a").await;
3849    let client_b = server.create_client(cx_b, "user_b").await;
3850    let client_c = server.create_client(cx_c, "user_c").await;
3851    server
3852        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3853        .await;
3854    let active_call_a = cx_a.read(ActiveCall::global);
3855
3856    client_a.language_registry().add(Arc::new(Language::new(
3857        LanguageConfig {
3858            name: "Rust".into(),
3859            matcher: LanguageMatcher {
3860                path_suffixes: vec!["rs".to_string()],
3861                ..Default::default()
3862            },
3863            ..Default::default()
3864        },
3865        Some(tree_sitter_rust::LANGUAGE.into()),
3866    )));
3867    let mut fake_language_servers = client_a
3868        .language_registry()
3869        .register_fake_lsp("Rust", Default::default());
3870
3871    // Share a project as client A
3872    client_a
3873        .fs()
3874        .insert_tree(
3875            "/a",
3876            json!({
3877                "a.rs": "let one = two",
3878                "other.rs": "",
3879            }),
3880        )
3881        .await;
3882    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3883
3884    // Cause the language server to start.
3885    let _buffer = project_a
3886        .update(cx_a, |project, cx| {
3887            project.open_local_buffer_with_lsp("/a/other.rs", cx)
3888        })
3889        .await
3890        .unwrap();
3891
3892    // Simulate a language server reporting errors for a file.
3893    let mut fake_language_server = fake_language_servers.next().await.unwrap();
3894    fake_language_server
3895        .receive_notification::<lsp::notification::DidOpenTextDocument>()
3896        .await;
3897    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3898        &lsp::PublishDiagnosticsParams {
3899            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3900            version: None,
3901            diagnostics: vec![lsp::Diagnostic {
3902                severity: Some(lsp::DiagnosticSeverity::WARNING),
3903                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3904                message: "message 0".to_string(),
3905                ..Default::default()
3906            }],
3907        },
3908    );
3909
3910    // Client A shares the project and, simultaneously, the language server
3911    // publishes a diagnostic. This is done to ensure that the server always
3912    // observes the latest diagnostics for a worktree.
3913    let project_id = active_call_a
3914        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3915        .await
3916        .unwrap();
3917    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3918        &lsp::PublishDiagnosticsParams {
3919            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3920            version: None,
3921            diagnostics: vec![lsp::Diagnostic {
3922                severity: Some(lsp::DiagnosticSeverity::ERROR),
3923                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3924                message: "message 1".to_string(),
3925                ..Default::default()
3926            }],
3927        },
3928    );
3929
3930    // Join the worktree as client B.
3931    let project_b = client_b.join_remote_project(project_id, cx_b).await;
3932
3933    // Wait for server to see the diagnostics update.
3934    executor.run_until_parked();
3935
3936    // Ensure client B observes the new diagnostics.
3937
3938    project_b.read_with(cx_b, |project, cx| {
3939        assert_eq!(
3940            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
3941            &[(
3942                ProjectPath {
3943                    worktree_id,
3944                    path: Arc::from(Path::new("a.rs")),
3945                },
3946                LanguageServerId(0),
3947                DiagnosticSummary {
3948                    error_count: 1,
3949                    warning_count: 0,
3950                },
3951            )]
3952        )
3953    });
3954
3955    // Join project as client C and observe the diagnostics.
3956    let project_c = client_c.join_remote_project(project_id, cx_c).await;
3957    executor.run_until_parked();
3958    let project_c_diagnostic_summaries =
3959        Rc::new(RefCell::new(project_c.read_with(cx_c, |project, cx| {
3960            project.diagnostic_summaries(false, cx).collect::<Vec<_>>()
3961        })));
3962    project_c.update(cx_c, |_, cx| {
3963        let summaries = project_c_diagnostic_summaries.clone();
3964        cx.subscribe(&project_c, {
3965            move |p, _, event, cx| {
3966                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
3967                    *summaries.borrow_mut() = p.diagnostic_summaries(false, cx).collect();
3968                }
3969            }
3970        })
3971        .detach();
3972    });
3973
3974    executor.run_until_parked();
3975    assert_eq!(
3976        project_c_diagnostic_summaries.borrow().as_slice(),
3977        &[(
3978            ProjectPath {
3979                worktree_id,
3980                path: Arc::from(Path::new("a.rs")),
3981            },
3982            LanguageServerId(0),
3983            DiagnosticSummary {
3984                error_count: 1,
3985                warning_count: 0,
3986            },
3987        )]
3988    );
3989
3990    // Simulate a language server reporting more errors for a file.
3991    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3992        &lsp::PublishDiagnosticsParams {
3993            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3994            version: None,
3995            diagnostics: vec![
3996                lsp::Diagnostic {
3997                    severity: Some(lsp::DiagnosticSeverity::ERROR),
3998                    range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3999                    message: "message 1".to_string(),
4000                    ..Default::default()
4001                },
4002                lsp::Diagnostic {
4003                    severity: Some(lsp::DiagnosticSeverity::WARNING),
4004                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 13)),
4005                    message: "message 2".to_string(),
4006                    ..Default::default()
4007                },
4008            ],
4009        },
4010    );
4011
4012    // Clients B and C get the updated summaries
4013    executor.run_until_parked();
4014
4015    project_b.read_with(cx_b, |project, cx| {
4016        assert_eq!(
4017            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4018            [(
4019                ProjectPath {
4020                    worktree_id,
4021                    path: Arc::from(Path::new("a.rs")),
4022                },
4023                LanguageServerId(0),
4024                DiagnosticSummary {
4025                    error_count: 1,
4026                    warning_count: 1,
4027                },
4028            )]
4029        );
4030    });
4031
4032    project_c.read_with(cx_c, |project, cx| {
4033        assert_eq!(
4034            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4035            [(
4036                ProjectPath {
4037                    worktree_id,
4038                    path: Arc::from(Path::new("a.rs")),
4039                },
4040                LanguageServerId(0),
4041                DiagnosticSummary {
4042                    error_count: 1,
4043                    warning_count: 1,
4044                },
4045            )]
4046        );
4047    });
4048
4049    // Open the file with the errors on client B. They should be present.
4050    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4051    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4052
4053    buffer_b.read_with(cx_b, |buffer, _| {
4054        assert_eq!(
4055            buffer
4056                .snapshot()
4057                .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
4058                .collect::<Vec<_>>(),
4059            &[
4060                DiagnosticEntry {
4061                    range: Point::new(0, 4)..Point::new(0, 7),
4062                    diagnostic: Diagnostic {
4063                        group_id: 2,
4064                        message: "message 1".to_string(),
4065                        severity: lsp::DiagnosticSeverity::ERROR,
4066                        is_primary: true,
4067                        ..Default::default()
4068                    }
4069                },
4070                DiagnosticEntry {
4071                    range: Point::new(0, 10)..Point::new(0, 13),
4072                    diagnostic: Diagnostic {
4073                        group_id: 3,
4074                        severity: lsp::DiagnosticSeverity::WARNING,
4075                        message: "message 2".to_string(),
4076                        is_primary: true,
4077                        ..Default::default()
4078                    }
4079                }
4080            ]
4081        );
4082    });
4083
4084    // Simulate a language server reporting no errors for a file.
4085    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
4086        &lsp::PublishDiagnosticsParams {
4087            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
4088            version: None,
4089            diagnostics: vec![],
4090        },
4091    );
4092    executor.run_until_parked();
4093
4094    project_a.read_with(cx_a, |project, cx| {
4095        assert_eq!(
4096            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4097            []
4098        )
4099    });
4100
4101    project_b.read_with(cx_b, |project, cx| {
4102        assert_eq!(
4103            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4104            []
4105        )
4106    });
4107
4108    project_c.read_with(cx_c, |project, cx| {
4109        assert_eq!(
4110            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4111            []
4112        )
4113    });
4114}
4115
4116#[gpui::test(iterations = 10)]
4117async fn test_collaborating_with_lsp_progress_updates_and_diagnostics_ordering(
4118    executor: BackgroundExecutor,
4119    cx_a: &mut TestAppContext,
4120    cx_b: &mut TestAppContext,
4121) {
4122    let mut server = TestServer::start(executor.clone()).await;
4123    let client_a = server.create_client(cx_a, "user_a").await;
4124    let client_b = server.create_client(cx_b, "user_b").await;
4125    server
4126        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4127        .await;
4128
4129    client_a.language_registry().add(rust_lang());
4130    let mut fake_language_servers = client_a.language_registry().register_fake_lsp(
4131        "Rust",
4132        FakeLspAdapter {
4133            disk_based_diagnostics_progress_token: Some("the-disk-based-token".into()),
4134            disk_based_diagnostics_sources: vec!["the-disk-based-diagnostics-source".into()],
4135            ..Default::default()
4136        },
4137    );
4138
4139    let file_names = &["one.rs", "two.rs", "three.rs", "four.rs", "five.rs"];
4140    client_a
4141        .fs()
4142        .insert_tree(
4143            "/test",
4144            json!({
4145                "one.rs": "const ONE: usize = 1;",
4146                "two.rs": "const TWO: usize = 2;",
4147                "three.rs": "const THREE: usize = 3;",
4148                "four.rs": "const FOUR: usize = 3;",
4149                "five.rs": "const FIVE: usize = 3;",
4150            }),
4151        )
4152        .await;
4153
4154    let (project_a, worktree_id) = client_a.build_local_project("/test", cx_a).await;
4155
4156    // Share a project as client A
4157    let active_call_a = cx_a.read(ActiveCall::global);
4158    let project_id = active_call_a
4159        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4160        .await
4161        .unwrap();
4162
4163    // Join the project as client B and open all three files.
4164    let project_b = client_b.join_remote_project(project_id, cx_b).await;
4165    let guest_buffers = futures::future::try_join_all(file_names.iter().map(|file_name| {
4166        project_b.update(cx_b, |p, cx| {
4167            p.open_buffer_with_lsp((worktree_id, file_name), cx)
4168        })
4169    }))
4170    .await
4171    .unwrap();
4172
4173    // Simulate a language server reporting errors for a file.
4174    let fake_language_server = fake_language_servers.next().await.unwrap();
4175    fake_language_server
4176        .request::<lsp::request::WorkDoneProgressCreate>(lsp::WorkDoneProgressCreateParams {
4177            token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4178        })
4179        .await
4180        .unwrap();
4181    fake_language_server.notify::<lsp::notification::Progress>(&lsp::ProgressParams {
4182        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4183        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin(
4184            lsp::WorkDoneProgressBegin {
4185                title: "Progress Began".into(),
4186                ..Default::default()
4187            },
4188        )),
4189    });
4190    for file_name in file_names {
4191        fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
4192            &lsp::PublishDiagnosticsParams {
4193                uri: lsp::Url::from_file_path(Path::new("/test").join(file_name)).unwrap(),
4194                version: None,
4195                diagnostics: vec![lsp::Diagnostic {
4196                    severity: Some(lsp::DiagnosticSeverity::WARNING),
4197                    source: Some("the-disk-based-diagnostics-source".into()),
4198                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
4199                    message: "message one".to_string(),
4200                    ..Default::default()
4201                }],
4202            },
4203        );
4204    }
4205    fake_language_server.notify::<lsp::notification::Progress>(&lsp::ProgressParams {
4206        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4207        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End(
4208            lsp::WorkDoneProgressEnd { message: None },
4209        )),
4210    });
4211
4212    // When the "disk base diagnostics finished" message is received, the buffers'
4213    // diagnostics are expected to be present.
4214    let disk_based_diagnostics_finished = Arc::new(AtomicBool::new(false));
4215    project_b.update(cx_b, {
4216        let project_b = project_b.clone();
4217        let disk_based_diagnostics_finished = disk_based_diagnostics_finished.clone();
4218        move |_, cx| {
4219            cx.subscribe(&project_b, move |_, _, event, cx| {
4220                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
4221                    disk_based_diagnostics_finished.store(true, SeqCst);
4222                    for (buffer, _) in &guest_buffers {
4223                        assert_eq!(
4224                            buffer
4225                                .read(cx)
4226                                .snapshot()
4227                                .diagnostics_in_range::<_, usize>(0..5, false)
4228                                .count(),
4229                            1,
4230                            "expected a diagnostic for buffer {:?}",
4231                            buffer.read(cx).file().unwrap().path(),
4232                        );
4233                    }
4234                }
4235            })
4236            .detach();
4237        }
4238    });
4239
4240    executor.run_until_parked();
4241    assert!(disk_based_diagnostics_finished.load(SeqCst));
4242}
4243
4244#[gpui::test(iterations = 10)]
4245async fn test_reloading_buffer_manually(
4246    executor: BackgroundExecutor,
4247    cx_a: &mut TestAppContext,
4248    cx_b: &mut TestAppContext,
4249) {
4250    let mut server = TestServer::start(executor.clone()).await;
4251    let client_a = server.create_client(cx_a, "user_a").await;
4252    let client_b = server.create_client(cx_b, "user_b").await;
4253    server
4254        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4255        .await;
4256    let active_call_a = cx_a.read(ActiveCall::global);
4257
4258    client_a
4259        .fs()
4260        .insert_tree("/a", json!({ "a.rs": "let one = 1;" }))
4261        .await;
4262    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
4263    let buffer_a = project_a
4264        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
4265        .await
4266        .unwrap();
4267    let project_id = active_call_a
4268        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4269        .await
4270        .unwrap();
4271
4272    let project_b = client_b.join_remote_project(project_id, cx_b).await;
4273
4274    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4275    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4276    buffer_b.update(cx_b, |buffer, cx| {
4277        buffer.edit([(4..7, "six")], None, cx);
4278        buffer.edit([(10..11, "6")], None, cx);
4279        assert_eq!(buffer.text(), "let six = 6;");
4280        assert!(buffer.is_dirty());
4281        assert!(!buffer.has_conflict());
4282    });
4283    executor.run_until_parked();
4284
4285    buffer_a.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "let six = 6;"));
4286
4287    client_a
4288        .fs()
4289        .save(
4290            "/a/a.rs".as_ref(),
4291            &Rope::from("let seven = 7;"),
4292            LineEnding::Unix,
4293        )
4294        .await
4295        .unwrap();
4296    executor.run_until_parked();
4297
4298    buffer_a.read_with(cx_a, |buffer, _| assert!(buffer.has_conflict()));
4299
4300    buffer_b.read_with(cx_b, |buffer, _| assert!(buffer.has_conflict()));
4301
4302    project_b
4303        .update(cx_b, |project, cx| {
4304            project.reload_buffers(HashSet::from_iter([buffer_b.clone()]), true, cx)
4305        })
4306        .await
4307        .unwrap();
4308
4309    buffer_a.read_with(cx_a, |buffer, _| {
4310        assert_eq!(buffer.text(), "let seven = 7;");
4311        assert!(!buffer.is_dirty());
4312        assert!(!buffer.has_conflict());
4313    });
4314
4315    buffer_b.read_with(cx_b, |buffer, _| {
4316        assert_eq!(buffer.text(), "let seven = 7;");
4317        assert!(!buffer.is_dirty());
4318        assert!(!buffer.has_conflict());
4319    });
4320
4321    buffer_a.update(cx_a, |buffer, cx| {
4322        // Undoing on the host is a no-op when the reload was initiated by the guest.
4323        buffer.undo(cx);
4324        assert_eq!(buffer.text(), "let seven = 7;");
4325        assert!(!buffer.is_dirty());
4326        assert!(!buffer.has_conflict());
4327    });
4328    buffer_b.update(cx_b, |buffer, cx| {
4329        // Undoing on the guest rolls back the buffer to before it was reloaded but the conflict gets cleared.
4330        buffer.undo(cx);
4331        assert_eq!(buffer.text(), "let six = 6;");
4332        assert!(buffer.is_dirty());
4333        assert!(!buffer.has_conflict());
4334    });
4335}
4336
4337#[gpui::test(iterations = 10)]
4338async fn test_formatting_buffer(
4339    executor: BackgroundExecutor,
4340    cx_a: &mut TestAppContext,
4341    cx_b: &mut TestAppContext,
4342) {
4343    let mut server = TestServer::start(executor.clone()).await;
4344    let client_a = server.create_client(cx_a, "user_a").await;
4345    let client_b = server.create_client(cx_b, "user_b").await;
4346    server
4347        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4348        .await;
4349    let active_call_a = cx_a.read(ActiveCall::global);
4350
4351    client_a.language_registry().add(rust_lang());
4352    let mut fake_language_servers = client_a
4353        .language_registry()
4354        .register_fake_lsp("Rust", FakeLspAdapter::default());
4355
4356    // Here we insert a fake tree with a directory that exists on disk. This is needed
4357    // because later we'll invoke a command, which requires passing a working directory
4358    // that points to a valid location on disk.
4359    let directory = env::current_dir().unwrap();
4360    client_a
4361        .fs()
4362        .insert_tree(&directory, json!({ "a.rs": "let one = \"two\"" }))
4363        .await;
4364    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
4365    let project_id = active_call_a
4366        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4367        .await
4368        .unwrap();
4369    let project_b = client_b.join_remote_project(project_id, cx_b).await;
4370    let lsp_store_b = project_b.update(cx_b, |p, _| p.lsp_store());
4371
4372    let buffer_b = project_b
4373        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
4374        .await
4375        .unwrap();
4376
4377    let _handle = lsp_store_b.update(cx_b, |lsp_store, cx| {
4378        lsp_store.register_buffer_with_language_servers(&buffer_b, cx)
4379    });
4380    let fake_language_server = fake_language_servers.next().await.unwrap();
4381    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
4382        Ok(Some(vec![
4383            lsp::TextEdit {
4384                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 4)),
4385                new_text: "h".to_string(),
4386            },
4387            lsp::TextEdit {
4388                range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 7)),
4389                new_text: "y".to_string(),
4390            },
4391        ]))
4392    });
4393
4394    project_b
4395        .update(cx_b, |project, cx| {
4396            project.format(
4397                HashSet::from_iter([buffer_b.clone()]),
4398                LspFormatTarget::Buffers,
4399                true,
4400                FormatTrigger::Save,
4401                cx,
4402            )
4403        })
4404        .await
4405        .unwrap();
4406
4407    // The edits from the LSP are applied, and a final newline is added.
4408    assert_eq!(
4409        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4410        "let honey = \"two\"\n"
4411    );
4412
4413    // Ensure buffer can be formatted using an external command. Notice how the
4414    // host's configuration is honored as opposed to using the guest's settings.
4415    cx_a.update(|cx| {
4416        SettingsStore::update_global(cx, |store, cx| {
4417            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4418                file.defaults.formatter = Some(SelectedFormatter::List(FormatterList(
4419                    vec![Formatter::External {
4420                        command: "awk".into(),
4421                        arguments: Some(vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()].into()),
4422                    }]
4423                    .into(),
4424                )));
4425            });
4426        });
4427    });
4428
4429    executor.allow_parking();
4430    project_b
4431        .update(cx_b, |project, cx| {
4432            project.format(
4433                HashSet::from_iter([buffer_b.clone()]),
4434                LspFormatTarget::Buffers,
4435                true,
4436                FormatTrigger::Save,
4437                cx,
4438            )
4439        })
4440        .await
4441        .unwrap();
4442    assert_eq!(
4443        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4444        format!("let honey = \"{}/a.rs\"\n", directory.to_str().unwrap())
4445    );
4446}
4447
4448#[gpui::test(iterations = 10)]
4449async fn test_prettier_formatting_buffer(
4450    executor: BackgroundExecutor,
4451    cx_a: &mut TestAppContext,
4452    cx_b: &mut TestAppContext,
4453) {
4454    let mut server = TestServer::start(executor.clone()).await;
4455    let client_a = server.create_client(cx_a, "user_a").await;
4456    let client_b = server.create_client(cx_b, "user_b").await;
4457    server
4458        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4459        .await;
4460    let active_call_a = cx_a.read(ActiveCall::global);
4461
4462    let test_plugin = "test_plugin";
4463
4464    client_a.language_registry().add(Arc::new(Language::new(
4465        LanguageConfig {
4466            name: "TypeScript".into(),
4467            matcher: LanguageMatcher {
4468                path_suffixes: vec!["ts".to_string()],
4469                ..Default::default()
4470            },
4471            ..Default::default()
4472        },
4473        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
4474    )));
4475    let mut fake_language_servers = client_a.language_registry().register_fake_lsp(
4476        "TypeScript",
4477        FakeLspAdapter {
4478            prettier_plugins: vec![test_plugin],
4479            ..Default::default()
4480        },
4481    );
4482
4483    // Here we insert a fake tree with a directory that exists on disk. This is needed
4484    // because later we'll invoke a command, which requires passing a working directory
4485    // that points to a valid location on disk.
4486    let directory = env::current_dir().unwrap();
4487    let buffer_text = "let one = \"two\"";
4488    client_a
4489        .fs()
4490        .insert_tree(&directory, json!({ "a.ts": buffer_text }))
4491        .await;
4492    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
4493    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
4494    let open_buffer = project_a.update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.ts"), cx));
4495    let buffer_a = cx_a.executor().spawn(open_buffer).await.unwrap();
4496
4497    let project_id = active_call_a
4498        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4499        .await
4500        .unwrap();
4501    let project_b = client_b.join_remote_project(project_id, cx_b).await;
4502    let (buffer_b, _) = project_b
4503        .update(cx_b, |p, cx| {
4504            p.open_buffer_with_lsp((worktree_id, "a.ts"), cx)
4505        })
4506        .await
4507        .unwrap();
4508
4509    cx_a.update(|cx| {
4510        SettingsStore::update_global(cx, |store, cx| {
4511            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4512                file.defaults.formatter = Some(SelectedFormatter::Auto);
4513                file.defaults.prettier = Some(PrettierSettings {
4514                    allowed: true,
4515                    ..PrettierSettings::default()
4516                });
4517            });
4518        });
4519    });
4520    cx_b.update(|cx| {
4521        SettingsStore::update_global(cx, |store, cx| {
4522            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4523                file.defaults.formatter = Some(SelectedFormatter::List(FormatterList(
4524                    vec![Formatter::LanguageServer { name: None }].into(),
4525                )));
4526                file.defaults.prettier = Some(PrettierSettings {
4527                    allowed: true,
4528                    ..PrettierSettings::default()
4529                });
4530            });
4531        });
4532    });
4533    let fake_language_server = fake_language_servers.next().await.unwrap();
4534    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
4535        panic!(
4536            "Unexpected: prettier should be preferred since it's enabled and language supports it"
4537        )
4538    });
4539
4540    project_b
4541        .update(cx_b, |project, cx| {
4542            project.format(
4543                HashSet::from_iter([buffer_b.clone()]),
4544                LspFormatTarget::Buffers,
4545                true,
4546                FormatTrigger::Save,
4547                cx,
4548            )
4549        })
4550        .await
4551        .unwrap();
4552
4553    executor.run_until_parked();
4554    assert_eq!(
4555        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4556        buffer_text.to_string() + "\n" + prettier_format_suffix,
4557        "Prettier formatting was not applied to client buffer after client's request"
4558    );
4559
4560    project_a
4561        .update(cx_a, |project, cx| {
4562            project.format(
4563                HashSet::from_iter([buffer_a.clone()]),
4564                LspFormatTarget::Buffers,
4565                true,
4566                FormatTrigger::Manual,
4567                cx,
4568            )
4569        })
4570        .await
4571        .unwrap();
4572
4573    executor.run_until_parked();
4574    assert_eq!(
4575        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4576        buffer_text.to_string() + "\n" + prettier_format_suffix + "\n" + prettier_format_suffix,
4577        "Prettier formatting was not applied to client buffer after host's request"
4578    );
4579}
4580
4581#[gpui::test(iterations = 10)]
4582async fn test_definition(
4583    executor: BackgroundExecutor,
4584    cx_a: &mut TestAppContext,
4585    cx_b: &mut TestAppContext,
4586) {
4587    let mut server = TestServer::start(executor.clone()).await;
4588    let client_a = server.create_client(cx_a, "user_a").await;
4589    let client_b = server.create_client(cx_b, "user_b").await;
4590    server
4591        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4592        .await;
4593    let active_call_a = cx_a.read(ActiveCall::global);
4594
4595    let mut fake_language_servers = client_a
4596        .language_registry()
4597        .register_fake_lsp("Rust", Default::default());
4598    client_a.language_registry().add(rust_lang());
4599
4600    client_a
4601        .fs()
4602        .insert_tree(
4603            "/root",
4604            json!({
4605                "dir-1": {
4606                    "a.rs": "const ONE: usize = b::TWO + b::THREE;",
4607                },
4608                "dir-2": {
4609                    "b.rs": "const TWO: c::T2 = 2;\nconst THREE: usize = 3;",
4610                    "c.rs": "type T2 = usize;",
4611                }
4612            }),
4613        )
4614        .await;
4615    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4616    let project_id = active_call_a
4617        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4618        .await
4619        .unwrap();
4620    let project_b = client_b.join_remote_project(project_id, cx_b).await;
4621
4622    // Open the file on client B.
4623    let (buffer_b, _handle) = project_b
4624        .update(cx_b, |p, cx| {
4625            p.open_buffer_with_lsp((worktree_id, "a.rs"), cx)
4626        })
4627        .await
4628        .unwrap();
4629
4630    // Request the definition of a symbol as the guest.
4631    let fake_language_server = fake_language_servers.next().await.unwrap();
4632    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4633        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4634            lsp::Location::new(
4635                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4636                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4637            ),
4638        )))
4639    });
4640
4641    let definitions_1 = project_b
4642        .update(cx_b, |p, cx| p.definition(&buffer_b, 23, cx))
4643        .await
4644        .unwrap();
4645    cx_b.read(|cx| {
4646        assert_eq!(definitions_1.len(), 1);
4647        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4648        let target_buffer = definitions_1[0].target.buffer.read(cx);
4649        assert_eq!(
4650            target_buffer.text(),
4651            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4652        );
4653        assert_eq!(
4654            definitions_1[0].target.range.to_point(target_buffer),
4655            Point::new(0, 6)..Point::new(0, 9)
4656        );
4657    });
4658
4659    // Try getting more definitions for the same buffer, ensuring the buffer gets reused from
4660    // the previous call to `definition`.
4661    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4662        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4663            lsp::Location::new(
4664                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4665                lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)),
4666            ),
4667        )))
4668    });
4669
4670    let definitions_2 = project_b
4671        .update(cx_b, |p, cx| p.definition(&buffer_b, 33, cx))
4672        .await
4673        .unwrap();
4674    cx_b.read(|cx| {
4675        assert_eq!(definitions_2.len(), 1);
4676        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4677        let target_buffer = definitions_2[0].target.buffer.read(cx);
4678        assert_eq!(
4679            target_buffer.text(),
4680            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4681        );
4682        assert_eq!(
4683            definitions_2[0].target.range.to_point(target_buffer),
4684            Point::new(1, 6)..Point::new(1, 11)
4685        );
4686    });
4687    assert_eq!(
4688        definitions_1[0].target.buffer,
4689        definitions_2[0].target.buffer
4690    );
4691
4692    fake_language_server.handle_request::<lsp::request::GotoTypeDefinition, _, _>(
4693        |req, _| async move {
4694            assert_eq!(
4695                req.text_document_position_params.position,
4696                lsp::Position::new(0, 7)
4697            );
4698            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4699                lsp::Location::new(
4700                    lsp::Url::from_file_path("/root/dir-2/c.rs").unwrap(),
4701                    lsp::Range::new(lsp::Position::new(0, 5), lsp::Position::new(0, 7)),
4702                ),
4703            )))
4704        },
4705    );
4706
4707    let type_definitions = project_b
4708        .update(cx_b, |p, cx| p.type_definition(&buffer_b, 7, cx))
4709        .await
4710        .unwrap();
4711    cx_b.read(|cx| {
4712        assert_eq!(type_definitions.len(), 1);
4713        let target_buffer = type_definitions[0].target.buffer.read(cx);
4714        assert_eq!(target_buffer.text(), "type T2 = usize;");
4715        assert_eq!(
4716            type_definitions[0].target.range.to_point(target_buffer),
4717            Point::new(0, 5)..Point::new(0, 7)
4718        );
4719    });
4720}
4721
4722#[gpui::test(iterations = 10)]
4723async fn test_references(
4724    executor: BackgroundExecutor,
4725    cx_a: &mut TestAppContext,
4726    cx_b: &mut TestAppContext,
4727) {
4728    let mut server = TestServer::start(executor.clone()).await;
4729    let client_a = server.create_client(cx_a, "user_a").await;
4730    let client_b = server.create_client(cx_b, "user_b").await;
4731    server
4732        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4733        .await;
4734    let active_call_a = cx_a.read(ActiveCall::global);
4735
4736    client_a.language_registry().add(rust_lang());
4737    let mut fake_language_servers = client_a.language_registry().register_fake_lsp(
4738        "Rust",
4739        FakeLspAdapter {
4740            name: "my-fake-lsp-adapter",
4741            capabilities: lsp::ServerCapabilities {
4742                references_provider: Some(lsp::OneOf::Left(true)),
4743                ..Default::default()
4744            },
4745            ..Default::default()
4746        },
4747    );
4748
4749    client_a
4750        .fs()
4751        .insert_tree(
4752            "/root",
4753            json!({
4754                "dir-1": {
4755                    "one.rs": "const ONE: usize = 1;",
4756                    "two.rs": "const TWO: usize = one::ONE + one::ONE;",
4757                },
4758                "dir-2": {
4759                    "three.rs": "const THREE: usize = two::TWO + one::ONE;",
4760                }
4761            }),
4762        )
4763        .await;
4764    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4765    let project_id = active_call_a
4766        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4767        .await
4768        .unwrap();
4769    let project_b = client_b.join_remote_project(project_id, cx_b).await;
4770
4771    // Open the file on client B.
4772    let (buffer_b, _handle) = project_b
4773        .update(cx_b, |p, cx| {
4774            p.open_buffer_with_lsp((worktree_id, "one.rs"), cx)
4775        })
4776        .await
4777        .unwrap();
4778
4779    // Request references to a symbol as the guest.
4780    let fake_language_server = fake_language_servers.next().await.unwrap();
4781    let (lsp_response_tx, rx) = mpsc::unbounded::<Result<Option<Vec<lsp::Location>>>>();
4782    fake_language_server.handle_request::<lsp::request::References, _, _>({
4783        let rx = Arc::new(Mutex::new(Some(rx)));
4784        move |params, _| {
4785            assert_eq!(
4786                params.text_document_position.text_document.uri.as_str(),
4787                "file:///root/dir-1/one.rs"
4788            );
4789            let rx = rx.clone();
4790            async move {
4791                let mut response_rx = rx.lock().take().unwrap();
4792                let result = response_rx.next().await.unwrap();
4793                *rx.lock() = Some(response_rx);
4794                result
4795            }
4796        }
4797    });
4798
4799    let references = project_b.update(cx_b, |p, cx| p.references(&buffer_b, 7, cx));
4800
4801    // User is informed that a request is pending.
4802    executor.run_until_parked();
4803    project_b.read_with(cx_b, |project, cx| {
4804        let status = project.language_server_statuses(cx).next().unwrap().1;
4805        assert_eq!(status.name, "my-fake-lsp-adapter");
4806        assert_eq!(
4807            status.pending_work.values().next().unwrap().message,
4808            Some("Finding references...".into())
4809        );
4810    });
4811
4812    // Cause the language server to respond.
4813    lsp_response_tx
4814        .unbounded_send(Ok(Some(vec![
4815            lsp::Location {
4816                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4817                range: lsp::Range::new(lsp::Position::new(0, 24), lsp::Position::new(0, 27)),
4818            },
4819            lsp::Location {
4820                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4821                range: lsp::Range::new(lsp::Position::new(0, 35), lsp::Position::new(0, 38)),
4822            },
4823            lsp::Location {
4824                uri: lsp::Url::from_file_path("/root/dir-2/three.rs").unwrap(),
4825                range: lsp::Range::new(lsp::Position::new(0, 37), lsp::Position::new(0, 40)),
4826            },
4827        ])))
4828        .unwrap();
4829
4830    let references = references.await.unwrap();
4831    executor.run_until_parked();
4832    project_b.read_with(cx_b, |project, cx| {
4833        // User is informed that a request is no longer pending.
4834        let status = project.language_server_statuses(cx).next().unwrap().1;
4835        assert!(status.pending_work.is_empty());
4836
4837        assert_eq!(references.len(), 3);
4838        assert_eq!(project.worktrees(cx).count(), 2);
4839
4840        let two_buffer = references[0].buffer.read(cx);
4841        let three_buffer = references[2].buffer.read(cx);
4842        assert_eq!(
4843            two_buffer.file().unwrap().path().as_ref(),
4844            Path::new("two.rs")
4845        );
4846        assert_eq!(references[1].buffer, references[0].buffer);
4847        assert_eq!(
4848            three_buffer.file().unwrap().full_path(cx),
4849            Path::new("/root/dir-2/three.rs")
4850        );
4851
4852        assert_eq!(references[0].range.to_offset(two_buffer), 24..27);
4853        assert_eq!(references[1].range.to_offset(two_buffer), 35..38);
4854        assert_eq!(references[2].range.to_offset(three_buffer), 37..40);
4855    });
4856
4857    let references = project_b.update(cx_b, |p, cx| p.references(&buffer_b, 7, cx));
4858
4859    // User is informed that a request is pending.
4860    executor.run_until_parked();
4861    project_b.read_with(cx_b, |project, cx| {
4862        let status = project.language_server_statuses(cx).next().unwrap().1;
4863        assert_eq!(status.name, "my-fake-lsp-adapter");
4864        assert_eq!(
4865            status.pending_work.values().next().unwrap().message,
4866            Some("Finding references...".into())
4867        );
4868    });
4869
4870    // Cause the LSP request to fail.
4871    lsp_response_tx
4872        .unbounded_send(Err(anyhow!("can't find references")))
4873        .unwrap();
4874    references.await.unwrap_err();
4875
4876    // User is informed that the request is no longer pending.
4877    executor.run_until_parked();
4878    project_b.read_with(cx_b, |project, cx| {
4879        let status = project.language_server_statuses(cx).next().unwrap().1;
4880        assert!(status.pending_work.is_empty());
4881    });
4882}
4883
4884#[gpui::test(iterations = 10)]
4885async fn test_project_search(
4886    executor: BackgroundExecutor,
4887    cx_a: &mut TestAppContext,
4888    cx_b: &mut TestAppContext,
4889) {
4890    let mut server = TestServer::start(executor.clone()).await;
4891    let client_a = server.create_client(cx_a, "user_a").await;
4892    let client_b = server.create_client(cx_b, "user_b").await;
4893    server
4894        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4895        .await;
4896    let active_call_a = cx_a.read(ActiveCall::global);
4897
4898    client_a
4899        .fs()
4900        .insert_tree(
4901            "/root",
4902            json!({
4903                "dir-1": {
4904                    "a": "hello world",
4905                    "b": "goodnight moon",
4906                    "c": "a world of goo",
4907                    "d": "world champion of clown world",
4908                },
4909                "dir-2": {
4910                    "e": "disney world is fun",
4911                }
4912            }),
4913        )
4914        .await;
4915    let (project_a, _) = client_a.build_local_project("/root/dir-1", cx_a).await;
4916    let (worktree_2, _) = project_a
4917        .update(cx_a, |p, cx| {
4918            p.find_or_create_worktree("/root/dir-2", true, cx)
4919        })
4920        .await
4921        .unwrap();
4922    worktree_2
4923        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
4924        .await;
4925    let project_id = active_call_a
4926        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4927        .await
4928        .unwrap();
4929
4930    let project_b = client_b.join_remote_project(project_id, cx_b).await;
4931
4932    // Perform a search as the guest.
4933    let mut results = HashMap::default();
4934    let search_rx = project_b.update(cx_b, |project, cx| {
4935        project.search(
4936            SearchQuery::text(
4937                "world",
4938                false,
4939                false,
4940                false,
4941                Default::default(),
4942                Default::default(),
4943                None,
4944            )
4945            .unwrap(),
4946            cx,
4947        )
4948    });
4949    while let Ok(result) = search_rx.recv().await {
4950        match result {
4951            SearchResult::Buffer { buffer, ranges } => {
4952                results.entry(buffer).or_insert(ranges);
4953            }
4954            SearchResult::LimitReached => {
4955                panic!("Unexpectedly reached search limit in tests. If you do want to assert limit-reached, change this panic call.")
4956            }
4957        };
4958    }
4959
4960    let mut ranges_by_path = results
4961        .into_iter()
4962        .map(|(buffer, ranges)| {
4963            buffer.read_with(cx_b, |buffer, cx| {
4964                let path = buffer.file().unwrap().full_path(cx);
4965                let offset_ranges = ranges
4966                    .into_iter()
4967                    .map(|range| range.to_offset(buffer))
4968                    .collect::<Vec<_>>();
4969                (path, offset_ranges)
4970            })
4971        })
4972        .collect::<Vec<_>>();
4973    ranges_by_path.sort_by_key(|(path, _)| path.clone());
4974
4975    assert_eq!(
4976        ranges_by_path,
4977        &[
4978            (PathBuf::from("dir-1/a"), vec![6..11]),
4979            (PathBuf::from("dir-1/c"), vec![2..7]),
4980            (PathBuf::from("dir-1/d"), vec![0..5, 24..29]),
4981            (PathBuf::from("dir-2/e"), vec![7..12]),
4982        ]
4983    );
4984}
4985
4986#[gpui::test(iterations = 10)]
4987async fn test_document_highlights(
4988    executor: BackgroundExecutor,
4989    cx_a: &mut TestAppContext,
4990    cx_b: &mut TestAppContext,
4991) {
4992    let mut server = TestServer::start(executor.clone()).await;
4993    let client_a = server.create_client(cx_a, "user_a").await;
4994    let client_b = server.create_client(cx_b, "user_b").await;
4995    server
4996        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4997        .await;
4998    let active_call_a = cx_a.read(ActiveCall::global);
4999
5000    client_a
5001        .fs()
5002        .insert_tree(
5003            "/root-1",
5004            json!({
5005                "main.rs": "fn double(number: i32) -> i32 { number + number }",
5006            }),
5007        )
5008        .await;
5009
5010    let mut fake_language_servers = client_a
5011        .language_registry()
5012        .register_fake_lsp("Rust", Default::default());
5013    client_a.language_registry().add(rust_lang());
5014
5015    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
5016    let project_id = active_call_a
5017        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5018        .await
5019        .unwrap();
5020    let project_b = client_b.join_remote_project(project_id, cx_b).await;
5021
5022    // Open the file on client B.
5023    let (buffer_b, _handle) = project_b
5024        .update(cx_b, |p, cx| {
5025            p.open_buffer_with_lsp((worktree_id, "main.rs"), cx)
5026        })
5027        .await
5028        .unwrap();
5029
5030    // Request document highlights as the guest.
5031    let fake_language_server = fake_language_servers.next().await.unwrap();
5032    fake_language_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
5033        |params, _| async move {
5034            assert_eq!(
5035                params
5036                    .text_document_position_params
5037                    .text_document
5038                    .uri
5039                    .as_str(),
5040                "file:///root-1/main.rs"
5041            );
5042            assert_eq!(
5043                params.text_document_position_params.position,
5044                lsp::Position::new(0, 34)
5045            );
5046            Ok(Some(vec![
5047                lsp::DocumentHighlight {
5048                    kind: Some(lsp::DocumentHighlightKind::WRITE),
5049                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 16)),
5050                },
5051                lsp::DocumentHighlight {
5052                    kind: Some(lsp::DocumentHighlightKind::READ),
5053                    range: lsp::Range::new(lsp::Position::new(0, 32), lsp::Position::new(0, 38)),
5054                },
5055                lsp::DocumentHighlight {
5056                    kind: Some(lsp::DocumentHighlightKind::READ),
5057                    range: lsp::Range::new(lsp::Position::new(0, 41), lsp::Position::new(0, 47)),
5058                },
5059            ]))
5060        },
5061    );
5062
5063    let highlights = project_b
5064        .update(cx_b, |p, cx| p.document_highlights(&buffer_b, 34, cx))
5065        .await
5066        .unwrap();
5067
5068    buffer_b.read_with(cx_b, |buffer, _| {
5069        let snapshot = buffer.snapshot();
5070
5071        let highlights = highlights
5072            .into_iter()
5073            .map(|highlight| (highlight.kind, highlight.range.to_offset(&snapshot)))
5074            .collect::<Vec<_>>();
5075        assert_eq!(
5076            highlights,
5077            &[
5078                (lsp::DocumentHighlightKind::WRITE, 10..16),
5079                (lsp::DocumentHighlightKind::READ, 32..38),
5080                (lsp::DocumentHighlightKind::READ, 41..47)
5081            ]
5082        )
5083    });
5084}
5085
5086#[gpui::test(iterations = 10)]
5087async fn test_lsp_hover(
5088    executor: BackgroundExecutor,
5089    cx_a: &mut TestAppContext,
5090    cx_b: &mut TestAppContext,
5091) {
5092    let mut server = TestServer::start(executor.clone()).await;
5093    let client_a = server.create_client(cx_a, "user_a").await;
5094    let client_b = server.create_client(cx_b, "user_b").await;
5095    server
5096        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5097        .await;
5098    let active_call_a = cx_a.read(ActiveCall::global);
5099
5100    client_a
5101        .fs()
5102        .insert_tree(
5103            "/root-1",
5104            json!({
5105                "main.rs": "use std::collections::HashMap;",
5106            }),
5107        )
5108        .await;
5109
5110    client_a.language_registry().add(rust_lang());
5111    let language_server_names = ["rust-analyzer", "CrabLang-ls"];
5112    let mut language_servers = [
5113        client_a.language_registry().register_fake_lsp(
5114            "Rust",
5115            FakeLspAdapter {
5116                name: "rust-analyzer",
5117                capabilities: lsp::ServerCapabilities {
5118                    hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
5119                    ..lsp::ServerCapabilities::default()
5120                },
5121                ..FakeLspAdapter::default()
5122            },
5123        ),
5124        client_a.language_registry().register_fake_lsp(
5125            "Rust",
5126            FakeLspAdapter {
5127                name: "CrabLang-ls",
5128                capabilities: lsp::ServerCapabilities {
5129                    hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
5130                    ..lsp::ServerCapabilities::default()
5131                },
5132                ..FakeLspAdapter::default()
5133            },
5134        ),
5135    ];
5136
5137    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
5138    let project_id = active_call_a
5139        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5140        .await
5141        .unwrap();
5142    let project_b = client_b.join_remote_project(project_id, cx_b).await;
5143
5144    // Open the file as the guest
5145    let (buffer_b, _handle) = project_b
5146        .update(cx_b, |p, cx| {
5147            p.open_buffer_with_lsp((worktree_id, "main.rs"), cx)
5148        })
5149        .await
5150        .unwrap();
5151
5152    let mut servers_with_hover_requests = HashMap::default();
5153    for i in 0..language_server_names.len() {
5154        let new_server = language_servers[i].next().await.unwrap_or_else(|| {
5155            panic!(
5156                "Failed to get language server #{i} with name {}",
5157                &language_server_names[i]
5158            )
5159        });
5160        let new_server_name = new_server.server.name();
5161        assert!(
5162            !servers_with_hover_requests.contains_key(&new_server_name),
5163            "Unexpected: initialized server with the same name twice. Name: `{new_server_name}`"
5164        );
5165        match new_server_name.as_ref() {
5166            "CrabLang-ls" => {
5167                servers_with_hover_requests.insert(
5168                    new_server_name.clone(),
5169                    new_server.handle_request::<lsp::request::HoverRequest, _, _>(
5170                        move |params, _| {
5171                            assert_eq!(
5172                                params
5173                                    .text_document_position_params
5174                                    .text_document
5175                                    .uri
5176                                    .as_str(),
5177                                "file:///root-1/main.rs"
5178                            );
5179                            let name = new_server_name.clone();
5180                            async move {
5181                                Ok(Some(lsp::Hover {
5182                                    contents: lsp::HoverContents::Scalar(
5183                                        lsp::MarkedString::String(format!("{name} hover")),
5184                                    ),
5185                                    range: None,
5186                                }))
5187                            }
5188                        },
5189                    ),
5190                );
5191            }
5192            "rust-analyzer" => {
5193                servers_with_hover_requests.insert(
5194                    new_server_name.clone(),
5195                    new_server.handle_request::<lsp::request::HoverRequest, _, _>(
5196                        |params, _| async move {
5197                            assert_eq!(
5198                                params
5199                                    .text_document_position_params
5200                                    .text_document
5201                                    .uri
5202                                    .as_str(),
5203                                "file:///root-1/main.rs"
5204                            );
5205                            assert_eq!(
5206                                params.text_document_position_params.position,
5207                                lsp::Position::new(0, 22)
5208                            );
5209                            Ok(Some(lsp::Hover {
5210                                contents: lsp::HoverContents::Array(vec![
5211                                    lsp::MarkedString::String("Test hover content.".to_string()),
5212                                    lsp::MarkedString::LanguageString(lsp::LanguageString {
5213                                        language: "Rust".to_string(),
5214                                        value: "let foo = 42;".to_string(),
5215                                    }),
5216                                ]),
5217                                range: Some(lsp::Range::new(
5218                                    lsp::Position::new(0, 22),
5219                                    lsp::Position::new(0, 29),
5220                                )),
5221                            }))
5222                        },
5223                    ),
5224                );
5225            }
5226            unexpected => panic!("Unexpected server name: {unexpected}"),
5227        }
5228    }
5229
5230    // Request hover information as the guest.
5231    let mut hovers = project_b
5232        .update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
5233        .await;
5234    assert_eq!(
5235        hovers.len(),
5236        2,
5237        "Expected two hovers from both language servers, but got: {hovers:?}"
5238    );
5239
5240    let _: Vec<()> = futures::future::join_all(servers_with_hover_requests.into_values().map(
5241        |mut hover_request| async move {
5242            hover_request
5243                .next()
5244                .await
5245                .expect("All hover requests should have been triggered")
5246        },
5247    ))
5248    .await;
5249
5250    hovers.sort_by_key(|hover| hover.contents.len());
5251    let first_hover = hovers.first().cloned().unwrap();
5252    assert_eq!(
5253        first_hover.contents,
5254        vec![project::HoverBlock {
5255            text: "CrabLang-ls hover".to_string(),
5256            kind: HoverBlockKind::Markdown,
5257        },]
5258    );
5259    let second_hover = hovers.last().cloned().unwrap();
5260    assert_eq!(
5261        second_hover.contents,
5262        vec![
5263            project::HoverBlock {
5264                text: "Test hover content.".to_string(),
5265                kind: HoverBlockKind::Markdown,
5266            },
5267            project::HoverBlock {
5268                text: "let foo = 42;".to_string(),
5269                kind: HoverBlockKind::Code {
5270                    language: "Rust".to_string()
5271                },
5272            }
5273        ]
5274    );
5275    buffer_b.read_with(cx_b, |buffer, _| {
5276        let snapshot = buffer.snapshot();
5277        assert_eq!(second_hover.range.unwrap().to_offset(&snapshot), 22..29);
5278    });
5279}
5280
5281#[gpui::test(iterations = 10)]
5282async fn test_project_symbols(
5283    executor: BackgroundExecutor,
5284    cx_a: &mut TestAppContext,
5285    cx_b: &mut TestAppContext,
5286) {
5287    let mut server = TestServer::start(executor.clone()).await;
5288    let client_a = server.create_client(cx_a, "user_a").await;
5289    let client_b = server.create_client(cx_b, "user_b").await;
5290    server
5291        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5292        .await;
5293    let active_call_a = cx_a.read(ActiveCall::global);
5294
5295    client_a.language_registry().add(rust_lang());
5296    let mut fake_language_servers = client_a
5297        .language_registry()
5298        .register_fake_lsp("Rust", Default::default());
5299
5300    client_a
5301        .fs()
5302        .insert_tree(
5303            "/code",
5304            json!({
5305                "crate-1": {
5306                    "one.rs": "const ONE: usize = 1;",
5307                },
5308                "crate-2": {
5309                    "two.rs": "const TWO: usize = 2; const THREE: usize = 3;",
5310                },
5311                "private": {
5312                    "passwords.txt": "the-password",
5313                }
5314            }),
5315        )
5316        .await;
5317    let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await;
5318    let project_id = active_call_a
5319        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5320        .await
5321        .unwrap();
5322    let project_b = client_b.join_remote_project(project_id, cx_b).await;
5323
5324    // Cause the language server to start.
5325    let _buffer = project_b
5326        .update(cx_b, |p, cx| {
5327            p.open_buffer_with_lsp((worktree_id, "one.rs"), cx)
5328        })
5329        .await
5330        .unwrap();
5331
5332    let fake_language_server = fake_language_servers.next().await.unwrap();
5333    fake_language_server.handle_request::<lsp::WorkspaceSymbolRequest, _, _>(|_, _| async move {
5334        Ok(Some(lsp::WorkspaceSymbolResponse::Flat(vec![
5335            #[allow(deprecated)]
5336            lsp::SymbolInformation {
5337                name: "TWO".into(),
5338                location: lsp::Location {
5339                    uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
5340                    range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5341                },
5342                kind: lsp::SymbolKind::CONSTANT,
5343                tags: None,
5344                container_name: None,
5345                deprecated: None,
5346            },
5347        ])))
5348    });
5349
5350    // Request the definition of a symbol as the guest.
5351    let symbols = project_b
5352        .update(cx_b, |p, cx| p.symbols("two", cx))
5353        .await
5354        .unwrap();
5355    assert_eq!(symbols.len(), 1);
5356    assert_eq!(symbols[0].name, "TWO");
5357
5358    // Open one of the returned symbols.
5359    let buffer_b_2 = project_b
5360        .update(cx_b, |project, cx| {
5361            project.open_buffer_for_symbol(&symbols[0], cx)
5362        })
5363        .await
5364        .unwrap();
5365
5366    buffer_b_2.read_with(cx_b, |buffer, cx| {
5367        assert_eq!(
5368            buffer.file().unwrap().full_path(cx),
5369            Path::new("/code/crate-2/two.rs")
5370        );
5371    });
5372
5373    // Attempt to craft a symbol and violate host's privacy by opening an arbitrary file.
5374    let mut fake_symbol = symbols[0].clone();
5375    fake_symbol.path.path = Path::new("/code/secrets").into();
5376    let error = project_b
5377        .update(cx_b, |project, cx| {
5378            project.open_buffer_for_symbol(&fake_symbol, cx)
5379        })
5380        .await
5381        .unwrap_err();
5382    assert!(error.to_string().contains("invalid symbol signature"));
5383}
5384
5385#[gpui::test(iterations = 10)]
5386async fn test_open_buffer_while_getting_definition_pointing_to_it(
5387    executor: BackgroundExecutor,
5388    cx_a: &mut TestAppContext,
5389    cx_b: &mut TestAppContext,
5390    mut rng: StdRng,
5391) {
5392    let mut server = TestServer::start(executor.clone()).await;
5393    let client_a = server.create_client(cx_a, "user_a").await;
5394    let client_b = server.create_client(cx_b, "user_b").await;
5395    server
5396        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5397        .await;
5398    let active_call_a = cx_a.read(ActiveCall::global);
5399
5400    client_a.language_registry().add(rust_lang());
5401    let mut fake_language_servers = client_a
5402        .language_registry()
5403        .register_fake_lsp("Rust", Default::default());
5404
5405    client_a
5406        .fs()
5407        .insert_tree(
5408            "/root",
5409            json!({
5410                "a.rs": "const ONE: usize = b::TWO;",
5411                "b.rs": "const TWO: usize = 2",
5412            }),
5413        )
5414        .await;
5415    let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await;
5416    let project_id = active_call_a
5417        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5418        .await
5419        .unwrap();
5420    let project_b = client_b.join_remote_project(project_id, cx_b).await;
5421
5422    let (buffer_b1, _lsp) = project_b
5423        .update(cx_b, |p, cx| {
5424            p.open_buffer_with_lsp((worktree_id, "a.rs"), cx)
5425        })
5426        .await
5427        .unwrap();
5428
5429    let fake_language_server = fake_language_servers.next().await.unwrap();
5430    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
5431        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
5432            lsp::Location::new(
5433                lsp::Url::from_file_path("/root/b.rs").unwrap(),
5434                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5435            ),
5436        )))
5437    });
5438
5439    let definitions;
5440    let buffer_b2;
5441    if rng.gen() {
5442        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5443        (buffer_b2, _) = project_b
5444            .update(cx_b, |p, cx| {
5445                p.open_buffer_with_lsp((worktree_id, "b.rs"), cx)
5446            })
5447            .await
5448            .unwrap();
5449    } else {
5450        (buffer_b2, _) = project_b
5451            .update(cx_b, |p, cx| {
5452                p.open_buffer_with_lsp((worktree_id, "b.rs"), cx)
5453            })
5454            .await
5455            .unwrap();
5456        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5457    }
5458
5459    let definitions = definitions.await.unwrap();
5460    assert_eq!(definitions.len(), 1);
5461    assert_eq!(definitions[0].target.buffer, buffer_b2);
5462}
5463
5464#[gpui::test(iterations = 10)]
5465async fn test_contacts(
5466    executor: BackgroundExecutor,
5467    cx_a: &mut TestAppContext,
5468    cx_b: &mut TestAppContext,
5469    cx_c: &mut TestAppContext,
5470    cx_d: &mut TestAppContext,
5471) {
5472    let mut server = TestServer::start(executor.clone()).await;
5473    let client_a = server.create_client(cx_a, "user_a").await;
5474    let client_b = server.create_client(cx_b, "user_b").await;
5475    let client_c = server.create_client(cx_c, "user_c").await;
5476    let client_d = server.create_client(cx_d, "user_d").await;
5477    server
5478        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
5479        .await;
5480    let active_call_a = cx_a.read(ActiveCall::global);
5481    let active_call_b = cx_b.read(ActiveCall::global);
5482    let active_call_c = cx_c.read(ActiveCall::global);
5483    let _active_call_d = cx_d.read(ActiveCall::global);
5484
5485    executor.run_until_parked();
5486    assert_eq!(
5487        contacts(&client_a, cx_a),
5488        [
5489            ("user_b".to_string(), "online", "free"),
5490            ("user_c".to_string(), "online", "free")
5491        ]
5492    );
5493    assert_eq!(
5494        contacts(&client_b, cx_b),
5495        [
5496            ("user_a".to_string(), "online", "free"),
5497            ("user_c".to_string(), "online", "free")
5498        ]
5499    );
5500    assert_eq!(
5501        contacts(&client_c, cx_c),
5502        [
5503            ("user_a".to_string(), "online", "free"),
5504            ("user_b".to_string(), "online", "free")
5505        ]
5506    );
5507    assert_eq!(contacts(&client_d, cx_d), []);
5508
5509    server.disconnect_client(client_c.peer_id().unwrap());
5510    server.forbid_connections();
5511    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5512    assert_eq!(
5513        contacts(&client_a, cx_a),
5514        [
5515            ("user_b".to_string(), "online", "free"),
5516            ("user_c".to_string(), "offline", "free")
5517        ]
5518    );
5519    assert_eq!(
5520        contacts(&client_b, cx_b),
5521        [
5522            ("user_a".to_string(), "online", "free"),
5523            ("user_c".to_string(), "offline", "free")
5524        ]
5525    );
5526    assert_eq!(contacts(&client_c, cx_c), []);
5527    assert_eq!(contacts(&client_d, cx_d), []);
5528
5529    server.allow_connections();
5530    client_c
5531        .authenticate_and_connect(false, &cx_c.to_async())
5532        .await
5533        .unwrap();
5534
5535    executor.run_until_parked();
5536    assert_eq!(
5537        contacts(&client_a, cx_a),
5538        [
5539            ("user_b".to_string(), "online", "free"),
5540            ("user_c".to_string(), "online", "free")
5541        ]
5542    );
5543    assert_eq!(
5544        contacts(&client_b, cx_b),
5545        [
5546            ("user_a".to_string(), "online", "free"),
5547            ("user_c".to_string(), "online", "free")
5548        ]
5549    );
5550    assert_eq!(
5551        contacts(&client_c, cx_c),
5552        [
5553            ("user_a".to_string(), "online", "free"),
5554            ("user_b".to_string(), "online", "free")
5555        ]
5556    );
5557    assert_eq!(contacts(&client_d, cx_d), []);
5558
5559    active_call_a
5560        .update(cx_a, |call, cx| {
5561            call.invite(client_b.user_id().unwrap(), None, cx)
5562        })
5563        .await
5564        .unwrap();
5565    executor.run_until_parked();
5566    assert_eq!(
5567        contacts(&client_a, cx_a),
5568        [
5569            ("user_b".to_string(), "online", "busy"),
5570            ("user_c".to_string(), "online", "free")
5571        ]
5572    );
5573    assert_eq!(
5574        contacts(&client_b, cx_b),
5575        [
5576            ("user_a".to_string(), "online", "busy"),
5577            ("user_c".to_string(), "online", "free")
5578        ]
5579    );
5580    assert_eq!(
5581        contacts(&client_c, cx_c),
5582        [
5583            ("user_a".to_string(), "online", "busy"),
5584            ("user_b".to_string(), "online", "busy")
5585        ]
5586    );
5587    assert_eq!(contacts(&client_d, cx_d), []);
5588
5589    // Client B and client D become contacts while client B is being called.
5590    server
5591        .make_contacts(&mut [(&client_b, cx_b), (&client_d, cx_d)])
5592        .await;
5593    executor.run_until_parked();
5594    assert_eq!(
5595        contacts(&client_a, cx_a),
5596        [
5597            ("user_b".to_string(), "online", "busy"),
5598            ("user_c".to_string(), "online", "free")
5599        ]
5600    );
5601    assert_eq!(
5602        contacts(&client_b, cx_b),
5603        [
5604            ("user_a".to_string(), "online", "busy"),
5605            ("user_c".to_string(), "online", "free"),
5606            ("user_d".to_string(), "online", "free"),
5607        ]
5608    );
5609    assert_eq!(
5610        contacts(&client_c, cx_c),
5611        [
5612            ("user_a".to_string(), "online", "busy"),
5613            ("user_b".to_string(), "online", "busy")
5614        ]
5615    );
5616    assert_eq!(
5617        contacts(&client_d, cx_d),
5618        [("user_b".to_string(), "online", "busy")]
5619    );
5620
5621    active_call_b.update(cx_b, |call, cx| call.decline_incoming(cx).unwrap());
5622    executor.run_until_parked();
5623    assert_eq!(
5624        contacts(&client_a, cx_a),
5625        [
5626            ("user_b".to_string(), "online", "free"),
5627            ("user_c".to_string(), "online", "free")
5628        ]
5629    );
5630    assert_eq!(
5631        contacts(&client_b, cx_b),
5632        [
5633            ("user_a".to_string(), "online", "free"),
5634            ("user_c".to_string(), "online", "free"),
5635            ("user_d".to_string(), "online", "free")
5636        ]
5637    );
5638    assert_eq!(
5639        contacts(&client_c, cx_c),
5640        [
5641            ("user_a".to_string(), "online", "free"),
5642            ("user_b".to_string(), "online", "free")
5643        ]
5644    );
5645    assert_eq!(
5646        contacts(&client_d, cx_d),
5647        [("user_b".to_string(), "online", "free")]
5648    );
5649
5650    active_call_c
5651        .update(cx_c, |call, cx| {
5652            call.invite(client_a.user_id().unwrap(), None, cx)
5653        })
5654        .await
5655        .unwrap();
5656    executor.run_until_parked();
5657    assert_eq!(
5658        contacts(&client_a, cx_a),
5659        [
5660            ("user_b".to_string(), "online", "free"),
5661            ("user_c".to_string(), "online", "busy")
5662        ]
5663    );
5664    assert_eq!(
5665        contacts(&client_b, cx_b),
5666        [
5667            ("user_a".to_string(), "online", "busy"),
5668            ("user_c".to_string(), "online", "busy"),
5669            ("user_d".to_string(), "online", "free")
5670        ]
5671    );
5672    assert_eq!(
5673        contacts(&client_c, cx_c),
5674        [
5675            ("user_a".to_string(), "online", "busy"),
5676            ("user_b".to_string(), "online", "free")
5677        ]
5678    );
5679    assert_eq!(
5680        contacts(&client_d, cx_d),
5681        [("user_b".to_string(), "online", "free")]
5682    );
5683
5684    active_call_a
5685        .update(cx_a, |call, cx| call.accept_incoming(cx))
5686        .await
5687        .unwrap();
5688    executor.run_until_parked();
5689    assert_eq!(
5690        contacts(&client_a, cx_a),
5691        [
5692            ("user_b".to_string(), "online", "free"),
5693            ("user_c".to_string(), "online", "busy")
5694        ]
5695    );
5696    assert_eq!(
5697        contacts(&client_b, cx_b),
5698        [
5699            ("user_a".to_string(), "online", "busy"),
5700            ("user_c".to_string(), "online", "busy"),
5701            ("user_d".to_string(), "online", "free")
5702        ]
5703    );
5704    assert_eq!(
5705        contacts(&client_c, cx_c),
5706        [
5707            ("user_a".to_string(), "online", "busy"),
5708            ("user_b".to_string(), "online", "free")
5709        ]
5710    );
5711    assert_eq!(
5712        contacts(&client_d, cx_d),
5713        [("user_b".to_string(), "online", "free")]
5714    );
5715
5716    active_call_a
5717        .update(cx_a, |call, cx| {
5718            call.invite(client_b.user_id().unwrap(), None, cx)
5719        })
5720        .await
5721        .unwrap();
5722    executor.run_until_parked();
5723    assert_eq!(
5724        contacts(&client_a, cx_a),
5725        [
5726            ("user_b".to_string(), "online", "busy"),
5727            ("user_c".to_string(), "online", "busy")
5728        ]
5729    );
5730    assert_eq!(
5731        contacts(&client_b, cx_b),
5732        [
5733            ("user_a".to_string(), "online", "busy"),
5734            ("user_c".to_string(), "online", "busy"),
5735            ("user_d".to_string(), "online", "free")
5736        ]
5737    );
5738    assert_eq!(
5739        contacts(&client_c, cx_c),
5740        [
5741            ("user_a".to_string(), "online", "busy"),
5742            ("user_b".to_string(), "online", "busy")
5743        ]
5744    );
5745    assert_eq!(
5746        contacts(&client_d, cx_d),
5747        [("user_b".to_string(), "online", "busy")]
5748    );
5749
5750    active_call_a
5751        .update(cx_a, |call, cx| call.hang_up(cx))
5752        .await
5753        .unwrap();
5754    executor.run_until_parked();
5755    assert_eq!(
5756        contacts(&client_a, cx_a),
5757        [
5758            ("user_b".to_string(), "online", "free"),
5759            ("user_c".to_string(), "online", "free")
5760        ]
5761    );
5762    assert_eq!(
5763        contacts(&client_b, cx_b),
5764        [
5765            ("user_a".to_string(), "online", "free"),
5766            ("user_c".to_string(), "online", "free"),
5767            ("user_d".to_string(), "online", "free")
5768        ]
5769    );
5770    assert_eq!(
5771        contacts(&client_c, cx_c),
5772        [
5773            ("user_a".to_string(), "online", "free"),
5774            ("user_b".to_string(), "online", "free")
5775        ]
5776    );
5777    assert_eq!(
5778        contacts(&client_d, cx_d),
5779        [("user_b".to_string(), "online", "free")]
5780    );
5781
5782    active_call_a
5783        .update(cx_a, |call, cx| {
5784            call.invite(client_b.user_id().unwrap(), None, cx)
5785        })
5786        .await
5787        .unwrap();
5788    executor.run_until_parked();
5789    assert_eq!(
5790        contacts(&client_a, cx_a),
5791        [
5792            ("user_b".to_string(), "online", "busy"),
5793            ("user_c".to_string(), "online", "free")
5794        ]
5795    );
5796    assert_eq!(
5797        contacts(&client_b, cx_b),
5798        [
5799            ("user_a".to_string(), "online", "busy"),
5800            ("user_c".to_string(), "online", "free"),
5801            ("user_d".to_string(), "online", "free")
5802        ]
5803    );
5804    assert_eq!(
5805        contacts(&client_c, cx_c),
5806        [
5807            ("user_a".to_string(), "online", "busy"),
5808            ("user_b".to_string(), "online", "busy")
5809        ]
5810    );
5811    assert_eq!(
5812        contacts(&client_d, cx_d),
5813        [("user_b".to_string(), "online", "busy")]
5814    );
5815
5816    server.forbid_connections();
5817    server.disconnect_client(client_a.peer_id().unwrap());
5818    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5819    assert_eq!(contacts(&client_a, cx_a), []);
5820    assert_eq!(
5821        contacts(&client_b, cx_b),
5822        [
5823            ("user_a".to_string(), "offline", "free"),
5824            ("user_c".to_string(), "online", "free"),
5825            ("user_d".to_string(), "online", "free")
5826        ]
5827    );
5828    assert_eq!(
5829        contacts(&client_c, cx_c),
5830        [
5831            ("user_a".to_string(), "offline", "free"),
5832            ("user_b".to_string(), "online", "free")
5833        ]
5834    );
5835    assert_eq!(
5836        contacts(&client_d, cx_d),
5837        [("user_b".to_string(), "online", "free")]
5838    );
5839
5840    // Test removing a contact
5841    client_b
5842        .user_store()
5843        .update(cx_b, |store, cx| {
5844            store.remove_contact(client_c.user_id().unwrap(), cx)
5845        })
5846        .await
5847        .unwrap();
5848    executor.run_until_parked();
5849    assert_eq!(
5850        contacts(&client_b, cx_b),
5851        [
5852            ("user_a".to_string(), "offline", "free"),
5853            ("user_d".to_string(), "online", "free")
5854        ]
5855    );
5856    assert_eq!(
5857        contacts(&client_c, cx_c),
5858        [("user_a".to_string(), "offline", "free"),]
5859    );
5860
5861    fn contacts(
5862        client: &TestClient,
5863        cx: &TestAppContext,
5864    ) -> Vec<(String, &'static str, &'static str)> {
5865        client.user_store().read_with(cx, |store, _| {
5866            store
5867                .contacts()
5868                .iter()
5869                .map(|contact| {
5870                    (
5871                        contact.user.github_login.clone(),
5872                        if contact.online { "online" } else { "offline" },
5873                        if contact.busy { "busy" } else { "free" },
5874                    )
5875                })
5876                .collect()
5877        })
5878    }
5879}
5880
5881#[gpui::test(iterations = 10)]
5882async fn test_contact_requests(
5883    executor: BackgroundExecutor,
5884    cx_a: &mut TestAppContext,
5885    cx_a2: &mut TestAppContext,
5886    cx_b: &mut TestAppContext,
5887    cx_b2: &mut TestAppContext,
5888    cx_c: &mut TestAppContext,
5889    cx_c2: &mut TestAppContext,
5890) {
5891    // Connect to a server as 3 clients.
5892    let mut server = TestServer::start(executor.clone()).await;
5893    let client_a = server.create_client(cx_a, "user_a").await;
5894    let client_a2 = server.create_client(cx_a2, "user_a").await;
5895    let client_b = server.create_client(cx_b, "user_b").await;
5896    let client_b2 = server.create_client(cx_b2, "user_b").await;
5897    let client_c = server.create_client(cx_c, "user_c").await;
5898    let client_c2 = server.create_client(cx_c2, "user_c").await;
5899
5900    assert_eq!(client_a.user_id().unwrap(), client_a2.user_id().unwrap());
5901    assert_eq!(client_b.user_id().unwrap(), client_b2.user_id().unwrap());
5902    assert_eq!(client_c.user_id().unwrap(), client_c2.user_id().unwrap());
5903
5904    // User A and User C request that user B become their contact.
5905    client_a
5906        .user_store()
5907        .update(cx_a, |store, cx| {
5908            store.request_contact(client_b.user_id().unwrap(), cx)
5909        })
5910        .await
5911        .unwrap();
5912    client_c
5913        .user_store()
5914        .update(cx_c, |store, cx| {
5915            store.request_contact(client_b.user_id().unwrap(), cx)
5916        })
5917        .await
5918        .unwrap();
5919    executor.run_until_parked();
5920
5921    // All users see the pending request appear in all their clients.
5922    assert_eq!(
5923        client_a.summarize_contacts(cx_a).outgoing_requests,
5924        &["user_b"]
5925    );
5926    assert_eq!(
5927        client_a2.summarize_contacts(cx_a2).outgoing_requests,
5928        &["user_b"]
5929    );
5930    assert_eq!(
5931        client_b.summarize_contacts(cx_b).incoming_requests,
5932        &["user_a", "user_c"]
5933    );
5934    assert_eq!(
5935        client_b2.summarize_contacts(cx_b2).incoming_requests,
5936        &["user_a", "user_c"]
5937    );
5938    assert_eq!(
5939        client_c.summarize_contacts(cx_c).outgoing_requests,
5940        &["user_b"]
5941    );
5942    assert_eq!(
5943        client_c2.summarize_contacts(cx_c2).outgoing_requests,
5944        &["user_b"]
5945    );
5946
5947    // Contact requests are present upon connecting (tested here via disconnect/reconnect)
5948    disconnect_and_reconnect(&client_a, cx_a).await;
5949    disconnect_and_reconnect(&client_b, cx_b).await;
5950    disconnect_and_reconnect(&client_c, cx_c).await;
5951    executor.run_until_parked();
5952    assert_eq!(
5953        client_a.summarize_contacts(cx_a).outgoing_requests,
5954        &["user_b"]
5955    );
5956    assert_eq!(
5957        client_b.summarize_contacts(cx_b).incoming_requests,
5958        &["user_a", "user_c"]
5959    );
5960    assert_eq!(
5961        client_c.summarize_contacts(cx_c).outgoing_requests,
5962        &["user_b"]
5963    );
5964
5965    // User B accepts the request from user A.
5966    client_b
5967        .user_store()
5968        .update(cx_b, |store, cx| {
5969            store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
5970        })
5971        .await
5972        .unwrap();
5973
5974    executor.run_until_parked();
5975
5976    // User B sees user A as their contact now in all client, and the incoming request from them is removed.
5977    let contacts_b = client_b.summarize_contacts(cx_b);
5978    assert_eq!(contacts_b.current, &["user_a"]);
5979    assert_eq!(contacts_b.incoming_requests, &["user_c"]);
5980    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
5981    assert_eq!(contacts_b2.current, &["user_a"]);
5982    assert_eq!(contacts_b2.incoming_requests, &["user_c"]);
5983
5984    // User A sees user B as their contact now in all clients, and the outgoing request to them is removed.
5985    let contacts_a = client_a.summarize_contacts(cx_a);
5986    assert_eq!(contacts_a.current, &["user_b"]);
5987    assert!(contacts_a.outgoing_requests.is_empty());
5988    let contacts_a2 = client_a2.summarize_contacts(cx_a2);
5989    assert_eq!(contacts_a2.current, &["user_b"]);
5990    assert!(contacts_a2.outgoing_requests.is_empty());
5991
5992    // Contacts are present upon connecting (tested here via disconnect/reconnect)
5993    disconnect_and_reconnect(&client_a, cx_a).await;
5994    disconnect_and_reconnect(&client_b, cx_b).await;
5995    disconnect_and_reconnect(&client_c, cx_c).await;
5996    executor.run_until_parked();
5997    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
5998    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
5999    assert_eq!(
6000        client_b.summarize_contacts(cx_b).incoming_requests,
6001        &["user_c"]
6002    );
6003    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
6004    assert_eq!(
6005        client_c.summarize_contacts(cx_c).outgoing_requests,
6006        &["user_b"]
6007    );
6008
6009    // User B rejects the request from user C.
6010    client_b
6011        .user_store()
6012        .update(cx_b, |store, cx| {
6013            store.respond_to_contact_request(client_c.user_id().unwrap(), false, cx)
6014        })
6015        .await
6016        .unwrap();
6017
6018    executor.run_until_parked();
6019
6020    // User B doesn't see user C as their contact, and the incoming request from them is removed.
6021    let contacts_b = client_b.summarize_contacts(cx_b);
6022    assert_eq!(contacts_b.current, &["user_a"]);
6023    assert!(contacts_b.incoming_requests.is_empty());
6024    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
6025    assert_eq!(contacts_b2.current, &["user_a"]);
6026    assert!(contacts_b2.incoming_requests.is_empty());
6027
6028    // User C doesn't see user B as their contact, and the outgoing request to them is removed.
6029    let contacts_c = client_c.summarize_contacts(cx_c);
6030    assert!(contacts_c.current.is_empty());
6031    assert!(contacts_c.outgoing_requests.is_empty());
6032    let contacts_c2 = client_c2.summarize_contacts(cx_c2);
6033    assert!(contacts_c2.current.is_empty());
6034    assert!(contacts_c2.outgoing_requests.is_empty());
6035
6036    // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect)
6037    disconnect_and_reconnect(&client_a, cx_a).await;
6038    disconnect_and_reconnect(&client_b, cx_b).await;
6039    disconnect_and_reconnect(&client_c, cx_c).await;
6040    executor.run_until_parked();
6041    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
6042    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
6043    assert!(client_b
6044        .summarize_contacts(cx_b)
6045        .incoming_requests
6046        .is_empty());
6047    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
6048    assert!(client_c
6049        .summarize_contacts(cx_c)
6050        .outgoing_requests
6051        .is_empty());
6052
6053    async fn disconnect_and_reconnect(client: &TestClient, cx: &mut TestAppContext) {
6054        client.disconnect(&cx.to_async());
6055        client.clear_contacts(cx).await;
6056        client
6057            .authenticate_and_connect(false, &cx.to_async())
6058            .await
6059            .unwrap();
6060    }
6061}
6062
6063// TODO: Re-enable this test once we can replace our swift Livekit SDK with the rust SDK
6064#[cfg(not(target_os = "macos"))]
6065#[gpui::test(iterations = 10)]
6066async fn test_join_call_after_screen_was_shared(
6067    executor: BackgroundExecutor,
6068    cx_a: &mut TestAppContext,
6069    cx_b: &mut TestAppContext,
6070) {
6071    let mut server = TestServer::start(executor.clone()).await;
6072
6073    let client_a = server.create_client(cx_a, "user_a").await;
6074    let client_b = server.create_client(cx_b, "user_b").await;
6075    server
6076        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6077        .await;
6078
6079    let active_call_a = cx_a.read(ActiveCall::global);
6080    let active_call_b = cx_b.read(ActiveCall::global);
6081
6082    // Call users B and C from client A.
6083    active_call_a
6084        .update(cx_a, |call, cx| {
6085            call.invite(client_b.user_id().unwrap(), None, cx)
6086        })
6087        .await
6088        .unwrap();
6089
6090    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
6091    executor.run_until_parked();
6092    assert_eq!(
6093        room_participants(&room_a, cx_a),
6094        RoomParticipants {
6095            remote: Default::default(),
6096            pending: vec!["user_b".to_string()]
6097        }
6098    );
6099
6100    // User B receives the call.
6101
6102    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
6103    let call_b = incoming_call_b.next().await.unwrap().unwrap();
6104    assert_eq!(call_b.calling_user.github_login, "user_a");
6105
6106    // User A shares their screen
6107    let display = gpui::TestScreenCaptureSource::new();
6108    cx_a.set_screen_capture_sources(vec![display]);
6109    active_call_a
6110        .update(cx_a, |call, cx| {
6111            call.room()
6112                .unwrap()
6113                .update(cx, |room, cx| room.share_screen(cx))
6114        })
6115        .await
6116        .unwrap();
6117
6118    client_b.user_store().update(cx_b, |user_store, _| {
6119        user_store.clear_cache();
6120    });
6121
6122    // User B joins the room
6123    active_call_b
6124        .update(cx_b, |call, cx| call.accept_incoming(cx))
6125        .await
6126        .unwrap();
6127
6128    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
6129    assert!(incoming_call_b.next().await.unwrap().is_none());
6130
6131    executor.run_until_parked();
6132    assert_eq!(
6133        room_participants(&room_a, cx_a),
6134        RoomParticipants {
6135            remote: vec!["user_b".to_string()],
6136            pending: vec![],
6137        }
6138    );
6139    assert_eq!(
6140        room_participants(&room_b, cx_b),
6141        RoomParticipants {
6142            remote: vec!["user_a".to_string()],
6143            pending: vec![],
6144        }
6145    );
6146
6147    // Ensure User B sees User A's screenshare.
6148
6149    room_b.read_with(cx_b, |room, _| {
6150        assert_eq!(
6151            room.remote_participants()
6152                .get(&client_a.user_id().unwrap())
6153                .unwrap()
6154                .video_tracks
6155                .len(),
6156            1
6157        );
6158    });
6159}
6160
6161#[gpui::test]
6162async fn test_right_click_menu_behind_collab_panel(cx: &mut TestAppContext) {
6163    let mut server = TestServer::start(cx.executor().clone()).await;
6164    let client_a = server.create_client(cx, "user_a").await;
6165    let (_workspace_a, cx) = client_a.build_test_workspace(cx).await;
6166
6167    cx.simulate_resize(size(px(300.), px(300.)));
6168
6169    cx.simulate_keystrokes("cmd-n cmd-n cmd-n");
6170    cx.update(|cx| cx.refresh());
6171
6172    let tab_bounds = cx.debug_bounds("TAB-2").unwrap();
6173    let new_tab_button_bounds = cx.debug_bounds("ICON-Plus").unwrap();
6174
6175    assert!(
6176        tab_bounds.intersects(&new_tab_button_bounds),
6177        "Tab should overlap with the new tab button, if this is failing check if there's been a redesign!"
6178    );
6179
6180    cx.simulate_event(MouseDownEvent {
6181        button: MouseButton::Right,
6182        position: new_tab_button_bounds.center(),
6183        modifiers: Modifiers::default(),
6184        click_count: 1,
6185        first_mouse: false,
6186    });
6187
6188    // regression test that the right click menu for tabs does not open.
6189    assert!(cx.debug_bounds("MENU_ITEM-Close").is_none());
6190
6191    let tab_bounds = cx.debug_bounds("TAB-1").unwrap();
6192    cx.simulate_event(MouseDownEvent {
6193        button: MouseButton::Right,
6194        position: tab_bounds.center(),
6195        modifiers: Modifiers::default(),
6196        click_count: 1,
6197        first_mouse: false,
6198    });
6199    assert!(cx.debug_bounds("MENU_ITEM-Close").is_some());
6200}
6201
6202#[gpui::test]
6203async fn test_pane_split_left(cx: &mut TestAppContext) {
6204    let (_, client) = TestServer::start1(cx).await;
6205    let (workspace, cx) = client.build_test_workspace(cx).await;
6206
6207    cx.simulate_keystrokes("cmd-n");
6208    workspace.update(cx, |workspace, cx| {
6209        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 1);
6210    });
6211    cx.simulate_keystrokes("cmd-k left");
6212    workspace.update(cx, |workspace, cx| {
6213        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 2);
6214    });
6215    cx.simulate_keystrokes("cmd-k");
6216    // sleep for longer than the timeout in keyboard shortcut handling
6217    // to verify that it doesn't fire in this case.
6218    cx.executor().advance_clock(Duration::from_secs(2));
6219    cx.simulate_keystrokes("left");
6220    workspace.update(cx, |workspace, cx| {
6221        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 2);
6222    });
6223}
6224
6225#[gpui::test]
6226async fn test_join_after_restart(cx1: &mut TestAppContext, cx2: &mut TestAppContext) {
6227    let (mut server, client) = TestServer::start1(cx1).await;
6228    let channel1 = server.make_public_channel("channel1", &client, cx1).await;
6229    let channel2 = server.make_public_channel("channel2", &client, cx1).await;
6230
6231    join_channel(channel1, &client, cx1).await.unwrap();
6232    drop(client);
6233
6234    let client2 = server.create_client(cx2, "user_a").await;
6235    join_channel(channel2, &client2, cx2).await.unwrap();
6236}
6237
6238#[gpui::test]
6239async fn test_preview_tabs(cx: &mut TestAppContext) {
6240    let (_server, client) = TestServer::start1(cx).await;
6241    let (workspace, cx) = client.build_test_workspace(cx).await;
6242    let project = workspace.update(cx, |workspace, _| workspace.project().clone());
6243
6244    let worktree_id = project.update(cx, |project, cx| {
6245        project.worktrees(cx).next().unwrap().read(cx).id()
6246    });
6247
6248    let path_1 = ProjectPath {
6249        worktree_id,
6250        path: Path::new("1.txt").into(),
6251    };
6252    let path_2 = ProjectPath {
6253        worktree_id,
6254        path: Path::new("2.js").into(),
6255    };
6256    let path_3 = ProjectPath {
6257        worktree_id,
6258        path: Path::new("3.rs").into(),
6259    };
6260
6261    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
6262
6263    let get_path = |pane: &Pane, idx: usize, cx: &AppContext| {
6264        pane.item_for_index(idx).unwrap().project_path(cx).unwrap()
6265    };
6266
6267    // Opening item 3 as a "permanent" tab
6268    workspace
6269        .update(cx, |workspace, cx| {
6270            workspace.open_path(path_3.clone(), None, false, cx)
6271        })
6272        .await
6273        .unwrap();
6274
6275    pane.update(cx, |pane, cx| {
6276        assert_eq!(pane.items_len(), 1);
6277        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6278        assert_eq!(pane.preview_item_id(), None);
6279
6280        assert!(!pane.can_navigate_backward());
6281        assert!(!pane.can_navigate_forward());
6282    });
6283
6284    // Open item 1 as preview
6285    workspace
6286        .update(cx, |workspace, cx| {
6287            workspace.open_path_preview(path_1.clone(), None, true, true, cx)
6288        })
6289        .await
6290        .unwrap();
6291
6292    pane.update(cx, |pane, cx| {
6293        assert_eq!(pane.items_len(), 2);
6294        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6295        assert_eq!(get_path(pane, 1, cx), path_1.clone());
6296        assert_eq!(
6297            pane.preview_item_id(),
6298            Some(pane.items().nth(1).unwrap().item_id())
6299        );
6300
6301        assert!(pane.can_navigate_backward());
6302        assert!(!pane.can_navigate_forward());
6303    });
6304
6305    // Open item 2 as preview
6306    workspace
6307        .update(cx, |workspace, cx| {
6308            workspace.open_path_preview(path_2.clone(), None, true, true, cx)
6309        })
6310        .await
6311        .unwrap();
6312
6313    pane.update(cx, |pane, cx| {
6314        assert_eq!(pane.items_len(), 2);
6315        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6316        assert_eq!(get_path(pane, 1, cx), path_2.clone());
6317        assert_eq!(
6318            pane.preview_item_id(),
6319            Some(pane.items().nth(1).unwrap().item_id())
6320        );
6321
6322        assert!(pane.can_navigate_backward());
6323        assert!(!pane.can_navigate_forward());
6324    });
6325
6326    // Going back should show item 1 as preview
6327    workspace
6328        .update(cx, |workspace, cx| workspace.go_back(pane.downgrade(), cx))
6329        .await
6330        .unwrap();
6331
6332    pane.update(cx, |pane, cx| {
6333        assert_eq!(pane.items_len(), 2);
6334        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6335        assert_eq!(get_path(pane, 1, cx), path_1.clone());
6336        assert_eq!(
6337            pane.preview_item_id(),
6338            Some(pane.items().nth(1).unwrap().item_id())
6339        );
6340
6341        assert!(pane.can_navigate_backward());
6342        assert!(pane.can_navigate_forward());
6343    });
6344
6345    // Closing item 1
6346    pane.update(cx, |pane, cx| {
6347        pane.close_item_by_id(
6348            pane.active_item().unwrap().item_id(),
6349            workspace::SaveIntent::Skip,
6350            cx,
6351        )
6352    })
6353    .await
6354    .unwrap();
6355
6356    pane.update(cx, |pane, cx| {
6357        assert_eq!(pane.items_len(), 1);
6358        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6359        assert_eq!(pane.preview_item_id(), None);
6360
6361        assert!(pane.can_navigate_backward());
6362        assert!(!pane.can_navigate_forward());
6363    });
6364
6365    // Going back should show item 1 as preview
6366    workspace
6367        .update(cx, |workspace, cx| workspace.go_back(pane.downgrade(), cx))
6368        .await
6369        .unwrap();
6370
6371    pane.update(cx, |pane, cx| {
6372        assert_eq!(pane.items_len(), 2);
6373        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6374        assert_eq!(get_path(pane, 1, cx), path_1.clone());
6375        assert_eq!(
6376            pane.preview_item_id(),
6377            Some(pane.items().nth(1).unwrap().item_id())
6378        );
6379
6380        assert!(pane.can_navigate_backward());
6381        assert!(pane.can_navigate_forward());
6382    });
6383
6384    // Close permanent tab
6385    pane.update(cx, |pane, cx| {
6386        let id = pane.items().next().unwrap().item_id();
6387        pane.close_item_by_id(id, workspace::SaveIntent::Skip, cx)
6388    })
6389    .await
6390    .unwrap();
6391
6392    pane.update(cx, |pane, cx| {
6393        assert_eq!(pane.items_len(), 1);
6394        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6395        assert_eq!(
6396            pane.preview_item_id(),
6397            Some(pane.items().next().unwrap().item_id())
6398        );
6399
6400        assert!(pane.can_navigate_backward());
6401        assert!(pane.can_navigate_forward());
6402    });
6403
6404    // Split pane to the right
6405    pane.update(cx, |pane, cx| {
6406        pane.split(workspace::SplitDirection::Right, cx);
6407    });
6408
6409    let right_pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
6410
6411    pane.update(cx, |pane, cx| {
6412        assert_eq!(pane.items_len(), 1);
6413        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6414        assert_eq!(
6415            pane.preview_item_id(),
6416            Some(pane.items().next().unwrap().item_id())
6417        );
6418
6419        assert!(pane.can_navigate_backward());
6420        assert!(pane.can_navigate_forward());
6421    });
6422
6423    right_pane.update(cx, |pane, cx| {
6424        assert_eq!(pane.items_len(), 1);
6425        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6426        assert_eq!(pane.preview_item_id(), None);
6427
6428        assert!(!pane.can_navigate_backward());
6429        assert!(!pane.can_navigate_forward());
6430    });
6431
6432    // Open item 2 as preview in right pane
6433    workspace
6434        .update(cx, |workspace, cx| {
6435            workspace.open_path_preview(path_2.clone(), None, true, true, cx)
6436        })
6437        .await
6438        .unwrap();
6439
6440    pane.update(cx, |pane, cx| {
6441        assert_eq!(pane.items_len(), 1);
6442        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6443        assert_eq!(
6444            pane.preview_item_id(),
6445            Some(pane.items().next().unwrap().item_id())
6446        );
6447
6448        assert!(pane.can_navigate_backward());
6449        assert!(pane.can_navigate_forward());
6450    });
6451
6452    right_pane.update(cx, |pane, cx| {
6453        assert_eq!(pane.items_len(), 2);
6454        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6455        assert_eq!(get_path(pane, 1, cx), path_2.clone());
6456        assert_eq!(
6457            pane.preview_item_id(),
6458            Some(pane.items().nth(1).unwrap().item_id())
6459        );
6460
6461        assert!(pane.can_navigate_backward());
6462        assert!(!pane.can_navigate_forward());
6463    });
6464
6465    // Focus left pane
6466    workspace.update(cx, |workspace, cx| {
6467        workspace.activate_pane_in_direction(workspace::SplitDirection::Left, cx)
6468    });
6469
6470    // Open item 2 as preview in left pane
6471    workspace
6472        .update(cx, |workspace, cx| {
6473            workspace.open_path_preview(path_2.clone(), None, true, true, cx)
6474        })
6475        .await
6476        .unwrap();
6477
6478    pane.update(cx, |pane, cx| {
6479        assert_eq!(pane.items_len(), 1);
6480        assert_eq!(get_path(pane, 0, cx), path_2.clone());
6481        assert_eq!(
6482            pane.preview_item_id(),
6483            Some(pane.items().next().unwrap().item_id())
6484        );
6485
6486        assert!(pane.can_navigate_backward());
6487        assert!(!pane.can_navigate_forward());
6488    });
6489
6490    right_pane.update(cx, |pane, cx| {
6491        assert_eq!(pane.items_len(), 2);
6492        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6493        assert_eq!(get_path(pane, 1, cx), path_2.clone());
6494        assert_eq!(
6495            pane.preview_item_id(),
6496            Some(pane.items().nth(1).unwrap().item_id())
6497        );
6498
6499        assert!(pane.can_navigate_backward());
6500        assert!(!pane.can_navigate_forward());
6501    });
6502}
6503
6504#[gpui::test(iterations = 10)]
6505async fn test_context_collaboration_with_reconnect(
6506    executor: BackgroundExecutor,
6507    cx_a: &mut TestAppContext,
6508    cx_b: &mut TestAppContext,
6509) {
6510    let mut server = TestServer::start(executor.clone()).await;
6511    let client_a = server.create_client(cx_a, "user_a").await;
6512    let client_b = server.create_client(cx_b, "user_b").await;
6513    server
6514        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6515        .await;
6516    let active_call_a = cx_a.read(ActiveCall::global);
6517
6518    client_a.fs().insert_tree("/a", Default::default()).await;
6519    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
6520    let project_id = active_call_a
6521        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6522        .await
6523        .unwrap();
6524    let project_b = client_b.join_remote_project(project_id, cx_b).await;
6525
6526    // Client A sees that a guest has joined.
6527    executor.run_until_parked();
6528
6529    project_a.read_with(cx_a, |project, _| {
6530        assert_eq!(project.collaborators().len(), 1);
6531    });
6532    project_b.read_with(cx_b, |project, _| {
6533        assert_eq!(project.collaborators().len(), 1);
6534    });
6535
6536    cx_a.update(context_server::init);
6537    cx_b.update(context_server::init);
6538    let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
6539    let context_store_a = cx_a
6540        .update(|cx| {
6541            ContextStore::new(
6542                project_a.clone(),
6543                prompt_builder.clone(),
6544                Arc::new(SlashCommandWorkingSet::default()),
6545                Arc::new(ToolWorkingSet::default()),
6546                cx,
6547            )
6548        })
6549        .await
6550        .unwrap();
6551    let context_store_b = cx_b
6552        .update(|cx| {
6553            ContextStore::new(
6554                project_b.clone(),
6555                prompt_builder.clone(),
6556                Arc::new(SlashCommandWorkingSet::default()),
6557                Arc::new(ToolWorkingSet::default()),
6558                cx,
6559            )
6560        })
6561        .await
6562        .unwrap();
6563
6564    // Client A creates a new chats.
6565    let context_a = context_store_a.update(cx_a, |store, cx| store.create(cx));
6566    executor.run_until_parked();
6567
6568    // Client B retrieves host's contexts and joins one.
6569    let context_b = context_store_b
6570        .update(cx_b, |store, cx| {
6571            let host_contexts = store.host_contexts().to_vec();
6572            assert_eq!(host_contexts.len(), 1);
6573            store.open_remote_context(host_contexts[0].id.clone(), cx)
6574        })
6575        .await
6576        .unwrap();
6577
6578    // Host and guest make changes
6579    context_a.update(cx_a, |context, cx| {
6580        context.buffer().update(cx, |buffer, cx| {
6581            buffer.edit([(0..0, "Host change\n")], None, cx)
6582        })
6583    });
6584    context_b.update(cx_b, |context, cx| {
6585        context.buffer().update(cx, |buffer, cx| {
6586            buffer.edit([(0..0, "Guest change\n")], None, cx)
6587        })
6588    });
6589    executor.run_until_parked();
6590    assert_eq!(
6591        context_a.read_with(cx_a, |context, cx| context.buffer().read(cx).text()),
6592        "Guest change\nHost change\n"
6593    );
6594    assert_eq!(
6595        context_b.read_with(cx_b, |context, cx| context.buffer().read(cx).text()),
6596        "Guest change\nHost change\n"
6597    );
6598
6599    // Disconnect client A and make some changes while disconnected.
6600    server.disconnect_client(client_a.peer_id().unwrap());
6601    server.forbid_connections();
6602    context_a.update(cx_a, |context, cx| {
6603        context.buffer().update(cx, |buffer, cx| {
6604            buffer.edit([(0..0, "Host offline change\n")], None, cx)
6605        })
6606    });
6607    context_b.update(cx_b, |context, cx| {
6608        context.buffer().update(cx, |buffer, cx| {
6609            buffer.edit([(0..0, "Guest offline change\n")], None, cx)
6610        })
6611    });
6612    executor.run_until_parked();
6613    assert_eq!(
6614        context_a.read_with(cx_a, |context, cx| context.buffer().read(cx).text()),
6615        "Host offline change\nGuest change\nHost change\n"
6616    );
6617    assert_eq!(
6618        context_b.read_with(cx_b, |context, cx| context.buffer().read(cx).text()),
6619        "Guest offline change\nGuest change\nHost change\n"
6620    );
6621
6622    // Allow client A to reconnect and verify that contexts converge.
6623    server.allow_connections();
6624    executor.advance_clock(RECEIVE_TIMEOUT);
6625    assert_eq!(
6626        context_a.read_with(cx_a, |context, cx| context.buffer().read(cx).text()),
6627        "Guest offline change\nHost offline change\nGuest change\nHost change\n"
6628    );
6629    assert_eq!(
6630        context_b.read_with(cx_b, |context, cx| context.buffer().read(cx).text()),
6631        "Guest offline change\nHost offline change\nGuest change\nHost change\n"
6632    );
6633
6634    // Client A disconnects without being able to reconnect. Context B becomes readonly.
6635    server.forbid_connections();
6636    server.disconnect_client(client_a.peer_id().unwrap());
6637    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
6638    context_b.read_with(cx_b, |context, cx| {
6639        assert!(context.buffer().read(cx).read_only());
6640    });
6641}
6642
6643#[gpui::test]
6644async fn test_remote_git_branches(
6645    executor: BackgroundExecutor,
6646    cx_a: &mut TestAppContext,
6647    cx_b: &mut TestAppContext,
6648) {
6649    let mut server = TestServer::start(executor.clone()).await;
6650    let client_a = server.create_client(cx_a, "user_a").await;
6651    let client_b = server.create_client(cx_b, "user_b").await;
6652    server
6653        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6654        .await;
6655    let active_call_a = cx_a.read(ActiveCall::global);
6656
6657    client_a
6658        .fs()
6659        .insert_tree("/project", serde_json::json!({ ".git":{} }))
6660        .await;
6661    let branches = ["main", "dev", "feature-1"];
6662    client_a
6663        .fs()
6664        .insert_branches(Path::new("/project/.git"), &branches);
6665    let branches_set = branches
6666        .into_iter()
6667        .map(ToString::to_string)
6668        .collect::<HashSet<_>>();
6669
6670    let (project_a, worktree_id) = client_a.build_local_project("/project", cx_a).await;
6671    let project_id = active_call_a
6672        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6673        .await
6674        .unwrap();
6675    let project_b = client_b.join_remote_project(project_id, cx_b).await;
6676
6677    let root_path = ProjectPath::root_path(worktree_id);
6678    // Client A sees that a guest has joined.
6679    executor.run_until_parked();
6680
6681    let branches_b = cx_b
6682        .update(|cx| project_b.update(cx, |project, cx| project.branches(root_path.clone(), cx)))
6683        .await
6684        .unwrap();
6685
6686    let new_branch = branches[2];
6687
6688    let branches_b = branches_b
6689        .into_iter()
6690        .map(|branch| branch.name.to_string())
6691        .collect::<HashSet<_>>();
6692
6693    assert_eq!(branches_b, branches_set);
6694
6695    cx_b.update(|cx| {
6696        project_b.update(cx, |project, cx| {
6697            project.update_or_create_branch(root_path.clone(), new_branch.to_string(), cx)
6698        })
6699    })
6700    .await
6701    .unwrap();
6702
6703    executor.run_until_parked();
6704
6705    let host_branch = cx_a.update(|cx| {
6706        project_a.update(cx, |project, cx| {
6707            project.worktree_store().update(cx, |worktree_store, cx| {
6708                worktree_store
6709                    .current_branch(root_path.clone(), cx)
6710                    .unwrap()
6711            })
6712        })
6713    });
6714
6715    assert_eq!(host_branch.as_ref(), branches[2]);
6716
6717    // Also try creating a new branch
6718    cx_b.update(|cx| {
6719        project_b.update(cx, |project, cx| {
6720            project.update_or_create_branch(root_path.clone(), "totally-new-branch".to_string(), cx)
6721        })
6722    })
6723    .await
6724    .unwrap();
6725
6726    executor.run_until_parked();
6727
6728    let host_branch = cx_a.update(|cx| {
6729        project_a.update(cx, |project, cx| {
6730            project.worktree_store().update(cx, |worktree_store, cx| {
6731                worktree_store.current_branch(root_path, cx).unwrap()
6732            })
6733        })
6734    });
6735
6736    assert_eq!(host_branch.as_ref(), "totally-new-branch");
6737}