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(false, 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::Uri::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::Uri::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::Uri::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::Uri::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::Uri::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::Uri::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        .unwrap();
4855    cx_b.read(|cx| {
4856        assert_eq!(
4857            definitions_1.len(),
4858            1,
4859            "Unexpected definitions: {definitions_1:?}"
4860        );
4861        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4862        let target_buffer = definitions_1[0].target.buffer.read(cx);
4863        assert_eq!(
4864            target_buffer.text(),
4865            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4866        );
4867        assert_eq!(
4868            definitions_1[0].target.range.to_point(target_buffer),
4869            Point::new(0, 6)..Point::new(0, 9)
4870        );
4871    });
4872
4873    // Try getting more definitions for the same buffer, ensuring the buffer gets reused from
4874    // the previous call to `definition`.
4875    fake_language_server.set_request_handler::<lsp::request::GotoDefinition, _, _>(
4876        |_, _| async move {
4877            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4878                lsp::Location::new(
4879                    lsp::Uri::from_file_path(path!("/root/dir-2/b.rs")).unwrap(),
4880                    lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)),
4881                ),
4882            )))
4883        },
4884    );
4885
4886    let definitions_2 = project_b
4887        .update(cx_b, |p, cx| p.definitions(&buffer_b, 33, cx))
4888        .await
4889        .unwrap()
4890        .unwrap();
4891    cx_b.read(|cx| {
4892        assert_eq!(definitions_2.len(), 1);
4893        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4894        let target_buffer = definitions_2[0].target.buffer.read(cx);
4895        assert_eq!(
4896            target_buffer.text(),
4897            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4898        );
4899        assert_eq!(
4900            definitions_2[0].target.range.to_point(target_buffer),
4901            Point::new(1, 6)..Point::new(1, 11)
4902        );
4903    });
4904    assert_eq!(
4905        definitions_1[0].target.buffer,
4906        definitions_2[0].target.buffer
4907    );
4908
4909    fake_language_server.set_request_handler::<lsp::request::GotoTypeDefinition, _, _>(
4910        |req, _| async move {
4911            assert_eq!(
4912                req.text_document_position_params.position,
4913                lsp::Position::new(0, 7)
4914            );
4915            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4916                lsp::Location::new(
4917                    lsp::Uri::from_file_path(path!("/root/dir-2/c.rs")).unwrap(),
4918                    lsp::Range::new(lsp::Position::new(0, 5), lsp::Position::new(0, 7)),
4919                ),
4920            )))
4921        },
4922    );
4923
4924    let type_definitions = project_b
4925        .update(cx_b, |p, cx| p.type_definitions(&buffer_b, 7, cx))
4926        .await
4927        .unwrap()
4928        .unwrap();
4929    cx_b.read(|cx| {
4930        assert_eq!(
4931            type_definitions.len(),
4932            1,
4933            "Unexpected type definitions: {type_definitions:?}"
4934        );
4935        let target_buffer = type_definitions[0].target.buffer.read(cx);
4936        assert_eq!(target_buffer.text(), "type T2 = usize;");
4937        assert_eq!(
4938            type_definitions[0].target.range.to_point(target_buffer),
4939            Point::new(0, 5)..Point::new(0, 7)
4940        );
4941    });
4942}
4943
4944#[gpui::test(iterations = 10)]
4945async fn test_references(
4946    executor: BackgroundExecutor,
4947    cx_a: &mut TestAppContext,
4948    cx_b: &mut TestAppContext,
4949) {
4950    let mut server = TestServer::start(executor.clone()).await;
4951    let client_a = server.create_client(cx_a, "user_a").await;
4952    let client_b = server.create_client(cx_b, "user_b").await;
4953    server
4954        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4955        .await;
4956    let active_call_a = cx_a.read(ActiveCall::global);
4957
4958    let capabilities = lsp::ServerCapabilities {
4959        references_provider: Some(lsp::OneOf::Left(true)),
4960        ..lsp::ServerCapabilities::default()
4961    };
4962    client_a.language_registry().add(rust_lang());
4963    let mut fake_language_servers = client_a.language_registry().register_fake_lsp(
4964        "Rust",
4965        FakeLspAdapter {
4966            name: "my-fake-lsp-adapter",
4967            capabilities: capabilities.clone(),
4968            ..FakeLspAdapter::default()
4969        },
4970    );
4971    client_b.language_registry().add(rust_lang());
4972    client_b.language_registry().register_fake_lsp_adapter(
4973        "Rust",
4974        FakeLspAdapter {
4975            name: "my-fake-lsp-adapter",
4976            capabilities,
4977            ..FakeLspAdapter::default()
4978        },
4979    );
4980
4981    client_a
4982        .fs()
4983        .insert_tree(
4984            path!("/root"),
4985            json!({
4986                "dir-1": {
4987                    "one.rs": "const ONE: usize = 1;",
4988                    "two.rs": "const TWO: usize = one::ONE + one::ONE;",
4989                },
4990                "dir-2": {
4991                    "three.rs": "const THREE: usize = two::TWO + one::ONE;",
4992                }
4993            }),
4994        )
4995        .await;
4996    let (project_a, worktree_id) = client_a
4997        .build_local_project(path!("/root/dir-1"), cx_a)
4998        .await;
4999    let project_id = active_call_a
5000        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5001        .await
5002        .unwrap();
5003    let project_b = client_b.join_remote_project(project_id, cx_b).await;
5004
5005    // Open the file on client B.
5006    let (buffer_b, _handle) = project_b
5007        .update(cx_b, |p, cx| {
5008            p.open_buffer_with_lsp((worktree_id, "one.rs"), cx)
5009        })
5010        .await
5011        .unwrap();
5012
5013    // Request references to a symbol as the guest.
5014    let fake_language_server = fake_language_servers.next().await.unwrap();
5015    let (lsp_response_tx, rx) = mpsc::unbounded::<Result<Option<Vec<lsp::Location>>>>();
5016    fake_language_server.set_request_handler::<lsp::request::References, _, _>({
5017        let rx = Arc::new(Mutex::new(Some(rx)));
5018        move |params, _| {
5019            assert_eq!(
5020                params.text_document_position.text_document.uri.as_str(),
5021                uri!("file:///root/dir-1/one.rs")
5022            );
5023            let rx = rx.clone();
5024            async move {
5025                let mut response_rx = rx.lock().take().unwrap();
5026                let result = response_rx.next().await.unwrap();
5027                *rx.lock() = Some(response_rx);
5028                result
5029            }
5030        }
5031    });
5032    cx_a.run_until_parked();
5033    cx_b.run_until_parked();
5034
5035    let references = project_b.update(cx_b, |p, cx| p.references(&buffer_b, 7, cx));
5036
5037    // User is informed that a request is pending.
5038    executor.run_until_parked();
5039    project_b.read_with(cx_b, |project, cx| {
5040        let status = project.language_server_statuses(cx).next().unwrap().1;
5041        assert_eq!(status.name.0, "my-fake-lsp-adapter");
5042        assert_eq!(
5043            status.pending_work.values().next().unwrap().message,
5044            Some("Finding references...".into())
5045        );
5046    });
5047
5048    // Cause the language server to respond.
5049    lsp_response_tx
5050        .unbounded_send(Ok(Some(vec![
5051            lsp::Location {
5052                uri: lsp::Uri::from_file_path(path!("/root/dir-1/two.rs")).unwrap(),
5053                range: lsp::Range::new(lsp::Position::new(0, 24), lsp::Position::new(0, 27)),
5054            },
5055            lsp::Location {
5056                uri: lsp::Uri::from_file_path(path!("/root/dir-1/two.rs")).unwrap(),
5057                range: lsp::Range::new(lsp::Position::new(0, 35), lsp::Position::new(0, 38)),
5058            },
5059            lsp::Location {
5060                uri: lsp::Uri::from_file_path(path!("/root/dir-2/three.rs")).unwrap(),
5061                range: lsp::Range::new(lsp::Position::new(0, 37), lsp::Position::new(0, 40)),
5062            },
5063        ])))
5064        .unwrap();
5065
5066    let references = references.await.unwrap().unwrap();
5067    executor.run_until_parked();
5068    project_b.read_with(cx_b, |project, cx| {
5069        // User is informed that a request is no longer pending.
5070        let status = project.language_server_statuses(cx).next().unwrap().1;
5071        assert!(status.pending_work.is_empty());
5072
5073        assert_eq!(references.len(), 3);
5074        assert_eq!(project.worktrees(cx).count(), 2);
5075
5076        let two_buffer = references[0].buffer.read(cx);
5077        let three_buffer = references[2].buffer.read(cx);
5078        assert_eq!(
5079            two_buffer.file().unwrap().path().as_ref(),
5080            Path::new("two.rs")
5081        );
5082        assert_eq!(references[1].buffer, references[0].buffer);
5083        assert_eq!(
5084            three_buffer.file().unwrap().full_path(cx),
5085            Path::new(path!("/root/dir-2/three.rs"))
5086        );
5087
5088        assert_eq!(references[0].range.to_offset(two_buffer), 24..27);
5089        assert_eq!(references[1].range.to_offset(two_buffer), 35..38);
5090        assert_eq!(references[2].range.to_offset(three_buffer), 37..40);
5091    });
5092
5093    let references = project_b.update(cx_b, |p, cx| p.references(&buffer_b, 7, cx));
5094
5095    // User is informed that a request is pending.
5096    executor.run_until_parked();
5097    project_b.read_with(cx_b, |project, cx| {
5098        let status = project.language_server_statuses(cx).next().unwrap().1;
5099        assert_eq!(status.name.0, "my-fake-lsp-adapter");
5100        assert_eq!(
5101            status.pending_work.values().next().unwrap().message,
5102            Some("Finding references...".into())
5103        );
5104    });
5105
5106    // Cause the LSP request to fail.
5107    lsp_response_tx
5108        .unbounded_send(Err(anyhow!("can't find references")))
5109        .unwrap();
5110    assert_eq!(references.await.unwrap().unwrap(), []);
5111
5112    // User is informed that the request is no longer pending.
5113    executor.run_until_parked();
5114    project_b.read_with(cx_b, |project, cx| {
5115        let status = project.language_server_statuses(cx).next().unwrap().1;
5116        assert!(status.pending_work.is_empty());
5117    });
5118}
5119
5120#[gpui::test(iterations = 10)]
5121async fn test_project_search(
5122    executor: BackgroundExecutor,
5123    cx_a: &mut TestAppContext,
5124    cx_b: &mut TestAppContext,
5125) {
5126    let mut server = TestServer::start(executor.clone()).await;
5127    let client_a = server.create_client(cx_a, "user_a").await;
5128    let client_b = server.create_client(cx_b, "user_b").await;
5129    server
5130        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5131        .await;
5132    let active_call_a = cx_a.read(ActiveCall::global);
5133
5134    client_a
5135        .fs()
5136        .insert_tree(
5137            "/root",
5138            json!({
5139                "dir-1": {
5140                    "a": "hello world",
5141                    "b": "goodnight moon",
5142                    "c": "a world of goo",
5143                    "d": "world champion of clown world",
5144                },
5145                "dir-2": {
5146                    "e": "disney world is fun",
5147                }
5148            }),
5149        )
5150        .await;
5151    let (project_a, _) = client_a.build_local_project("/root/dir-1", cx_a).await;
5152    let (worktree_2, _) = project_a
5153        .update(cx_a, |p, cx| {
5154            p.find_or_create_worktree("/root/dir-2", true, cx)
5155        })
5156        .await
5157        .unwrap();
5158    worktree_2
5159        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
5160        .await;
5161    let project_id = active_call_a
5162        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5163        .await
5164        .unwrap();
5165
5166    let project_b = client_b.join_remote_project(project_id, cx_b).await;
5167
5168    // Perform a search as the guest.
5169    let mut results = HashMap::default();
5170    let search_rx = project_b.update(cx_b, |project, cx| {
5171        project.search(
5172            SearchQuery::text(
5173                "world",
5174                false,
5175                false,
5176                false,
5177                Default::default(),
5178                Default::default(),
5179                false,
5180                None,
5181            )
5182            .unwrap(),
5183            cx,
5184        )
5185    });
5186    while let Ok(result) = search_rx.recv().await {
5187        match result {
5188            SearchResult::Buffer { buffer, ranges } => {
5189                results.entry(buffer).or_insert(ranges);
5190            }
5191            SearchResult::LimitReached => {
5192                panic!(
5193                    "Unexpectedly reached search limit in tests. If you do want to assert limit-reached, change this panic call."
5194                )
5195            }
5196        };
5197    }
5198
5199    let mut ranges_by_path = results
5200        .into_iter()
5201        .map(|(buffer, ranges)| {
5202            buffer.read_with(cx_b, |buffer, cx| {
5203                let path = buffer.file().unwrap().full_path(cx);
5204                let offset_ranges = ranges
5205                    .into_iter()
5206                    .map(|range| range.to_offset(buffer))
5207                    .collect::<Vec<_>>();
5208                (path, offset_ranges)
5209            })
5210        })
5211        .collect::<Vec<_>>();
5212    ranges_by_path.sort_by_key(|(path, _)| path.clone());
5213
5214    assert_eq!(
5215        ranges_by_path,
5216        &[
5217            (PathBuf::from("dir-1/a"), vec![6..11]),
5218            (PathBuf::from("dir-1/c"), vec![2..7]),
5219            (PathBuf::from("dir-1/d"), vec![0..5, 24..29]),
5220            (PathBuf::from("dir-2/e"), vec![7..12]),
5221        ]
5222    );
5223}
5224
5225#[gpui::test(iterations = 10)]
5226async fn test_document_highlights(
5227    executor: BackgroundExecutor,
5228    cx_a: &mut TestAppContext,
5229    cx_b: &mut TestAppContext,
5230) {
5231    let mut server = TestServer::start(executor.clone()).await;
5232    let client_a = server.create_client(cx_a, "user_a").await;
5233    let client_b = server.create_client(cx_b, "user_b").await;
5234    server
5235        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5236        .await;
5237    let active_call_a = cx_a.read(ActiveCall::global);
5238
5239    client_a
5240        .fs()
5241        .insert_tree(
5242            path!("/root-1"),
5243            json!({
5244                "main.rs": "fn double(number: i32) -> i32 { number + number }",
5245            }),
5246        )
5247        .await;
5248
5249    client_a.language_registry().add(rust_lang());
5250    let capabilities = lsp::ServerCapabilities {
5251        document_highlight_provider: Some(lsp::OneOf::Left(true)),
5252        ..lsp::ServerCapabilities::default()
5253    };
5254    let mut fake_language_servers = client_a.language_registry().register_fake_lsp(
5255        "Rust",
5256        FakeLspAdapter {
5257            capabilities: capabilities.clone(),
5258            ..FakeLspAdapter::default()
5259        },
5260    );
5261    client_b.language_registry().add(rust_lang());
5262    client_b.language_registry().register_fake_lsp_adapter(
5263        "Rust",
5264        FakeLspAdapter {
5265            capabilities,
5266            ..FakeLspAdapter::default()
5267        },
5268    );
5269
5270    let (project_a, worktree_id) = client_a.build_local_project(path!("/root-1"), cx_a).await;
5271    let project_id = active_call_a
5272        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5273        .await
5274        .unwrap();
5275    let project_b = client_b.join_remote_project(project_id, cx_b).await;
5276
5277    // Open the file on client B.
5278    let (buffer_b, _handle) = project_b
5279        .update(cx_b, |p, cx| {
5280            p.open_buffer_with_lsp((worktree_id, "main.rs"), cx)
5281        })
5282        .await
5283        .unwrap();
5284
5285    // Request document highlights as the guest.
5286    let fake_language_server = fake_language_servers.next().await.unwrap();
5287    fake_language_server.set_request_handler::<lsp::request::DocumentHighlightRequest, _, _>(
5288        |params, _| async move {
5289            assert_eq!(
5290                params
5291                    .text_document_position_params
5292                    .text_document
5293                    .uri
5294                    .as_str(),
5295                uri!("file:///root-1/main.rs")
5296            );
5297            assert_eq!(
5298                params.text_document_position_params.position,
5299                lsp::Position::new(0, 34)
5300            );
5301            Ok(Some(vec![
5302                lsp::DocumentHighlight {
5303                    kind: Some(lsp::DocumentHighlightKind::WRITE),
5304                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 16)),
5305                },
5306                lsp::DocumentHighlight {
5307                    kind: Some(lsp::DocumentHighlightKind::READ),
5308                    range: lsp::Range::new(lsp::Position::new(0, 32), lsp::Position::new(0, 38)),
5309                },
5310                lsp::DocumentHighlight {
5311                    kind: Some(lsp::DocumentHighlightKind::READ),
5312                    range: lsp::Range::new(lsp::Position::new(0, 41), lsp::Position::new(0, 47)),
5313                },
5314            ]))
5315        },
5316    );
5317    cx_a.run_until_parked();
5318    cx_b.run_until_parked();
5319
5320    let highlights = project_b
5321        .update(cx_b, |p, cx| p.document_highlights(&buffer_b, 34, cx))
5322        .await
5323        .unwrap();
5324
5325    buffer_b.read_with(cx_b, |buffer, _| {
5326        let snapshot = buffer.snapshot();
5327
5328        let highlights = highlights
5329            .into_iter()
5330            .map(|highlight| (highlight.kind, highlight.range.to_offset(&snapshot)))
5331            .collect::<Vec<_>>();
5332        assert_eq!(
5333            highlights,
5334            &[
5335                (lsp::DocumentHighlightKind::WRITE, 10..16),
5336                (lsp::DocumentHighlightKind::READ, 32..38),
5337                (lsp::DocumentHighlightKind::READ, 41..47)
5338            ]
5339        )
5340    });
5341}
5342
5343#[gpui::test(iterations = 10)]
5344async fn test_lsp_hover(
5345    executor: BackgroundExecutor,
5346    cx_a: &mut TestAppContext,
5347    cx_b: &mut TestAppContext,
5348) {
5349    let mut server = TestServer::start(executor.clone()).await;
5350    let client_a = server.create_client(cx_a, "user_a").await;
5351    let client_b = server.create_client(cx_b, "user_b").await;
5352    server
5353        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5354        .await;
5355    let active_call_a = cx_a.read(ActiveCall::global);
5356
5357    client_a
5358        .fs()
5359        .insert_tree(
5360            path!("/root-1"),
5361            json!({
5362                "main.rs": "use std::collections::HashMap;",
5363            }),
5364        )
5365        .await;
5366
5367    client_a.language_registry().add(rust_lang());
5368    let language_server_names = ["rust-analyzer", "CrabLang-ls"];
5369    let capabilities_1 = lsp::ServerCapabilities {
5370        hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
5371        ..lsp::ServerCapabilities::default()
5372    };
5373    let capabilities_2 = lsp::ServerCapabilities {
5374        hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
5375        ..lsp::ServerCapabilities::default()
5376    };
5377    let mut language_servers = [
5378        client_a.language_registry().register_fake_lsp(
5379            "Rust",
5380            FakeLspAdapter {
5381                name: language_server_names[0],
5382                capabilities: capabilities_1.clone(),
5383                ..FakeLspAdapter::default()
5384            },
5385        ),
5386        client_a.language_registry().register_fake_lsp(
5387            "Rust",
5388            FakeLspAdapter {
5389                name: language_server_names[1],
5390                capabilities: capabilities_2.clone(),
5391                ..FakeLspAdapter::default()
5392            },
5393        ),
5394    ];
5395    client_b.language_registry().add(rust_lang());
5396    client_b.language_registry().register_fake_lsp_adapter(
5397        "Rust",
5398        FakeLspAdapter {
5399            name: language_server_names[0],
5400            capabilities: capabilities_1,
5401            ..FakeLspAdapter::default()
5402        },
5403    );
5404    client_b.language_registry().register_fake_lsp_adapter(
5405        "Rust",
5406        FakeLspAdapter {
5407            name: language_server_names[1],
5408            capabilities: capabilities_2,
5409            ..FakeLspAdapter::default()
5410        },
5411    );
5412
5413    let (project_a, worktree_id) = client_a.build_local_project(path!("/root-1"), cx_a).await;
5414    let project_id = active_call_a
5415        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5416        .await
5417        .unwrap();
5418    let project_b = client_b.join_remote_project(project_id, cx_b).await;
5419
5420    // Open the file as the guest
5421    let (buffer_b, _handle) = project_b
5422        .update(cx_b, |p, cx| {
5423            p.open_buffer_with_lsp((worktree_id, "main.rs"), cx)
5424        })
5425        .await
5426        .unwrap();
5427
5428    let mut servers_with_hover_requests = HashMap::default();
5429    for i in 0..language_server_names.len() {
5430        let new_server = language_servers[i].next().await.unwrap_or_else(|| {
5431            panic!(
5432                "Failed to get language server #{i} with name {}",
5433                &language_server_names[i]
5434            )
5435        });
5436        let new_server_name = new_server.server.name();
5437        assert!(
5438            !servers_with_hover_requests.contains_key(&new_server_name),
5439            "Unexpected: initialized server with the same name twice. Name: `{new_server_name}`"
5440        );
5441        match new_server_name.as_ref() {
5442            "CrabLang-ls" => {
5443                servers_with_hover_requests.insert(
5444                    new_server_name.clone(),
5445                    new_server.set_request_handler::<lsp::request::HoverRequest, _, _>(
5446                        move |params, _| {
5447                            assert_eq!(
5448                                params
5449                                    .text_document_position_params
5450                                    .text_document
5451                                    .uri
5452                                    .as_str(),
5453                                uri!("file:///root-1/main.rs")
5454                            );
5455                            let name = new_server_name.clone();
5456                            async move {
5457                                Ok(Some(lsp::Hover {
5458                                    contents: lsp::HoverContents::Scalar(
5459                                        lsp::MarkedString::String(format!("{name} hover")),
5460                                    ),
5461                                    range: None,
5462                                }))
5463                            }
5464                        },
5465                    ),
5466                );
5467            }
5468            "rust-analyzer" => {
5469                servers_with_hover_requests.insert(
5470                    new_server_name.clone(),
5471                    new_server.set_request_handler::<lsp::request::HoverRequest, _, _>(
5472                        |params, _| async move {
5473                            assert_eq!(
5474                                params
5475                                    .text_document_position_params
5476                                    .text_document
5477                                    .uri
5478                                    .as_str(),
5479                                uri!("file:///root-1/main.rs")
5480                            );
5481                            assert_eq!(
5482                                params.text_document_position_params.position,
5483                                lsp::Position::new(0, 22)
5484                            );
5485                            Ok(Some(lsp::Hover {
5486                                contents: lsp::HoverContents::Array(vec![
5487                                    lsp::MarkedString::String("Test hover content.".to_string()),
5488                                    lsp::MarkedString::LanguageString(lsp::LanguageString {
5489                                        language: "Rust".to_string(),
5490                                        value: "let foo = 42;".to_string(),
5491                                    }),
5492                                ]),
5493                                range: Some(lsp::Range::new(
5494                                    lsp::Position::new(0, 22),
5495                                    lsp::Position::new(0, 29),
5496                                )),
5497                            }))
5498                        },
5499                    ),
5500                );
5501            }
5502            unexpected => panic!("Unexpected server name: {unexpected}"),
5503        }
5504    }
5505    cx_a.run_until_parked();
5506    cx_b.run_until_parked();
5507
5508    // Request hover information as the guest.
5509    let mut hovers = project_b
5510        .update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
5511        .await
5512        .unwrap();
5513    assert_eq!(
5514        hovers.len(),
5515        2,
5516        "Expected two hovers from both language servers, but got: {hovers:?}"
5517    );
5518
5519    let _: Vec<()> = futures::future::join_all(servers_with_hover_requests.into_values().map(
5520        |mut hover_request| async move {
5521            hover_request
5522                .next()
5523                .await
5524                .expect("All hover requests should have been triggered")
5525        },
5526    ))
5527    .await;
5528
5529    hovers.sort_by_key(|hover| hover.contents.len());
5530    let first_hover = hovers.first().cloned().unwrap();
5531    assert_eq!(
5532        first_hover.contents,
5533        vec![project::HoverBlock {
5534            text: "CrabLang-ls hover".to_string(),
5535            kind: HoverBlockKind::Markdown,
5536        },]
5537    );
5538    let second_hover = hovers.last().cloned().unwrap();
5539    assert_eq!(
5540        second_hover.contents,
5541        vec![
5542            project::HoverBlock {
5543                text: "Test hover content.".to_string(),
5544                kind: HoverBlockKind::Markdown,
5545            },
5546            project::HoverBlock {
5547                text: "let foo = 42;".to_string(),
5548                kind: HoverBlockKind::Code {
5549                    language: "Rust".to_string()
5550                },
5551            }
5552        ]
5553    );
5554    buffer_b.read_with(cx_b, |buffer, _| {
5555        let snapshot = buffer.snapshot();
5556        assert_eq!(second_hover.range.unwrap().to_offset(&snapshot), 22..29);
5557    });
5558}
5559
5560#[gpui::test(iterations = 10)]
5561async fn test_project_symbols(
5562    executor: BackgroundExecutor,
5563    cx_a: &mut TestAppContext,
5564    cx_b: &mut TestAppContext,
5565) {
5566    let mut server = TestServer::start(executor.clone()).await;
5567    let client_a = server.create_client(cx_a, "user_a").await;
5568    let client_b = server.create_client(cx_b, "user_b").await;
5569    server
5570        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5571        .await;
5572    let active_call_a = cx_a.read(ActiveCall::global);
5573
5574    client_a.language_registry().add(rust_lang());
5575    let mut fake_language_servers = client_a.language_registry().register_fake_lsp(
5576        "Rust",
5577        FakeLspAdapter {
5578            capabilities: lsp::ServerCapabilities {
5579                workspace_symbol_provider: Some(OneOf::Left(true)),
5580                ..Default::default()
5581            },
5582            ..Default::default()
5583        },
5584    );
5585
5586    client_a
5587        .fs()
5588        .insert_tree(
5589            path!("/code"),
5590            json!({
5591                "crate-1": {
5592                    "one.rs": "const ONE: usize = 1;",
5593                },
5594                "crate-2": {
5595                    "two.rs": "const TWO: usize = 2; const THREE: usize = 3;",
5596                },
5597                "private": {
5598                    "passwords.txt": "the-password",
5599                }
5600            }),
5601        )
5602        .await;
5603    let (project_a, worktree_id) = client_a
5604        .build_local_project(path!("/code/crate-1"), cx_a)
5605        .await;
5606    let project_id = active_call_a
5607        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5608        .await
5609        .unwrap();
5610    let project_b = client_b.join_remote_project(project_id, cx_b).await;
5611
5612    // Cause the language server to start.
5613    let _buffer = project_b
5614        .update(cx_b, |p, cx| {
5615            p.open_buffer_with_lsp((worktree_id, "one.rs"), cx)
5616        })
5617        .await
5618        .unwrap();
5619
5620    let fake_language_server = fake_language_servers.next().await.unwrap();
5621    fake_language_server.set_request_handler::<lsp::WorkspaceSymbolRequest, _, _>(
5622        |_, _| async move {
5623            Ok(Some(lsp::WorkspaceSymbolResponse::Flat(vec![
5624                #[allow(deprecated)]
5625                lsp::SymbolInformation {
5626                    name: "TWO".into(),
5627                    location: lsp::Location {
5628                        uri: lsp::Uri::from_file_path(path!("/code/crate-2/two.rs")).unwrap(),
5629                        range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5630                    },
5631                    kind: lsp::SymbolKind::CONSTANT,
5632                    tags: None,
5633                    container_name: None,
5634                    deprecated: None,
5635                },
5636            ])))
5637        },
5638    );
5639
5640    // Request the definition of a symbol as the guest.
5641    let symbols = project_b
5642        .update(cx_b, |p, cx| p.symbols("two", cx))
5643        .await
5644        .unwrap();
5645    assert_eq!(symbols.len(), 1);
5646    assert_eq!(symbols[0].name, "TWO");
5647
5648    // Open one of the returned symbols.
5649    let buffer_b_2 = project_b
5650        .update(cx_b, |project, cx| {
5651            project.open_buffer_for_symbol(&symbols[0], cx)
5652        })
5653        .await
5654        .unwrap();
5655
5656    buffer_b_2.read_with(cx_b, |buffer, cx| {
5657        assert_eq!(
5658            buffer.file().unwrap().full_path(cx),
5659            Path::new(path!("/code/crate-2/two.rs"))
5660        );
5661    });
5662
5663    // Attempt to craft a symbol and violate host's privacy by opening an arbitrary file.
5664    let mut fake_symbol = symbols[0].clone();
5665    fake_symbol.path.path = Path::new(path!("/code/secrets")).into();
5666    let error = project_b
5667        .update(cx_b, |project, cx| {
5668            project.open_buffer_for_symbol(&fake_symbol, cx)
5669        })
5670        .await
5671        .unwrap_err();
5672    assert!(error.to_string().contains("invalid symbol signature"));
5673}
5674
5675#[gpui::test(iterations = 10)]
5676async fn test_open_buffer_while_getting_definition_pointing_to_it(
5677    executor: BackgroundExecutor,
5678    cx_a: &mut TestAppContext,
5679    cx_b: &mut TestAppContext,
5680    mut rng: StdRng,
5681) {
5682    let mut server = TestServer::start(executor.clone()).await;
5683    let client_a = server.create_client(cx_a, "user_a").await;
5684    let client_b = server.create_client(cx_b, "user_b").await;
5685    server
5686        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5687        .await;
5688    let active_call_a = cx_a.read(ActiveCall::global);
5689
5690    let capabilities = lsp::ServerCapabilities {
5691        definition_provider: Some(OneOf::Left(true)),
5692        ..lsp::ServerCapabilities::default()
5693    };
5694    client_a.language_registry().add(rust_lang());
5695    let mut fake_language_servers = client_a.language_registry().register_fake_lsp(
5696        "Rust",
5697        FakeLspAdapter {
5698            capabilities: capabilities.clone(),
5699            ..FakeLspAdapter::default()
5700        },
5701    );
5702    client_b.language_registry().add(rust_lang());
5703    client_b.language_registry().register_fake_lsp_adapter(
5704        "Rust",
5705        FakeLspAdapter {
5706            capabilities,
5707            ..FakeLspAdapter::default()
5708        },
5709    );
5710
5711    client_a
5712        .fs()
5713        .insert_tree(
5714            path!("/root"),
5715            json!({
5716                "a.rs": "const ONE: usize = b::TWO;",
5717                "b.rs": "const TWO: usize = 2",
5718            }),
5719        )
5720        .await;
5721    let (project_a, worktree_id) = client_a.build_local_project(path!("/root"), cx_a).await;
5722    let project_id = active_call_a
5723        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5724        .await
5725        .unwrap();
5726    let project_b = client_b.join_remote_project(project_id, cx_b).await;
5727
5728    let (buffer_b1, _lsp) = project_b
5729        .update(cx_b, |p, cx| {
5730            p.open_buffer_with_lsp((worktree_id, "a.rs"), cx)
5731        })
5732        .await
5733        .unwrap();
5734
5735    let fake_language_server = fake_language_servers.next().await.unwrap();
5736    fake_language_server.set_request_handler::<lsp::request::GotoDefinition, _, _>(
5737        |_, _| async move {
5738            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
5739                lsp::Location::new(
5740                    lsp::Uri::from_file_path(path!("/root/b.rs")).unwrap(),
5741                    lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5742                ),
5743            )))
5744        },
5745    );
5746
5747    let definitions;
5748    let buffer_b2;
5749    if rng.random() {
5750        cx_a.run_until_parked();
5751        cx_b.run_until_parked();
5752        definitions = project_b.update(cx_b, |p, cx| p.definitions(&buffer_b1, 23, cx));
5753        (buffer_b2, _) = project_b
5754            .update(cx_b, |p, cx| {
5755                p.open_buffer_with_lsp((worktree_id, "b.rs"), cx)
5756            })
5757            .await
5758            .unwrap();
5759    } else {
5760        (buffer_b2, _) = project_b
5761            .update(cx_b, |p, cx| {
5762                p.open_buffer_with_lsp((worktree_id, "b.rs"), cx)
5763            })
5764            .await
5765            .unwrap();
5766        cx_a.run_until_parked();
5767        cx_b.run_until_parked();
5768        definitions = project_b.update(cx_b, |p, cx| p.definitions(&buffer_b1, 23, cx));
5769    }
5770
5771    let definitions = definitions.await.unwrap().unwrap();
5772    assert_eq!(
5773        definitions.len(),
5774        1,
5775        "Unexpected definitions: {definitions:?}"
5776    );
5777    assert_eq!(definitions[0].target.buffer, buffer_b2);
5778}
5779
5780#[gpui::test(iterations = 10)]
5781async fn test_contacts(
5782    executor: BackgroundExecutor,
5783    cx_a: &mut TestAppContext,
5784    cx_b: &mut TestAppContext,
5785    cx_c: &mut TestAppContext,
5786    cx_d: &mut TestAppContext,
5787) {
5788    let mut server = TestServer::start(executor.clone()).await;
5789    let client_a = server.create_client(cx_a, "user_a").await;
5790    let client_b = server.create_client(cx_b, "user_b").await;
5791    let client_c = server.create_client(cx_c, "user_c").await;
5792    let client_d = server.create_client(cx_d, "user_d").await;
5793    server
5794        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
5795        .await;
5796    let active_call_a = cx_a.read(ActiveCall::global);
5797    let active_call_b = cx_b.read(ActiveCall::global);
5798    let active_call_c = cx_c.read(ActiveCall::global);
5799    let _active_call_d = cx_d.read(ActiveCall::global);
5800
5801    executor.run_until_parked();
5802    assert_eq!(
5803        contacts(&client_a, cx_a),
5804        [
5805            ("user_b".to_string(), "online", "free"),
5806            ("user_c".to_string(), "online", "free")
5807        ]
5808    );
5809    assert_eq!(
5810        contacts(&client_b, cx_b),
5811        [
5812            ("user_a".to_string(), "online", "free"),
5813            ("user_c".to_string(), "online", "free")
5814        ]
5815    );
5816    assert_eq!(
5817        contacts(&client_c, cx_c),
5818        [
5819            ("user_a".to_string(), "online", "free"),
5820            ("user_b".to_string(), "online", "free")
5821        ]
5822    );
5823    assert_eq!(contacts(&client_d, cx_d), []);
5824
5825    server.disconnect_client(client_c.peer_id().unwrap());
5826    server.forbid_connections();
5827    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5828    assert_eq!(
5829        contacts(&client_a, cx_a),
5830        [
5831            ("user_b".to_string(), "online", "free"),
5832            ("user_c".to_string(), "offline", "free")
5833        ]
5834    );
5835    assert_eq!(
5836        contacts(&client_b, cx_b),
5837        [
5838            ("user_a".to_string(), "online", "free"),
5839            ("user_c".to_string(), "offline", "free")
5840        ]
5841    );
5842    assert_eq!(contacts(&client_c, cx_c), []);
5843    assert_eq!(contacts(&client_d, cx_d), []);
5844
5845    server.allow_connections();
5846    client_c
5847        .connect(false, &cx_c.to_async())
5848        .await
5849        .into_response()
5850        .unwrap();
5851
5852    executor.run_until_parked();
5853    assert_eq!(
5854        contacts(&client_a, cx_a),
5855        [
5856            ("user_b".to_string(), "online", "free"),
5857            ("user_c".to_string(), "online", "free")
5858        ]
5859    );
5860    assert_eq!(
5861        contacts(&client_b, cx_b),
5862        [
5863            ("user_a".to_string(), "online", "free"),
5864            ("user_c".to_string(), "online", "free")
5865        ]
5866    );
5867    assert_eq!(
5868        contacts(&client_c, cx_c),
5869        [
5870            ("user_a".to_string(), "online", "free"),
5871            ("user_b".to_string(), "online", "free")
5872        ]
5873    );
5874    assert_eq!(contacts(&client_d, cx_d), []);
5875
5876    active_call_a
5877        .update(cx_a, |call, cx| {
5878            call.invite(client_b.user_id().unwrap(), None, cx)
5879        })
5880        .await
5881        .unwrap();
5882    executor.run_until_parked();
5883    assert_eq!(
5884        contacts(&client_a, cx_a),
5885        [
5886            ("user_b".to_string(), "online", "busy"),
5887            ("user_c".to_string(), "online", "free")
5888        ]
5889    );
5890    assert_eq!(
5891        contacts(&client_b, cx_b),
5892        [
5893            ("user_a".to_string(), "online", "busy"),
5894            ("user_c".to_string(), "online", "free")
5895        ]
5896    );
5897    assert_eq!(
5898        contacts(&client_c, cx_c),
5899        [
5900            ("user_a".to_string(), "online", "busy"),
5901            ("user_b".to_string(), "online", "busy")
5902        ]
5903    );
5904    assert_eq!(contacts(&client_d, cx_d), []);
5905
5906    // Client B and client D become contacts while client B is being called.
5907    server
5908        .make_contacts(&mut [(&client_b, cx_b), (&client_d, cx_d)])
5909        .await;
5910    executor.run_until_parked();
5911    assert_eq!(
5912        contacts(&client_a, cx_a),
5913        [
5914            ("user_b".to_string(), "online", "busy"),
5915            ("user_c".to_string(), "online", "free")
5916        ]
5917    );
5918    assert_eq!(
5919        contacts(&client_b, cx_b),
5920        [
5921            ("user_a".to_string(), "online", "busy"),
5922            ("user_c".to_string(), "online", "free"),
5923            ("user_d".to_string(), "online", "free"),
5924        ]
5925    );
5926    assert_eq!(
5927        contacts(&client_c, cx_c),
5928        [
5929            ("user_a".to_string(), "online", "busy"),
5930            ("user_b".to_string(), "online", "busy")
5931        ]
5932    );
5933    assert_eq!(
5934        contacts(&client_d, cx_d),
5935        [("user_b".to_string(), "online", "busy")]
5936    );
5937
5938    active_call_b.update(cx_b, |call, cx| call.decline_incoming(cx).unwrap());
5939    executor.run_until_parked();
5940    assert_eq!(
5941        contacts(&client_a, cx_a),
5942        [
5943            ("user_b".to_string(), "online", "free"),
5944            ("user_c".to_string(), "online", "free")
5945        ]
5946    );
5947    assert_eq!(
5948        contacts(&client_b, cx_b),
5949        [
5950            ("user_a".to_string(), "online", "free"),
5951            ("user_c".to_string(), "online", "free"),
5952            ("user_d".to_string(), "online", "free")
5953        ]
5954    );
5955    assert_eq!(
5956        contacts(&client_c, cx_c),
5957        [
5958            ("user_a".to_string(), "online", "free"),
5959            ("user_b".to_string(), "online", "free")
5960        ]
5961    );
5962    assert_eq!(
5963        contacts(&client_d, cx_d),
5964        [("user_b".to_string(), "online", "free")]
5965    );
5966
5967    active_call_c
5968        .update(cx_c, |call, cx| {
5969            call.invite(client_a.user_id().unwrap(), None, cx)
5970        })
5971        .await
5972        .unwrap();
5973    executor.run_until_parked();
5974    assert_eq!(
5975        contacts(&client_a, cx_a),
5976        [
5977            ("user_b".to_string(), "online", "free"),
5978            ("user_c".to_string(), "online", "busy")
5979        ]
5980    );
5981    assert_eq!(
5982        contacts(&client_b, cx_b),
5983        [
5984            ("user_a".to_string(), "online", "busy"),
5985            ("user_c".to_string(), "online", "busy"),
5986            ("user_d".to_string(), "online", "free")
5987        ]
5988    );
5989    assert_eq!(
5990        contacts(&client_c, cx_c),
5991        [
5992            ("user_a".to_string(), "online", "busy"),
5993            ("user_b".to_string(), "online", "free")
5994        ]
5995    );
5996    assert_eq!(
5997        contacts(&client_d, cx_d),
5998        [("user_b".to_string(), "online", "free")]
5999    );
6000
6001    active_call_a
6002        .update(cx_a, |call, cx| call.accept_incoming(cx))
6003        .await
6004        .unwrap();
6005    executor.run_until_parked();
6006    assert_eq!(
6007        contacts(&client_a, cx_a),
6008        [
6009            ("user_b".to_string(), "online", "free"),
6010            ("user_c".to_string(), "online", "busy")
6011        ]
6012    );
6013    assert_eq!(
6014        contacts(&client_b, cx_b),
6015        [
6016            ("user_a".to_string(), "online", "busy"),
6017            ("user_c".to_string(), "online", "busy"),
6018            ("user_d".to_string(), "online", "free")
6019        ]
6020    );
6021    assert_eq!(
6022        contacts(&client_c, cx_c),
6023        [
6024            ("user_a".to_string(), "online", "busy"),
6025            ("user_b".to_string(), "online", "free")
6026        ]
6027    );
6028    assert_eq!(
6029        contacts(&client_d, cx_d),
6030        [("user_b".to_string(), "online", "free")]
6031    );
6032
6033    active_call_a
6034        .update(cx_a, |call, cx| {
6035            call.invite(client_b.user_id().unwrap(), None, cx)
6036        })
6037        .await
6038        .unwrap();
6039    executor.run_until_parked();
6040    assert_eq!(
6041        contacts(&client_a, cx_a),
6042        [
6043            ("user_b".to_string(), "online", "busy"),
6044            ("user_c".to_string(), "online", "busy")
6045        ]
6046    );
6047    assert_eq!(
6048        contacts(&client_b, cx_b),
6049        [
6050            ("user_a".to_string(), "online", "busy"),
6051            ("user_c".to_string(), "online", "busy"),
6052            ("user_d".to_string(), "online", "free")
6053        ]
6054    );
6055    assert_eq!(
6056        contacts(&client_c, cx_c),
6057        [
6058            ("user_a".to_string(), "online", "busy"),
6059            ("user_b".to_string(), "online", "busy")
6060        ]
6061    );
6062    assert_eq!(
6063        contacts(&client_d, cx_d),
6064        [("user_b".to_string(), "online", "busy")]
6065    );
6066
6067    active_call_a
6068        .update(cx_a, |call, cx| call.hang_up(cx))
6069        .await
6070        .unwrap();
6071    executor.run_until_parked();
6072    assert_eq!(
6073        contacts(&client_a, cx_a),
6074        [
6075            ("user_b".to_string(), "online", "free"),
6076            ("user_c".to_string(), "online", "free")
6077        ]
6078    );
6079    assert_eq!(
6080        contacts(&client_b, cx_b),
6081        [
6082            ("user_a".to_string(), "online", "free"),
6083            ("user_c".to_string(), "online", "free"),
6084            ("user_d".to_string(), "online", "free")
6085        ]
6086    );
6087    assert_eq!(
6088        contacts(&client_c, cx_c),
6089        [
6090            ("user_a".to_string(), "online", "free"),
6091            ("user_b".to_string(), "online", "free")
6092        ]
6093    );
6094    assert_eq!(
6095        contacts(&client_d, cx_d),
6096        [("user_b".to_string(), "online", "free")]
6097    );
6098
6099    active_call_a
6100        .update(cx_a, |call, cx| {
6101            call.invite(client_b.user_id().unwrap(), None, cx)
6102        })
6103        .await
6104        .unwrap();
6105    executor.run_until_parked();
6106    assert_eq!(
6107        contacts(&client_a, cx_a),
6108        [
6109            ("user_b".to_string(), "online", "busy"),
6110            ("user_c".to_string(), "online", "free")
6111        ]
6112    );
6113    assert_eq!(
6114        contacts(&client_b, cx_b),
6115        [
6116            ("user_a".to_string(), "online", "busy"),
6117            ("user_c".to_string(), "online", "free"),
6118            ("user_d".to_string(), "online", "free")
6119        ]
6120    );
6121    assert_eq!(
6122        contacts(&client_c, cx_c),
6123        [
6124            ("user_a".to_string(), "online", "busy"),
6125            ("user_b".to_string(), "online", "busy")
6126        ]
6127    );
6128    assert_eq!(
6129        contacts(&client_d, cx_d),
6130        [("user_b".to_string(), "online", "busy")]
6131    );
6132
6133    server.forbid_connections();
6134    server.disconnect_client(client_a.peer_id().unwrap());
6135    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
6136    assert_eq!(contacts(&client_a, cx_a), []);
6137    assert_eq!(
6138        contacts(&client_b, cx_b),
6139        [
6140            ("user_a".to_string(), "offline", "free"),
6141            ("user_c".to_string(), "online", "free"),
6142            ("user_d".to_string(), "online", "free")
6143        ]
6144    );
6145    assert_eq!(
6146        contacts(&client_c, cx_c),
6147        [
6148            ("user_a".to_string(), "offline", "free"),
6149            ("user_b".to_string(), "online", "free")
6150        ]
6151    );
6152    assert_eq!(
6153        contacts(&client_d, cx_d),
6154        [("user_b".to_string(), "online", "free")]
6155    );
6156
6157    // Test removing a contact
6158    client_b
6159        .user_store()
6160        .update(cx_b, |store, cx| {
6161            store.remove_contact(client_c.user_id().unwrap(), cx)
6162        })
6163        .await
6164        .unwrap();
6165    executor.run_until_parked();
6166    assert_eq!(
6167        contacts(&client_b, cx_b),
6168        [
6169            ("user_a".to_string(), "offline", "free"),
6170            ("user_d".to_string(), "online", "free")
6171        ]
6172    );
6173    assert_eq!(
6174        contacts(&client_c, cx_c),
6175        [("user_a".to_string(), "offline", "free"),]
6176    );
6177
6178    fn contacts(
6179        client: &TestClient,
6180        cx: &TestAppContext,
6181    ) -> Vec<(String, &'static str, &'static str)> {
6182        client.user_store().read_with(cx, |store, _| {
6183            store
6184                .contacts()
6185                .iter()
6186                .map(|contact| {
6187                    (
6188                        contact.user.github_login.clone().to_string(),
6189                        if contact.online { "online" } else { "offline" },
6190                        if contact.busy { "busy" } else { "free" },
6191                    )
6192                })
6193                .collect()
6194        })
6195    }
6196}
6197
6198#[gpui::test(iterations = 10)]
6199async fn test_contact_requests(
6200    executor: BackgroundExecutor,
6201    cx_a: &mut TestAppContext,
6202    cx_a2: &mut TestAppContext,
6203    cx_b: &mut TestAppContext,
6204    cx_b2: &mut TestAppContext,
6205    cx_c: &mut TestAppContext,
6206    cx_c2: &mut TestAppContext,
6207) {
6208    // Connect to a server as 3 clients.
6209    let mut server = TestServer::start(executor.clone()).await;
6210    let client_a = server.create_client(cx_a, "user_a").await;
6211    let client_a2 = server.create_client(cx_a2, "user_a").await;
6212    let client_b = server.create_client(cx_b, "user_b").await;
6213    let client_b2 = server.create_client(cx_b2, "user_b").await;
6214    let client_c = server.create_client(cx_c, "user_c").await;
6215    let client_c2 = server.create_client(cx_c2, "user_c").await;
6216
6217    assert_eq!(client_a.user_id().unwrap(), client_a2.user_id().unwrap());
6218    assert_eq!(client_b.user_id().unwrap(), client_b2.user_id().unwrap());
6219    assert_eq!(client_c.user_id().unwrap(), client_c2.user_id().unwrap());
6220
6221    // User A and User C request that user B become their contact.
6222    client_a
6223        .user_store()
6224        .update(cx_a, |store, cx| {
6225            store.request_contact(client_b.user_id().unwrap(), cx)
6226        })
6227        .await
6228        .unwrap();
6229    client_c
6230        .user_store()
6231        .update(cx_c, |store, cx| {
6232            store.request_contact(client_b.user_id().unwrap(), cx)
6233        })
6234        .await
6235        .unwrap();
6236    executor.run_until_parked();
6237
6238    // All users see the pending request appear in all their clients.
6239    assert_eq!(
6240        client_a.summarize_contacts(cx_a).outgoing_requests,
6241        &["user_b"]
6242    );
6243    assert_eq!(
6244        client_a2.summarize_contacts(cx_a2).outgoing_requests,
6245        &["user_b"]
6246    );
6247    assert_eq!(
6248        client_b.summarize_contacts(cx_b).incoming_requests,
6249        &["user_a", "user_c"]
6250    );
6251    assert_eq!(
6252        client_b2.summarize_contacts(cx_b2).incoming_requests,
6253        &["user_a", "user_c"]
6254    );
6255    assert_eq!(
6256        client_c.summarize_contacts(cx_c).outgoing_requests,
6257        &["user_b"]
6258    );
6259    assert_eq!(
6260        client_c2.summarize_contacts(cx_c2).outgoing_requests,
6261        &["user_b"]
6262    );
6263
6264    // Contact requests are present upon connecting (tested here via disconnect/reconnect)
6265    disconnect_and_reconnect(&client_a, cx_a).await;
6266    disconnect_and_reconnect(&client_b, cx_b).await;
6267    disconnect_and_reconnect(&client_c, cx_c).await;
6268    executor.run_until_parked();
6269    assert_eq!(
6270        client_a.summarize_contacts(cx_a).outgoing_requests,
6271        &["user_b"]
6272    );
6273    assert_eq!(
6274        client_b.summarize_contacts(cx_b).incoming_requests,
6275        &["user_a", "user_c"]
6276    );
6277    assert_eq!(
6278        client_c.summarize_contacts(cx_c).outgoing_requests,
6279        &["user_b"]
6280    );
6281
6282    // User B accepts the request from user A.
6283    client_b
6284        .user_store()
6285        .update(cx_b, |store, cx| {
6286            store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
6287        })
6288        .await
6289        .unwrap();
6290
6291    executor.run_until_parked();
6292
6293    // User B sees user A as their contact now in all client, and the incoming request from them is removed.
6294    let contacts_b = client_b.summarize_contacts(cx_b);
6295    assert_eq!(contacts_b.current, &["user_a"]);
6296    assert_eq!(contacts_b.incoming_requests, &["user_c"]);
6297    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
6298    assert_eq!(contacts_b2.current, &["user_a"]);
6299    assert_eq!(contacts_b2.incoming_requests, &["user_c"]);
6300
6301    // User A sees user B as their contact now in all clients, and the outgoing request to them is removed.
6302    let contacts_a = client_a.summarize_contacts(cx_a);
6303    assert_eq!(contacts_a.current, &["user_b"]);
6304    assert!(contacts_a.outgoing_requests.is_empty());
6305    let contacts_a2 = client_a2.summarize_contacts(cx_a2);
6306    assert_eq!(contacts_a2.current, &["user_b"]);
6307    assert!(contacts_a2.outgoing_requests.is_empty());
6308
6309    // Contacts are present upon connecting (tested here via disconnect/reconnect)
6310    disconnect_and_reconnect(&client_a, cx_a).await;
6311    disconnect_and_reconnect(&client_b, cx_b).await;
6312    disconnect_and_reconnect(&client_c, cx_c).await;
6313    executor.run_until_parked();
6314    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
6315    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
6316    assert_eq!(
6317        client_b.summarize_contacts(cx_b).incoming_requests,
6318        &["user_c"]
6319    );
6320    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
6321    assert_eq!(
6322        client_c.summarize_contacts(cx_c).outgoing_requests,
6323        &["user_b"]
6324    );
6325
6326    // User B rejects the request from user C.
6327    client_b
6328        .user_store()
6329        .update(cx_b, |store, cx| {
6330            store.respond_to_contact_request(client_c.user_id().unwrap(), false, cx)
6331        })
6332        .await
6333        .unwrap();
6334
6335    executor.run_until_parked();
6336
6337    // User B doesn't see user C as their contact, and the incoming request from them is removed.
6338    let contacts_b = client_b.summarize_contacts(cx_b);
6339    assert_eq!(contacts_b.current, &["user_a"]);
6340    assert!(contacts_b.incoming_requests.is_empty());
6341    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
6342    assert_eq!(contacts_b2.current, &["user_a"]);
6343    assert!(contacts_b2.incoming_requests.is_empty());
6344
6345    // User C doesn't see user B as their contact, and the outgoing request to them is removed.
6346    let contacts_c = client_c.summarize_contacts(cx_c);
6347    assert!(contacts_c.current.is_empty());
6348    assert!(contacts_c.outgoing_requests.is_empty());
6349    let contacts_c2 = client_c2.summarize_contacts(cx_c2);
6350    assert!(contacts_c2.current.is_empty());
6351    assert!(contacts_c2.outgoing_requests.is_empty());
6352
6353    // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect)
6354    disconnect_and_reconnect(&client_a, cx_a).await;
6355    disconnect_and_reconnect(&client_b, cx_b).await;
6356    disconnect_and_reconnect(&client_c, cx_c).await;
6357    executor.run_until_parked();
6358    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
6359    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
6360    assert!(
6361        client_b
6362            .summarize_contacts(cx_b)
6363            .incoming_requests
6364            .is_empty()
6365    );
6366    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
6367    assert!(
6368        client_c
6369            .summarize_contacts(cx_c)
6370            .outgoing_requests
6371            .is_empty()
6372    );
6373
6374    async fn disconnect_and_reconnect(client: &TestClient, cx: &mut TestAppContext) {
6375        client.disconnect(&cx.to_async());
6376        client.clear_contacts(cx).await;
6377        client
6378            .connect(false, &cx.to_async())
6379            .await
6380            .into_response()
6381            .unwrap();
6382    }
6383}
6384
6385#[gpui::test(iterations = 10)]
6386async fn test_join_call_after_screen_was_shared(
6387    executor: BackgroundExecutor,
6388    cx_a: &mut TestAppContext,
6389    cx_b: &mut TestAppContext,
6390) {
6391    let mut server = TestServer::start(executor.clone()).await;
6392
6393    let client_a = server.create_client(cx_a, "user_a").await;
6394    let client_b = server.create_client(cx_b, "user_b").await;
6395    server
6396        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6397        .await;
6398
6399    let active_call_a = cx_a.read(ActiveCall::global);
6400    let active_call_b = cx_b.read(ActiveCall::global);
6401
6402    // Call users B and C from client A.
6403    active_call_a
6404        .update(cx_a, |call, cx| {
6405            call.invite(client_b.user_id().unwrap(), None, cx)
6406        })
6407        .await
6408        .unwrap();
6409
6410    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
6411    executor.run_until_parked();
6412    assert_eq!(
6413        room_participants(&room_a, cx_a),
6414        RoomParticipants {
6415            remote: Default::default(),
6416            pending: vec!["user_b".to_string()]
6417        }
6418    );
6419
6420    // User B receives the call.
6421
6422    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
6423    let call_b = incoming_call_b.next().await.unwrap().unwrap();
6424    assert_eq!(call_b.calling_user.github_login, "user_a");
6425
6426    // User A shares their screen
6427    let display = gpui::TestScreenCaptureSource::new();
6428    cx_a.set_screen_capture_sources(vec![display]);
6429    let screen_a = cx_a
6430        .update(|cx| cx.screen_capture_sources())
6431        .await
6432        .unwrap()
6433        .unwrap()
6434        .into_iter()
6435        .next()
6436        .unwrap();
6437
6438    active_call_a
6439        .update(cx_a, |call, cx| {
6440            call.room()
6441                .unwrap()
6442                .update(cx, |room, cx| room.share_screen(screen_a, cx))
6443        })
6444        .await
6445        .unwrap();
6446
6447    client_b.user_store().update(cx_b, |user_store, _| {
6448        user_store.clear_cache();
6449    });
6450
6451    // User B joins the room
6452    active_call_b
6453        .update(cx_b, |call, cx| call.accept_incoming(cx))
6454        .await
6455        .unwrap();
6456
6457    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
6458    assert!(incoming_call_b.next().await.unwrap().is_none());
6459
6460    executor.run_until_parked();
6461    assert_eq!(
6462        room_participants(&room_a, cx_a),
6463        RoomParticipants {
6464            remote: vec!["user_b".to_string()],
6465            pending: vec![],
6466        }
6467    );
6468    assert_eq!(
6469        room_participants(&room_b, cx_b),
6470        RoomParticipants {
6471            remote: vec!["user_a".to_string()],
6472            pending: vec![],
6473        }
6474    );
6475
6476    // Ensure User B sees User A's screenshare.
6477
6478    room_b.read_with(cx_b, |room, _| {
6479        assert_eq!(
6480            room.remote_participants()
6481                .get(&client_a.user_id().unwrap())
6482                .unwrap()
6483                .video_tracks
6484                .len(),
6485            1
6486        );
6487    });
6488}
6489
6490#[gpui::test]
6491async fn test_right_click_menu_behind_collab_panel(cx: &mut TestAppContext) {
6492    let mut server = TestServer::start(cx.executor().clone()).await;
6493    let client_a = server.create_client(cx, "user_a").await;
6494    let (_workspace_a, cx) = client_a.build_test_workspace(cx).await;
6495
6496    cx.simulate_resize(size(px(300.), px(300.)));
6497
6498    cx.simulate_keystrokes("cmd-n cmd-n cmd-n");
6499    cx.update(|window, _cx| window.refresh());
6500
6501    let tab_bounds = cx.debug_bounds("TAB-2").unwrap();
6502    let new_tab_button_bounds = cx.debug_bounds("ICON-Plus").unwrap();
6503
6504    assert!(
6505        tab_bounds.intersects(&new_tab_button_bounds),
6506        "Tab should overlap with the new tab button, if this is failing check if there's been a redesign!"
6507    );
6508
6509    cx.simulate_event(MouseDownEvent {
6510        button: MouseButton::Right,
6511        position: new_tab_button_bounds.center(),
6512        modifiers: Modifiers::default(),
6513        click_count: 1,
6514        first_mouse: false,
6515    });
6516
6517    // regression test that the right click menu for tabs does not open.
6518    assert!(cx.debug_bounds("MENU_ITEM-Close").is_none());
6519
6520    let tab_bounds = cx.debug_bounds("TAB-1").unwrap();
6521    cx.simulate_event(MouseDownEvent {
6522        button: MouseButton::Right,
6523        position: tab_bounds.center(),
6524        modifiers: Modifiers::default(),
6525        click_count: 1,
6526        first_mouse: false,
6527    });
6528    assert!(cx.debug_bounds("MENU_ITEM-Close").is_some());
6529}
6530
6531#[gpui::test]
6532async fn test_pane_split_left(cx: &mut TestAppContext) {
6533    let (_, client) = TestServer::start1(cx).await;
6534    let (workspace, cx) = client.build_test_workspace(cx).await;
6535
6536    cx.simulate_keystrokes("cmd-n");
6537    workspace.update(cx, |workspace, cx| {
6538        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 1);
6539    });
6540    cx.simulate_keystrokes("cmd-k left");
6541    workspace.update(cx, |workspace, cx| {
6542        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 2);
6543    });
6544    cx.simulate_keystrokes("cmd-k");
6545    // sleep for longer than the timeout in keyboard shortcut handling
6546    // to verify that it doesn't fire in this case.
6547    cx.executor().advance_clock(Duration::from_secs(2));
6548    cx.simulate_keystrokes("left");
6549    workspace.update(cx, |workspace, cx| {
6550        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 2);
6551    });
6552}
6553
6554#[gpui::test]
6555async fn test_join_after_restart(cx1: &mut TestAppContext, cx2: &mut TestAppContext) {
6556    let (mut server, client) = TestServer::start1(cx1).await;
6557    let channel1 = server.make_public_channel("channel1", &client, cx1).await;
6558    let channel2 = server.make_public_channel("channel2", &client, cx1).await;
6559
6560    join_channel(channel1, &client, cx1).await.unwrap();
6561    drop(client);
6562
6563    let client2 = server.create_client(cx2, "user_a").await;
6564    join_channel(channel2, &client2, cx2).await.unwrap();
6565}
6566
6567#[gpui::test]
6568async fn test_preview_tabs(cx: &mut TestAppContext) {
6569    let (_server, client) = TestServer::start1(cx).await;
6570    let (workspace, cx) = client.build_test_workspace(cx).await;
6571    let project = workspace.read_with(cx, |workspace, _| workspace.project().clone());
6572
6573    let worktree_id = project.update(cx, |project, cx| {
6574        project.worktrees(cx).next().unwrap().read(cx).id()
6575    });
6576
6577    let path_1 = ProjectPath {
6578        worktree_id,
6579        path: Path::new("1.txt").into(),
6580    };
6581    let path_2 = ProjectPath {
6582        worktree_id,
6583        path: Path::new("2.js").into(),
6584    };
6585    let path_3 = ProjectPath {
6586        worktree_id,
6587        path: Path::new("3.rs").into(),
6588    };
6589
6590    let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
6591
6592    let get_path = |pane: &Pane, idx: usize, cx: &App| {
6593        pane.item_for_index(idx).unwrap().project_path(cx).unwrap()
6594    };
6595
6596    // Opening item 3 as a "permanent" tab
6597    workspace
6598        .update_in(cx, |workspace, window, cx| {
6599            workspace.open_path(path_3.clone(), None, false, window, cx)
6600        })
6601        .await
6602        .unwrap();
6603
6604    pane.update(cx, |pane, cx| {
6605        assert_eq!(pane.items_len(), 1);
6606        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6607        assert_eq!(pane.preview_item_id(), None);
6608
6609        assert!(!pane.can_navigate_backward());
6610        assert!(!pane.can_navigate_forward());
6611    });
6612
6613    // Open item 1 as preview
6614    workspace
6615        .update_in(cx, |workspace, window, cx| {
6616            workspace.open_path_preview(path_1.clone(), None, true, true, true, window, cx)
6617        })
6618        .await
6619        .unwrap();
6620
6621    pane.update(cx, |pane, cx| {
6622        assert_eq!(pane.items_len(), 2);
6623        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6624        assert_eq!(get_path(pane, 1, cx), path_1.clone());
6625        assert_eq!(
6626            pane.preview_item_id(),
6627            Some(pane.items().nth(1).unwrap().item_id())
6628        );
6629
6630        assert!(pane.can_navigate_backward());
6631        assert!(!pane.can_navigate_forward());
6632    });
6633
6634    // Open item 2 as preview
6635    workspace
6636        .update_in(cx, |workspace, window, cx| {
6637            workspace.open_path_preview(path_2.clone(), None, true, true, true, window, cx)
6638        })
6639        .await
6640        .unwrap();
6641
6642    pane.update(cx, |pane, cx| {
6643        assert_eq!(pane.items_len(), 2);
6644        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6645        assert_eq!(get_path(pane, 1, cx), path_2.clone());
6646        assert_eq!(
6647            pane.preview_item_id(),
6648            Some(pane.items().nth(1).unwrap().item_id())
6649        );
6650
6651        assert!(pane.can_navigate_backward());
6652        assert!(!pane.can_navigate_forward());
6653    });
6654
6655    // Going back should show item 1 as preview
6656    workspace
6657        .update_in(cx, |workspace, window, cx| {
6658            workspace.go_back(pane.downgrade(), window, cx)
6659        })
6660        .await
6661        .unwrap();
6662
6663    pane.update(cx, |pane, cx| {
6664        assert_eq!(pane.items_len(), 2);
6665        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6666        assert_eq!(get_path(pane, 1, cx), path_1.clone());
6667        assert_eq!(
6668            pane.preview_item_id(),
6669            Some(pane.items().nth(1).unwrap().item_id())
6670        );
6671
6672        assert!(pane.can_navigate_backward());
6673        assert!(pane.can_navigate_forward());
6674    });
6675
6676    // Closing item 1
6677    pane.update_in(cx, |pane, window, cx| {
6678        pane.close_item_by_id(
6679            pane.active_item().unwrap().item_id(),
6680            workspace::SaveIntent::Skip,
6681            window,
6682            cx,
6683        )
6684    })
6685    .await
6686    .unwrap();
6687
6688    pane.update(cx, |pane, cx| {
6689        assert_eq!(pane.items_len(), 1);
6690        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6691        assert_eq!(pane.preview_item_id(), None);
6692
6693        assert!(pane.can_navigate_backward());
6694        assert!(!pane.can_navigate_forward());
6695    });
6696
6697    // Going back should show item 1 as preview
6698    workspace
6699        .update_in(cx, |workspace, window, cx| {
6700            workspace.go_back(pane.downgrade(), window, cx)
6701        })
6702        .await
6703        .unwrap();
6704
6705    pane.update(cx, |pane, cx| {
6706        assert_eq!(pane.items_len(), 2);
6707        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6708        assert_eq!(get_path(pane, 1, cx), path_1.clone());
6709        assert_eq!(
6710            pane.preview_item_id(),
6711            Some(pane.items().nth(1).unwrap().item_id())
6712        );
6713
6714        assert!(pane.can_navigate_backward());
6715        assert!(pane.can_navigate_forward());
6716    });
6717
6718    // Close permanent tab
6719    pane.update_in(cx, |pane, window, cx| {
6720        let id = pane.items().next().unwrap().item_id();
6721        pane.close_item_by_id(id, workspace::SaveIntent::Skip, window, cx)
6722    })
6723    .await
6724    .unwrap();
6725
6726    pane.update(cx, |pane, cx| {
6727        assert_eq!(pane.items_len(), 1);
6728        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6729        assert_eq!(
6730            pane.preview_item_id(),
6731            Some(pane.items().next().unwrap().item_id())
6732        );
6733
6734        assert!(pane.can_navigate_backward());
6735        assert!(pane.can_navigate_forward());
6736    });
6737
6738    // Split pane to the right
6739    pane.update(cx, |pane, cx| {
6740        pane.split(workspace::SplitDirection::Right, cx);
6741    });
6742
6743    let right_pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
6744
6745    pane.update(cx, |pane, cx| {
6746        assert_eq!(pane.items_len(), 1);
6747        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6748        assert_eq!(
6749            pane.preview_item_id(),
6750            Some(pane.items().next().unwrap().item_id())
6751        );
6752
6753        assert!(pane.can_navigate_backward());
6754        assert!(pane.can_navigate_forward());
6755    });
6756
6757    right_pane.update(cx, |pane, cx| {
6758        assert_eq!(pane.items_len(), 1);
6759        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6760        assert_eq!(pane.preview_item_id(), None);
6761
6762        assert!(!pane.can_navigate_backward());
6763        assert!(!pane.can_navigate_forward());
6764    });
6765
6766    // Open item 2 as preview in right pane
6767    workspace
6768        .update_in(cx, |workspace, window, cx| {
6769            workspace.open_path_preview(path_2.clone(), None, true, true, true, window, cx)
6770        })
6771        .await
6772        .unwrap();
6773
6774    pane.update(cx, |pane, cx| {
6775        assert_eq!(pane.items_len(), 1);
6776        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6777        assert_eq!(
6778            pane.preview_item_id(),
6779            Some(pane.items().next().unwrap().item_id())
6780        );
6781
6782        assert!(pane.can_navigate_backward());
6783        assert!(pane.can_navigate_forward());
6784    });
6785
6786    right_pane.update(cx, |pane, cx| {
6787        assert_eq!(pane.items_len(), 2);
6788        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6789        assert_eq!(get_path(pane, 1, cx), path_2.clone());
6790        assert_eq!(
6791            pane.preview_item_id(),
6792            Some(pane.items().nth(1).unwrap().item_id())
6793        );
6794
6795        assert!(pane.can_navigate_backward());
6796        assert!(!pane.can_navigate_forward());
6797    });
6798
6799    // Focus left pane
6800    workspace.update_in(cx, |workspace, window, cx| {
6801        workspace.activate_pane_in_direction(workspace::SplitDirection::Left, window, cx)
6802    });
6803
6804    // Open item 2 as preview in left pane
6805    workspace
6806        .update_in(cx, |workspace, window, cx| {
6807            workspace.open_path_preview(path_2.clone(), None, true, true, true, window, cx)
6808        })
6809        .await
6810        .unwrap();
6811
6812    pane.update(cx, |pane, cx| {
6813        assert_eq!(pane.items_len(), 1);
6814        assert_eq!(get_path(pane, 0, cx), path_2.clone());
6815        assert_eq!(
6816            pane.preview_item_id(),
6817            Some(pane.items().next().unwrap().item_id())
6818        );
6819
6820        assert!(pane.can_navigate_backward());
6821        assert!(!pane.can_navigate_forward());
6822    });
6823
6824    right_pane.update(cx, |pane, cx| {
6825        assert_eq!(pane.items_len(), 2);
6826        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6827        assert_eq!(get_path(pane, 1, cx), path_2.clone());
6828        assert_eq!(
6829            pane.preview_item_id(),
6830            Some(pane.items().nth(1).unwrap().item_id())
6831        );
6832
6833        assert!(pane.can_navigate_backward());
6834        assert!(!pane.can_navigate_forward());
6835    });
6836}
6837
6838#[gpui::test(iterations = 10)]
6839async fn test_context_collaboration_with_reconnect(
6840    executor: BackgroundExecutor,
6841    cx_a: &mut TestAppContext,
6842    cx_b: &mut TestAppContext,
6843) {
6844    let mut server = TestServer::start(executor.clone()).await;
6845    let client_a = server.create_client(cx_a, "user_a").await;
6846    let client_b = server.create_client(cx_b, "user_b").await;
6847    server
6848        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6849        .await;
6850    let active_call_a = cx_a.read(ActiveCall::global);
6851
6852    client_a.fs().insert_tree("/a", Default::default()).await;
6853    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
6854    let project_id = active_call_a
6855        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6856        .await
6857        .unwrap();
6858    let project_b = client_b.join_remote_project(project_id, cx_b).await;
6859
6860    // Client A sees that a guest has joined.
6861    executor.run_until_parked();
6862
6863    project_a.read_with(cx_a, |project, _| {
6864        assert_eq!(project.collaborators().len(), 1);
6865    });
6866    project_b.read_with(cx_b, |project, _| {
6867        assert_eq!(project.collaborators().len(), 1);
6868    });
6869
6870    let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
6871    let context_store_a = cx_a
6872        .update(|cx| {
6873            ContextStore::new(
6874                project_a.clone(),
6875                prompt_builder.clone(),
6876                Arc::new(SlashCommandWorkingSet::default()),
6877                cx,
6878            )
6879        })
6880        .await
6881        .unwrap();
6882    let context_store_b = cx_b
6883        .update(|cx| {
6884            ContextStore::new(
6885                project_b.clone(),
6886                prompt_builder.clone(),
6887                Arc::new(SlashCommandWorkingSet::default()),
6888                cx,
6889            )
6890        })
6891        .await
6892        .unwrap();
6893
6894    // Client A creates a new chats.
6895    let context_a = context_store_a.update(cx_a, |store, cx| store.create(cx));
6896    executor.run_until_parked();
6897
6898    // Client B retrieves host's contexts and joins one.
6899    let context_b = context_store_b
6900        .update(cx_b, |store, cx| {
6901            let host_contexts = store.host_contexts().to_vec();
6902            assert_eq!(host_contexts.len(), 1);
6903            store.open_remote_context(host_contexts[0].id.clone(), cx)
6904        })
6905        .await
6906        .unwrap();
6907
6908    // Host and guest make changes
6909    context_a.update(cx_a, |context, cx| {
6910        context.buffer().update(cx, |buffer, cx| {
6911            buffer.edit([(0..0, "Host change\n")], None, cx)
6912        })
6913    });
6914    context_b.update(cx_b, |context, cx| {
6915        context.buffer().update(cx, |buffer, cx| {
6916            buffer.edit([(0..0, "Guest change\n")], None, cx)
6917        })
6918    });
6919    executor.run_until_parked();
6920    assert_eq!(
6921        context_a.read_with(cx_a, |context, cx| context.buffer().read(cx).text()),
6922        "Guest change\nHost change\n"
6923    );
6924    assert_eq!(
6925        context_b.read_with(cx_b, |context, cx| context.buffer().read(cx).text()),
6926        "Guest change\nHost change\n"
6927    );
6928
6929    // Disconnect client A and make some changes while disconnected.
6930    server.disconnect_client(client_a.peer_id().unwrap());
6931    server.forbid_connections();
6932    context_a.update(cx_a, |context, cx| {
6933        context.buffer().update(cx, |buffer, cx| {
6934            buffer.edit([(0..0, "Host offline change\n")], None, cx)
6935        })
6936    });
6937    context_b.update(cx_b, |context, cx| {
6938        context.buffer().update(cx, |buffer, cx| {
6939            buffer.edit([(0..0, "Guest offline change\n")], None, cx)
6940        })
6941    });
6942    executor.run_until_parked();
6943    assert_eq!(
6944        context_a.read_with(cx_a, |context, cx| context.buffer().read(cx).text()),
6945        "Host offline change\nGuest change\nHost change\n"
6946    );
6947    assert_eq!(
6948        context_b.read_with(cx_b, |context, cx| context.buffer().read(cx).text()),
6949        "Guest offline change\nGuest change\nHost change\n"
6950    );
6951
6952    // Allow client A to reconnect and verify that contexts converge.
6953    server.allow_connections();
6954    executor.advance_clock(RECEIVE_TIMEOUT);
6955    assert_eq!(
6956        context_a.read_with(cx_a, |context, cx| context.buffer().read(cx).text()),
6957        "Guest offline change\nHost offline change\nGuest change\nHost change\n"
6958    );
6959    assert_eq!(
6960        context_b.read_with(cx_b, |context, cx| context.buffer().read(cx).text()),
6961        "Guest offline change\nHost offline change\nGuest change\nHost change\n"
6962    );
6963
6964    // Client A disconnects without being able to reconnect. Context B becomes readonly.
6965    server.forbid_connections();
6966    server.disconnect_client(client_a.peer_id().unwrap());
6967    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
6968    context_b.read_with(cx_b, |context, cx| {
6969        assert!(context.buffer().read(cx).read_only());
6970    });
6971}
6972
6973#[gpui::test]
6974async fn test_remote_git_branches(
6975    executor: BackgroundExecutor,
6976    cx_a: &mut TestAppContext,
6977    cx_b: &mut TestAppContext,
6978) {
6979    let mut server = TestServer::start(executor.clone()).await;
6980    let client_a = server.create_client(cx_a, "user_a").await;
6981    let client_b = server.create_client(cx_b, "user_b").await;
6982    server
6983        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6984        .await;
6985    let active_call_a = cx_a.read(ActiveCall::global);
6986
6987    client_a
6988        .fs()
6989        .insert_tree("/project", serde_json::json!({ ".git":{} }))
6990        .await;
6991    let branches = ["main", "dev", "feature-1"];
6992    client_a
6993        .fs()
6994        .insert_branches(Path::new("/project/.git"), &branches);
6995    let branches_set = branches
6996        .into_iter()
6997        .map(ToString::to_string)
6998        .collect::<HashSet<_>>();
6999
7000    let (project_a, _) = client_a.build_local_project("/project", cx_a).await;
7001
7002    let project_id = active_call_a
7003        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7004        .await
7005        .unwrap();
7006    let project_b = client_b.join_remote_project(project_id, cx_b).await;
7007
7008    // Client A sees that a guest has joined and the repo has been populated
7009    executor.run_until_parked();
7010
7011    let repo_b = cx_b.update(|cx| project_b.read(cx).active_repository(cx).unwrap());
7012
7013    let branches_b = cx_b
7014        .update(|cx| repo_b.update(cx, |repository, _| repository.branches()))
7015        .await
7016        .unwrap()
7017        .unwrap();
7018
7019    let new_branch = branches[2];
7020
7021    let branches_b = branches_b
7022        .into_iter()
7023        .map(|branch| branch.name().to_string())
7024        .collect::<HashSet<_>>();
7025
7026    assert_eq!(branches_b, branches_set);
7027
7028    cx_b.update(|cx| {
7029        repo_b.update(cx, |repository, _cx| {
7030            repository.change_branch(new_branch.to_string())
7031        })
7032    })
7033    .await
7034    .unwrap()
7035    .unwrap();
7036
7037    executor.run_until_parked();
7038
7039    let host_branch = cx_a.update(|cx| {
7040        project_a.update(cx, |project, cx| {
7041            project
7042                .repositories(cx)
7043                .values()
7044                .next()
7045                .unwrap()
7046                .read(cx)
7047                .branch
7048                .as_ref()
7049                .unwrap()
7050                .clone()
7051        })
7052    });
7053
7054    assert_eq!(host_branch.name(), branches[2]);
7055
7056    // Also try creating a new branch
7057    cx_b.update(|cx| {
7058        repo_b.update(cx, |repository, _cx| {
7059            repository.create_branch("totally-new-branch".to_string())
7060        })
7061    })
7062    .await
7063    .unwrap()
7064    .unwrap();
7065
7066    cx_b.update(|cx| {
7067        repo_b.update(cx, |repository, _cx| {
7068            repository.change_branch("totally-new-branch".to_string())
7069        })
7070    })
7071    .await
7072    .unwrap()
7073    .unwrap();
7074
7075    executor.run_until_parked();
7076
7077    let host_branch = cx_a.update(|cx| {
7078        project_a.update(cx, |project, cx| {
7079            project
7080                .repositories(cx)
7081                .values()
7082                .next()
7083                .unwrap()
7084                .read(cx)
7085                .branch
7086                .as_ref()
7087                .unwrap()
7088                .clone()
7089        })
7090    });
7091
7092    assert_eq!(host_branch.name(), "totally-new-branch");
7093}