integration_tests.rs

   1use crate::{
   2    rpc::{CLEANUP_TIMEOUT, RECONNECT_TIMEOUT},
   3    tests::{
   4        channel_id, following_tests::join_channel, room_participants, rust_lang, RoomParticipants,
   5        TestClient, TestServer,
   6    },
   7};
   8use anyhow::{anyhow, Result};
   9use assistant::{ContextStore, PromptBuilder};
  10use call::{room, ActiveCall, ParticipantLocation, Room};
  11use client::{User, RECEIVE_TIMEOUT};
  12use collections::{HashMap, HashSet};
  13use fs::{FakeFs, Fs as _, RemoveOptions};
  14use futures::{channel::mpsc, StreamExt as _};
  15use git::repository::GitFileStatus;
  16use gpui::{
  17    px, size, AppContext, BackgroundExecutor, Model, Modifiers, MouseButton, MouseDownEvent,
  18    TestAppContext, UpdateGlobal,
  19};
  20use language::{
  21    language_settings::{
  22        AllLanguageSettings, Formatter, FormatterList, PrettierSettings, SelectedFormatter,
  23    },
  24    tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language, LanguageConfig,
  25    LanguageMatcher, LineEnding, OffsetRangeExt, Point, Rope,
  26};
  27use live_kit_client::MacOSDisplay;
  28use lsp::LanguageServerId;
  29use parking_lot::Mutex;
  30use project::{
  31    search::SearchQuery, search::SearchResult, DiagnosticSummary, FormatTrigger, HoverBlockKind,
  32    Project, ProjectPath,
  33};
  34use rand::prelude::*;
  35use serde_json::json;
  36use settings::SettingsStore;
  37use std::{
  38    cell::{Cell, RefCell},
  39    env, future, mem,
  40    path::{Path, PathBuf},
  41    rc::Rc,
  42    sync::{
  43        atomic::{AtomicBool, Ordering::SeqCst},
  44        Arc,
  45    },
  46    time::Duration,
  47};
  48use unindent::Unindent as _;
  49use workspace::Pane;
  50
  51#[ctor::ctor]
  52fn init_logger() {
  53    if std::env::var("RUST_LOG").is_ok() {
  54        env_logger::init();
  55    }
  56}
  57
  58#[gpui::test(iterations = 10)]
  59async fn test_basic_calls(
  60    executor: BackgroundExecutor,
  61    cx_a: &mut TestAppContext,
  62    cx_b: &mut TestAppContext,
  63    cx_b2: &mut TestAppContext,
  64    cx_c: &mut TestAppContext,
  65) {
  66    let mut server = TestServer::start(executor.clone()).await;
  67
  68    let client_a = server.create_client(cx_a, "user_a").await;
  69    let client_b = server.create_client(cx_b, "user_b").await;
  70    let client_c = server.create_client(cx_c, "user_c").await;
  71    server
  72        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
  73        .await;
  74
  75    let active_call_a = cx_a.read(ActiveCall::global);
  76    let active_call_b = cx_b.read(ActiveCall::global);
  77    let active_call_c = cx_c.read(ActiveCall::global);
  78
  79    // Call user B from client A.
  80    active_call_a
  81        .update(cx_a, |call, cx| {
  82            call.invite(client_b.user_id().unwrap(), None, cx)
  83        })
  84        .await
  85        .unwrap();
  86    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
  87    executor.run_until_parked();
  88    assert_eq!(
  89        room_participants(&room_a, cx_a),
  90        RoomParticipants {
  91            remote: Default::default(),
  92            pending: vec!["user_b".to_string()]
  93        }
  94    );
  95
  96    // User B receives the call.
  97
  98    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
  99    let call_b = incoming_call_b.next().await.unwrap().unwrap();
 100    assert_eq!(call_b.calling_user.github_login, "user_a");
 101
 102    // User B connects via another client and also receives a ring on the newly-connected client.
 103    let _client_b2 = server.create_client(cx_b2, "user_b").await;
 104    let active_call_b2 = cx_b2.read(ActiveCall::global);
 105
 106    let mut incoming_call_b2 = active_call_b2.read_with(cx_b2, |call, _| call.incoming());
 107    executor.run_until_parked();
 108    let call_b2 = incoming_call_b2.next().await.unwrap().unwrap();
 109    assert_eq!(call_b2.calling_user.github_login, "user_a");
 110
 111    // User B joins the room using the first client.
 112    active_call_b
 113        .update(cx_b, |call, cx| call.accept_incoming(cx))
 114        .await
 115        .unwrap();
 116
 117    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 118    assert!(incoming_call_b.next().await.unwrap().is_none());
 119
 120    executor.run_until_parked();
 121    assert_eq!(
 122        room_participants(&room_a, cx_a),
 123        RoomParticipants {
 124            remote: vec!["user_b".to_string()],
 125            pending: Default::default()
 126        }
 127    );
 128    assert_eq!(
 129        room_participants(&room_b, cx_b),
 130        RoomParticipants {
 131            remote: vec!["user_a".to_string()],
 132            pending: Default::default()
 133        }
 134    );
 135
 136    // Call user C from client B.
 137
 138    let mut incoming_call_c = active_call_c.read_with(cx_c, |call, _| call.incoming());
 139    active_call_b
 140        .update(cx_b, |call, cx| {
 141            call.invite(client_c.user_id().unwrap(), None, cx)
 142        })
 143        .await
 144        .unwrap();
 145
 146    executor.run_until_parked();
 147    assert_eq!(
 148        room_participants(&room_a, cx_a),
 149        RoomParticipants {
 150            remote: vec!["user_b".to_string()],
 151            pending: vec!["user_c".to_string()]
 152        }
 153    );
 154    assert_eq!(
 155        room_participants(&room_b, cx_b),
 156        RoomParticipants {
 157            remote: vec!["user_a".to_string()],
 158            pending: vec!["user_c".to_string()]
 159        }
 160    );
 161
 162    // User C receives the call, but declines it.
 163    let call_c = incoming_call_c.next().await.unwrap().unwrap();
 164    assert_eq!(call_c.calling_user.github_login, "user_b");
 165    active_call_c.update(cx_c, |call, cx| call.decline_incoming(cx).unwrap());
 166    assert!(incoming_call_c.next().await.unwrap().is_none());
 167
 168    executor.run_until_parked();
 169    assert_eq!(
 170        room_participants(&room_a, cx_a),
 171        RoomParticipants {
 172            remote: vec!["user_b".to_string()],
 173            pending: Default::default()
 174        }
 175    );
 176    assert_eq!(
 177        room_participants(&room_b, cx_b),
 178        RoomParticipants {
 179            remote: vec!["user_a".to_string()],
 180            pending: Default::default()
 181        }
 182    );
 183
 184    // Call user C again from user A.
 185    active_call_a
 186        .update(cx_a, |call, cx| {
 187            call.invite(client_c.user_id().unwrap(), None, cx)
 188        })
 189        .await
 190        .unwrap();
 191
 192    executor.run_until_parked();
 193    assert_eq!(
 194        room_participants(&room_a, cx_a),
 195        RoomParticipants {
 196            remote: vec!["user_b".to_string()],
 197            pending: vec!["user_c".to_string()]
 198        }
 199    );
 200    assert_eq!(
 201        room_participants(&room_b, cx_b),
 202        RoomParticipants {
 203            remote: vec!["user_a".to_string()],
 204            pending: vec!["user_c".to_string()]
 205        }
 206    );
 207
 208    // User C accepts the call.
 209    let call_c = incoming_call_c.next().await.unwrap().unwrap();
 210    assert_eq!(call_c.calling_user.github_login, "user_a");
 211    active_call_c
 212        .update(cx_c, |call, cx| call.accept_incoming(cx))
 213        .await
 214        .unwrap();
 215    assert!(incoming_call_c.next().await.unwrap().is_none());
 216
 217    let room_c = active_call_c.read_with(cx_c, |call, _| call.room().unwrap().clone());
 218
 219    executor.run_until_parked();
 220    assert_eq!(
 221        room_participants(&room_a, cx_a),
 222        RoomParticipants {
 223            remote: vec!["user_b".to_string(), "user_c".to_string()],
 224            pending: Default::default()
 225        }
 226    );
 227    assert_eq!(
 228        room_participants(&room_b, cx_b),
 229        RoomParticipants {
 230            remote: vec!["user_a".to_string(), "user_c".to_string()],
 231            pending: Default::default()
 232        }
 233    );
 234    assert_eq!(
 235        room_participants(&room_c, cx_c),
 236        RoomParticipants {
 237            remote: vec!["user_a".to_string(), "user_b".to_string()],
 238            pending: Default::default()
 239        }
 240    );
 241
 242    // User A shares their screen
 243    let display = MacOSDisplay::new();
 244    let events_b = active_call_events(cx_b);
 245    let events_c = active_call_events(cx_c);
 246    active_call_a
 247        .update(cx_a, |call, cx| {
 248            call.room().unwrap().update(cx, |room, cx| {
 249                room.set_display_sources(vec![display.clone()]);
 250                room.share_screen(cx)
 251            })
 252        })
 253        .await
 254        .unwrap();
 255
 256    executor.run_until_parked();
 257
 258    // User B observes the remote screen sharing track.
 259    assert_eq!(events_b.borrow().len(), 1);
 260    let event_b = events_b.borrow().first().unwrap().clone();
 261    if let call::room::Event::RemoteVideoTracksChanged { participant_id } = event_b {
 262        assert_eq!(participant_id, client_a.peer_id().unwrap());
 263
 264        room_b.read_with(cx_b, |room, _| {
 265            assert_eq!(
 266                room.remote_participants()[&client_a.user_id().unwrap()]
 267                    .video_tracks
 268                    .len(),
 269                1
 270            );
 271        });
 272    } else {
 273        panic!("unexpected event")
 274    }
 275
 276    // User C observes the remote screen sharing track.
 277    assert_eq!(events_c.borrow().len(), 1);
 278    let event_c = events_c.borrow().first().unwrap().clone();
 279    if let call::room::Event::RemoteVideoTracksChanged { participant_id } = event_c {
 280        assert_eq!(participant_id, client_a.peer_id().unwrap());
 281
 282        room_c.read_with(cx_c, |room, _| {
 283            assert_eq!(
 284                room.remote_participants()[&client_a.user_id().unwrap()]
 285                    .video_tracks
 286                    .len(),
 287                1
 288            );
 289        });
 290    } else {
 291        panic!("unexpected event")
 292    }
 293
 294    // User A leaves the room.
 295    active_call_a
 296        .update(cx_a, |call, cx| {
 297            let hang_up = call.hang_up(cx);
 298            assert!(call.room().is_none());
 299            hang_up
 300        })
 301        .await
 302        .unwrap();
 303    executor.run_until_parked();
 304    assert_eq!(
 305        room_participants(&room_a, cx_a),
 306        RoomParticipants {
 307            remote: Default::default(),
 308            pending: Default::default()
 309        }
 310    );
 311    assert_eq!(
 312        room_participants(&room_b, cx_b),
 313        RoomParticipants {
 314            remote: vec!["user_c".to_string()],
 315            pending: Default::default()
 316        }
 317    );
 318    assert_eq!(
 319        room_participants(&room_c, cx_c),
 320        RoomParticipants {
 321            remote: vec!["user_b".to_string()],
 322            pending: Default::default()
 323        }
 324    );
 325
 326    // User B gets disconnected from the LiveKit server, which causes them
 327    // to automatically leave the room. User C leaves the room as well because
 328    // nobody else is in there.
 329    server
 330        .test_live_kit_server
 331        .disconnect_client(client_b.user_id().unwrap().to_string())
 332        .await;
 333    executor.run_until_parked();
 334
 335    active_call_b.read_with(cx_b, |call, _| assert!(call.room().is_none()));
 336
 337    active_call_c.read_with(cx_c, |call, _| assert!(call.room().is_none()));
 338    assert_eq!(
 339        room_participants(&room_a, cx_a),
 340        RoomParticipants {
 341            remote: Default::default(),
 342            pending: Default::default()
 343        }
 344    );
 345    assert_eq!(
 346        room_participants(&room_b, cx_b),
 347        RoomParticipants {
 348            remote: Default::default(),
 349            pending: Default::default()
 350        }
 351    );
 352    assert_eq!(
 353        room_participants(&room_c, cx_c),
 354        RoomParticipants {
 355            remote: Default::default(),
 356            pending: Default::default()
 357        }
 358    );
 359}
 360
 361#[gpui::test(iterations = 10)]
 362async fn test_calling_multiple_users_simultaneously(
 363    executor: BackgroundExecutor,
 364    cx_a: &mut TestAppContext,
 365    cx_b: &mut TestAppContext,
 366    cx_c: &mut TestAppContext,
 367    cx_d: &mut TestAppContext,
 368) {
 369    let mut server = TestServer::start(executor.clone()).await;
 370
 371    let client_a = server.create_client(cx_a, "user_a").await;
 372    let client_b = server.create_client(cx_b, "user_b").await;
 373    let client_c = server.create_client(cx_c, "user_c").await;
 374    let client_d = server.create_client(cx_d, "user_d").await;
 375    server
 376        .make_contacts(&mut [
 377            (&client_a, cx_a),
 378            (&client_b, cx_b),
 379            (&client_c, cx_c),
 380            (&client_d, cx_d),
 381        ])
 382        .await;
 383
 384    let active_call_a = cx_a.read(ActiveCall::global);
 385    let active_call_b = cx_b.read(ActiveCall::global);
 386    let active_call_c = cx_c.read(ActiveCall::global);
 387    let active_call_d = cx_d.read(ActiveCall::global);
 388
 389    // Simultaneously call user B and user C from client A.
 390    let b_invite = active_call_a.update(cx_a, |call, cx| {
 391        call.invite(client_b.user_id().unwrap(), None, cx)
 392    });
 393    let c_invite = active_call_a.update(cx_a, |call, cx| {
 394        call.invite(client_c.user_id().unwrap(), None, cx)
 395    });
 396    b_invite.await.unwrap();
 397    c_invite.await.unwrap();
 398
 399    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 400    executor.run_until_parked();
 401    assert_eq!(
 402        room_participants(&room_a, cx_a),
 403        RoomParticipants {
 404            remote: Default::default(),
 405            pending: vec!["user_b".to_string(), "user_c".to_string()]
 406        }
 407    );
 408
 409    // Call client D from client A.
 410    active_call_a
 411        .update(cx_a, |call, cx| {
 412            call.invite(client_d.user_id().unwrap(), None, cx)
 413        })
 414        .await
 415        .unwrap();
 416    executor.run_until_parked();
 417    assert_eq!(
 418        room_participants(&room_a, cx_a),
 419        RoomParticipants {
 420            remote: Default::default(),
 421            pending: vec![
 422                "user_b".to_string(),
 423                "user_c".to_string(),
 424                "user_d".to_string()
 425            ]
 426        }
 427    );
 428
 429    // Accept the call on all clients simultaneously.
 430    let accept_b = active_call_b.update(cx_b, |call, cx| call.accept_incoming(cx));
 431    let accept_c = active_call_c.update(cx_c, |call, cx| call.accept_incoming(cx));
 432    let accept_d = active_call_d.update(cx_d, |call, cx| call.accept_incoming(cx));
 433    accept_b.await.unwrap();
 434    accept_c.await.unwrap();
 435    accept_d.await.unwrap();
 436
 437    executor.run_until_parked();
 438
 439    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 440
 441    let room_c = active_call_c.read_with(cx_c, |call, _| call.room().unwrap().clone());
 442
 443    let room_d = active_call_d.read_with(cx_d, |call, _| call.room().unwrap().clone());
 444    assert_eq!(
 445        room_participants(&room_a, cx_a),
 446        RoomParticipants {
 447            remote: vec![
 448                "user_b".to_string(),
 449                "user_c".to_string(),
 450                "user_d".to_string(),
 451            ],
 452            pending: Default::default()
 453        }
 454    );
 455    assert_eq!(
 456        room_participants(&room_b, cx_b),
 457        RoomParticipants {
 458            remote: vec![
 459                "user_a".to_string(),
 460                "user_c".to_string(),
 461                "user_d".to_string(),
 462            ],
 463            pending: Default::default()
 464        }
 465    );
 466    assert_eq!(
 467        room_participants(&room_c, cx_c),
 468        RoomParticipants {
 469            remote: vec![
 470                "user_a".to_string(),
 471                "user_b".to_string(),
 472                "user_d".to_string(),
 473            ],
 474            pending: Default::default()
 475        }
 476    );
 477    assert_eq!(
 478        room_participants(&room_d, cx_d),
 479        RoomParticipants {
 480            remote: vec![
 481                "user_a".to_string(),
 482                "user_b".to_string(),
 483                "user_c".to_string(),
 484            ],
 485            pending: Default::default()
 486        }
 487    );
 488}
 489
 490#[gpui::test(iterations = 10)]
 491async fn test_joining_channels_and_calling_multiple_users_simultaneously(
 492    executor: BackgroundExecutor,
 493    cx_a: &mut TestAppContext,
 494    cx_b: &mut TestAppContext,
 495    cx_c: &mut TestAppContext,
 496) {
 497    let mut server = TestServer::start(executor.clone()).await;
 498
 499    let client_a = server.create_client(cx_a, "user_a").await;
 500    let client_b = server.create_client(cx_b, "user_b").await;
 501    let client_c = server.create_client(cx_c, "user_c").await;
 502    server
 503        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
 504        .await;
 505
 506    let channel_1 = server
 507        .make_channel(
 508            "channel1",
 509            None,
 510            (&client_a, cx_a),
 511            &mut [(&client_b, cx_b), (&client_c, cx_c)],
 512        )
 513        .await;
 514
 515    let channel_2 = server
 516        .make_channel(
 517            "channel2",
 518            None,
 519            (&client_a, cx_a),
 520            &mut [(&client_b, cx_b), (&client_c, cx_c)],
 521        )
 522        .await;
 523
 524    let active_call_a = cx_a.read(ActiveCall::global);
 525
 526    // Simultaneously join channel 1 and then channel 2
 527    active_call_a
 528        .update(cx_a, |call, cx| call.join_channel(channel_1, cx))
 529        .detach();
 530    let join_channel_2 = active_call_a.update(cx_a, |call, cx| call.join_channel(channel_2, cx));
 531
 532    join_channel_2.await.unwrap();
 533
 534    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 535    executor.run_until_parked();
 536
 537    assert_eq!(channel_id(&room_a, cx_a), Some(channel_2));
 538
 539    // Leave the room
 540    active_call_a
 541        .update(cx_a, |call, cx| 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.build_dev_server_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.build_dev_server_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.build_dev_server_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.build_dev_server_project(project1_id, cx_b).await;
1518    let project_b2 = client_b.build_dev_server_project(project2_id, cx_b).await;
1519    let project_b3 = client_b.build_dev_server_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()),
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()),
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.build_dev_server_project(project_id, cx_b).await;
2314    let project_c = client_c.build_dev_server_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");
2332    });
2333
2334    buffer_c.read_with(cx_c, |buffer, _| {
2335        assert_eq!(&*buffer.language().unwrap().name(), "Rust");
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");
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");
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");
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.build_dev_server_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.build_dev_server_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.build_dev_server_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.build_dev_server_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.build_dev_server_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.build_dev_server_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.build_dev_server_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.build_dev_server_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.build_dev_server_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.build_dev_server_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.build_dev_server_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.build_dev_server_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.build_dev_server_project(project_id, cx_b).await;
3713    let project_c = client_c.build_dev_server_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.build_dev_server_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()),
3859    )));
3860    let mut fake_language_servers = client_a
3861        .language_registry()
3862        .register_fake_lsp_adapter("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.build_dev_server_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.build_dev_server_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_adapter(
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.build_dev_server_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.build_dev_server_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_adapter("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.build_dev_server_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: 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()),
4462    )));
4463    let mut fake_language_servers = client_a.language_registry().register_fake_lsp_adapter(
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.build_dev_server_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_adapter("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.build_dev_server_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_adapter(
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.build_dev_server_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.build_dev_server_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_adapter("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.build_dev_server_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 fake_language_servers = client_a.language_registry().register_fake_lsp_adapter(
5083        "Rust",
5084        FakeLspAdapter {
5085            name: "rust-analyzer",
5086            capabilities: lsp::ServerCapabilities {
5087                hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
5088                ..lsp::ServerCapabilities::default()
5089            },
5090            ..FakeLspAdapter::default()
5091        },
5092    );
5093    let _other_server = client_a.language_registry().register_fake_lsp_adapter(
5094        "Rust",
5095        FakeLspAdapter {
5096            name: "CrabLang-ls",
5097            capabilities: lsp::ServerCapabilities {
5098                hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
5099                ..lsp::ServerCapabilities::default()
5100            },
5101            ..FakeLspAdapter::default()
5102        },
5103    );
5104
5105    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
5106    let project_id = active_call_a
5107        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5108        .await
5109        .unwrap();
5110    let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
5111
5112    // Open the file as the guest
5113    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx));
5114    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
5115
5116    let mut servers_with_hover_requests = HashMap::default();
5117    for i in 0..language_server_names.len() {
5118        let new_server = fake_language_servers.next().await.unwrap_or_else(|| {
5119            panic!(
5120                "Failed to get language server #{i} with name {}",
5121                &language_server_names[i]
5122            )
5123        });
5124        let new_server_name = new_server.server.name();
5125        assert!(
5126            !servers_with_hover_requests.contains_key(new_server_name),
5127            "Unexpected: initialized server with the same name twice. Name: `{new_server_name}`"
5128        );
5129        let new_server_name = new_server_name.to_string();
5130        match new_server_name.as_str() {
5131            "CrabLang-ls" => {
5132                servers_with_hover_requests.insert(
5133                    new_server_name.clone(),
5134                    new_server.handle_request::<lsp::request::HoverRequest, _, _>(
5135                        move |params, _| {
5136                            assert_eq!(
5137                                params
5138                                    .text_document_position_params
5139                                    .text_document
5140                                    .uri
5141                                    .as_str(),
5142                                "file:///root-1/main.rs"
5143                            );
5144                            let name = new_server_name.clone();
5145                            async move {
5146                                Ok(Some(lsp::Hover {
5147                                    contents: lsp::HoverContents::Scalar(
5148                                        lsp::MarkedString::String(format!("{name} hover")),
5149                                    ),
5150                                    range: None,
5151                                }))
5152                            }
5153                        },
5154                    ),
5155                );
5156            }
5157            "rust-analyzer" => {
5158                servers_with_hover_requests.insert(
5159                    new_server_name.clone(),
5160                    new_server.handle_request::<lsp::request::HoverRequest, _, _>(
5161                        |params, _| async move {
5162                            assert_eq!(
5163                                params
5164                                    .text_document_position_params
5165                                    .text_document
5166                                    .uri
5167                                    .as_str(),
5168                                "file:///root-1/main.rs"
5169                            );
5170                            assert_eq!(
5171                                params.text_document_position_params.position,
5172                                lsp::Position::new(0, 22)
5173                            );
5174                            Ok(Some(lsp::Hover {
5175                                contents: lsp::HoverContents::Array(vec![
5176                                    lsp::MarkedString::String("Test hover content.".to_string()),
5177                                    lsp::MarkedString::LanguageString(lsp::LanguageString {
5178                                        language: "Rust".to_string(),
5179                                        value: "let foo = 42;".to_string(),
5180                                    }),
5181                                ]),
5182                                range: Some(lsp::Range::new(
5183                                    lsp::Position::new(0, 22),
5184                                    lsp::Position::new(0, 29),
5185                                )),
5186                            }))
5187                        },
5188                    ),
5189                );
5190            }
5191            unexpected => panic!("Unexpected server name: {unexpected}"),
5192        }
5193    }
5194
5195    // Request hover information as the guest.
5196    let mut hovers = project_b
5197        .update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
5198        .await;
5199    assert_eq!(
5200        hovers.len(),
5201        2,
5202        "Expected two hovers from both language servers, but got: {hovers:?}"
5203    );
5204
5205    let _: Vec<()> = futures::future::join_all(servers_with_hover_requests.into_values().map(
5206        |mut hover_request| async move {
5207            hover_request
5208                .next()
5209                .await
5210                .expect("All hover requests should have been triggered")
5211        },
5212    ))
5213    .await;
5214
5215    hovers.sort_by_key(|hover| hover.contents.len());
5216    let first_hover = hovers.first().cloned().unwrap();
5217    assert_eq!(
5218        first_hover.contents,
5219        vec![project::HoverBlock {
5220            text: "CrabLang-ls hover".to_string(),
5221            kind: HoverBlockKind::Markdown,
5222        },]
5223    );
5224    let second_hover = hovers.last().cloned().unwrap();
5225    assert_eq!(
5226        second_hover.contents,
5227        vec![
5228            project::HoverBlock {
5229                text: "Test hover content.".to_string(),
5230                kind: HoverBlockKind::Markdown,
5231            },
5232            project::HoverBlock {
5233                text: "let foo = 42;".to_string(),
5234                kind: HoverBlockKind::Code {
5235                    language: "Rust".to_string()
5236                },
5237            }
5238        ]
5239    );
5240    buffer_b.read_with(cx_b, |buffer, _| {
5241        let snapshot = buffer.snapshot();
5242        assert_eq!(second_hover.range.unwrap().to_offset(&snapshot), 22..29);
5243    });
5244}
5245
5246#[gpui::test(iterations = 10)]
5247async fn test_project_symbols(
5248    executor: BackgroundExecutor,
5249    cx_a: &mut TestAppContext,
5250    cx_b: &mut TestAppContext,
5251) {
5252    let mut server = TestServer::start(executor.clone()).await;
5253    let client_a = server.create_client(cx_a, "user_a").await;
5254    let client_b = server.create_client(cx_b, "user_b").await;
5255    server
5256        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5257        .await;
5258    let active_call_a = cx_a.read(ActiveCall::global);
5259
5260    client_a.language_registry().add(rust_lang());
5261    let mut fake_language_servers = client_a
5262        .language_registry()
5263        .register_fake_lsp_adapter("Rust", Default::default());
5264
5265    client_a
5266        .fs()
5267        .insert_tree(
5268            "/code",
5269            json!({
5270                "crate-1": {
5271                    "one.rs": "const ONE: usize = 1;",
5272                },
5273                "crate-2": {
5274                    "two.rs": "const TWO: usize = 2; const THREE: usize = 3;",
5275                },
5276                "private": {
5277                    "passwords.txt": "the-password",
5278                }
5279            }),
5280        )
5281        .await;
5282    let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await;
5283    let project_id = active_call_a
5284        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5285        .await
5286        .unwrap();
5287    let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
5288
5289    // Cause the language server to start.
5290    let open_buffer_task =
5291        project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx));
5292    let _buffer = cx_b.executor().spawn(open_buffer_task).await.unwrap();
5293
5294    let fake_language_server = fake_language_servers.next().await.unwrap();
5295    fake_language_server.handle_request::<lsp::WorkspaceSymbolRequest, _, _>(|_, _| async move {
5296        Ok(Some(lsp::WorkspaceSymbolResponse::Flat(vec![
5297            #[allow(deprecated)]
5298            lsp::SymbolInformation {
5299                name: "TWO".into(),
5300                location: lsp::Location {
5301                    uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
5302                    range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5303                },
5304                kind: lsp::SymbolKind::CONSTANT,
5305                tags: None,
5306                container_name: None,
5307                deprecated: None,
5308            },
5309        ])))
5310    });
5311
5312    // Request the definition of a symbol as the guest.
5313    let symbols = project_b
5314        .update(cx_b, |p, cx| p.symbols("two", cx))
5315        .await
5316        .unwrap();
5317    assert_eq!(symbols.len(), 1);
5318    assert_eq!(symbols[0].name, "TWO");
5319
5320    // Open one of the returned symbols.
5321    let buffer_b_2 = project_b
5322        .update(cx_b, |project, cx| {
5323            project.open_buffer_for_symbol(&symbols[0], cx)
5324        })
5325        .await
5326        .unwrap();
5327
5328    buffer_b_2.read_with(cx_b, |buffer, cx| {
5329        assert_eq!(
5330            buffer.file().unwrap().full_path(cx),
5331            Path::new("/code/crate-2/two.rs")
5332        );
5333    });
5334
5335    // Attempt to craft a symbol and violate host's privacy by opening an arbitrary file.
5336    let mut fake_symbol = symbols[0].clone();
5337    fake_symbol.path.path = Path::new("/code/secrets").into();
5338    let error = project_b
5339        .update(cx_b, |project, cx| {
5340            project.open_buffer_for_symbol(&fake_symbol, cx)
5341        })
5342        .await
5343        .unwrap_err();
5344    assert!(error.to_string().contains("invalid symbol signature"));
5345}
5346
5347#[gpui::test(iterations = 10)]
5348async fn test_open_buffer_while_getting_definition_pointing_to_it(
5349    executor: BackgroundExecutor,
5350    cx_a: &mut TestAppContext,
5351    cx_b: &mut TestAppContext,
5352    mut rng: StdRng,
5353) {
5354    let mut server = TestServer::start(executor.clone()).await;
5355    let client_a = server.create_client(cx_a, "user_a").await;
5356    let client_b = server.create_client(cx_b, "user_b").await;
5357    server
5358        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5359        .await;
5360    let active_call_a = cx_a.read(ActiveCall::global);
5361
5362    client_a.language_registry().add(rust_lang());
5363    let mut fake_language_servers = client_a
5364        .language_registry()
5365        .register_fake_lsp_adapter("Rust", Default::default());
5366
5367    client_a
5368        .fs()
5369        .insert_tree(
5370            "/root",
5371            json!({
5372                "a.rs": "const ONE: usize = b::TWO;",
5373                "b.rs": "const TWO: usize = 2",
5374            }),
5375        )
5376        .await;
5377    let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await;
5378    let project_id = active_call_a
5379        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5380        .await
5381        .unwrap();
5382    let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
5383
5384    let open_buffer_task = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
5385    let buffer_b1 = cx_b.executor().spawn(open_buffer_task).await.unwrap();
5386
5387    let fake_language_server = fake_language_servers.next().await.unwrap();
5388    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
5389        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
5390            lsp::Location::new(
5391                lsp::Url::from_file_path("/root/b.rs").unwrap(),
5392                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5393            ),
5394        )))
5395    });
5396
5397    let definitions;
5398    let buffer_b2;
5399    if rng.gen() {
5400        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5401        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5402    } else {
5403        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5404        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5405    }
5406
5407    let buffer_b2 = buffer_b2.await.unwrap();
5408    let definitions = definitions.await.unwrap();
5409    assert_eq!(definitions.len(), 1);
5410    assert_eq!(definitions[0].target.buffer, buffer_b2);
5411}
5412
5413#[gpui::test(iterations = 10)]
5414async fn test_contacts(
5415    executor: BackgroundExecutor,
5416    cx_a: &mut TestAppContext,
5417    cx_b: &mut TestAppContext,
5418    cx_c: &mut TestAppContext,
5419    cx_d: &mut TestAppContext,
5420) {
5421    let mut server = TestServer::start(executor.clone()).await;
5422    let client_a = server.create_client(cx_a, "user_a").await;
5423    let client_b = server.create_client(cx_b, "user_b").await;
5424    let client_c = server.create_client(cx_c, "user_c").await;
5425    let client_d = server.create_client(cx_d, "user_d").await;
5426    server
5427        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
5428        .await;
5429    let active_call_a = cx_a.read(ActiveCall::global);
5430    let active_call_b = cx_b.read(ActiveCall::global);
5431    let active_call_c = cx_c.read(ActiveCall::global);
5432    let _active_call_d = cx_d.read(ActiveCall::global);
5433
5434    executor.run_until_parked();
5435    assert_eq!(
5436        contacts(&client_a, cx_a),
5437        [
5438            ("user_b".to_string(), "online", "free"),
5439            ("user_c".to_string(), "online", "free")
5440        ]
5441    );
5442    assert_eq!(
5443        contacts(&client_b, cx_b),
5444        [
5445            ("user_a".to_string(), "online", "free"),
5446            ("user_c".to_string(), "online", "free")
5447        ]
5448    );
5449    assert_eq!(
5450        contacts(&client_c, cx_c),
5451        [
5452            ("user_a".to_string(), "online", "free"),
5453            ("user_b".to_string(), "online", "free")
5454        ]
5455    );
5456    assert_eq!(contacts(&client_d, cx_d), []);
5457
5458    server.disconnect_client(client_c.peer_id().unwrap());
5459    server.forbid_connections();
5460    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5461    assert_eq!(
5462        contacts(&client_a, cx_a),
5463        [
5464            ("user_b".to_string(), "online", "free"),
5465            ("user_c".to_string(), "offline", "free")
5466        ]
5467    );
5468    assert_eq!(
5469        contacts(&client_b, cx_b),
5470        [
5471            ("user_a".to_string(), "online", "free"),
5472            ("user_c".to_string(), "offline", "free")
5473        ]
5474    );
5475    assert_eq!(contacts(&client_c, cx_c), []);
5476    assert_eq!(contacts(&client_d, cx_d), []);
5477
5478    server.allow_connections();
5479    client_c
5480        .authenticate_and_connect(false, &cx_c.to_async())
5481        .await
5482        .unwrap();
5483
5484    executor.run_until_parked();
5485    assert_eq!(
5486        contacts(&client_a, cx_a),
5487        [
5488            ("user_b".to_string(), "online", "free"),
5489            ("user_c".to_string(), "online", "free")
5490        ]
5491    );
5492    assert_eq!(
5493        contacts(&client_b, cx_b),
5494        [
5495            ("user_a".to_string(), "online", "free"),
5496            ("user_c".to_string(), "online", "free")
5497        ]
5498    );
5499    assert_eq!(
5500        contacts(&client_c, cx_c),
5501        [
5502            ("user_a".to_string(), "online", "free"),
5503            ("user_b".to_string(), "online", "free")
5504        ]
5505    );
5506    assert_eq!(contacts(&client_d, cx_d), []);
5507
5508    active_call_a
5509        .update(cx_a, |call, cx| {
5510            call.invite(client_b.user_id().unwrap(), None, cx)
5511        })
5512        .await
5513        .unwrap();
5514    executor.run_until_parked();
5515    assert_eq!(
5516        contacts(&client_a, cx_a),
5517        [
5518            ("user_b".to_string(), "online", "busy"),
5519            ("user_c".to_string(), "online", "free")
5520        ]
5521    );
5522    assert_eq!(
5523        contacts(&client_b, cx_b),
5524        [
5525            ("user_a".to_string(), "online", "busy"),
5526            ("user_c".to_string(), "online", "free")
5527        ]
5528    );
5529    assert_eq!(
5530        contacts(&client_c, cx_c),
5531        [
5532            ("user_a".to_string(), "online", "busy"),
5533            ("user_b".to_string(), "online", "busy")
5534        ]
5535    );
5536    assert_eq!(contacts(&client_d, cx_d), []);
5537
5538    // Client B and client D become contacts while client B is being called.
5539    server
5540        .make_contacts(&mut [(&client_b, cx_b), (&client_d, cx_d)])
5541        .await;
5542    executor.run_until_parked();
5543    assert_eq!(
5544        contacts(&client_a, cx_a),
5545        [
5546            ("user_b".to_string(), "online", "busy"),
5547            ("user_c".to_string(), "online", "free")
5548        ]
5549    );
5550    assert_eq!(
5551        contacts(&client_b, cx_b),
5552        [
5553            ("user_a".to_string(), "online", "busy"),
5554            ("user_c".to_string(), "online", "free"),
5555            ("user_d".to_string(), "online", "free"),
5556        ]
5557    );
5558    assert_eq!(
5559        contacts(&client_c, cx_c),
5560        [
5561            ("user_a".to_string(), "online", "busy"),
5562            ("user_b".to_string(), "online", "busy")
5563        ]
5564    );
5565    assert_eq!(
5566        contacts(&client_d, cx_d),
5567        [("user_b".to_string(), "online", "busy")]
5568    );
5569
5570    active_call_b.update(cx_b, |call, cx| call.decline_incoming(cx).unwrap());
5571    executor.run_until_parked();
5572    assert_eq!(
5573        contacts(&client_a, cx_a),
5574        [
5575            ("user_b".to_string(), "online", "free"),
5576            ("user_c".to_string(), "online", "free")
5577        ]
5578    );
5579    assert_eq!(
5580        contacts(&client_b, cx_b),
5581        [
5582            ("user_a".to_string(), "online", "free"),
5583            ("user_c".to_string(), "online", "free"),
5584            ("user_d".to_string(), "online", "free")
5585        ]
5586    );
5587    assert_eq!(
5588        contacts(&client_c, cx_c),
5589        [
5590            ("user_a".to_string(), "online", "free"),
5591            ("user_b".to_string(), "online", "free")
5592        ]
5593    );
5594    assert_eq!(
5595        contacts(&client_d, cx_d),
5596        [("user_b".to_string(), "online", "free")]
5597    );
5598
5599    active_call_c
5600        .update(cx_c, |call, cx| {
5601            call.invite(client_a.user_id().unwrap(), None, cx)
5602        })
5603        .await
5604        .unwrap();
5605    executor.run_until_parked();
5606    assert_eq!(
5607        contacts(&client_a, cx_a),
5608        [
5609            ("user_b".to_string(), "online", "free"),
5610            ("user_c".to_string(), "online", "busy")
5611        ]
5612    );
5613    assert_eq!(
5614        contacts(&client_b, cx_b),
5615        [
5616            ("user_a".to_string(), "online", "busy"),
5617            ("user_c".to_string(), "online", "busy"),
5618            ("user_d".to_string(), "online", "free")
5619        ]
5620    );
5621    assert_eq!(
5622        contacts(&client_c, cx_c),
5623        [
5624            ("user_a".to_string(), "online", "busy"),
5625            ("user_b".to_string(), "online", "free")
5626        ]
5627    );
5628    assert_eq!(
5629        contacts(&client_d, cx_d),
5630        [("user_b".to_string(), "online", "free")]
5631    );
5632
5633    active_call_a
5634        .update(cx_a, |call, cx| call.accept_incoming(cx))
5635        .await
5636        .unwrap();
5637    executor.run_until_parked();
5638    assert_eq!(
5639        contacts(&client_a, cx_a),
5640        [
5641            ("user_b".to_string(), "online", "free"),
5642            ("user_c".to_string(), "online", "busy")
5643        ]
5644    );
5645    assert_eq!(
5646        contacts(&client_b, cx_b),
5647        [
5648            ("user_a".to_string(), "online", "busy"),
5649            ("user_c".to_string(), "online", "busy"),
5650            ("user_d".to_string(), "online", "free")
5651        ]
5652    );
5653    assert_eq!(
5654        contacts(&client_c, cx_c),
5655        [
5656            ("user_a".to_string(), "online", "busy"),
5657            ("user_b".to_string(), "online", "free")
5658        ]
5659    );
5660    assert_eq!(
5661        contacts(&client_d, cx_d),
5662        [("user_b".to_string(), "online", "free")]
5663    );
5664
5665    active_call_a
5666        .update(cx_a, |call, cx| {
5667            call.invite(client_b.user_id().unwrap(), None, cx)
5668        })
5669        .await
5670        .unwrap();
5671    executor.run_until_parked();
5672    assert_eq!(
5673        contacts(&client_a, cx_a),
5674        [
5675            ("user_b".to_string(), "online", "busy"),
5676            ("user_c".to_string(), "online", "busy")
5677        ]
5678    );
5679    assert_eq!(
5680        contacts(&client_b, cx_b),
5681        [
5682            ("user_a".to_string(), "online", "busy"),
5683            ("user_c".to_string(), "online", "busy"),
5684            ("user_d".to_string(), "online", "free")
5685        ]
5686    );
5687    assert_eq!(
5688        contacts(&client_c, cx_c),
5689        [
5690            ("user_a".to_string(), "online", "busy"),
5691            ("user_b".to_string(), "online", "busy")
5692        ]
5693    );
5694    assert_eq!(
5695        contacts(&client_d, cx_d),
5696        [("user_b".to_string(), "online", "busy")]
5697    );
5698
5699    active_call_a
5700        .update(cx_a, |call, cx| call.hang_up(cx))
5701        .await
5702        .unwrap();
5703    executor.run_until_parked();
5704    assert_eq!(
5705        contacts(&client_a, cx_a),
5706        [
5707            ("user_b".to_string(), "online", "free"),
5708            ("user_c".to_string(), "online", "free")
5709        ]
5710    );
5711    assert_eq!(
5712        contacts(&client_b, cx_b),
5713        [
5714            ("user_a".to_string(), "online", "free"),
5715            ("user_c".to_string(), "online", "free"),
5716            ("user_d".to_string(), "online", "free")
5717        ]
5718    );
5719    assert_eq!(
5720        contacts(&client_c, cx_c),
5721        [
5722            ("user_a".to_string(), "online", "free"),
5723            ("user_b".to_string(), "online", "free")
5724        ]
5725    );
5726    assert_eq!(
5727        contacts(&client_d, cx_d),
5728        [("user_b".to_string(), "online", "free")]
5729    );
5730
5731    active_call_a
5732        .update(cx_a, |call, cx| {
5733            call.invite(client_b.user_id().unwrap(), None, cx)
5734        })
5735        .await
5736        .unwrap();
5737    executor.run_until_parked();
5738    assert_eq!(
5739        contacts(&client_a, cx_a),
5740        [
5741            ("user_b".to_string(), "online", "busy"),
5742            ("user_c".to_string(), "online", "free")
5743        ]
5744    );
5745    assert_eq!(
5746        contacts(&client_b, cx_b),
5747        [
5748            ("user_a".to_string(), "online", "busy"),
5749            ("user_c".to_string(), "online", "free"),
5750            ("user_d".to_string(), "online", "free")
5751        ]
5752    );
5753    assert_eq!(
5754        contacts(&client_c, cx_c),
5755        [
5756            ("user_a".to_string(), "online", "busy"),
5757            ("user_b".to_string(), "online", "busy")
5758        ]
5759    );
5760    assert_eq!(
5761        contacts(&client_d, cx_d),
5762        [("user_b".to_string(), "online", "busy")]
5763    );
5764
5765    server.forbid_connections();
5766    server.disconnect_client(client_a.peer_id().unwrap());
5767    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5768    assert_eq!(contacts(&client_a, cx_a), []);
5769    assert_eq!(
5770        contacts(&client_b, cx_b),
5771        [
5772            ("user_a".to_string(), "offline", "free"),
5773            ("user_c".to_string(), "online", "free"),
5774            ("user_d".to_string(), "online", "free")
5775        ]
5776    );
5777    assert_eq!(
5778        contacts(&client_c, cx_c),
5779        [
5780            ("user_a".to_string(), "offline", "free"),
5781            ("user_b".to_string(), "online", "free")
5782        ]
5783    );
5784    assert_eq!(
5785        contacts(&client_d, cx_d),
5786        [("user_b".to_string(), "online", "free")]
5787    );
5788
5789    // Test removing a contact
5790    client_b
5791        .user_store()
5792        .update(cx_b, |store, cx| {
5793            store.remove_contact(client_c.user_id().unwrap(), cx)
5794        })
5795        .await
5796        .unwrap();
5797    executor.run_until_parked();
5798    assert_eq!(
5799        contacts(&client_b, cx_b),
5800        [
5801            ("user_a".to_string(), "offline", "free"),
5802            ("user_d".to_string(), "online", "free")
5803        ]
5804    );
5805    assert_eq!(
5806        contacts(&client_c, cx_c),
5807        [("user_a".to_string(), "offline", "free"),]
5808    );
5809
5810    fn contacts(
5811        client: &TestClient,
5812        cx: &TestAppContext,
5813    ) -> Vec<(String, &'static str, &'static str)> {
5814        client.user_store().read_with(cx, |store, _| {
5815            store
5816                .contacts()
5817                .iter()
5818                .map(|contact| {
5819                    (
5820                        contact.user.github_login.clone(),
5821                        if contact.online { "online" } else { "offline" },
5822                        if contact.busy { "busy" } else { "free" },
5823                    )
5824                })
5825                .collect()
5826        })
5827    }
5828}
5829
5830#[gpui::test(iterations = 10)]
5831async fn test_contact_requests(
5832    executor: BackgroundExecutor,
5833    cx_a: &mut TestAppContext,
5834    cx_a2: &mut TestAppContext,
5835    cx_b: &mut TestAppContext,
5836    cx_b2: &mut TestAppContext,
5837    cx_c: &mut TestAppContext,
5838    cx_c2: &mut TestAppContext,
5839) {
5840    // Connect to a server as 3 clients.
5841    let mut server = TestServer::start(executor.clone()).await;
5842    let client_a = server.create_client(cx_a, "user_a").await;
5843    let client_a2 = server.create_client(cx_a2, "user_a").await;
5844    let client_b = server.create_client(cx_b, "user_b").await;
5845    let client_b2 = server.create_client(cx_b2, "user_b").await;
5846    let client_c = server.create_client(cx_c, "user_c").await;
5847    let client_c2 = server.create_client(cx_c2, "user_c").await;
5848
5849    assert_eq!(client_a.user_id().unwrap(), client_a2.user_id().unwrap());
5850    assert_eq!(client_b.user_id().unwrap(), client_b2.user_id().unwrap());
5851    assert_eq!(client_c.user_id().unwrap(), client_c2.user_id().unwrap());
5852
5853    // User A and User C request that user B become their contact.
5854    client_a
5855        .user_store()
5856        .update(cx_a, |store, cx| {
5857            store.request_contact(client_b.user_id().unwrap(), cx)
5858        })
5859        .await
5860        .unwrap();
5861    client_c
5862        .user_store()
5863        .update(cx_c, |store, cx| {
5864            store.request_contact(client_b.user_id().unwrap(), cx)
5865        })
5866        .await
5867        .unwrap();
5868    executor.run_until_parked();
5869
5870    // All users see the pending request appear in all their clients.
5871    assert_eq!(
5872        client_a.summarize_contacts(cx_a).outgoing_requests,
5873        &["user_b"]
5874    );
5875    assert_eq!(
5876        client_a2.summarize_contacts(cx_a2).outgoing_requests,
5877        &["user_b"]
5878    );
5879    assert_eq!(
5880        client_b.summarize_contacts(cx_b).incoming_requests,
5881        &["user_a", "user_c"]
5882    );
5883    assert_eq!(
5884        client_b2.summarize_contacts(cx_b2).incoming_requests,
5885        &["user_a", "user_c"]
5886    );
5887    assert_eq!(
5888        client_c.summarize_contacts(cx_c).outgoing_requests,
5889        &["user_b"]
5890    );
5891    assert_eq!(
5892        client_c2.summarize_contacts(cx_c2).outgoing_requests,
5893        &["user_b"]
5894    );
5895
5896    // Contact requests are present upon connecting (tested here via disconnect/reconnect)
5897    disconnect_and_reconnect(&client_a, cx_a).await;
5898    disconnect_and_reconnect(&client_b, cx_b).await;
5899    disconnect_and_reconnect(&client_c, cx_c).await;
5900    executor.run_until_parked();
5901    assert_eq!(
5902        client_a.summarize_contacts(cx_a).outgoing_requests,
5903        &["user_b"]
5904    );
5905    assert_eq!(
5906        client_b.summarize_contacts(cx_b).incoming_requests,
5907        &["user_a", "user_c"]
5908    );
5909    assert_eq!(
5910        client_c.summarize_contacts(cx_c).outgoing_requests,
5911        &["user_b"]
5912    );
5913
5914    // User B accepts the request from user A.
5915    client_b
5916        .user_store()
5917        .update(cx_b, |store, cx| {
5918            store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
5919        })
5920        .await
5921        .unwrap();
5922
5923    executor.run_until_parked();
5924
5925    // User B sees user A as their contact now in all client, and the incoming request from them is removed.
5926    let contacts_b = client_b.summarize_contacts(cx_b);
5927    assert_eq!(contacts_b.current, &["user_a"]);
5928    assert_eq!(contacts_b.incoming_requests, &["user_c"]);
5929    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
5930    assert_eq!(contacts_b2.current, &["user_a"]);
5931    assert_eq!(contacts_b2.incoming_requests, &["user_c"]);
5932
5933    // User A sees user B as their contact now in all clients, and the outgoing request to them is removed.
5934    let contacts_a = client_a.summarize_contacts(cx_a);
5935    assert_eq!(contacts_a.current, &["user_b"]);
5936    assert!(contacts_a.outgoing_requests.is_empty());
5937    let contacts_a2 = client_a2.summarize_contacts(cx_a2);
5938    assert_eq!(contacts_a2.current, &["user_b"]);
5939    assert!(contacts_a2.outgoing_requests.is_empty());
5940
5941    // Contacts are present upon connecting (tested here via disconnect/reconnect)
5942    disconnect_and_reconnect(&client_a, cx_a).await;
5943    disconnect_and_reconnect(&client_b, cx_b).await;
5944    disconnect_and_reconnect(&client_c, cx_c).await;
5945    executor.run_until_parked();
5946    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
5947    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
5948    assert_eq!(
5949        client_b.summarize_contacts(cx_b).incoming_requests,
5950        &["user_c"]
5951    );
5952    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
5953    assert_eq!(
5954        client_c.summarize_contacts(cx_c).outgoing_requests,
5955        &["user_b"]
5956    );
5957
5958    // User B rejects the request from user C.
5959    client_b
5960        .user_store()
5961        .update(cx_b, |store, cx| {
5962            store.respond_to_contact_request(client_c.user_id().unwrap(), false, cx)
5963        })
5964        .await
5965        .unwrap();
5966
5967    executor.run_until_parked();
5968
5969    // User B doesn't see user C as their contact, and the incoming request from them is removed.
5970    let contacts_b = client_b.summarize_contacts(cx_b);
5971    assert_eq!(contacts_b.current, &["user_a"]);
5972    assert!(contacts_b.incoming_requests.is_empty());
5973    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
5974    assert_eq!(contacts_b2.current, &["user_a"]);
5975    assert!(contacts_b2.incoming_requests.is_empty());
5976
5977    // User C doesn't see user B as their contact, and the outgoing request to them is removed.
5978    let contacts_c = client_c.summarize_contacts(cx_c);
5979    assert!(contacts_c.current.is_empty());
5980    assert!(contacts_c.outgoing_requests.is_empty());
5981    let contacts_c2 = client_c2.summarize_contacts(cx_c2);
5982    assert!(contacts_c2.current.is_empty());
5983    assert!(contacts_c2.outgoing_requests.is_empty());
5984
5985    // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect)
5986    disconnect_and_reconnect(&client_a, cx_a).await;
5987    disconnect_and_reconnect(&client_b, cx_b).await;
5988    disconnect_and_reconnect(&client_c, cx_c).await;
5989    executor.run_until_parked();
5990    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
5991    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
5992    assert!(client_b
5993        .summarize_contacts(cx_b)
5994        .incoming_requests
5995        .is_empty());
5996    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
5997    assert!(client_c
5998        .summarize_contacts(cx_c)
5999        .outgoing_requests
6000        .is_empty());
6001
6002    async fn disconnect_and_reconnect(client: &TestClient, cx: &mut TestAppContext) {
6003        client.disconnect(&cx.to_async());
6004        client.clear_contacts(cx).await;
6005        client
6006            .authenticate_and_connect(false, &cx.to_async())
6007            .await
6008            .unwrap();
6009    }
6010}
6011
6012#[gpui::test(iterations = 10)]
6013async fn test_join_call_after_screen_was_shared(
6014    executor: BackgroundExecutor,
6015    cx_a: &mut TestAppContext,
6016    cx_b: &mut TestAppContext,
6017) {
6018    let mut server = TestServer::start(executor.clone()).await;
6019
6020    let client_a = server.create_client(cx_a, "user_a").await;
6021    let client_b = server.create_client(cx_b, "user_b").await;
6022    server
6023        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6024        .await;
6025
6026    let active_call_a = cx_a.read(ActiveCall::global);
6027    let active_call_b = cx_b.read(ActiveCall::global);
6028
6029    // Call users B and C from client A.
6030    active_call_a
6031        .update(cx_a, |call, cx| {
6032            call.invite(client_b.user_id().unwrap(), None, cx)
6033        })
6034        .await
6035        .unwrap();
6036
6037    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
6038    executor.run_until_parked();
6039    assert_eq!(
6040        room_participants(&room_a, cx_a),
6041        RoomParticipants {
6042            remote: Default::default(),
6043            pending: vec!["user_b".to_string()]
6044        }
6045    );
6046
6047    // User B receives the call.
6048
6049    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
6050    let call_b = incoming_call_b.next().await.unwrap().unwrap();
6051    assert_eq!(call_b.calling_user.github_login, "user_a");
6052
6053    // User A shares their screen
6054    let display = MacOSDisplay::new();
6055    active_call_a
6056        .update(cx_a, |call, cx| {
6057            call.room().unwrap().update(cx, |room, cx| {
6058                room.set_display_sources(vec![display.clone()]);
6059                room.share_screen(cx)
6060            })
6061        })
6062        .await
6063        .unwrap();
6064
6065    client_b.user_store().update(cx_b, |user_store, _| {
6066        user_store.clear_cache();
6067    });
6068
6069    // User B joins the room
6070    active_call_b
6071        .update(cx_b, |call, cx| call.accept_incoming(cx))
6072        .await
6073        .unwrap();
6074
6075    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
6076    assert!(incoming_call_b.next().await.unwrap().is_none());
6077
6078    executor.run_until_parked();
6079    assert_eq!(
6080        room_participants(&room_a, cx_a),
6081        RoomParticipants {
6082            remote: vec!["user_b".to_string()],
6083            pending: vec![],
6084        }
6085    );
6086    assert_eq!(
6087        room_participants(&room_b, cx_b),
6088        RoomParticipants {
6089            remote: vec!["user_a".to_string()],
6090            pending: vec![],
6091        }
6092    );
6093
6094    // Ensure User B sees User A's screenshare.
6095
6096    room_b.read_with(cx_b, |room, _| {
6097        assert_eq!(
6098            room.remote_participants()
6099                .get(&client_a.user_id().unwrap())
6100                .unwrap()
6101                .video_tracks
6102                .len(),
6103            1
6104        );
6105    });
6106}
6107
6108#[gpui::test]
6109async fn test_right_click_menu_behind_collab_panel(cx: &mut TestAppContext) {
6110    let mut server = TestServer::start(cx.executor().clone()).await;
6111    let client_a = server.create_client(cx, "user_a").await;
6112    let (_workspace_a, cx) = client_a.build_test_workspace(cx).await;
6113
6114    cx.simulate_resize(size(px(300.), px(300.)));
6115
6116    cx.simulate_keystrokes("cmd-n cmd-n cmd-n");
6117    cx.update(|cx| cx.refresh());
6118
6119    let tab_bounds = cx.debug_bounds("TAB-2").unwrap();
6120    let new_tab_button_bounds = cx.debug_bounds("ICON-Plus").unwrap();
6121
6122    assert!(
6123        tab_bounds.intersects(&new_tab_button_bounds),
6124        "Tab should overlap with the new tab button, if this is failing check if there's been a redesign!"
6125    );
6126
6127    cx.simulate_event(MouseDownEvent {
6128        button: MouseButton::Right,
6129        position: new_tab_button_bounds.center(),
6130        modifiers: Modifiers::default(),
6131        click_count: 1,
6132        first_mouse: false,
6133    });
6134
6135    // regression test that the right click menu for tabs does not open.
6136    assert!(cx.debug_bounds("MENU_ITEM-Close").is_none());
6137
6138    let tab_bounds = cx.debug_bounds("TAB-1").unwrap();
6139    cx.simulate_event(MouseDownEvent {
6140        button: MouseButton::Right,
6141        position: tab_bounds.center(),
6142        modifiers: Modifiers::default(),
6143        click_count: 1,
6144        first_mouse: false,
6145    });
6146    assert!(cx.debug_bounds("MENU_ITEM-Close").is_some());
6147}
6148
6149#[gpui::test]
6150async fn test_pane_split_left(cx: &mut TestAppContext) {
6151    let (_, client) = TestServer::start1(cx).await;
6152    let (workspace, cx) = client.build_test_workspace(cx).await;
6153
6154    cx.simulate_keystrokes("cmd-n");
6155    workspace.update(cx, |workspace, cx| {
6156        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 1);
6157    });
6158    cx.simulate_keystrokes("cmd-k left");
6159    workspace.update(cx, |workspace, cx| {
6160        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 2);
6161    });
6162    cx.simulate_keystrokes("cmd-k");
6163    // sleep for longer than the timeout in keyboard shortcut handling
6164    // to verify that it doesn't fire in this case.
6165    cx.executor().advance_clock(Duration::from_secs(2));
6166    cx.simulate_keystrokes("left");
6167    workspace.update(cx, |workspace, cx| {
6168        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 2);
6169    });
6170}
6171
6172#[gpui::test]
6173async fn test_join_after_restart(cx1: &mut TestAppContext, cx2: &mut TestAppContext) {
6174    let (mut server, client) = TestServer::start1(cx1).await;
6175    let channel1 = server.make_public_channel("channel1", &client, cx1).await;
6176    let channel2 = server.make_public_channel("channel2", &client, cx1).await;
6177
6178    join_channel(channel1, &client, cx1).await.unwrap();
6179    drop(client);
6180
6181    let client2 = server.create_client(cx2, "user_a").await;
6182    join_channel(channel2, &client2, cx2).await.unwrap();
6183}
6184
6185#[gpui::test]
6186async fn test_preview_tabs(cx: &mut TestAppContext) {
6187    let (_server, client) = TestServer::start1(cx).await;
6188    let (workspace, cx) = client.build_test_workspace(cx).await;
6189    let project = workspace.update(cx, |workspace, _| workspace.project().clone());
6190
6191    let worktree_id = project.update(cx, |project, cx| {
6192        project.worktrees(cx).next().unwrap().read(cx).id()
6193    });
6194
6195    let path_1 = ProjectPath {
6196        worktree_id,
6197        path: Path::new("1.txt").into(),
6198    };
6199    let path_2 = ProjectPath {
6200        worktree_id,
6201        path: Path::new("2.js").into(),
6202    };
6203    let path_3 = ProjectPath {
6204        worktree_id,
6205        path: Path::new("3.rs").into(),
6206    };
6207
6208    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
6209
6210    let get_path = |pane: &Pane, idx: usize, cx: &AppContext| {
6211        pane.item_for_index(idx).unwrap().project_path(cx).unwrap()
6212    };
6213
6214    // Opening item 3 as a "permanent" tab
6215    workspace
6216        .update(cx, |workspace, cx| {
6217            workspace.open_path(path_3.clone(), None, false, cx)
6218        })
6219        .await
6220        .unwrap();
6221
6222    pane.update(cx, |pane, cx| {
6223        assert_eq!(pane.items_len(), 1);
6224        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6225        assert_eq!(pane.preview_item_id(), None);
6226
6227        assert!(!pane.can_navigate_backward());
6228        assert!(!pane.can_navigate_forward());
6229    });
6230
6231    // Open item 1 as preview
6232    workspace
6233        .update(cx, |workspace, cx| {
6234            workspace.open_path_preview(path_1.clone(), None, true, true, cx)
6235        })
6236        .await
6237        .unwrap();
6238
6239    pane.update(cx, |pane, cx| {
6240        assert_eq!(pane.items_len(), 2);
6241        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6242        assert_eq!(get_path(pane, 1, cx), path_1.clone());
6243        assert_eq!(
6244            pane.preview_item_id(),
6245            Some(pane.items().nth(1).unwrap().item_id())
6246        );
6247
6248        assert!(pane.can_navigate_backward());
6249        assert!(!pane.can_navigate_forward());
6250    });
6251
6252    // Open item 2 as preview
6253    workspace
6254        .update(cx, |workspace, cx| {
6255            workspace.open_path_preview(path_2.clone(), None, true, true, cx)
6256        })
6257        .await
6258        .unwrap();
6259
6260    pane.update(cx, |pane, cx| {
6261        assert_eq!(pane.items_len(), 2);
6262        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6263        assert_eq!(get_path(pane, 1, cx), path_2.clone());
6264        assert_eq!(
6265            pane.preview_item_id(),
6266            Some(pane.items().nth(1).unwrap().item_id())
6267        );
6268
6269        assert!(pane.can_navigate_backward());
6270        assert!(!pane.can_navigate_forward());
6271    });
6272
6273    // Going back should show item 1 as preview
6274    workspace
6275        .update(cx, |workspace, cx| workspace.go_back(pane.downgrade(), cx))
6276        .await
6277        .unwrap();
6278
6279    pane.update(cx, |pane, cx| {
6280        assert_eq!(pane.items_len(), 2);
6281        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6282        assert_eq!(get_path(pane, 1, cx), path_1.clone());
6283        assert_eq!(
6284            pane.preview_item_id(),
6285            Some(pane.items().nth(1).unwrap().item_id())
6286        );
6287
6288        assert!(pane.can_navigate_backward());
6289        assert!(pane.can_navigate_forward());
6290    });
6291
6292    // Closing item 1
6293    pane.update(cx, |pane, cx| {
6294        pane.close_item_by_id(
6295            pane.active_item().unwrap().item_id(),
6296            workspace::SaveIntent::Skip,
6297            cx,
6298        )
6299    })
6300    .await
6301    .unwrap();
6302
6303    pane.update(cx, |pane, cx| {
6304        assert_eq!(pane.items_len(), 1);
6305        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6306        assert_eq!(pane.preview_item_id(), None);
6307
6308        assert!(pane.can_navigate_backward());
6309        assert!(!pane.can_navigate_forward());
6310    });
6311
6312    // Going back should show item 1 as preview
6313    workspace
6314        .update(cx, |workspace, cx| workspace.go_back(pane.downgrade(), cx))
6315        .await
6316        .unwrap();
6317
6318    pane.update(cx, |pane, cx| {
6319        assert_eq!(pane.items_len(), 2);
6320        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6321        assert_eq!(get_path(pane, 1, cx), path_1.clone());
6322        assert_eq!(
6323            pane.preview_item_id(),
6324            Some(pane.items().nth(1).unwrap().item_id())
6325        );
6326
6327        assert!(pane.can_navigate_backward());
6328        assert!(pane.can_navigate_forward());
6329    });
6330
6331    // Close permanent tab
6332    pane.update(cx, |pane, cx| {
6333        let id = pane.items().next().unwrap().item_id();
6334        pane.close_item_by_id(id, workspace::SaveIntent::Skip, cx)
6335    })
6336    .await
6337    .unwrap();
6338
6339    pane.update(cx, |pane, cx| {
6340        assert_eq!(pane.items_len(), 1);
6341        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6342        assert_eq!(
6343            pane.preview_item_id(),
6344            Some(pane.items().next().unwrap().item_id())
6345        );
6346
6347        assert!(pane.can_navigate_backward());
6348        assert!(pane.can_navigate_forward());
6349    });
6350
6351    // Split pane to the right
6352    pane.update(cx, |pane, cx| {
6353        pane.split(workspace::SplitDirection::Right, cx);
6354    });
6355
6356    let right_pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
6357
6358    pane.update(cx, |pane, cx| {
6359        assert_eq!(pane.items_len(), 1);
6360        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6361        assert_eq!(
6362            pane.preview_item_id(),
6363            Some(pane.items().next().unwrap().item_id())
6364        );
6365
6366        assert!(pane.can_navigate_backward());
6367        assert!(pane.can_navigate_forward());
6368    });
6369
6370    right_pane.update(cx, |pane, cx| {
6371        assert_eq!(pane.items_len(), 1);
6372        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6373        assert_eq!(pane.preview_item_id(), None);
6374
6375        assert!(!pane.can_navigate_backward());
6376        assert!(!pane.can_navigate_forward());
6377    });
6378
6379    // Open item 2 as preview in right pane
6380    workspace
6381        .update(cx, |workspace, cx| {
6382            workspace.open_path_preview(path_2.clone(), None, true, true, cx)
6383        })
6384        .await
6385        .unwrap();
6386
6387    pane.update(cx, |pane, cx| {
6388        assert_eq!(pane.items_len(), 1);
6389        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6390        assert_eq!(
6391            pane.preview_item_id(),
6392            Some(pane.items().next().unwrap().item_id())
6393        );
6394
6395        assert!(pane.can_navigate_backward());
6396        assert!(pane.can_navigate_forward());
6397    });
6398
6399    right_pane.update(cx, |pane, cx| {
6400        assert_eq!(pane.items_len(), 2);
6401        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6402        assert_eq!(get_path(pane, 1, cx), path_2.clone());
6403        assert_eq!(
6404            pane.preview_item_id(),
6405            Some(pane.items().nth(1).unwrap().item_id())
6406        );
6407
6408        assert!(pane.can_navigate_backward());
6409        assert!(!pane.can_navigate_forward());
6410    });
6411
6412    // Focus left pane
6413    workspace.update(cx, |workspace, cx| {
6414        workspace.activate_pane_in_direction(workspace::SplitDirection::Left, cx)
6415    });
6416
6417    // Open item 2 as preview in left pane
6418    workspace
6419        .update(cx, |workspace, cx| {
6420            workspace.open_path_preview(path_2.clone(), None, true, true, cx)
6421        })
6422        .await
6423        .unwrap();
6424
6425    pane.update(cx, |pane, cx| {
6426        assert_eq!(pane.items_len(), 1);
6427        assert_eq!(get_path(pane, 0, cx), path_2.clone());
6428        assert_eq!(
6429            pane.preview_item_id(),
6430            Some(pane.items().next().unwrap().item_id())
6431        );
6432
6433        assert!(pane.can_navigate_backward());
6434        assert!(!pane.can_navigate_forward());
6435    });
6436
6437    right_pane.update(cx, |pane, cx| {
6438        assert_eq!(pane.items_len(), 2);
6439        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6440        assert_eq!(get_path(pane, 1, cx), path_2.clone());
6441        assert_eq!(
6442            pane.preview_item_id(),
6443            Some(pane.items().nth(1).unwrap().item_id())
6444        );
6445
6446        assert!(pane.can_navigate_backward());
6447        assert!(!pane.can_navigate_forward());
6448    });
6449}
6450
6451#[gpui::test(iterations = 10)]
6452async fn test_context_collaboration_with_reconnect(
6453    executor: BackgroundExecutor,
6454    cx_a: &mut TestAppContext,
6455    cx_b: &mut TestAppContext,
6456) {
6457    let mut server = TestServer::start(executor.clone()).await;
6458    let client_a = server.create_client(cx_a, "user_a").await;
6459    let client_b = server.create_client(cx_b, "user_b").await;
6460    server
6461        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6462        .await;
6463    let active_call_a = cx_a.read(ActiveCall::global);
6464
6465    client_a.fs().insert_tree("/a", Default::default()).await;
6466    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
6467    let project_id = active_call_a
6468        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6469        .await
6470        .unwrap();
6471    let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
6472
6473    // Client A sees that a guest has joined.
6474    executor.run_until_parked();
6475
6476    project_a.read_with(cx_a, |project, _| {
6477        assert_eq!(project.collaborators().len(), 1);
6478    });
6479    project_b.read_with(cx_b, |project, _| {
6480        assert_eq!(project.collaborators().len(), 1);
6481    });
6482
6483    let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
6484    let context_store_a = cx_a
6485        .update(|cx| ContextStore::new(project_a.clone(), prompt_builder.clone(), cx))
6486        .await
6487        .unwrap();
6488    let context_store_b = cx_b
6489        .update(|cx| ContextStore::new(project_b.clone(), prompt_builder.clone(), cx))
6490        .await
6491        .unwrap();
6492
6493    // Client A creates a new context.
6494    let context_a = context_store_a.update(cx_a, |store, cx| store.create(cx));
6495    executor.run_until_parked();
6496
6497    // Client B retrieves host's contexts and joins one.
6498    let context_b = context_store_b
6499        .update(cx_b, |store, cx| {
6500            let host_contexts = store.host_contexts().to_vec();
6501            assert_eq!(host_contexts.len(), 1);
6502            store.open_remote_context(host_contexts[0].id.clone(), cx)
6503        })
6504        .await
6505        .unwrap();
6506
6507    // Host and guest make changes
6508    context_a.update(cx_a, |context, cx| {
6509        context.buffer().update(cx, |buffer, cx| {
6510            buffer.edit([(0..0, "Host change\n")], None, cx)
6511        })
6512    });
6513    context_b.update(cx_b, |context, cx| {
6514        context.buffer().update(cx, |buffer, cx| {
6515            buffer.edit([(0..0, "Guest change\n")], None, cx)
6516        })
6517    });
6518    executor.run_until_parked();
6519    assert_eq!(
6520        context_a.read_with(cx_a, |context, cx| context.buffer().read(cx).text()),
6521        "Guest change\nHost change\n"
6522    );
6523    assert_eq!(
6524        context_b.read_with(cx_b, |context, cx| context.buffer().read(cx).text()),
6525        "Guest change\nHost change\n"
6526    );
6527
6528    // Disconnect client A and make some changes while disconnected.
6529    server.disconnect_client(client_a.peer_id().unwrap());
6530    server.forbid_connections();
6531    context_a.update(cx_a, |context, cx| {
6532        context.buffer().update(cx, |buffer, cx| {
6533            buffer.edit([(0..0, "Host offline change\n")], None, cx)
6534        })
6535    });
6536    context_b.update(cx_b, |context, cx| {
6537        context.buffer().update(cx, |buffer, cx| {
6538            buffer.edit([(0..0, "Guest offline change\n")], None, cx)
6539        })
6540    });
6541    executor.run_until_parked();
6542    assert_eq!(
6543        context_a.read_with(cx_a, |context, cx| context.buffer().read(cx).text()),
6544        "Host offline change\nGuest change\nHost change\n"
6545    );
6546    assert_eq!(
6547        context_b.read_with(cx_b, |context, cx| context.buffer().read(cx).text()),
6548        "Guest offline change\nGuest change\nHost change\n"
6549    );
6550
6551    // Allow client A to reconnect and verify that contexts converge.
6552    server.allow_connections();
6553    executor.advance_clock(RECEIVE_TIMEOUT);
6554    assert_eq!(
6555        context_a.read_with(cx_a, |context, cx| context.buffer().read(cx).text()),
6556        "Guest offline change\nHost offline change\nGuest change\nHost change\n"
6557    );
6558    assert_eq!(
6559        context_b.read_with(cx_b, |context, cx| context.buffer().read(cx).text()),
6560        "Guest offline change\nHost offline change\nGuest change\nHost change\n"
6561    );
6562
6563    // Client A disconnects without being able to reconnect. Context B becomes readonly.
6564    server.forbid_connections();
6565    server.disconnect_client(client_a.peer_id().unwrap());
6566    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
6567    context_b.read_with(cx_b, |context, cx| {
6568        assert!(context.buffer().read(cx).read_only());
6569    });
6570}