integration_tests.rs

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