integration_tests.rs

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