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.has_update_observer()));
1382
1383    project_b
1384        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
1385        .await
1386        .unwrap();
1387
1388    // When client B leaves the room, the project becomes read-only.
1389    active_call_b
1390        .update(cx_b, |call, cx| call.hang_up(cx))
1391        .await
1392        .unwrap();
1393    executor.run_until_parked();
1394
1395    assert!(project_b.read_with(cx_b, |project, _| project.is_disconnected()));
1396
1397    // Client C opens the project.
1398    let project_c = client_c.build_dev_server_project(project_id, cx_c).await;
1399
1400    // When client A unshares the project, client C's project becomes read-only.
1401    project_a
1402        .update(cx_a, |project, cx| project.unshare(cx))
1403        .unwrap();
1404    executor.run_until_parked();
1405
1406    assert!(worktree_a.read_with(cx_a, |tree, _| !tree.has_update_observer()));
1407
1408    assert!(project_c.read_with(cx_c, |project, _| project.is_disconnected()));
1409
1410    // Client C can open the project again after client A re-shares.
1411    let project_id = active_call_a
1412        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
1413        .await
1414        .unwrap();
1415    let project_c2 = client_c.build_dev_server_project(project_id, cx_c).await;
1416    executor.run_until_parked();
1417
1418    assert!(worktree_a.read_with(cx_a, |tree, _| tree.has_update_observer()));
1419    project_c2
1420        .update(cx_c, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
1421        .await
1422        .unwrap();
1423
1424    // When client A (the host) leaves the room, the project gets unshared and guests are notified.
1425    active_call_a
1426        .update(cx_a, |call, cx| call.hang_up(cx))
1427        .await
1428        .unwrap();
1429    executor.run_until_parked();
1430
1431    project_a.read_with(cx_a, |project, _| assert!(!project.is_shared()));
1432
1433    project_c2.read_with(cx_c, |project, _| {
1434        assert!(project.is_disconnected());
1435        assert!(project.collaborators().is_empty());
1436    });
1437}
1438
1439#[gpui::test(iterations = 10)]
1440async fn test_project_reconnect(
1441    executor: BackgroundExecutor,
1442    cx_a: &mut TestAppContext,
1443    cx_b: &mut TestAppContext,
1444) {
1445    let mut server = TestServer::start(executor.clone()).await;
1446    let client_a = server.create_client(cx_a, "user_a").await;
1447    let client_b = server.create_client(cx_b, "user_b").await;
1448    server
1449        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
1450        .await;
1451
1452    cx_b.update(editor::init);
1453
1454    client_a
1455        .fs()
1456        .insert_tree(
1457            "/root-1",
1458            json!({
1459                "dir1": {
1460                    "a.txt": "a",
1461                    "b.txt": "b",
1462                    "subdir1": {
1463                        "c.txt": "c",
1464                        "d.txt": "d",
1465                        "e.txt": "e",
1466                    }
1467                },
1468                "dir2": {
1469                    "v.txt": "v",
1470                },
1471                "dir3": {
1472                    "w.txt": "w",
1473                    "x.txt": "x",
1474                    "y.txt": "y",
1475                },
1476                "dir4": {
1477                    "z.txt": "z",
1478                },
1479            }),
1480        )
1481        .await;
1482    client_a
1483        .fs()
1484        .insert_tree(
1485            "/root-2",
1486            json!({
1487                "2.txt": "2",
1488            }),
1489        )
1490        .await;
1491    client_a
1492        .fs()
1493        .insert_tree(
1494            "/root-3",
1495            json!({
1496                "3.txt": "3",
1497            }),
1498        )
1499        .await;
1500
1501    let active_call_a = cx_a.read(ActiveCall::global);
1502    let (project_a1, _) = client_a.build_local_project("/root-1/dir1", cx_a).await;
1503    let (project_a2, _) = client_a.build_local_project("/root-2", cx_a).await;
1504    let (project_a3, _) = client_a.build_local_project("/root-3", cx_a).await;
1505    let worktree_a1 = project_a1.read_with(cx_a, |project, _| project.worktrees().next().unwrap());
1506    let project1_id = active_call_a
1507        .update(cx_a, |call, cx| call.share_project(project_a1.clone(), cx))
1508        .await
1509        .unwrap();
1510    let project2_id = active_call_a
1511        .update(cx_a, |call, cx| call.share_project(project_a2.clone(), cx))
1512        .await
1513        .unwrap();
1514    let project3_id = active_call_a
1515        .update(cx_a, |call, cx| call.share_project(project_a3.clone(), cx))
1516        .await
1517        .unwrap();
1518
1519    let project_b1 = client_b.build_dev_server_project(project1_id, cx_b).await;
1520    let project_b2 = client_b.build_dev_server_project(project2_id, cx_b).await;
1521    let project_b3 = client_b.build_dev_server_project(project3_id, cx_b).await;
1522    executor.run_until_parked();
1523
1524    let worktree1_id = worktree_a1.read_with(cx_a, |worktree, _| {
1525        assert!(worktree.has_update_observer());
1526        worktree.id()
1527    });
1528    let (worktree_a2, _) = project_a1
1529        .update(cx_a, |p, cx| {
1530            p.find_or_create_local_worktree("/root-1/dir2", true, cx)
1531        })
1532        .await
1533        .unwrap();
1534    executor.run_until_parked();
1535
1536    let worktree2_id = worktree_a2.read_with(cx_a, |tree, _| {
1537        assert!(tree.has_update_observer());
1538        tree.id()
1539    });
1540    executor.run_until_parked();
1541
1542    project_b1.read_with(cx_b, |project, cx| {
1543        assert!(project.worktree_for_id(worktree2_id, cx).is_some())
1544    });
1545
1546    let buffer_a1 = project_a1
1547        .update(cx_a, |p, cx| p.open_buffer((worktree1_id, "a.txt"), cx))
1548        .await
1549        .unwrap();
1550    let buffer_b1 = project_b1
1551        .update(cx_b, |p, cx| p.open_buffer((worktree1_id, "a.txt"), cx))
1552        .await
1553        .unwrap();
1554
1555    // Drop client A's connection.
1556    server.forbid_connections();
1557    server.disconnect_client(client_a.peer_id().unwrap());
1558    executor.advance_clock(RECEIVE_TIMEOUT);
1559
1560    project_a1.read_with(cx_a, |project, _| {
1561        assert!(project.is_shared());
1562        assert_eq!(project.collaborators().len(), 1);
1563    });
1564
1565    project_b1.read_with(cx_b, |project, _| {
1566        assert!(!project.is_disconnected());
1567        assert_eq!(project.collaborators().len(), 1);
1568    });
1569
1570    worktree_a1.read_with(cx_a, |tree, _| assert!(tree.has_update_observer()));
1571
1572    // While client A is disconnected, add and remove files from client A's project.
1573    client_a
1574        .fs()
1575        .insert_tree(
1576            "/root-1/dir1/subdir2",
1577            json!({
1578                "f.txt": "f-contents",
1579                "g.txt": "g-contents",
1580                "h.txt": "h-contents",
1581                "i.txt": "i-contents",
1582            }),
1583        )
1584        .await;
1585    client_a
1586        .fs()
1587        .remove_dir(
1588            "/root-1/dir1/subdir1".as_ref(),
1589            RemoveOptions {
1590                recursive: true,
1591                ..Default::default()
1592            },
1593        )
1594        .await
1595        .unwrap();
1596
1597    // While client A is disconnected, add and remove worktrees from client A's project.
1598    project_a1.update(cx_a, |project, cx| {
1599        project.remove_worktree(worktree2_id, cx)
1600    });
1601    let (worktree_a3, _) = project_a1
1602        .update(cx_a, |p, cx| {
1603            p.find_or_create_local_worktree("/root-1/dir3", true, cx)
1604        })
1605        .await
1606        .unwrap();
1607    worktree_a3
1608        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
1609        .await;
1610
1611    let worktree3_id = worktree_a3.read_with(cx_a, |tree, _| {
1612        assert!(!tree.has_update_observer());
1613        tree.id()
1614    });
1615    executor.run_until_parked();
1616
1617    // While client A is disconnected, close project 2
1618    cx_a.update(|_| drop(project_a2));
1619
1620    // While client A is disconnected, mutate a buffer on both the host and the guest.
1621    buffer_a1.update(cx_a, |buf, cx| buf.edit([(0..0, "W")], None, cx));
1622    buffer_b1.update(cx_b, |buf, cx| buf.edit([(1..1, "Z")], None, cx));
1623    executor.run_until_parked();
1624
1625    // Client A reconnects. Their project is re-shared, and client B re-joins it.
1626    server.allow_connections();
1627    client_a
1628        .authenticate_and_connect(false, &cx_a.to_async())
1629        .await
1630        .unwrap();
1631    executor.run_until_parked();
1632
1633    project_a1.read_with(cx_a, |project, cx| {
1634        assert!(project.is_shared());
1635        assert!(worktree_a1.read(cx).has_update_observer());
1636        assert_eq!(
1637            worktree_a1
1638                .read(cx)
1639                .snapshot()
1640                .paths()
1641                .map(|p| p.to_str().unwrap())
1642                .collect::<Vec<_>>(),
1643            vec![
1644                "a.txt",
1645                "b.txt",
1646                "subdir2",
1647                "subdir2/f.txt",
1648                "subdir2/g.txt",
1649                "subdir2/h.txt",
1650                "subdir2/i.txt"
1651            ]
1652        );
1653        assert!(worktree_a3.read(cx).has_update_observer());
1654        assert_eq!(
1655            worktree_a3
1656                .read(cx)
1657                .snapshot()
1658                .paths()
1659                .map(|p| p.to_str().unwrap())
1660                .collect::<Vec<_>>(),
1661            vec!["w.txt", "x.txt", "y.txt"]
1662        );
1663    });
1664
1665    project_b1.read_with(cx_b, |project, cx| {
1666        assert!(!project.is_disconnected());
1667        assert_eq!(
1668            project
1669                .worktree_for_id(worktree1_id, cx)
1670                .unwrap()
1671                .read(cx)
1672                .snapshot()
1673                .paths()
1674                .map(|p| p.to_str().unwrap())
1675                .collect::<Vec<_>>(),
1676            vec![
1677                "a.txt",
1678                "b.txt",
1679                "subdir2",
1680                "subdir2/f.txt",
1681                "subdir2/g.txt",
1682                "subdir2/h.txt",
1683                "subdir2/i.txt"
1684            ]
1685        );
1686        assert!(project.worktree_for_id(worktree2_id, cx).is_none());
1687        assert_eq!(
1688            project
1689                .worktree_for_id(worktree3_id, cx)
1690                .unwrap()
1691                .read(cx)
1692                .snapshot()
1693                .paths()
1694                .map(|p| p.to_str().unwrap())
1695                .collect::<Vec<_>>(),
1696            vec!["w.txt", "x.txt", "y.txt"]
1697        );
1698    });
1699
1700    project_b2.read_with(cx_b, |project, _| assert!(project.is_disconnected()));
1701
1702    project_b3.read_with(cx_b, |project, _| assert!(!project.is_disconnected()));
1703
1704    buffer_a1.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "WaZ"));
1705
1706    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "WaZ"));
1707
1708    // Drop client B's connection.
1709    server.forbid_connections();
1710    server.disconnect_client(client_b.peer_id().unwrap());
1711    executor.advance_clock(RECEIVE_TIMEOUT);
1712
1713    // While client B is disconnected, add and remove files from client A's project
1714    client_a
1715        .fs()
1716        .insert_file("/root-1/dir1/subdir2/j.txt", "j-contents".into())
1717        .await;
1718    client_a
1719        .fs()
1720        .remove_file("/root-1/dir1/subdir2/i.txt".as_ref(), Default::default())
1721        .await
1722        .unwrap();
1723
1724    // While client B is disconnected, add and remove worktrees from client A's project.
1725    let (worktree_a4, _) = project_a1
1726        .update(cx_a, |p, cx| {
1727            p.find_or_create_local_worktree("/root-1/dir4", true, cx)
1728        })
1729        .await
1730        .unwrap();
1731    executor.run_until_parked();
1732
1733    let worktree4_id = worktree_a4.read_with(cx_a, |tree, _| {
1734        assert!(tree.has_update_observer());
1735        tree.id()
1736    });
1737    project_a1.update(cx_a, |project, cx| {
1738        project.remove_worktree(worktree3_id, cx)
1739    });
1740    executor.run_until_parked();
1741
1742    // While client B is disconnected, mutate a buffer on both the host and the guest.
1743    buffer_a1.update(cx_a, |buf, cx| buf.edit([(1..1, "X")], None, cx));
1744    buffer_b1.update(cx_b, |buf, cx| buf.edit([(2..2, "Y")], None, cx));
1745    executor.run_until_parked();
1746
1747    // While disconnected, close project 3
1748    cx_a.update(|_| drop(project_a3));
1749
1750    // Client B reconnects. They re-join the room and the remaining shared project.
1751    server.allow_connections();
1752    client_b
1753        .authenticate_and_connect(false, &cx_b.to_async())
1754        .await
1755        .unwrap();
1756    executor.run_until_parked();
1757
1758    project_b1.read_with(cx_b, |project, cx| {
1759        assert!(!project.is_disconnected());
1760        assert_eq!(
1761            project
1762                .worktree_for_id(worktree1_id, cx)
1763                .unwrap()
1764                .read(cx)
1765                .snapshot()
1766                .paths()
1767                .map(|p| p.to_str().unwrap())
1768                .collect::<Vec<_>>(),
1769            vec![
1770                "a.txt",
1771                "b.txt",
1772                "subdir2",
1773                "subdir2/f.txt",
1774                "subdir2/g.txt",
1775                "subdir2/h.txt",
1776                "subdir2/j.txt"
1777            ]
1778        );
1779        assert!(project.worktree_for_id(worktree2_id, cx).is_none());
1780        assert_eq!(
1781            project
1782                .worktree_for_id(worktree4_id, cx)
1783                .unwrap()
1784                .read(cx)
1785                .snapshot()
1786                .paths()
1787                .map(|p| p.to_str().unwrap())
1788                .collect::<Vec<_>>(),
1789            vec!["z.txt"]
1790        );
1791    });
1792
1793    project_b3.read_with(cx_b, |project, _| assert!(project.is_disconnected()));
1794
1795    buffer_a1.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "WXaYZ"));
1796
1797    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "WXaYZ"));
1798}
1799
1800#[gpui::test(iterations = 10)]
1801async fn test_active_call_events(
1802    executor: BackgroundExecutor,
1803    cx_a: &mut TestAppContext,
1804    cx_b: &mut TestAppContext,
1805) {
1806    let mut server = TestServer::start(executor.clone()).await;
1807    let client_a = server.create_client(cx_a, "user_a").await;
1808    let client_b = server.create_client(cx_b, "user_b").await;
1809    client_a.fs().insert_tree("/a", json!({})).await;
1810    client_b.fs().insert_tree("/b", json!({})).await;
1811
1812    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
1813    let (project_b, _) = client_b.build_local_project("/b", cx_b).await;
1814
1815    server
1816        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
1817        .await;
1818    let active_call_a = cx_a.read(ActiveCall::global);
1819    let active_call_b = cx_b.read(ActiveCall::global);
1820
1821    let events_a = active_call_events(cx_a);
1822    let events_b = active_call_events(cx_b);
1823
1824    let project_a_id = active_call_a
1825        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
1826        .await
1827        .unwrap();
1828    executor.run_until_parked();
1829    assert_eq!(mem::take(&mut *events_a.borrow_mut()), vec![]);
1830    assert_eq!(
1831        mem::take(&mut *events_b.borrow_mut()),
1832        vec![room::Event::RemoteProjectShared {
1833            owner: Arc::new(User {
1834                id: client_a.user_id().unwrap(),
1835                github_login: "user_a".to_string(),
1836                avatar_uri: "avatar_a".into(),
1837            }),
1838            project_id: project_a_id,
1839            worktree_root_names: vec!["a".to_string()],
1840        }]
1841    );
1842
1843    let project_b_id = active_call_b
1844        .update(cx_b, |call, cx| call.share_project(project_b.clone(), cx))
1845        .await
1846        .unwrap();
1847    executor.run_until_parked();
1848    assert_eq!(
1849        mem::take(&mut *events_a.borrow_mut()),
1850        vec![room::Event::RemoteProjectShared {
1851            owner: Arc::new(User {
1852                id: client_b.user_id().unwrap(),
1853                github_login: "user_b".to_string(),
1854                avatar_uri: "avatar_b".into(),
1855            }),
1856            project_id: project_b_id,
1857            worktree_root_names: vec!["b".to_string()]
1858        }]
1859    );
1860    assert_eq!(mem::take(&mut *events_b.borrow_mut()), vec![]);
1861
1862    // Sharing a project twice is idempotent.
1863    let project_b_id_2 = active_call_b
1864        .update(cx_b, |call, cx| call.share_project(project_b.clone(), cx))
1865        .await
1866        .unwrap();
1867    assert_eq!(project_b_id_2, project_b_id);
1868    executor.run_until_parked();
1869    assert_eq!(mem::take(&mut *events_a.borrow_mut()), vec![]);
1870    assert_eq!(mem::take(&mut *events_b.borrow_mut()), vec![]);
1871
1872    // Unsharing a project should dispatch the RemoteProjectUnshared event.
1873    active_call_a
1874        .update(cx_a, |call, cx| call.hang_up(cx))
1875        .await
1876        .unwrap();
1877    executor.run_until_parked();
1878
1879    assert_eq!(
1880        mem::take(&mut *events_a.borrow_mut()),
1881        vec![room::Event::RoomLeft { channel_id: None }]
1882    );
1883    assert_eq!(
1884        mem::take(&mut *events_b.borrow_mut()),
1885        vec![room::Event::RemoteProjectUnshared {
1886            project_id: project_a_id,
1887        }]
1888    );
1889}
1890
1891fn active_call_events(cx: &mut TestAppContext) -> Rc<RefCell<Vec<room::Event>>> {
1892    let events = Rc::new(RefCell::new(Vec::new()));
1893    let active_call = cx.read(ActiveCall::global);
1894    cx.update({
1895        let events = events.clone();
1896        |cx| {
1897            cx.subscribe(&active_call, move |_, event, _| {
1898                events.borrow_mut().push(event.clone())
1899            })
1900            .detach()
1901        }
1902    });
1903    events
1904}
1905
1906#[gpui::test]
1907async fn test_mute_deafen(
1908    executor: BackgroundExecutor,
1909    cx_a: &mut TestAppContext,
1910    cx_b: &mut TestAppContext,
1911    cx_c: &mut TestAppContext,
1912) {
1913    let mut server = TestServer::start(executor.clone()).await;
1914    let client_a = server.create_client(cx_a, "user_a").await;
1915    let client_b = server.create_client(cx_b, "user_b").await;
1916    let client_c = server.create_client(cx_c, "user_c").await;
1917
1918    server
1919        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
1920        .await;
1921
1922    let active_call_a = cx_a.read(ActiveCall::global);
1923    let active_call_b = cx_b.read(ActiveCall::global);
1924    let active_call_c = cx_c.read(ActiveCall::global);
1925
1926    // User A calls user B, B answers.
1927    active_call_a
1928        .update(cx_a, |call, cx| {
1929            call.invite(client_b.user_id().unwrap(), None, cx)
1930        })
1931        .await
1932        .unwrap();
1933    executor.run_until_parked();
1934    active_call_b
1935        .update(cx_b, |call, cx| call.accept_incoming(cx))
1936        .await
1937        .unwrap();
1938    executor.run_until_parked();
1939
1940    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
1941    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
1942
1943    room_a.read_with(cx_a, |room, _| assert!(!room.is_muted()));
1944    room_b.read_with(cx_b, |room, _| assert!(!room.is_muted()));
1945
1946    // Users A and B are both muted.
1947    assert_eq!(
1948        participant_audio_state(&room_a, cx_a),
1949        &[ParticipantAudioState {
1950            user_id: client_b.user_id().unwrap(),
1951            is_muted: false,
1952            audio_tracks_playing: vec![true],
1953        }]
1954    );
1955    assert_eq!(
1956        participant_audio_state(&room_b, cx_b),
1957        &[ParticipantAudioState {
1958            user_id: client_a.user_id().unwrap(),
1959            is_muted: false,
1960            audio_tracks_playing: vec![true],
1961        }]
1962    );
1963
1964    // User A mutes
1965    room_a.update(cx_a, |room, cx| room.toggle_mute(cx));
1966    executor.run_until_parked();
1967
1968    // User A hears user B, but B doesn't hear A.
1969    room_a.read_with(cx_a, |room, _| assert!(room.is_muted()));
1970    room_b.read_with(cx_b, |room, _| assert!(!room.is_muted()));
1971    assert_eq!(
1972        participant_audio_state(&room_a, cx_a),
1973        &[ParticipantAudioState {
1974            user_id: client_b.user_id().unwrap(),
1975            is_muted: false,
1976            audio_tracks_playing: vec![true],
1977        }]
1978    );
1979    assert_eq!(
1980        participant_audio_state(&room_b, cx_b),
1981        &[ParticipantAudioState {
1982            user_id: client_a.user_id().unwrap(),
1983            is_muted: true,
1984            audio_tracks_playing: vec![true],
1985        }]
1986    );
1987
1988    // User A deafens
1989    room_a.update(cx_a, |room, cx| room.toggle_deafen(cx));
1990    executor.run_until_parked();
1991
1992    // User A does not hear user B.
1993    room_a.read_with(cx_a, |room, _| assert!(room.is_muted()));
1994    room_b.read_with(cx_b, |room, _| assert!(!room.is_muted()));
1995    assert_eq!(
1996        participant_audio_state(&room_a, cx_a),
1997        &[ParticipantAudioState {
1998            user_id: client_b.user_id().unwrap(),
1999            is_muted: false,
2000            audio_tracks_playing: vec![false],
2001        }]
2002    );
2003    assert_eq!(
2004        participant_audio_state(&room_b, cx_b),
2005        &[ParticipantAudioState {
2006            user_id: client_a.user_id().unwrap(),
2007            is_muted: true,
2008            audio_tracks_playing: vec![true],
2009        }]
2010    );
2011
2012    // User B calls user C, C joins.
2013    active_call_b
2014        .update(cx_b, |call, cx| {
2015            call.invite(client_c.user_id().unwrap(), None, cx)
2016        })
2017        .await
2018        .unwrap();
2019    executor.run_until_parked();
2020    active_call_c
2021        .update(cx_c, |call, cx| call.accept_incoming(cx))
2022        .await
2023        .unwrap();
2024    executor.run_until_parked();
2025
2026    // User A does not hear users B or C.
2027    assert_eq!(
2028        participant_audio_state(&room_a, cx_a),
2029        &[
2030            ParticipantAudioState {
2031                user_id: client_b.user_id().unwrap(),
2032                is_muted: false,
2033                audio_tracks_playing: vec![false],
2034            },
2035            ParticipantAudioState {
2036                user_id: client_c.user_id().unwrap(),
2037                is_muted: false,
2038                audio_tracks_playing: vec![false],
2039            }
2040        ]
2041    );
2042    assert_eq!(
2043        participant_audio_state(&room_b, cx_b),
2044        &[
2045            ParticipantAudioState {
2046                user_id: client_a.user_id().unwrap(),
2047                is_muted: true,
2048                audio_tracks_playing: vec![true],
2049            },
2050            ParticipantAudioState {
2051                user_id: client_c.user_id().unwrap(),
2052                is_muted: false,
2053                audio_tracks_playing: vec![true],
2054            }
2055        ]
2056    );
2057
2058    #[derive(PartialEq, Eq, Debug)]
2059    struct ParticipantAudioState {
2060        user_id: u64,
2061        is_muted: bool,
2062        audio_tracks_playing: Vec<bool>,
2063    }
2064
2065    fn participant_audio_state(
2066        room: &Model<Room>,
2067        cx: &TestAppContext,
2068    ) -> Vec<ParticipantAudioState> {
2069        room.read_with(cx, |room, _| {
2070            room.remote_participants()
2071                .iter()
2072                .map(|(user_id, participant)| ParticipantAudioState {
2073                    user_id: *user_id,
2074                    is_muted: participant.muted,
2075                    audio_tracks_playing: participant
2076                        .audio_tracks
2077                        .values()
2078                        .map(|track| track.is_playing())
2079                        .collect(),
2080                })
2081                .collect::<Vec<_>>()
2082        })
2083    }
2084}
2085
2086#[gpui::test(iterations = 10)]
2087async fn test_room_location(
2088    executor: BackgroundExecutor,
2089    cx_a: &mut TestAppContext,
2090    cx_b: &mut TestAppContext,
2091) {
2092    let mut server = TestServer::start(executor.clone()).await;
2093    let client_a = server.create_client(cx_a, "user_a").await;
2094    let client_b = server.create_client(cx_b, "user_b").await;
2095    client_a.fs().insert_tree("/a", json!({})).await;
2096    client_b.fs().insert_tree("/b", json!({})).await;
2097
2098    let active_call_a = cx_a.read(ActiveCall::global);
2099    let active_call_b = cx_b.read(ActiveCall::global);
2100
2101    let a_notified = Rc::new(Cell::new(false));
2102    cx_a.update({
2103        let notified = a_notified.clone();
2104        |cx| {
2105            cx.observe(&active_call_a, move |_, _| notified.set(true))
2106                .detach()
2107        }
2108    });
2109
2110    let b_notified = Rc::new(Cell::new(false));
2111    cx_b.update({
2112        let b_notified = b_notified.clone();
2113        |cx| {
2114            cx.observe(&active_call_b, move |_, _| b_notified.set(true))
2115                .detach()
2116        }
2117    });
2118
2119    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
2120    active_call_a
2121        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
2122        .await
2123        .unwrap();
2124    let (project_b, _) = client_b.build_local_project("/b", cx_b).await;
2125
2126    server
2127        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2128        .await;
2129
2130    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
2131
2132    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
2133    executor.run_until_parked();
2134    assert!(a_notified.take());
2135    assert_eq!(
2136        participant_locations(&room_a, cx_a),
2137        vec![("user_b".to_string(), ParticipantLocation::External)]
2138    );
2139    assert!(b_notified.take());
2140    assert_eq!(
2141        participant_locations(&room_b, cx_b),
2142        vec![("user_a".to_string(), ParticipantLocation::UnsharedProject)]
2143    );
2144
2145    let project_a_id = active_call_a
2146        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2147        .await
2148        .unwrap();
2149    executor.run_until_parked();
2150    assert!(a_notified.take());
2151    assert_eq!(
2152        participant_locations(&room_a, cx_a),
2153        vec![("user_b".to_string(), ParticipantLocation::External)]
2154    );
2155    assert!(b_notified.take());
2156    assert_eq!(
2157        participant_locations(&room_b, cx_b),
2158        vec![(
2159            "user_a".to_string(),
2160            ParticipantLocation::SharedProject {
2161                project_id: project_a_id
2162            }
2163        )]
2164    );
2165
2166    let project_b_id = active_call_b
2167        .update(cx_b, |call, cx| call.share_project(project_b.clone(), cx))
2168        .await
2169        .unwrap();
2170    executor.run_until_parked();
2171    assert!(a_notified.take());
2172    assert_eq!(
2173        participant_locations(&room_a, cx_a),
2174        vec![("user_b".to_string(), ParticipantLocation::External)]
2175    );
2176    assert!(b_notified.take());
2177    assert_eq!(
2178        participant_locations(&room_b, cx_b),
2179        vec![(
2180            "user_a".to_string(),
2181            ParticipantLocation::SharedProject {
2182                project_id: project_a_id
2183            }
2184        )]
2185    );
2186
2187    active_call_b
2188        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
2189        .await
2190        .unwrap();
2191    executor.run_until_parked();
2192    assert!(a_notified.take());
2193    assert_eq!(
2194        participant_locations(&room_a, cx_a),
2195        vec![(
2196            "user_b".to_string(),
2197            ParticipantLocation::SharedProject {
2198                project_id: project_b_id
2199            }
2200        )]
2201    );
2202    assert!(b_notified.take());
2203    assert_eq!(
2204        participant_locations(&room_b, cx_b),
2205        vec![(
2206            "user_a".to_string(),
2207            ParticipantLocation::SharedProject {
2208                project_id: project_a_id
2209            }
2210        )]
2211    );
2212
2213    active_call_b
2214        .update(cx_b, |call, cx| call.set_location(None, cx))
2215        .await
2216        .unwrap();
2217    executor.run_until_parked();
2218    assert!(a_notified.take());
2219    assert_eq!(
2220        participant_locations(&room_a, cx_a),
2221        vec![("user_b".to_string(), ParticipantLocation::External)]
2222    );
2223    assert!(b_notified.take());
2224    assert_eq!(
2225        participant_locations(&room_b, cx_b),
2226        vec![(
2227            "user_a".to_string(),
2228            ParticipantLocation::SharedProject {
2229                project_id: project_a_id
2230            }
2231        )]
2232    );
2233
2234    fn participant_locations(
2235        room: &Model<Room>,
2236        cx: &TestAppContext,
2237    ) -> Vec<(String, ParticipantLocation)> {
2238        room.read_with(cx, |room, _| {
2239            room.remote_participants()
2240                .values()
2241                .map(|participant| {
2242                    (
2243                        participant.user.github_login.to_string(),
2244                        participant.location,
2245                    )
2246                })
2247                .collect()
2248        })
2249    }
2250}
2251
2252#[gpui::test(iterations = 10)]
2253async fn test_propagate_saves_and_fs_changes(
2254    executor: BackgroundExecutor,
2255    cx_a: &mut TestAppContext,
2256    cx_b: &mut TestAppContext,
2257    cx_c: &mut TestAppContext,
2258) {
2259    let mut server = TestServer::start(executor.clone()).await;
2260    let client_a = server.create_client(cx_a, "user_a").await;
2261    let client_b = server.create_client(cx_b, "user_b").await;
2262    let client_c = server.create_client(cx_c, "user_c").await;
2263
2264    server
2265        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
2266        .await;
2267    let active_call_a = cx_a.read(ActiveCall::global);
2268
2269    let rust = Arc::new(Language::new(
2270        LanguageConfig {
2271            name: "Rust".into(),
2272            matcher: LanguageMatcher {
2273                path_suffixes: vec!["rs".to_string()],
2274                ..Default::default()
2275            },
2276            ..Default::default()
2277        },
2278        Some(tree_sitter_rust::language()),
2279    ));
2280    let javascript = Arc::new(Language::new(
2281        LanguageConfig {
2282            name: "JavaScript".into(),
2283            matcher: LanguageMatcher {
2284                path_suffixes: vec!["js".to_string()],
2285                ..Default::default()
2286            },
2287            ..Default::default()
2288        },
2289        Some(tree_sitter_rust::language()),
2290    ));
2291    for client in [&client_a, &client_b, &client_c] {
2292        client.language_registry().add(rust.clone());
2293        client.language_registry().add(javascript.clone());
2294    }
2295
2296    client_a
2297        .fs()
2298        .insert_tree(
2299            "/a",
2300            json!({
2301                "file1.rs": "",
2302                "file2": ""
2303            }),
2304        )
2305        .await;
2306    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
2307
2308    let worktree_a = project_a.read_with(cx_a, |p, _| p.worktrees().next().unwrap());
2309    let project_id = active_call_a
2310        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2311        .await
2312        .unwrap();
2313
2314    // Join that worktree as clients B and C.
2315    let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
2316    let project_c = client_c.build_dev_server_project(project_id, cx_c).await;
2317
2318    let worktree_b = project_b.read_with(cx_b, |p, _| p.worktrees().next().unwrap());
2319
2320    let worktree_c = project_c.read_with(cx_c, |p, _| p.worktrees().next().unwrap());
2321
2322    // Open and edit a buffer as both guests B and C.
2323    let buffer_b = project_b
2324        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "file1.rs"), cx))
2325        .await
2326        .unwrap();
2327    let buffer_c = project_c
2328        .update(cx_c, |p, cx| p.open_buffer((worktree_id, "file1.rs"), cx))
2329        .await
2330        .unwrap();
2331
2332    buffer_b.read_with(cx_b, |buffer, _| {
2333        assert_eq!(&*buffer.language().unwrap().name(), "Rust");
2334    });
2335
2336    buffer_c.read_with(cx_c, |buffer, _| {
2337        assert_eq!(&*buffer.language().unwrap().name(), "Rust");
2338    });
2339    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "i-am-b, ")], None, cx));
2340    buffer_c.update(cx_c, |buf, cx| buf.edit([(0..0, "i-am-c, ")], None, cx));
2341
2342    // Open and edit that buffer as the host.
2343    let buffer_a = project_a
2344        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "file1.rs"), cx))
2345        .await
2346        .unwrap();
2347
2348    executor.run_until_parked();
2349
2350    buffer_a.read_with(cx_a, |buf, _| assert_eq!(buf.text(), "i-am-c, i-am-b, "));
2351    buffer_a.update(cx_a, |buf, cx| {
2352        buf.edit([(buf.len()..buf.len(), "i-am-a")], None, cx)
2353    });
2354
2355    executor.run_until_parked();
2356
2357    buffer_a.read_with(cx_a, |buf, _| {
2358        assert_eq!(buf.text(), "i-am-c, i-am-b, i-am-a");
2359    });
2360
2361    buffer_b.read_with(cx_b, |buf, _| {
2362        assert_eq!(buf.text(), "i-am-c, i-am-b, i-am-a");
2363    });
2364
2365    buffer_c.read_with(cx_c, |buf, _| {
2366        assert_eq!(buf.text(), "i-am-c, i-am-b, i-am-a");
2367    });
2368
2369    // Edit the buffer as the host and concurrently save as guest B.
2370    let save_b = project_b.update(cx_b, |project, cx| {
2371        project.save_buffer(buffer_b.clone(), cx)
2372    });
2373    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "hi-a, ")], None, cx));
2374    save_b.await.unwrap();
2375    assert_eq!(
2376        client_a.fs().load("/a/file1.rs".as_ref()).await.unwrap(),
2377        "hi-a, i-am-c, i-am-b, i-am-a"
2378    );
2379
2380    executor.run_until_parked();
2381
2382    buffer_a.read_with(cx_a, |buf, _| assert!(!buf.is_dirty()));
2383
2384    buffer_b.read_with(cx_b, |buf, _| assert!(!buf.is_dirty()));
2385
2386    buffer_c.read_with(cx_c, |buf, _| assert!(!buf.is_dirty()));
2387
2388    // Make changes on host's file system, see those changes on guest worktrees.
2389    client_a
2390        .fs()
2391        .rename(
2392            "/a/file1.rs".as_ref(),
2393            "/a/file1.js".as_ref(),
2394            Default::default(),
2395        )
2396        .await
2397        .unwrap();
2398    client_a
2399        .fs()
2400        .rename("/a/file2".as_ref(), "/a/file3".as_ref(), Default::default())
2401        .await
2402        .unwrap();
2403    client_a.fs().insert_file("/a/file4", "4".into()).await;
2404    executor.run_until_parked();
2405
2406    worktree_a.read_with(cx_a, |tree, _| {
2407        assert_eq!(
2408            tree.paths()
2409                .map(|p| p.to_string_lossy())
2410                .collect::<Vec<_>>(),
2411            ["file1.js", "file3", "file4"]
2412        )
2413    });
2414
2415    worktree_b.read_with(cx_b, |tree, _| {
2416        assert_eq!(
2417            tree.paths()
2418                .map(|p| p.to_string_lossy())
2419                .collect::<Vec<_>>(),
2420            ["file1.js", "file3", "file4"]
2421        )
2422    });
2423
2424    worktree_c.read_with(cx_c, |tree, _| {
2425        assert_eq!(
2426            tree.paths()
2427                .map(|p| p.to_string_lossy())
2428                .collect::<Vec<_>>(),
2429            ["file1.js", "file3", "file4"]
2430        )
2431    });
2432
2433    // Ensure buffer files are updated as well.
2434
2435    buffer_a.read_with(cx_a, |buffer, _| {
2436        assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
2437        assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
2438    });
2439
2440    buffer_b.read_with(cx_b, |buffer, _| {
2441        assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
2442        assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
2443    });
2444
2445    buffer_c.read_with(cx_c, |buffer, _| {
2446        assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
2447        assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
2448    });
2449
2450    let new_buffer_a = project_a
2451        .update(cx_a, |p, cx| p.create_buffer(cx))
2452        .await
2453        .unwrap();
2454
2455    let new_buffer_id = new_buffer_a.read_with(cx_a, |buffer, _| buffer.remote_id());
2456    let new_buffer_b = project_b
2457        .update(cx_b, |p, cx| p.open_buffer_by_id(new_buffer_id, cx))
2458        .await
2459        .unwrap();
2460
2461    new_buffer_b.read_with(cx_b, |buffer, _| {
2462        assert!(buffer.file().is_none());
2463    });
2464
2465    new_buffer_a.update(cx_a, |buffer, cx| {
2466        buffer.edit([(0..0, "ok")], None, cx);
2467    });
2468    project_a
2469        .update(cx_a, |project, cx| {
2470            let path = ProjectPath {
2471                path: Arc::from(Path::new("file3.rs")),
2472                worktree_id: worktree_a.read(cx).id(),
2473            };
2474
2475            project.save_buffer_as(new_buffer_a.clone(), path, cx)
2476        })
2477        .await
2478        .unwrap();
2479
2480    executor.run_until_parked();
2481
2482    new_buffer_b.read_with(cx_b, |buffer_b, _| {
2483        assert_eq!(
2484            buffer_b.file().unwrap().path().as_ref(),
2485            Path::new("file3.rs")
2486        );
2487
2488        new_buffer_a.read_with(cx_a, |buffer_a, _| {
2489            assert_eq!(buffer_b.saved_mtime(), buffer_a.saved_mtime());
2490            assert_eq!(buffer_b.saved_version(), buffer_a.saved_version());
2491        });
2492    });
2493}
2494
2495#[gpui::test(iterations = 10)]
2496async fn test_git_diff_base_change(
2497    executor: BackgroundExecutor,
2498    cx_a: &mut TestAppContext,
2499    cx_b: &mut TestAppContext,
2500) {
2501    let mut server = TestServer::start(executor.clone()).await;
2502    let client_a = server.create_client(cx_a, "user_a").await;
2503    let client_b = server.create_client(cx_b, "user_b").await;
2504    server
2505        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2506        .await;
2507    let active_call_a = cx_a.read(ActiveCall::global);
2508
2509    client_a
2510        .fs()
2511        .insert_tree(
2512            "/dir",
2513            json!({
2514            ".git": {},
2515            "sub": {
2516                ".git": {},
2517                "b.txt": "
2518                    one
2519                    two
2520                    three
2521                ".unindent(),
2522            },
2523            "a.txt": "
2524                    one
2525                    two
2526                    three
2527                ".unindent(),
2528            }),
2529        )
2530        .await;
2531
2532    let (project_local, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2533    let project_id = active_call_a
2534        .update(cx_a, |call, cx| {
2535            call.share_project(project_local.clone(), cx)
2536        })
2537        .await
2538        .unwrap();
2539
2540    let project_remote = client_b.build_dev_server_project(project_id, cx_b).await;
2541
2542    let diff_base = "
2543        one
2544        three
2545    "
2546    .unindent();
2547
2548    let new_diff_base = "
2549        one
2550        two
2551    "
2552    .unindent();
2553
2554    client_a.fs().set_index_for_repo(
2555        Path::new("/dir/.git"),
2556        &[(Path::new("a.txt"), diff_base.clone())],
2557    );
2558
2559    // Create the buffer
2560    let buffer_local_a = project_local
2561        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2562        .await
2563        .unwrap();
2564
2565    // Wait for it to catch up to the new diff
2566    executor.run_until_parked();
2567
2568    // Smoke test diffing
2569
2570    buffer_local_a.read_with(cx_a, |buffer, _| {
2571        assert_eq!(
2572            buffer.diff_base().map(|rope| rope.to_string()).as_deref(),
2573            Some(diff_base.as_str())
2574        );
2575        git::diff::assert_hunks(
2576            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2577            &buffer,
2578            &diff_base,
2579            &[(1..2, "", "two\n")],
2580        );
2581    });
2582
2583    // Create remote buffer
2584    let buffer_remote_a = project_remote
2585        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2586        .await
2587        .unwrap();
2588
2589    // Wait remote buffer to catch up to the new diff
2590    executor.run_until_parked();
2591
2592    // Smoke test diffing
2593
2594    buffer_remote_a.read_with(cx_b, |buffer, _| {
2595        assert_eq!(
2596            buffer.diff_base().map(|rope| rope.to_string()).as_deref(),
2597            Some(diff_base.as_str())
2598        );
2599        git::diff::assert_hunks(
2600            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2601            &buffer,
2602            &diff_base,
2603            &[(1..2, "", "two\n")],
2604        );
2605    });
2606
2607    client_a.fs().set_index_for_repo(
2608        Path::new("/dir/.git"),
2609        &[(Path::new("a.txt"), new_diff_base.clone())],
2610    );
2611
2612    // Wait for buffer_local_a to receive it
2613    executor.run_until_parked();
2614
2615    // Smoke test new diffing
2616
2617    buffer_local_a.read_with(cx_a, |buffer, _| {
2618        assert_eq!(
2619            buffer.diff_base().map(|rope| rope.to_string()).as_deref(),
2620            Some(new_diff_base.as_str())
2621        );
2622
2623        git::diff::assert_hunks(
2624            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2625            &buffer,
2626            &diff_base,
2627            &[(2..3, "", "three\n")],
2628        );
2629    });
2630
2631    // Smoke test B
2632
2633    buffer_remote_a.read_with(cx_b, |buffer, _| {
2634        assert_eq!(
2635            buffer.diff_base().map(|rope| rope.to_string()).as_deref(),
2636            Some(new_diff_base.as_str())
2637        );
2638        git::diff::assert_hunks(
2639            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2640            &buffer,
2641            &diff_base,
2642            &[(2..3, "", "three\n")],
2643        );
2644    });
2645
2646    //Nested git dir
2647
2648    let diff_base = "
2649        one
2650        three
2651    "
2652    .unindent();
2653
2654    let new_diff_base = "
2655        one
2656        two
2657    "
2658    .unindent();
2659
2660    client_a.fs().set_index_for_repo(
2661        Path::new("/dir/sub/.git"),
2662        &[(Path::new("b.txt"), diff_base.clone())],
2663    );
2664
2665    // Create the buffer
2666    let buffer_local_b = project_local
2667        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "sub/b.txt"), cx))
2668        .await
2669        .unwrap();
2670
2671    // Wait for it to catch up to the new diff
2672    executor.run_until_parked();
2673
2674    // Smoke test diffing
2675
2676    buffer_local_b.read_with(cx_a, |buffer, _| {
2677        assert_eq!(
2678            buffer.diff_base().map(|rope| rope.to_string()).as_deref(),
2679            Some(diff_base.as_str())
2680        );
2681        git::diff::assert_hunks(
2682            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2683            &buffer,
2684            &diff_base,
2685            &[(1..2, "", "two\n")],
2686        );
2687    });
2688
2689    // Create remote buffer
2690    let buffer_remote_b = project_remote
2691        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "sub/b.txt"), cx))
2692        .await
2693        .unwrap();
2694
2695    // Wait remote buffer to catch up to the new diff
2696    executor.run_until_parked();
2697
2698    // Smoke test diffing
2699
2700    buffer_remote_b.read_with(cx_b, |buffer, _| {
2701        assert_eq!(
2702            buffer.diff_base().map(|rope| rope.to_string()).as_deref(),
2703            Some(diff_base.as_str())
2704        );
2705        git::diff::assert_hunks(
2706            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2707            &buffer,
2708            &diff_base,
2709            &[(1..2, "", "two\n")],
2710        );
2711    });
2712
2713    client_a.fs().set_index_for_repo(
2714        Path::new("/dir/sub/.git"),
2715        &[(Path::new("b.txt"), new_diff_base.clone())],
2716    );
2717
2718    // Wait for buffer_local_b to receive it
2719    executor.run_until_parked();
2720
2721    // Smoke test new diffing
2722
2723    buffer_local_b.read_with(cx_a, |buffer, _| {
2724        assert_eq!(
2725            buffer.diff_base().map(|rope| rope.to_string()).as_deref(),
2726            Some(new_diff_base.as_str())
2727        );
2728        println!("{:?}", buffer.as_rope().to_string());
2729        println!("{:?}", buffer.diff_base());
2730        println!(
2731            "{:?}",
2732            buffer
2733                .snapshot()
2734                .git_diff_hunks_in_row_range(0..4)
2735                .collect::<Vec<_>>()
2736        );
2737
2738        git::diff::assert_hunks(
2739            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2740            &buffer,
2741            &diff_base,
2742            &[(2..3, "", "three\n")],
2743        );
2744    });
2745
2746    // Smoke test B
2747
2748    buffer_remote_b.read_with(cx_b, |buffer, _| {
2749        assert_eq!(
2750            buffer.diff_base().map(|rope| rope.to_string()).as_deref(),
2751            Some(new_diff_base.as_str())
2752        );
2753        git::diff::assert_hunks(
2754            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2755            &buffer,
2756            &diff_base,
2757            &[(2..3, "", "three\n")],
2758        );
2759    });
2760}
2761
2762#[gpui::test]
2763async fn test_git_branch_name(
2764    executor: BackgroundExecutor,
2765    cx_a: &mut TestAppContext,
2766    cx_b: &mut TestAppContext,
2767    cx_c: &mut TestAppContext,
2768) {
2769    let mut server = TestServer::start(executor.clone()).await;
2770    let client_a = server.create_client(cx_a, "user_a").await;
2771    let client_b = server.create_client(cx_b, "user_b").await;
2772    let client_c = server.create_client(cx_c, "user_c").await;
2773    server
2774        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
2775        .await;
2776    let active_call_a = cx_a.read(ActiveCall::global);
2777
2778    client_a
2779        .fs()
2780        .insert_tree(
2781            "/dir",
2782            json!({
2783            ".git": {},
2784            }),
2785        )
2786        .await;
2787
2788    let (project_local, _worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2789    let project_id = active_call_a
2790        .update(cx_a, |call, cx| {
2791            call.share_project(project_local.clone(), cx)
2792        })
2793        .await
2794        .unwrap();
2795
2796    let project_remote = client_b.build_dev_server_project(project_id, cx_b).await;
2797    client_a
2798        .fs()
2799        .set_branch_name(Path::new("/dir/.git"), Some("branch-1"));
2800
2801    // Wait for it to catch up to the new branch
2802    executor.run_until_parked();
2803
2804    #[track_caller]
2805    fn assert_branch(branch_name: Option<impl Into<String>>, project: &Project, cx: &AppContext) {
2806        let branch_name = branch_name.map(Into::into);
2807        let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
2808        assert_eq!(worktrees.len(), 1);
2809        let worktree = worktrees[0].clone();
2810        let root_entry = worktree.read(cx).snapshot().root_git_entry().unwrap();
2811        assert_eq!(root_entry.branch(), branch_name.map(Into::into));
2812    }
2813
2814    // Smoke test branch reading
2815
2816    project_local.read_with(cx_a, |project, cx| {
2817        assert_branch(Some("branch-1"), project, cx)
2818    });
2819
2820    project_remote.read_with(cx_b, |project, cx| {
2821        assert_branch(Some("branch-1"), project, cx)
2822    });
2823
2824    client_a
2825        .fs()
2826        .set_branch_name(Path::new("/dir/.git"), Some("branch-2"));
2827
2828    // Wait for buffer_local_a to receive it
2829    executor.run_until_parked();
2830
2831    // Smoke test branch reading
2832
2833    project_local.read_with(cx_a, |project, cx| {
2834        assert_branch(Some("branch-2"), project, cx)
2835    });
2836
2837    project_remote.read_with(cx_b, |project, cx| {
2838        assert_branch(Some("branch-2"), project, cx)
2839    });
2840
2841    let project_remote_c = client_c.build_dev_server_project(project_id, cx_c).await;
2842    executor.run_until_parked();
2843
2844    project_remote_c.read_with(cx_c, |project, cx| {
2845        assert_branch(Some("branch-2"), project, cx)
2846    });
2847}
2848
2849#[gpui::test]
2850async fn test_git_status_sync(
2851    executor: BackgroundExecutor,
2852    cx_a: &mut TestAppContext,
2853    cx_b: &mut TestAppContext,
2854    cx_c: &mut TestAppContext,
2855) {
2856    let mut server = TestServer::start(executor.clone()).await;
2857    let client_a = server.create_client(cx_a, "user_a").await;
2858    let client_b = server.create_client(cx_b, "user_b").await;
2859    let client_c = server.create_client(cx_c, "user_c").await;
2860    server
2861        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
2862        .await;
2863    let active_call_a = cx_a.read(ActiveCall::global);
2864
2865    client_a
2866        .fs()
2867        .insert_tree(
2868            "/dir",
2869            json!({
2870            ".git": {},
2871            "a.txt": "a",
2872            "b.txt": "b",
2873            }),
2874        )
2875        .await;
2876
2877    const A_TXT: &str = "a.txt";
2878    const B_TXT: &str = "b.txt";
2879
2880    client_a.fs().set_status_for_repo_via_git_operation(
2881        Path::new("/dir/.git"),
2882        &[
2883            (&Path::new(A_TXT), GitFileStatus::Added),
2884            (&Path::new(B_TXT), GitFileStatus::Added),
2885        ],
2886    );
2887
2888    let (project_local, _worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2889    let project_id = active_call_a
2890        .update(cx_a, |call, cx| {
2891            call.share_project(project_local.clone(), cx)
2892        })
2893        .await
2894        .unwrap();
2895
2896    let project_remote = client_b.build_dev_server_project(project_id, cx_b).await;
2897
2898    // Wait for it to catch up to the new status
2899    executor.run_until_parked();
2900
2901    #[track_caller]
2902    fn assert_status(
2903        file: &impl AsRef<Path>,
2904        status: Option<GitFileStatus>,
2905        project: &Project,
2906        cx: &AppContext,
2907    ) {
2908        let file = file.as_ref();
2909        let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
2910        assert_eq!(worktrees.len(), 1);
2911        let worktree = worktrees[0].clone();
2912        let snapshot = worktree.read(cx).snapshot();
2913        assert_eq!(snapshot.status_for_file(file), status);
2914    }
2915
2916    // Smoke test status reading
2917
2918    project_local.read_with(cx_a, |project, cx| {
2919        assert_status(&Path::new(A_TXT), Some(GitFileStatus::Added), project, cx);
2920        assert_status(&Path::new(B_TXT), Some(GitFileStatus::Added), project, cx);
2921    });
2922
2923    project_remote.read_with(cx_b, |project, cx| {
2924        assert_status(&Path::new(A_TXT), Some(GitFileStatus::Added), project, cx);
2925        assert_status(&Path::new(B_TXT), Some(GitFileStatus::Added), project, cx);
2926    });
2927
2928    client_a.fs().set_status_for_repo_via_working_copy_change(
2929        Path::new("/dir/.git"),
2930        &[
2931            (&Path::new(A_TXT), GitFileStatus::Modified),
2932            (&Path::new(B_TXT), GitFileStatus::Modified),
2933        ],
2934    );
2935
2936    // Wait for buffer_local_a to receive it
2937    executor.run_until_parked();
2938
2939    // Smoke test status reading
2940
2941    project_local.read_with(cx_a, |project, cx| {
2942        assert_status(
2943            &Path::new(A_TXT),
2944            Some(GitFileStatus::Modified),
2945            project,
2946            cx,
2947        );
2948        assert_status(
2949            &Path::new(B_TXT),
2950            Some(GitFileStatus::Modified),
2951            project,
2952            cx,
2953        );
2954    });
2955
2956    project_remote.read_with(cx_b, |project, cx| {
2957        assert_status(
2958            &Path::new(A_TXT),
2959            Some(GitFileStatus::Modified),
2960            project,
2961            cx,
2962        );
2963        assert_status(
2964            &Path::new(B_TXT),
2965            Some(GitFileStatus::Modified),
2966            project,
2967            cx,
2968        );
2969    });
2970
2971    // And synchronization while joining
2972    let project_remote_c = client_c.build_dev_server_project(project_id, cx_c).await;
2973    executor.run_until_parked();
2974
2975    project_remote_c.read_with(cx_c, |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
2991#[gpui::test(iterations = 10)]
2992async fn test_fs_operations(
2993    executor: BackgroundExecutor,
2994    cx_a: &mut TestAppContext,
2995    cx_b: &mut TestAppContext,
2996) {
2997    let mut server = TestServer::start(executor.clone()).await;
2998    let client_a = server.create_client(cx_a, "user_a").await;
2999    let client_b = server.create_client(cx_b, "user_b").await;
3000    server
3001        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3002        .await;
3003    let active_call_a = cx_a.read(ActiveCall::global);
3004
3005    client_a
3006        .fs()
3007        .insert_tree(
3008            "/dir",
3009            json!({
3010                "a.txt": "a-contents",
3011                "b.txt": "b-contents",
3012            }),
3013        )
3014        .await;
3015    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3016    let project_id = active_call_a
3017        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3018        .await
3019        .unwrap();
3020    let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
3021
3022    let worktree_a = project_a.read_with(cx_a, |project, _| project.worktrees().next().unwrap());
3023    let worktree_b = project_b.read_with(cx_b, |project, _| project.worktrees().next().unwrap());
3024
3025    let entry = project_b
3026        .update(cx_b, |project, cx| {
3027            project.create_entry((worktree_id, "c.txt"), false, cx)
3028        })
3029        .await
3030        .unwrap()
3031        .to_included()
3032        .unwrap();
3033
3034    worktree_a.read_with(cx_a, |worktree, _| {
3035        assert_eq!(
3036            worktree
3037                .paths()
3038                .map(|p| p.to_string_lossy())
3039                .collect::<Vec<_>>(),
3040            ["a.txt", "b.txt", "c.txt"]
3041        );
3042    });
3043
3044    worktree_b.read_with(cx_b, |worktree, _| {
3045        assert_eq!(
3046            worktree
3047                .paths()
3048                .map(|p| p.to_string_lossy())
3049                .collect::<Vec<_>>(),
3050            ["a.txt", "b.txt", "c.txt"]
3051        );
3052    });
3053
3054    project_b
3055        .update(cx_b, |project, cx| {
3056            project.rename_entry(entry.id, Path::new("d.txt"), cx)
3057        })
3058        .await
3059        .unwrap()
3060        .to_included()
3061        .unwrap();
3062
3063    worktree_a.read_with(cx_a, |worktree, _| {
3064        assert_eq!(
3065            worktree
3066                .paths()
3067                .map(|p| p.to_string_lossy())
3068                .collect::<Vec<_>>(),
3069            ["a.txt", "b.txt", "d.txt"]
3070        );
3071    });
3072
3073    worktree_b.read_with(cx_b, |worktree, _| {
3074        assert_eq!(
3075            worktree
3076                .paths()
3077                .map(|p| p.to_string_lossy())
3078                .collect::<Vec<_>>(),
3079            ["a.txt", "b.txt", "d.txt"]
3080        );
3081    });
3082
3083    let dir_entry = project_b
3084        .update(cx_b, |project, cx| {
3085            project.create_entry((worktree_id, "DIR"), true, cx)
3086        })
3087        .await
3088        .unwrap()
3089        .to_included()
3090        .unwrap();
3091
3092    worktree_a.read_with(cx_a, |worktree, _| {
3093        assert_eq!(
3094            worktree
3095                .paths()
3096                .map(|p| p.to_string_lossy())
3097                .collect::<Vec<_>>(),
3098            ["DIR", "a.txt", "b.txt", "d.txt"]
3099        );
3100    });
3101
3102    worktree_b.read_with(cx_b, |worktree, _| {
3103        assert_eq!(
3104            worktree
3105                .paths()
3106                .map(|p| p.to_string_lossy())
3107                .collect::<Vec<_>>(),
3108            ["DIR", "a.txt", "b.txt", "d.txt"]
3109        );
3110    });
3111
3112    project_b
3113        .update(cx_b, |project, cx| {
3114            project.create_entry((worktree_id, "DIR/e.txt"), false, cx)
3115        })
3116        .await
3117        .unwrap()
3118        .to_included()
3119        .unwrap();
3120
3121    project_b
3122        .update(cx_b, |project, cx| {
3123            project.create_entry((worktree_id, "DIR/SUBDIR"), true, cx)
3124        })
3125        .await
3126        .unwrap()
3127        .to_included()
3128        .unwrap();
3129
3130    project_b
3131        .update(cx_b, |project, cx| {
3132            project.create_entry((worktree_id, "DIR/SUBDIR/f.txt"), false, cx)
3133        })
3134        .await
3135        .unwrap()
3136        .to_included()
3137        .unwrap();
3138
3139    worktree_a.read_with(cx_a, |worktree, _| {
3140        assert_eq!(
3141            worktree
3142                .paths()
3143                .map(|p| p.to_string_lossy())
3144                .collect::<Vec<_>>(),
3145            [
3146                "DIR",
3147                "DIR/SUBDIR",
3148                "DIR/SUBDIR/f.txt",
3149                "DIR/e.txt",
3150                "a.txt",
3151                "b.txt",
3152                "d.txt"
3153            ]
3154        );
3155    });
3156
3157    worktree_b.read_with(cx_b, |worktree, _| {
3158        assert_eq!(
3159            worktree
3160                .paths()
3161                .map(|p| p.to_string_lossy())
3162                .collect::<Vec<_>>(),
3163            [
3164                "DIR",
3165                "DIR/SUBDIR",
3166                "DIR/SUBDIR/f.txt",
3167                "DIR/e.txt",
3168                "a.txt",
3169                "b.txt",
3170                "d.txt"
3171            ]
3172        );
3173    });
3174
3175    project_b
3176        .update(cx_b, |project, cx| {
3177            project.copy_entry(entry.id, Path::new("f.txt"), cx)
3178        })
3179        .await
3180        .unwrap()
3181        .unwrap();
3182
3183    worktree_a.read_with(cx_a, |worktree, _| {
3184        assert_eq!(
3185            worktree
3186                .paths()
3187                .map(|p| p.to_string_lossy())
3188                .collect::<Vec<_>>(),
3189            [
3190                "DIR",
3191                "DIR/SUBDIR",
3192                "DIR/SUBDIR/f.txt",
3193                "DIR/e.txt",
3194                "a.txt",
3195                "b.txt",
3196                "d.txt",
3197                "f.txt"
3198            ]
3199        );
3200    });
3201
3202    worktree_b.read_with(cx_b, |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    project_b
3222        .update(cx_b, |project, cx| {
3223            project.delete_entry(dir_entry.id, false, cx).unwrap()
3224        })
3225        .await
3226        .unwrap();
3227    executor.run_until_parked();
3228
3229    worktree_a.read_with(cx_a, |worktree, _| {
3230        assert_eq!(
3231            worktree
3232                .paths()
3233                .map(|p| p.to_string_lossy())
3234                .collect::<Vec<_>>(),
3235            ["a.txt", "b.txt", "d.txt", "f.txt"]
3236        );
3237    });
3238
3239    worktree_b.read_with(cx_b, |worktree, _| {
3240        assert_eq!(
3241            worktree
3242                .paths()
3243                .map(|p| p.to_string_lossy())
3244                .collect::<Vec<_>>(),
3245            ["a.txt", "b.txt", "d.txt", "f.txt"]
3246        );
3247    });
3248
3249    project_b
3250        .update(cx_b, |project, cx| {
3251            project.delete_entry(entry.id, false, cx).unwrap()
3252        })
3253        .await
3254        .unwrap();
3255
3256    worktree_a.read_with(cx_a, |worktree, _| {
3257        assert_eq!(
3258            worktree
3259                .paths()
3260                .map(|p| p.to_string_lossy())
3261                .collect::<Vec<_>>(),
3262            ["a.txt", "b.txt", "f.txt"]
3263        );
3264    });
3265
3266    worktree_b.read_with(cx_b, |worktree, _| {
3267        assert_eq!(
3268            worktree
3269                .paths()
3270                .map(|p| p.to_string_lossy())
3271                .collect::<Vec<_>>(),
3272            ["a.txt", "b.txt", "f.txt"]
3273        );
3274    });
3275}
3276
3277#[gpui::test(iterations = 10)]
3278async fn test_local_settings(
3279    executor: BackgroundExecutor,
3280    cx_a: &mut TestAppContext,
3281    cx_b: &mut TestAppContext,
3282) {
3283    let mut server = TestServer::start(executor.clone()).await;
3284    let client_a = server.create_client(cx_a, "user_a").await;
3285    let client_b = server.create_client(cx_b, "user_b").await;
3286    server
3287        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3288        .await;
3289    let active_call_a = cx_a.read(ActiveCall::global);
3290
3291    // As client A, open a project that contains some local settings files
3292    client_a
3293        .fs()
3294        .insert_tree(
3295            "/dir",
3296            json!({
3297                ".zed": {
3298                    "settings.json": r#"{ "tab_size": 2 }"#
3299                },
3300                "a": {
3301                    ".zed": {
3302                        "settings.json": r#"{ "tab_size": 8 }"#
3303                    },
3304                    "a.txt": "a-contents",
3305                },
3306                "b": {
3307                    "b.txt": "b-contents",
3308                }
3309            }),
3310        )
3311        .await;
3312    let (project_a, _) = client_a.build_local_project("/dir", cx_a).await;
3313    executor.run_until_parked();
3314    let project_id = active_call_a
3315        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3316        .await
3317        .unwrap();
3318    executor.run_until_parked();
3319
3320    // As client B, join that project and observe the local settings.
3321    let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
3322
3323    let worktree_b = project_b.read_with(cx_b, |project, _| project.worktrees().next().unwrap());
3324    executor.run_until_parked();
3325    cx_b.read(|cx| {
3326        let store = cx.global::<SettingsStore>();
3327        assert_eq!(
3328            store
3329                .local_settings(worktree_b.read(cx).id().to_usize())
3330                .collect::<Vec<_>>(),
3331            &[
3332                (Path::new("").into(), r#"{"tab_size":2}"#.to_string()),
3333                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3334            ]
3335        )
3336    });
3337
3338    // As client A, update a settings file. As Client B, see the changed settings.
3339    client_a
3340        .fs()
3341        .insert_file("/dir/.zed/settings.json", r#"{}"#.into())
3342        .await;
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#"{}"#.to_string()),
3352                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3353            ]
3354        )
3355    });
3356
3357    // As client A, create and remove some settings files. As client B, see the changed settings.
3358    client_a
3359        .fs()
3360        .remove_file("/dir/.zed/settings.json".as_ref(), Default::default())
3361        .await
3362        .unwrap();
3363    client_a
3364        .fs()
3365        .create_dir("/dir/b/.zed".as_ref())
3366        .await
3367        .unwrap();
3368    client_a
3369        .fs()
3370        .insert_file("/dir/b/.zed/settings.json", r#"{"tab_size": 4}"#.into())
3371        .await;
3372    executor.run_until_parked();
3373    cx_b.read(|cx| {
3374        let store = cx.global::<SettingsStore>();
3375        assert_eq!(
3376            store
3377                .local_settings(worktree_b.read(cx).id().to_usize())
3378                .collect::<Vec<_>>(),
3379            &[
3380                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3381                (Path::new("b").into(), r#"{"tab_size":4}"#.to_string()),
3382            ]
3383        )
3384    });
3385
3386    // As client B, disconnect.
3387    server.forbid_connections();
3388    server.disconnect_client(client_b.peer_id().unwrap());
3389
3390    // As client A, change and remove settings files while client B is disconnected.
3391    client_a
3392        .fs()
3393        .insert_file("/dir/a/.zed/settings.json", r#"{"hard_tabs":true}"#.into())
3394        .await;
3395    client_a
3396        .fs()
3397        .remove_file("/dir/b/.zed/settings.json".as_ref(), Default::default())
3398        .await
3399        .unwrap();
3400    executor.run_until_parked();
3401
3402    // As client B, reconnect and see the changed settings.
3403    server.allow_connections();
3404    executor.advance_clock(RECEIVE_TIMEOUT);
3405    cx_b.read(|cx| {
3406        let store = cx.global::<SettingsStore>();
3407        assert_eq!(
3408            store
3409                .local_settings(worktree_b.read(cx).id().to_usize())
3410                .collect::<Vec<_>>(),
3411            &[(Path::new("a").into(), r#"{"hard_tabs":true}"#.to_string()),]
3412        )
3413    });
3414}
3415
3416#[gpui::test(iterations = 10)]
3417async fn test_buffer_conflict_after_save(
3418    executor: BackgroundExecutor,
3419    cx_a: &mut TestAppContext,
3420    cx_b: &mut TestAppContext,
3421) {
3422    let mut server = TestServer::start(executor.clone()).await;
3423    let client_a = server.create_client(cx_a, "user_a").await;
3424    let client_b = server.create_client(cx_b, "user_b").await;
3425    server
3426        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3427        .await;
3428    let active_call_a = cx_a.read(ActiveCall::global);
3429
3430    client_a
3431        .fs()
3432        .insert_tree(
3433            "/dir",
3434            json!({
3435                "a.txt": "a-contents",
3436            }),
3437        )
3438        .await;
3439    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3440    let project_id = active_call_a
3441        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3442        .await
3443        .unwrap();
3444    let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
3445
3446    // Open a buffer as client B
3447    let buffer_b = project_b
3448        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3449        .await
3450        .unwrap();
3451
3452    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "world ")], None, cx));
3453
3454    buffer_b.read_with(cx_b, |buf, _| {
3455        assert!(buf.is_dirty());
3456        assert!(!buf.has_conflict());
3457    });
3458
3459    project_b
3460        .update(cx_b, |project, cx| {
3461            project.save_buffer(buffer_b.clone(), cx)
3462        })
3463        .await
3464        .unwrap();
3465
3466    buffer_b.read_with(cx_b, |buffer_b, _| assert!(!buffer_b.is_dirty()));
3467
3468    buffer_b.read_with(cx_b, |buf, _| {
3469        assert!(!buf.has_conflict());
3470    });
3471
3472    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "hello ")], None, cx));
3473
3474    buffer_b.read_with(cx_b, |buf, _| {
3475        assert!(buf.is_dirty());
3476        assert!(!buf.has_conflict());
3477    });
3478}
3479
3480#[gpui::test(iterations = 10)]
3481async fn test_buffer_reloading(
3482    executor: BackgroundExecutor,
3483    cx_a: &mut TestAppContext,
3484    cx_b: &mut TestAppContext,
3485) {
3486    let mut server = TestServer::start(executor.clone()).await;
3487    let client_a = server.create_client(cx_a, "user_a").await;
3488    let client_b = server.create_client(cx_b, "user_b").await;
3489    server
3490        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3491        .await;
3492    let active_call_a = cx_a.read(ActiveCall::global);
3493
3494    client_a
3495        .fs()
3496        .insert_tree(
3497            "/dir",
3498            json!({
3499                "a.txt": "a\nb\nc",
3500            }),
3501        )
3502        .await;
3503    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3504    let project_id = active_call_a
3505        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3506        .await
3507        .unwrap();
3508    let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
3509
3510    // Open a buffer as client B
3511    let buffer_b = project_b
3512        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3513        .await
3514        .unwrap();
3515
3516    buffer_b.read_with(cx_b, |buf, _| {
3517        assert!(!buf.is_dirty());
3518        assert!(!buf.has_conflict());
3519        assert_eq!(buf.line_ending(), LineEnding::Unix);
3520    });
3521
3522    let new_contents = Rope::from("d\ne\nf");
3523    client_a
3524        .fs()
3525        .save("/dir/a.txt".as_ref(), &new_contents, LineEnding::Windows)
3526        .await
3527        .unwrap();
3528
3529    executor.run_until_parked();
3530
3531    buffer_b.read_with(cx_b, |buf, _| {
3532        assert_eq!(buf.text(), new_contents.to_string());
3533        assert!(!buf.is_dirty());
3534        assert!(!buf.has_conflict());
3535        assert_eq!(buf.line_ending(), LineEnding::Windows);
3536    });
3537}
3538
3539#[gpui::test(iterations = 10)]
3540async fn test_editing_while_guest_opens_buffer(
3541    executor: BackgroundExecutor,
3542    cx_a: &mut TestAppContext,
3543    cx_b: &mut TestAppContext,
3544) {
3545    let mut server = TestServer::start(executor.clone()).await;
3546    let client_a = server.create_client(cx_a, "user_a").await;
3547    let client_b = server.create_client(cx_b, "user_b").await;
3548    server
3549        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3550        .await;
3551    let active_call_a = cx_a.read(ActiveCall::global);
3552
3553    client_a
3554        .fs()
3555        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3556        .await;
3557    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3558    let project_id = active_call_a
3559        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3560        .await
3561        .unwrap();
3562    let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
3563
3564    // Open a buffer as client A
3565    let buffer_a = project_a
3566        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3567        .await
3568        .unwrap();
3569
3570    // Start opening the same buffer as client B
3571    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx));
3572    let buffer_b = cx_b.executor().spawn(open_buffer);
3573
3574    // Edit the buffer as client A while client B is still opening it.
3575    cx_b.executor().simulate_random_delay().await;
3576    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "X")], None, cx));
3577    cx_b.executor().simulate_random_delay().await;
3578    buffer_a.update(cx_a, |buf, cx| buf.edit([(1..1, "Y")], None, cx));
3579
3580    let text = buffer_a.read_with(cx_a, |buf, _| buf.text());
3581    let buffer_b = buffer_b.await.unwrap();
3582    executor.run_until_parked();
3583
3584    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), text));
3585}
3586
3587#[gpui::test(iterations = 10)]
3588async fn test_leaving_worktree_while_opening_buffer(
3589    executor: BackgroundExecutor,
3590    cx_a: &mut TestAppContext,
3591    cx_b: &mut TestAppContext,
3592) {
3593    let mut server = TestServer::start(executor.clone()).await;
3594    let client_a = server.create_client(cx_a, "user_a").await;
3595    let client_b = server.create_client(cx_b, "user_b").await;
3596    server
3597        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3598        .await;
3599    let active_call_a = cx_a.read(ActiveCall::global);
3600
3601    client_a
3602        .fs()
3603        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3604        .await;
3605    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3606    let project_id = active_call_a
3607        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3608        .await
3609        .unwrap();
3610    let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
3611
3612    // See that a guest has joined as client A.
3613    executor.run_until_parked();
3614
3615    project_a.read_with(cx_a, |p, _| assert_eq!(p.collaborators().len(), 1));
3616
3617    // Begin opening a buffer as client B, but leave the project before the open completes.
3618    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx));
3619    let buffer_b = cx_b.executor().spawn(open_buffer);
3620    cx_b.update(|_| drop(project_b));
3621    drop(buffer_b);
3622
3623    // See that the guest has left.
3624    executor.run_until_parked();
3625
3626    project_a.read_with(cx_a, |p, _| assert!(p.collaborators().is_empty()));
3627}
3628
3629#[gpui::test(iterations = 10)]
3630async fn test_canceling_buffer_opening(
3631    executor: BackgroundExecutor,
3632    cx_a: &mut TestAppContext,
3633    cx_b: &mut TestAppContext,
3634) {
3635    let mut server = TestServer::start(executor.clone()).await;
3636    let client_a = server.create_client(cx_a, "user_a").await;
3637    let client_b = server.create_client(cx_b, "user_b").await;
3638    server
3639        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3640        .await;
3641    let active_call_a = cx_a.read(ActiveCall::global);
3642
3643    client_a
3644        .fs()
3645        .insert_tree(
3646            "/dir",
3647            json!({
3648                "a.txt": "abc",
3649            }),
3650        )
3651        .await;
3652    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3653    let project_id = active_call_a
3654        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3655        .await
3656        .unwrap();
3657    let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
3658
3659    let buffer_a = project_a
3660        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3661        .await
3662        .unwrap();
3663
3664    // Open a buffer as client B but cancel after a random amount of time.
3665    let buffer_b = project_b.update(cx_b, |p, cx| {
3666        p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3667    });
3668    executor.simulate_random_delay().await;
3669    drop(buffer_b);
3670
3671    // Try opening the same buffer again as client B, and ensure we can
3672    // still do it despite the cancellation above.
3673    let buffer_b = project_b
3674        .update(cx_b, |p, cx| {
3675            p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3676        })
3677        .await
3678        .unwrap();
3679
3680    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "abc"));
3681}
3682
3683#[gpui::test(iterations = 10)]
3684async fn test_leaving_project(
3685    executor: BackgroundExecutor,
3686    cx_a: &mut TestAppContext,
3687    cx_b: &mut TestAppContext,
3688    cx_c: &mut TestAppContext,
3689) {
3690    let mut server = TestServer::start(executor.clone()).await;
3691    let client_a = server.create_client(cx_a, "user_a").await;
3692    let client_b = server.create_client(cx_b, "user_b").await;
3693    let client_c = server.create_client(cx_c, "user_c").await;
3694    server
3695        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3696        .await;
3697    let active_call_a = cx_a.read(ActiveCall::global);
3698
3699    client_a
3700        .fs()
3701        .insert_tree(
3702            "/a",
3703            json!({
3704                "a.txt": "a-contents",
3705                "b.txt": "b-contents",
3706            }),
3707        )
3708        .await;
3709    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
3710    let project_id = active_call_a
3711        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3712        .await
3713        .unwrap();
3714    let project_b1 = client_b.build_dev_server_project(project_id, cx_b).await;
3715    let project_c = client_c.build_dev_server_project(project_id, cx_c).await;
3716
3717    // Client A sees that a guest has joined.
3718    executor.run_until_parked();
3719
3720    project_a.read_with(cx_a, |project, _| {
3721        assert_eq!(project.collaborators().len(), 2);
3722    });
3723
3724    project_b1.read_with(cx_b, |project, _| {
3725        assert_eq!(project.collaborators().len(), 2);
3726    });
3727
3728    project_c.read_with(cx_c, |project, _| {
3729        assert_eq!(project.collaborators().len(), 2);
3730    });
3731
3732    // Client B opens a buffer.
3733    let buffer_b1 = project_b1
3734        .update(cx_b, |project, cx| {
3735            let worktree_id = project.worktrees().next().unwrap().read(cx).id();
3736            project.open_buffer((worktree_id, "a.txt"), cx)
3737        })
3738        .await
3739        .unwrap();
3740
3741    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3742
3743    // Drop client B's project and ensure client A and client C observe client B leaving.
3744    cx_b.update(|_| drop(project_b1));
3745    executor.run_until_parked();
3746
3747    project_a.read_with(cx_a, |project, _| {
3748        assert_eq!(project.collaborators().len(), 1);
3749    });
3750
3751    project_c.read_with(cx_c, |project, _| {
3752        assert_eq!(project.collaborators().len(), 1);
3753    });
3754
3755    // Client B re-joins the project and can open buffers as before.
3756    let project_b2 = client_b.build_dev_server_project(project_id, cx_b).await;
3757    executor.run_until_parked();
3758
3759    project_a.read_with(cx_a, |project, _| {
3760        assert_eq!(project.collaborators().len(), 2);
3761    });
3762
3763    project_b2.read_with(cx_b, |project, _| {
3764        assert_eq!(project.collaborators().len(), 2);
3765    });
3766
3767    project_c.read_with(cx_c, |project, _| {
3768        assert_eq!(project.collaborators().len(), 2);
3769    });
3770
3771    let buffer_b2 = project_b2
3772        .update(cx_b, |project, cx| {
3773            let worktree_id = project.worktrees().next().unwrap().read(cx).id();
3774            project.open_buffer((worktree_id, "a.txt"), cx)
3775        })
3776        .await
3777        .unwrap();
3778
3779    buffer_b2.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3780
3781    project_a.read_with(cx_a, |project, _| {
3782        assert_eq!(project.collaborators().len(), 2);
3783    });
3784
3785    // Drop client B's connection and ensure client A and client C observe client B leaving.
3786    client_b.disconnect(&cx_b.to_async());
3787    executor.advance_clock(RECONNECT_TIMEOUT);
3788
3789    project_a.read_with(cx_a, |project, _| {
3790        assert_eq!(project.collaborators().len(), 1);
3791    });
3792
3793    project_b2.read_with(cx_b, |project, _| {
3794        assert!(project.is_disconnected());
3795    });
3796
3797    project_c.read_with(cx_c, |project, _| {
3798        assert_eq!(project.collaborators().len(), 1);
3799    });
3800
3801    // Client B can't join the project, unless they re-join the room.
3802    cx_b.spawn(|cx| {
3803        Project::in_room(
3804            project_id,
3805            client_b.app_state.client.clone(),
3806            client_b.user_store().clone(),
3807            client_b.language_registry().clone(),
3808            FakeFs::new(cx.background_executor().clone()),
3809            cx,
3810        )
3811    })
3812    .await
3813    .unwrap_err();
3814
3815    // Simulate connection loss for client C and ensure client A observes client C leaving the project.
3816    client_c.wait_for_current_user(cx_c).await;
3817    server.forbid_connections();
3818    server.disconnect_client(client_c.peer_id().unwrap());
3819    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
3820    executor.run_until_parked();
3821
3822    project_a.read_with(cx_a, |project, _| {
3823        assert_eq!(project.collaborators().len(), 0);
3824    });
3825
3826    project_b2.read_with(cx_b, |project, _| {
3827        assert!(project.is_disconnected());
3828    });
3829
3830    project_c.read_with(cx_c, |project, _| {
3831        assert!(project.is_disconnected());
3832    });
3833}
3834
3835#[gpui::test(iterations = 10)]
3836async fn test_collaborating_with_diagnostics(
3837    executor: BackgroundExecutor,
3838    cx_a: &mut TestAppContext,
3839    cx_b: &mut TestAppContext,
3840    cx_c: &mut TestAppContext,
3841) {
3842    let mut server = TestServer::start(executor.clone()).await;
3843    let client_a = server.create_client(cx_a, "user_a").await;
3844    let client_b = server.create_client(cx_b, "user_b").await;
3845    let client_c = server.create_client(cx_c, "user_c").await;
3846    server
3847        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3848        .await;
3849    let active_call_a = cx_a.read(ActiveCall::global);
3850
3851    client_a.language_registry().add(Arc::new(Language::new(
3852        LanguageConfig {
3853            name: "Rust".into(),
3854            matcher: LanguageMatcher {
3855                path_suffixes: vec!["rs".to_string()],
3856                ..Default::default()
3857            },
3858            ..Default::default()
3859        },
3860        Some(tree_sitter_rust::language()),
3861    )));
3862    let mut fake_language_servers = client_a
3863        .language_registry()
3864        .register_fake_lsp_adapter("Rust", Default::default());
3865
3866    // Share a project as client A
3867    client_a
3868        .fs()
3869        .insert_tree(
3870            "/a",
3871            json!({
3872                "a.rs": "let one = two",
3873                "other.rs": "",
3874            }),
3875        )
3876        .await;
3877    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3878
3879    // Cause the language server to start.
3880    let _buffer = project_a
3881        .update(cx_a, |project, cx| {
3882            project.open_buffer(
3883                ProjectPath {
3884                    worktree_id,
3885                    path: Path::new("other.rs").into(),
3886                },
3887                cx,
3888            )
3889        })
3890        .await
3891        .unwrap();
3892
3893    // Simulate a language server reporting errors for a file.
3894    let mut fake_language_server = fake_language_servers.next().await.unwrap();
3895    fake_language_server
3896        .receive_notification::<lsp::notification::DidOpenTextDocument>()
3897        .await;
3898    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3899        lsp::PublishDiagnosticsParams {
3900            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3901            version: None,
3902            diagnostics: vec![lsp::Diagnostic {
3903                severity: Some(lsp::DiagnosticSeverity::WARNING),
3904                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3905                message: "message 0".to_string(),
3906                ..Default::default()
3907            }],
3908        },
3909    );
3910
3911    // Client A shares the project and, simultaneously, the language server
3912    // publishes a diagnostic. This is done to ensure that the server always
3913    // observes the latest diagnostics for a worktree.
3914    let project_id = active_call_a
3915        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3916        .await
3917        .unwrap();
3918    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3919        lsp::PublishDiagnosticsParams {
3920            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3921            version: None,
3922            diagnostics: vec![lsp::Diagnostic {
3923                severity: Some(lsp::DiagnosticSeverity::ERROR),
3924                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3925                message: "message 1".to_string(),
3926                ..Default::default()
3927            }],
3928        },
3929    );
3930
3931    // Join the worktree as client B.
3932    let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
3933
3934    // Wait for server to see the diagnostics update.
3935    executor.run_until_parked();
3936
3937    // Ensure client B observes the new diagnostics.
3938
3939    project_b.read_with(cx_b, |project, cx| {
3940        assert_eq!(
3941            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
3942            &[(
3943                ProjectPath {
3944                    worktree_id,
3945                    path: Arc::from(Path::new("a.rs")),
3946                },
3947                LanguageServerId(0),
3948                DiagnosticSummary {
3949                    error_count: 1,
3950                    warning_count: 0,
3951                },
3952            )]
3953        )
3954    });
3955
3956    // Join project as client C and observe the diagnostics.
3957    let project_c = client_c.build_dev_server_project(project_id, cx_c).await;
3958    executor.run_until_parked();
3959    let project_c_diagnostic_summaries =
3960        Rc::new(RefCell::new(project_c.read_with(cx_c, |project, cx| {
3961            project.diagnostic_summaries(false, cx).collect::<Vec<_>>()
3962        })));
3963    project_c.update(cx_c, |_, cx| {
3964        let summaries = project_c_diagnostic_summaries.clone();
3965        cx.subscribe(&project_c, {
3966            move |p, _, event, cx| {
3967                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
3968                    *summaries.borrow_mut() = p.diagnostic_summaries(false, cx).collect();
3969                }
3970            }
3971        })
3972        .detach();
3973    });
3974
3975    executor.run_until_parked();
3976    assert_eq!(
3977        project_c_diagnostic_summaries.borrow().as_slice(),
3978        &[(
3979            ProjectPath {
3980                worktree_id,
3981                path: Arc::from(Path::new("a.rs")),
3982            },
3983            LanguageServerId(0),
3984            DiagnosticSummary {
3985                error_count: 1,
3986                warning_count: 0,
3987            },
3988        )]
3989    );
3990
3991    // Simulate a language server reporting more errors for a file.
3992    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3993        lsp::PublishDiagnosticsParams {
3994            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3995            version: None,
3996            diagnostics: vec![
3997                lsp::Diagnostic {
3998                    severity: Some(lsp::DiagnosticSeverity::ERROR),
3999                    range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
4000                    message: "message 1".to_string(),
4001                    ..Default::default()
4002                },
4003                lsp::Diagnostic {
4004                    severity: Some(lsp::DiagnosticSeverity::WARNING),
4005                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 13)),
4006                    message: "message 2".to_string(),
4007                    ..Default::default()
4008                },
4009            ],
4010        },
4011    );
4012
4013    // Clients B and C get the updated summaries
4014    executor.run_until_parked();
4015
4016    project_b.read_with(cx_b, |project, cx| {
4017        assert_eq!(
4018            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4019            [(
4020                ProjectPath {
4021                    worktree_id,
4022                    path: Arc::from(Path::new("a.rs")),
4023                },
4024                LanguageServerId(0),
4025                DiagnosticSummary {
4026                    error_count: 1,
4027                    warning_count: 1,
4028                },
4029            )]
4030        );
4031    });
4032
4033    project_c.read_with(cx_c, |project, cx| {
4034        assert_eq!(
4035            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4036            [(
4037                ProjectPath {
4038                    worktree_id,
4039                    path: Arc::from(Path::new("a.rs")),
4040                },
4041                LanguageServerId(0),
4042                DiagnosticSummary {
4043                    error_count: 1,
4044                    warning_count: 1,
4045                },
4046            )]
4047        );
4048    });
4049
4050    // Open the file with the errors on client B. They should be present.
4051    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4052    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4053
4054    buffer_b.read_with(cx_b, |buffer, _| {
4055        assert_eq!(
4056            buffer
4057                .snapshot()
4058                .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
4059                .collect::<Vec<_>>(),
4060            &[
4061                DiagnosticEntry {
4062                    range: Point::new(0, 4)..Point::new(0, 7),
4063                    diagnostic: Diagnostic {
4064                        group_id: 2,
4065                        message: "message 1".to_string(),
4066                        severity: lsp::DiagnosticSeverity::ERROR,
4067                        is_primary: true,
4068                        ..Default::default()
4069                    }
4070                },
4071                DiagnosticEntry {
4072                    range: Point::new(0, 10)..Point::new(0, 13),
4073                    diagnostic: Diagnostic {
4074                        group_id: 3,
4075                        severity: lsp::DiagnosticSeverity::WARNING,
4076                        message: "message 2".to_string(),
4077                        is_primary: true,
4078                        ..Default::default()
4079                    }
4080                }
4081            ]
4082        );
4083    });
4084
4085    // Simulate a language server reporting no errors for a file.
4086    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
4087        lsp::PublishDiagnosticsParams {
4088            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
4089            version: None,
4090            diagnostics: vec![],
4091        },
4092    );
4093    executor.run_until_parked();
4094
4095    project_a.read_with(cx_a, |project, cx| {
4096        assert_eq!(
4097            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4098            []
4099        )
4100    });
4101
4102    project_b.read_with(cx_b, |project, cx| {
4103        assert_eq!(
4104            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4105            []
4106        )
4107    });
4108
4109    project_c.read_with(cx_c, |project, cx| {
4110        assert_eq!(
4111            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4112            []
4113        )
4114    });
4115}
4116
4117#[gpui::test(iterations = 10)]
4118async fn test_collaborating_with_lsp_progress_updates_and_diagnostics_ordering(
4119    executor: BackgroundExecutor,
4120    cx_a: &mut TestAppContext,
4121    cx_b: &mut TestAppContext,
4122) {
4123    let mut server = TestServer::start(executor.clone()).await;
4124    let client_a = server.create_client(cx_a, "user_a").await;
4125    let client_b = server.create_client(cx_b, "user_b").await;
4126    server
4127        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4128        .await;
4129
4130    client_a.language_registry().add(rust_lang());
4131    let mut fake_language_servers = client_a.language_registry().register_fake_lsp_adapter(
4132        "Rust",
4133        FakeLspAdapter {
4134            disk_based_diagnostics_progress_token: Some("the-disk-based-token".into()),
4135            disk_based_diagnostics_sources: vec!["the-disk-based-diagnostics-source".into()],
4136            ..Default::default()
4137        },
4138    );
4139
4140    let file_names = &["one.rs", "two.rs", "three.rs", "four.rs", "five.rs"];
4141    client_a
4142        .fs()
4143        .insert_tree(
4144            "/test",
4145            json!({
4146                "one.rs": "const ONE: usize = 1;",
4147                "two.rs": "const TWO: usize = 2;",
4148                "three.rs": "const THREE: usize = 3;",
4149                "four.rs": "const FOUR: usize = 3;",
4150                "five.rs": "const FIVE: usize = 3;",
4151            }),
4152        )
4153        .await;
4154
4155    let (project_a, worktree_id) = client_a.build_local_project("/test", cx_a).await;
4156
4157    // Share a project as client A
4158    let active_call_a = cx_a.read(ActiveCall::global);
4159    let project_id = active_call_a
4160        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4161        .await
4162        .unwrap();
4163
4164    // Join the project as client B and open all three files.
4165    let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
4166    let guest_buffers = futures::future::try_join_all(file_names.iter().map(|file_name| {
4167        project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, file_name), cx))
4168    }))
4169    .await
4170    .unwrap();
4171
4172    // Simulate a language server reporting errors for a file.
4173    let fake_language_server = fake_language_servers.next().await.unwrap();
4174    fake_language_server
4175        .request::<lsp::request::WorkDoneProgressCreate>(lsp::WorkDoneProgressCreateParams {
4176            token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4177        })
4178        .await
4179        .unwrap();
4180    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
4181        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4182        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin(
4183            lsp::WorkDoneProgressBegin {
4184                title: "Progress Began".into(),
4185                ..Default::default()
4186            },
4187        )),
4188    });
4189    for file_name in file_names {
4190        fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
4191            lsp::PublishDiagnosticsParams {
4192                uri: lsp::Url::from_file_path(Path::new("/test").join(file_name)).unwrap(),
4193                version: None,
4194                diagnostics: vec![lsp::Diagnostic {
4195                    severity: Some(lsp::DiagnosticSeverity::WARNING),
4196                    source: Some("the-disk-based-diagnostics-source".into()),
4197                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
4198                    message: "message one".to_string(),
4199                    ..Default::default()
4200                }],
4201            },
4202        );
4203    }
4204    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
4205        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4206        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End(
4207            lsp::WorkDoneProgressEnd { message: None },
4208        )),
4209    });
4210
4211    // When the "disk base diagnostics finished" message is received, the buffers'
4212    // diagnostics are expected to be present.
4213    let disk_based_diagnostics_finished = Arc::new(AtomicBool::new(false));
4214    project_b.update(cx_b, {
4215        let project_b = project_b.clone();
4216        let disk_based_diagnostics_finished = disk_based_diagnostics_finished.clone();
4217        move |_, cx| {
4218            cx.subscribe(&project_b, move |_, _, event, cx| {
4219                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
4220                    disk_based_diagnostics_finished.store(true, SeqCst);
4221                    for buffer in &guest_buffers {
4222                        assert_eq!(
4223                            buffer
4224                                .read(cx)
4225                                .snapshot()
4226                                .diagnostics_in_range::<_, usize>(0..5, false)
4227                                .count(),
4228                            1,
4229                            "expected a diagnostic for buffer {:?}",
4230                            buffer.read(cx).file().unwrap().path(),
4231                        );
4232                    }
4233                }
4234            })
4235            .detach();
4236        }
4237    });
4238
4239    executor.run_until_parked();
4240    assert!(disk_based_diagnostics_finished.load(SeqCst));
4241}
4242
4243#[gpui::test(iterations = 10)]
4244async fn test_reloading_buffer_manually(
4245    executor: BackgroundExecutor,
4246    cx_a: &mut TestAppContext,
4247    cx_b: &mut TestAppContext,
4248) {
4249    let mut server = TestServer::start(executor.clone()).await;
4250    let client_a = server.create_client(cx_a, "user_a").await;
4251    let client_b = server.create_client(cx_b, "user_b").await;
4252    server
4253        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4254        .await;
4255    let active_call_a = cx_a.read(ActiveCall::global);
4256
4257    client_a
4258        .fs()
4259        .insert_tree("/a", json!({ "a.rs": "let one = 1;" }))
4260        .await;
4261    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
4262    let buffer_a = project_a
4263        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
4264        .await
4265        .unwrap();
4266    let project_id = active_call_a
4267        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4268        .await
4269        .unwrap();
4270
4271    let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
4272
4273    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4274    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4275    buffer_b.update(cx_b, |buffer, cx| {
4276        buffer.edit([(4..7, "six")], None, cx);
4277        buffer.edit([(10..11, "6")], None, cx);
4278        assert_eq!(buffer.text(), "let six = 6;");
4279        assert!(buffer.is_dirty());
4280        assert!(!buffer.has_conflict());
4281    });
4282    executor.run_until_parked();
4283
4284    buffer_a.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "let six = 6;"));
4285
4286    client_a
4287        .fs()
4288        .save(
4289            "/a/a.rs".as_ref(),
4290            &Rope::from("let seven = 7;"),
4291            LineEnding::Unix,
4292        )
4293        .await
4294        .unwrap();
4295    executor.run_until_parked();
4296
4297    buffer_a.read_with(cx_a, |buffer, _| assert!(buffer.has_conflict()));
4298
4299    buffer_b.read_with(cx_b, |buffer, _| assert!(buffer.has_conflict()));
4300
4301    project_b
4302        .update(cx_b, |project, cx| {
4303            project.reload_buffers(HashSet::from_iter([buffer_b.clone()]), true, cx)
4304        })
4305        .await
4306        .unwrap();
4307
4308    buffer_a.read_with(cx_a, |buffer, _| {
4309        assert_eq!(buffer.text(), "let seven = 7;");
4310        assert!(!buffer.is_dirty());
4311        assert!(!buffer.has_conflict());
4312    });
4313
4314    buffer_b.read_with(cx_b, |buffer, _| {
4315        assert_eq!(buffer.text(), "let seven = 7;");
4316        assert!(!buffer.is_dirty());
4317        assert!(!buffer.has_conflict());
4318    });
4319
4320    buffer_a.update(cx_a, |buffer, cx| {
4321        // Undoing on the host is a no-op when the reload was initiated by the guest.
4322        buffer.undo(cx);
4323        assert_eq!(buffer.text(), "let seven = 7;");
4324        assert!(!buffer.is_dirty());
4325        assert!(!buffer.has_conflict());
4326    });
4327    buffer_b.update(cx_b, |buffer, cx| {
4328        // Undoing on the guest rolls back the buffer to before it was reloaded but the conflict gets cleared.
4329        buffer.undo(cx);
4330        assert_eq!(buffer.text(), "let six = 6;");
4331        assert!(buffer.is_dirty());
4332        assert!(!buffer.has_conflict());
4333    });
4334}
4335
4336#[gpui::test(iterations = 10)]
4337async fn test_formatting_buffer(
4338    executor: BackgroundExecutor,
4339    cx_a: &mut TestAppContext,
4340    cx_b: &mut TestAppContext,
4341) {
4342    executor.allow_parking();
4343    let mut server = TestServer::start(executor.clone()).await;
4344    let client_a = server.create_client(cx_a, "user_a").await;
4345    let client_b = server.create_client(cx_b, "user_b").await;
4346    server
4347        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4348        .await;
4349    let active_call_a = cx_a.read(ActiveCall::global);
4350
4351    client_a.language_registry().add(rust_lang());
4352    let mut fake_language_servers = client_a
4353        .language_registry()
4354        .register_fake_lsp_adapter("Rust", FakeLspAdapter::default());
4355
4356    // Here we insert a fake tree with a directory that exists on disk. This is needed
4357    // because later we'll invoke a command, which requires passing a working directory
4358    // that points to a valid location on disk.
4359    let directory = env::current_dir().unwrap();
4360    client_a
4361        .fs()
4362        .insert_tree(&directory, json!({ "a.rs": "let one = \"two\"" }))
4363        .await;
4364    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
4365    let project_id = active_call_a
4366        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4367        .await
4368        .unwrap();
4369    let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
4370
4371    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4372    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4373
4374    let fake_language_server = fake_language_servers.next().await.unwrap();
4375    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
4376        Ok(Some(vec![
4377            lsp::TextEdit {
4378                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 4)),
4379                new_text: "h".to_string(),
4380            },
4381            lsp::TextEdit {
4382                range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 7)),
4383                new_text: "y".to_string(),
4384            },
4385        ]))
4386    });
4387
4388    project_b
4389        .update(cx_b, |project, cx| {
4390            project.format(
4391                HashSet::from_iter([buffer_b.clone()]),
4392                true,
4393                FormatTrigger::Save,
4394                cx,
4395            )
4396        })
4397        .await
4398        .unwrap();
4399
4400    // The edits from the LSP are applied, and a final newline is added.
4401    assert_eq!(
4402        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4403        "let honey = \"two\"\n"
4404    );
4405
4406    // Ensure buffer can be formatted using an external command. Notice how the
4407    // host's configuration is honored as opposed to using the guest's settings.
4408    cx_a.update(|cx| {
4409        SettingsStore::update_global(cx, |store, cx| {
4410            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4411                file.defaults.formatter = Some(Formatter::External {
4412                    command: "awk".into(),
4413                    arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()].into(),
4414                });
4415            });
4416        });
4417    });
4418    project_b
4419        .update(cx_b, |project, cx| {
4420            project.format(
4421                HashSet::from_iter([buffer_b.clone()]),
4422                true,
4423                FormatTrigger::Save,
4424                cx,
4425            )
4426        })
4427        .await
4428        .unwrap();
4429    assert_eq!(
4430        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4431        format!("let honey = \"{}/a.rs\"\n", directory.to_str().unwrap())
4432    );
4433}
4434
4435#[gpui::test(iterations = 10)]
4436async fn test_prettier_formatting_buffer(
4437    executor: BackgroundExecutor,
4438    cx_a: &mut TestAppContext,
4439    cx_b: &mut TestAppContext,
4440) {
4441    let mut server = TestServer::start(executor.clone()).await;
4442    let client_a = server.create_client(cx_a, "user_a").await;
4443    let client_b = server.create_client(cx_b, "user_b").await;
4444    server
4445        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4446        .await;
4447    let active_call_a = cx_a.read(ActiveCall::global);
4448
4449    let test_plugin = "test_plugin";
4450
4451    client_a.language_registry().add(Arc::new(Language::new(
4452        LanguageConfig {
4453            name: "TypeScript".into(),
4454            matcher: LanguageMatcher {
4455                path_suffixes: vec!["ts".to_string()],
4456                ..Default::default()
4457            },
4458            ..Default::default()
4459        },
4460        Some(tree_sitter_rust::language()),
4461    )));
4462    let mut fake_language_servers = client_a.language_registry().register_fake_lsp_adapter(
4463        "TypeScript",
4464        FakeLspAdapter {
4465            prettier_plugins: vec![test_plugin],
4466            ..Default::default()
4467        },
4468    );
4469
4470    // Here we insert a fake tree with a directory that exists on disk. This is needed
4471    // because later we'll invoke a command, which requires passing a working directory
4472    // that points to a valid location on disk.
4473    let directory = env::current_dir().unwrap();
4474    let buffer_text = "let one = \"two\"";
4475    client_a
4476        .fs()
4477        .insert_tree(&directory, json!({ "a.ts": buffer_text }))
4478        .await;
4479    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
4480    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
4481    let open_buffer = project_a.update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.ts"), cx));
4482    let buffer_a = cx_a.executor().spawn(open_buffer).await.unwrap();
4483
4484    let project_id = active_call_a
4485        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4486        .await
4487        .unwrap();
4488    let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
4489    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.ts"), cx));
4490    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4491
4492    cx_a.update(|cx| {
4493        SettingsStore::update_global(cx, |store, cx| {
4494            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4495                file.defaults.formatter = Some(Formatter::Auto);
4496                file.defaults.prettier = Some(PrettierSettings {
4497                    allowed: true,
4498                    ..PrettierSettings::default()
4499                });
4500            });
4501        });
4502    });
4503    cx_b.update(|cx| {
4504        SettingsStore::update_global(cx, |store, cx| {
4505            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4506                file.defaults.formatter = Some(Formatter::LanguageServer);
4507                file.defaults.prettier = Some(PrettierSettings {
4508                    allowed: true,
4509                    ..PrettierSettings::default()
4510                });
4511            });
4512        });
4513    });
4514    let fake_language_server = fake_language_servers.next().await.unwrap();
4515    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
4516        panic!(
4517            "Unexpected: prettier should be preferred since it's enabled and language supports it"
4518        )
4519    });
4520
4521    project_b
4522        .update(cx_b, |project, cx| {
4523            project.format(
4524                HashSet::from_iter([buffer_b.clone()]),
4525                true,
4526                FormatTrigger::Save,
4527                cx,
4528            )
4529        })
4530        .await
4531        .unwrap();
4532
4533    executor.run_until_parked();
4534    assert_eq!(
4535        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4536        buffer_text.to_string() + "\n" + prettier_format_suffix,
4537        "Prettier formatting was not applied to client buffer after client's request"
4538    );
4539
4540    project_a
4541        .update(cx_a, |project, cx| {
4542            project.format(
4543                HashSet::from_iter([buffer_a.clone()]),
4544                true,
4545                FormatTrigger::Manual,
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 + "\n" + prettier_format_suffix,
4556        "Prettier formatting was not applied to client buffer after host's request"
4557    );
4558}
4559
4560#[gpui::test(iterations = 10)]
4561async fn test_definition(
4562    executor: BackgroundExecutor,
4563    cx_a: &mut TestAppContext,
4564    cx_b: &mut TestAppContext,
4565) {
4566    let mut server = TestServer::start(executor.clone()).await;
4567    let client_a = server.create_client(cx_a, "user_a").await;
4568    let client_b = server.create_client(cx_b, "user_b").await;
4569    server
4570        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4571        .await;
4572    let active_call_a = cx_a.read(ActiveCall::global);
4573
4574    let mut fake_language_servers = client_a
4575        .language_registry()
4576        .register_fake_lsp_adapter("Rust", Default::default());
4577    client_a.language_registry().add(rust_lang());
4578
4579    client_a
4580        .fs()
4581        .insert_tree(
4582            "/root",
4583            json!({
4584                "dir-1": {
4585                    "a.rs": "const ONE: usize = b::TWO + b::THREE;",
4586                },
4587                "dir-2": {
4588                    "b.rs": "const TWO: c::T2 = 2;\nconst THREE: usize = 3;",
4589                    "c.rs": "type T2 = usize;",
4590                }
4591            }),
4592        )
4593        .await;
4594    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4595    let project_id = active_call_a
4596        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4597        .await
4598        .unwrap();
4599    let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
4600
4601    // Open the file on client B.
4602    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4603    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4604
4605    // Request the definition of a symbol as the guest.
4606    let fake_language_server = fake_language_servers.next().await.unwrap();
4607    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4608        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4609            lsp::Location::new(
4610                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4611                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4612            ),
4613        )))
4614    });
4615
4616    let definitions_1 = project_b
4617        .update(cx_b, |p, cx| p.definition(&buffer_b, 23, cx))
4618        .await
4619        .unwrap();
4620    cx_b.read(|cx| {
4621        assert_eq!(definitions_1.len(), 1);
4622        assert_eq!(project_b.read(cx).worktrees().count(), 2);
4623        let target_buffer = definitions_1[0].target.buffer.read(cx);
4624        assert_eq!(
4625            target_buffer.text(),
4626            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4627        );
4628        assert_eq!(
4629            definitions_1[0].target.range.to_point(target_buffer),
4630            Point::new(0, 6)..Point::new(0, 9)
4631        );
4632    });
4633
4634    // Try getting more definitions for the same buffer, ensuring the buffer gets reused from
4635    // the previous call to `definition`.
4636    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4637        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4638            lsp::Location::new(
4639                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4640                lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)),
4641            ),
4642        )))
4643    });
4644
4645    let definitions_2 = project_b
4646        .update(cx_b, |p, cx| p.definition(&buffer_b, 33, cx))
4647        .await
4648        .unwrap();
4649    cx_b.read(|cx| {
4650        assert_eq!(definitions_2.len(), 1);
4651        assert_eq!(project_b.read(cx).worktrees().count(), 2);
4652        let target_buffer = definitions_2[0].target.buffer.read(cx);
4653        assert_eq!(
4654            target_buffer.text(),
4655            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4656        );
4657        assert_eq!(
4658            definitions_2[0].target.range.to_point(target_buffer),
4659            Point::new(1, 6)..Point::new(1, 11)
4660        );
4661    });
4662    assert_eq!(
4663        definitions_1[0].target.buffer,
4664        definitions_2[0].target.buffer
4665    );
4666
4667    fake_language_server.handle_request::<lsp::request::GotoTypeDefinition, _, _>(
4668        |req, _| async move {
4669            assert_eq!(
4670                req.text_document_position_params.position,
4671                lsp::Position::new(0, 7)
4672            );
4673            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4674                lsp::Location::new(
4675                    lsp::Url::from_file_path("/root/dir-2/c.rs").unwrap(),
4676                    lsp::Range::new(lsp::Position::new(0, 5), lsp::Position::new(0, 7)),
4677                ),
4678            )))
4679        },
4680    );
4681
4682    let type_definitions = project_b
4683        .update(cx_b, |p, cx| p.type_definition(&buffer_b, 7, cx))
4684        .await
4685        .unwrap();
4686    cx_b.read(|cx| {
4687        assert_eq!(type_definitions.len(), 1);
4688        let target_buffer = type_definitions[0].target.buffer.read(cx);
4689        assert_eq!(target_buffer.text(), "type T2 = usize;");
4690        assert_eq!(
4691            type_definitions[0].target.range.to_point(target_buffer),
4692            Point::new(0, 5)..Point::new(0, 7)
4693        );
4694    });
4695}
4696
4697#[gpui::test(iterations = 10)]
4698async fn test_references(
4699    executor: BackgroundExecutor,
4700    cx_a: &mut TestAppContext,
4701    cx_b: &mut TestAppContext,
4702) {
4703    let mut server = TestServer::start(executor.clone()).await;
4704    let client_a = server.create_client(cx_a, "user_a").await;
4705    let client_b = server.create_client(cx_b, "user_b").await;
4706    server
4707        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4708        .await;
4709    let active_call_a = cx_a.read(ActiveCall::global);
4710
4711    client_a.language_registry().add(rust_lang());
4712    let mut fake_language_servers = client_a.language_registry().register_fake_lsp_adapter(
4713        "Rust",
4714        FakeLspAdapter {
4715            name: "my-fake-lsp-adapter",
4716            capabilities: lsp::ServerCapabilities {
4717                references_provider: Some(lsp::OneOf::Left(true)),
4718                ..Default::default()
4719            },
4720            ..Default::default()
4721        },
4722    );
4723
4724    client_a
4725        .fs()
4726        .insert_tree(
4727            "/root",
4728            json!({
4729                "dir-1": {
4730                    "one.rs": "const ONE: usize = 1;",
4731                    "two.rs": "const TWO: usize = one::ONE + one::ONE;",
4732                },
4733                "dir-2": {
4734                    "three.rs": "const THREE: usize = two::TWO + one::ONE;",
4735                }
4736            }),
4737        )
4738        .await;
4739    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4740    let project_id = active_call_a
4741        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4742        .await
4743        .unwrap();
4744    let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
4745
4746    // Open the file on client B.
4747    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx));
4748    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4749
4750    // Request references to a symbol as the guest.
4751    let fake_language_server = fake_language_servers.next().await.unwrap();
4752    let (lsp_response_tx, rx) = mpsc::unbounded::<Result<Option<Vec<lsp::Location>>>>();
4753    fake_language_server.handle_request::<lsp::request::References, _, _>({
4754        let rx = Arc::new(Mutex::new(Some(rx)));
4755        move |params, _| {
4756            assert_eq!(
4757                params.text_document_position.text_document.uri.as_str(),
4758                "file:///root/dir-1/one.rs"
4759            );
4760            let rx = rx.clone();
4761            async move {
4762                let mut response_rx = rx.lock().take().unwrap();
4763                let result = response_rx.next().await.unwrap();
4764                *rx.lock() = Some(response_rx);
4765                result
4766            }
4767        }
4768    });
4769
4770    let references = project_b.update(cx_b, |p, cx| p.references(&buffer_b, 7, cx));
4771
4772    // User is informed that a request is pending.
4773    executor.run_until_parked();
4774    project_b.read_with(cx_b, |project, _| {
4775        let status = project.language_server_statuses().next().unwrap().1;
4776        assert_eq!(status.name, "my-fake-lsp-adapter");
4777        assert_eq!(
4778            status.pending_work.values().next().unwrap().message,
4779            Some("Finding references...".into())
4780        );
4781    });
4782
4783    // Cause the language server to respond.
4784    lsp_response_tx
4785        .unbounded_send(Ok(Some(vec![
4786            lsp::Location {
4787                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4788                range: lsp::Range::new(lsp::Position::new(0, 24), lsp::Position::new(0, 27)),
4789            },
4790            lsp::Location {
4791                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4792                range: lsp::Range::new(lsp::Position::new(0, 35), lsp::Position::new(0, 38)),
4793            },
4794            lsp::Location {
4795                uri: lsp::Url::from_file_path("/root/dir-2/three.rs").unwrap(),
4796                range: lsp::Range::new(lsp::Position::new(0, 37), lsp::Position::new(0, 40)),
4797            },
4798        ])))
4799        .unwrap();
4800
4801    let references = references.await.unwrap();
4802    executor.run_until_parked();
4803    project_b.read_with(cx_b, |project, cx| {
4804        // User is informed that a request is no longer pending.
4805        let status = project.language_server_statuses().next().unwrap().1;
4806        assert!(status.pending_work.is_empty());
4807
4808        assert_eq!(references.len(), 3);
4809        assert_eq!(project.worktrees().count(), 2);
4810
4811        let two_buffer = references[0].buffer.read(cx);
4812        let three_buffer = references[2].buffer.read(cx);
4813        assert_eq!(
4814            two_buffer.file().unwrap().path().as_ref(),
4815            Path::new("two.rs")
4816        );
4817        assert_eq!(references[1].buffer, references[0].buffer);
4818        assert_eq!(
4819            three_buffer.file().unwrap().full_path(cx),
4820            Path::new("/root/dir-2/three.rs")
4821        );
4822
4823        assert_eq!(references[0].range.to_offset(two_buffer), 24..27);
4824        assert_eq!(references[1].range.to_offset(two_buffer), 35..38);
4825        assert_eq!(references[2].range.to_offset(three_buffer), 37..40);
4826    });
4827
4828    let references = project_b.update(cx_b, |p, cx| p.references(&buffer_b, 7, cx));
4829
4830    // User is informed that a request is pending.
4831    executor.run_until_parked();
4832    project_b.read_with(cx_b, |project, _| {
4833        let status = project.language_server_statuses().next().unwrap().1;
4834        assert_eq!(status.name, "my-fake-lsp-adapter");
4835        assert_eq!(
4836            status.pending_work.values().next().unwrap().message,
4837            Some("Finding references...".into())
4838        );
4839    });
4840
4841    // Cause the LSP request to fail.
4842    lsp_response_tx
4843        .unbounded_send(Err(anyhow!("can't find references")))
4844        .unwrap();
4845    references.await.unwrap_err();
4846
4847    // User is informed that the request is no longer pending.
4848    executor.run_until_parked();
4849    project_b.read_with(cx_b, |project, _| {
4850        let status = project.language_server_statuses().next().unwrap().1;
4851        assert!(status.pending_work.is_empty());
4852    });
4853}
4854
4855#[gpui::test(iterations = 10)]
4856async fn test_project_search(
4857    executor: BackgroundExecutor,
4858    cx_a: &mut TestAppContext,
4859    cx_b: &mut TestAppContext,
4860) {
4861    let mut server = TestServer::start(executor.clone()).await;
4862    let client_a = server.create_client(cx_a, "user_a").await;
4863    let client_b = server.create_client(cx_b, "user_b").await;
4864    server
4865        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4866        .await;
4867    let active_call_a = cx_a.read(ActiveCall::global);
4868
4869    client_a
4870        .fs()
4871        .insert_tree(
4872            "/root",
4873            json!({
4874                "dir-1": {
4875                    "a": "hello world",
4876                    "b": "goodnight moon",
4877                    "c": "a world of goo",
4878                    "d": "world champion of clown world",
4879                },
4880                "dir-2": {
4881                    "e": "disney world is fun",
4882                }
4883            }),
4884        )
4885        .await;
4886    let (project_a, _) = client_a.build_local_project("/root/dir-1", cx_a).await;
4887    let (worktree_2, _) = project_a
4888        .update(cx_a, |p, cx| {
4889            p.find_or_create_local_worktree("/root/dir-2", true, cx)
4890        })
4891        .await
4892        .unwrap();
4893    worktree_2
4894        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
4895        .await;
4896    let project_id = active_call_a
4897        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4898        .await
4899        .unwrap();
4900
4901    let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
4902
4903    // Perform a search as the guest.
4904    let mut results = HashMap::default();
4905    let mut search_rx = project_b.update(cx_b, |project, cx| {
4906        project.search(
4907            SearchQuery::text(
4908                "world",
4909                false,
4910                false,
4911                false,
4912                Default::default(),
4913                Default::default(),
4914            )
4915            .unwrap(),
4916            cx,
4917        )
4918    });
4919    while let Some(result) = search_rx.next().await {
4920        match result {
4921            SearchResult::Buffer { buffer, ranges } => {
4922                results.entry(buffer).or_insert(ranges);
4923            }
4924            SearchResult::LimitReached => {
4925                panic!("Unexpectedly reached search limit in tests. If you do want to assert limit-reached, change this panic call.")
4926            }
4927        };
4928    }
4929
4930    let mut ranges_by_path = results
4931        .into_iter()
4932        .map(|(buffer, ranges)| {
4933            buffer.read_with(cx_b, |buffer, cx| {
4934                let path = buffer.file().unwrap().full_path(cx);
4935                let offset_ranges = ranges
4936                    .into_iter()
4937                    .map(|range| range.to_offset(buffer))
4938                    .collect::<Vec<_>>();
4939                (path, offset_ranges)
4940            })
4941        })
4942        .collect::<Vec<_>>();
4943    ranges_by_path.sort_by_key(|(path, _)| path.clone());
4944
4945    assert_eq!(
4946        ranges_by_path,
4947        &[
4948            (PathBuf::from("dir-1/a"), vec![6..11]),
4949            (PathBuf::from("dir-1/c"), vec![2..7]),
4950            (PathBuf::from("dir-1/d"), vec![0..5, 24..29]),
4951            (PathBuf::from("dir-2/e"), vec![7..12]),
4952        ]
4953    );
4954}
4955
4956#[gpui::test(iterations = 10)]
4957async fn test_document_highlights(
4958    executor: BackgroundExecutor,
4959    cx_a: &mut TestAppContext,
4960    cx_b: &mut TestAppContext,
4961) {
4962    let mut server = TestServer::start(executor.clone()).await;
4963    let client_a = server.create_client(cx_a, "user_a").await;
4964    let client_b = server.create_client(cx_b, "user_b").await;
4965    server
4966        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4967        .await;
4968    let active_call_a = cx_a.read(ActiveCall::global);
4969
4970    client_a
4971        .fs()
4972        .insert_tree(
4973            "/root-1",
4974            json!({
4975                "main.rs": "fn double(number: i32) -> i32 { number + number }",
4976            }),
4977        )
4978        .await;
4979
4980    let mut fake_language_servers = client_a
4981        .language_registry()
4982        .register_fake_lsp_adapter("Rust", Default::default());
4983    client_a.language_registry().add(rust_lang());
4984
4985    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4986    let project_id = active_call_a
4987        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4988        .await
4989        .unwrap();
4990    let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
4991
4992    // Open the file on client B.
4993    let open_b = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx));
4994    let buffer_b = cx_b.executor().spawn(open_b).await.unwrap();
4995
4996    // Request document highlights as the guest.
4997    let fake_language_server = fake_language_servers.next().await.unwrap();
4998    fake_language_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
4999        |params, _| async move {
5000            assert_eq!(
5001                params
5002                    .text_document_position_params
5003                    .text_document
5004                    .uri
5005                    .as_str(),
5006                "file:///root-1/main.rs"
5007            );
5008            assert_eq!(
5009                params.text_document_position_params.position,
5010                lsp::Position::new(0, 34)
5011            );
5012            Ok(Some(vec![
5013                lsp::DocumentHighlight {
5014                    kind: Some(lsp::DocumentHighlightKind::WRITE),
5015                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 16)),
5016                },
5017                lsp::DocumentHighlight {
5018                    kind: Some(lsp::DocumentHighlightKind::READ),
5019                    range: lsp::Range::new(lsp::Position::new(0, 32), lsp::Position::new(0, 38)),
5020                },
5021                lsp::DocumentHighlight {
5022                    kind: Some(lsp::DocumentHighlightKind::READ),
5023                    range: lsp::Range::new(lsp::Position::new(0, 41), lsp::Position::new(0, 47)),
5024                },
5025            ]))
5026        },
5027    );
5028
5029    let highlights = project_b
5030        .update(cx_b, |p, cx| p.document_highlights(&buffer_b, 34, cx))
5031        .await
5032        .unwrap();
5033
5034    buffer_b.read_with(cx_b, |buffer, _| {
5035        let snapshot = buffer.snapshot();
5036
5037        let highlights = highlights
5038            .into_iter()
5039            .map(|highlight| (highlight.kind, highlight.range.to_offset(&snapshot)))
5040            .collect::<Vec<_>>();
5041        assert_eq!(
5042            highlights,
5043            &[
5044                (lsp::DocumentHighlightKind::WRITE, 10..16),
5045                (lsp::DocumentHighlightKind::READ, 32..38),
5046                (lsp::DocumentHighlightKind::READ, 41..47)
5047            ]
5048        )
5049    });
5050}
5051
5052#[gpui::test(iterations = 10)]
5053async fn test_lsp_hover(
5054    executor: BackgroundExecutor,
5055    cx_a: &mut TestAppContext,
5056    cx_b: &mut TestAppContext,
5057) {
5058    let mut server = TestServer::start(executor.clone()).await;
5059    let client_a = server.create_client(cx_a, "user_a").await;
5060    let client_b = server.create_client(cx_b, "user_b").await;
5061    server
5062        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5063        .await;
5064    let active_call_a = cx_a.read(ActiveCall::global);
5065
5066    client_a
5067        .fs()
5068        .insert_tree(
5069            "/root-1",
5070            json!({
5071                "main.rs": "use std::collections::HashMap;",
5072            }),
5073        )
5074        .await;
5075
5076    client_a.language_registry().add(rust_lang());
5077    let language_server_names = ["rust-analyzer", "CrabLang-ls"];
5078    let mut fake_language_servers = client_a
5079        .language_registry()
5080        .register_specific_fake_lsp_adapter(
5081            "Rust",
5082            true,
5083            FakeLspAdapter {
5084                name: "rust-analyzer",
5085                capabilities: lsp::ServerCapabilities {
5086                    hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
5087                    ..lsp::ServerCapabilities::default()
5088                },
5089                ..FakeLspAdapter::default()
5090            },
5091        );
5092    let _other_server = client_a
5093        .language_registry()
5094        .register_specific_fake_lsp_adapter(
5095            "Rust",
5096            false,
5097            FakeLspAdapter {
5098                name: "CrabLang-ls",
5099                capabilities: lsp::ServerCapabilities {
5100                    hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
5101                    ..lsp::ServerCapabilities::default()
5102                },
5103                ..FakeLspAdapter::default()
5104            },
5105        );
5106
5107    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
5108    let project_id = active_call_a
5109        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5110        .await
5111        .unwrap();
5112    let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
5113
5114    // Open the file as the guest
5115    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx));
5116    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
5117
5118    let mut servers_with_hover_requests = HashMap::default();
5119    for i in 0..language_server_names.len() {
5120        let new_server = fake_language_servers.next().await.unwrap_or_else(|| {
5121            panic!(
5122                "Failed to get language server #{i} with name {}",
5123                &language_server_names[i]
5124            )
5125        });
5126        let new_server_name = new_server.server.name();
5127        assert!(
5128            !servers_with_hover_requests.contains_key(new_server_name),
5129            "Unexpected: initialized server with the same name twice. Name: `{new_server_name}`"
5130        );
5131        let new_server_name = new_server_name.to_string();
5132        match new_server_name.as_str() {
5133            "CrabLang-ls" => {
5134                servers_with_hover_requests.insert(
5135                    new_server_name.clone(),
5136                    new_server.handle_request::<lsp::request::HoverRequest, _, _>(
5137                        move |params, _| {
5138                            assert_eq!(
5139                                params
5140                                    .text_document_position_params
5141                                    .text_document
5142                                    .uri
5143                                    .as_str(),
5144                                "file:///root-1/main.rs"
5145                            );
5146                            let name = new_server_name.clone();
5147                            async move {
5148                                Ok(Some(lsp::Hover {
5149                                    contents: lsp::HoverContents::Scalar(
5150                                        lsp::MarkedString::String(format!("{name} hover")),
5151                                    ),
5152                                    range: None,
5153                                }))
5154                            }
5155                        },
5156                    ),
5157                );
5158            }
5159            "rust-analyzer" => {
5160                servers_with_hover_requests.insert(
5161                    new_server_name.clone(),
5162                    new_server.handle_request::<lsp::request::HoverRequest, _, _>(
5163                        |params, _| async move {
5164                            assert_eq!(
5165                                params
5166                                    .text_document_position_params
5167                                    .text_document
5168                                    .uri
5169                                    .as_str(),
5170                                "file:///root-1/main.rs"
5171                            );
5172                            assert_eq!(
5173                                params.text_document_position_params.position,
5174                                lsp::Position::new(0, 22)
5175                            );
5176                            Ok(Some(lsp::Hover {
5177                                contents: lsp::HoverContents::Array(vec![
5178                                    lsp::MarkedString::String("Test hover content.".to_string()),
5179                                    lsp::MarkedString::LanguageString(lsp::LanguageString {
5180                                        language: "Rust".to_string(),
5181                                        value: "let foo = 42;".to_string(),
5182                                    }),
5183                                ]),
5184                                range: Some(lsp::Range::new(
5185                                    lsp::Position::new(0, 22),
5186                                    lsp::Position::new(0, 29),
5187                                )),
5188                            }))
5189                        },
5190                    ),
5191                );
5192            }
5193            unexpected => panic!("Unexpected server name: {unexpected}"),
5194        }
5195    }
5196
5197    // Request hover information as the guest.
5198    let mut hovers = project_b
5199        .update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
5200        .await;
5201    assert_eq!(
5202        hovers.len(),
5203        2,
5204        "Expected two hovers from both language servers, but got: {hovers:?}"
5205    );
5206
5207    let _: Vec<()> = futures::future::join_all(servers_with_hover_requests.into_values().map(
5208        |mut hover_request| async move {
5209            hover_request
5210                .next()
5211                .await
5212                .expect("All hover requests should have been triggered")
5213        },
5214    ))
5215    .await;
5216
5217    hovers.sort_by_key(|hover| hover.contents.len());
5218    let first_hover = hovers.first().cloned().unwrap();
5219    assert_eq!(
5220        first_hover.contents,
5221        vec![project::HoverBlock {
5222            text: "CrabLang-ls hover".to_string(),
5223            kind: HoverBlockKind::Markdown,
5224        },]
5225    );
5226    let second_hover = hovers.last().cloned().unwrap();
5227    assert_eq!(
5228        second_hover.contents,
5229        vec![
5230            project::HoverBlock {
5231                text: "Test hover content.".to_string(),
5232                kind: HoverBlockKind::Markdown,
5233            },
5234            project::HoverBlock {
5235                text: "let foo = 42;".to_string(),
5236                kind: HoverBlockKind::Code {
5237                    language: "Rust".to_string()
5238                },
5239            }
5240        ]
5241    );
5242    buffer_b.read_with(cx_b, |buffer, _| {
5243        let snapshot = buffer.snapshot();
5244        assert_eq!(second_hover.range.unwrap().to_offset(&snapshot), 22..29);
5245    });
5246}
5247
5248#[gpui::test(iterations = 10)]
5249async fn test_project_symbols(
5250    executor: BackgroundExecutor,
5251    cx_a: &mut TestAppContext,
5252    cx_b: &mut TestAppContext,
5253) {
5254    let mut server = TestServer::start(executor.clone()).await;
5255    let client_a = server.create_client(cx_a, "user_a").await;
5256    let client_b = server.create_client(cx_b, "user_b").await;
5257    server
5258        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5259        .await;
5260    let active_call_a = cx_a.read(ActiveCall::global);
5261
5262    client_a.language_registry().add(rust_lang());
5263    let mut fake_language_servers = client_a
5264        .language_registry()
5265        .register_fake_lsp_adapter("Rust", Default::default());
5266
5267    client_a
5268        .fs()
5269        .insert_tree(
5270            "/code",
5271            json!({
5272                "crate-1": {
5273                    "one.rs": "const ONE: usize = 1;",
5274                },
5275                "crate-2": {
5276                    "two.rs": "const TWO: usize = 2; const THREE: usize = 3;",
5277                },
5278                "private": {
5279                    "passwords.txt": "the-password",
5280                }
5281            }),
5282        )
5283        .await;
5284    let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await;
5285    let project_id = active_call_a
5286        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5287        .await
5288        .unwrap();
5289    let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
5290
5291    // Cause the language server to start.
5292    let open_buffer_task =
5293        project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx));
5294    let _buffer = cx_b.executor().spawn(open_buffer_task).await.unwrap();
5295
5296    let fake_language_server = fake_language_servers.next().await.unwrap();
5297    fake_language_server.handle_request::<lsp::WorkspaceSymbolRequest, _, _>(|_, _| async move {
5298        Ok(Some(lsp::WorkspaceSymbolResponse::Flat(vec![
5299            #[allow(deprecated)]
5300            lsp::SymbolInformation {
5301                name: "TWO".into(),
5302                location: lsp::Location {
5303                    uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
5304                    range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5305                },
5306                kind: lsp::SymbolKind::CONSTANT,
5307                tags: None,
5308                container_name: None,
5309                deprecated: None,
5310            },
5311        ])))
5312    });
5313
5314    // Request the definition of a symbol as the guest.
5315    let symbols = project_b
5316        .update(cx_b, |p, cx| p.symbols("two", cx))
5317        .await
5318        .unwrap();
5319    assert_eq!(symbols.len(), 1);
5320    assert_eq!(symbols[0].name, "TWO");
5321
5322    // Open one of the returned symbols.
5323    let buffer_b_2 = project_b
5324        .update(cx_b, |project, cx| {
5325            project.open_buffer_for_symbol(&symbols[0], cx)
5326        })
5327        .await
5328        .unwrap();
5329
5330    buffer_b_2.read_with(cx_b, |buffer, cx| {
5331        assert_eq!(
5332            buffer.file().unwrap().full_path(cx),
5333            Path::new("/code/crate-2/two.rs")
5334        );
5335    });
5336
5337    // Attempt to craft a symbol and violate host's privacy by opening an arbitrary file.
5338    let mut fake_symbol = symbols[0].clone();
5339    fake_symbol.path.path = Path::new("/code/secrets").into();
5340    let error = project_b
5341        .update(cx_b, |project, cx| {
5342            project.open_buffer_for_symbol(&fake_symbol, cx)
5343        })
5344        .await
5345        .unwrap_err();
5346    assert!(error.to_string().contains("invalid symbol signature"));
5347}
5348
5349#[gpui::test(iterations = 10)]
5350async fn test_open_buffer_while_getting_definition_pointing_to_it(
5351    executor: BackgroundExecutor,
5352    cx_a: &mut TestAppContext,
5353    cx_b: &mut TestAppContext,
5354    mut rng: StdRng,
5355) {
5356    let mut server = TestServer::start(executor.clone()).await;
5357    let client_a = server.create_client(cx_a, "user_a").await;
5358    let client_b = server.create_client(cx_b, "user_b").await;
5359    server
5360        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5361        .await;
5362    let active_call_a = cx_a.read(ActiveCall::global);
5363
5364    client_a.language_registry().add(rust_lang());
5365    let mut fake_language_servers = client_a
5366        .language_registry()
5367        .register_fake_lsp_adapter("Rust", Default::default());
5368
5369    client_a
5370        .fs()
5371        .insert_tree(
5372            "/root",
5373            json!({
5374                "a.rs": "const ONE: usize = b::TWO;",
5375                "b.rs": "const TWO: usize = 2",
5376            }),
5377        )
5378        .await;
5379    let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await;
5380    let project_id = active_call_a
5381        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5382        .await
5383        .unwrap();
5384    let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
5385
5386    let open_buffer_task = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
5387    let buffer_b1 = cx_b.executor().spawn(open_buffer_task).await.unwrap();
5388
5389    let fake_language_server = fake_language_servers.next().await.unwrap();
5390    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
5391        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
5392            lsp::Location::new(
5393                lsp::Url::from_file_path("/root/b.rs").unwrap(),
5394                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5395            ),
5396        )))
5397    });
5398
5399    let definitions;
5400    let buffer_b2;
5401    if rng.gen() {
5402        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5403        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5404    } else {
5405        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5406        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5407    }
5408
5409    let buffer_b2 = buffer_b2.await.unwrap();
5410    let definitions = definitions.await.unwrap();
5411    assert_eq!(definitions.len(), 1);
5412    assert_eq!(definitions[0].target.buffer, buffer_b2);
5413}
5414
5415#[gpui::test(iterations = 10)]
5416async fn test_contacts(
5417    executor: BackgroundExecutor,
5418    cx_a: &mut TestAppContext,
5419    cx_b: &mut TestAppContext,
5420    cx_c: &mut TestAppContext,
5421    cx_d: &mut TestAppContext,
5422) {
5423    let mut server = TestServer::start(executor.clone()).await;
5424    let client_a = server.create_client(cx_a, "user_a").await;
5425    let client_b = server.create_client(cx_b, "user_b").await;
5426    let client_c = server.create_client(cx_c, "user_c").await;
5427    let client_d = server.create_client(cx_d, "user_d").await;
5428    server
5429        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
5430        .await;
5431    let active_call_a = cx_a.read(ActiveCall::global);
5432    let active_call_b = cx_b.read(ActiveCall::global);
5433    let active_call_c = cx_c.read(ActiveCall::global);
5434    let _active_call_d = cx_d.read(ActiveCall::global);
5435
5436    executor.run_until_parked();
5437    assert_eq!(
5438        contacts(&client_a, cx_a),
5439        [
5440            ("user_b".to_string(), "online", "free"),
5441            ("user_c".to_string(), "online", "free")
5442        ]
5443    );
5444    assert_eq!(
5445        contacts(&client_b, cx_b),
5446        [
5447            ("user_a".to_string(), "online", "free"),
5448            ("user_c".to_string(), "online", "free")
5449        ]
5450    );
5451    assert_eq!(
5452        contacts(&client_c, cx_c),
5453        [
5454            ("user_a".to_string(), "online", "free"),
5455            ("user_b".to_string(), "online", "free")
5456        ]
5457    );
5458    assert_eq!(contacts(&client_d, cx_d), []);
5459
5460    server.disconnect_client(client_c.peer_id().unwrap());
5461    server.forbid_connections();
5462    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5463    assert_eq!(
5464        contacts(&client_a, cx_a),
5465        [
5466            ("user_b".to_string(), "online", "free"),
5467            ("user_c".to_string(), "offline", "free")
5468        ]
5469    );
5470    assert_eq!(
5471        contacts(&client_b, cx_b),
5472        [
5473            ("user_a".to_string(), "online", "free"),
5474            ("user_c".to_string(), "offline", "free")
5475        ]
5476    );
5477    assert_eq!(contacts(&client_c, cx_c), []);
5478    assert_eq!(contacts(&client_d, cx_d), []);
5479
5480    server.allow_connections();
5481    client_c
5482        .authenticate_and_connect(false, &cx_c.to_async())
5483        .await
5484        .unwrap();
5485
5486    executor.run_until_parked();
5487    assert_eq!(
5488        contacts(&client_a, cx_a),
5489        [
5490            ("user_b".to_string(), "online", "free"),
5491            ("user_c".to_string(), "online", "free")
5492        ]
5493    );
5494    assert_eq!(
5495        contacts(&client_b, cx_b),
5496        [
5497            ("user_a".to_string(), "online", "free"),
5498            ("user_c".to_string(), "online", "free")
5499        ]
5500    );
5501    assert_eq!(
5502        contacts(&client_c, cx_c),
5503        [
5504            ("user_a".to_string(), "online", "free"),
5505            ("user_b".to_string(), "online", "free")
5506        ]
5507    );
5508    assert_eq!(contacts(&client_d, cx_d), []);
5509
5510    active_call_a
5511        .update(cx_a, |call, cx| {
5512            call.invite(client_b.user_id().unwrap(), None, cx)
5513        })
5514        .await
5515        .unwrap();
5516    executor.run_until_parked();
5517    assert_eq!(
5518        contacts(&client_a, cx_a),
5519        [
5520            ("user_b".to_string(), "online", "busy"),
5521            ("user_c".to_string(), "online", "free")
5522        ]
5523    );
5524    assert_eq!(
5525        contacts(&client_b, cx_b),
5526        [
5527            ("user_a".to_string(), "online", "busy"),
5528            ("user_c".to_string(), "online", "free")
5529        ]
5530    );
5531    assert_eq!(
5532        contacts(&client_c, cx_c),
5533        [
5534            ("user_a".to_string(), "online", "busy"),
5535            ("user_b".to_string(), "online", "busy")
5536        ]
5537    );
5538    assert_eq!(contacts(&client_d, cx_d), []);
5539
5540    // Client B and client D become contacts while client B is being called.
5541    server
5542        .make_contacts(&mut [(&client_b, cx_b), (&client_d, cx_d)])
5543        .await;
5544    executor.run_until_parked();
5545    assert_eq!(
5546        contacts(&client_a, cx_a),
5547        [
5548            ("user_b".to_string(), "online", "busy"),
5549            ("user_c".to_string(), "online", "free")
5550        ]
5551    );
5552    assert_eq!(
5553        contacts(&client_b, cx_b),
5554        [
5555            ("user_a".to_string(), "online", "busy"),
5556            ("user_c".to_string(), "online", "free"),
5557            ("user_d".to_string(), "online", "free"),
5558        ]
5559    );
5560    assert_eq!(
5561        contacts(&client_c, cx_c),
5562        [
5563            ("user_a".to_string(), "online", "busy"),
5564            ("user_b".to_string(), "online", "busy")
5565        ]
5566    );
5567    assert_eq!(
5568        contacts(&client_d, cx_d),
5569        [("user_b".to_string(), "online", "busy")]
5570    );
5571
5572    active_call_b.update(cx_b, |call, cx| call.decline_incoming(cx).unwrap());
5573    executor.run_until_parked();
5574    assert_eq!(
5575        contacts(&client_a, cx_a),
5576        [
5577            ("user_b".to_string(), "online", "free"),
5578            ("user_c".to_string(), "online", "free")
5579        ]
5580    );
5581    assert_eq!(
5582        contacts(&client_b, cx_b),
5583        [
5584            ("user_a".to_string(), "online", "free"),
5585            ("user_c".to_string(), "online", "free"),
5586            ("user_d".to_string(), "online", "free")
5587        ]
5588    );
5589    assert_eq!(
5590        contacts(&client_c, cx_c),
5591        [
5592            ("user_a".to_string(), "online", "free"),
5593            ("user_b".to_string(), "online", "free")
5594        ]
5595    );
5596    assert_eq!(
5597        contacts(&client_d, cx_d),
5598        [("user_b".to_string(), "online", "free")]
5599    );
5600
5601    active_call_c
5602        .update(cx_c, |call, cx| {
5603            call.invite(client_a.user_id().unwrap(), None, cx)
5604        })
5605        .await
5606        .unwrap();
5607    executor.run_until_parked();
5608    assert_eq!(
5609        contacts(&client_a, cx_a),
5610        [
5611            ("user_b".to_string(), "online", "free"),
5612            ("user_c".to_string(), "online", "busy")
5613        ]
5614    );
5615    assert_eq!(
5616        contacts(&client_b, cx_b),
5617        [
5618            ("user_a".to_string(), "online", "busy"),
5619            ("user_c".to_string(), "online", "busy"),
5620            ("user_d".to_string(), "online", "free")
5621        ]
5622    );
5623    assert_eq!(
5624        contacts(&client_c, cx_c),
5625        [
5626            ("user_a".to_string(), "online", "busy"),
5627            ("user_b".to_string(), "online", "free")
5628        ]
5629    );
5630    assert_eq!(
5631        contacts(&client_d, cx_d),
5632        [("user_b".to_string(), "online", "free")]
5633    );
5634
5635    active_call_a
5636        .update(cx_a, |call, cx| call.accept_incoming(cx))
5637        .await
5638        .unwrap();
5639    executor.run_until_parked();
5640    assert_eq!(
5641        contacts(&client_a, cx_a),
5642        [
5643            ("user_b".to_string(), "online", "free"),
5644            ("user_c".to_string(), "online", "busy")
5645        ]
5646    );
5647    assert_eq!(
5648        contacts(&client_b, cx_b),
5649        [
5650            ("user_a".to_string(), "online", "busy"),
5651            ("user_c".to_string(), "online", "busy"),
5652            ("user_d".to_string(), "online", "free")
5653        ]
5654    );
5655    assert_eq!(
5656        contacts(&client_c, cx_c),
5657        [
5658            ("user_a".to_string(), "online", "busy"),
5659            ("user_b".to_string(), "online", "free")
5660        ]
5661    );
5662    assert_eq!(
5663        contacts(&client_d, cx_d),
5664        [("user_b".to_string(), "online", "free")]
5665    );
5666
5667    active_call_a
5668        .update(cx_a, |call, cx| {
5669            call.invite(client_b.user_id().unwrap(), None, cx)
5670        })
5671        .await
5672        .unwrap();
5673    executor.run_until_parked();
5674    assert_eq!(
5675        contacts(&client_a, cx_a),
5676        [
5677            ("user_b".to_string(), "online", "busy"),
5678            ("user_c".to_string(), "online", "busy")
5679        ]
5680    );
5681    assert_eq!(
5682        contacts(&client_b, cx_b),
5683        [
5684            ("user_a".to_string(), "online", "busy"),
5685            ("user_c".to_string(), "online", "busy"),
5686            ("user_d".to_string(), "online", "free")
5687        ]
5688    );
5689    assert_eq!(
5690        contacts(&client_c, cx_c),
5691        [
5692            ("user_a".to_string(), "online", "busy"),
5693            ("user_b".to_string(), "online", "busy")
5694        ]
5695    );
5696    assert_eq!(
5697        contacts(&client_d, cx_d),
5698        [("user_b".to_string(), "online", "busy")]
5699    );
5700
5701    active_call_a
5702        .update(cx_a, |call, cx| call.hang_up(cx))
5703        .await
5704        .unwrap();
5705    executor.run_until_parked();
5706    assert_eq!(
5707        contacts(&client_a, cx_a),
5708        [
5709            ("user_b".to_string(), "online", "free"),
5710            ("user_c".to_string(), "online", "free")
5711        ]
5712    );
5713    assert_eq!(
5714        contacts(&client_b, cx_b),
5715        [
5716            ("user_a".to_string(), "online", "free"),
5717            ("user_c".to_string(), "online", "free"),
5718            ("user_d".to_string(), "online", "free")
5719        ]
5720    );
5721    assert_eq!(
5722        contacts(&client_c, cx_c),
5723        [
5724            ("user_a".to_string(), "online", "free"),
5725            ("user_b".to_string(), "online", "free")
5726        ]
5727    );
5728    assert_eq!(
5729        contacts(&client_d, cx_d),
5730        [("user_b".to_string(), "online", "free")]
5731    );
5732
5733    active_call_a
5734        .update(cx_a, |call, cx| {
5735            call.invite(client_b.user_id().unwrap(), None, cx)
5736        })
5737        .await
5738        .unwrap();
5739    executor.run_until_parked();
5740    assert_eq!(
5741        contacts(&client_a, cx_a),
5742        [
5743            ("user_b".to_string(), "online", "busy"),
5744            ("user_c".to_string(), "online", "free")
5745        ]
5746    );
5747    assert_eq!(
5748        contacts(&client_b, cx_b),
5749        [
5750            ("user_a".to_string(), "online", "busy"),
5751            ("user_c".to_string(), "online", "free"),
5752            ("user_d".to_string(), "online", "free")
5753        ]
5754    );
5755    assert_eq!(
5756        contacts(&client_c, cx_c),
5757        [
5758            ("user_a".to_string(), "online", "busy"),
5759            ("user_b".to_string(), "online", "busy")
5760        ]
5761    );
5762    assert_eq!(
5763        contacts(&client_d, cx_d),
5764        [("user_b".to_string(), "online", "busy")]
5765    );
5766
5767    server.forbid_connections();
5768    server.disconnect_client(client_a.peer_id().unwrap());
5769    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5770    assert_eq!(contacts(&client_a, cx_a), []);
5771    assert_eq!(
5772        contacts(&client_b, cx_b),
5773        [
5774            ("user_a".to_string(), "offline", "free"),
5775            ("user_c".to_string(), "online", "free"),
5776            ("user_d".to_string(), "online", "free")
5777        ]
5778    );
5779    assert_eq!(
5780        contacts(&client_c, cx_c),
5781        [
5782            ("user_a".to_string(), "offline", "free"),
5783            ("user_b".to_string(), "online", "free")
5784        ]
5785    );
5786    assert_eq!(
5787        contacts(&client_d, cx_d),
5788        [("user_b".to_string(), "online", "free")]
5789    );
5790
5791    // Test removing a contact
5792    client_b
5793        .user_store()
5794        .update(cx_b, |store, cx| {
5795            store.remove_contact(client_c.user_id().unwrap(), cx)
5796        })
5797        .await
5798        .unwrap();
5799    executor.run_until_parked();
5800    assert_eq!(
5801        contacts(&client_b, cx_b),
5802        [
5803            ("user_a".to_string(), "offline", "free"),
5804            ("user_d".to_string(), "online", "free")
5805        ]
5806    );
5807    assert_eq!(
5808        contacts(&client_c, cx_c),
5809        [("user_a".to_string(), "offline", "free"),]
5810    );
5811
5812    fn contacts(
5813        client: &TestClient,
5814        cx: &TestAppContext,
5815    ) -> Vec<(String, &'static str, &'static str)> {
5816        client.user_store().read_with(cx, |store, _| {
5817            store
5818                .contacts()
5819                .iter()
5820                .map(|contact| {
5821                    (
5822                        contact.user.github_login.clone(),
5823                        if contact.online { "online" } else { "offline" },
5824                        if contact.busy { "busy" } else { "free" },
5825                    )
5826                })
5827                .collect()
5828        })
5829    }
5830}
5831
5832#[gpui::test(iterations = 10)]
5833async fn test_contact_requests(
5834    executor: BackgroundExecutor,
5835    cx_a: &mut TestAppContext,
5836    cx_a2: &mut TestAppContext,
5837    cx_b: &mut TestAppContext,
5838    cx_b2: &mut TestAppContext,
5839    cx_c: &mut TestAppContext,
5840    cx_c2: &mut TestAppContext,
5841) {
5842    // Connect to a server as 3 clients.
5843    let mut server = TestServer::start(executor.clone()).await;
5844    let client_a = server.create_client(cx_a, "user_a").await;
5845    let client_a2 = server.create_client(cx_a2, "user_a").await;
5846    let client_b = server.create_client(cx_b, "user_b").await;
5847    let client_b2 = server.create_client(cx_b2, "user_b").await;
5848    let client_c = server.create_client(cx_c, "user_c").await;
5849    let client_c2 = server.create_client(cx_c2, "user_c").await;
5850
5851    assert_eq!(client_a.user_id().unwrap(), client_a2.user_id().unwrap());
5852    assert_eq!(client_b.user_id().unwrap(), client_b2.user_id().unwrap());
5853    assert_eq!(client_c.user_id().unwrap(), client_c2.user_id().unwrap());
5854
5855    // User A and User C request that user B become their contact.
5856    client_a
5857        .user_store()
5858        .update(cx_a, |store, cx| {
5859            store.request_contact(client_b.user_id().unwrap(), cx)
5860        })
5861        .await
5862        .unwrap();
5863    client_c
5864        .user_store()
5865        .update(cx_c, |store, cx| {
5866            store.request_contact(client_b.user_id().unwrap(), cx)
5867        })
5868        .await
5869        .unwrap();
5870    executor.run_until_parked();
5871
5872    // All users see the pending request appear in all their clients.
5873    assert_eq!(
5874        client_a.summarize_contacts(cx_a).outgoing_requests,
5875        &["user_b"]
5876    );
5877    assert_eq!(
5878        client_a2.summarize_contacts(cx_a2).outgoing_requests,
5879        &["user_b"]
5880    );
5881    assert_eq!(
5882        client_b.summarize_contacts(cx_b).incoming_requests,
5883        &["user_a", "user_c"]
5884    );
5885    assert_eq!(
5886        client_b2.summarize_contacts(cx_b2).incoming_requests,
5887        &["user_a", "user_c"]
5888    );
5889    assert_eq!(
5890        client_c.summarize_contacts(cx_c).outgoing_requests,
5891        &["user_b"]
5892    );
5893    assert_eq!(
5894        client_c2.summarize_contacts(cx_c2).outgoing_requests,
5895        &["user_b"]
5896    );
5897
5898    // Contact requests are present upon connecting (tested here via disconnect/reconnect)
5899    disconnect_and_reconnect(&client_a, cx_a).await;
5900    disconnect_and_reconnect(&client_b, cx_b).await;
5901    disconnect_and_reconnect(&client_c, cx_c).await;
5902    executor.run_until_parked();
5903    assert_eq!(
5904        client_a.summarize_contacts(cx_a).outgoing_requests,
5905        &["user_b"]
5906    );
5907    assert_eq!(
5908        client_b.summarize_contacts(cx_b).incoming_requests,
5909        &["user_a", "user_c"]
5910    );
5911    assert_eq!(
5912        client_c.summarize_contacts(cx_c).outgoing_requests,
5913        &["user_b"]
5914    );
5915
5916    // User B accepts the request from user A.
5917    client_b
5918        .user_store()
5919        .update(cx_b, |store, cx| {
5920            store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
5921        })
5922        .await
5923        .unwrap();
5924
5925    executor.run_until_parked();
5926
5927    // User B sees user A as their contact now in all client, and the incoming request from them is removed.
5928    let contacts_b = client_b.summarize_contacts(cx_b);
5929    assert_eq!(contacts_b.current, &["user_a"]);
5930    assert_eq!(contacts_b.incoming_requests, &["user_c"]);
5931    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
5932    assert_eq!(contacts_b2.current, &["user_a"]);
5933    assert_eq!(contacts_b2.incoming_requests, &["user_c"]);
5934
5935    // User A sees user B as their contact now in all clients, and the outgoing request to them is removed.
5936    let contacts_a = client_a.summarize_contacts(cx_a);
5937    assert_eq!(contacts_a.current, &["user_b"]);
5938    assert!(contacts_a.outgoing_requests.is_empty());
5939    let contacts_a2 = client_a2.summarize_contacts(cx_a2);
5940    assert_eq!(contacts_a2.current, &["user_b"]);
5941    assert!(contacts_a2.outgoing_requests.is_empty());
5942
5943    // Contacts are present upon connecting (tested here via disconnect/reconnect)
5944    disconnect_and_reconnect(&client_a, cx_a).await;
5945    disconnect_and_reconnect(&client_b, cx_b).await;
5946    disconnect_and_reconnect(&client_c, cx_c).await;
5947    executor.run_until_parked();
5948    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
5949    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
5950    assert_eq!(
5951        client_b.summarize_contacts(cx_b).incoming_requests,
5952        &["user_c"]
5953    );
5954    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
5955    assert_eq!(
5956        client_c.summarize_contacts(cx_c).outgoing_requests,
5957        &["user_b"]
5958    );
5959
5960    // User B rejects the request from user C.
5961    client_b
5962        .user_store()
5963        .update(cx_b, |store, cx| {
5964            store.respond_to_contact_request(client_c.user_id().unwrap(), false, cx)
5965        })
5966        .await
5967        .unwrap();
5968
5969    executor.run_until_parked();
5970
5971    // User B doesn't see user C as their contact, and the incoming request from them is removed.
5972    let contacts_b = client_b.summarize_contacts(cx_b);
5973    assert_eq!(contacts_b.current, &["user_a"]);
5974    assert!(contacts_b.incoming_requests.is_empty());
5975    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
5976    assert_eq!(contacts_b2.current, &["user_a"]);
5977    assert!(contacts_b2.incoming_requests.is_empty());
5978
5979    // User C doesn't see user B as their contact, and the outgoing request to them is removed.
5980    let contacts_c = client_c.summarize_contacts(cx_c);
5981    assert!(contacts_c.current.is_empty());
5982    assert!(contacts_c.outgoing_requests.is_empty());
5983    let contacts_c2 = client_c2.summarize_contacts(cx_c2);
5984    assert!(contacts_c2.current.is_empty());
5985    assert!(contacts_c2.outgoing_requests.is_empty());
5986
5987    // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect)
5988    disconnect_and_reconnect(&client_a, cx_a).await;
5989    disconnect_and_reconnect(&client_b, cx_b).await;
5990    disconnect_and_reconnect(&client_c, cx_c).await;
5991    executor.run_until_parked();
5992    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
5993    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
5994    assert!(client_b
5995        .summarize_contacts(cx_b)
5996        .incoming_requests
5997        .is_empty());
5998    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
5999    assert!(client_c
6000        .summarize_contacts(cx_c)
6001        .outgoing_requests
6002        .is_empty());
6003
6004    async fn disconnect_and_reconnect(client: &TestClient, cx: &mut TestAppContext) {
6005        client.disconnect(&cx.to_async());
6006        client.clear_contacts(cx).await;
6007        client
6008            .authenticate_and_connect(false, &cx.to_async())
6009            .await
6010            .unwrap();
6011    }
6012}
6013
6014#[gpui::test(iterations = 10)]
6015async fn test_join_call_after_screen_was_shared(
6016    executor: BackgroundExecutor,
6017    cx_a: &mut TestAppContext,
6018    cx_b: &mut TestAppContext,
6019) {
6020    let mut server = TestServer::start(executor.clone()).await;
6021
6022    let client_a = server.create_client(cx_a, "user_a").await;
6023    let client_b = server.create_client(cx_b, "user_b").await;
6024    server
6025        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6026        .await;
6027
6028    let active_call_a = cx_a.read(ActiveCall::global);
6029    let active_call_b = cx_b.read(ActiveCall::global);
6030
6031    // Call users B and C from client A.
6032    active_call_a
6033        .update(cx_a, |call, cx| {
6034            call.invite(client_b.user_id().unwrap(), None, cx)
6035        })
6036        .await
6037        .unwrap();
6038
6039    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
6040    executor.run_until_parked();
6041    assert_eq!(
6042        room_participants(&room_a, cx_a),
6043        RoomParticipants {
6044            remote: Default::default(),
6045            pending: vec!["user_b".to_string()]
6046        }
6047    );
6048
6049    // User B receives the call.
6050
6051    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
6052    let call_b = incoming_call_b.next().await.unwrap().unwrap();
6053    assert_eq!(call_b.calling_user.github_login, "user_a");
6054
6055    // User A shares their screen
6056    let display = MacOSDisplay::new();
6057    active_call_a
6058        .update(cx_a, |call, cx| {
6059            call.room().unwrap().update(cx, |room, cx| {
6060                room.set_display_sources(vec![display.clone()]);
6061                room.share_screen(cx)
6062            })
6063        })
6064        .await
6065        .unwrap();
6066
6067    client_b.user_store().update(cx_b, |user_store, _| {
6068        user_store.clear_cache();
6069    });
6070
6071    // User B joins the room
6072    active_call_b
6073        .update(cx_b, |call, cx| call.accept_incoming(cx))
6074        .await
6075        .unwrap();
6076
6077    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
6078    assert!(incoming_call_b.next().await.unwrap().is_none());
6079
6080    executor.run_until_parked();
6081    assert_eq!(
6082        room_participants(&room_a, cx_a),
6083        RoomParticipants {
6084            remote: vec!["user_b".to_string()],
6085            pending: vec![],
6086        }
6087    );
6088    assert_eq!(
6089        room_participants(&room_b, cx_b),
6090        RoomParticipants {
6091            remote: vec!["user_a".to_string()],
6092            pending: vec![],
6093        }
6094    );
6095
6096    // Ensure User B sees User A's screenshare.
6097
6098    room_b.read_with(cx_b, |room, _| {
6099        assert_eq!(
6100            room.remote_participants()
6101                .get(&client_a.user_id().unwrap())
6102                .unwrap()
6103                .video_tracks
6104                .len(),
6105            1
6106        );
6107    });
6108}
6109
6110#[gpui::test]
6111async fn test_right_click_menu_behind_collab_panel(cx: &mut TestAppContext) {
6112    let mut server = TestServer::start(cx.executor().clone()).await;
6113    let client_a = server.create_client(cx, "user_a").await;
6114    let (_workspace_a, cx) = client_a.build_test_workspace(cx).await;
6115
6116    cx.simulate_resize(size(px(300.), px(300.)));
6117
6118    cx.simulate_keystrokes("cmd-n cmd-n cmd-n");
6119    cx.update(|cx| cx.refresh());
6120
6121    let tab_bounds = cx.debug_bounds("TAB-2").unwrap();
6122    let new_tab_button_bounds = cx.debug_bounds("ICON-Plus").unwrap();
6123
6124    assert!(
6125        tab_bounds.intersects(&new_tab_button_bounds),
6126        "Tab should overlap with the new tab button, if this is failing check if there's been a redesign!"
6127    );
6128
6129    cx.simulate_event(MouseDownEvent {
6130        button: MouseButton::Right,
6131        position: new_tab_button_bounds.center(),
6132        modifiers: Modifiers::default(),
6133        click_count: 1,
6134        first_mouse: false,
6135    });
6136
6137    // regression test that the right click menu for tabs does not open.
6138    assert!(cx.debug_bounds("MENU_ITEM-Close").is_none());
6139
6140    let tab_bounds = cx.debug_bounds("TAB-1").unwrap();
6141    cx.simulate_event(MouseDownEvent {
6142        button: MouseButton::Right,
6143        position: tab_bounds.center(),
6144        modifiers: Modifiers::default(),
6145        click_count: 1,
6146        first_mouse: false,
6147    });
6148    assert!(cx.debug_bounds("MENU_ITEM-Close").is_some());
6149}
6150
6151#[gpui::test]
6152async fn test_pane_split_left(cx: &mut TestAppContext) {
6153    let (_, client) = TestServer::start1(cx).await;
6154    let (workspace, cx) = client.build_test_workspace(cx).await;
6155
6156    cx.simulate_keystrokes("cmd-n");
6157    workspace.update(cx, |workspace, cx| {
6158        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 1);
6159    });
6160    cx.simulate_keystrokes("cmd-k left");
6161    workspace.update(cx, |workspace, cx| {
6162        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 2);
6163    });
6164    cx.simulate_keystrokes("cmd-k");
6165    // sleep for longer than the timeout in keyboard shortcut handling
6166    // to verify that it doesn't fire in this case.
6167    cx.executor().advance_clock(Duration::from_secs(2));
6168    cx.simulate_keystrokes("left");
6169    workspace.update(cx, |workspace, cx| {
6170        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 2);
6171    });
6172}
6173
6174#[gpui::test]
6175async fn test_join_after_restart(cx1: &mut TestAppContext, cx2: &mut TestAppContext) {
6176    let (mut server, client) = TestServer::start1(cx1).await;
6177    let channel1 = server.make_public_channel("channel1", &client, cx1).await;
6178    let channel2 = server.make_public_channel("channel2", &client, cx1).await;
6179
6180    join_channel(channel1, &client, cx1).await.unwrap();
6181    drop(client);
6182
6183    let client2 = server.create_client(cx2, "user_a").await;
6184    join_channel(channel2, &client2, cx2).await.unwrap();
6185}
6186
6187#[gpui::test]
6188async fn test_preview_tabs(cx: &mut TestAppContext) {
6189    let (_server, client) = TestServer::start1(cx).await;
6190    let (workspace, cx) = client.build_test_workspace(cx).await;
6191    let project = workspace.update(cx, |workspace, _| workspace.project().clone());
6192
6193    let worktree_id = project.update(cx, |project, cx| {
6194        project.worktrees().next().unwrap().read(cx).id()
6195    });
6196
6197    let path_1 = ProjectPath {
6198        worktree_id,
6199        path: Path::new("1.txt").into(),
6200    };
6201    let path_2 = ProjectPath {
6202        worktree_id,
6203        path: Path::new("2.js").into(),
6204    };
6205    let path_3 = ProjectPath {
6206        worktree_id,
6207        path: Path::new("3.rs").into(),
6208    };
6209
6210    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
6211
6212    let get_path = |pane: &Pane, idx: usize, cx: &AppContext| {
6213        pane.item_for_index(idx).unwrap().project_path(cx).unwrap()
6214    };
6215
6216    // Opening item 3 as a "permanent" tab
6217    workspace
6218        .update(cx, |workspace, cx| {
6219            workspace.open_path(path_3.clone(), None, false, cx)
6220        })
6221        .await
6222        .unwrap();
6223
6224    pane.update(cx, |pane, cx| {
6225        assert_eq!(pane.items_len(), 1);
6226        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6227        assert_eq!(pane.preview_item_id(), None);
6228
6229        assert!(!pane.can_navigate_backward());
6230        assert!(!pane.can_navigate_forward());
6231    });
6232
6233    // Open item 1 as preview
6234    workspace
6235        .update(cx, |workspace, cx| {
6236            workspace.open_path_preview(path_1.clone(), None, true, true, cx)
6237        })
6238        .await
6239        .unwrap();
6240
6241    pane.update(cx, |pane, cx| {
6242        assert_eq!(pane.items_len(), 2);
6243        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6244        assert_eq!(get_path(pane, 1, cx), path_1.clone());
6245        assert_eq!(
6246            pane.preview_item_id(),
6247            Some(pane.items().nth(1).unwrap().item_id())
6248        );
6249
6250        assert!(pane.can_navigate_backward());
6251        assert!(!pane.can_navigate_forward());
6252    });
6253
6254    // Open item 2 as preview
6255    workspace
6256        .update(cx, |workspace, cx| {
6257            workspace.open_path_preview(path_2.clone(), None, true, true, cx)
6258        })
6259        .await
6260        .unwrap();
6261
6262    pane.update(cx, |pane, cx| {
6263        assert_eq!(pane.items_len(), 2);
6264        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6265        assert_eq!(get_path(pane, 1, cx), path_2.clone());
6266        assert_eq!(
6267            pane.preview_item_id(),
6268            Some(pane.items().nth(1).unwrap().item_id())
6269        );
6270
6271        assert!(pane.can_navigate_backward());
6272        assert!(!pane.can_navigate_forward());
6273    });
6274
6275    // Going back should show item 1 as preview
6276    workspace
6277        .update(cx, |workspace, cx| workspace.go_back(pane.downgrade(), cx))
6278        .await
6279        .unwrap();
6280
6281    pane.update(cx, |pane, cx| {
6282        assert_eq!(pane.items_len(), 2);
6283        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6284        assert_eq!(get_path(pane, 1, cx), path_1.clone());
6285        assert_eq!(
6286            pane.preview_item_id(),
6287            Some(pane.items().nth(1).unwrap().item_id())
6288        );
6289
6290        assert!(pane.can_navigate_backward());
6291        assert!(pane.can_navigate_forward());
6292    });
6293
6294    // Closing item 1
6295    pane.update(cx, |pane, cx| {
6296        pane.close_item_by_id(
6297            pane.active_item().unwrap().item_id(),
6298            workspace::SaveIntent::Skip,
6299            cx,
6300        )
6301    })
6302    .await
6303    .unwrap();
6304
6305    pane.update(cx, |pane, cx| {
6306        assert_eq!(pane.items_len(), 1);
6307        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6308        assert_eq!(pane.preview_item_id(), None);
6309
6310        assert!(pane.can_navigate_backward());
6311        assert!(!pane.can_navigate_forward());
6312    });
6313
6314    // Going back should show item 1 as preview
6315    workspace
6316        .update(cx, |workspace, cx| workspace.go_back(pane.downgrade(), cx))
6317        .await
6318        .unwrap();
6319
6320    pane.update(cx, |pane, cx| {
6321        assert_eq!(pane.items_len(), 2);
6322        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6323        assert_eq!(get_path(pane, 1, cx), path_1.clone());
6324        assert_eq!(
6325            pane.preview_item_id(),
6326            Some(pane.items().nth(1).unwrap().item_id())
6327        );
6328
6329        assert!(pane.can_navigate_backward());
6330        assert!(pane.can_navigate_forward());
6331    });
6332
6333    // Close permanent tab
6334    pane.update(cx, |pane, cx| {
6335        let id = pane.items().nth(0).unwrap().item_id();
6336        pane.close_item_by_id(id, workspace::SaveIntent::Skip, cx)
6337    })
6338    .await
6339    .unwrap();
6340
6341    pane.update(cx, |pane, cx| {
6342        assert_eq!(pane.items_len(), 1);
6343        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6344        assert_eq!(
6345            pane.preview_item_id(),
6346            Some(pane.items().nth(0).unwrap().item_id())
6347        );
6348
6349        assert!(pane.can_navigate_backward());
6350        assert!(pane.can_navigate_forward());
6351    });
6352
6353    // Split pane to the right
6354    pane.update(cx, |pane, cx| {
6355        pane.split(workspace::SplitDirection::Right, cx);
6356    });
6357
6358    let right_pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
6359
6360    pane.update(cx, |pane, cx| {
6361        assert_eq!(pane.items_len(), 1);
6362        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6363        assert_eq!(
6364            pane.preview_item_id(),
6365            Some(pane.items().nth(0).unwrap().item_id())
6366        );
6367
6368        assert!(pane.can_navigate_backward());
6369        assert!(pane.can_navigate_forward());
6370    });
6371
6372    right_pane.update(cx, |pane, cx| {
6373        assert_eq!(pane.items_len(), 1);
6374        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6375        assert_eq!(pane.preview_item_id(), None);
6376
6377        assert!(!pane.can_navigate_backward());
6378        assert!(!pane.can_navigate_forward());
6379    });
6380
6381    // Open item 2 as preview in right pane
6382    workspace
6383        .update(cx, |workspace, cx| {
6384            workspace.open_path_preview(path_2.clone(), None, true, true, cx)
6385        })
6386        .await
6387        .unwrap();
6388
6389    pane.update(cx, |pane, cx| {
6390        assert_eq!(pane.items_len(), 1);
6391        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6392        assert_eq!(
6393            pane.preview_item_id(),
6394            Some(pane.items().nth(0).unwrap().item_id())
6395        );
6396
6397        assert!(pane.can_navigate_backward());
6398        assert!(pane.can_navigate_forward());
6399    });
6400
6401    right_pane.update(cx, |pane, cx| {
6402        assert_eq!(pane.items_len(), 2);
6403        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6404        assert_eq!(get_path(pane, 1, cx), path_2.clone());
6405        assert_eq!(
6406            pane.preview_item_id(),
6407            Some(pane.items().nth(1).unwrap().item_id())
6408        );
6409
6410        assert!(pane.can_navigate_backward());
6411        assert!(!pane.can_navigate_forward());
6412    });
6413
6414    // Focus left pane
6415    workspace.update(cx, |workspace, cx| {
6416        workspace.activate_pane_in_direction(workspace::SplitDirection::Left, cx)
6417    });
6418
6419    // Open item 2 as preview in left pane
6420    workspace
6421        .update(cx, |workspace, cx| {
6422            workspace.open_path_preview(path_2.clone(), None, true, true, cx)
6423        })
6424        .await
6425        .unwrap();
6426
6427    pane.update(cx, |pane, cx| {
6428        assert_eq!(pane.items_len(), 1);
6429        assert_eq!(get_path(pane, 0, cx), path_2.clone());
6430        assert_eq!(
6431            pane.preview_item_id(),
6432            Some(pane.items().nth(0).unwrap().item_id())
6433        );
6434
6435        assert!(pane.can_navigate_backward());
6436        assert!(!pane.can_navigate_forward());
6437    });
6438
6439    right_pane.update(cx, |pane, cx| {
6440        assert_eq!(pane.items_len(), 2);
6441        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6442        assert_eq!(get_path(pane, 1, cx), path_2.clone());
6443        assert_eq!(
6444            pane.preview_item_id(),
6445            Some(pane.items().nth(1).unwrap().item_id())
6446        );
6447
6448        assert!(pane.can_navigate_backward());
6449        assert!(!pane.can_navigate_forward());
6450    });
6451}