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