integration_tests.rs

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