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