integration_tests.rs

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