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