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