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