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(
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 unmuted.
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 B calls user C, C joins.
1966    active_call_b
1967        .update(cx_b, |call, cx| {
1968            call.invite(client_c.user_id().unwrap(), None, cx)
1969        })
1970        .await
1971        .unwrap();
1972    executor.run_until_parked();
1973    active_call_c
1974        .update(cx_c, |call, cx| call.accept_incoming(cx))
1975        .await
1976        .unwrap();
1977    executor.run_until_parked();
1978
1979    assert_eq!(
1980        participant_audio_state(&room_b, cx_b),
1981        &[
1982            ParticipantAudioState {
1983                user_id: client_a.user_id().unwrap(),
1984                is_muted: true,
1985                audio_tracks_playing: vec![true],
1986            },
1987            ParticipantAudioState {
1988                user_id: client_c.user_id().unwrap(),
1989                is_muted: false,
1990                audio_tracks_playing: vec![true],
1991            }
1992        ]
1993    );
1994
1995    #[derive(PartialEq, Eq, Debug)]
1996    struct ParticipantAudioState {
1997        user_id: u64,
1998        is_muted: bool,
1999        audio_tracks_playing: Vec<bool>,
2000    }
2001
2002    fn participant_audio_state(
2003        room: &Model<Room>,
2004        cx: &TestAppContext,
2005    ) -> Vec<ParticipantAudioState> {
2006        room.read_with(cx, |room, _| {
2007            room.remote_participants()
2008                .iter()
2009                .map(|(user_id, participant)| ParticipantAudioState {
2010                    user_id: *user_id,
2011                    is_muted: participant.muted,
2012                    audio_tracks_playing: participant
2013                        .audio_tracks
2014                        .values()
2015                        .map(|track| track.is_playing())
2016                        .collect(),
2017                })
2018                .collect::<Vec<_>>()
2019        })
2020    }
2021}
2022
2023#[gpui::test(iterations = 10)]
2024async fn test_room_location(
2025    executor: BackgroundExecutor,
2026    cx_a: &mut TestAppContext,
2027    cx_b: &mut TestAppContext,
2028) {
2029    let mut server = TestServer::start(executor.clone()).await;
2030    let client_a = server.create_client(cx_a, "user_a").await;
2031    let client_b = server.create_client(cx_b, "user_b").await;
2032    client_a.fs().insert_tree("/a", json!({})).await;
2033    client_b.fs().insert_tree("/b", json!({})).await;
2034
2035    let active_call_a = cx_a.read(ActiveCall::global);
2036    let active_call_b = cx_b.read(ActiveCall::global);
2037
2038    let a_notified = Rc::new(Cell::new(false));
2039    cx_a.update({
2040        let notified = a_notified.clone();
2041        |cx| {
2042            cx.observe(&active_call_a, move |_, _| notified.set(true))
2043                .detach()
2044        }
2045    });
2046
2047    let b_notified = Rc::new(Cell::new(false));
2048    cx_b.update({
2049        let b_notified = b_notified.clone();
2050        |cx| {
2051            cx.observe(&active_call_b, move |_, _| b_notified.set(true))
2052                .detach()
2053        }
2054    });
2055
2056    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
2057    active_call_a
2058        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
2059        .await
2060        .unwrap();
2061    let (project_b, _) = client_b.build_local_project("/b", cx_b).await;
2062
2063    server
2064        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2065        .await;
2066
2067    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
2068
2069    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
2070    executor.run_until_parked();
2071    assert!(a_notified.take());
2072    assert_eq!(
2073        participant_locations(&room_a, cx_a),
2074        vec![("user_b".to_string(), ParticipantLocation::External)]
2075    );
2076    assert!(b_notified.take());
2077    assert_eq!(
2078        participant_locations(&room_b, cx_b),
2079        vec![("user_a".to_string(), ParticipantLocation::UnsharedProject)]
2080    );
2081
2082    let project_a_id = active_call_a
2083        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2084        .await
2085        .unwrap();
2086    executor.run_until_parked();
2087    assert!(a_notified.take());
2088    assert_eq!(
2089        participant_locations(&room_a, cx_a),
2090        vec![("user_b".to_string(), ParticipantLocation::External)]
2091    );
2092    assert!(b_notified.take());
2093    assert_eq!(
2094        participant_locations(&room_b, cx_b),
2095        vec![(
2096            "user_a".to_string(),
2097            ParticipantLocation::SharedProject {
2098                project_id: project_a_id
2099            }
2100        )]
2101    );
2102
2103    let project_b_id = active_call_b
2104        .update(cx_b, |call, cx| call.share_project(project_b.clone(), cx))
2105        .await
2106        .unwrap();
2107    executor.run_until_parked();
2108    assert!(a_notified.take());
2109    assert_eq!(
2110        participant_locations(&room_a, cx_a),
2111        vec![("user_b".to_string(), ParticipantLocation::External)]
2112    );
2113    assert!(b_notified.take());
2114    assert_eq!(
2115        participant_locations(&room_b, cx_b),
2116        vec![(
2117            "user_a".to_string(),
2118            ParticipantLocation::SharedProject {
2119                project_id: project_a_id
2120            }
2121        )]
2122    );
2123
2124    active_call_b
2125        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
2126        .await
2127        .unwrap();
2128    executor.run_until_parked();
2129    assert!(a_notified.take());
2130    assert_eq!(
2131        participant_locations(&room_a, cx_a),
2132        vec![(
2133            "user_b".to_string(),
2134            ParticipantLocation::SharedProject {
2135                project_id: project_b_id
2136            }
2137        )]
2138    );
2139    assert!(b_notified.take());
2140    assert_eq!(
2141        participant_locations(&room_b, cx_b),
2142        vec![(
2143            "user_a".to_string(),
2144            ParticipantLocation::SharedProject {
2145                project_id: project_a_id
2146            }
2147        )]
2148    );
2149
2150    active_call_b
2151        .update(cx_b, |call, cx| call.set_location(None, cx))
2152        .await
2153        .unwrap();
2154    executor.run_until_parked();
2155    assert!(a_notified.take());
2156    assert_eq!(
2157        participant_locations(&room_a, cx_a),
2158        vec![("user_b".to_string(), ParticipantLocation::External)]
2159    );
2160    assert!(b_notified.take());
2161    assert_eq!(
2162        participant_locations(&room_b, cx_b),
2163        vec![(
2164            "user_a".to_string(),
2165            ParticipantLocation::SharedProject {
2166                project_id: project_a_id
2167            }
2168        )]
2169    );
2170
2171    fn participant_locations(
2172        room: &Model<Room>,
2173        cx: &TestAppContext,
2174    ) -> Vec<(String, ParticipantLocation)> {
2175        room.read_with(cx, |room, _| {
2176            room.remote_participants()
2177                .values()
2178                .map(|participant| {
2179                    (
2180                        participant.user.github_login.to_string(),
2181                        participant.location,
2182                    )
2183                })
2184                .collect()
2185        })
2186    }
2187}
2188
2189#[gpui::test(iterations = 10)]
2190async fn test_propagate_saves_and_fs_changes(
2191    executor: BackgroundExecutor,
2192    cx_a: &mut TestAppContext,
2193    cx_b: &mut TestAppContext,
2194    cx_c: &mut TestAppContext,
2195) {
2196    let mut server = TestServer::start(executor.clone()).await;
2197    let client_a = server.create_client(cx_a, "user_a").await;
2198    let client_b = server.create_client(cx_b, "user_b").await;
2199    let client_c = server.create_client(cx_c, "user_c").await;
2200
2201    server
2202        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
2203        .await;
2204    let active_call_a = cx_a.read(ActiveCall::global);
2205
2206    let rust = Arc::new(Language::new(
2207        LanguageConfig {
2208            name: "Rust".into(),
2209            matcher: LanguageMatcher {
2210                path_suffixes: vec!["rs".to_string()],
2211                ..Default::default()
2212            },
2213            ..Default::default()
2214        },
2215        Some(tree_sitter_rust::language()),
2216    ));
2217    let javascript = Arc::new(Language::new(
2218        LanguageConfig {
2219            name: "JavaScript".into(),
2220            matcher: LanguageMatcher {
2221                path_suffixes: vec!["js".to_string()],
2222                ..Default::default()
2223            },
2224            ..Default::default()
2225        },
2226        Some(tree_sitter_rust::language()),
2227    ));
2228    for client in [&client_a, &client_b, &client_c] {
2229        client.language_registry().add(rust.clone());
2230        client.language_registry().add(javascript.clone());
2231    }
2232
2233    client_a
2234        .fs()
2235        .insert_tree(
2236            "/a",
2237            json!({
2238                "file1.rs": "",
2239                "file2": ""
2240            }),
2241        )
2242        .await;
2243    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
2244
2245    let worktree_a = project_a.read_with(cx_a, |p, _| p.worktrees().next().unwrap());
2246    let project_id = active_call_a
2247        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2248        .await
2249        .unwrap();
2250
2251    // Join that worktree as clients B and C.
2252    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2253    let project_c = client_c.build_remote_project(project_id, cx_c).await;
2254
2255    let worktree_b = project_b.read_with(cx_b, |p, _| p.worktrees().next().unwrap());
2256
2257    let worktree_c = project_c.read_with(cx_c, |p, _| p.worktrees().next().unwrap());
2258
2259    // Open and edit a buffer as both guests B and C.
2260    let buffer_b = project_b
2261        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "file1.rs"), cx))
2262        .await
2263        .unwrap();
2264    let buffer_c = project_c
2265        .update(cx_c, |p, cx| p.open_buffer((worktree_id, "file1.rs"), cx))
2266        .await
2267        .unwrap();
2268
2269    buffer_b.read_with(cx_b, |buffer, _| {
2270        assert_eq!(&*buffer.language().unwrap().name(), "Rust");
2271    });
2272
2273    buffer_c.read_with(cx_c, |buffer, _| {
2274        assert_eq!(&*buffer.language().unwrap().name(), "Rust");
2275    });
2276    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "i-am-b, ")], None, cx));
2277    buffer_c.update(cx_c, |buf, cx| buf.edit([(0..0, "i-am-c, ")], None, cx));
2278
2279    // Open and edit that buffer as the host.
2280    let buffer_a = project_a
2281        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "file1.rs"), cx))
2282        .await
2283        .unwrap();
2284
2285    executor.run_until_parked();
2286
2287    buffer_a.read_with(cx_a, |buf, _| assert_eq!(buf.text(), "i-am-c, i-am-b, "));
2288    buffer_a.update(cx_a, |buf, cx| {
2289        buf.edit([(buf.len()..buf.len(), "i-am-a")], None, cx)
2290    });
2291
2292    executor.run_until_parked();
2293
2294    buffer_a.read_with(cx_a, |buf, _| {
2295        assert_eq!(buf.text(), "i-am-c, i-am-b, i-am-a");
2296    });
2297
2298    buffer_b.read_with(cx_b, |buf, _| {
2299        assert_eq!(buf.text(), "i-am-c, i-am-b, i-am-a");
2300    });
2301
2302    buffer_c.read_with(cx_c, |buf, _| {
2303        assert_eq!(buf.text(), "i-am-c, i-am-b, i-am-a");
2304    });
2305
2306    // Edit the buffer as the host and concurrently save as guest B.
2307    let save_b = project_b.update(cx_b, |project, cx| {
2308        project.save_buffer(buffer_b.clone(), cx)
2309    });
2310    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "hi-a, ")], None, cx));
2311    save_b.await.unwrap();
2312    assert_eq!(
2313        client_a.fs().load("/a/file1.rs".as_ref()).await.unwrap(),
2314        "hi-a, i-am-c, i-am-b, i-am-a"
2315    );
2316
2317    executor.run_until_parked();
2318
2319    buffer_a.read_with(cx_a, |buf, _| assert!(!buf.is_dirty()));
2320
2321    buffer_b.read_with(cx_b, |buf, _| assert!(!buf.is_dirty()));
2322
2323    buffer_c.read_with(cx_c, |buf, _| assert!(!buf.is_dirty()));
2324
2325    // Make changes on host's file system, see those changes on guest worktrees.
2326    client_a
2327        .fs()
2328        .rename(
2329            "/a/file1.rs".as_ref(),
2330            "/a/file1.js".as_ref(),
2331            Default::default(),
2332        )
2333        .await
2334        .unwrap();
2335    client_a
2336        .fs()
2337        .rename("/a/file2".as_ref(), "/a/file3".as_ref(), Default::default())
2338        .await
2339        .unwrap();
2340    client_a.fs().insert_file("/a/file4", "4".into()).await;
2341    executor.run_until_parked();
2342
2343    worktree_a.read_with(cx_a, |tree, _| {
2344        assert_eq!(
2345            tree.paths()
2346                .map(|p| p.to_string_lossy())
2347                .collect::<Vec<_>>(),
2348            ["file1.js", "file3", "file4"]
2349        )
2350    });
2351
2352    worktree_b.read_with(cx_b, |tree, _| {
2353        assert_eq!(
2354            tree.paths()
2355                .map(|p| p.to_string_lossy())
2356                .collect::<Vec<_>>(),
2357            ["file1.js", "file3", "file4"]
2358        )
2359    });
2360
2361    worktree_c.read_with(cx_c, |tree, _| {
2362        assert_eq!(
2363            tree.paths()
2364                .map(|p| p.to_string_lossy())
2365                .collect::<Vec<_>>(),
2366            ["file1.js", "file3", "file4"]
2367        )
2368    });
2369
2370    // Ensure buffer files are updated as well.
2371
2372    buffer_a.read_with(cx_a, |buffer, _| {
2373        assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
2374        assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
2375    });
2376
2377    buffer_b.read_with(cx_b, |buffer, _| {
2378        assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
2379        assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
2380    });
2381
2382    buffer_c.read_with(cx_c, |buffer, _| {
2383        assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
2384        assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
2385    });
2386
2387    let new_buffer_a = project_a
2388        .update(cx_a, |p, cx| p.create_buffer("", None, cx))
2389        .unwrap();
2390
2391    let new_buffer_id = new_buffer_a.read_with(cx_a, |buffer, _| buffer.remote_id());
2392    let new_buffer_b = project_b
2393        .update(cx_b, |p, cx| p.open_buffer_by_id(new_buffer_id, cx))
2394        .await
2395        .unwrap();
2396
2397    new_buffer_b.read_with(cx_b, |buffer, _| {
2398        assert!(buffer.file().is_none());
2399    });
2400
2401    new_buffer_a.update(cx_a, |buffer, cx| {
2402        buffer.edit([(0..0, "ok")], None, cx);
2403    });
2404    project_a
2405        .update(cx_a, |project, cx| {
2406            project.save_buffer_as(new_buffer_a.clone(), "/a/file3.rs".into(), cx)
2407        })
2408        .await
2409        .unwrap();
2410
2411    executor.run_until_parked();
2412
2413    new_buffer_b.read_with(cx_b, |buffer_b, _| {
2414        assert_eq!(
2415            buffer_b.file().unwrap().path().as_ref(),
2416            Path::new("file3.rs")
2417        );
2418
2419        new_buffer_a.read_with(cx_a, |buffer_a, _| {
2420            assert_eq!(buffer_b.saved_mtime(), buffer_a.saved_mtime());
2421            assert_eq!(buffer_b.saved_version(), buffer_a.saved_version());
2422        });
2423    });
2424}
2425
2426#[gpui::test(iterations = 10)]
2427async fn test_git_diff_base_change(
2428    executor: BackgroundExecutor,
2429    cx_a: &mut TestAppContext,
2430    cx_b: &mut TestAppContext,
2431) {
2432    let mut server = TestServer::start(executor.clone()).await;
2433    let client_a = server.create_client(cx_a, "user_a").await;
2434    let client_b = server.create_client(cx_b, "user_b").await;
2435    server
2436        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2437        .await;
2438    let active_call_a = cx_a.read(ActiveCall::global);
2439
2440    client_a
2441        .fs()
2442        .insert_tree(
2443            "/dir",
2444            json!({
2445            ".git": {},
2446            "sub": {
2447                ".git": {},
2448                "b.txt": "
2449                    one
2450                    two
2451                    three
2452                ".unindent(),
2453            },
2454            "a.txt": "
2455                    one
2456                    two
2457                    three
2458                ".unindent(),
2459            }),
2460        )
2461        .await;
2462
2463    let (project_local, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2464    let project_id = active_call_a
2465        .update(cx_a, |call, cx| {
2466            call.share_project(project_local.clone(), cx)
2467        })
2468        .await
2469        .unwrap();
2470
2471    let project_remote = client_b.build_remote_project(project_id, cx_b).await;
2472
2473    let diff_base = "
2474        one
2475        three
2476    "
2477    .unindent();
2478
2479    let new_diff_base = "
2480        one
2481        two
2482    "
2483    .unindent();
2484
2485    client_a.fs().set_index_for_repo(
2486        Path::new("/dir/.git"),
2487        &[(Path::new("a.txt"), diff_base.clone())],
2488    );
2489
2490    // Create the buffer
2491    let buffer_local_a = project_local
2492        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2493        .await
2494        .unwrap();
2495
2496    // Wait for it to catch up to the new diff
2497    executor.run_until_parked();
2498
2499    // Smoke test diffing
2500
2501    buffer_local_a.read_with(cx_a, |buffer, _| {
2502        assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
2503        git::diff::assert_hunks(
2504            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2505            &buffer,
2506            &diff_base,
2507            &[(1..2, "", "two\n")],
2508        );
2509    });
2510
2511    // Create remote buffer
2512    let buffer_remote_a = project_remote
2513        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2514        .await
2515        .unwrap();
2516
2517    // Wait remote buffer to catch up to the new diff
2518    executor.run_until_parked();
2519
2520    // Smoke test diffing
2521
2522    buffer_remote_a.read_with(cx_b, |buffer, _| {
2523        assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
2524        git::diff::assert_hunks(
2525            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2526            &buffer,
2527            &diff_base,
2528            &[(1..2, "", "two\n")],
2529        );
2530    });
2531
2532    client_a.fs().set_index_for_repo(
2533        Path::new("/dir/.git"),
2534        &[(Path::new("a.txt"), new_diff_base.clone())],
2535    );
2536
2537    // Wait for buffer_local_a to receive it
2538    executor.run_until_parked();
2539
2540    // Smoke test new diffing
2541
2542    buffer_local_a.read_with(cx_a, |buffer, _| {
2543        assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
2544
2545        git::diff::assert_hunks(
2546            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2547            &buffer,
2548            &diff_base,
2549            &[(2..3, "", "three\n")],
2550        );
2551    });
2552
2553    // Smoke test B
2554
2555    buffer_remote_a.read_with(cx_b, |buffer, _| {
2556        assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
2557        git::diff::assert_hunks(
2558            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2559            &buffer,
2560            &diff_base,
2561            &[(2..3, "", "three\n")],
2562        );
2563    });
2564
2565    //Nested git dir
2566
2567    let diff_base = "
2568        one
2569        three
2570    "
2571    .unindent();
2572
2573    let new_diff_base = "
2574        one
2575        two
2576    "
2577    .unindent();
2578
2579    client_a.fs().set_index_for_repo(
2580        Path::new("/dir/sub/.git"),
2581        &[(Path::new("b.txt"), diff_base.clone())],
2582    );
2583
2584    // Create the buffer
2585    let buffer_local_b = project_local
2586        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "sub/b.txt"), cx))
2587        .await
2588        .unwrap();
2589
2590    // Wait for it to catch up to the new diff
2591    executor.run_until_parked();
2592
2593    // Smoke test diffing
2594
2595    buffer_local_b.read_with(cx_a, |buffer, _| {
2596        assert_eq!(buffer.diff_base(), Some(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            &[(1..2, "", "two\n")],
2602        );
2603    });
2604
2605    // Create remote buffer
2606    let buffer_remote_b = project_remote
2607        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "sub/b.txt"), cx))
2608        .await
2609        .unwrap();
2610
2611    // Wait remote buffer to catch up to the new diff
2612    executor.run_until_parked();
2613
2614    // Smoke test diffing
2615
2616    buffer_remote_b.read_with(cx_b, |buffer, _| {
2617        assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
2618        git::diff::assert_hunks(
2619            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2620            &buffer,
2621            &diff_base,
2622            &[(1..2, "", "two\n")],
2623        );
2624    });
2625
2626    client_a.fs().set_index_for_repo(
2627        Path::new("/dir/sub/.git"),
2628        &[(Path::new("b.txt"), new_diff_base.clone())],
2629    );
2630
2631    // Wait for buffer_local_b to receive it
2632    executor.run_until_parked();
2633
2634    // Smoke test new diffing
2635
2636    buffer_local_b.read_with(cx_a, |buffer, _| {
2637        assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
2638        println!("{:?}", buffer.as_rope().to_string());
2639        println!("{:?}", buffer.diff_base());
2640        println!(
2641            "{:?}",
2642            buffer
2643                .snapshot()
2644                .git_diff_hunks_in_row_range(0..4)
2645                .collect::<Vec<_>>()
2646        );
2647
2648        git::diff::assert_hunks(
2649            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2650            &buffer,
2651            &diff_base,
2652            &[(2..3, "", "three\n")],
2653        );
2654    });
2655
2656    // Smoke test B
2657
2658    buffer_remote_b.read_with(cx_b, |buffer, _| {
2659        assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
2660        git::diff::assert_hunks(
2661            buffer.snapshot().git_diff_hunks_in_row_range(0..4),
2662            &buffer,
2663            &diff_base,
2664            &[(2..3, "", "three\n")],
2665        );
2666    });
2667}
2668
2669#[gpui::test]
2670async fn test_git_branch_name(
2671    executor: BackgroundExecutor,
2672    cx_a: &mut TestAppContext,
2673    cx_b: &mut TestAppContext,
2674    cx_c: &mut TestAppContext,
2675) {
2676    let mut server = TestServer::start(executor.clone()).await;
2677    let client_a = server.create_client(cx_a, "user_a").await;
2678    let client_b = server.create_client(cx_b, "user_b").await;
2679    let client_c = server.create_client(cx_c, "user_c").await;
2680    server
2681        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
2682        .await;
2683    let active_call_a = cx_a.read(ActiveCall::global);
2684
2685    client_a
2686        .fs()
2687        .insert_tree(
2688            "/dir",
2689            json!({
2690            ".git": {},
2691            }),
2692        )
2693        .await;
2694
2695    let (project_local, _worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2696    let project_id = active_call_a
2697        .update(cx_a, |call, cx| {
2698            call.share_project(project_local.clone(), cx)
2699        })
2700        .await
2701        .unwrap();
2702
2703    let project_remote = client_b.build_remote_project(project_id, cx_b).await;
2704    client_a
2705        .fs()
2706        .set_branch_name(Path::new("/dir/.git"), Some("branch-1"));
2707
2708    // Wait for it to catch up to the new branch
2709    executor.run_until_parked();
2710
2711    #[track_caller]
2712    fn assert_branch(branch_name: Option<impl Into<String>>, project: &Project, cx: &AppContext) {
2713        let branch_name = branch_name.map(Into::into);
2714        let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
2715        assert_eq!(worktrees.len(), 1);
2716        let worktree = worktrees[0].clone();
2717        let root_entry = worktree.read(cx).snapshot().root_git_entry().unwrap();
2718        assert_eq!(root_entry.branch(), branch_name.map(Into::into));
2719    }
2720
2721    // Smoke test branch reading
2722
2723    project_local.read_with(cx_a, |project, cx| {
2724        assert_branch(Some("branch-1"), project, cx)
2725    });
2726
2727    project_remote.read_with(cx_b, |project, cx| {
2728        assert_branch(Some("branch-1"), project, cx)
2729    });
2730
2731    client_a
2732        .fs()
2733        .set_branch_name(Path::new("/dir/.git"), Some("branch-2"));
2734
2735    // Wait for buffer_local_a to receive it
2736    executor.run_until_parked();
2737
2738    // Smoke test branch reading
2739
2740    project_local.read_with(cx_a, |project, cx| {
2741        assert_branch(Some("branch-2"), project, cx)
2742    });
2743
2744    project_remote.read_with(cx_b, |project, cx| {
2745        assert_branch(Some("branch-2"), project, cx)
2746    });
2747
2748    let project_remote_c = client_c.build_remote_project(project_id, cx_c).await;
2749    executor.run_until_parked();
2750
2751    project_remote_c.read_with(cx_c, |project, cx| {
2752        assert_branch(Some("branch-2"), project, cx)
2753    });
2754}
2755
2756#[gpui::test]
2757async fn test_git_status_sync(
2758    executor: BackgroundExecutor,
2759    cx_a: &mut TestAppContext,
2760    cx_b: &mut TestAppContext,
2761    cx_c: &mut TestAppContext,
2762) {
2763    let mut server = TestServer::start(executor.clone()).await;
2764    let client_a = server.create_client(cx_a, "user_a").await;
2765    let client_b = server.create_client(cx_b, "user_b").await;
2766    let client_c = server.create_client(cx_c, "user_c").await;
2767    server
2768        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
2769        .await;
2770    let active_call_a = cx_a.read(ActiveCall::global);
2771
2772    client_a
2773        .fs()
2774        .insert_tree(
2775            "/dir",
2776            json!({
2777            ".git": {},
2778            "a.txt": "a",
2779            "b.txt": "b",
2780            }),
2781        )
2782        .await;
2783
2784    const A_TXT: &'static str = "a.txt";
2785    const B_TXT: &'static str = "b.txt";
2786
2787    client_a.fs().set_status_for_repo_via_git_operation(
2788        Path::new("/dir/.git"),
2789        &[
2790            (&Path::new(A_TXT), GitFileStatus::Added),
2791            (&Path::new(B_TXT), GitFileStatus::Added),
2792        ],
2793    );
2794
2795    let (project_local, _worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2796    let project_id = active_call_a
2797        .update(cx_a, |call, cx| {
2798            call.share_project(project_local.clone(), cx)
2799        })
2800        .await
2801        .unwrap();
2802
2803    let project_remote = client_b.build_remote_project(project_id, cx_b).await;
2804
2805    // Wait for it to catch up to the new status
2806    executor.run_until_parked();
2807
2808    #[track_caller]
2809    fn assert_status(
2810        file: &impl AsRef<Path>,
2811        status: Option<GitFileStatus>,
2812        project: &Project,
2813        cx: &AppContext,
2814    ) {
2815        let file = file.as_ref();
2816        let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
2817        assert_eq!(worktrees.len(), 1);
2818        let worktree = worktrees[0].clone();
2819        let snapshot = worktree.read(cx).snapshot();
2820        assert_eq!(snapshot.status_for_file(file), status);
2821    }
2822
2823    // Smoke test status reading
2824
2825    project_local.read_with(cx_a, |project, cx| {
2826        assert_status(&Path::new(A_TXT), Some(GitFileStatus::Added), project, cx);
2827        assert_status(&Path::new(B_TXT), Some(GitFileStatus::Added), project, cx);
2828    });
2829
2830    project_remote.read_with(cx_b, |project, cx| {
2831        assert_status(&Path::new(A_TXT), Some(GitFileStatus::Added), project, cx);
2832        assert_status(&Path::new(B_TXT), Some(GitFileStatus::Added), project, cx);
2833    });
2834
2835    client_a.fs().set_status_for_repo_via_working_copy_change(
2836        Path::new("/dir/.git"),
2837        &[
2838            (&Path::new(A_TXT), GitFileStatus::Modified),
2839            (&Path::new(B_TXT), GitFileStatus::Modified),
2840        ],
2841    );
2842
2843    // Wait for buffer_local_a to receive it
2844    executor.run_until_parked();
2845
2846    // Smoke test status reading
2847
2848    project_local.read_with(cx_a, |project, cx| {
2849        assert_status(
2850            &Path::new(A_TXT),
2851            Some(GitFileStatus::Modified),
2852            project,
2853            cx,
2854        );
2855        assert_status(
2856            &Path::new(B_TXT),
2857            Some(GitFileStatus::Modified),
2858            project,
2859            cx,
2860        );
2861    });
2862
2863    project_remote.read_with(cx_b, |project, cx| {
2864        assert_status(
2865            &Path::new(A_TXT),
2866            Some(GitFileStatus::Modified),
2867            project,
2868            cx,
2869        );
2870        assert_status(
2871            &Path::new(B_TXT),
2872            Some(GitFileStatus::Modified),
2873            project,
2874            cx,
2875        );
2876    });
2877
2878    // And synchronization while joining
2879    let project_remote_c = client_c.build_remote_project(project_id, cx_c).await;
2880    executor.run_until_parked();
2881
2882    project_remote_c.read_with(cx_c, |project, cx| {
2883        assert_status(
2884            &Path::new(A_TXT),
2885            Some(GitFileStatus::Modified),
2886            project,
2887            cx,
2888        );
2889        assert_status(
2890            &Path::new(B_TXT),
2891            Some(GitFileStatus::Modified),
2892            project,
2893            cx,
2894        );
2895    });
2896}
2897
2898#[gpui::test(iterations = 10)]
2899async fn test_fs_operations(
2900    executor: BackgroundExecutor,
2901    cx_a: &mut TestAppContext,
2902    cx_b: &mut TestAppContext,
2903) {
2904    let mut server = TestServer::start(executor.clone()).await;
2905    let client_a = server.create_client(cx_a, "user_a").await;
2906    let client_b = server.create_client(cx_b, "user_b").await;
2907    server
2908        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2909        .await;
2910    let active_call_a = cx_a.read(ActiveCall::global);
2911
2912    client_a
2913        .fs()
2914        .insert_tree(
2915            "/dir",
2916            json!({
2917                "a.txt": "a-contents",
2918                "b.txt": "b-contents",
2919            }),
2920        )
2921        .await;
2922    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2923    let project_id = active_call_a
2924        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2925        .await
2926        .unwrap();
2927    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2928
2929    let worktree_a = project_a.read_with(cx_a, |project, _| project.worktrees().next().unwrap());
2930
2931    let worktree_b = project_b.read_with(cx_b, |project, _| project.worktrees().next().unwrap());
2932
2933    let entry = project_b
2934        .update(cx_b, |project, cx| {
2935            project.create_entry((worktree_id, "c.txt"), false, cx)
2936        })
2937        .await
2938        .unwrap()
2939        .unwrap();
2940
2941    worktree_a.read_with(cx_a, |worktree, _| {
2942        assert_eq!(
2943            worktree
2944                .paths()
2945                .map(|p| p.to_string_lossy())
2946                .collect::<Vec<_>>(),
2947            ["a.txt", "b.txt", "c.txt"]
2948        );
2949    });
2950
2951    worktree_b.read_with(cx_b, |worktree, _| {
2952        assert_eq!(
2953            worktree
2954                .paths()
2955                .map(|p| p.to_string_lossy())
2956                .collect::<Vec<_>>(),
2957            ["a.txt", "b.txt", "c.txt"]
2958        );
2959    });
2960
2961    project_b
2962        .update(cx_b, |project, cx| {
2963            project.rename_entry(entry.id, Path::new("d.txt"), cx)
2964        })
2965        .await
2966        .unwrap()
2967        .unwrap();
2968
2969    worktree_a.read_with(cx_a, |worktree, _| {
2970        assert_eq!(
2971            worktree
2972                .paths()
2973                .map(|p| p.to_string_lossy())
2974                .collect::<Vec<_>>(),
2975            ["a.txt", "b.txt", "d.txt"]
2976        );
2977    });
2978
2979    worktree_b.read_with(cx_b, |worktree, _| {
2980        assert_eq!(
2981            worktree
2982                .paths()
2983                .map(|p| p.to_string_lossy())
2984                .collect::<Vec<_>>(),
2985            ["a.txt", "b.txt", "d.txt"]
2986        );
2987    });
2988
2989    let dir_entry = project_b
2990        .update(cx_b, |project, cx| {
2991            project.create_entry((worktree_id, "DIR"), true, cx)
2992        })
2993        .await
2994        .unwrap()
2995        .unwrap();
2996
2997    worktree_a.read_with(cx_a, |worktree, _| {
2998        assert_eq!(
2999            worktree
3000                .paths()
3001                .map(|p| p.to_string_lossy())
3002                .collect::<Vec<_>>(),
3003            ["DIR", "a.txt", "b.txt", "d.txt"]
3004        );
3005    });
3006
3007    worktree_b.read_with(cx_b, |worktree, _| {
3008        assert_eq!(
3009            worktree
3010                .paths()
3011                .map(|p| p.to_string_lossy())
3012                .collect::<Vec<_>>(),
3013            ["DIR", "a.txt", "b.txt", "d.txt"]
3014        );
3015    });
3016
3017    project_b
3018        .update(cx_b, |project, cx| {
3019            project.create_entry((worktree_id, "DIR/e.txt"), false, cx)
3020        })
3021        .await
3022        .unwrap()
3023        .unwrap();
3024    project_b
3025        .update(cx_b, |project, cx| {
3026            project.create_entry((worktree_id, "DIR/SUBDIR"), true, cx)
3027        })
3028        .await
3029        .unwrap()
3030        .unwrap();
3031    project_b
3032        .update(cx_b, |project, cx| {
3033            project.create_entry((worktree_id, "DIR/SUBDIR/f.txt"), false, cx)
3034        })
3035        .await
3036        .unwrap()
3037        .unwrap();
3038
3039    worktree_a.read_with(cx_a, |worktree, _| {
3040        assert_eq!(
3041            worktree
3042                .paths()
3043                .map(|p| p.to_string_lossy())
3044                .collect::<Vec<_>>(),
3045            [
3046                "DIR",
3047                "DIR/SUBDIR",
3048                "DIR/SUBDIR/f.txt",
3049                "DIR/e.txt",
3050                "a.txt",
3051                "b.txt",
3052                "d.txt"
3053            ]
3054        );
3055    });
3056
3057    worktree_b.read_with(cx_b, |worktree, _| {
3058        assert_eq!(
3059            worktree
3060                .paths()
3061                .map(|p| p.to_string_lossy())
3062                .collect::<Vec<_>>(),
3063            [
3064                "DIR",
3065                "DIR/SUBDIR",
3066                "DIR/SUBDIR/f.txt",
3067                "DIR/e.txt",
3068                "a.txt",
3069                "b.txt",
3070                "d.txt"
3071            ]
3072        );
3073    });
3074
3075    project_b
3076        .update(cx_b, |project, cx| {
3077            project.copy_entry(entry.id, Path::new("f.txt"), cx)
3078        })
3079        .await
3080        .unwrap()
3081        .unwrap();
3082
3083    worktree_a.read_with(cx_a, |worktree, _| {
3084        assert_eq!(
3085            worktree
3086                .paths()
3087                .map(|p| p.to_string_lossy())
3088                .collect::<Vec<_>>(),
3089            [
3090                "DIR",
3091                "DIR/SUBDIR",
3092                "DIR/SUBDIR/f.txt",
3093                "DIR/e.txt",
3094                "a.txt",
3095                "b.txt",
3096                "d.txt",
3097                "f.txt"
3098            ]
3099        );
3100    });
3101
3102    worktree_b.read_with(cx_b, |worktree, _| {
3103        assert_eq!(
3104            worktree
3105                .paths()
3106                .map(|p| p.to_string_lossy())
3107                .collect::<Vec<_>>(),
3108            [
3109                "DIR",
3110                "DIR/SUBDIR",
3111                "DIR/SUBDIR/f.txt",
3112                "DIR/e.txt",
3113                "a.txt",
3114                "b.txt",
3115                "d.txt",
3116                "f.txt"
3117            ]
3118        );
3119    });
3120
3121    project_b
3122        .update(cx_b, |project, cx| {
3123            project.delete_entry(dir_entry.id, cx).unwrap()
3124        })
3125        .await
3126        .unwrap();
3127    executor.run_until_parked();
3128
3129    worktree_a.read_with(cx_a, |worktree, _| {
3130        assert_eq!(
3131            worktree
3132                .paths()
3133                .map(|p| p.to_string_lossy())
3134                .collect::<Vec<_>>(),
3135            ["a.txt", "b.txt", "d.txt", "f.txt"]
3136        );
3137    });
3138
3139    worktree_b.read_with(cx_b, |worktree, _| {
3140        assert_eq!(
3141            worktree
3142                .paths()
3143                .map(|p| p.to_string_lossy())
3144                .collect::<Vec<_>>(),
3145            ["a.txt", "b.txt", "d.txt", "f.txt"]
3146        );
3147    });
3148
3149    project_b
3150        .update(cx_b, |project, cx| {
3151            project.delete_entry(entry.id, cx).unwrap()
3152        })
3153        .await
3154        .unwrap();
3155
3156    worktree_a.read_with(cx_a, |worktree, _| {
3157        assert_eq!(
3158            worktree
3159                .paths()
3160                .map(|p| p.to_string_lossy())
3161                .collect::<Vec<_>>(),
3162            ["a.txt", "b.txt", "f.txt"]
3163        );
3164    });
3165
3166    worktree_b.read_with(cx_b, |worktree, _| {
3167        assert_eq!(
3168            worktree
3169                .paths()
3170                .map(|p| p.to_string_lossy())
3171                .collect::<Vec<_>>(),
3172            ["a.txt", "b.txt", "f.txt"]
3173        );
3174    });
3175}
3176
3177#[gpui::test(iterations = 10)]
3178async fn test_local_settings(
3179    executor: BackgroundExecutor,
3180    cx_a: &mut TestAppContext,
3181    cx_b: &mut TestAppContext,
3182) {
3183    let mut server = TestServer::start(executor.clone()).await;
3184    let client_a = server.create_client(cx_a, "user_a").await;
3185    let client_b = server.create_client(cx_b, "user_b").await;
3186    server
3187        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3188        .await;
3189    let active_call_a = cx_a.read(ActiveCall::global);
3190
3191    // As client A, open a project that contains some local settings files
3192    client_a
3193        .fs()
3194        .insert_tree(
3195            "/dir",
3196            json!({
3197                ".zed": {
3198                    "settings.json": r#"{ "tab_size": 2 }"#
3199                },
3200                "a": {
3201                    ".zed": {
3202                        "settings.json": r#"{ "tab_size": 8 }"#
3203                    },
3204                    "a.txt": "a-contents",
3205                },
3206                "b": {
3207                    "b.txt": "b-contents",
3208                }
3209            }),
3210        )
3211        .await;
3212    let (project_a, _) = client_a.build_local_project("/dir", cx_a).await;
3213    executor.run_until_parked();
3214    let project_id = active_call_a
3215        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3216        .await
3217        .unwrap();
3218    executor.run_until_parked();
3219
3220    // As client B, join that project and observe the local settings.
3221    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3222
3223    let worktree_b = project_b.read_with(cx_b, |project, _| project.worktrees().next().unwrap());
3224    executor.run_until_parked();
3225    cx_b.read(|cx| {
3226        let store = cx.global::<SettingsStore>();
3227        assert_eq!(
3228            store
3229                .local_settings(worktree_b.read(cx).id().to_usize())
3230                .collect::<Vec<_>>(),
3231            &[
3232                (Path::new("").into(), r#"{"tab_size":2}"#.to_string()),
3233                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3234            ]
3235        )
3236    });
3237
3238    // As client A, update a settings file. As Client B, see the changed settings.
3239    client_a
3240        .fs()
3241        .insert_file("/dir/.zed/settings.json", r#"{}"#.into())
3242        .await;
3243    executor.run_until_parked();
3244    cx_b.read(|cx| {
3245        let store = cx.global::<SettingsStore>();
3246        assert_eq!(
3247            store
3248                .local_settings(worktree_b.read(cx).id().to_usize())
3249                .collect::<Vec<_>>(),
3250            &[
3251                (Path::new("").into(), r#"{}"#.to_string()),
3252                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3253            ]
3254        )
3255    });
3256
3257    // As client A, create and remove some settings files. As client B, see the changed settings.
3258    client_a
3259        .fs()
3260        .remove_file("/dir/.zed/settings.json".as_ref(), Default::default())
3261        .await
3262        .unwrap();
3263    client_a
3264        .fs()
3265        .create_dir("/dir/b/.zed".as_ref())
3266        .await
3267        .unwrap();
3268    client_a
3269        .fs()
3270        .insert_file("/dir/b/.zed/settings.json", r#"{"tab_size": 4}"#.into())
3271        .await;
3272    executor.run_until_parked();
3273    cx_b.read(|cx| {
3274        let store = cx.global::<SettingsStore>();
3275        assert_eq!(
3276            store
3277                .local_settings(worktree_b.read(cx).id().to_usize())
3278                .collect::<Vec<_>>(),
3279            &[
3280                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3281                (Path::new("b").into(), r#"{"tab_size":4}"#.to_string()),
3282            ]
3283        )
3284    });
3285
3286    // As client B, disconnect.
3287    server.forbid_connections();
3288    server.disconnect_client(client_b.peer_id().unwrap());
3289
3290    // As client A, change and remove settings files while client B is disconnected.
3291    client_a
3292        .fs()
3293        .insert_file("/dir/a/.zed/settings.json", r#"{"hard_tabs":true}"#.into())
3294        .await;
3295    client_a
3296        .fs()
3297        .remove_file("/dir/b/.zed/settings.json".as_ref(), Default::default())
3298        .await
3299        .unwrap();
3300    executor.run_until_parked();
3301
3302    // As client B, reconnect and see the changed settings.
3303    server.allow_connections();
3304    executor.advance_clock(RECEIVE_TIMEOUT);
3305    cx_b.read(|cx| {
3306        let store = cx.global::<SettingsStore>();
3307        assert_eq!(
3308            store
3309                .local_settings(worktree_b.read(cx).id().to_usize())
3310                .collect::<Vec<_>>(),
3311            &[(Path::new("a").into(), r#"{"hard_tabs":true}"#.to_string()),]
3312        )
3313    });
3314}
3315
3316#[gpui::test(iterations = 10)]
3317async fn test_buffer_conflict_after_save(
3318    executor: BackgroundExecutor,
3319    cx_a: &mut TestAppContext,
3320    cx_b: &mut TestAppContext,
3321) {
3322    let mut server = TestServer::start(executor.clone()).await;
3323    let client_a = server.create_client(cx_a, "user_a").await;
3324    let client_b = server.create_client(cx_b, "user_b").await;
3325    server
3326        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3327        .await;
3328    let active_call_a = cx_a.read(ActiveCall::global);
3329
3330    client_a
3331        .fs()
3332        .insert_tree(
3333            "/dir",
3334            json!({
3335                "a.txt": "a-contents",
3336            }),
3337        )
3338        .await;
3339    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3340    let project_id = active_call_a
3341        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3342        .await
3343        .unwrap();
3344    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3345
3346    // Open a buffer as client B
3347    let buffer_b = project_b
3348        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3349        .await
3350        .unwrap();
3351
3352    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "world ")], None, cx));
3353
3354    buffer_b.read_with(cx_b, |buf, _| {
3355        assert!(buf.is_dirty());
3356        assert!(!buf.has_conflict());
3357    });
3358
3359    project_b
3360        .update(cx_b, |project, cx| {
3361            project.save_buffer(buffer_b.clone(), cx)
3362        })
3363        .await
3364        .unwrap();
3365
3366    buffer_b.read_with(cx_b, |buffer_b, _| assert!(!buffer_b.is_dirty()));
3367
3368    buffer_b.read_with(cx_b, |buf, _| {
3369        assert!(!buf.has_conflict());
3370    });
3371
3372    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "hello ")], None, cx));
3373
3374    buffer_b.read_with(cx_b, |buf, _| {
3375        assert!(buf.is_dirty());
3376        assert!(!buf.has_conflict());
3377    });
3378}
3379
3380#[gpui::test(iterations = 10)]
3381async fn test_buffer_reloading(
3382    executor: BackgroundExecutor,
3383    cx_a: &mut TestAppContext,
3384    cx_b: &mut TestAppContext,
3385) {
3386    let mut server = TestServer::start(executor.clone()).await;
3387    let client_a = server.create_client(cx_a, "user_a").await;
3388    let client_b = server.create_client(cx_b, "user_b").await;
3389    server
3390        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3391        .await;
3392    let active_call_a = cx_a.read(ActiveCall::global);
3393
3394    client_a
3395        .fs()
3396        .insert_tree(
3397            "/dir",
3398            json!({
3399                "a.txt": "a\nb\nc",
3400            }),
3401        )
3402        .await;
3403    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3404    let project_id = active_call_a
3405        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3406        .await
3407        .unwrap();
3408    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3409
3410    // Open a buffer as client B
3411    let buffer_b = project_b
3412        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3413        .await
3414        .unwrap();
3415
3416    buffer_b.read_with(cx_b, |buf, _| {
3417        assert!(!buf.is_dirty());
3418        assert!(!buf.has_conflict());
3419        assert_eq!(buf.line_ending(), LineEnding::Unix);
3420    });
3421
3422    let new_contents = Rope::from("d\ne\nf");
3423    client_a
3424        .fs()
3425        .save("/dir/a.txt".as_ref(), &new_contents, LineEnding::Windows)
3426        .await
3427        .unwrap();
3428
3429    executor.run_until_parked();
3430
3431    buffer_b.read_with(cx_b, |buf, _| {
3432        assert_eq!(buf.text(), new_contents.to_string());
3433        assert!(!buf.is_dirty());
3434        assert!(!buf.has_conflict());
3435        assert_eq!(buf.line_ending(), LineEnding::Windows);
3436    });
3437}
3438
3439#[gpui::test(iterations = 10)]
3440async fn test_editing_while_guest_opens_buffer(
3441    executor: BackgroundExecutor,
3442    cx_a: &mut TestAppContext,
3443    cx_b: &mut TestAppContext,
3444) {
3445    let mut server = TestServer::start(executor.clone()).await;
3446    let client_a = server.create_client(cx_a, "user_a").await;
3447    let client_b = server.create_client(cx_b, "user_b").await;
3448    server
3449        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3450        .await;
3451    let active_call_a = cx_a.read(ActiveCall::global);
3452
3453    client_a
3454        .fs()
3455        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3456        .await;
3457    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3458    let project_id = active_call_a
3459        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3460        .await
3461        .unwrap();
3462    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3463
3464    // Open a buffer as client A
3465    let buffer_a = project_a
3466        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3467        .await
3468        .unwrap();
3469
3470    // Start opening the same buffer as client B
3471    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx));
3472    let buffer_b = cx_b.executor().spawn(open_buffer);
3473
3474    // Edit the buffer as client A while client B is still opening it.
3475    cx_b.executor().simulate_random_delay().await;
3476    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "X")], None, cx));
3477    cx_b.executor().simulate_random_delay().await;
3478    buffer_a.update(cx_a, |buf, cx| buf.edit([(1..1, "Y")], None, cx));
3479
3480    let text = buffer_a.read_with(cx_a, |buf, _| buf.text());
3481    let buffer_b = buffer_b.await.unwrap();
3482    executor.run_until_parked();
3483
3484    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), text));
3485}
3486
3487#[gpui::test(iterations = 10)]
3488async fn test_leaving_worktree_while_opening_buffer(
3489    executor: BackgroundExecutor,
3490    cx_a: &mut TestAppContext,
3491    cx_b: &mut TestAppContext,
3492) {
3493    let mut server = TestServer::start(executor.clone()).await;
3494    let client_a = server.create_client(cx_a, "user_a").await;
3495    let client_b = server.create_client(cx_b, "user_b").await;
3496    server
3497        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3498        .await;
3499    let active_call_a = cx_a.read(ActiveCall::global);
3500
3501    client_a
3502        .fs()
3503        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3504        .await;
3505    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3506    let project_id = active_call_a
3507        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3508        .await
3509        .unwrap();
3510    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3511
3512    // See that a guest has joined as client A.
3513    executor.run_until_parked();
3514
3515    project_a.read_with(cx_a, |p, _| assert_eq!(p.collaborators().len(), 1));
3516
3517    // Begin opening a buffer as client B, but leave the project before the open completes.
3518    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx));
3519    let buffer_b = cx_b.executor().spawn(open_buffer);
3520    cx_b.update(|_| drop(project_b));
3521    drop(buffer_b);
3522
3523    // See that the guest has left.
3524    executor.run_until_parked();
3525
3526    project_a.read_with(cx_a, |p, _| assert!(p.collaborators().is_empty()));
3527}
3528
3529#[gpui::test(iterations = 10)]
3530async fn test_canceling_buffer_opening(
3531    executor: BackgroundExecutor,
3532    cx_a: &mut TestAppContext,
3533    cx_b: &mut TestAppContext,
3534) {
3535    let mut server = TestServer::start(executor.clone()).await;
3536    let client_a = server.create_client(cx_a, "user_a").await;
3537    let client_b = server.create_client(cx_b, "user_b").await;
3538    server
3539        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3540        .await;
3541    let active_call_a = cx_a.read(ActiveCall::global);
3542
3543    client_a
3544        .fs()
3545        .insert_tree(
3546            "/dir",
3547            json!({
3548                "a.txt": "abc",
3549            }),
3550        )
3551        .await;
3552    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3553    let project_id = active_call_a
3554        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3555        .await
3556        .unwrap();
3557    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3558
3559    let buffer_a = project_a
3560        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3561        .await
3562        .unwrap();
3563
3564    // Open a buffer as client B but cancel after a random amount of time.
3565    let buffer_b = project_b.update(cx_b, |p, cx| {
3566        p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3567    });
3568    executor.simulate_random_delay().await;
3569    drop(buffer_b);
3570
3571    // Try opening the same buffer again as client B, and ensure we can
3572    // still do it despite the cancellation above.
3573    let buffer_b = project_b
3574        .update(cx_b, |p, cx| {
3575            p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3576        })
3577        .await
3578        .unwrap();
3579
3580    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "abc"));
3581}
3582
3583#[gpui::test(iterations = 10)]
3584async fn test_leaving_project(
3585    executor: BackgroundExecutor,
3586    cx_a: &mut TestAppContext,
3587    cx_b: &mut TestAppContext,
3588    cx_c: &mut TestAppContext,
3589) {
3590    let mut server = TestServer::start(executor.clone()).await;
3591    let client_a = server.create_client(cx_a, "user_a").await;
3592    let client_b = server.create_client(cx_b, "user_b").await;
3593    let client_c = server.create_client(cx_c, "user_c").await;
3594    server
3595        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3596        .await;
3597    let active_call_a = cx_a.read(ActiveCall::global);
3598
3599    client_a
3600        .fs()
3601        .insert_tree(
3602            "/a",
3603            json!({
3604                "a.txt": "a-contents",
3605                "b.txt": "b-contents",
3606            }),
3607        )
3608        .await;
3609    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
3610    let project_id = active_call_a
3611        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3612        .await
3613        .unwrap();
3614    let project_b1 = client_b.build_remote_project(project_id, cx_b).await;
3615    let project_c = client_c.build_remote_project(project_id, cx_c).await;
3616
3617    // Client A sees that a guest has joined.
3618    executor.run_until_parked();
3619
3620    project_a.read_with(cx_a, |project, _| {
3621        assert_eq!(project.collaborators().len(), 2);
3622    });
3623
3624    project_b1.read_with(cx_b, |project, _| {
3625        assert_eq!(project.collaborators().len(), 2);
3626    });
3627
3628    project_c.read_with(cx_c, |project, _| {
3629        assert_eq!(project.collaborators().len(), 2);
3630    });
3631
3632    // Client B opens a buffer.
3633    let buffer_b1 = project_b1
3634        .update(cx_b, |project, cx| {
3635            let worktree_id = project.worktrees().next().unwrap().read(cx).id();
3636            project.open_buffer((worktree_id, "a.txt"), cx)
3637        })
3638        .await
3639        .unwrap();
3640
3641    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3642
3643    // Drop client B's project and ensure client A and client C observe client B leaving.
3644    cx_b.update(|_| drop(project_b1));
3645    executor.run_until_parked();
3646
3647    project_a.read_with(cx_a, |project, _| {
3648        assert_eq!(project.collaborators().len(), 1);
3649    });
3650
3651    project_c.read_with(cx_c, |project, _| {
3652        assert_eq!(project.collaborators().len(), 1);
3653    });
3654
3655    // Client B re-joins the project and can open buffers as before.
3656    let project_b2 = client_b.build_remote_project(project_id, cx_b).await;
3657    executor.run_until_parked();
3658
3659    project_a.read_with(cx_a, |project, _| {
3660        assert_eq!(project.collaborators().len(), 2);
3661    });
3662
3663    project_b2.read_with(cx_b, |project, _| {
3664        assert_eq!(project.collaborators().len(), 2);
3665    });
3666
3667    project_c.read_with(cx_c, |project, _| {
3668        assert_eq!(project.collaborators().len(), 2);
3669    });
3670
3671    let buffer_b2 = project_b2
3672        .update(cx_b, |project, cx| {
3673            let worktree_id = project.worktrees().next().unwrap().read(cx).id();
3674            project.open_buffer((worktree_id, "a.txt"), cx)
3675        })
3676        .await
3677        .unwrap();
3678
3679    buffer_b2.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3680
3681    // Drop client B's connection and ensure client A and client C observe client B leaving.
3682    client_b.disconnect(&cx_b.to_async());
3683    executor.advance_clock(RECONNECT_TIMEOUT);
3684
3685    project_a.read_with(cx_a, |project, _| {
3686        assert_eq!(project.collaborators().len(), 1);
3687    });
3688
3689    project_b2.read_with(cx_b, |project, _| {
3690        assert!(project.is_disconnected());
3691    });
3692
3693    project_c.read_with(cx_c, |project, _| {
3694        assert_eq!(project.collaborators().len(), 1);
3695    });
3696
3697    // Client B can't join the project, unless they re-join the room.
3698    cx_b.spawn(|cx| {
3699        Project::remote(
3700            project_id,
3701            client_b.app_state.client.clone(),
3702            client_b.user_store().clone(),
3703            client_b.language_registry().clone(),
3704            FakeFs::new(cx.background_executor().clone()),
3705            ChannelRole::Member,
3706            cx,
3707        )
3708    })
3709    .await
3710    .unwrap_err();
3711
3712    // Simulate connection loss for client C and ensure client A observes client C leaving the project.
3713    client_c.wait_for_current_user(cx_c).await;
3714    server.forbid_connections();
3715    server.disconnect_client(client_c.peer_id().unwrap());
3716    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
3717    executor.run_until_parked();
3718
3719    project_a.read_with(cx_a, |project, _| {
3720        assert_eq!(project.collaborators().len(), 0);
3721    });
3722
3723    project_b2.read_with(cx_b, |project, _| {
3724        assert!(project.is_disconnected());
3725    });
3726
3727    project_c.read_with(cx_c, |project, _| {
3728        assert!(project.is_disconnected());
3729    });
3730}
3731
3732#[gpui::test(iterations = 10)]
3733async fn test_collaborating_with_diagnostics(
3734    executor: BackgroundExecutor,
3735    cx_a: &mut TestAppContext,
3736    cx_b: &mut TestAppContext,
3737    cx_c: &mut TestAppContext,
3738) {
3739    let mut server = TestServer::start(executor.clone()).await;
3740    let client_a = server.create_client(cx_a, "user_a").await;
3741    let client_b = server.create_client(cx_b, "user_b").await;
3742    let client_c = server.create_client(cx_c, "user_c").await;
3743    server
3744        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3745        .await;
3746    let active_call_a = cx_a.read(ActiveCall::global);
3747
3748    // Set up a fake language server.
3749    let mut language = Language::new(
3750        LanguageConfig {
3751            name: "Rust".into(),
3752            matcher: LanguageMatcher {
3753                path_suffixes: vec!["rs".to_string()],
3754                ..Default::default()
3755            },
3756            ..Default::default()
3757        },
3758        Some(tree_sitter_rust::language()),
3759    );
3760    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
3761    client_a.language_registry().add(Arc::new(language));
3762
3763    // Share a project as client A
3764    client_a
3765        .fs()
3766        .insert_tree(
3767            "/a",
3768            json!({
3769                "a.rs": "let one = two",
3770                "other.rs": "",
3771            }),
3772        )
3773        .await;
3774    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3775
3776    // Cause the language server to start.
3777    let _buffer = project_a
3778        .update(cx_a, |project, cx| {
3779            project.open_buffer(
3780                ProjectPath {
3781                    worktree_id,
3782                    path: Path::new("other.rs").into(),
3783                },
3784                cx,
3785            )
3786        })
3787        .await
3788        .unwrap();
3789
3790    // Simulate a language server reporting errors for a file.
3791    let mut fake_language_server = fake_language_servers.next().await.unwrap();
3792    fake_language_server
3793        .receive_notification::<lsp::notification::DidOpenTextDocument>()
3794        .await;
3795    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3796        lsp::PublishDiagnosticsParams {
3797            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3798            version: None,
3799            diagnostics: vec![lsp::Diagnostic {
3800                severity: Some(lsp::DiagnosticSeverity::WARNING),
3801                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3802                message: "message 0".to_string(),
3803                ..Default::default()
3804            }],
3805        },
3806    );
3807
3808    // Client A shares the project and, simultaneously, the language server
3809    // publishes a diagnostic. This is done to ensure that the server always
3810    // observes the latest diagnostics for a worktree.
3811    let project_id = active_call_a
3812        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3813        .await
3814        .unwrap();
3815    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3816        lsp::PublishDiagnosticsParams {
3817            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3818            version: None,
3819            diagnostics: vec![lsp::Diagnostic {
3820                severity: Some(lsp::DiagnosticSeverity::ERROR),
3821                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3822                message: "message 1".to_string(),
3823                ..Default::default()
3824            }],
3825        },
3826    );
3827
3828    // Join the worktree as client B.
3829    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3830
3831    // Wait for server to see the diagnostics update.
3832    executor.run_until_parked();
3833
3834    // Ensure client B observes the new diagnostics.
3835
3836    project_b.read_with(cx_b, |project, cx| {
3837        assert_eq!(
3838            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
3839            &[(
3840                ProjectPath {
3841                    worktree_id,
3842                    path: Arc::from(Path::new("a.rs")),
3843                },
3844                LanguageServerId(0),
3845                DiagnosticSummary {
3846                    error_count: 1,
3847                    warning_count: 0,
3848                    ..Default::default()
3849                },
3850            )]
3851        )
3852    });
3853
3854    // Join project as client C and observe the diagnostics.
3855    let project_c = client_c.build_remote_project(project_id, cx_c).await;
3856    executor.run_until_parked();
3857    let project_c_diagnostic_summaries =
3858        Rc::new(RefCell::new(project_c.read_with(cx_c, |project, cx| {
3859            project.diagnostic_summaries(false, cx).collect::<Vec<_>>()
3860        })));
3861    project_c.update(cx_c, |_, cx| {
3862        let summaries = project_c_diagnostic_summaries.clone();
3863        cx.subscribe(&project_c, {
3864            move |p, _, event, cx| {
3865                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
3866                    *summaries.borrow_mut() = p.diagnostic_summaries(false, cx).collect();
3867                }
3868            }
3869        })
3870        .detach();
3871    });
3872
3873    executor.run_until_parked();
3874    assert_eq!(
3875        project_c_diagnostic_summaries.borrow().as_slice(),
3876        &[(
3877            ProjectPath {
3878                worktree_id,
3879                path: Arc::from(Path::new("a.rs")),
3880            },
3881            LanguageServerId(0),
3882            DiagnosticSummary {
3883                error_count: 1,
3884                warning_count: 0,
3885                ..Default::default()
3886            },
3887        )]
3888    );
3889
3890    // Simulate a language server reporting more errors for a file.
3891    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3892        lsp::PublishDiagnosticsParams {
3893            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3894            version: None,
3895            diagnostics: vec![
3896                lsp::Diagnostic {
3897                    severity: Some(lsp::DiagnosticSeverity::ERROR),
3898                    range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3899                    message: "message 1".to_string(),
3900                    ..Default::default()
3901                },
3902                lsp::Diagnostic {
3903                    severity: Some(lsp::DiagnosticSeverity::WARNING),
3904                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 13)),
3905                    message: "message 2".to_string(),
3906                    ..Default::default()
3907                },
3908            ],
3909        },
3910    );
3911
3912    // Clients B and C get the updated summaries
3913    executor.run_until_parked();
3914
3915    project_b.read_with(cx_b, |project, cx| {
3916        assert_eq!(
3917            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
3918            [(
3919                ProjectPath {
3920                    worktree_id,
3921                    path: Arc::from(Path::new("a.rs")),
3922                },
3923                LanguageServerId(0),
3924                DiagnosticSummary {
3925                    error_count: 1,
3926                    warning_count: 1,
3927                },
3928            )]
3929        );
3930    });
3931
3932    project_c.read_with(cx_c, |project, cx| {
3933        assert_eq!(
3934            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
3935            [(
3936                ProjectPath {
3937                    worktree_id,
3938                    path: Arc::from(Path::new("a.rs")),
3939                },
3940                LanguageServerId(0),
3941                DiagnosticSummary {
3942                    error_count: 1,
3943                    warning_count: 1,
3944                },
3945            )]
3946        );
3947    });
3948
3949    // Open the file with the errors on client B. They should be present.
3950    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
3951    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
3952
3953    buffer_b.read_with(cx_b, |buffer, _| {
3954        assert_eq!(
3955            buffer
3956                .snapshot()
3957                .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
3958                .collect::<Vec<_>>(),
3959            &[
3960                DiagnosticEntry {
3961                    range: Point::new(0, 4)..Point::new(0, 7),
3962                    diagnostic: Diagnostic {
3963                        group_id: 2,
3964                        message: "message 1".to_string(),
3965                        severity: lsp::DiagnosticSeverity::ERROR,
3966                        is_primary: true,
3967                        ..Default::default()
3968                    }
3969                },
3970                DiagnosticEntry {
3971                    range: Point::new(0, 10)..Point::new(0, 13),
3972                    diagnostic: Diagnostic {
3973                        group_id: 3,
3974                        severity: lsp::DiagnosticSeverity::WARNING,
3975                        message: "message 2".to_string(),
3976                        is_primary: true,
3977                        ..Default::default()
3978                    }
3979                }
3980            ]
3981        );
3982    });
3983
3984    // Simulate a language server reporting no errors for a file.
3985    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3986        lsp::PublishDiagnosticsParams {
3987            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3988            version: None,
3989            diagnostics: vec![],
3990        },
3991    );
3992    executor.run_until_parked();
3993
3994    project_a.read_with(cx_a, |project, cx| {
3995        assert_eq!(
3996            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
3997            []
3998        )
3999    });
4000
4001    project_b.read_with(cx_b, |project, cx| {
4002        assert_eq!(
4003            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4004            []
4005        )
4006    });
4007
4008    project_c.read_with(cx_c, |project, cx| {
4009        assert_eq!(
4010            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4011            []
4012        )
4013    });
4014}
4015
4016#[gpui::test(iterations = 10)]
4017async fn test_collaborating_with_lsp_progress_updates_and_diagnostics_ordering(
4018    executor: BackgroundExecutor,
4019    cx_a: &mut TestAppContext,
4020    cx_b: &mut TestAppContext,
4021) {
4022    let mut server = TestServer::start(executor.clone()).await;
4023    let client_a = server.create_client(cx_a, "user_a").await;
4024    let client_b = server.create_client(cx_b, "user_b").await;
4025    server
4026        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4027        .await;
4028
4029    // Set up a fake language server.
4030    let mut language = Language::new(
4031        LanguageConfig {
4032            name: "Rust".into(),
4033            matcher: LanguageMatcher {
4034                path_suffixes: vec!["rs".to_string()],
4035                ..Default::default()
4036            },
4037            ..Default::default()
4038        },
4039        Some(tree_sitter_rust::language()),
4040    );
4041    let mut fake_language_servers = language
4042        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4043            disk_based_diagnostics_progress_token: Some("the-disk-based-token".into()),
4044            disk_based_diagnostics_sources: vec!["the-disk-based-diagnostics-source".into()],
4045            ..Default::default()
4046        }))
4047        .await;
4048    client_a.language_registry().add(Arc::new(language));
4049
4050    let file_names = &["one.rs", "two.rs", "three.rs", "four.rs", "five.rs"];
4051    client_a
4052        .fs()
4053        .insert_tree(
4054            "/test",
4055            json!({
4056                "one.rs": "const ONE: usize = 1;",
4057                "two.rs": "const TWO: usize = 2;",
4058                "three.rs": "const THREE: usize = 3;",
4059                "four.rs": "const FOUR: usize = 3;",
4060                "five.rs": "const FIVE: usize = 3;",
4061            }),
4062        )
4063        .await;
4064
4065    let (project_a, worktree_id) = client_a.build_local_project("/test", cx_a).await;
4066
4067    // Share a project as client A
4068    let active_call_a = cx_a.read(ActiveCall::global);
4069    let project_id = active_call_a
4070        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4071        .await
4072        .unwrap();
4073
4074    // Join the project as client B and open all three files.
4075    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4076    let guest_buffers = futures::future::try_join_all(file_names.iter().map(|file_name| {
4077        project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, file_name), cx))
4078    }))
4079    .await
4080    .unwrap();
4081
4082    // Simulate a language server reporting errors for a file.
4083    let fake_language_server = fake_language_servers.next().await.unwrap();
4084    fake_language_server
4085        .request::<lsp::request::WorkDoneProgressCreate>(lsp::WorkDoneProgressCreateParams {
4086            token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4087        })
4088        .await
4089        .unwrap();
4090    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
4091        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4092        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin(
4093            lsp::WorkDoneProgressBegin {
4094                title: "Progress Began".into(),
4095                ..Default::default()
4096            },
4097        )),
4098    });
4099    for file_name in file_names {
4100        fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
4101            lsp::PublishDiagnosticsParams {
4102                uri: lsp::Url::from_file_path(Path::new("/test").join(file_name)).unwrap(),
4103                version: None,
4104                diagnostics: vec![lsp::Diagnostic {
4105                    severity: Some(lsp::DiagnosticSeverity::WARNING),
4106                    source: Some("the-disk-based-diagnostics-source".into()),
4107                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
4108                    message: "message one".to_string(),
4109                    ..Default::default()
4110                }],
4111            },
4112        );
4113    }
4114    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
4115        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4116        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End(
4117            lsp::WorkDoneProgressEnd { message: None },
4118        )),
4119    });
4120
4121    // When the "disk base diagnostics finished" message is received, the buffers'
4122    // diagnostics are expected to be present.
4123    let disk_based_diagnostics_finished = Arc::new(AtomicBool::new(false));
4124    project_b.update(cx_b, {
4125        let project_b = project_b.clone();
4126        let disk_based_diagnostics_finished = disk_based_diagnostics_finished.clone();
4127        move |_, cx| {
4128            cx.subscribe(&project_b, move |_, _, event, cx| {
4129                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
4130                    disk_based_diagnostics_finished.store(true, SeqCst);
4131                    for buffer in &guest_buffers {
4132                        assert_eq!(
4133                            buffer
4134                                .read(cx)
4135                                .snapshot()
4136                                .diagnostics_in_range::<_, usize>(0..5, false)
4137                                .count(),
4138                            1,
4139                            "expected a diagnostic for buffer {:?}",
4140                            buffer.read(cx).file().unwrap().path(),
4141                        );
4142                    }
4143                }
4144            })
4145            .detach();
4146        }
4147    });
4148
4149    executor.run_until_parked();
4150    assert!(disk_based_diagnostics_finished.load(SeqCst));
4151}
4152
4153#[gpui::test(iterations = 10)]
4154async fn test_reloading_buffer_manually(
4155    executor: BackgroundExecutor,
4156    cx_a: &mut TestAppContext,
4157    cx_b: &mut TestAppContext,
4158) {
4159    let mut server = TestServer::start(executor.clone()).await;
4160    let client_a = server.create_client(cx_a, "user_a").await;
4161    let client_b = server.create_client(cx_b, "user_b").await;
4162    server
4163        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4164        .await;
4165    let active_call_a = cx_a.read(ActiveCall::global);
4166
4167    client_a
4168        .fs()
4169        .insert_tree("/a", json!({ "a.rs": "let one = 1;" }))
4170        .await;
4171    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
4172    let buffer_a = project_a
4173        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
4174        .await
4175        .unwrap();
4176    let project_id = active_call_a
4177        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4178        .await
4179        .unwrap();
4180
4181    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4182
4183    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4184    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4185    buffer_b.update(cx_b, |buffer, cx| {
4186        buffer.edit([(4..7, "six")], None, cx);
4187        buffer.edit([(10..11, "6")], None, cx);
4188        assert_eq!(buffer.text(), "let six = 6;");
4189        assert!(buffer.is_dirty());
4190        assert!(!buffer.has_conflict());
4191    });
4192    executor.run_until_parked();
4193
4194    buffer_a.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "let six = 6;"));
4195
4196    client_a
4197        .fs()
4198        .save(
4199            "/a/a.rs".as_ref(),
4200            &Rope::from("let seven = 7;"),
4201            LineEnding::Unix,
4202        )
4203        .await
4204        .unwrap();
4205    executor.run_until_parked();
4206
4207    buffer_a.read_with(cx_a, |buffer, _| assert!(buffer.has_conflict()));
4208
4209    buffer_b.read_with(cx_b, |buffer, _| assert!(buffer.has_conflict()));
4210
4211    project_b
4212        .update(cx_b, |project, cx| {
4213            project.reload_buffers(HashSet::from_iter([buffer_b.clone()]), true, cx)
4214        })
4215        .await
4216        .unwrap();
4217
4218    buffer_a.read_with(cx_a, |buffer, _| {
4219        assert_eq!(buffer.text(), "let seven = 7;");
4220        assert!(!buffer.is_dirty());
4221        assert!(!buffer.has_conflict());
4222    });
4223
4224    buffer_b.read_with(cx_b, |buffer, _| {
4225        assert_eq!(buffer.text(), "let seven = 7;");
4226        assert!(!buffer.is_dirty());
4227        assert!(!buffer.has_conflict());
4228    });
4229
4230    buffer_a.update(cx_a, |buffer, cx| {
4231        // Undoing on the host is a no-op when the reload was initiated by the guest.
4232        buffer.undo(cx);
4233        assert_eq!(buffer.text(), "let seven = 7;");
4234        assert!(!buffer.is_dirty());
4235        assert!(!buffer.has_conflict());
4236    });
4237    buffer_b.update(cx_b, |buffer, cx| {
4238        // Undoing on the guest rolls back the buffer to before it was reloaded but the conflict gets cleared.
4239        buffer.undo(cx);
4240        assert_eq!(buffer.text(), "let six = 6;");
4241        assert!(buffer.is_dirty());
4242        assert!(!buffer.has_conflict());
4243    });
4244}
4245
4246#[gpui::test(iterations = 10)]
4247async fn test_formatting_buffer(
4248    executor: BackgroundExecutor,
4249    cx_a: &mut TestAppContext,
4250    cx_b: &mut TestAppContext,
4251) {
4252    executor.allow_parking();
4253    let mut server = TestServer::start(executor.clone()).await;
4254    let client_a = server.create_client(cx_a, "user_a").await;
4255    let client_b = server.create_client(cx_b, "user_b").await;
4256    server
4257        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4258        .await;
4259    let active_call_a = cx_a.read(ActiveCall::global);
4260
4261    // Set up a fake language server.
4262    let mut language = Language::new(
4263        LanguageConfig {
4264            name: "Rust".into(),
4265            matcher: LanguageMatcher {
4266                path_suffixes: vec!["rs".to_string()],
4267                ..Default::default()
4268            },
4269            ..Default::default()
4270        },
4271        Some(tree_sitter_rust::language()),
4272    );
4273    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4274    client_a.language_registry().add(Arc::new(language));
4275
4276    // Here we insert a fake tree with a directory that exists on disk. This is needed
4277    // because later we'll invoke a command, which requires passing a working directory
4278    // that points to a valid location on disk.
4279    let directory = env::current_dir().unwrap();
4280    client_a
4281        .fs()
4282        .insert_tree(&directory, json!({ "a.rs": "let one = \"two\"" }))
4283        .await;
4284    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
4285    let project_id = active_call_a
4286        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4287        .await
4288        .unwrap();
4289    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4290
4291    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4292    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4293
4294    let fake_language_server = fake_language_servers.next().await.unwrap();
4295    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
4296        Ok(Some(vec![
4297            lsp::TextEdit {
4298                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 4)),
4299                new_text: "h".to_string(),
4300            },
4301            lsp::TextEdit {
4302                range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 7)),
4303                new_text: "y".to_string(),
4304            },
4305        ]))
4306    });
4307
4308    project_b
4309        .update(cx_b, |project, cx| {
4310            project.format(
4311                HashSet::from_iter([buffer_b.clone()]),
4312                true,
4313                FormatTrigger::Save,
4314                cx,
4315            )
4316        })
4317        .await
4318        .unwrap();
4319
4320    // The edits from the LSP are applied, and a final newline is added.
4321    assert_eq!(
4322        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4323        "let honey = \"two\"\n"
4324    );
4325
4326    // Ensure buffer can be formatted using an external command. Notice how the
4327    // host's configuration is honored as opposed to using the guest's settings.
4328    cx_a.update(|cx| {
4329        cx.update_global(|store: &mut SettingsStore, cx| {
4330            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4331                file.defaults.formatter = Some(Formatter::External {
4332                    command: "awk".into(),
4333                    arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()].into(),
4334                });
4335            });
4336        });
4337    });
4338    project_b
4339        .update(cx_b, |project, cx| {
4340            project.format(
4341                HashSet::from_iter([buffer_b.clone()]),
4342                true,
4343                FormatTrigger::Save,
4344                cx,
4345            )
4346        })
4347        .await
4348        .unwrap();
4349    assert_eq!(
4350        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4351        format!("let honey = \"{}/a.rs\"\n", directory.to_str().unwrap())
4352    );
4353}
4354
4355#[gpui::test(iterations = 10)]
4356async fn test_prettier_formatting_buffer(
4357    executor: BackgroundExecutor,
4358    cx_a: &mut TestAppContext,
4359    cx_b: &mut TestAppContext,
4360) {
4361    let mut server = TestServer::start(executor.clone()).await;
4362    let client_a = server.create_client(cx_a, "user_a").await;
4363    let client_b = server.create_client(cx_b, "user_b").await;
4364    server
4365        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4366        .await;
4367    let active_call_a = cx_a.read(ActiveCall::global);
4368
4369    // Set up a fake language server.
4370    let mut language = Language::new(
4371        LanguageConfig {
4372            name: "Rust".into(),
4373            matcher: LanguageMatcher {
4374                path_suffixes: vec!["rs".to_string()],
4375                ..Default::default()
4376            },
4377            prettier_parser_name: Some("test_parser".to_string()),
4378            ..Default::default()
4379        },
4380        Some(tree_sitter_rust::language()),
4381    );
4382    let test_plugin = "test_plugin";
4383    let mut fake_language_servers = language
4384        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4385            prettier_plugins: vec![test_plugin],
4386            ..Default::default()
4387        }))
4388        .await;
4389    let language = Arc::new(language);
4390    client_a.language_registry().add(Arc::clone(&language));
4391
4392    // Here we insert a fake tree with a directory that exists on disk. This is needed
4393    // because later we'll invoke a command, which requires passing a working directory
4394    // that points to a valid location on disk.
4395    let directory = env::current_dir().unwrap();
4396    let buffer_text = "let one = \"two\"";
4397    client_a
4398        .fs()
4399        .insert_tree(&directory, json!({ "a.rs": buffer_text }))
4400        .await;
4401    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
4402    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
4403    let open_buffer = project_a.update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4404    let buffer_a = cx_a.executor().spawn(open_buffer).await.unwrap();
4405
4406    let project_id = active_call_a
4407        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4408        .await
4409        .unwrap();
4410    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4411    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4412    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4413
4414    cx_a.update(|cx| {
4415        cx.update_global(|store: &mut SettingsStore, cx| {
4416            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4417                file.defaults.formatter = Some(Formatter::Auto);
4418            });
4419        });
4420    });
4421    cx_b.update(|cx| {
4422        cx.update_global(|store: &mut SettingsStore, cx| {
4423            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4424                file.defaults.formatter = Some(Formatter::LanguageServer);
4425            });
4426        });
4427    });
4428    let fake_language_server = fake_language_servers.next().await.unwrap();
4429    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
4430        panic!(
4431            "Unexpected: prettier should be preferred since it's enabled and language supports it"
4432        )
4433    });
4434
4435    project_b
4436        .update(cx_b, |project, cx| {
4437            project.format(
4438                HashSet::from_iter([buffer_b.clone()]),
4439                true,
4440                FormatTrigger::Save,
4441                cx,
4442            )
4443        })
4444        .await
4445        .unwrap();
4446
4447    executor.run_until_parked();
4448    assert_eq!(
4449        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4450        buffer_text.to_string() + "\n" + prettier_format_suffix,
4451        "Prettier formatting was not applied to client buffer after client's request"
4452    );
4453
4454    project_a
4455        .update(cx_a, |project, cx| {
4456            project.format(
4457                HashSet::from_iter([buffer_a.clone()]),
4458                true,
4459                FormatTrigger::Manual,
4460                cx,
4461            )
4462        })
4463        .await
4464        .unwrap();
4465
4466    executor.run_until_parked();
4467    assert_eq!(
4468        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4469        buffer_text.to_string() + "\n" + prettier_format_suffix + "\n" + prettier_format_suffix,
4470        "Prettier formatting was not applied to client buffer after host's request"
4471    );
4472}
4473
4474#[gpui::test(iterations = 10)]
4475async fn test_definition(
4476    executor: BackgroundExecutor,
4477    cx_a: &mut TestAppContext,
4478    cx_b: &mut TestAppContext,
4479) {
4480    let mut server = TestServer::start(executor.clone()).await;
4481    let client_a = server.create_client(cx_a, "user_a").await;
4482    let client_b = server.create_client(cx_b, "user_b").await;
4483    server
4484        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4485        .await;
4486    let active_call_a = cx_a.read(ActiveCall::global);
4487
4488    // Set up a fake language server.
4489    let mut language = Language::new(
4490        LanguageConfig {
4491            name: "Rust".into(),
4492            matcher: LanguageMatcher {
4493                path_suffixes: vec!["rs".to_string()],
4494                ..Default::default()
4495            },
4496            ..Default::default()
4497        },
4498        Some(tree_sitter_rust::language()),
4499    );
4500    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4501    client_a.language_registry().add(Arc::new(language));
4502
4503    client_a
4504        .fs()
4505        .insert_tree(
4506            "/root",
4507            json!({
4508                "dir-1": {
4509                    "a.rs": "const ONE: usize = b::TWO + b::THREE;",
4510                },
4511                "dir-2": {
4512                    "b.rs": "const TWO: c::T2 = 2;\nconst THREE: usize = 3;",
4513                    "c.rs": "type T2 = usize;",
4514                }
4515            }),
4516        )
4517        .await;
4518    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4519    let project_id = active_call_a
4520        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4521        .await
4522        .unwrap();
4523    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4524
4525    // Open the file on client B.
4526    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4527    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4528
4529    // Request the definition of a symbol as the guest.
4530    let fake_language_server = fake_language_servers.next().await.unwrap();
4531    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4532        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4533            lsp::Location::new(
4534                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4535                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4536            ),
4537        )))
4538    });
4539
4540    let definitions_1 = project_b
4541        .update(cx_b, |p, cx| p.definition(&buffer_b, 23, cx))
4542        .await
4543        .unwrap();
4544    cx_b.read(|cx| {
4545        assert_eq!(definitions_1.len(), 1);
4546        assert_eq!(project_b.read(cx).worktrees().count(), 2);
4547        let target_buffer = definitions_1[0].target.buffer.read(cx);
4548        assert_eq!(
4549            target_buffer.text(),
4550            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4551        );
4552        assert_eq!(
4553            definitions_1[0].target.range.to_point(target_buffer),
4554            Point::new(0, 6)..Point::new(0, 9)
4555        );
4556    });
4557
4558    // Try getting more definitions for the same buffer, ensuring the buffer gets reused from
4559    // the previous call to `definition`.
4560    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4561        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4562            lsp::Location::new(
4563                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4564                lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)),
4565            ),
4566        )))
4567    });
4568
4569    let definitions_2 = project_b
4570        .update(cx_b, |p, cx| p.definition(&buffer_b, 33, cx))
4571        .await
4572        .unwrap();
4573    cx_b.read(|cx| {
4574        assert_eq!(definitions_2.len(), 1);
4575        assert_eq!(project_b.read(cx).worktrees().count(), 2);
4576        let target_buffer = definitions_2[0].target.buffer.read(cx);
4577        assert_eq!(
4578            target_buffer.text(),
4579            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4580        );
4581        assert_eq!(
4582            definitions_2[0].target.range.to_point(target_buffer),
4583            Point::new(1, 6)..Point::new(1, 11)
4584        );
4585    });
4586    assert_eq!(
4587        definitions_1[0].target.buffer,
4588        definitions_2[0].target.buffer
4589    );
4590
4591    fake_language_server.handle_request::<lsp::request::GotoTypeDefinition, _, _>(
4592        |req, _| async move {
4593            assert_eq!(
4594                req.text_document_position_params.position,
4595                lsp::Position::new(0, 7)
4596            );
4597            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4598                lsp::Location::new(
4599                    lsp::Url::from_file_path("/root/dir-2/c.rs").unwrap(),
4600                    lsp::Range::new(lsp::Position::new(0, 5), lsp::Position::new(0, 7)),
4601                ),
4602            )))
4603        },
4604    );
4605
4606    let type_definitions = project_b
4607        .update(cx_b, |p, cx| p.type_definition(&buffer_b, 7, cx))
4608        .await
4609        .unwrap();
4610    cx_b.read(|cx| {
4611        assert_eq!(type_definitions.len(), 1);
4612        let target_buffer = type_definitions[0].target.buffer.read(cx);
4613        assert_eq!(target_buffer.text(), "type T2 = usize;");
4614        assert_eq!(
4615            type_definitions[0].target.range.to_point(target_buffer),
4616            Point::new(0, 5)..Point::new(0, 7)
4617        );
4618    });
4619}
4620
4621#[gpui::test(iterations = 10)]
4622async fn test_references(
4623    executor: BackgroundExecutor,
4624    cx_a: &mut TestAppContext,
4625    cx_b: &mut TestAppContext,
4626) {
4627    let mut server = TestServer::start(executor.clone()).await;
4628    let client_a = server.create_client(cx_a, "user_a").await;
4629    let client_b = server.create_client(cx_b, "user_b").await;
4630    server
4631        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4632        .await;
4633    let active_call_a = cx_a.read(ActiveCall::global);
4634
4635    // Set up a fake language server.
4636    let mut language = Language::new(
4637        LanguageConfig {
4638            name: "Rust".into(),
4639            matcher: LanguageMatcher {
4640                path_suffixes: vec!["rs".to_string()],
4641                ..Default::default()
4642            },
4643            ..Default::default()
4644        },
4645        Some(tree_sitter_rust::language()),
4646    );
4647    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4648    client_a.language_registry().add(Arc::new(language));
4649
4650    client_a
4651        .fs()
4652        .insert_tree(
4653            "/root",
4654            json!({
4655                "dir-1": {
4656                    "one.rs": "const ONE: usize = 1;",
4657                    "two.rs": "const TWO: usize = one::ONE + one::ONE;",
4658                },
4659                "dir-2": {
4660                    "three.rs": "const THREE: usize = two::TWO + one::ONE;",
4661                }
4662            }),
4663        )
4664        .await;
4665    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4666    let project_id = active_call_a
4667        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4668        .await
4669        .unwrap();
4670    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4671
4672    // Open the file on client B.
4673    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx));
4674    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4675
4676    // Request references to a symbol as the guest.
4677    let fake_language_server = fake_language_servers.next().await.unwrap();
4678    fake_language_server.handle_request::<lsp::request::References, _, _>(|params, _| async move {
4679        assert_eq!(
4680            params.text_document_position.text_document.uri.as_str(),
4681            "file:///root/dir-1/one.rs"
4682        );
4683        Ok(Some(vec![
4684            lsp::Location {
4685                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4686                range: lsp::Range::new(lsp::Position::new(0, 24), lsp::Position::new(0, 27)),
4687            },
4688            lsp::Location {
4689                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4690                range: lsp::Range::new(lsp::Position::new(0, 35), lsp::Position::new(0, 38)),
4691            },
4692            lsp::Location {
4693                uri: lsp::Url::from_file_path("/root/dir-2/three.rs").unwrap(),
4694                range: lsp::Range::new(lsp::Position::new(0, 37), lsp::Position::new(0, 40)),
4695            },
4696        ]))
4697    });
4698
4699    let references = project_b
4700        .update(cx_b, |p, cx| p.references(&buffer_b, 7, cx))
4701        .await
4702        .unwrap();
4703    cx_b.read(|cx| {
4704        assert_eq!(references.len(), 3);
4705        assert_eq!(project_b.read(cx).worktrees().count(), 2);
4706
4707        let two_buffer = references[0].buffer.read(cx);
4708        let three_buffer = references[2].buffer.read(cx);
4709        assert_eq!(
4710            two_buffer.file().unwrap().path().as_ref(),
4711            Path::new("two.rs")
4712        );
4713        assert_eq!(references[1].buffer, references[0].buffer);
4714        assert_eq!(
4715            three_buffer.file().unwrap().full_path(cx),
4716            Path::new("/root/dir-2/three.rs")
4717        );
4718
4719        assert_eq!(references[0].range.to_offset(two_buffer), 24..27);
4720        assert_eq!(references[1].range.to_offset(two_buffer), 35..38);
4721        assert_eq!(references[2].range.to_offset(three_buffer), 37..40);
4722    });
4723}
4724
4725#[gpui::test(iterations = 10)]
4726async fn test_project_search(
4727    executor: BackgroundExecutor,
4728    cx_a: &mut TestAppContext,
4729    cx_b: &mut TestAppContext,
4730) {
4731    let mut server = TestServer::start(executor.clone()).await;
4732    let client_a = server.create_client(cx_a, "user_a").await;
4733    let client_b = server.create_client(cx_b, "user_b").await;
4734    server
4735        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4736        .await;
4737    let active_call_a = cx_a.read(ActiveCall::global);
4738
4739    client_a
4740        .fs()
4741        .insert_tree(
4742            "/root",
4743            json!({
4744                "dir-1": {
4745                    "a": "hello world",
4746                    "b": "goodnight moon",
4747                    "c": "a world of goo",
4748                    "d": "world champion of clown world",
4749                },
4750                "dir-2": {
4751                    "e": "disney world is fun",
4752                }
4753            }),
4754        )
4755        .await;
4756    let (project_a, _) = client_a.build_local_project("/root/dir-1", cx_a).await;
4757    let (worktree_2, _) = project_a
4758        .update(cx_a, |p, cx| {
4759            p.find_or_create_local_worktree("/root/dir-2", true, cx)
4760        })
4761        .await
4762        .unwrap();
4763    worktree_2
4764        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
4765        .await;
4766    let project_id = active_call_a
4767        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4768        .await
4769        .unwrap();
4770
4771    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4772
4773    // Perform a search as the guest.
4774    let mut results = HashMap::default();
4775    let mut search_rx = project_b.update(cx_b, |project, cx| {
4776        project.search(
4777            SearchQuery::text("world", false, false, false, Vec::new(), Vec::new()).unwrap(),
4778            cx,
4779        )
4780    });
4781    while let Some((buffer, ranges)) = search_rx.next().await {
4782        results.entry(buffer).or_insert(ranges);
4783    }
4784
4785    let mut ranges_by_path = results
4786        .into_iter()
4787        .map(|(buffer, ranges)| {
4788            buffer.read_with(cx_b, |buffer, cx| {
4789                let path = buffer.file().unwrap().full_path(cx);
4790                let offset_ranges = ranges
4791                    .into_iter()
4792                    .map(|range| range.to_offset(buffer))
4793                    .collect::<Vec<_>>();
4794                (path, offset_ranges)
4795            })
4796        })
4797        .collect::<Vec<_>>();
4798    ranges_by_path.sort_by_key(|(path, _)| path.clone());
4799
4800    assert_eq!(
4801        ranges_by_path,
4802        &[
4803            (PathBuf::from("dir-1/a"), vec![6..11]),
4804            (PathBuf::from("dir-1/c"), vec![2..7]),
4805            (PathBuf::from("dir-1/d"), vec![0..5, 24..29]),
4806            (PathBuf::from("dir-2/e"), vec![7..12]),
4807        ]
4808    );
4809}
4810
4811#[gpui::test(iterations = 10)]
4812async fn test_document_highlights(
4813    executor: BackgroundExecutor,
4814    cx_a: &mut TestAppContext,
4815    cx_b: &mut TestAppContext,
4816) {
4817    let mut server = TestServer::start(executor.clone()).await;
4818    let client_a = server.create_client(cx_a, "user_a").await;
4819    let client_b = server.create_client(cx_b, "user_b").await;
4820    server
4821        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4822        .await;
4823    let active_call_a = cx_a.read(ActiveCall::global);
4824
4825    client_a
4826        .fs()
4827        .insert_tree(
4828            "/root-1",
4829            json!({
4830                "main.rs": "fn double(number: i32) -> i32 { number + number }",
4831            }),
4832        )
4833        .await;
4834
4835    // Set up a fake language server.
4836    let mut language = Language::new(
4837        LanguageConfig {
4838            name: "Rust".into(),
4839            matcher: LanguageMatcher {
4840                path_suffixes: vec!["rs".to_string()],
4841                ..Default::default()
4842            },
4843            ..Default::default()
4844        },
4845        Some(tree_sitter_rust::language()),
4846    );
4847    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4848    client_a.language_registry().add(Arc::new(language));
4849
4850    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4851    let project_id = active_call_a
4852        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4853        .await
4854        .unwrap();
4855    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4856
4857    // Open the file on client B.
4858    let open_b = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx));
4859    let buffer_b = cx_b.executor().spawn(open_b).await.unwrap();
4860
4861    // Request document highlights as the guest.
4862    let fake_language_server = fake_language_servers.next().await.unwrap();
4863    fake_language_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
4864        |params, _| async move {
4865            assert_eq!(
4866                params
4867                    .text_document_position_params
4868                    .text_document
4869                    .uri
4870                    .as_str(),
4871                "file:///root-1/main.rs"
4872            );
4873            assert_eq!(
4874                params.text_document_position_params.position,
4875                lsp::Position::new(0, 34)
4876            );
4877            Ok(Some(vec![
4878                lsp::DocumentHighlight {
4879                    kind: Some(lsp::DocumentHighlightKind::WRITE),
4880                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 16)),
4881                },
4882                lsp::DocumentHighlight {
4883                    kind: Some(lsp::DocumentHighlightKind::READ),
4884                    range: lsp::Range::new(lsp::Position::new(0, 32), lsp::Position::new(0, 38)),
4885                },
4886                lsp::DocumentHighlight {
4887                    kind: Some(lsp::DocumentHighlightKind::READ),
4888                    range: lsp::Range::new(lsp::Position::new(0, 41), lsp::Position::new(0, 47)),
4889                },
4890            ]))
4891        },
4892    );
4893
4894    let highlights = project_b
4895        .update(cx_b, |p, cx| p.document_highlights(&buffer_b, 34, cx))
4896        .await
4897        .unwrap();
4898
4899    buffer_b.read_with(cx_b, |buffer, _| {
4900        let snapshot = buffer.snapshot();
4901
4902        let highlights = highlights
4903            .into_iter()
4904            .map(|highlight| (highlight.kind, highlight.range.to_offset(&snapshot)))
4905            .collect::<Vec<_>>();
4906        assert_eq!(
4907            highlights,
4908            &[
4909                (lsp::DocumentHighlightKind::WRITE, 10..16),
4910                (lsp::DocumentHighlightKind::READ, 32..38),
4911                (lsp::DocumentHighlightKind::READ, 41..47)
4912            ]
4913        )
4914    });
4915}
4916
4917#[gpui::test(iterations = 10)]
4918async fn test_lsp_hover(
4919    executor: BackgroundExecutor,
4920    cx_a: &mut TestAppContext,
4921    cx_b: &mut TestAppContext,
4922) {
4923    let mut server = TestServer::start(executor.clone()).await;
4924    let client_a = server.create_client(cx_a, "user_a").await;
4925    let client_b = server.create_client(cx_b, "user_b").await;
4926    server
4927        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4928        .await;
4929    let active_call_a = cx_a.read(ActiveCall::global);
4930
4931    client_a
4932        .fs()
4933        .insert_tree(
4934            "/root-1",
4935            json!({
4936                "main.rs": "use std::collections::HashMap;",
4937            }),
4938        )
4939        .await;
4940
4941    // Set up a fake language server.
4942    let mut language = Language::new(
4943        LanguageConfig {
4944            name: "Rust".into(),
4945            matcher: LanguageMatcher {
4946                path_suffixes: vec!["rs".to_string()],
4947                ..Default::default()
4948            },
4949            ..Default::default()
4950        },
4951        Some(tree_sitter_rust::language()),
4952    );
4953    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4954    client_a.language_registry().add(Arc::new(language));
4955
4956    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4957    let project_id = active_call_a
4958        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4959        .await
4960        .unwrap();
4961    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4962
4963    // Open the file as the guest
4964    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx));
4965    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4966
4967    // Request hover information as the guest.
4968    let fake_language_server = fake_language_servers.next().await.unwrap();
4969    fake_language_server.handle_request::<lsp::request::HoverRequest, _, _>(
4970        |params, _| async move {
4971            assert_eq!(
4972                params
4973                    .text_document_position_params
4974                    .text_document
4975                    .uri
4976                    .as_str(),
4977                "file:///root-1/main.rs"
4978            );
4979            assert_eq!(
4980                params.text_document_position_params.position,
4981                lsp::Position::new(0, 22)
4982            );
4983            Ok(Some(lsp::Hover {
4984                contents: lsp::HoverContents::Array(vec![
4985                    lsp::MarkedString::String("Test hover content.".to_string()),
4986                    lsp::MarkedString::LanguageString(lsp::LanguageString {
4987                        language: "Rust".to_string(),
4988                        value: "let foo = 42;".to_string(),
4989                    }),
4990                ]),
4991                range: Some(lsp::Range::new(
4992                    lsp::Position::new(0, 22),
4993                    lsp::Position::new(0, 29),
4994                )),
4995            }))
4996        },
4997    );
4998
4999    let hover_info = project_b
5000        .update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
5001        .await
5002        .unwrap()
5003        .unwrap();
5004
5005    buffer_b.read_with(cx_b, |buffer, _| {
5006        let snapshot = buffer.snapshot();
5007        assert_eq!(hover_info.range.unwrap().to_offset(&snapshot), 22..29);
5008        assert_eq!(
5009            hover_info.contents,
5010            vec![
5011                project::HoverBlock {
5012                    text: "Test hover content.".to_string(),
5013                    kind: HoverBlockKind::Markdown,
5014                },
5015                project::HoverBlock {
5016                    text: "let foo = 42;".to_string(),
5017                    kind: HoverBlockKind::Code {
5018                        language: "Rust".to_string()
5019                    },
5020                }
5021            ]
5022        );
5023    });
5024}
5025
5026#[gpui::test(iterations = 10)]
5027async fn test_project_symbols(
5028    executor: BackgroundExecutor,
5029    cx_a: &mut TestAppContext,
5030    cx_b: &mut TestAppContext,
5031) {
5032    let mut server = TestServer::start(executor.clone()).await;
5033    let client_a = server.create_client(cx_a, "user_a").await;
5034    let client_b = server.create_client(cx_b, "user_b").await;
5035    server
5036        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5037        .await;
5038    let active_call_a = cx_a.read(ActiveCall::global);
5039
5040    // Set up a fake language server.
5041    let mut language = Language::new(
5042        LanguageConfig {
5043            name: "Rust".into(),
5044            matcher: LanguageMatcher {
5045                path_suffixes: vec!["rs".to_string()],
5046                ..Default::default()
5047            },
5048            ..Default::default()
5049        },
5050        Some(tree_sitter_rust::language()),
5051    );
5052    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5053    client_a.language_registry().add(Arc::new(language));
5054
5055    client_a
5056        .fs()
5057        .insert_tree(
5058            "/code",
5059            json!({
5060                "crate-1": {
5061                    "one.rs": "const ONE: usize = 1;",
5062                },
5063                "crate-2": {
5064                    "two.rs": "const TWO: usize = 2; const THREE: usize = 3;",
5065                },
5066                "private": {
5067                    "passwords.txt": "the-password",
5068                }
5069            }),
5070        )
5071        .await;
5072    let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await;
5073    let project_id = active_call_a
5074        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5075        .await
5076        .unwrap();
5077    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5078
5079    // Cause the language server to start.
5080    let open_buffer_task =
5081        project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx));
5082    let _buffer = cx_b.executor().spawn(open_buffer_task).await.unwrap();
5083
5084    let fake_language_server = fake_language_servers.next().await.unwrap();
5085    fake_language_server.handle_request::<lsp::WorkspaceSymbolRequest, _, _>(|_, _| async move {
5086        Ok(Some(lsp::WorkspaceSymbolResponse::Flat(vec![
5087            #[allow(deprecated)]
5088            lsp::SymbolInformation {
5089                name: "TWO".into(),
5090                location: lsp::Location {
5091                    uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
5092                    range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5093                },
5094                kind: lsp::SymbolKind::CONSTANT,
5095                tags: None,
5096                container_name: None,
5097                deprecated: None,
5098            },
5099        ])))
5100    });
5101
5102    // Request the definition of a symbol as the guest.
5103    let symbols = project_b
5104        .update(cx_b, |p, cx| p.symbols("two", cx))
5105        .await
5106        .unwrap();
5107    assert_eq!(symbols.len(), 1);
5108    assert_eq!(symbols[0].name, "TWO");
5109
5110    // Open one of the returned symbols.
5111    let buffer_b_2 = project_b
5112        .update(cx_b, |project, cx| {
5113            project.open_buffer_for_symbol(&symbols[0], cx)
5114        })
5115        .await
5116        .unwrap();
5117
5118    buffer_b_2.read_with(cx_b, |buffer, cx| {
5119        assert_eq!(
5120            buffer.file().unwrap().full_path(cx),
5121            Path::new("/code/crate-2/two.rs")
5122        );
5123    });
5124
5125    // Attempt to craft a symbol and violate host's privacy by opening an arbitrary file.
5126    let mut fake_symbol = symbols[0].clone();
5127    fake_symbol.path.path = Path::new("/code/secrets").into();
5128    let error = project_b
5129        .update(cx_b, |project, cx| {
5130            project.open_buffer_for_symbol(&fake_symbol, cx)
5131        })
5132        .await
5133        .unwrap_err();
5134    assert!(error.to_string().contains("invalid symbol signature"));
5135}
5136
5137#[gpui::test(iterations = 10)]
5138async fn test_open_buffer_while_getting_definition_pointing_to_it(
5139    executor: BackgroundExecutor,
5140    cx_a: &mut TestAppContext,
5141    cx_b: &mut TestAppContext,
5142    mut rng: StdRng,
5143) {
5144    let mut server = TestServer::start(executor.clone()).await;
5145    let client_a = server.create_client(cx_a, "user_a").await;
5146    let client_b = server.create_client(cx_b, "user_b").await;
5147    server
5148        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5149        .await;
5150    let active_call_a = cx_a.read(ActiveCall::global);
5151
5152    // Set up a fake language server.
5153    let mut language = Language::new(
5154        LanguageConfig {
5155            name: "Rust".into(),
5156            matcher: LanguageMatcher {
5157                path_suffixes: vec!["rs".to_string()],
5158                ..Default::default()
5159            },
5160            ..Default::default()
5161        },
5162        Some(tree_sitter_rust::language()),
5163    );
5164    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5165    client_a.language_registry().add(Arc::new(language));
5166
5167    client_a
5168        .fs()
5169        .insert_tree(
5170            "/root",
5171            json!({
5172                "a.rs": "const ONE: usize = b::TWO;",
5173                "b.rs": "const TWO: usize = 2",
5174            }),
5175        )
5176        .await;
5177    let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await;
5178    let project_id = active_call_a
5179        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5180        .await
5181        .unwrap();
5182    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5183
5184    let open_buffer_task = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
5185    let buffer_b1 = cx_b.executor().spawn(open_buffer_task).await.unwrap();
5186
5187    let fake_language_server = fake_language_servers.next().await.unwrap();
5188    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
5189        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
5190            lsp::Location::new(
5191                lsp::Url::from_file_path("/root/b.rs").unwrap(),
5192                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5193            ),
5194        )))
5195    });
5196
5197    let definitions;
5198    let buffer_b2;
5199    if rng.gen() {
5200        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5201        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5202    } else {
5203        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5204        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5205    }
5206
5207    let buffer_b2 = buffer_b2.await.unwrap();
5208    let definitions = definitions.await.unwrap();
5209    assert_eq!(definitions.len(), 1);
5210    assert_eq!(definitions[0].target.buffer, buffer_b2);
5211}
5212
5213#[gpui::test(iterations = 10)]
5214async fn test_contacts(
5215    executor: BackgroundExecutor,
5216    cx_a: &mut TestAppContext,
5217    cx_b: &mut TestAppContext,
5218    cx_c: &mut TestAppContext,
5219    cx_d: &mut TestAppContext,
5220) {
5221    let mut server = TestServer::start(executor.clone()).await;
5222    let client_a = server.create_client(cx_a, "user_a").await;
5223    let client_b = server.create_client(cx_b, "user_b").await;
5224    let client_c = server.create_client(cx_c, "user_c").await;
5225    let client_d = server.create_client(cx_d, "user_d").await;
5226    server
5227        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
5228        .await;
5229    let active_call_a = cx_a.read(ActiveCall::global);
5230    let active_call_b = cx_b.read(ActiveCall::global);
5231    let active_call_c = cx_c.read(ActiveCall::global);
5232    let _active_call_d = cx_d.read(ActiveCall::global);
5233
5234    executor.run_until_parked();
5235    assert_eq!(
5236        contacts(&client_a, cx_a),
5237        [
5238            ("user_b".to_string(), "online", "free"),
5239            ("user_c".to_string(), "online", "free")
5240        ]
5241    );
5242    assert_eq!(
5243        contacts(&client_b, cx_b),
5244        [
5245            ("user_a".to_string(), "online", "free"),
5246            ("user_c".to_string(), "online", "free")
5247        ]
5248    );
5249    assert_eq!(
5250        contacts(&client_c, cx_c),
5251        [
5252            ("user_a".to_string(), "online", "free"),
5253            ("user_b".to_string(), "online", "free")
5254        ]
5255    );
5256    assert_eq!(contacts(&client_d, cx_d), []);
5257
5258    server.disconnect_client(client_c.peer_id().unwrap());
5259    server.forbid_connections();
5260    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5261    assert_eq!(
5262        contacts(&client_a, cx_a),
5263        [
5264            ("user_b".to_string(), "online", "free"),
5265            ("user_c".to_string(), "offline", "free")
5266        ]
5267    );
5268    assert_eq!(
5269        contacts(&client_b, cx_b),
5270        [
5271            ("user_a".to_string(), "online", "free"),
5272            ("user_c".to_string(), "offline", "free")
5273        ]
5274    );
5275    assert_eq!(contacts(&client_c, cx_c), []);
5276    assert_eq!(contacts(&client_d, cx_d), []);
5277
5278    server.allow_connections();
5279    client_c
5280        .authenticate_and_connect(false, &cx_c.to_async())
5281        .await
5282        .unwrap();
5283
5284    executor.run_until_parked();
5285    assert_eq!(
5286        contacts(&client_a, cx_a),
5287        [
5288            ("user_b".to_string(), "online", "free"),
5289            ("user_c".to_string(), "online", "free")
5290        ]
5291    );
5292    assert_eq!(
5293        contacts(&client_b, cx_b),
5294        [
5295            ("user_a".to_string(), "online", "free"),
5296            ("user_c".to_string(), "online", "free")
5297        ]
5298    );
5299    assert_eq!(
5300        contacts(&client_c, cx_c),
5301        [
5302            ("user_a".to_string(), "online", "free"),
5303            ("user_b".to_string(), "online", "free")
5304        ]
5305    );
5306    assert_eq!(contacts(&client_d, cx_d), []);
5307
5308    active_call_a
5309        .update(cx_a, |call, cx| {
5310            call.invite(client_b.user_id().unwrap(), None, cx)
5311        })
5312        .await
5313        .unwrap();
5314    executor.run_until_parked();
5315    assert_eq!(
5316        contacts(&client_a, cx_a),
5317        [
5318            ("user_b".to_string(), "online", "busy"),
5319            ("user_c".to_string(), "online", "free")
5320        ]
5321    );
5322    assert_eq!(
5323        contacts(&client_b, cx_b),
5324        [
5325            ("user_a".to_string(), "online", "busy"),
5326            ("user_c".to_string(), "online", "free")
5327        ]
5328    );
5329    assert_eq!(
5330        contacts(&client_c, cx_c),
5331        [
5332            ("user_a".to_string(), "online", "busy"),
5333            ("user_b".to_string(), "online", "busy")
5334        ]
5335    );
5336    assert_eq!(contacts(&client_d, cx_d), []);
5337
5338    // Client B and client D become contacts while client B is being called.
5339    server
5340        .make_contacts(&mut [(&client_b, cx_b), (&client_d, cx_d)])
5341        .await;
5342    executor.run_until_parked();
5343    assert_eq!(
5344        contacts(&client_a, cx_a),
5345        [
5346            ("user_b".to_string(), "online", "busy"),
5347            ("user_c".to_string(), "online", "free")
5348        ]
5349    );
5350    assert_eq!(
5351        contacts(&client_b, cx_b),
5352        [
5353            ("user_a".to_string(), "online", "busy"),
5354            ("user_c".to_string(), "online", "free"),
5355            ("user_d".to_string(), "online", "free"),
5356        ]
5357    );
5358    assert_eq!(
5359        contacts(&client_c, cx_c),
5360        [
5361            ("user_a".to_string(), "online", "busy"),
5362            ("user_b".to_string(), "online", "busy")
5363        ]
5364    );
5365    assert_eq!(
5366        contacts(&client_d, cx_d),
5367        [("user_b".to_string(), "online", "busy")]
5368    );
5369
5370    active_call_b.update(cx_b, |call, cx| call.decline_incoming(cx).unwrap());
5371    executor.run_until_parked();
5372    assert_eq!(
5373        contacts(&client_a, cx_a),
5374        [
5375            ("user_b".to_string(), "online", "free"),
5376            ("user_c".to_string(), "online", "free")
5377        ]
5378    );
5379    assert_eq!(
5380        contacts(&client_b, cx_b),
5381        [
5382            ("user_a".to_string(), "online", "free"),
5383            ("user_c".to_string(), "online", "free"),
5384            ("user_d".to_string(), "online", "free")
5385        ]
5386    );
5387    assert_eq!(
5388        contacts(&client_c, cx_c),
5389        [
5390            ("user_a".to_string(), "online", "free"),
5391            ("user_b".to_string(), "online", "free")
5392        ]
5393    );
5394    assert_eq!(
5395        contacts(&client_d, cx_d),
5396        [("user_b".to_string(), "online", "free")]
5397    );
5398
5399    active_call_c
5400        .update(cx_c, |call, cx| {
5401            call.invite(client_a.user_id().unwrap(), None, cx)
5402        })
5403        .await
5404        .unwrap();
5405    executor.run_until_parked();
5406    assert_eq!(
5407        contacts(&client_a, cx_a),
5408        [
5409            ("user_b".to_string(), "online", "free"),
5410            ("user_c".to_string(), "online", "busy")
5411        ]
5412    );
5413    assert_eq!(
5414        contacts(&client_b, cx_b),
5415        [
5416            ("user_a".to_string(), "online", "busy"),
5417            ("user_c".to_string(), "online", "busy"),
5418            ("user_d".to_string(), "online", "free")
5419        ]
5420    );
5421    assert_eq!(
5422        contacts(&client_c, cx_c),
5423        [
5424            ("user_a".to_string(), "online", "busy"),
5425            ("user_b".to_string(), "online", "free")
5426        ]
5427    );
5428    assert_eq!(
5429        contacts(&client_d, cx_d),
5430        [("user_b".to_string(), "online", "free")]
5431    );
5432
5433    active_call_a
5434        .update(cx_a, |call, cx| call.accept_incoming(cx))
5435        .await
5436        .unwrap();
5437    executor.run_until_parked();
5438    assert_eq!(
5439        contacts(&client_a, cx_a),
5440        [
5441            ("user_b".to_string(), "online", "free"),
5442            ("user_c".to_string(), "online", "busy")
5443        ]
5444    );
5445    assert_eq!(
5446        contacts(&client_b, cx_b),
5447        [
5448            ("user_a".to_string(), "online", "busy"),
5449            ("user_c".to_string(), "online", "busy"),
5450            ("user_d".to_string(), "online", "free")
5451        ]
5452    );
5453    assert_eq!(
5454        contacts(&client_c, cx_c),
5455        [
5456            ("user_a".to_string(), "online", "busy"),
5457            ("user_b".to_string(), "online", "free")
5458        ]
5459    );
5460    assert_eq!(
5461        contacts(&client_d, cx_d),
5462        [("user_b".to_string(), "online", "free")]
5463    );
5464
5465    active_call_a
5466        .update(cx_a, |call, cx| {
5467            call.invite(client_b.user_id().unwrap(), None, cx)
5468        })
5469        .await
5470        .unwrap();
5471    executor.run_until_parked();
5472    assert_eq!(
5473        contacts(&client_a, cx_a),
5474        [
5475            ("user_b".to_string(), "online", "busy"),
5476            ("user_c".to_string(), "online", "busy")
5477        ]
5478    );
5479    assert_eq!(
5480        contacts(&client_b, cx_b),
5481        [
5482            ("user_a".to_string(), "online", "busy"),
5483            ("user_c".to_string(), "online", "busy"),
5484            ("user_d".to_string(), "online", "free")
5485        ]
5486    );
5487    assert_eq!(
5488        contacts(&client_c, cx_c),
5489        [
5490            ("user_a".to_string(), "online", "busy"),
5491            ("user_b".to_string(), "online", "busy")
5492        ]
5493    );
5494    assert_eq!(
5495        contacts(&client_d, cx_d),
5496        [("user_b".to_string(), "online", "busy")]
5497    );
5498
5499    active_call_a
5500        .update(cx_a, |call, cx| call.hang_up(cx))
5501        .await
5502        .unwrap();
5503    executor.run_until_parked();
5504    assert_eq!(
5505        contacts(&client_a, cx_a),
5506        [
5507            ("user_b".to_string(), "online", "free"),
5508            ("user_c".to_string(), "online", "free")
5509        ]
5510    );
5511    assert_eq!(
5512        contacts(&client_b, cx_b),
5513        [
5514            ("user_a".to_string(), "online", "free"),
5515            ("user_c".to_string(), "online", "free"),
5516            ("user_d".to_string(), "online", "free")
5517        ]
5518    );
5519    assert_eq!(
5520        contacts(&client_c, cx_c),
5521        [
5522            ("user_a".to_string(), "online", "free"),
5523            ("user_b".to_string(), "online", "free")
5524        ]
5525    );
5526    assert_eq!(
5527        contacts(&client_d, cx_d),
5528        [("user_b".to_string(), "online", "free")]
5529    );
5530
5531    active_call_a
5532        .update(cx_a, |call, cx| {
5533            call.invite(client_b.user_id().unwrap(), None, cx)
5534        })
5535        .await
5536        .unwrap();
5537    executor.run_until_parked();
5538    assert_eq!(
5539        contacts(&client_a, cx_a),
5540        [
5541            ("user_b".to_string(), "online", "busy"),
5542            ("user_c".to_string(), "online", "free")
5543        ]
5544    );
5545    assert_eq!(
5546        contacts(&client_b, cx_b),
5547        [
5548            ("user_a".to_string(), "online", "busy"),
5549            ("user_c".to_string(), "online", "free"),
5550            ("user_d".to_string(), "online", "free")
5551        ]
5552    );
5553    assert_eq!(
5554        contacts(&client_c, cx_c),
5555        [
5556            ("user_a".to_string(), "online", "busy"),
5557            ("user_b".to_string(), "online", "busy")
5558        ]
5559    );
5560    assert_eq!(
5561        contacts(&client_d, cx_d),
5562        [("user_b".to_string(), "online", "busy")]
5563    );
5564
5565    server.forbid_connections();
5566    server.disconnect_client(client_a.peer_id().unwrap());
5567    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5568    assert_eq!(contacts(&client_a, cx_a), []);
5569    assert_eq!(
5570        contacts(&client_b, cx_b),
5571        [
5572            ("user_a".to_string(), "offline", "free"),
5573            ("user_c".to_string(), "online", "free"),
5574            ("user_d".to_string(), "online", "free")
5575        ]
5576    );
5577    assert_eq!(
5578        contacts(&client_c, cx_c),
5579        [
5580            ("user_a".to_string(), "offline", "free"),
5581            ("user_b".to_string(), "online", "free")
5582        ]
5583    );
5584    assert_eq!(
5585        contacts(&client_d, cx_d),
5586        [("user_b".to_string(), "online", "free")]
5587    );
5588
5589    // Test removing a contact
5590    client_b
5591        .user_store()
5592        .update(cx_b, |store, cx| {
5593            store.remove_contact(client_c.user_id().unwrap(), cx)
5594        })
5595        .await
5596        .unwrap();
5597    executor.run_until_parked();
5598    assert_eq!(
5599        contacts(&client_b, cx_b),
5600        [
5601            ("user_a".to_string(), "offline", "free"),
5602            ("user_d".to_string(), "online", "free")
5603        ]
5604    );
5605    assert_eq!(
5606        contacts(&client_c, cx_c),
5607        [("user_a".to_string(), "offline", "free"),]
5608    );
5609
5610    fn contacts(
5611        client: &TestClient,
5612        cx: &TestAppContext,
5613    ) -> Vec<(String, &'static str, &'static str)> {
5614        client.user_store().read_with(cx, |store, _| {
5615            store
5616                .contacts()
5617                .iter()
5618                .map(|contact| {
5619                    (
5620                        contact.user.github_login.clone(),
5621                        if contact.online { "online" } else { "offline" },
5622                        if contact.busy { "busy" } else { "free" },
5623                    )
5624                })
5625                .collect()
5626        })
5627    }
5628}
5629
5630#[gpui::test(iterations = 10)]
5631async fn test_contact_requests(
5632    executor: BackgroundExecutor,
5633    cx_a: &mut TestAppContext,
5634    cx_a2: &mut TestAppContext,
5635    cx_b: &mut TestAppContext,
5636    cx_b2: &mut TestAppContext,
5637    cx_c: &mut TestAppContext,
5638    cx_c2: &mut TestAppContext,
5639) {
5640    // Connect to a server as 3 clients.
5641    let mut server = TestServer::start(executor.clone()).await;
5642    let client_a = server.create_client(cx_a, "user_a").await;
5643    let client_a2 = server.create_client(cx_a2, "user_a").await;
5644    let client_b = server.create_client(cx_b, "user_b").await;
5645    let client_b2 = server.create_client(cx_b2, "user_b").await;
5646    let client_c = server.create_client(cx_c, "user_c").await;
5647    let client_c2 = server.create_client(cx_c2, "user_c").await;
5648
5649    assert_eq!(client_a.user_id().unwrap(), client_a2.user_id().unwrap());
5650    assert_eq!(client_b.user_id().unwrap(), client_b2.user_id().unwrap());
5651    assert_eq!(client_c.user_id().unwrap(), client_c2.user_id().unwrap());
5652
5653    // User A and User C request that user B become their contact.
5654    client_a
5655        .user_store()
5656        .update(cx_a, |store, cx| {
5657            store.request_contact(client_b.user_id().unwrap(), cx)
5658        })
5659        .await
5660        .unwrap();
5661    client_c
5662        .user_store()
5663        .update(cx_c, |store, cx| {
5664            store.request_contact(client_b.user_id().unwrap(), cx)
5665        })
5666        .await
5667        .unwrap();
5668    executor.run_until_parked();
5669
5670    // All users see the pending request appear in all their clients.
5671    assert_eq!(
5672        client_a.summarize_contacts(cx_a).outgoing_requests,
5673        &["user_b"]
5674    );
5675    assert_eq!(
5676        client_a2.summarize_contacts(cx_a2).outgoing_requests,
5677        &["user_b"]
5678    );
5679    assert_eq!(
5680        client_b.summarize_contacts(cx_b).incoming_requests,
5681        &["user_a", "user_c"]
5682    );
5683    assert_eq!(
5684        client_b2.summarize_contacts(cx_b2).incoming_requests,
5685        &["user_a", "user_c"]
5686    );
5687    assert_eq!(
5688        client_c.summarize_contacts(cx_c).outgoing_requests,
5689        &["user_b"]
5690    );
5691    assert_eq!(
5692        client_c2.summarize_contacts(cx_c2).outgoing_requests,
5693        &["user_b"]
5694    );
5695
5696    // Contact requests are present upon connecting (tested here via disconnect/reconnect)
5697    disconnect_and_reconnect(&client_a, cx_a).await;
5698    disconnect_and_reconnect(&client_b, cx_b).await;
5699    disconnect_and_reconnect(&client_c, cx_c).await;
5700    executor.run_until_parked();
5701    assert_eq!(
5702        client_a.summarize_contacts(cx_a).outgoing_requests,
5703        &["user_b"]
5704    );
5705    assert_eq!(
5706        client_b.summarize_contacts(cx_b).incoming_requests,
5707        &["user_a", "user_c"]
5708    );
5709    assert_eq!(
5710        client_c.summarize_contacts(cx_c).outgoing_requests,
5711        &["user_b"]
5712    );
5713
5714    // User B accepts the request from user A.
5715    client_b
5716        .user_store()
5717        .update(cx_b, |store, cx| {
5718            store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
5719        })
5720        .await
5721        .unwrap();
5722
5723    executor.run_until_parked();
5724
5725    // User B sees user A as their contact now in all client, and the incoming request from them is removed.
5726    let contacts_b = client_b.summarize_contacts(cx_b);
5727    assert_eq!(contacts_b.current, &["user_a"]);
5728    assert_eq!(contacts_b.incoming_requests, &["user_c"]);
5729    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
5730    assert_eq!(contacts_b2.current, &["user_a"]);
5731    assert_eq!(contacts_b2.incoming_requests, &["user_c"]);
5732
5733    // User A sees user B as their contact now in all clients, and the outgoing request to them is removed.
5734    let contacts_a = client_a.summarize_contacts(cx_a);
5735    assert_eq!(contacts_a.current, &["user_b"]);
5736    assert!(contacts_a.outgoing_requests.is_empty());
5737    let contacts_a2 = client_a2.summarize_contacts(cx_a2);
5738    assert_eq!(contacts_a2.current, &["user_b"]);
5739    assert!(contacts_a2.outgoing_requests.is_empty());
5740
5741    // Contacts are present upon connecting (tested here via disconnect/reconnect)
5742    disconnect_and_reconnect(&client_a, cx_a).await;
5743    disconnect_and_reconnect(&client_b, cx_b).await;
5744    disconnect_and_reconnect(&client_c, cx_c).await;
5745    executor.run_until_parked();
5746    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
5747    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
5748    assert_eq!(
5749        client_b.summarize_contacts(cx_b).incoming_requests,
5750        &["user_c"]
5751    );
5752    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
5753    assert_eq!(
5754        client_c.summarize_contacts(cx_c).outgoing_requests,
5755        &["user_b"]
5756    );
5757
5758    // User B rejects the request from user C.
5759    client_b
5760        .user_store()
5761        .update(cx_b, |store, cx| {
5762            store.respond_to_contact_request(client_c.user_id().unwrap(), false, cx)
5763        })
5764        .await
5765        .unwrap();
5766
5767    executor.run_until_parked();
5768
5769    // User B doesn't see user C as their contact, and the incoming request from them is removed.
5770    let contacts_b = client_b.summarize_contacts(cx_b);
5771    assert_eq!(contacts_b.current, &["user_a"]);
5772    assert!(contacts_b.incoming_requests.is_empty());
5773    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
5774    assert_eq!(contacts_b2.current, &["user_a"]);
5775    assert!(contacts_b2.incoming_requests.is_empty());
5776
5777    // User C doesn't see user B as their contact, and the outgoing request to them is removed.
5778    let contacts_c = client_c.summarize_contacts(cx_c);
5779    assert!(contacts_c.current.is_empty());
5780    assert!(contacts_c.outgoing_requests.is_empty());
5781    let contacts_c2 = client_c2.summarize_contacts(cx_c2);
5782    assert!(contacts_c2.current.is_empty());
5783    assert!(contacts_c2.outgoing_requests.is_empty());
5784
5785    // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect)
5786    disconnect_and_reconnect(&client_a, cx_a).await;
5787    disconnect_and_reconnect(&client_b, cx_b).await;
5788    disconnect_and_reconnect(&client_c, cx_c).await;
5789    executor.run_until_parked();
5790    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
5791    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
5792    assert!(client_b
5793        .summarize_contacts(cx_b)
5794        .incoming_requests
5795        .is_empty());
5796    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
5797    assert!(client_c
5798        .summarize_contacts(cx_c)
5799        .outgoing_requests
5800        .is_empty());
5801
5802    async fn disconnect_and_reconnect(client: &TestClient, cx: &mut TestAppContext) {
5803        client.disconnect(&cx.to_async());
5804        client.clear_contacts(cx).await;
5805        client
5806            .authenticate_and_connect(false, &cx.to_async())
5807            .await
5808            .unwrap();
5809    }
5810}
5811
5812#[gpui::test(iterations = 10)]
5813async fn test_join_call_after_screen_was_shared(
5814    executor: BackgroundExecutor,
5815    cx_a: &mut TestAppContext,
5816    cx_b: &mut TestAppContext,
5817) {
5818    let mut server = TestServer::start(executor.clone()).await;
5819
5820    let client_a = server.create_client(cx_a, "user_a").await;
5821    let client_b = server.create_client(cx_b, "user_b").await;
5822    server
5823        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5824        .await;
5825
5826    let active_call_a = cx_a.read(ActiveCall::global);
5827    let active_call_b = cx_b.read(ActiveCall::global);
5828
5829    // Call users B and C from client A.
5830    active_call_a
5831        .update(cx_a, |call, cx| {
5832            call.invite(client_b.user_id().unwrap(), None, cx)
5833        })
5834        .await
5835        .unwrap();
5836
5837    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
5838    executor.run_until_parked();
5839    assert_eq!(
5840        room_participants(&room_a, cx_a),
5841        RoomParticipants {
5842            remote: Default::default(),
5843            pending: vec!["user_b".to_string()]
5844        }
5845    );
5846
5847    // User B receives the call.
5848
5849    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
5850    let call_b = incoming_call_b.next().await.unwrap().unwrap();
5851    assert_eq!(call_b.calling_user.github_login, "user_a");
5852
5853    // User A shares their screen
5854    let display = MacOSDisplay::new();
5855    active_call_a
5856        .update(cx_a, |call, cx| {
5857            call.room().unwrap().update(cx, |room, cx| {
5858                room.set_display_sources(vec![display.clone()]);
5859                room.share_screen(cx)
5860            })
5861        })
5862        .await
5863        .unwrap();
5864
5865    client_b.user_store().update(cx_b, |user_store, _| {
5866        user_store.clear_cache();
5867    });
5868
5869    // User B joins the room
5870    active_call_b
5871        .update(cx_b, |call, cx| call.accept_incoming(cx))
5872        .await
5873        .unwrap();
5874
5875    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
5876    assert!(incoming_call_b.next().await.unwrap().is_none());
5877
5878    executor.run_until_parked();
5879    assert_eq!(
5880        room_participants(&room_a, cx_a),
5881        RoomParticipants {
5882            remote: vec!["user_b".to_string()],
5883            pending: vec![],
5884        }
5885    );
5886    assert_eq!(
5887        room_participants(&room_b, cx_b),
5888        RoomParticipants {
5889            remote: vec!["user_a".to_string()],
5890            pending: vec![],
5891        }
5892    );
5893
5894    // Ensure User B sees User A's screenshare.
5895
5896    room_b.read_with(cx_b, |room, _| {
5897        assert_eq!(
5898            room.remote_participants()
5899                .get(&client_a.user_id().unwrap())
5900                .unwrap()
5901                .video_tracks
5902                .len(),
5903            1
5904        );
5905    });
5906}
5907
5908#[gpui::test]
5909async fn test_right_click_menu_behind_collab_panel(cx: &mut TestAppContext) {
5910    let mut server = TestServer::start(cx.executor().clone()).await;
5911    let client_a = server.create_client(cx, "user_a").await;
5912    let (_workspace_a, cx) = client_a.build_test_workspace(cx).await;
5913
5914    cx.simulate_resize(size(px(300.), px(300.)));
5915
5916    cx.simulate_keystrokes("cmd-n cmd-n cmd-n");
5917    cx.update(|cx| cx.refresh());
5918
5919    let tab_bounds = cx.debug_bounds("TAB-2").unwrap();
5920    let new_tab_button_bounds = cx.debug_bounds("ICON-Plus").unwrap();
5921
5922    assert!(
5923        tab_bounds.intersects(&new_tab_button_bounds),
5924        "Tab should overlap with the new tab button, if this is failing check if there's been a redesign!"
5925    );
5926
5927    cx.simulate_event(MouseDownEvent {
5928        button: MouseButton::Right,
5929        position: new_tab_button_bounds.center(),
5930        modifiers: Modifiers::default(),
5931        click_count: 1,
5932    });
5933
5934    // regression test that the right click menu for tabs does not open.
5935    assert!(cx.debug_bounds("MENU_ITEM-Close").is_none());
5936
5937    let tab_bounds = cx.debug_bounds("TAB-1").unwrap();
5938    cx.simulate_event(MouseDownEvent {
5939        button: MouseButton::Right,
5940        position: tab_bounds.center(),
5941        modifiers: Modifiers::default(),
5942        click_count: 1,
5943    });
5944    assert!(cx.debug_bounds("MENU_ITEM-Close").is_some());
5945}
5946
5947#[gpui::test]
5948async fn test_cmd_k_left(cx: &mut TestAppContext) {
5949    let client = TestServer::start1(cx).await;
5950    let (workspace, cx) = client.build_test_workspace(cx).await;
5951
5952    cx.simulate_keystrokes("cmd-n");
5953    workspace.update(cx, |workspace, cx| {
5954        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 1);
5955    });
5956    cx.simulate_keystrokes("cmd-k left");
5957    workspace.update(cx, |workspace, cx| {
5958        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 2);
5959    });
5960    cx.simulate_keystrokes("cmd-k");
5961    // sleep for longer than the timeout in keyboard shortcut handling
5962    // to verify that it doesn't fire in this case.
5963    cx.executor().advance_clock(Duration::from_secs(2));
5964    cx.simulate_keystrokes("left");
5965    workspace.update(cx, |workspace, cx| {
5966        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 2);
5967    });
5968}