integration_tests.rs

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