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!(repo.read(cx).status_for_path(&file.into()), status);
3076    }
3077
3078    project_local.read_with(cx_a, |project, cx| {
3079        assert_status("a.txt", Some(A_STATUS_START), project, cx);
3080        assert_status("b.txt", Some(B_STATUS_START), project, cx);
3081        assert_status("c.txt", None, project, cx);
3082    });
3083
3084    project_remote.read_with(cx_b, |project, cx| {
3085        assert_status("a.txt", Some(A_STATUS_START), project, cx);
3086        assert_status("b.txt", Some(B_STATUS_START), project, cx);
3087        assert_status("c.txt", None, project, cx);
3088    });
3089
3090    const A_STATUS_END: FileStatus = FileStatus::Tracked(TrackedStatus {
3091        index_status: StatusCode::Added,
3092        worktree_status: StatusCode::Unmodified,
3093    });
3094    const B_STATUS_END: FileStatus = FileStatus::Tracked(TrackedStatus {
3095        index_status: StatusCode::Deleted,
3096        worktree_status: StatusCode::Added,
3097    });
3098    const C_STATUS_END: FileStatus = FileStatus::Tracked(TrackedStatus {
3099        index_status: StatusCode::Unmodified,
3100        worktree_status: StatusCode::Modified,
3101    });
3102
3103    // Delete b.txt from the index, mark conflict as resolved,
3104    // and modify c.txt in the working copy.
3105    client_a.fs().set_index_for_repo(
3106        path!("/dir/.git").as_ref(),
3107        &[("a.txt".into(), "a".into()), ("c.txt".into(), "c".into())],
3108    );
3109    client_a
3110        .fs()
3111        .set_unmerged_paths_for_repo(path!("/dir/.git").as_ref(), &[]);
3112    client_a
3113        .fs()
3114        .atomic_write(path!("/dir/c.txt").into(), "CC".into())
3115        .await
3116        .unwrap();
3117
3118    // Wait for buffer_local_a to receive it
3119    executor.run_until_parked();
3120
3121    // Smoke test status reading
3122    project_local.read_with(cx_a, |project, cx| {
3123        assert_status("a.txt", Some(A_STATUS_END), project, cx);
3124        assert_status("b.txt", Some(B_STATUS_END), project, cx);
3125        assert_status("c.txt", Some(C_STATUS_END), project, cx);
3126    });
3127
3128    project_remote.read_with(cx_b, |project, cx| {
3129        assert_status("a.txt", Some(A_STATUS_END), project, cx);
3130        assert_status("b.txt", Some(B_STATUS_END), project, cx);
3131        assert_status("c.txt", Some(C_STATUS_END), project, cx);
3132    });
3133
3134    // And synchronization while joining
3135    let project_remote_c = client_c.join_remote_project(project_id, cx_c).await;
3136    executor.run_until_parked();
3137
3138    project_remote_c.read_with(cx_c, |project, cx| {
3139        assert_status("a.txt", Some(A_STATUS_END), project, cx);
3140        assert_status("b.txt", Some(B_STATUS_END), project, cx);
3141        assert_status("c.txt", Some(C_STATUS_END), project, cx);
3142    });
3143
3144    // Now remove the original git repository and check that collaborators are notified.
3145    client_a
3146        .fs()
3147        .remove_dir(path!("/dir/.git").as_ref(), RemoveOptions::default())
3148        .await
3149        .unwrap();
3150
3151    executor.run_until_parked();
3152    project_remote.update(cx_b, |project, cx| {
3153        pretty_assertions::assert_eq!(
3154            project.git_store().read(cx).repo_snapshots(cx),
3155            HashMap::default()
3156        );
3157    });
3158    project_remote_c.update(cx_c, |project, cx| {
3159        pretty_assertions::assert_eq!(
3160            project.git_store().read(cx).repo_snapshots(cx),
3161            HashMap::default()
3162        );
3163    });
3164}
3165
3166#[gpui::test(iterations = 10)]
3167async fn test_fs_operations(
3168    executor: BackgroundExecutor,
3169    cx_a: &mut TestAppContext,
3170    cx_b: &mut TestAppContext,
3171) {
3172    let mut server = TestServer::start(executor.clone()).await;
3173    let client_a = server.create_client(cx_a, "user_a").await;
3174    let client_b = server.create_client(cx_b, "user_b").await;
3175    server
3176        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3177        .await;
3178    let active_call_a = cx_a.read(ActiveCall::global);
3179
3180    client_a
3181        .fs()
3182        .insert_tree(
3183            path!("/dir"),
3184            json!({
3185                "a.txt": "a-contents",
3186                "b.txt": "b-contents",
3187            }),
3188        )
3189        .await;
3190    let (project_a, worktree_id) = client_a.build_local_project(path!("/dir"), cx_a).await;
3191    let project_id = active_call_a
3192        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3193        .await
3194        .unwrap();
3195    let project_b = client_b.join_remote_project(project_id, cx_b).await;
3196
3197    let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
3198    let worktree_b = project_b.read_with(cx_b, |project, cx| project.worktrees(cx).next().unwrap());
3199
3200    let entry = project_b
3201        .update(cx_b, |project, cx| {
3202            project.create_entry((worktree_id, "c.txt"), false, cx)
3203        })
3204        .await
3205        .unwrap()
3206        .into_included()
3207        .unwrap();
3208
3209    worktree_a.read_with(cx_a, |worktree, _| {
3210        assert_eq!(
3211            worktree
3212                .paths()
3213                .map(|p| p.to_string_lossy())
3214                .collect::<Vec<_>>(),
3215            ["a.txt", "b.txt", "c.txt"]
3216        );
3217    });
3218
3219    worktree_b.read_with(cx_b, |worktree, _| {
3220        assert_eq!(
3221            worktree
3222                .paths()
3223                .map(|p| p.to_string_lossy())
3224                .collect::<Vec<_>>(),
3225            ["a.txt", "b.txt", "c.txt"]
3226        );
3227    });
3228
3229    project_b
3230        .update(cx_b, |project, cx| {
3231            project.rename_entry(entry.id, Path::new("d.txt"), cx)
3232        })
3233        .await
3234        .unwrap()
3235        .into_included()
3236        .unwrap();
3237
3238    worktree_a.read_with(cx_a, |worktree, _| {
3239        assert_eq!(
3240            worktree
3241                .paths()
3242                .map(|p| p.to_string_lossy())
3243                .collect::<Vec<_>>(),
3244            ["a.txt", "b.txt", "d.txt"]
3245        );
3246    });
3247
3248    worktree_b.read_with(cx_b, |worktree, _| {
3249        assert_eq!(
3250            worktree
3251                .paths()
3252                .map(|p| p.to_string_lossy())
3253                .collect::<Vec<_>>(),
3254            ["a.txt", "b.txt", "d.txt"]
3255        );
3256    });
3257
3258    let dir_entry = project_b
3259        .update(cx_b, |project, cx| {
3260            project.create_entry((worktree_id, "DIR"), true, cx)
3261        })
3262        .await
3263        .unwrap()
3264        .into_included()
3265        .unwrap();
3266
3267    worktree_a.read_with(cx_a, |worktree, _| {
3268        assert_eq!(
3269            worktree
3270                .paths()
3271                .map(|p| p.to_string_lossy())
3272                .collect::<Vec<_>>(),
3273            ["DIR", "a.txt", "b.txt", "d.txt"]
3274        );
3275    });
3276
3277    worktree_b.read_with(cx_b, |worktree, _| {
3278        assert_eq!(
3279            worktree
3280                .paths()
3281                .map(|p| p.to_string_lossy())
3282                .collect::<Vec<_>>(),
3283            ["DIR", "a.txt", "b.txt", "d.txt"]
3284        );
3285    });
3286
3287    project_b
3288        .update(cx_b, |project, cx| {
3289            project.create_entry((worktree_id, "DIR/e.txt"), false, cx)
3290        })
3291        .await
3292        .unwrap()
3293        .into_included()
3294        .unwrap();
3295
3296    project_b
3297        .update(cx_b, |project, cx| {
3298            project.create_entry((worktree_id, "DIR/SUBDIR"), true, cx)
3299        })
3300        .await
3301        .unwrap()
3302        .into_included()
3303        .unwrap();
3304
3305    project_b
3306        .update(cx_b, |project, cx| {
3307            project.create_entry((worktree_id, "DIR/SUBDIR/f.txt"), false, cx)
3308        })
3309        .await
3310        .unwrap()
3311        .into_included()
3312        .unwrap();
3313
3314    worktree_a.read_with(cx_a, |worktree, _| {
3315        assert_eq!(
3316            worktree
3317                .paths()
3318                .map(|p| p.to_string_lossy())
3319                .collect::<Vec<_>>(),
3320            [
3321                path!("DIR"),
3322                path!("DIR/SUBDIR"),
3323                path!("DIR/SUBDIR/f.txt"),
3324                path!("DIR/e.txt"),
3325                path!("a.txt"),
3326                path!("b.txt"),
3327                path!("d.txt")
3328            ]
3329        );
3330    });
3331
3332    worktree_b.read_with(cx_b, |worktree, _| {
3333        assert_eq!(
3334            worktree
3335                .paths()
3336                .map(|p| p.to_string_lossy())
3337                .collect::<Vec<_>>(),
3338            [
3339                path!("DIR"),
3340                path!("DIR/SUBDIR"),
3341                path!("DIR/SUBDIR/f.txt"),
3342                path!("DIR/e.txt"),
3343                path!("a.txt"),
3344                path!("b.txt"),
3345                path!("d.txt")
3346            ]
3347        );
3348    });
3349
3350    project_b
3351        .update(cx_b, |project, cx| {
3352            project.copy_entry(entry.id, None, Path::new("f.txt"), cx)
3353        })
3354        .await
3355        .unwrap()
3356        .unwrap();
3357
3358    worktree_a.read_with(cx_a, |worktree, _| {
3359        assert_eq!(
3360            worktree
3361                .paths()
3362                .map(|p| p.to_string_lossy())
3363                .collect::<Vec<_>>(),
3364            [
3365                path!("DIR"),
3366                path!("DIR/SUBDIR"),
3367                path!("DIR/SUBDIR/f.txt"),
3368                path!("DIR/e.txt"),
3369                path!("a.txt"),
3370                path!("b.txt"),
3371                path!("d.txt"),
3372                path!("f.txt")
3373            ]
3374        );
3375    });
3376
3377    worktree_b.read_with(cx_b, |worktree, _| {
3378        assert_eq!(
3379            worktree
3380                .paths()
3381                .map(|p| p.to_string_lossy())
3382                .collect::<Vec<_>>(),
3383            [
3384                path!("DIR"),
3385                path!("DIR/SUBDIR"),
3386                path!("DIR/SUBDIR/f.txt"),
3387                path!("DIR/e.txt"),
3388                path!("a.txt"),
3389                path!("b.txt"),
3390                path!("d.txt"),
3391                path!("f.txt")
3392            ]
3393        );
3394    });
3395
3396    project_b
3397        .update(cx_b, |project, cx| {
3398            project.delete_entry(dir_entry.id, false, cx).unwrap()
3399        })
3400        .await
3401        .unwrap();
3402    executor.run_until_parked();
3403
3404    worktree_a.read_with(cx_a, |worktree, _| {
3405        assert_eq!(
3406            worktree
3407                .paths()
3408                .map(|p| p.to_string_lossy())
3409                .collect::<Vec<_>>(),
3410            ["a.txt", "b.txt", "d.txt", "f.txt"]
3411        );
3412    });
3413
3414    worktree_b.read_with(cx_b, |worktree, _| {
3415        assert_eq!(
3416            worktree
3417                .paths()
3418                .map(|p| p.to_string_lossy())
3419                .collect::<Vec<_>>(),
3420            ["a.txt", "b.txt", "d.txt", "f.txt"]
3421        );
3422    });
3423
3424    project_b
3425        .update(cx_b, |project, cx| {
3426            project.delete_entry(entry.id, false, cx).unwrap()
3427        })
3428        .await
3429        .unwrap();
3430
3431    worktree_a.read_with(cx_a, |worktree, _| {
3432        assert_eq!(
3433            worktree
3434                .paths()
3435                .map(|p| p.to_string_lossy())
3436                .collect::<Vec<_>>(),
3437            ["a.txt", "b.txt", "f.txt"]
3438        );
3439    });
3440
3441    worktree_b.read_with(cx_b, |worktree, _| {
3442        assert_eq!(
3443            worktree
3444                .paths()
3445                .map(|p| p.to_string_lossy())
3446                .collect::<Vec<_>>(),
3447            ["a.txt", "b.txt", "f.txt"]
3448        );
3449    });
3450}
3451
3452#[gpui::test(iterations = 10)]
3453async fn test_local_settings(
3454    executor: BackgroundExecutor,
3455    cx_a: &mut TestAppContext,
3456    cx_b: &mut TestAppContext,
3457) {
3458    let mut server = TestServer::start(executor.clone()).await;
3459    let client_a = server.create_client(cx_a, "user_a").await;
3460    let client_b = server.create_client(cx_b, "user_b").await;
3461    server
3462        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3463        .await;
3464    let active_call_a = cx_a.read(ActiveCall::global);
3465
3466    // As client A, open a project that contains some local settings files
3467    client_a
3468        .fs()
3469        .insert_tree(
3470            "/dir",
3471            json!({
3472                ".zed": {
3473                    "settings.json": r#"{ "tab_size": 2 }"#
3474                },
3475                "a": {
3476                    ".zed": {
3477                        "settings.json": r#"{ "tab_size": 8 }"#
3478                    },
3479                    "a.txt": "a-contents",
3480                },
3481                "b": {
3482                    "b.txt": "b-contents",
3483                }
3484            }),
3485        )
3486        .await;
3487    let (project_a, _) = client_a.build_local_project("/dir", cx_a).await;
3488    executor.run_until_parked();
3489    let project_id = active_call_a
3490        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3491        .await
3492        .unwrap();
3493    executor.run_until_parked();
3494
3495    // As client B, join that project and observe the local settings.
3496    let project_b = client_b.join_remote_project(project_id, cx_b).await;
3497
3498    let worktree_b = project_b.read_with(cx_b, |project, cx| project.worktrees(cx).next().unwrap());
3499    executor.run_until_parked();
3500    cx_b.read(|cx| {
3501        let store = cx.global::<SettingsStore>();
3502        assert_eq!(
3503            store
3504                .local_settings(worktree_b.read(cx).id())
3505                .collect::<Vec<_>>(),
3506            &[
3507                (Path::new("").into(), r#"{"tab_size":2}"#.to_string()),
3508                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3509            ]
3510        )
3511    });
3512
3513    // As client A, update a settings file. As Client B, see the changed settings.
3514    client_a
3515        .fs()
3516        .insert_file("/dir/.zed/settings.json", r#"{}"#.into())
3517        .await;
3518    executor.run_until_parked();
3519    cx_b.read(|cx| {
3520        let store = cx.global::<SettingsStore>();
3521        assert_eq!(
3522            store
3523                .local_settings(worktree_b.read(cx).id())
3524                .collect::<Vec<_>>(),
3525            &[
3526                (Path::new("").into(), r#"{}"#.to_string()),
3527                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3528            ]
3529        )
3530    });
3531
3532    // As client A, create and remove some settings files. As client B, see the changed settings.
3533    client_a
3534        .fs()
3535        .remove_file("/dir/.zed/settings.json".as_ref(), Default::default())
3536        .await
3537        .unwrap();
3538    client_a
3539        .fs()
3540        .create_dir("/dir/b/.zed".as_ref())
3541        .await
3542        .unwrap();
3543    client_a
3544        .fs()
3545        .insert_file("/dir/b/.zed/settings.json", r#"{"tab_size": 4}"#.into())
3546        .await;
3547    executor.run_until_parked();
3548    cx_b.read(|cx| {
3549        let store = cx.global::<SettingsStore>();
3550        assert_eq!(
3551            store
3552                .local_settings(worktree_b.read(cx).id())
3553                .collect::<Vec<_>>(),
3554            &[
3555                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3556                (Path::new("b").into(), r#"{"tab_size":4}"#.to_string()),
3557            ]
3558        )
3559    });
3560
3561    // As client B, disconnect.
3562    server.forbid_connections();
3563    server.disconnect_client(client_b.peer_id().unwrap());
3564
3565    // As client A, change and remove settings files while client B is disconnected.
3566    client_a
3567        .fs()
3568        .insert_file("/dir/a/.zed/settings.json", r#"{"hard_tabs":true}"#.into())
3569        .await;
3570    client_a
3571        .fs()
3572        .remove_file("/dir/b/.zed/settings.json".as_ref(), Default::default())
3573        .await
3574        .unwrap();
3575    executor.run_until_parked();
3576
3577    // As client B, reconnect and see the changed settings.
3578    server.allow_connections();
3579    executor.advance_clock(RECEIVE_TIMEOUT);
3580    cx_b.read(|cx| {
3581        let store = cx.global::<SettingsStore>();
3582        assert_eq!(
3583            store
3584                .local_settings(worktree_b.read(cx).id())
3585                .collect::<Vec<_>>(),
3586            &[(Path::new("a").into(), r#"{"hard_tabs":true}"#.to_string()),]
3587        )
3588    });
3589}
3590
3591#[gpui::test(iterations = 10)]
3592async fn test_buffer_conflict_after_save(
3593    executor: BackgroundExecutor,
3594    cx_a: &mut TestAppContext,
3595    cx_b: &mut TestAppContext,
3596) {
3597    let mut server = TestServer::start(executor.clone()).await;
3598    let client_a = server.create_client(cx_a, "user_a").await;
3599    let client_b = server.create_client(cx_b, "user_b").await;
3600    server
3601        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3602        .await;
3603    let active_call_a = cx_a.read(ActiveCall::global);
3604
3605    client_a
3606        .fs()
3607        .insert_tree(
3608            path!("/dir"),
3609            json!({
3610                "a.txt": "a-contents",
3611            }),
3612        )
3613        .await;
3614    let (project_a, worktree_id) = client_a.build_local_project(path!("/dir"), cx_a).await;
3615    let project_id = active_call_a
3616        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3617        .await
3618        .unwrap();
3619    let project_b = client_b.join_remote_project(project_id, cx_b).await;
3620
3621    // Open a buffer as client B
3622    let buffer_b = project_b
3623        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3624        .await
3625        .unwrap();
3626
3627    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "world ")], None, cx));
3628
3629    buffer_b.read_with(cx_b, |buf, _| {
3630        assert!(buf.is_dirty());
3631        assert!(!buf.has_conflict());
3632    });
3633
3634    project_b
3635        .update(cx_b, |project, cx| {
3636            project.save_buffer(buffer_b.clone(), cx)
3637        })
3638        .await
3639        .unwrap();
3640
3641    buffer_b.read_with(cx_b, |buffer_b, _| assert!(!buffer_b.is_dirty()));
3642
3643    buffer_b.read_with(cx_b, |buf, _| {
3644        assert!(!buf.has_conflict());
3645    });
3646
3647    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "hello ")], None, cx));
3648
3649    buffer_b.read_with(cx_b, |buf, _| {
3650        assert!(buf.is_dirty());
3651        assert!(!buf.has_conflict());
3652    });
3653}
3654
3655#[gpui::test(iterations = 10)]
3656async fn test_buffer_reloading(
3657    executor: BackgroundExecutor,
3658    cx_a: &mut TestAppContext,
3659    cx_b: &mut TestAppContext,
3660) {
3661    let mut server = TestServer::start(executor.clone()).await;
3662    let client_a = server.create_client(cx_a, "user_a").await;
3663    let client_b = server.create_client(cx_b, "user_b").await;
3664    server
3665        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3666        .await;
3667    let active_call_a = cx_a.read(ActiveCall::global);
3668
3669    client_a
3670        .fs()
3671        .insert_tree(
3672            path!("/dir"),
3673            json!({
3674                "a.txt": "a\nb\nc",
3675            }),
3676        )
3677        .await;
3678    let (project_a, worktree_id) = client_a.build_local_project(path!("/dir"), cx_a).await;
3679    let project_id = active_call_a
3680        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3681        .await
3682        .unwrap();
3683    let project_b = client_b.join_remote_project(project_id, cx_b).await;
3684
3685    // Open a buffer as client B
3686    let buffer_b = project_b
3687        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3688        .await
3689        .unwrap();
3690
3691    buffer_b.read_with(cx_b, |buf, _| {
3692        assert!(!buf.is_dirty());
3693        assert!(!buf.has_conflict());
3694        assert_eq!(buf.line_ending(), LineEnding::Unix);
3695    });
3696
3697    let new_contents = Rope::from("d\ne\nf");
3698    client_a
3699        .fs()
3700        .save(
3701            path!("/dir/a.txt").as_ref(),
3702            &new_contents,
3703            LineEnding::Windows,
3704        )
3705        .await
3706        .unwrap();
3707
3708    executor.run_until_parked();
3709
3710    buffer_b.read_with(cx_b, |buf, _| {
3711        assert_eq!(buf.text(), new_contents.to_string());
3712        assert!(!buf.is_dirty());
3713        assert!(!buf.has_conflict());
3714        assert_eq!(buf.line_ending(), LineEnding::Windows);
3715    });
3716}
3717
3718#[gpui::test(iterations = 10)]
3719async fn test_editing_while_guest_opens_buffer(
3720    executor: BackgroundExecutor,
3721    cx_a: &mut TestAppContext,
3722    cx_b: &mut TestAppContext,
3723) {
3724    let mut server = TestServer::start(executor.clone()).await;
3725    let client_a = server.create_client(cx_a, "user_a").await;
3726    let client_b = server.create_client(cx_b, "user_b").await;
3727    server
3728        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3729        .await;
3730    let active_call_a = cx_a.read(ActiveCall::global);
3731
3732    client_a
3733        .fs()
3734        .insert_tree(path!("/dir"), json!({ "a.txt": "a-contents" }))
3735        .await;
3736    let (project_a, worktree_id) = client_a.build_local_project(path!("/dir"), cx_a).await;
3737    let project_id = active_call_a
3738        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3739        .await
3740        .unwrap();
3741    let project_b = client_b.join_remote_project(project_id, cx_b).await;
3742
3743    // Open a buffer as client A
3744    let buffer_a = project_a
3745        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3746        .await
3747        .unwrap();
3748
3749    // Start opening the same buffer as client B
3750    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx));
3751    let buffer_b = cx_b.executor().spawn(open_buffer);
3752
3753    // Edit the buffer as client A while client B is still opening it.
3754    cx_b.executor().simulate_random_delay().await;
3755    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "X")], None, cx));
3756    cx_b.executor().simulate_random_delay().await;
3757    buffer_a.update(cx_a, |buf, cx| buf.edit([(1..1, "Y")], None, cx));
3758
3759    let text = buffer_a.read_with(cx_a, |buf, _| buf.text());
3760    let buffer_b = buffer_b.await.unwrap();
3761    executor.run_until_parked();
3762
3763    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), text));
3764}
3765
3766#[gpui::test(iterations = 10)]
3767async fn test_leaving_worktree_while_opening_buffer(
3768    executor: BackgroundExecutor,
3769    cx_a: &mut TestAppContext,
3770    cx_b: &mut TestAppContext,
3771) {
3772    let mut server = TestServer::start(executor.clone()).await;
3773    let client_a = server.create_client(cx_a, "user_a").await;
3774    let client_b = server.create_client(cx_b, "user_b").await;
3775    server
3776        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3777        .await;
3778    let active_call_a = cx_a.read(ActiveCall::global);
3779
3780    client_a
3781        .fs()
3782        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3783        .await;
3784    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3785    let project_id = active_call_a
3786        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3787        .await
3788        .unwrap();
3789    let project_b = client_b.join_remote_project(project_id, cx_b).await;
3790
3791    // See that a guest has joined as client A.
3792    executor.run_until_parked();
3793
3794    project_a.read_with(cx_a, |p, _| assert_eq!(p.collaborators().len(), 1));
3795
3796    // Begin opening a buffer as client B, but leave the project before the open completes.
3797    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx));
3798    let buffer_b = cx_b.executor().spawn(open_buffer);
3799    cx_b.update(|_| drop(project_b));
3800    drop(buffer_b);
3801
3802    // See that the guest has left.
3803    executor.run_until_parked();
3804
3805    project_a.read_with(cx_a, |p, _| assert!(p.collaborators().is_empty()));
3806}
3807
3808#[gpui::test(iterations = 10)]
3809async fn test_canceling_buffer_opening(
3810    executor: BackgroundExecutor,
3811    cx_a: &mut TestAppContext,
3812    cx_b: &mut TestAppContext,
3813) {
3814    let mut server = TestServer::start(executor.clone()).await;
3815    let client_a = server.create_client(cx_a, "user_a").await;
3816    let client_b = server.create_client(cx_b, "user_b").await;
3817    server
3818        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3819        .await;
3820    let active_call_a = cx_a.read(ActiveCall::global);
3821
3822    client_a
3823        .fs()
3824        .insert_tree(
3825            "/dir",
3826            json!({
3827                "a.txt": "abc",
3828            }),
3829        )
3830        .await;
3831    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3832    let project_id = active_call_a
3833        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3834        .await
3835        .unwrap();
3836    let project_b = client_b.join_remote_project(project_id, cx_b).await;
3837
3838    let buffer_a = project_a
3839        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3840        .await
3841        .unwrap();
3842
3843    // Open a buffer as client B but cancel after a random amount of time.
3844    let buffer_b = project_b.update(cx_b, |p, cx| {
3845        p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3846    });
3847    executor.simulate_random_delay().await;
3848    drop(buffer_b);
3849
3850    // Try opening the same buffer again as client B, and ensure we can
3851    // still do it despite the cancellation above.
3852    let buffer_b = project_b
3853        .update(cx_b, |p, cx| {
3854            p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3855        })
3856        .await
3857        .unwrap();
3858
3859    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "abc"));
3860}
3861
3862#[gpui::test(iterations = 10)]
3863async fn test_leaving_project(
3864    executor: BackgroundExecutor,
3865    cx_a: &mut TestAppContext,
3866    cx_b: &mut TestAppContext,
3867    cx_c: &mut TestAppContext,
3868) {
3869    let mut server = TestServer::start(executor.clone()).await;
3870    let client_a = server.create_client(cx_a, "user_a").await;
3871    let client_b = server.create_client(cx_b, "user_b").await;
3872    let client_c = server.create_client(cx_c, "user_c").await;
3873    server
3874        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3875        .await;
3876    let active_call_a = cx_a.read(ActiveCall::global);
3877
3878    client_a
3879        .fs()
3880        .insert_tree(
3881            "/a",
3882            json!({
3883                "a.txt": "a-contents",
3884                "b.txt": "b-contents",
3885            }),
3886        )
3887        .await;
3888    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
3889    let project_id = active_call_a
3890        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3891        .await
3892        .unwrap();
3893    let project_b1 = client_b.join_remote_project(project_id, cx_b).await;
3894    let project_c = client_c.join_remote_project(project_id, cx_c).await;
3895
3896    // Client A sees that a guest has joined.
3897    executor.run_until_parked();
3898
3899    project_a.read_with(cx_a, |project, _| {
3900        assert_eq!(project.collaborators().len(), 2);
3901    });
3902
3903    project_b1.read_with(cx_b, |project, _| {
3904        assert_eq!(project.collaborators().len(), 2);
3905    });
3906
3907    project_c.read_with(cx_c, |project, _| {
3908        assert_eq!(project.collaborators().len(), 2);
3909    });
3910
3911    // Client B opens a buffer.
3912    let buffer_b1 = project_b1
3913        .update(cx_b, |project, cx| {
3914            let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
3915            project.open_buffer((worktree_id, "a.txt"), cx)
3916        })
3917        .await
3918        .unwrap();
3919
3920    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3921
3922    // Drop client B's project and ensure client A and client C observe client B leaving.
3923    cx_b.update(|_| drop(project_b1));
3924    executor.run_until_parked();
3925
3926    project_a.read_with(cx_a, |project, _| {
3927        assert_eq!(project.collaborators().len(), 1);
3928    });
3929
3930    project_c.read_with(cx_c, |project, _| {
3931        assert_eq!(project.collaborators().len(), 1);
3932    });
3933
3934    // Client B re-joins the project and can open buffers as before.
3935    let project_b2 = client_b.join_remote_project(project_id, cx_b).await;
3936    executor.run_until_parked();
3937
3938    project_a.read_with(cx_a, |project, _| {
3939        assert_eq!(project.collaborators().len(), 2);
3940    });
3941
3942    project_b2.read_with(cx_b, |project, _| {
3943        assert_eq!(project.collaborators().len(), 2);
3944    });
3945
3946    project_c.read_with(cx_c, |project, _| {
3947        assert_eq!(project.collaborators().len(), 2);
3948    });
3949
3950    let buffer_b2 = project_b2
3951        .update(cx_b, |project, cx| {
3952            let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
3953            project.open_buffer((worktree_id, "a.txt"), cx)
3954        })
3955        .await
3956        .unwrap();
3957
3958    buffer_b2.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3959
3960    project_a.read_with(cx_a, |project, _| {
3961        assert_eq!(project.collaborators().len(), 2);
3962    });
3963
3964    // Drop client B's connection and ensure client A and client C observe client B leaving.
3965    client_b.disconnect(&cx_b.to_async());
3966    executor.advance_clock(RECONNECT_TIMEOUT);
3967
3968    project_a.read_with(cx_a, |project, _| {
3969        assert_eq!(project.collaborators().len(), 1);
3970    });
3971
3972    project_b2.read_with(cx_b, |project, cx| {
3973        assert!(project.is_disconnected(cx));
3974    });
3975
3976    project_c.read_with(cx_c, |project, _| {
3977        assert_eq!(project.collaborators().len(), 1);
3978    });
3979
3980    // Client B can't join the project, unless they re-join the room.
3981    cx_b.spawn(|cx| {
3982        Project::in_room(
3983            project_id,
3984            client_b.app_state.client.clone(),
3985            client_b.user_store().clone(),
3986            client_b.language_registry().clone(),
3987            FakeFs::new(cx.background_executor().clone()),
3988            cx,
3989        )
3990    })
3991    .await
3992    .unwrap_err();
3993
3994    // Simulate connection loss for client C and ensure client A observes client C leaving the project.
3995    client_c.wait_for_current_user(cx_c).await;
3996    server.forbid_connections();
3997    server.disconnect_client(client_c.peer_id().unwrap());
3998    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
3999    executor.run_until_parked();
4000
4001    project_a.read_with(cx_a, |project, _| {
4002        assert_eq!(project.collaborators().len(), 0);
4003    });
4004
4005    project_b2.read_with(cx_b, |project, cx| {
4006        assert!(project.is_disconnected(cx));
4007    });
4008
4009    project_c.read_with(cx_c, |project, cx| {
4010        assert!(project.is_disconnected(cx));
4011    });
4012}
4013
4014#[gpui::test(iterations = 10)]
4015async fn test_collaborating_with_diagnostics(
4016    executor: BackgroundExecutor,
4017    cx_a: &mut TestAppContext,
4018    cx_b: &mut TestAppContext,
4019    cx_c: &mut TestAppContext,
4020) {
4021    let mut server = TestServer::start(executor.clone()).await;
4022    let client_a = server.create_client(cx_a, "user_a").await;
4023    let client_b = server.create_client(cx_b, "user_b").await;
4024    let client_c = server.create_client(cx_c, "user_c").await;
4025    server
4026        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
4027        .await;
4028    let active_call_a = cx_a.read(ActiveCall::global);
4029
4030    client_a.language_registry().add(Arc::new(Language::new(
4031        LanguageConfig {
4032            name: "Rust".into(),
4033            matcher: LanguageMatcher {
4034                path_suffixes: vec!["rs".to_string()],
4035                ..Default::default()
4036            },
4037            ..Default::default()
4038        },
4039        Some(tree_sitter_rust::LANGUAGE.into()),
4040    )));
4041    let mut fake_language_servers = client_a
4042        .language_registry()
4043        .register_fake_lsp("Rust", Default::default());
4044
4045    // Share a project as client A
4046    client_a
4047        .fs()
4048        .insert_tree(
4049            path!("/a"),
4050            json!({
4051                "a.rs": "let one = two",
4052                "other.rs": "",
4053            }),
4054        )
4055        .await;
4056    let (project_a, worktree_id) = client_a.build_local_project(path!("/a"), cx_a).await;
4057
4058    // Cause the language server to start.
4059    let _buffer = project_a
4060        .update(cx_a, |project, cx| {
4061            project.open_local_buffer_with_lsp(path!("/a/other.rs"), cx)
4062        })
4063        .await
4064        .unwrap();
4065
4066    // Simulate a language server reporting errors for a file.
4067    let mut fake_language_server = fake_language_servers.next().await.unwrap();
4068    fake_language_server
4069        .receive_notification::<lsp::notification::DidOpenTextDocument>()
4070        .await;
4071    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
4072        &lsp::PublishDiagnosticsParams {
4073            uri: lsp::Url::from_file_path(path!("/a/a.rs")).unwrap(),
4074            version: None,
4075            diagnostics: vec![lsp::Diagnostic {
4076                severity: Some(lsp::DiagnosticSeverity::WARNING),
4077                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
4078                message: "message 0".to_string(),
4079                ..Default::default()
4080            }],
4081        },
4082    );
4083
4084    // Client A shares the project and, simultaneously, the language server
4085    // publishes a diagnostic. This is done to ensure that the server always
4086    // observes the latest diagnostics for a worktree.
4087    let project_id = active_call_a
4088        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4089        .await
4090        .unwrap();
4091    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
4092        &lsp::PublishDiagnosticsParams {
4093            uri: lsp::Url::from_file_path(path!("/a/a.rs")).unwrap(),
4094            version: None,
4095            diagnostics: vec![lsp::Diagnostic {
4096                severity: Some(lsp::DiagnosticSeverity::ERROR),
4097                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
4098                message: "message 1".to_string(),
4099                ..Default::default()
4100            }],
4101        },
4102    );
4103
4104    // Join the worktree as client B.
4105    let project_b = client_b.join_remote_project(project_id, cx_b).await;
4106
4107    // Wait for server to see the diagnostics update.
4108    executor.run_until_parked();
4109
4110    // Ensure client B observes the new diagnostics.
4111
4112    project_b.read_with(cx_b, |project, cx| {
4113        assert_eq!(
4114            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4115            &[(
4116                ProjectPath {
4117                    worktree_id,
4118                    path: Arc::from(Path::new("a.rs")),
4119                },
4120                LanguageServerId(0),
4121                DiagnosticSummary {
4122                    error_count: 1,
4123                    warning_count: 0,
4124                },
4125            )]
4126        )
4127    });
4128
4129    // Join project as client C and observe the diagnostics.
4130    let project_c = client_c.join_remote_project(project_id, cx_c).await;
4131    executor.run_until_parked();
4132    let project_c_diagnostic_summaries =
4133        Rc::new(RefCell::new(project_c.read_with(cx_c, |project, cx| {
4134            project.diagnostic_summaries(false, cx).collect::<Vec<_>>()
4135        })));
4136    project_c.update(cx_c, |_, cx| {
4137        let summaries = project_c_diagnostic_summaries.clone();
4138        cx.subscribe(&project_c, {
4139            move |p, _, event, cx| {
4140                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
4141                    *summaries.borrow_mut() = p.diagnostic_summaries(false, cx).collect();
4142                }
4143            }
4144        })
4145        .detach();
4146    });
4147
4148    executor.run_until_parked();
4149    assert_eq!(
4150        project_c_diagnostic_summaries.borrow().as_slice(),
4151        &[(
4152            ProjectPath {
4153                worktree_id,
4154                path: Arc::from(Path::new("a.rs")),
4155            },
4156            LanguageServerId(0),
4157            DiagnosticSummary {
4158                error_count: 1,
4159                warning_count: 0,
4160            },
4161        )]
4162    );
4163
4164    // Simulate a language server reporting more errors for a file.
4165    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
4166        &lsp::PublishDiagnosticsParams {
4167            uri: lsp::Url::from_file_path(path!("/a/a.rs")).unwrap(),
4168            version: None,
4169            diagnostics: vec![
4170                lsp::Diagnostic {
4171                    severity: Some(lsp::DiagnosticSeverity::ERROR),
4172                    range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
4173                    message: "message 1".to_string(),
4174                    ..Default::default()
4175                },
4176                lsp::Diagnostic {
4177                    severity: Some(lsp::DiagnosticSeverity::WARNING),
4178                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 13)),
4179                    message: "message 2".to_string(),
4180                    ..Default::default()
4181                },
4182            ],
4183        },
4184    );
4185
4186    // Clients B and C get the updated summaries
4187    executor.run_until_parked();
4188
4189    project_b.read_with(cx_b, |project, cx| {
4190        assert_eq!(
4191            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4192            [(
4193                ProjectPath {
4194                    worktree_id,
4195                    path: Arc::from(Path::new("a.rs")),
4196                },
4197                LanguageServerId(0),
4198                DiagnosticSummary {
4199                    error_count: 1,
4200                    warning_count: 1,
4201                },
4202            )]
4203        );
4204    });
4205
4206    project_c.read_with(cx_c, |project, cx| {
4207        assert_eq!(
4208            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4209            [(
4210                ProjectPath {
4211                    worktree_id,
4212                    path: Arc::from(Path::new("a.rs")),
4213                },
4214                LanguageServerId(0),
4215                DiagnosticSummary {
4216                    error_count: 1,
4217                    warning_count: 1,
4218                },
4219            )]
4220        );
4221    });
4222
4223    // Open the file with the errors on client B. They should be present.
4224    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4225    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4226
4227    buffer_b.read_with(cx_b, |buffer, _| {
4228        assert_eq!(
4229            buffer
4230                .snapshot()
4231                .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
4232                .collect::<Vec<_>>(),
4233            &[
4234                DiagnosticEntry {
4235                    range: Point::new(0, 4)..Point::new(0, 7),
4236                    diagnostic: Diagnostic {
4237                        group_id: 2,
4238                        message: "message 1".to_string(),
4239                        severity: lsp::DiagnosticSeverity::ERROR,
4240                        is_primary: true,
4241                        source_kind: DiagnosticSourceKind::Pushed,
4242                        ..Diagnostic::default()
4243                    }
4244                },
4245                DiagnosticEntry {
4246                    range: Point::new(0, 10)..Point::new(0, 13),
4247                    diagnostic: Diagnostic {
4248                        group_id: 3,
4249                        severity: lsp::DiagnosticSeverity::WARNING,
4250                        message: "message 2".to_string(),
4251                        is_primary: true,
4252                        source_kind: DiagnosticSourceKind::Pushed,
4253                        ..Diagnostic::default()
4254                    }
4255                }
4256            ]
4257        );
4258    });
4259
4260    // Simulate a language server reporting no errors for a file.
4261    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
4262        &lsp::PublishDiagnosticsParams {
4263            uri: lsp::Url::from_file_path(path!("/a/a.rs")).unwrap(),
4264            version: None,
4265            diagnostics: Vec::new(),
4266        },
4267    );
4268    executor.run_until_parked();
4269
4270    project_a.read_with(cx_a, |project, cx| {
4271        assert_eq!(
4272            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4273            []
4274        )
4275    });
4276
4277    project_b.read_with(cx_b, |project, cx| {
4278        assert_eq!(
4279            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4280            []
4281        )
4282    });
4283
4284    project_c.read_with(cx_c, |project, cx| {
4285        assert_eq!(
4286            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4287            []
4288        )
4289    });
4290}
4291
4292#[gpui::test(iterations = 10)]
4293async fn test_collaborating_with_lsp_progress_updates_and_diagnostics_ordering(
4294    executor: BackgroundExecutor,
4295    cx_a: &mut TestAppContext,
4296    cx_b: &mut TestAppContext,
4297) {
4298    let mut server = TestServer::start(executor.clone()).await;
4299    let client_a = server.create_client(cx_a, "user_a").await;
4300    let client_b = server.create_client(cx_b, "user_b").await;
4301    server
4302        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4303        .await;
4304
4305    client_a.language_registry().add(rust_lang());
4306    let mut fake_language_servers = client_a.language_registry().register_fake_lsp(
4307        "Rust",
4308        FakeLspAdapter {
4309            disk_based_diagnostics_progress_token: Some("the-disk-based-token".into()),
4310            disk_based_diagnostics_sources: vec!["the-disk-based-diagnostics-source".into()],
4311            ..Default::default()
4312        },
4313    );
4314
4315    let file_names = &["one.rs", "two.rs", "three.rs", "four.rs", "five.rs"];
4316    client_a
4317        .fs()
4318        .insert_tree(
4319            path!("/test"),
4320            json!({
4321                "one.rs": "const ONE: usize = 1;",
4322                "two.rs": "const TWO: usize = 2;",
4323                "three.rs": "const THREE: usize = 3;",
4324                "four.rs": "const FOUR: usize = 3;",
4325                "five.rs": "const FIVE: usize = 3;",
4326            }),
4327        )
4328        .await;
4329
4330    let (project_a, worktree_id) = client_a.build_local_project(path!("/test"), cx_a).await;
4331
4332    // Share a project as client A
4333    let active_call_a = cx_a.read(ActiveCall::global);
4334    let project_id = active_call_a
4335        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4336        .await
4337        .unwrap();
4338
4339    // Join the project as client B and open all three files.
4340    let project_b = client_b.join_remote_project(project_id, cx_b).await;
4341    let guest_buffers = futures::future::try_join_all(file_names.iter().map(|file_name| {
4342        project_b.update(cx_b, |p, cx| {
4343            p.open_buffer_with_lsp((worktree_id, file_name), cx)
4344        })
4345    }))
4346    .await
4347    .unwrap();
4348
4349    // Simulate a language server reporting errors for a file.
4350    let fake_language_server = fake_language_servers.next().await.unwrap();
4351    fake_language_server
4352        .request::<lsp::request::WorkDoneProgressCreate>(lsp::WorkDoneProgressCreateParams {
4353            token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4354        })
4355        .await
4356        .into_response()
4357        .unwrap();
4358    fake_language_server.notify::<lsp::notification::Progress>(&lsp::ProgressParams {
4359        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4360        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin(
4361            lsp::WorkDoneProgressBegin {
4362                title: "Progress Began".into(),
4363                ..Default::default()
4364            },
4365        )),
4366    });
4367    for file_name in file_names {
4368        fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
4369            &lsp::PublishDiagnosticsParams {
4370                uri: lsp::Url::from_file_path(Path::new(path!("/test")).join(file_name)).unwrap(),
4371                version: None,
4372                diagnostics: vec![lsp::Diagnostic {
4373                    severity: Some(lsp::DiagnosticSeverity::WARNING),
4374                    source: Some("the-disk-based-diagnostics-source".into()),
4375                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
4376                    message: "message one".to_string(),
4377                    ..Default::default()
4378                }],
4379            },
4380        );
4381    }
4382    fake_language_server.notify::<lsp::notification::Progress>(&lsp::ProgressParams {
4383        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4384        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End(
4385            lsp::WorkDoneProgressEnd { message: None },
4386        )),
4387    });
4388
4389    // When the "disk base diagnostics finished" message is received, the buffers'
4390    // diagnostics are expected to be present.
4391    let disk_based_diagnostics_finished = Arc::new(AtomicBool::new(false));
4392    project_b.update(cx_b, {
4393        let project_b = project_b.clone();
4394        let disk_based_diagnostics_finished = disk_based_diagnostics_finished.clone();
4395        move |_, cx| {
4396            cx.subscribe(&project_b, move |_, _, event, cx| {
4397                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
4398                    disk_based_diagnostics_finished.store(true, SeqCst);
4399                    for (buffer, _) in &guest_buffers {
4400                        assert_eq!(
4401                            buffer
4402                                .read(cx)
4403                                .snapshot()
4404                                .diagnostics_in_range::<_, usize>(0..5, false)
4405                                .count(),
4406                            1,
4407                            "expected a diagnostic for buffer {:?}",
4408                            buffer.read(cx).file().unwrap().path(),
4409                        );
4410                    }
4411                }
4412            })
4413            .detach();
4414        }
4415    });
4416
4417    executor.run_until_parked();
4418    assert!(disk_based_diagnostics_finished.load(SeqCst));
4419}
4420
4421#[gpui::test(iterations = 10)]
4422async fn test_reloading_buffer_manually(
4423    executor: BackgroundExecutor,
4424    cx_a: &mut TestAppContext,
4425    cx_b: &mut TestAppContext,
4426) {
4427    let mut server = TestServer::start(executor.clone()).await;
4428    let client_a = server.create_client(cx_a, "user_a").await;
4429    let client_b = server.create_client(cx_b, "user_b").await;
4430    server
4431        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4432        .await;
4433    let active_call_a = cx_a.read(ActiveCall::global);
4434
4435    client_a
4436        .fs()
4437        .insert_tree(path!("/a"), json!({ "a.rs": "let one = 1;" }))
4438        .await;
4439    let (project_a, worktree_id) = client_a.build_local_project(path!("/a"), cx_a).await;
4440    let buffer_a = project_a
4441        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
4442        .await
4443        .unwrap();
4444    let project_id = active_call_a
4445        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4446        .await
4447        .unwrap();
4448
4449    let project_b = client_b.join_remote_project(project_id, cx_b).await;
4450
4451    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4452    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4453    buffer_b.update(cx_b, |buffer, cx| {
4454        buffer.edit([(4..7, "six")], None, cx);
4455        buffer.edit([(10..11, "6")], None, cx);
4456        assert_eq!(buffer.text(), "let six = 6;");
4457        assert!(buffer.is_dirty());
4458        assert!(!buffer.has_conflict());
4459    });
4460    executor.run_until_parked();
4461
4462    buffer_a.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "let six = 6;"));
4463
4464    client_a
4465        .fs()
4466        .save(
4467            path!("/a/a.rs").as_ref(),
4468            &Rope::from("let seven = 7;"),
4469            LineEnding::Unix,
4470        )
4471        .await
4472        .unwrap();
4473    executor.run_until_parked();
4474
4475    buffer_a.read_with(cx_a, |buffer, _| assert!(buffer.has_conflict()));
4476
4477    buffer_b.read_with(cx_b, |buffer, _| assert!(buffer.has_conflict()));
4478
4479    project_b
4480        .update(cx_b, |project, cx| {
4481            project.reload_buffers(HashSet::from_iter([buffer_b.clone()]), true, cx)
4482        })
4483        .await
4484        .unwrap();
4485
4486    buffer_a.read_with(cx_a, |buffer, _| {
4487        assert_eq!(buffer.text(), "let seven = 7;");
4488        assert!(!buffer.is_dirty());
4489        assert!(!buffer.has_conflict());
4490    });
4491
4492    buffer_b.read_with(cx_b, |buffer, _| {
4493        assert_eq!(buffer.text(), "let seven = 7;");
4494        assert!(!buffer.is_dirty());
4495        assert!(!buffer.has_conflict());
4496    });
4497
4498    buffer_a.update(cx_a, |buffer, cx| {
4499        // Undoing on the host is a no-op when the reload was initiated by the guest.
4500        buffer.undo(cx);
4501        assert_eq!(buffer.text(), "let seven = 7;");
4502        assert!(!buffer.is_dirty());
4503        assert!(!buffer.has_conflict());
4504    });
4505    buffer_b.update(cx_b, |buffer, cx| {
4506        // Undoing on the guest rolls back the buffer to before it was reloaded but the conflict gets cleared.
4507        buffer.undo(cx);
4508        assert_eq!(buffer.text(), "let six = 6;");
4509        assert!(buffer.is_dirty());
4510        assert!(!buffer.has_conflict());
4511    });
4512}
4513
4514#[gpui::test(iterations = 10)]
4515async fn test_formatting_buffer(
4516    executor: BackgroundExecutor,
4517    cx_a: &mut TestAppContext,
4518    cx_b: &mut TestAppContext,
4519) {
4520    let mut server = TestServer::start(executor.clone()).await;
4521    let client_a = server.create_client(cx_a, "user_a").await;
4522    let client_b = server.create_client(cx_b, "user_b").await;
4523    server
4524        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4525        .await;
4526    let active_call_a = cx_a.read(ActiveCall::global);
4527
4528    client_a.language_registry().add(rust_lang());
4529    let mut fake_language_servers = client_a
4530        .language_registry()
4531        .register_fake_lsp("Rust", FakeLspAdapter::default());
4532
4533    // Here we insert a fake tree with a directory that exists on disk. This is needed
4534    // because later we'll invoke a command, which requires passing a working directory
4535    // that points to a valid location on disk.
4536    let directory = env::current_dir().unwrap();
4537    client_a
4538        .fs()
4539        .insert_tree(&directory, json!({ "a.rs": "let one = \"two\"" }))
4540        .await;
4541    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
4542    let project_id = active_call_a
4543        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4544        .await
4545        .unwrap();
4546    let project_b = client_b.join_remote_project(project_id, cx_b).await;
4547
4548    let buffer_b = project_b
4549        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
4550        .await
4551        .unwrap();
4552
4553    let _handle = project_b.update(cx_b, |project, cx| {
4554        project.register_buffer_with_language_servers(&buffer_b, cx)
4555    });
4556    let fake_language_server = fake_language_servers.next().await.unwrap();
4557    fake_language_server.set_request_handler::<lsp::request::Formatting, _, _>(|_, _| async move {
4558        Ok(Some(vec![
4559            lsp::TextEdit {
4560                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 4)),
4561                new_text: "h".to_string(),
4562            },
4563            lsp::TextEdit {
4564                range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 7)),
4565                new_text: "y".to_string(),
4566            },
4567        ]))
4568    });
4569
4570    project_b
4571        .update(cx_b, |project, cx| {
4572            project.format(
4573                HashSet::from_iter([buffer_b.clone()]),
4574                LspFormatTarget::Buffers,
4575                true,
4576                FormatTrigger::Save,
4577                cx,
4578            )
4579        })
4580        .await
4581        .unwrap();
4582
4583    // The edits from the LSP are applied, and a final newline is added.
4584    assert_eq!(
4585        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4586        "let honey = \"two\"\n"
4587    );
4588
4589    // There is no `awk` command on Windows.
4590    #[cfg(not(target_os = "windows"))]
4591    {
4592        // Ensure buffer can be formatted using an external command. Notice how the
4593        // host's configuration is honored as opposed to using the guest's settings.
4594        cx_a.update(|cx| {
4595            SettingsStore::update_global(cx, |store, cx| {
4596                store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4597                    file.defaults.formatter = Some(SelectedFormatter::List(FormatterList::Single(
4598                        Formatter::External {
4599                            command: "awk".into(),
4600                            arguments: Some(
4601                                vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()].into(),
4602                            ),
4603                        },
4604                    )));
4605                });
4606            });
4607        });
4608
4609        executor.allow_parking();
4610        project_b
4611            .update(cx_b, |project, cx| {
4612                project.format(
4613                    HashSet::from_iter([buffer_b.clone()]),
4614                    LspFormatTarget::Buffers,
4615                    true,
4616                    FormatTrigger::Save,
4617                    cx,
4618                )
4619            })
4620            .await
4621            .unwrap();
4622        assert_eq!(
4623            buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4624            format!("let honey = \"{}/a.rs\"\n", directory.to_str().unwrap())
4625        );
4626    }
4627}
4628
4629#[gpui::test(iterations = 10)]
4630async fn test_prettier_formatting_buffer(
4631    executor: BackgroundExecutor,
4632    cx_a: &mut TestAppContext,
4633    cx_b: &mut TestAppContext,
4634) {
4635    let mut server = TestServer::start(executor.clone()).await;
4636    let client_a = server.create_client(cx_a, "user_a").await;
4637    let client_b = server.create_client(cx_b, "user_b").await;
4638    server
4639        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4640        .await;
4641    let active_call_a = cx_a.read(ActiveCall::global);
4642
4643    let test_plugin = "test_plugin";
4644
4645    client_a.language_registry().add(Arc::new(Language::new(
4646        LanguageConfig {
4647            name: "TypeScript".into(),
4648            matcher: LanguageMatcher {
4649                path_suffixes: vec!["ts".to_string()],
4650                ..Default::default()
4651            },
4652            ..Default::default()
4653        },
4654        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
4655    )));
4656    let mut fake_language_servers = client_a.language_registry().register_fake_lsp(
4657        "TypeScript",
4658        FakeLspAdapter {
4659            prettier_plugins: vec![test_plugin],
4660            ..Default::default()
4661        },
4662    );
4663
4664    // Here we insert a fake tree with a directory that exists on disk. This is needed
4665    // because later we'll invoke a command, which requires passing a working directory
4666    // that points to a valid location on disk.
4667    let directory = env::current_dir().unwrap();
4668    let buffer_text = "let one = \"two\"";
4669    client_a
4670        .fs()
4671        .insert_tree(&directory, json!({ "a.ts": buffer_text }))
4672        .await;
4673    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
4674    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
4675    let open_buffer = project_a.update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.ts"), cx));
4676    let buffer_a = cx_a.executor().spawn(open_buffer).await.unwrap();
4677
4678    let project_id = active_call_a
4679        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4680        .await
4681        .unwrap();
4682    let project_b = client_b.join_remote_project(project_id, cx_b).await;
4683    let (buffer_b, _) = project_b
4684        .update(cx_b, |p, cx| {
4685            p.open_buffer_with_lsp((worktree_id, "a.ts"), cx)
4686        })
4687        .await
4688        .unwrap();
4689
4690    cx_a.update(|cx| {
4691        SettingsStore::update_global(cx, |store, cx| {
4692            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4693                file.defaults.formatter = Some(SelectedFormatter::Auto);
4694                file.defaults.prettier = Some(PrettierSettings {
4695                    allowed: true,
4696                    ..PrettierSettings::default()
4697                });
4698            });
4699        });
4700    });
4701    cx_b.update(|cx| {
4702        SettingsStore::update_global(cx, |store, cx| {
4703            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4704                file.defaults.formatter = Some(SelectedFormatter::List(FormatterList::Single(
4705                    Formatter::LanguageServer { name: None },
4706                )));
4707                file.defaults.prettier = Some(PrettierSettings {
4708                    allowed: true,
4709                    ..PrettierSettings::default()
4710                });
4711            });
4712        });
4713    });
4714    let fake_language_server = fake_language_servers.next().await.unwrap();
4715    fake_language_server.set_request_handler::<lsp::request::Formatting, _, _>(|_, _| async move {
4716        panic!(
4717            "Unexpected: prettier should be preferred since it's enabled and language supports it"
4718        )
4719    });
4720
4721    project_b
4722        .update(cx_b, |project, cx| {
4723            project.format(
4724                HashSet::from_iter([buffer_b.clone()]),
4725                LspFormatTarget::Buffers,
4726                true,
4727                FormatTrigger::Save,
4728                cx,
4729            )
4730        })
4731        .await
4732        .unwrap();
4733
4734    executor.run_until_parked();
4735    assert_eq!(
4736        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4737        buffer_text.to_string() + "\n" + prettier_format_suffix,
4738        "Prettier formatting was not applied to client buffer after client's request"
4739    );
4740
4741    project_a
4742        .update(cx_a, |project, cx| {
4743            project.format(
4744                HashSet::from_iter([buffer_a.clone()]),
4745                LspFormatTarget::Buffers,
4746                true,
4747                FormatTrigger::Manual,
4748                cx,
4749            )
4750        })
4751        .await
4752        .unwrap();
4753
4754    executor.run_until_parked();
4755    assert_eq!(
4756        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4757        buffer_text.to_string() + "\n" + prettier_format_suffix + "\n" + prettier_format_suffix,
4758        "Prettier formatting was not applied to client buffer after host's request"
4759    );
4760}
4761
4762#[gpui::test(iterations = 10)]
4763async fn test_definition(
4764    executor: BackgroundExecutor,
4765    cx_a: &mut TestAppContext,
4766    cx_b: &mut TestAppContext,
4767) {
4768    let mut server = TestServer::start(executor.clone()).await;
4769    let client_a = server.create_client(cx_a, "user_a").await;
4770    let client_b = server.create_client(cx_b, "user_b").await;
4771    server
4772        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4773        .await;
4774    let active_call_a = cx_a.read(ActiveCall::global);
4775
4776    let capabilities = lsp::ServerCapabilities {
4777        definition_provider: Some(OneOf::Left(true)),
4778        type_definition_provider: Some(lsp::TypeDefinitionProviderCapability::Simple(true)),
4779        ..lsp::ServerCapabilities::default()
4780    };
4781    client_a.language_registry().add(rust_lang());
4782    let mut fake_language_servers = client_a.language_registry().register_fake_lsp(
4783        "Rust",
4784        FakeLspAdapter {
4785            capabilities: capabilities.clone(),
4786            ..FakeLspAdapter::default()
4787        },
4788    );
4789    client_b.language_registry().add(rust_lang());
4790    client_b.language_registry().register_fake_lsp_adapter(
4791        "Rust",
4792        FakeLspAdapter {
4793            capabilities,
4794            ..FakeLspAdapter::default()
4795        },
4796    );
4797
4798    client_a
4799        .fs()
4800        .insert_tree(
4801            path!("/root"),
4802            json!({
4803                "dir-1": {
4804                    "a.rs": "const ONE: usize = b::TWO + b::THREE;",
4805                },
4806                "dir-2": {
4807                    "b.rs": "const TWO: c::T2 = 2;\nconst THREE: usize = 3;",
4808                    "c.rs": "type T2 = usize;",
4809                }
4810            }),
4811        )
4812        .await;
4813    let (project_a, worktree_id) = client_a
4814        .build_local_project(path!("/root/dir-1"), cx_a)
4815        .await;
4816    let project_id = active_call_a
4817        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4818        .await
4819        .unwrap();
4820    let project_b = client_b.join_remote_project(project_id, cx_b).await;
4821
4822    // Open the file on client B.
4823    let (buffer_b, _handle) = project_b
4824        .update(cx_b, |p, cx| {
4825            p.open_buffer_with_lsp((worktree_id, "a.rs"), cx)
4826        })
4827        .await
4828        .unwrap();
4829
4830    // Request the definition of a symbol as the guest.
4831    let fake_language_server = fake_language_servers.next().await.unwrap();
4832    fake_language_server.set_request_handler::<lsp::request::GotoDefinition, _, _>(
4833        |_, _| async move {
4834            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4835                lsp::Location::new(
4836                    lsp::Url::from_file_path(path!("/root/dir-2/b.rs")).unwrap(),
4837                    lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4838                ),
4839            )))
4840        },
4841    );
4842    cx_a.run_until_parked();
4843    cx_b.run_until_parked();
4844
4845    let definitions_1 = project_b
4846        .update(cx_b, |p, cx| p.definitions(&buffer_b, 23, cx))
4847        .await
4848        .unwrap()
4849        .unwrap();
4850    cx_b.read(|cx| {
4851        assert_eq!(
4852            definitions_1.len(),
4853            1,
4854            "Unexpected definitions: {definitions_1:?}"
4855        );
4856        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4857        let target_buffer = definitions_1[0].target.buffer.read(cx);
4858        assert_eq!(
4859            target_buffer.text(),
4860            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4861        );
4862        assert_eq!(
4863            definitions_1[0].target.range.to_point(target_buffer),
4864            Point::new(0, 6)..Point::new(0, 9)
4865        );
4866    });
4867
4868    // Try getting more definitions for the same buffer, ensuring the buffer gets reused from
4869    // the previous call to `definition`.
4870    fake_language_server.set_request_handler::<lsp::request::GotoDefinition, _, _>(
4871        |_, _| async move {
4872            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4873                lsp::Location::new(
4874                    lsp::Url::from_file_path(path!("/root/dir-2/b.rs")).unwrap(),
4875                    lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)),
4876                ),
4877            )))
4878        },
4879    );
4880
4881    let definitions_2 = project_b
4882        .update(cx_b, |p, cx| p.definitions(&buffer_b, 33, cx))
4883        .await
4884        .unwrap()
4885        .unwrap();
4886    cx_b.read(|cx| {
4887        assert_eq!(definitions_2.len(), 1);
4888        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4889        let target_buffer = definitions_2[0].target.buffer.read(cx);
4890        assert_eq!(
4891            target_buffer.text(),
4892            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4893        );
4894        assert_eq!(
4895            definitions_2[0].target.range.to_point(target_buffer),
4896            Point::new(1, 6)..Point::new(1, 11)
4897        );
4898    });
4899    assert_eq!(
4900        definitions_1[0].target.buffer,
4901        definitions_2[0].target.buffer
4902    );
4903
4904    fake_language_server.set_request_handler::<lsp::request::GotoTypeDefinition, _, _>(
4905        |req, _| async move {
4906            assert_eq!(
4907                req.text_document_position_params.position,
4908                lsp::Position::new(0, 7)
4909            );
4910            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4911                lsp::Location::new(
4912                    lsp::Url::from_file_path(path!("/root/dir-2/c.rs")).unwrap(),
4913                    lsp::Range::new(lsp::Position::new(0, 5), lsp::Position::new(0, 7)),
4914                ),
4915            )))
4916        },
4917    );
4918
4919    let type_definitions = project_b
4920        .update(cx_b, |p, cx| p.type_definitions(&buffer_b, 7, cx))
4921        .await
4922        .unwrap()
4923        .unwrap();
4924    cx_b.read(|cx| {
4925        assert_eq!(
4926            type_definitions.len(),
4927            1,
4928            "Unexpected type definitions: {type_definitions:?}"
4929        );
4930        let target_buffer = type_definitions[0].target.buffer.read(cx);
4931        assert_eq!(target_buffer.text(), "type T2 = usize;");
4932        assert_eq!(
4933            type_definitions[0].target.range.to_point(target_buffer),
4934            Point::new(0, 5)..Point::new(0, 7)
4935        );
4936    });
4937}
4938
4939#[gpui::test(iterations = 10)]
4940async fn test_references(
4941    executor: BackgroundExecutor,
4942    cx_a: &mut TestAppContext,
4943    cx_b: &mut TestAppContext,
4944) {
4945    let mut server = TestServer::start(executor.clone()).await;
4946    let client_a = server.create_client(cx_a, "user_a").await;
4947    let client_b = server.create_client(cx_b, "user_b").await;
4948    server
4949        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4950        .await;
4951    let active_call_a = cx_a.read(ActiveCall::global);
4952
4953    let capabilities = lsp::ServerCapabilities {
4954        references_provider: Some(lsp::OneOf::Left(true)),
4955        ..lsp::ServerCapabilities::default()
4956    };
4957    client_a.language_registry().add(rust_lang());
4958    let mut fake_language_servers = client_a.language_registry().register_fake_lsp(
4959        "Rust",
4960        FakeLspAdapter {
4961            name: "my-fake-lsp-adapter",
4962            capabilities: capabilities.clone(),
4963            ..FakeLspAdapter::default()
4964        },
4965    );
4966    client_b.language_registry().add(rust_lang());
4967    client_b.language_registry().register_fake_lsp_adapter(
4968        "Rust",
4969        FakeLspAdapter {
4970            name: "my-fake-lsp-adapter",
4971            capabilities,
4972            ..FakeLspAdapter::default()
4973        },
4974    );
4975
4976    client_a
4977        .fs()
4978        .insert_tree(
4979            path!("/root"),
4980            json!({
4981                "dir-1": {
4982                    "one.rs": "const ONE: usize = 1;",
4983                    "two.rs": "const TWO: usize = one::ONE + one::ONE;",
4984                },
4985                "dir-2": {
4986                    "three.rs": "const THREE: usize = two::TWO + one::ONE;",
4987                }
4988            }),
4989        )
4990        .await;
4991    let (project_a, worktree_id) = client_a
4992        .build_local_project(path!("/root/dir-1"), cx_a)
4993        .await;
4994    let project_id = active_call_a
4995        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4996        .await
4997        .unwrap();
4998    let project_b = client_b.join_remote_project(project_id, cx_b).await;
4999
5000    // Open the file on client B.
5001    let (buffer_b, _handle) = project_b
5002        .update(cx_b, |p, cx| {
5003            p.open_buffer_with_lsp((worktree_id, "one.rs"), cx)
5004        })
5005        .await
5006        .unwrap();
5007
5008    // Request references to a symbol as the guest.
5009    let fake_language_server = fake_language_servers.next().await.unwrap();
5010    let (lsp_response_tx, rx) = mpsc::unbounded::<Result<Option<Vec<lsp::Location>>>>();
5011    fake_language_server.set_request_handler::<lsp::request::References, _, _>({
5012        let rx = Arc::new(Mutex::new(Some(rx)));
5013        move |params, _| {
5014            assert_eq!(
5015                params.text_document_position.text_document.uri.as_str(),
5016                uri!("file:///root/dir-1/one.rs")
5017            );
5018            let rx = rx.clone();
5019            async move {
5020                let mut response_rx = rx.lock().take().unwrap();
5021                let result = response_rx.next().await.unwrap();
5022                *rx.lock() = Some(response_rx);
5023                result
5024            }
5025        }
5026    });
5027    cx_a.run_until_parked();
5028    cx_b.run_until_parked();
5029
5030    let references = project_b.update(cx_b, |p, cx| p.references(&buffer_b, 7, cx));
5031
5032    // User is informed that a request is pending.
5033    executor.run_until_parked();
5034    project_b.read_with(cx_b, |project, cx| {
5035        let status = project.language_server_statuses(cx).next().unwrap().1;
5036        assert_eq!(status.name.0, "my-fake-lsp-adapter");
5037        assert_eq!(
5038            status.pending_work.values().next().unwrap().message,
5039            Some("Finding references...".into())
5040        );
5041    });
5042
5043    // Cause the language server to respond.
5044    lsp_response_tx
5045        .unbounded_send(Ok(Some(vec![
5046            lsp::Location {
5047                uri: lsp::Url::from_file_path(path!("/root/dir-1/two.rs")).unwrap(),
5048                range: lsp::Range::new(lsp::Position::new(0, 24), lsp::Position::new(0, 27)),
5049            },
5050            lsp::Location {
5051                uri: lsp::Url::from_file_path(path!("/root/dir-1/two.rs")).unwrap(),
5052                range: lsp::Range::new(lsp::Position::new(0, 35), lsp::Position::new(0, 38)),
5053            },
5054            lsp::Location {
5055                uri: lsp::Url::from_file_path(path!("/root/dir-2/three.rs")).unwrap(),
5056                range: lsp::Range::new(lsp::Position::new(0, 37), lsp::Position::new(0, 40)),
5057            },
5058        ])))
5059        .unwrap();
5060
5061    let references = references.await.unwrap().unwrap();
5062    executor.run_until_parked();
5063    project_b.read_with(cx_b, |project, cx| {
5064        // User is informed that a request is no longer pending.
5065        let status = project.language_server_statuses(cx).next().unwrap().1;
5066        assert!(status.pending_work.is_empty());
5067
5068        assert_eq!(references.len(), 3);
5069        assert_eq!(project.worktrees(cx).count(), 2);
5070
5071        let two_buffer = references[0].buffer.read(cx);
5072        let three_buffer = references[2].buffer.read(cx);
5073        assert_eq!(
5074            two_buffer.file().unwrap().path().as_ref(),
5075            Path::new("two.rs")
5076        );
5077        assert_eq!(references[1].buffer, references[0].buffer);
5078        assert_eq!(
5079            three_buffer.file().unwrap().full_path(cx),
5080            Path::new(path!("/root/dir-2/three.rs"))
5081        );
5082
5083        assert_eq!(references[0].range.to_offset(two_buffer), 24..27);
5084        assert_eq!(references[1].range.to_offset(two_buffer), 35..38);
5085        assert_eq!(references[2].range.to_offset(three_buffer), 37..40);
5086    });
5087
5088    let references = project_b.update(cx_b, |p, cx| p.references(&buffer_b, 7, cx));
5089
5090    // User is informed that a request is pending.
5091    executor.run_until_parked();
5092    project_b.read_with(cx_b, |project, cx| {
5093        let status = project.language_server_statuses(cx).next().unwrap().1;
5094        assert_eq!(status.name.0, "my-fake-lsp-adapter");
5095        assert_eq!(
5096            status.pending_work.values().next().unwrap().message,
5097            Some("Finding references...".into())
5098        );
5099    });
5100
5101    // Cause the LSP request to fail.
5102    lsp_response_tx
5103        .unbounded_send(Err(anyhow!("can't find references")))
5104        .unwrap();
5105    assert_eq!(references.await.unwrap().unwrap(), []);
5106
5107    // User is informed that the request is no longer pending.
5108    executor.run_until_parked();
5109    project_b.read_with(cx_b, |project, cx| {
5110        let status = project.language_server_statuses(cx).next().unwrap().1;
5111        assert!(status.pending_work.is_empty());
5112    });
5113}
5114
5115#[gpui::test(iterations = 10)]
5116async fn test_project_search(
5117    executor: BackgroundExecutor,
5118    cx_a: &mut TestAppContext,
5119    cx_b: &mut TestAppContext,
5120) {
5121    let mut server = TestServer::start(executor.clone()).await;
5122    let client_a = server.create_client(cx_a, "user_a").await;
5123    let client_b = server.create_client(cx_b, "user_b").await;
5124    server
5125        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5126        .await;
5127    let active_call_a = cx_a.read(ActiveCall::global);
5128
5129    client_a
5130        .fs()
5131        .insert_tree(
5132            "/root",
5133            json!({
5134                "dir-1": {
5135                    "a": "hello world",
5136                    "b": "goodnight moon",
5137                    "c": "a world of goo",
5138                    "d": "world champion of clown world",
5139                },
5140                "dir-2": {
5141                    "e": "disney world is fun",
5142                }
5143            }),
5144        )
5145        .await;
5146    let (project_a, _) = client_a.build_local_project("/root/dir-1", cx_a).await;
5147    let (worktree_2, _) = project_a
5148        .update(cx_a, |p, cx| {
5149            p.find_or_create_worktree("/root/dir-2", true, cx)
5150        })
5151        .await
5152        .unwrap();
5153    worktree_2
5154        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
5155        .await;
5156    let project_id = active_call_a
5157        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5158        .await
5159        .unwrap();
5160
5161    let project_b = client_b.join_remote_project(project_id, cx_b).await;
5162
5163    // Perform a search as the guest.
5164    let mut results = HashMap::default();
5165    let search_rx = project_b.update(cx_b, |project, cx| {
5166        project.search(
5167            SearchQuery::text(
5168                "world",
5169                false,
5170                false,
5171                false,
5172                Default::default(),
5173                Default::default(),
5174                false,
5175                None,
5176            )
5177            .unwrap(),
5178            cx,
5179        )
5180    });
5181    while let Ok(result) = search_rx.recv().await {
5182        match result {
5183            SearchResult::Buffer { buffer, ranges } => {
5184                results.entry(buffer).or_insert(ranges);
5185            }
5186            SearchResult::LimitReached => {
5187                panic!(
5188                    "Unexpectedly reached search limit in tests. If you do want to assert limit-reached, change this panic call."
5189                )
5190            }
5191        };
5192    }
5193
5194    let mut ranges_by_path = results
5195        .into_iter()
5196        .map(|(buffer, ranges)| {
5197            buffer.read_with(cx_b, |buffer, cx| {
5198                let path = buffer.file().unwrap().full_path(cx);
5199                let offset_ranges = ranges
5200                    .into_iter()
5201                    .map(|range| range.to_offset(buffer))
5202                    .collect::<Vec<_>>();
5203                (path, offset_ranges)
5204            })
5205        })
5206        .collect::<Vec<_>>();
5207    ranges_by_path.sort_by_key(|(path, _)| path.clone());
5208
5209    assert_eq!(
5210        ranges_by_path,
5211        &[
5212            (PathBuf::from("dir-1/a"), vec![6..11]),
5213            (PathBuf::from("dir-1/c"), vec![2..7]),
5214            (PathBuf::from("dir-1/d"), vec![0..5, 24..29]),
5215            (PathBuf::from("dir-2/e"), vec![7..12]),
5216        ]
5217    );
5218}
5219
5220#[gpui::test(iterations = 10)]
5221async fn test_document_highlights(
5222    executor: BackgroundExecutor,
5223    cx_a: &mut TestAppContext,
5224    cx_b: &mut TestAppContext,
5225) {
5226    let mut server = TestServer::start(executor.clone()).await;
5227    let client_a = server.create_client(cx_a, "user_a").await;
5228    let client_b = server.create_client(cx_b, "user_b").await;
5229    server
5230        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5231        .await;
5232    let active_call_a = cx_a.read(ActiveCall::global);
5233
5234    client_a
5235        .fs()
5236        .insert_tree(
5237            path!("/root-1"),
5238            json!({
5239                "main.rs": "fn double(number: i32) -> i32 { number + number }",
5240            }),
5241        )
5242        .await;
5243
5244    client_a.language_registry().add(rust_lang());
5245    let capabilities = lsp::ServerCapabilities {
5246        document_highlight_provider: Some(lsp::OneOf::Left(true)),
5247        ..lsp::ServerCapabilities::default()
5248    };
5249    let mut fake_language_servers = client_a.language_registry().register_fake_lsp(
5250        "Rust",
5251        FakeLspAdapter {
5252            capabilities: capabilities.clone(),
5253            ..FakeLspAdapter::default()
5254        },
5255    );
5256    client_b.language_registry().add(rust_lang());
5257    client_b.language_registry().register_fake_lsp_adapter(
5258        "Rust",
5259        FakeLspAdapter {
5260            capabilities,
5261            ..FakeLspAdapter::default()
5262        },
5263    );
5264
5265    let (project_a, worktree_id) = client_a.build_local_project(path!("/root-1"), cx_a).await;
5266    let project_id = active_call_a
5267        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5268        .await
5269        .unwrap();
5270    let project_b = client_b.join_remote_project(project_id, cx_b).await;
5271
5272    // Open the file on client B.
5273    let (buffer_b, _handle) = project_b
5274        .update(cx_b, |p, cx| {
5275            p.open_buffer_with_lsp((worktree_id, "main.rs"), cx)
5276        })
5277        .await
5278        .unwrap();
5279
5280    // Request document highlights as the guest.
5281    let fake_language_server = fake_language_servers.next().await.unwrap();
5282    fake_language_server.set_request_handler::<lsp::request::DocumentHighlightRequest, _, _>(
5283        |params, _| async move {
5284            assert_eq!(
5285                params
5286                    .text_document_position_params
5287                    .text_document
5288                    .uri
5289                    .as_str(),
5290                uri!("file:///root-1/main.rs")
5291            );
5292            assert_eq!(
5293                params.text_document_position_params.position,
5294                lsp::Position::new(0, 34)
5295            );
5296            Ok(Some(vec![
5297                lsp::DocumentHighlight {
5298                    kind: Some(lsp::DocumentHighlightKind::WRITE),
5299                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 16)),
5300                },
5301                lsp::DocumentHighlight {
5302                    kind: Some(lsp::DocumentHighlightKind::READ),
5303                    range: lsp::Range::new(lsp::Position::new(0, 32), lsp::Position::new(0, 38)),
5304                },
5305                lsp::DocumentHighlight {
5306                    kind: Some(lsp::DocumentHighlightKind::READ),
5307                    range: lsp::Range::new(lsp::Position::new(0, 41), lsp::Position::new(0, 47)),
5308                },
5309            ]))
5310        },
5311    );
5312    cx_a.run_until_parked();
5313    cx_b.run_until_parked();
5314
5315    let highlights = project_b
5316        .update(cx_b, |p, cx| p.document_highlights(&buffer_b, 34, cx))
5317        .await
5318        .unwrap();
5319
5320    buffer_b.read_with(cx_b, |buffer, _| {
5321        let snapshot = buffer.snapshot();
5322
5323        let highlights = highlights
5324            .into_iter()
5325            .map(|highlight| (highlight.kind, highlight.range.to_offset(&snapshot)))
5326            .collect::<Vec<_>>();
5327        assert_eq!(
5328            highlights,
5329            &[
5330                (lsp::DocumentHighlightKind::WRITE, 10..16),
5331                (lsp::DocumentHighlightKind::READ, 32..38),
5332                (lsp::DocumentHighlightKind::READ, 41..47)
5333            ]
5334        )
5335    });
5336}
5337
5338#[gpui::test(iterations = 10)]
5339async fn test_lsp_hover(
5340    executor: BackgroundExecutor,
5341    cx_a: &mut TestAppContext,
5342    cx_b: &mut TestAppContext,
5343) {
5344    let mut server = TestServer::start(executor.clone()).await;
5345    let client_a = server.create_client(cx_a, "user_a").await;
5346    let client_b = server.create_client(cx_b, "user_b").await;
5347    server
5348        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5349        .await;
5350    let active_call_a = cx_a.read(ActiveCall::global);
5351
5352    client_a
5353        .fs()
5354        .insert_tree(
5355            path!("/root-1"),
5356            json!({
5357                "main.rs": "use std::collections::HashMap;",
5358            }),
5359        )
5360        .await;
5361
5362    client_a.language_registry().add(rust_lang());
5363    let language_server_names = ["rust-analyzer", "CrabLang-ls"];
5364    let capabilities_1 = lsp::ServerCapabilities {
5365        hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
5366        ..lsp::ServerCapabilities::default()
5367    };
5368    let capabilities_2 = lsp::ServerCapabilities {
5369        hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
5370        ..lsp::ServerCapabilities::default()
5371    };
5372    let mut language_servers = [
5373        client_a.language_registry().register_fake_lsp(
5374            "Rust",
5375            FakeLspAdapter {
5376                name: language_server_names[0],
5377                capabilities: capabilities_1.clone(),
5378                ..FakeLspAdapter::default()
5379            },
5380        ),
5381        client_a.language_registry().register_fake_lsp(
5382            "Rust",
5383            FakeLspAdapter {
5384                name: language_server_names[1],
5385                capabilities: capabilities_2.clone(),
5386                ..FakeLspAdapter::default()
5387            },
5388        ),
5389    ];
5390    client_b.language_registry().add(rust_lang());
5391    client_b.language_registry().register_fake_lsp_adapter(
5392        "Rust",
5393        FakeLspAdapter {
5394            name: language_server_names[0],
5395            capabilities: capabilities_1,
5396            ..FakeLspAdapter::default()
5397        },
5398    );
5399    client_b.language_registry().register_fake_lsp_adapter(
5400        "Rust",
5401        FakeLspAdapter {
5402            name: language_server_names[1],
5403            capabilities: capabilities_2,
5404            ..FakeLspAdapter::default()
5405        },
5406    );
5407
5408    let (project_a, worktree_id) = client_a.build_local_project(path!("/root-1"), cx_a).await;
5409    let project_id = active_call_a
5410        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5411        .await
5412        .unwrap();
5413    let project_b = client_b.join_remote_project(project_id, cx_b).await;
5414
5415    // Open the file as the guest
5416    let (buffer_b, _handle) = project_b
5417        .update(cx_b, |p, cx| {
5418            p.open_buffer_with_lsp((worktree_id, "main.rs"), cx)
5419        })
5420        .await
5421        .unwrap();
5422
5423    let mut servers_with_hover_requests = HashMap::default();
5424    for i in 0..language_server_names.len() {
5425        let new_server = language_servers[i].next().await.unwrap_or_else(|| {
5426            panic!(
5427                "Failed to get language server #{i} with name {}",
5428                &language_server_names[i]
5429            )
5430        });
5431        let new_server_name = new_server.server.name();
5432        assert!(
5433            !servers_with_hover_requests.contains_key(&new_server_name),
5434            "Unexpected: initialized server with the same name twice. Name: `{new_server_name}`"
5435        );
5436        match new_server_name.as_ref() {
5437            "CrabLang-ls" => {
5438                servers_with_hover_requests.insert(
5439                    new_server_name.clone(),
5440                    new_server.set_request_handler::<lsp::request::HoverRequest, _, _>(
5441                        move |params, _| {
5442                            assert_eq!(
5443                                params
5444                                    .text_document_position_params
5445                                    .text_document
5446                                    .uri
5447                                    .as_str(),
5448                                uri!("file:///root-1/main.rs")
5449                            );
5450                            let name = new_server_name.clone();
5451                            async move {
5452                                Ok(Some(lsp::Hover {
5453                                    contents: lsp::HoverContents::Scalar(
5454                                        lsp::MarkedString::String(format!("{name} hover")),
5455                                    ),
5456                                    range: None,
5457                                }))
5458                            }
5459                        },
5460                    ),
5461                );
5462            }
5463            "rust-analyzer" => {
5464                servers_with_hover_requests.insert(
5465                    new_server_name.clone(),
5466                    new_server.set_request_handler::<lsp::request::HoverRequest, _, _>(
5467                        |params, _| async move {
5468                            assert_eq!(
5469                                params
5470                                    .text_document_position_params
5471                                    .text_document
5472                                    .uri
5473                                    .as_str(),
5474                                uri!("file:///root-1/main.rs")
5475                            );
5476                            assert_eq!(
5477                                params.text_document_position_params.position,
5478                                lsp::Position::new(0, 22)
5479                            );
5480                            Ok(Some(lsp::Hover {
5481                                contents: lsp::HoverContents::Array(vec![
5482                                    lsp::MarkedString::String("Test hover content.".to_string()),
5483                                    lsp::MarkedString::LanguageString(lsp::LanguageString {
5484                                        language: "Rust".to_string(),
5485                                        value: "let foo = 42;".to_string(),
5486                                    }),
5487                                ]),
5488                                range: Some(lsp::Range::new(
5489                                    lsp::Position::new(0, 22),
5490                                    lsp::Position::new(0, 29),
5491                                )),
5492                            }))
5493                        },
5494                    ),
5495                );
5496            }
5497            unexpected => panic!("Unexpected server name: {unexpected}"),
5498        }
5499    }
5500    cx_a.run_until_parked();
5501    cx_b.run_until_parked();
5502
5503    // Request hover information as the guest.
5504    let mut hovers = project_b
5505        .update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
5506        .await
5507        .unwrap();
5508    assert_eq!(
5509        hovers.len(),
5510        2,
5511        "Expected two hovers from both language servers, but got: {hovers:?}"
5512    );
5513
5514    let _: Vec<()> = futures::future::join_all(servers_with_hover_requests.into_values().map(
5515        |mut hover_request| async move {
5516            hover_request
5517                .next()
5518                .await
5519                .expect("All hover requests should have been triggered")
5520        },
5521    ))
5522    .await;
5523
5524    hovers.sort_by_key(|hover| hover.contents.len());
5525    let first_hover = hovers.first().cloned().unwrap();
5526    assert_eq!(
5527        first_hover.contents,
5528        vec![project::HoverBlock {
5529            text: "CrabLang-ls hover".to_string(),
5530            kind: HoverBlockKind::Markdown,
5531        },]
5532    );
5533    let second_hover = hovers.last().cloned().unwrap();
5534    assert_eq!(
5535        second_hover.contents,
5536        vec![
5537            project::HoverBlock {
5538                text: "Test hover content.".to_string(),
5539                kind: HoverBlockKind::Markdown,
5540            },
5541            project::HoverBlock {
5542                text: "let foo = 42;".to_string(),
5543                kind: HoverBlockKind::Code {
5544                    language: "Rust".to_string()
5545                },
5546            }
5547        ]
5548    );
5549    buffer_b.read_with(cx_b, |buffer, _| {
5550        let snapshot = buffer.snapshot();
5551        assert_eq!(second_hover.range.unwrap().to_offset(&snapshot), 22..29);
5552    });
5553}
5554
5555#[gpui::test(iterations = 10)]
5556async fn test_project_symbols(
5557    executor: BackgroundExecutor,
5558    cx_a: &mut TestAppContext,
5559    cx_b: &mut TestAppContext,
5560) {
5561    let mut server = TestServer::start(executor.clone()).await;
5562    let client_a = server.create_client(cx_a, "user_a").await;
5563    let client_b = server.create_client(cx_b, "user_b").await;
5564    server
5565        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5566        .await;
5567    let active_call_a = cx_a.read(ActiveCall::global);
5568
5569    client_a.language_registry().add(rust_lang());
5570    let mut fake_language_servers = client_a.language_registry().register_fake_lsp(
5571        "Rust",
5572        FakeLspAdapter {
5573            capabilities: lsp::ServerCapabilities {
5574                workspace_symbol_provider: Some(OneOf::Left(true)),
5575                ..Default::default()
5576            },
5577            ..Default::default()
5578        },
5579    );
5580
5581    client_a
5582        .fs()
5583        .insert_tree(
5584            path!("/code"),
5585            json!({
5586                "crate-1": {
5587                    "one.rs": "const ONE: usize = 1;",
5588                },
5589                "crate-2": {
5590                    "two.rs": "const TWO: usize = 2; const THREE: usize = 3;",
5591                },
5592                "private": {
5593                    "passwords.txt": "the-password",
5594                }
5595            }),
5596        )
5597        .await;
5598    let (project_a, worktree_id) = client_a
5599        .build_local_project(path!("/code/crate-1"), cx_a)
5600        .await;
5601    let project_id = active_call_a
5602        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5603        .await
5604        .unwrap();
5605    let project_b = client_b.join_remote_project(project_id, cx_b).await;
5606
5607    // Cause the language server to start.
5608    let _buffer = project_b
5609        .update(cx_b, |p, cx| {
5610            p.open_buffer_with_lsp((worktree_id, "one.rs"), cx)
5611        })
5612        .await
5613        .unwrap();
5614
5615    let fake_language_server = fake_language_servers.next().await.unwrap();
5616    fake_language_server.set_request_handler::<lsp::WorkspaceSymbolRequest, _, _>(
5617        |_, _| async move {
5618            Ok(Some(lsp::WorkspaceSymbolResponse::Flat(vec![
5619                #[allow(deprecated)]
5620                lsp::SymbolInformation {
5621                    name: "TWO".into(),
5622                    location: lsp::Location {
5623                        uri: lsp::Url::from_file_path(path!("/code/crate-2/two.rs")).unwrap(),
5624                        range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5625                    },
5626                    kind: lsp::SymbolKind::CONSTANT,
5627                    tags: None,
5628                    container_name: None,
5629                    deprecated: None,
5630                },
5631            ])))
5632        },
5633    );
5634
5635    // Request the definition of a symbol as the guest.
5636    let symbols = project_b
5637        .update(cx_b, |p, cx| p.symbols("two", cx))
5638        .await
5639        .unwrap();
5640    assert_eq!(symbols.len(), 1);
5641    assert_eq!(symbols[0].name, "TWO");
5642
5643    // Open one of the returned symbols.
5644    let buffer_b_2 = project_b
5645        .update(cx_b, |project, cx| {
5646            project.open_buffer_for_symbol(&symbols[0], cx)
5647        })
5648        .await
5649        .unwrap();
5650
5651    buffer_b_2.read_with(cx_b, |buffer, cx| {
5652        assert_eq!(
5653            buffer.file().unwrap().full_path(cx),
5654            Path::new(path!("/code/crate-2/two.rs"))
5655        );
5656    });
5657
5658    // Attempt to craft a symbol and violate host's privacy by opening an arbitrary file.
5659    let mut fake_symbol = symbols[0].clone();
5660    fake_symbol.path.path = Path::new(path!("/code/secrets")).into();
5661    let error = project_b
5662        .update(cx_b, |project, cx| {
5663            project.open_buffer_for_symbol(&fake_symbol, cx)
5664        })
5665        .await
5666        .unwrap_err();
5667    assert!(error.to_string().contains("invalid symbol signature"));
5668}
5669
5670#[gpui::test(iterations = 10)]
5671async fn test_open_buffer_while_getting_definition_pointing_to_it(
5672    executor: BackgroundExecutor,
5673    cx_a: &mut TestAppContext,
5674    cx_b: &mut TestAppContext,
5675    mut rng: StdRng,
5676) {
5677    let mut server = TestServer::start(executor.clone()).await;
5678    let client_a = server.create_client(cx_a, "user_a").await;
5679    let client_b = server.create_client(cx_b, "user_b").await;
5680    server
5681        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5682        .await;
5683    let active_call_a = cx_a.read(ActiveCall::global);
5684
5685    let capabilities = lsp::ServerCapabilities {
5686        definition_provider: Some(OneOf::Left(true)),
5687        ..lsp::ServerCapabilities::default()
5688    };
5689    client_a.language_registry().add(rust_lang());
5690    let mut fake_language_servers = client_a.language_registry().register_fake_lsp(
5691        "Rust",
5692        FakeLspAdapter {
5693            capabilities: capabilities.clone(),
5694            ..FakeLspAdapter::default()
5695        },
5696    );
5697    client_b.language_registry().add(rust_lang());
5698    client_b.language_registry().register_fake_lsp_adapter(
5699        "Rust",
5700        FakeLspAdapter {
5701            capabilities,
5702            ..FakeLspAdapter::default()
5703        },
5704    );
5705
5706    client_a
5707        .fs()
5708        .insert_tree(
5709            path!("/root"),
5710            json!({
5711                "a.rs": "const ONE: usize = b::TWO;",
5712                "b.rs": "const TWO: usize = 2",
5713            }),
5714        )
5715        .await;
5716    let (project_a, worktree_id) = client_a.build_local_project(path!("/root"), cx_a).await;
5717    let project_id = active_call_a
5718        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5719        .await
5720        .unwrap();
5721    let project_b = client_b.join_remote_project(project_id, cx_b).await;
5722
5723    let (buffer_b1, _lsp) = project_b
5724        .update(cx_b, |p, cx| {
5725            p.open_buffer_with_lsp((worktree_id, "a.rs"), cx)
5726        })
5727        .await
5728        .unwrap();
5729
5730    let fake_language_server = fake_language_servers.next().await.unwrap();
5731    fake_language_server.set_request_handler::<lsp::request::GotoDefinition, _, _>(
5732        |_, _| async move {
5733            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
5734                lsp::Location::new(
5735                    lsp::Url::from_file_path(path!("/root/b.rs")).unwrap(),
5736                    lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5737                ),
5738            )))
5739        },
5740    );
5741
5742    let definitions;
5743    let buffer_b2;
5744    if rng.r#gen() {
5745        cx_a.run_until_parked();
5746        cx_b.run_until_parked();
5747        definitions = project_b.update(cx_b, |p, cx| p.definitions(&buffer_b1, 23, cx));
5748        (buffer_b2, _) = project_b
5749            .update(cx_b, |p, cx| {
5750                p.open_buffer_with_lsp((worktree_id, "b.rs"), cx)
5751            })
5752            .await
5753            .unwrap();
5754    } else {
5755        (buffer_b2, _) = project_b
5756            .update(cx_b, |p, cx| {
5757                p.open_buffer_with_lsp((worktree_id, "b.rs"), cx)
5758            })
5759            .await
5760            .unwrap();
5761        cx_a.run_until_parked();
5762        cx_b.run_until_parked();
5763        definitions = project_b.update(cx_b, |p, cx| p.definitions(&buffer_b1, 23, cx));
5764    }
5765
5766    let definitions = definitions.await.unwrap().unwrap();
5767    assert_eq!(
5768        definitions.len(),
5769        1,
5770        "Unexpected definitions: {definitions:?}"
5771    );
5772    assert_eq!(definitions[0].target.buffer, buffer_b2);
5773}
5774
5775#[gpui::test(iterations = 10)]
5776async fn test_contacts(
5777    executor: BackgroundExecutor,
5778    cx_a: &mut TestAppContext,
5779    cx_b: &mut TestAppContext,
5780    cx_c: &mut TestAppContext,
5781    cx_d: &mut TestAppContext,
5782) {
5783    let mut server = TestServer::start(executor.clone()).await;
5784    let client_a = server.create_client(cx_a, "user_a").await;
5785    let client_b = server.create_client(cx_b, "user_b").await;
5786    let client_c = server.create_client(cx_c, "user_c").await;
5787    let client_d = server.create_client(cx_d, "user_d").await;
5788    server
5789        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
5790        .await;
5791    let active_call_a = cx_a.read(ActiveCall::global);
5792    let active_call_b = cx_b.read(ActiveCall::global);
5793    let active_call_c = cx_c.read(ActiveCall::global);
5794    let _active_call_d = cx_d.read(ActiveCall::global);
5795
5796    executor.run_until_parked();
5797    assert_eq!(
5798        contacts(&client_a, cx_a),
5799        [
5800            ("user_b".to_string(), "online", "free"),
5801            ("user_c".to_string(), "online", "free")
5802        ]
5803    );
5804    assert_eq!(
5805        contacts(&client_b, cx_b),
5806        [
5807            ("user_a".to_string(), "online", "free"),
5808            ("user_c".to_string(), "online", "free")
5809        ]
5810    );
5811    assert_eq!(
5812        contacts(&client_c, cx_c),
5813        [
5814            ("user_a".to_string(), "online", "free"),
5815            ("user_b".to_string(), "online", "free")
5816        ]
5817    );
5818    assert_eq!(contacts(&client_d, cx_d), []);
5819
5820    server.disconnect_client(client_c.peer_id().unwrap());
5821    server.forbid_connections();
5822    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5823    assert_eq!(
5824        contacts(&client_a, cx_a),
5825        [
5826            ("user_b".to_string(), "online", "free"),
5827            ("user_c".to_string(), "offline", "free")
5828        ]
5829    );
5830    assert_eq!(
5831        contacts(&client_b, cx_b),
5832        [
5833            ("user_a".to_string(), "online", "free"),
5834            ("user_c".to_string(), "offline", "free")
5835        ]
5836    );
5837    assert_eq!(contacts(&client_c, cx_c), []);
5838    assert_eq!(contacts(&client_d, cx_d), []);
5839
5840    server.allow_connections();
5841    client_c
5842        .connect(false, &cx_c.to_async())
5843        .await
5844        .into_response()
5845        .unwrap();
5846
5847    executor.run_until_parked();
5848    assert_eq!(
5849        contacts(&client_a, cx_a),
5850        [
5851            ("user_b".to_string(), "online", "free"),
5852            ("user_c".to_string(), "online", "free")
5853        ]
5854    );
5855    assert_eq!(
5856        contacts(&client_b, cx_b),
5857        [
5858            ("user_a".to_string(), "online", "free"),
5859            ("user_c".to_string(), "online", "free")
5860        ]
5861    );
5862    assert_eq!(
5863        contacts(&client_c, cx_c),
5864        [
5865            ("user_a".to_string(), "online", "free"),
5866            ("user_b".to_string(), "online", "free")
5867        ]
5868    );
5869    assert_eq!(contacts(&client_d, cx_d), []);
5870
5871    active_call_a
5872        .update(cx_a, |call, cx| {
5873            call.invite(client_b.user_id().unwrap(), None, cx)
5874        })
5875        .await
5876        .unwrap();
5877    executor.run_until_parked();
5878    assert_eq!(
5879        contacts(&client_a, cx_a),
5880        [
5881            ("user_b".to_string(), "online", "busy"),
5882            ("user_c".to_string(), "online", "free")
5883        ]
5884    );
5885    assert_eq!(
5886        contacts(&client_b, cx_b),
5887        [
5888            ("user_a".to_string(), "online", "busy"),
5889            ("user_c".to_string(), "online", "free")
5890        ]
5891    );
5892    assert_eq!(
5893        contacts(&client_c, cx_c),
5894        [
5895            ("user_a".to_string(), "online", "busy"),
5896            ("user_b".to_string(), "online", "busy")
5897        ]
5898    );
5899    assert_eq!(contacts(&client_d, cx_d), []);
5900
5901    // Client B and client D become contacts while client B is being called.
5902    server
5903        .make_contacts(&mut [(&client_b, cx_b), (&client_d, cx_d)])
5904        .await;
5905    executor.run_until_parked();
5906    assert_eq!(
5907        contacts(&client_a, cx_a),
5908        [
5909            ("user_b".to_string(), "online", "busy"),
5910            ("user_c".to_string(), "online", "free")
5911        ]
5912    );
5913    assert_eq!(
5914        contacts(&client_b, cx_b),
5915        [
5916            ("user_a".to_string(), "online", "busy"),
5917            ("user_c".to_string(), "online", "free"),
5918            ("user_d".to_string(), "online", "free"),
5919        ]
5920    );
5921    assert_eq!(
5922        contacts(&client_c, cx_c),
5923        [
5924            ("user_a".to_string(), "online", "busy"),
5925            ("user_b".to_string(), "online", "busy")
5926        ]
5927    );
5928    assert_eq!(
5929        contacts(&client_d, cx_d),
5930        [("user_b".to_string(), "online", "busy")]
5931    );
5932
5933    active_call_b.update(cx_b, |call, cx| call.decline_incoming(cx).unwrap());
5934    executor.run_until_parked();
5935    assert_eq!(
5936        contacts(&client_a, cx_a),
5937        [
5938            ("user_b".to_string(), "online", "free"),
5939            ("user_c".to_string(), "online", "free")
5940        ]
5941    );
5942    assert_eq!(
5943        contacts(&client_b, cx_b),
5944        [
5945            ("user_a".to_string(), "online", "free"),
5946            ("user_c".to_string(), "online", "free"),
5947            ("user_d".to_string(), "online", "free")
5948        ]
5949    );
5950    assert_eq!(
5951        contacts(&client_c, cx_c),
5952        [
5953            ("user_a".to_string(), "online", "free"),
5954            ("user_b".to_string(), "online", "free")
5955        ]
5956    );
5957    assert_eq!(
5958        contacts(&client_d, cx_d),
5959        [("user_b".to_string(), "online", "free")]
5960    );
5961
5962    active_call_c
5963        .update(cx_c, |call, cx| {
5964            call.invite(client_a.user_id().unwrap(), None, cx)
5965        })
5966        .await
5967        .unwrap();
5968    executor.run_until_parked();
5969    assert_eq!(
5970        contacts(&client_a, cx_a),
5971        [
5972            ("user_b".to_string(), "online", "free"),
5973            ("user_c".to_string(), "online", "busy")
5974        ]
5975    );
5976    assert_eq!(
5977        contacts(&client_b, cx_b),
5978        [
5979            ("user_a".to_string(), "online", "busy"),
5980            ("user_c".to_string(), "online", "busy"),
5981            ("user_d".to_string(), "online", "free")
5982        ]
5983    );
5984    assert_eq!(
5985        contacts(&client_c, cx_c),
5986        [
5987            ("user_a".to_string(), "online", "busy"),
5988            ("user_b".to_string(), "online", "free")
5989        ]
5990    );
5991    assert_eq!(
5992        contacts(&client_d, cx_d),
5993        [("user_b".to_string(), "online", "free")]
5994    );
5995
5996    active_call_a
5997        .update(cx_a, |call, cx| call.accept_incoming(cx))
5998        .await
5999        .unwrap();
6000    executor.run_until_parked();
6001    assert_eq!(
6002        contacts(&client_a, cx_a),
6003        [
6004            ("user_b".to_string(), "online", "free"),
6005            ("user_c".to_string(), "online", "busy")
6006        ]
6007    );
6008    assert_eq!(
6009        contacts(&client_b, cx_b),
6010        [
6011            ("user_a".to_string(), "online", "busy"),
6012            ("user_c".to_string(), "online", "busy"),
6013            ("user_d".to_string(), "online", "free")
6014        ]
6015    );
6016    assert_eq!(
6017        contacts(&client_c, cx_c),
6018        [
6019            ("user_a".to_string(), "online", "busy"),
6020            ("user_b".to_string(), "online", "free")
6021        ]
6022    );
6023    assert_eq!(
6024        contacts(&client_d, cx_d),
6025        [("user_b".to_string(), "online", "free")]
6026    );
6027
6028    active_call_a
6029        .update(cx_a, |call, cx| {
6030            call.invite(client_b.user_id().unwrap(), None, cx)
6031        })
6032        .await
6033        .unwrap();
6034    executor.run_until_parked();
6035    assert_eq!(
6036        contacts(&client_a, cx_a),
6037        [
6038            ("user_b".to_string(), "online", "busy"),
6039            ("user_c".to_string(), "online", "busy")
6040        ]
6041    );
6042    assert_eq!(
6043        contacts(&client_b, cx_b),
6044        [
6045            ("user_a".to_string(), "online", "busy"),
6046            ("user_c".to_string(), "online", "busy"),
6047            ("user_d".to_string(), "online", "free")
6048        ]
6049    );
6050    assert_eq!(
6051        contacts(&client_c, cx_c),
6052        [
6053            ("user_a".to_string(), "online", "busy"),
6054            ("user_b".to_string(), "online", "busy")
6055        ]
6056    );
6057    assert_eq!(
6058        contacts(&client_d, cx_d),
6059        [("user_b".to_string(), "online", "busy")]
6060    );
6061
6062    active_call_a
6063        .update(cx_a, |call, cx| call.hang_up(cx))
6064        .await
6065        .unwrap();
6066    executor.run_until_parked();
6067    assert_eq!(
6068        contacts(&client_a, cx_a),
6069        [
6070            ("user_b".to_string(), "online", "free"),
6071            ("user_c".to_string(), "online", "free")
6072        ]
6073    );
6074    assert_eq!(
6075        contacts(&client_b, cx_b),
6076        [
6077            ("user_a".to_string(), "online", "free"),
6078            ("user_c".to_string(), "online", "free"),
6079            ("user_d".to_string(), "online", "free")
6080        ]
6081    );
6082    assert_eq!(
6083        contacts(&client_c, cx_c),
6084        [
6085            ("user_a".to_string(), "online", "free"),
6086            ("user_b".to_string(), "online", "free")
6087        ]
6088    );
6089    assert_eq!(
6090        contacts(&client_d, cx_d),
6091        [("user_b".to_string(), "online", "free")]
6092    );
6093
6094    active_call_a
6095        .update(cx_a, |call, cx| {
6096            call.invite(client_b.user_id().unwrap(), None, cx)
6097        })
6098        .await
6099        .unwrap();
6100    executor.run_until_parked();
6101    assert_eq!(
6102        contacts(&client_a, cx_a),
6103        [
6104            ("user_b".to_string(), "online", "busy"),
6105            ("user_c".to_string(), "online", "free")
6106        ]
6107    );
6108    assert_eq!(
6109        contacts(&client_b, cx_b),
6110        [
6111            ("user_a".to_string(), "online", "busy"),
6112            ("user_c".to_string(), "online", "free"),
6113            ("user_d".to_string(), "online", "free")
6114        ]
6115    );
6116    assert_eq!(
6117        contacts(&client_c, cx_c),
6118        [
6119            ("user_a".to_string(), "online", "busy"),
6120            ("user_b".to_string(), "online", "busy")
6121        ]
6122    );
6123    assert_eq!(
6124        contacts(&client_d, cx_d),
6125        [("user_b".to_string(), "online", "busy")]
6126    );
6127
6128    server.forbid_connections();
6129    server.disconnect_client(client_a.peer_id().unwrap());
6130    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
6131    assert_eq!(contacts(&client_a, cx_a), []);
6132    assert_eq!(
6133        contacts(&client_b, cx_b),
6134        [
6135            ("user_a".to_string(), "offline", "free"),
6136            ("user_c".to_string(), "online", "free"),
6137            ("user_d".to_string(), "online", "free")
6138        ]
6139    );
6140    assert_eq!(
6141        contacts(&client_c, cx_c),
6142        [
6143            ("user_a".to_string(), "offline", "free"),
6144            ("user_b".to_string(), "online", "free")
6145        ]
6146    );
6147    assert_eq!(
6148        contacts(&client_d, cx_d),
6149        [("user_b".to_string(), "online", "free")]
6150    );
6151
6152    // Test removing a contact
6153    client_b
6154        .user_store()
6155        .update(cx_b, |store, cx| {
6156            store.remove_contact(client_c.user_id().unwrap(), cx)
6157        })
6158        .await
6159        .unwrap();
6160    executor.run_until_parked();
6161    assert_eq!(
6162        contacts(&client_b, cx_b),
6163        [
6164            ("user_a".to_string(), "offline", "free"),
6165            ("user_d".to_string(), "online", "free")
6166        ]
6167    );
6168    assert_eq!(
6169        contacts(&client_c, cx_c),
6170        [("user_a".to_string(), "offline", "free"),]
6171    );
6172
6173    fn contacts(
6174        client: &TestClient,
6175        cx: &TestAppContext,
6176    ) -> Vec<(String, &'static str, &'static str)> {
6177        client.user_store().read_with(cx, |store, _| {
6178            store
6179                .contacts()
6180                .iter()
6181                .map(|contact| {
6182                    (
6183                        contact.user.github_login.clone().to_string(),
6184                        if contact.online { "online" } else { "offline" },
6185                        if contact.busy { "busy" } else { "free" },
6186                    )
6187                })
6188                .collect()
6189        })
6190    }
6191}
6192
6193#[gpui::test(iterations = 10)]
6194async fn test_contact_requests(
6195    executor: BackgroundExecutor,
6196    cx_a: &mut TestAppContext,
6197    cx_a2: &mut TestAppContext,
6198    cx_b: &mut TestAppContext,
6199    cx_b2: &mut TestAppContext,
6200    cx_c: &mut TestAppContext,
6201    cx_c2: &mut TestAppContext,
6202) {
6203    // Connect to a server as 3 clients.
6204    let mut server = TestServer::start(executor.clone()).await;
6205    let client_a = server.create_client(cx_a, "user_a").await;
6206    let client_a2 = server.create_client(cx_a2, "user_a").await;
6207    let client_b = server.create_client(cx_b, "user_b").await;
6208    let client_b2 = server.create_client(cx_b2, "user_b").await;
6209    let client_c = server.create_client(cx_c, "user_c").await;
6210    let client_c2 = server.create_client(cx_c2, "user_c").await;
6211
6212    assert_eq!(client_a.user_id().unwrap(), client_a2.user_id().unwrap());
6213    assert_eq!(client_b.user_id().unwrap(), client_b2.user_id().unwrap());
6214    assert_eq!(client_c.user_id().unwrap(), client_c2.user_id().unwrap());
6215
6216    // User A and User C request that user B become their contact.
6217    client_a
6218        .user_store()
6219        .update(cx_a, |store, cx| {
6220            store.request_contact(client_b.user_id().unwrap(), cx)
6221        })
6222        .await
6223        .unwrap();
6224    client_c
6225        .user_store()
6226        .update(cx_c, |store, cx| {
6227            store.request_contact(client_b.user_id().unwrap(), cx)
6228        })
6229        .await
6230        .unwrap();
6231    executor.run_until_parked();
6232
6233    // All users see the pending request appear in all their clients.
6234    assert_eq!(
6235        client_a.summarize_contacts(cx_a).outgoing_requests,
6236        &["user_b"]
6237    );
6238    assert_eq!(
6239        client_a2.summarize_contacts(cx_a2).outgoing_requests,
6240        &["user_b"]
6241    );
6242    assert_eq!(
6243        client_b.summarize_contacts(cx_b).incoming_requests,
6244        &["user_a", "user_c"]
6245    );
6246    assert_eq!(
6247        client_b2.summarize_contacts(cx_b2).incoming_requests,
6248        &["user_a", "user_c"]
6249    );
6250    assert_eq!(
6251        client_c.summarize_contacts(cx_c).outgoing_requests,
6252        &["user_b"]
6253    );
6254    assert_eq!(
6255        client_c2.summarize_contacts(cx_c2).outgoing_requests,
6256        &["user_b"]
6257    );
6258
6259    // Contact requests are present upon connecting (tested here via disconnect/reconnect)
6260    disconnect_and_reconnect(&client_a, cx_a).await;
6261    disconnect_and_reconnect(&client_b, cx_b).await;
6262    disconnect_and_reconnect(&client_c, cx_c).await;
6263    executor.run_until_parked();
6264    assert_eq!(
6265        client_a.summarize_contacts(cx_a).outgoing_requests,
6266        &["user_b"]
6267    );
6268    assert_eq!(
6269        client_b.summarize_contacts(cx_b).incoming_requests,
6270        &["user_a", "user_c"]
6271    );
6272    assert_eq!(
6273        client_c.summarize_contacts(cx_c).outgoing_requests,
6274        &["user_b"]
6275    );
6276
6277    // User B accepts the request from user A.
6278    client_b
6279        .user_store()
6280        .update(cx_b, |store, cx| {
6281            store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
6282        })
6283        .await
6284        .unwrap();
6285
6286    executor.run_until_parked();
6287
6288    // User B sees user A as their contact now in all client, and the incoming request from them is removed.
6289    let contacts_b = client_b.summarize_contacts(cx_b);
6290    assert_eq!(contacts_b.current, &["user_a"]);
6291    assert_eq!(contacts_b.incoming_requests, &["user_c"]);
6292    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
6293    assert_eq!(contacts_b2.current, &["user_a"]);
6294    assert_eq!(contacts_b2.incoming_requests, &["user_c"]);
6295
6296    // User A sees user B as their contact now in all clients, and the outgoing request to them is removed.
6297    let contacts_a = client_a.summarize_contacts(cx_a);
6298    assert_eq!(contacts_a.current, &["user_b"]);
6299    assert!(contacts_a.outgoing_requests.is_empty());
6300    let contacts_a2 = client_a2.summarize_contacts(cx_a2);
6301    assert_eq!(contacts_a2.current, &["user_b"]);
6302    assert!(contacts_a2.outgoing_requests.is_empty());
6303
6304    // Contacts are present upon connecting (tested here via disconnect/reconnect)
6305    disconnect_and_reconnect(&client_a, cx_a).await;
6306    disconnect_and_reconnect(&client_b, cx_b).await;
6307    disconnect_and_reconnect(&client_c, cx_c).await;
6308    executor.run_until_parked();
6309    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
6310    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
6311    assert_eq!(
6312        client_b.summarize_contacts(cx_b).incoming_requests,
6313        &["user_c"]
6314    );
6315    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
6316    assert_eq!(
6317        client_c.summarize_contacts(cx_c).outgoing_requests,
6318        &["user_b"]
6319    );
6320
6321    // User B rejects the request from user C.
6322    client_b
6323        .user_store()
6324        .update(cx_b, |store, cx| {
6325            store.respond_to_contact_request(client_c.user_id().unwrap(), false, cx)
6326        })
6327        .await
6328        .unwrap();
6329
6330    executor.run_until_parked();
6331
6332    // User B doesn't see user C as their contact, and the incoming request from them is removed.
6333    let contacts_b = client_b.summarize_contacts(cx_b);
6334    assert_eq!(contacts_b.current, &["user_a"]);
6335    assert!(contacts_b.incoming_requests.is_empty());
6336    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
6337    assert_eq!(contacts_b2.current, &["user_a"]);
6338    assert!(contacts_b2.incoming_requests.is_empty());
6339
6340    // User C doesn't see user B as their contact, and the outgoing request to them is removed.
6341    let contacts_c = client_c.summarize_contacts(cx_c);
6342    assert!(contacts_c.current.is_empty());
6343    assert!(contacts_c.outgoing_requests.is_empty());
6344    let contacts_c2 = client_c2.summarize_contacts(cx_c2);
6345    assert!(contacts_c2.current.is_empty());
6346    assert!(contacts_c2.outgoing_requests.is_empty());
6347
6348    // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect)
6349    disconnect_and_reconnect(&client_a, cx_a).await;
6350    disconnect_and_reconnect(&client_b, cx_b).await;
6351    disconnect_and_reconnect(&client_c, cx_c).await;
6352    executor.run_until_parked();
6353    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
6354    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
6355    assert!(
6356        client_b
6357            .summarize_contacts(cx_b)
6358            .incoming_requests
6359            .is_empty()
6360    );
6361    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
6362    assert!(
6363        client_c
6364            .summarize_contacts(cx_c)
6365            .outgoing_requests
6366            .is_empty()
6367    );
6368
6369    async fn disconnect_and_reconnect(client: &TestClient, cx: &mut TestAppContext) {
6370        client.disconnect(&cx.to_async());
6371        client.clear_contacts(cx).await;
6372        client
6373            .connect(false, &cx.to_async())
6374            .await
6375            .into_response()
6376            .unwrap();
6377    }
6378}
6379
6380#[gpui::test(iterations = 10)]
6381async fn test_join_call_after_screen_was_shared(
6382    executor: BackgroundExecutor,
6383    cx_a: &mut TestAppContext,
6384    cx_b: &mut TestAppContext,
6385) {
6386    let mut server = TestServer::start(executor.clone()).await;
6387
6388    let client_a = server.create_client(cx_a, "user_a").await;
6389    let client_b = server.create_client(cx_b, "user_b").await;
6390    server
6391        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6392        .await;
6393
6394    let active_call_a = cx_a.read(ActiveCall::global);
6395    let active_call_b = cx_b.read(ActiveCall::global);
6396
6397    // Call users B and C from client A.
6398    active_call_a
6399        .update(cx_a, |call, cx| {
6400            call.invite(client_b.user_id().unwrap(), None, cx)
6401        })
6402        .await
6403        .unwrap();
6404
6405    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
6406    executor.run_until_parked();
6407    assert_eq!(
6408        room_participants(&room_a, cx_a),
6409        RoomParticipants {
6410            remote: Default::default(),
6411            pending: vec!["user_b".to_string()]
6412        }
6413    );
6414
6415    // User B receives the call.
6416
6417    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
6418    let call_b = incoming_call_b.next().await.unwrap().unwrap();
6419    assert_eq!(call_b.calling_user.github_login, "user_a");
6420
6421    // User A shares their screen
6422    let display = gpui::TestScreenCaptureSource::new();
6423    cx_a.set_screen_capture_sources(vec![display]);
6424    let screen_a = cx_a
6425        .update(|cx| cx.screen_capture_sources())
6426        .await
6427        .unwrap()
6428        .unwrap()
6429        .into_iter()
6430        .next()
6431        .unwrap();
6432
6433    active_call_a
6434        .update(cx_a, |call, cx| {
6435            call.room()
6436                .unwrap()
6437                .update(cx, |room, cx| room.share_screen(screen_a, cx))
6438        })
6439        .await
6440        .unwrap();
6441
6442    client_b.user_store().update(cx_b, |user_store, _| {
6443        user_store.clear_cache();
6444    });
6445
6446    // User B joins the room
6447    active_call_b
6448        .update(cx_b, |call, cx| call.accept_incoming(cx))
6449        .await
6450        .unwrap();
6451
6452    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
6453    assert!(incoming_call_b.next().await.unwrap().is_none());
6454
6455    executor.run_until_parked();
6456    assert_eq!(
6457        room_participants(&room_a, cx_a),
6458        RoomParticipants {
6459            remote: vec!["user_b".to_string()],
6460            pending: vec![],
6461        }
6462    );
6463    assert_eq!(
6464        room_participants(&room_b, cx_b),
6465        RoomParticipants {
6466            remote: vec!["user_a".to_string()],
6467            pending: vec![],
6468        }
6469    );
6470
6471    // Ensure User B sees User A's screenshare.
6472
6473    room_b.read_with(cx_b, |room, _| {
6474        assert_eq!(
6475            room.remote_participants()
6476                .get(&client_a.user_id().unwrap())
6477                .unwrap()
6478                .video_tracks
6479                .len(),
6480            1
6481        );
6482    });
6483}
6484
6485#[gpui::test]
6486async fn test_right_click_menu_behind_collab_panel(cx: &mut TestAppContext) {
6487    let mut server = TestServer::start(cx.executor().clone()).await;
6488    let client_a = server.create_client(cx, "user_a").await;
6489    let (_workspace_a, cx) = client_a.build_test_workspace(cx).await;
6490
6491    cx.simulate_resize(size(px(300.), px(300.)));
6492
6493    cx.simulate_keystrokes("cmd-n cmd-n cmd-n");
6494    cx.update(|window, _cx| window.refresh());
6495
6496    let tab_bounds = cx.debug_bounds("TAB-2").unwrap();
6497    let new_tab_button_bounds = cx.debug_bounds("ICON-Plus").unwrap();
6498
6499    assert!(
6500        tab_bounds.intersects(&new_tab_button_bounds),
6501        "Tab should overlap with the new tab button, if this is failing check if there's been a redesign!"
6502    );
6503
6504    cx.simulate_event(MouseDownEvent {
6505        button: MouseButton::Right,
6506        position: new_tab_button_bounds.center(),
6507        modifiers: Modifiers::default(),
6508        click_count: 1,
6509        first_mouse: false,
6510    });
6511
6512    // regression test that the right click menu for tabs does not open.
6513    assert!(cx.debug_bounds("MENU_ITEM-Close").is_none());
6514
6515    let tab_bounds = cx.debug_bounds("TAB-1").unwrap();
6516    cx.simulate_event(MouseDownEvent {
6517        button: MouseButton::Right,
6518        position: tab_bounds.center(),
6519        modifiers: Modifiers::default(),
6520        click_count: 1,
6521        first_mouse: false,
6522    });
6523    assert!(cx.debug_bounds("MENU_ITEM-Close").is_some());
6524}
6525
6526#[gpui::test]
6527async fn test_pane_split_left(cx: &mut TestAppContext) {
6528    let (_, client) = TestServer::start1(cx).await;
6529    let (workspace, cx) = client.build_test_workspace(cx).await;
6530
6531    cx.simulate_keystrokes("cmd-n");
6532    workspace.update(cx, |workspace, cx| {
6533        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 1);
6534    });
6535    cx.simulate_keystrokes("cmd-k left");
6536    workspace.update(cx, |workspace, cx| {
6537        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 2);
6538    });
6539    cx.simulate_keystrokes("cmd-k");
6540    // sleep for longer than the timeout in keyboard shortcut handling
6541    // to verify that it doesn't fire in this case.
6542    cx.executor().advance_clock(Duration::from_secs(2));
6543    cx.simulate_keystrokes("left");
6544    workspace.update(cx, |workspace, cx| {
6545        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 2);
6546    });
6547}
6548
6549#[gpui::test]
6550async fn test_join_after_restart(cx1: &mut TestAppContext, cx2: &mut TestAppContext) {
6551    let (mut server, client) = TestServer::start1(cx1).await;
6552    let channel1 = server.make_public_channel("channel1", &client, cx1).await;
6553    let channel2 = server.make_public_channel("channel2", &client, cx1).await;
6554
6555    join_channel(channel1, &client, cx1).await.unwrap();
6556    drop(client);
6557
6558    let client2 = server.create_client(cx2, "user_a").await;
6559    join_channel(channel2, &client2, cx2).await.unwrap();
6560}
6561
6562#[gpui::test]
6563async fn test_preview_tabs(cx: &mut TestAppContext) {
6564    let (_server, client) = TestServer::start1(cx).await;
6565    let (workspace, cx) = client.build_test_workspace(cx).await;
6566    let project = workspace.read_with(cx, |workspace, _| workspace.project().clone());
6567
6568    let worktree_id = project.update(cx, |project, cx| {
6569        project.worktrees(cx).next().unwrap().read(cx).id()
6570    });
6571
6572    let path_1 = ProjectPath {
6573        worktree_id,
6574        path: Path::new("1.txt").into(),
6575    };
6576    let path_2 = ProjectPath {
6577        worktree_id,
6578        path: Path::new("2.js").into(),
6579    };
6580    let path_3 = ProjectPath {
6581        worktree_id,
6582        path: Path::new("3.rs").into(),
6583    };
6584
6585    let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
6586
6587    let get_path = |pane: &Pane, idx: usize, cx: &App| {
6588        pane.item_for_index(idx).unwrap().project_path(cx).unwrap()
6589    };
6590
6591    // Opening item 3 as a "permanent" tab
6592    workspace
6593        .update_in(cx, |workspace, window, cx| {
6594            workspace.open_path(path_3.clone(), None, false, window, cx)
6595        })
6596        .await
6597        .unwrap();
6598
6599    pane.update(cx, |pane, cx| {
6600        assert_eq!(pane.items_len(), 1);
6601        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6602        assert_eq!(pane.preview_item_id(), None);
6603
6604        assert!(!pane.can_navigate_backward());
6605        assert!(!pane.can_navigate_forward());
6606    });
6607
6608    // Open item 1 as preview
6609    workspace
6610        .update_in(cx, |workspace, window, cx| {
6611            workspace.open_path_preview(path_1.clone(), None, true, true, true, window, cx)
6612        })
6613        .await
6614        .unwrap();
6615
6616    pane.update(cx, |pane, cx| {
6617        assert_eq!(pane.items_len(), 2);
6618        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6619        assert_eq!(get_path(pane, 1, cx), path_1.clone());
6620        assert_eq!(
6621            pane.preview_item_id(),
6622            Some(pane.items().nth(1).unwrap().item_id())
6623        );
6624
6625        assert!(pane.can_navigate_backward());
6626        assert!(!pane.can_navigate_forward());
6627    });
6628
6629    // Open item 2 as preview
6630    workspace
6631        .update_in(cx, |workspace, window, cx| {
6632            workspace.open_path_preview(path_2.clone(), None, true, true, true, window, cx)
6633        })
6634        .await
6635        .unwrap();
6636
6637    pane.update(cx, |pane, cx| {
6638        assert_eq!(pane.items_len(), 2);
6639        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6640        assert_eq!(get_path(pane, 1, cx), path_2.clone());
6641        assert_eq!(
6642            pane.preview_item_id(),
6643            Some(pane.items().nth(1).unwrap().item_id())
6644        );
6645
6646        assert!(pane.can_navigate_backward());
6647        assert!(!pane.can_navigate_forward());
6648    });
6649
6650    // Going back should show item 1 as preview
6651    workspace
6652        .update_in(cx, |workspace, window, cx| {
6653            workspace.go_back(pane.downgrade(), window, cx)
6654        })
6655        .await
6656        .unwrap();
6657
6658    pane.update(cx, |pane, cx| {
6659        assert_eq!(pane.items_len(), 2);
6660        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6661        assert_eq!(get_path(pane, 1, cx), path_1.clone());
6662        assert_eq!(
6663            pane.preview_item_id(),
6664            Some(pane.items().nth(1).unwrap().item_id())
6665        );
6666
6667        assert!(pane.can_navigate_backward());
6668        assert!(pane.can_navigate_forward());
6669    });
6670
6671    // Closing item 1
6672    pane.update_in(cx, |pane, window, cx| {
6673        pane.close_item_by_id(
6674            pane.active_item().unwrap().item_id(),
6675            workspace::SaveIntent::Skip,
6676            window,
6677            cx,
6678        )
6679    })
6680    .await
6681    .unwrap();
6682
6683    pane.update(cx, |pane, cx| {
6684        assert_eq!(pane.items_len(), 1);
6685        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6686        assert_eq!(pane.preview_item_id(), None);
6687
6688        assert!(pane.can_navigate_backward());
6689        assert!(!pane.can_navigate_forward());
6690    });
6691
6692    // Going back should show item 1 as preview
6693    workspace
6694        .update_in(cx, |workspace, window, cx| {
6695            workspace.go_back(pane.downgrade(), window, cx)
6696        })
6697        .await
6698        .unwrap();
6699
6700    pane.update(cx, |pane, cx| {
6701        assert_eq!(pane.items_len(), 2);
6702        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6703        assert_eq!(get_path(pane, 1, cx), path_1.clone());
6704        assert_eq!(
6705            pane.preview_item_id(),
6706            Some(pane.items().nth(1).unwrap().item_id())
6707        );
6708
6709        assert!(pane.can_navigate_backward());
6710        assert!(pane.can_navigate_forward());
6711    });
6712
6713    // Close permanent tab
6714    pane.update_in(cx, |pane, window, cx| {
6715        let id = pane.items().next().unwrap().item_id();
6716        pane.close_item_by_id(id, workspace::SaveIntent::Skip, window, cx)
6717    })
6718    .await
6719    .unwrap();
6720
6721    pane.update(cx, |pane, cx| {
6722        assert_eq!(pane.items_len(), 1);
6723        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6724        assert_eq!(
6725            pane.preview_item_id(),
6726            Some(pane.items().next().unwrap().item_id())
6727        );
6728
6729        assert!(pane.can_navigate_backward());
6730        assert!(pane.can_navigate_forward());
6731    });
6732
6733    // Split pane to the right
6734    pane.update(cx, |pane, cx| {
6735        pane.split(workspace::SplitDirection::Right, cx);
6736    });
6737
6738    let right_pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
6739
6740    pane.update(cx, |pane, cx| {
6741        assert_eq!(pane.items_len(), 1);
6742        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6743        assert_eq!(
6744            pane.preview_item_id(),
6745            Some(pane.items().next().unwrap().item_id())
6746        );
6747
6748        assert!(pane.can_navigate_backward());
6749        assert!(pane.can_navigate_forward());
6750    });
6751
6752    right_pane.update(cx, |pane, cx| {
6753        assert_eq!(pane.items_len(), 1);
6754        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6755        assert_eq!(pane.preview_item_id(), None);
6756
6757        assert!(!pane.can_navigate_backward());
6758        assert!(!pane.can_navigate_forward());
6759    });
6760
6761    // Open item 2 as preview in right pane
6762    workspace
6763        .update_in(cx, |workspace, window, cx| {
6764            workspace.open_path_preview(path_2.clone(), None, true, true, true, window, cx)
6765        })
6766        .await
6767        .unwrap();
6768
6769    pane.update(cx, |pane, cx| {
6770        assert_eq!(pane.items_len(), 1);
6771        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6772        assert_eq!(
6773            pane.preview_item_id(),
6774            Some(pane.items().next().unwrap().item_id())
6775        );
6776
6777        assert!(pane.can_navigate_backward());
6778        assert!(pane.can_navigate_forward());
6779    });
6780
6781    right_pane.update(cx, |pane, cx| {
6782        assert_eq!(pane.items_len(), 2);
6783        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6784        assert_eq!(get_path(pane, 1, cx), path_2.clone());
6785        assert_eq!(
6786            pane.preview_item_id(),
6787            Some(pane.items().nth(1).unwrap().item_id())
6788        );
6789
6790        assert!(pane.can_navigate_backward());
6791        assert!(!pane.can_navigate_forward());
6792    });
6793
6794    // Focus left pane
6795    workspace.update_in(cx, |workspace, window, cx| {
6796        workspace.activate_pane_in_direction(workspace::SplitDirection::Left, window, cx)
6797    });
6798
6799    // Open item 2 as preview in left pane
6800    workspace
6801        .update_in(cx, |workspace, window, cx| {
6802            workspace.open_path_preview(path_2.clone(), None, true, true, true, window, cx)
6803        })
6804        .await
6805        .unwrap();
6806
6807    pane.update(cx, |pane, cx| {
6808        assert_eq!(pane.items_len(), 1);
6809        assert_eq!(get_path(pane, 0, cx), path_2.clone());
6810        assert_eq!(
6811            pane.preview_item_id(),
6812            Some(pane.items().next().unwrap().item_id())
6813        );
6814
6815        assert!(pane.can_navigate_backward());
6816        assert!(!pane.can_navigate_forward());
6817    });
6818
6819    right_pane.update(cx, |pane, cx| {
6820        assert_eq!(pane.items_len(), 2);
6821        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6822        assert_eq!(get_path(pane, 1, cx), path_2.clone());
6823        assert_eq!(
6824            pane.preview_item_id(),
6825            Some(pane.items().nth(1).unwrap().item_id())
6826        );
6827
6828        assert!(pane.can_navigate_backward());
6829        assert!(!pane.can_navigate_forward());
6830    });
6831}
6832
6833#[gpui::test(iterations = 10)]
6834async fn test_context_collaboration_with_reconnect(
6835    executor: BackgroundExecutor,
6836    cx_a: &mut TestAppContext,
6837    cx_b: &mut TestAppContext,
6838) {
6839    let mut server = TestServer::start(executor.clone()).await;
6840    let client_a = server.create_client(cx_a, "user_a").await;
6841    let client_b = server.create_client(cx_b, "user_b").await;
6842    server
6843        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6844        .await;
6845    let active_call_a = cx_a.read(ActiveCall::global);
6846
6847    client_a.fs().insert_tree("/a", Default::default()).await;
6848    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
6849    let project_id = active_call_a
6850        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6851        .await
6852        .unwrap();
6853    let project_b = client_b.join_remote_project(project_id, cx_b).await;
6854
6855    // Client A sees that a guest has joined.
6856    executor.run_until_parked();
6857
6858    project_a.read_with(cx_a, |project, _| {
6859        assert_eq!(project.collaborators().len(), 1);
6860    });
6861    project_b.read_with(cx_b, |project, _| {
6862        assert_eq!(project.collaborators().len(), 1);
6863    });
6864
6865    let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
6866    let context_store_a = cx_a
6867        .update(|cx| {
6868            ContextStore::new(
6869                project_a.clone(),
6870                prompt_builder.clone(),
6871                Arc::new(SlashCommandWorkingSet::default()),
6872                cx,
6873            )
6874        })
6875        .await
6876        .unwrap();
6877    let context_store_b = cx_b
6878        .update(|cx| {
6879            ContextStore::new(
6880                project_b.clone(),
6881                prompt_builder.clone(),
6882                Arc::new(SlashCommandWorkingSet::default()),
6883                cx,
6884            )
6885        })
6886        .await
6887        .unwrap();
6888
6889    // Client A creates a new chats.
6890    let context_a = context_store_a.update(cx_a, |store, cx| store.create(cx));
6891    executor.run_until_parked();
6892
6893    // Client B retrieves host's contexts and joins one.
6894    let context_b = context_store_b
6895        .update(cx_b, |store, cx| {
6896            let host_contexts = store.host_contexts().to_vec();
6897            assert_eq!(host_contexts.len(), 1);
6898            store.open_remote_context(host_contexts[0].id.clone(), cx)
6899        })
6900        .await
6901        .unwrap();
6902
6903    // Host and guest make changes
6904    context_a.update(cx_a, |context, cx| {
6905        context.buffer().update(cx, |buffer, cx| {
6906            buffer.edit([(0..0, "Host change\n")], None, cx)
6907        })
6908    });
6909    context_b.update(cx_b, |context, cx| {
6910        context.buffer().update(cx, |buffer, cx| {
6911            buffer.edit([(0..0, "Guest change\n")], None, cx)
6912        })
6913    });
6914    executor.run_until_parked();
6915    assert_eq!(
6916        context_a.read_with(cx_a, |context, cx| context.buffer().read(cx).text()),
6917        "Guest change\nHost change\n"
6918    );
6919    assert_eq!(
6920        context_b.read_with(cx_b, |context, cx| context.buffer().read(cx).text()),
6921        "Guest change\nHost change\n"
6922    );
6923
6924    // Disconnect client A and make some changes while disconnected.
6925    server.disconnect_client(client_a.peer_id().unwrap());
6926    server.forbid_connections();
6927    context_a.update(cx_a, |context, cx| {
6928        context.buffer().update(cx, |buffer, cx| {
6929            buffer.edit([(0..0, "Host offline change\n")], None, cx)
6930        })
6931    });
6932    context_b.update(cx_b, |context, cx| {
6933        context.buffer().update(cx, |buffer, cx| {
6934            buffer.edit([(0..0, "Guest offline change\n")], None, cx)
6935        })
6936    });
6937    executor.run_until_parked();
6938    assert_eq!(
6939        context_a.read_with(cx_a, |context, cx| context.buffer().read(cx).text()),
6940        "Host offline change\nGuest change\nHost change\n"
6941    );
6942    assert_eq!(
6943        context_b.read_with(cx_b, |context, cx| context.buffer().read(cx).text()),
6944        "Guest offline change\nGuest change\nHost change\n"
6945    );
6946
6947    // Allow client A to reconnect and verify that contexts converge.
6948    server.allow_connections();
6949    executor.advance_clock(RECEIVE_TIMEOUT);
6950    assert_eq!(
6951        context_a.read_with(cx_a, |context, cx| context.buffer().read(cx).text()),
6952        "Guest offline change\nHost offline change\nGuest change\nHost change\n"
6953    );
6954    assert_eq!(
6955        context_b.read_with(cx_b, |context, cx| context.buffer().read(cx).text()),
6956        "Guest offline change\nHost offline change\nGuest change\nHost change\n"
6957    );
6958
6959    // Client A disconnects without being able to reconnect. Context B becomes readonly.
6960    server.forbid_connections();
6961    server.disconnect_client(client_a.peer_id().unwrap());
6962    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
6963    context_b.read_with(cx_b, |context, cx| {
6964        assert!(context.buffer().read(cx).read_only());
6965    });
6966}
6967
6968#[gpui::test]
6969async fn test_remote_git_branches(
6970    executor: BackgroundExecutor,
6971    cx_a: &mut TestAppContext,
6972    cx_b: &mut TestAppContext,
6973) {
6974    let mut server = TestServer::start(executor.clone()).await;
6975    let client_a = server.create_client(cx_a, "user_a").await;
6976    let client_b = server.create_client(cx_b, "user_b").await;
6977    server
6978        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6979        .await;
6980    let active_call_a = cx_a.read(ActiveCall::global);
6981
6982    client_a
6983        .fs()
6984        .insert_tree("/project", serde_json::json!({ ".git":{} }))
6985        .await;
6986    let branches = ["main", "dev", "feature-1"];
6987    client_a
6988        .fs()
6989        .insert_branches(Path::new("/project/.git"), &branches);
6990    let branches_set = branches
6991        .into_iter()
6992        .map(ToString::to_string)
6993        .collect::<HashSet<_>>();
6994
6995    let (project_a, _) = client_a.build_local_project("/project", cx_a).await;
6996
6997    let project_id = active_call_a
6998        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6999        .await
7000        .unwrap();
7001    let project_b = client_b.join_remote_project(project_id, cx_b).await;
7002
7003    // Client A sees that a guest has joined and the repo has been populated
7004    executor.run_until_parked();
7005
7006    let repo_b = cx_b.update(|cx| project_b.read(cx).active_repository(cx).unwrap());
7007
7008    let branches_b = cx_b
7009        .update(|cx| repo_b.update(cx, |repository, _| repository.branches()))
7010        .await
7011        .unwrap()
7012        .unwrap();
7013
7014    let new_branch = branches[2];
7015
7016    let branches_b = branches_b
7017        .into_iter()
7018        .map(|branch| branch.name().to_string())
7019        .collect::<HashSet<_>>();
7020
7021    assert_eq!(branches_b, branches_set);
7022
7023    cx_b.update(|cx| {
7024        repo_b.update(cx, |repository, _cx| {
7025            repository.change_branch(new_branch.to_string())
7026        })
7027    })
7028    .await
7029    .unwrap()
7030    .unwrap();
7031
7032    executor.run_until_parked();
7033
7034    let host_branch = cx_a.update(|cx| {
7035        project_a.update(cx, |project, cx| {
7036            project
7037                .repositories(cx)
7038                .values()
7039                .next()
7040                .unwrap()
7041                .read(cx)
7042                .branch
7043                .as_ref()
7044                .unwrap()
7045                .clone()
7046        })
7047    });
7048
7049    assert_eq!(host_branch.name(), branches[2]);
7050
7051    // Also try creating a new branch
7052    cx_b.update(|cx| {
7053        repo_b.update(cx, |repository, _cx| {
7054            repository.create_branch("totally-new-branch".to_string())
7055        })
7056    })
7057    .await
7058    .unwrap()
7059    .unwrap();
7060
7061    cx_b.update(|cx| {
7062        repo_b.update(cx, |repository, _cx| {
7063            repository.change_branch("totally-new-branch".to_string())
7064        })
7065    })
7066    .await
7067    .unwrap()
7068    .unwrap();
7069
7070    executor.run_until_parked();
7071
7072    let host_branch = cx_a.update(|cx| {
7073        project_a.update(cx, |project, cx| {
7074            project
7075                .repositories(cx)
7076                .values()
7077                .next()
7078                .unwrap()
7079                .read(cx)
7080                .branch
7081                .as_ref()
7082                .unwrap()
7083                .clone()
7084        })
7085    });
7086
7087    assert_eq!(host_branch.name(), "totally-new-branch");
7088}