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