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