integration_tests.rs

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