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