integration_tests.rs

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