integration_tests.rs

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