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