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