integration_tests.rs

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