integration_tests.rs

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