integration_tests.rs

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