integration_tests.rs

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