integration_tests.rs

   1use crate::{
   2    rpc::{CLEANUP_TIMEOUT, RECONNECT_TIMEOUT},
   3    tests::{
   4        channel_id, following_tests::join_channel, room_participants, rust_lang, RoomParticipants,
   5        TestClient, TestServer,
   6    },
   7};
   8use anyhow::{anyhow, Result};
   9use assistant::{ContextStore, PromptBuilder};
  10use call::{room, ActiveCall, ParticipantLocation, Room};
  11use client::{User, RECEIVE_TIMEOUT};
  12use collections::{HashMap, HashSet};
  13use fs::{FakeFs, Fs as _, RemoveOptions};
  14use futures::{channel::mpsc, StreamExt as _};
  15use git::repository::GitFileStatus;
  16use gpui::{
  17    px, size, AppContext, BackgroundExecutor, Model, Modifiers, MouseButton, MouseDownEvent,
  18    TestAppContext, UpdateGlobal,
  19};
  20use language::{
  21    language_settings::{
  22        AllLanguageSettings, Formatter, FormatterList, PrettierSettings, SelectedFormatter,
  23    },
  24    tree_sitter_rust, 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        let new_server_name = new_server_name.to_string();
5137        match new_server_name.as_str() {
5138            "CrabLang-ls" => {
5139                servers_with_hover_requests.insert(
5140                    new_server_name.clone(),
5141                    new_server.handle_request::<lsp::request::HoverRequest, _, _>(
5142                        move |params, _| {
5143                            assert_eq!(
5144                                params
5145                                    .text_document_position_params
5146                                    .text_document
5147                                    .uri
5148                                    .as_str(),
5149                                "file:///root-1/main.rs"
5150                            );
5151                            let name = new_server_name.clone();
5152                            async move {
5153                                Ok(Some(lsp::Hover {
5154                                    contents: lsp::HoverContents::Scalar(
5155                                        lsp::MarkedString::String(format!("{name} hover")),
5156                                    ),
5157                                    range: None,
5158                                }))
5159                            }
5160                        },
5161                    ),
5162                );
5163            }
5164            "rust-analyzer" => {
5165                servers_with_hover_requests.insert(
5166                    new_server_name.clone(),
5167                    new_server.handle_request::<lsp::request::HoverRequest, _, _>(
5168                        |params, _| async move {
5169                            assert_eq!(
5170                                params
5171                                    .text_document_position_params
5172                                    .text_document
5173                                    .uri
5174                                    .as_str(),
5175                                "file:///root-1/main.rs"
5176                            );
5177                            assert_eq!(
5178                                params.text_document_position_params.position,
5179                                lsp::Position::new(0, 22)
5180                            );
5181                            Ok(Some(lsp::Hover {
5182                                contents: lsp::HoverContents::Array(vec![
5183                                    lsp::MarkedString::String("Test hover content.".to_string()),
5184                                    lsp::MarkedString::LanguageString(lsp::LanguageString {
5185                                        language: "Rust".to_string(),
5186                                        value: "let foo = 42;".to_string(),
5187                                    }),
5188                                ]),
5189                                range: Some(lsp::Range::new(
5190                                    lsp::Position::new(0, 22),
5191                                    lsp::Position::new(0, 29),
5192                                )),
5193                            }))
5194                        },
5195                    ),
5196                );
5197            }
5198            unexpected => panic!("Unexpected server name: {unexpected}"),
5199        }
5200    }
5201
5202    // Request hover information as the guest.
5203    let mut hovers = project_b
5204        .update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
5205        .await;
5206    assert_eq!(
5207        hovers.len(),
5208        2,
5209        "Expected two hovers from both language servers, but got: {hovers:?}"
5210    );
5211
5212    let _: Vec<()> = futures::future::join_all(servers_with_hover_requests.into_values().map(
5213        |mut hover_request| async move {
5214            hover_request
5215                .next()
5216                .await
5217                .expect("All hover requests should have been triggered")
5218        },
5219    ))
5220    .await;
5221
5222    hovers.sort_by_key(|hover| hover.contents.len());
5223    let first_hover = hovers.first().cloned().unwrap();
5224    assert_eq!(
5225        first_hover.contents,
5226        vec![project::HoverBlock {
5227            text: "CrabLang-ls hover".to_string(),
5228            kind: HoverBlockKind::Markdown,
5229        },]
5230    );
5231    let second_hover = hovers.last().cloned().unwrap();
5232    assert_eq!(
5233        second_hover.contents,
5234        vec![
5235            project::HoverBlock {
5236                text: "Test hover content.".to_string(),
5237                kind: HoverBlockKind::Markdown,
5238            },
5239            project::HoverBlock {
5240                text: "let foo = 42;".to_string(),
5241                kind: HoverBlockKind::Code {
5242                    language: "Rust".to_string()
5243                },
5244            }
5245        ]
5246    );
5247    buffer_b.read_with(cx_b, |buffer, _| {
5248        let snapshot = buffer.snapshot();
5249        assert_eq!(second_hover.range.unwrap().to_offset(&snapshot), 22..29);
5250    });
5251}
5252
5253#[gpui::test(iterations = 10)]
5254async fn test_project_symbols(
5255    executor: BackgroundExecutor,
5256    cx_a: &mut TestAppContext,
5257    cx_b: &mut TestAppContext,
5258) {
5259    let mut server = TestServer::start(executor.clone()).await;
5260    let client_a = server.create_client(cx_a, "user_a").await;
5261    let client_b = server.create_client(cx_b, "user_b").await;
5262    server
5263        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5264        .await;
5265    let active_call_a = cx_a.read(ActiveCall::global);
5266
5267    client_a.language_registry().add(rust_lang());
5268    let mut fake_language_servers = client_a
5269        .language_registry()
5270        .register_fake_lsp("Rust", Default::default());
5271
5272    client_a
5273        .fs()
5274        .insert_tree(
5275            "/code",
5276            json!({
5277                "crate-1": {
5278                    "one.rs": "const ONE: usize = 1;",
5279                },
5280                "crate-2": {
5281                    "two.rs": "const TWO: usize = 2; const THREE: usize = 3;",
5282                },
5283                "private": {
5284                    "passwords.txt": "the-password",
5285                }
5286            }),
5287        )
5288        .await;
5289    let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await;
5290    let project_id = active_call_a
5291        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5292        .await
5293        .unwrap();
5294    let project_b = client_b.join_remote_project(project_id, cx_b).await;
5295
5296    // Cause the language server to start.
5297    let open_buffer_task =
5298        project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx));
5299    let _buffer = cx_b.executor().spawn(open_buffer_task).await.unwrap();
5300
5301    let fake_language_server = fake_language_servers.next().await.unwrap();
5302    fake_language_server.handle_request::<lsp::WorkspaceSymbolRequest, _, _>(|_, _| async move {
5303        Ok(Some(lsp::WorkspaceSymbolResponse::Flat(vec![
5304            #[allow(deprecated)]
5305            lsp::SymbolInformation {
5306                name: "TWO".into(),
5307                location: lsp::Location {
5308                    uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
5309                    range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5310                },
5311                kind: lsp::SymbolKind::CONSTANT,
5312                tags: None,
5313                container_name: None,
5314                deprecated: None,
5315            },
5316        ])))
5317    });
5318
5319    // Request the definition of a symbol as the guest.
5320    let symbols = project_b
5321        .update(cx_b, |p, cx| p.symbols("two", cx))
5322        .await
5323        .unwrap();
5324    assert_eq!(symbols.len(), 1);
5325    assert_eq!(symbols[0].name, "TWO");
5326
5327    // Open one of the returned symbols.
5328    let buffer_b_2 = project_b
5329        .update(cx_b, |project, cx| {
5330            project.open_buffer_for_symbol(&symbols[0], cx)
5331        })
5332        .await
5333        .unwrap();
5334
5335    buffer_b_2.read_with(cx_b, |buffer, cx| {
5336        assert_eq!(
5337            buffer.file().unwrap().full_path(cx),
5338            Path::new("/code/crate-2/two.rs")
5339        );
5340    });
5341
5342    // Attempt to craft a symbol and violate host's privacy by opening an arbitrary file.
5343    let mut fake_symbol = symbols[0].clone();
5344    fake_symbol.path.path = Path::new("/code/secrets").into();
5345    let error = project_b
5346        .update(cx_b, |project, cx| {
5347            project.open_buffer_for_symbol(&fake_symbol, cx)
5348        })
5349        .await
5350        .unwrap_err();
5351    assert!(error.to_string().contains("invalid symbol signature"));
5352}
5353
5354#[gpui::test(iterations = 10)]
5355async fn test_open_buffer_while_getting_definition_pointing_to_it(
5356    executor: BackgroundExecutor,
5357    cx_a: &mut TestAppContext,
5358    cx_b: &mut TestAppContext,
5359    mut rng: StdRng,
5360) {
5361    let mut server = TestServer::start(executor.clone()).await;
5362    let client_a = server.create_client(cx_a, "user_a").await;
5363    let client_b = server.create_client(cx_b, "user_b").await;
5364    server
5365        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5366        .await;
5367    let active_call_a = cx_a.read(ActiveCall::global);
5368
5369    client_a.language_registry().add(rust_lang());
5370    let mut fake_language_servers = client_a
5371        .language_registry()
5372        .register_fake_lsp("Rust", Default::default());
5373
5374    client_a
5375        .fs()
5376        .insert_tree(
5377            "/root",
5378            json!({
5379                "a.rs": "const ONE: usize = b::TWO;",
5380                "b.rs": "const TWO: usize = 2",
5381            }),
5382        )
5383        .await;
5384    let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await;
5385    let project_id = active_call_a
5386        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5387        .await
5388        .unwrap();
5389    let project_b = client_b.join_remote_project(project_id, cx_b).await;
5390
5391    let open_buffer_task = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
5392    let buffer_b1 = cx_b.executor().spawn(open_buffer_task).await.unwrap();
5393
5394    let fake_language_server = fake_language_servers.next().await.unwrap();
5395    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
5396        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
5397            lsp::Location::new(
5398                lsp::Url::from_file_path("/root/b.rs").unwrap(),
5399                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5400            ),
5401        )))
5402    });
5403
5404    let definitions;
5405    let buffer_b2;
5406    if rng.gen() {
5407        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5408        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5409    } else {
5410        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5411        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5412    }
5413
5414    let buffer_b2 = buffer_b2.await.unwrap();
5415    let definitions = definitions.await.unwrap();
5416    assert_eq!(definitions.len(), 1);
5417    assert_eq!(definitions[0].target.buffer, buffer_b2);
5418}
5419
5420#[gpui::test(iterations = 10)]
5421async fn test_contacts(
5422    executor: BackgroundExecutor,
5423    cx_a: &mut TestAppContext,
5424    cx_b: &mut TestAppContext,
5425    cx_c: &mut TestAppContext,
5426    cx_d: &mut TestAppContext,
5427) {
5428    let mut server = TestServer::start(executor.clone()).await;
5429    let client_a = server.create_client(cx_a, "user_a").await;
5430    let client_b = server.create_client(cx_b, "user_b").await;
5431    let client_c = server.create_client(cx_c, "user_c").await;
5432    let client_d = server.create_client(cx_d, "user_d").await;
5433    server
5434        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
5435        .await;
5436    let active_call_a = cx_a.read(ActiveCall::global);
5437    let active_call_b = cx_b.read(ActiveCall::global);
5438    let active_call_c = cx_c.read(ActiveCall::global);
5439    let _active_call_d = cx_d.read(ActiveCall::global);
5440
5441    executor.run_until_parked();
5442    assert_eq!(
5443        contacts(&client_a, cx_a),
5444        [
5445            ("user_b".to_string(), "online", "free"),
5446            ("user_c".to_string(), "online", "free")
5447        ]
5448    );
5449    assert_eq!(
5450        contacts(&client_b, cx_b),
5451        [
5452            ("user_a".to_string(), "online", "free"),
5453            ("user_c".to_string(), "online", "free")
5454        ]
5455    );
5456    assert_eq!(
5457        contacts(&client_c, cx_c),
5458        [
5459            ("user_a".to_string(), "online", "free"),
5460            ("user_b".to_string(), "online", "free")
5461        ]
5462    );
5463    assert_eq!(contacts(&client_d, cx_d), []);
5464
5465    server.disconnect_client(client_c.peer_id().unwrap());
5466    server.forbid_connections();
5467    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5468    assert_eq!(
5469        contacts(&client_a, cx_a),
5470        [
5471            ("user_b".to_string(), "online", "free"),
5472            ("user_c".to_string(), "offline", "free")
5473        ]
5474    );
5475    assert_eq!(
5476        contacts(&client_b, cx_b),
5477        [
5478            ("user_a".to_string(), "online", "free"),
5479            ("user_c".to_string(), "offline", "free")
5480        ]
5481    );
5482    assert_eq!(contacts(&client_c, cx_c), []);
5483    assert_eq!(contacts(&client_d, cx_d), []);
5484
5485    server.allow_connections();
5486    client_c
5487        .authenticate_and_connect(false, &cx_c.to_async())
5488        .await
5489        .unwrap();
5490
5491    executor.run_until_parked();
5492    assert_eq!(
5493        contacts(&client_a, cx_a),
5494        [
5495            ("user_b".to_string(), "online", "free"),
5496            ("user_c".to_string(), "online", "free")
5497        ]
5498    );
5499    assert_eq!(
5500        contacts(&client_b, cx_b),
5501        [
5502            ("user_a".to_string(), "online", "free"),
5503            ("user_c".to_string(), "online", "free")
5504        ]
5505    );
5506    assert_eq!(
5507        contacts(&client_c, cx_c),
5508        [
5509            ("user_a".to_string(), "online", "free"),
5510            ("user_b".to_string(), "online", "free")
5511        ]
5512    );
5513    assert_eq!(contacts(&client_d, cx_d), []);
5514
5515    active_call_a
5516        .update(cx_a, |call, cx| {
5517            call.invite(client_b.user_id().unwrap(), None, cx)
5518        })
5519        .await
5520        .unwrap();
5521    executor.run_until_parked();
5522    assert_eq!(
5523        contacts(&client_a, cx_a),
5524        [
5525            ("user_b".to_string(), "online", "busy"),
5526            ("user_c".to_string(), "online", "free")
5527        ]
5528    );
5529    assert_eq!(
5530        contacts(&client_b, cx_b),
5531        [
5532            ("user_a".to_string(), "online", "busy"),
5533            ("user_c".to_string(), "online", "free")
5534        ]
5535    );
5536    assert_eq!(
5537        contacts(&client_c, cx_c),
5538        [
5539            ("user_a".to_string(), "online", "busy"),
5540            ("user_b".to_string(), "online", "busy")
5541        ]
5542    );
5543    assert_eq!(contacts(&client_d, cx_d), []);
5544
5545    // Client B and client D become contacts while client B is being called.
5546    server
5547        .make_contacts(&mut [(&client_b, cx_b), (&client_d, cx_d)])
5548        .await;
5549    executor.run_until_parked();
5550    assert_eq!(
5551        contacts(&client_a, cx_a),
5552        [
5553            ("user_b".to_string(), "online", "busy"),
5554            ("user_c".to_string(), "online", "free")
5555        ]
5556    );
5557    assert_eq!(
5558        contacts(&client_b, cx_b),
5559        [
5560            ("user_a".to_string(), "online", "busy"),
5561            ("user_c".to_string(), "online", "free"),
5562            ("user_d".to_string(), "online", "free"),
5563        ]
5564    );
5565    assert_eq!(
5566        contacts(&client_c, cx_c),
5567        [
5568            ("user_a".to_string(), "online", "busy"),
5569            ("user_b".to_string(), "online", "busy")
5570        ]
5571    );
5572    assert_eq!(
5573        contacts(&client_d, cx_d),
5574        [("user_b".to_string(), "online", "busy")]
5575    );
5576
5577    active_call_b.update(cx_b, |call, cx| call.decline_incoming(cx).unwrap());
5578    executor.run_until_parked();
5579    assert_eq!(
5580        contacts(&client_a, cx_a),
5581        [
5582            ("user_b".to_string(), "online", "free"),
5583            ("user_c".to_string(), "online", "free")
5584        ]
5585    );
5586    assert_eq!(
5587        contacts(&client_b, cx_b),
5588        [
5589            ("user_a".to_string(), "online", "free"),
5590            ("user_c".to_string(), "online", "free"),
5591            ("user_d".to_string(), "online", "free")
5592        ]
5593    );
5594    assert_eq!(
5595        contacts(&client_c, cx_c),
5596        [
5597            ("user_a".to_string(), "online", "free"),
5598            ("user_b".to_string(), "online", "free")
5599        ]
5600    );
5601    assert_eq!(
5602        contacts(&client_d, cx_d),
5603        [("user_b".to_string(), "online", "free")]
5604    );
5605
5606    active_call_c
5607        .update(cx_c, |call, cx| {
5608            call.invite(client_a.user_id().unwrap(), None, cx)
5609        })
5610        .await
5611        .unwrap();
5612    executor.run_until_parked();
5613    assert_eq!(
5614        contacts(&client_a, cx_a),
5615        [
5616            ("user_b".to_string(), "online", "free"),
5617            ("user_c".to_string(), "online", "busy")
5618        ]
5619    );
5620    assert_eq!(
5621        contacts(&client_b, cx_b),
5622        [
5623            ("user_a".to_string(), "online", "busy"),
5624            ("user_c".to_string(), "online", "busy"),
5625            ("user_d".to_string(), "online", "free")
5626        ]
5627    );
5628    assert_eq!(
5629        contacts(&client_c, cx_c),
5630        [
5631            ("user_a".to_string(), "online", "busy"),
5632            ("user_b".to_string(), "online", "free")
5633        ]
5634    );
5635    assert_eq!(
5636        contacts(&client_d, cx_d),
5637        [("user_b".to_string(), "online", "free")]
5638    );
5639
5640    active_call_a
5641        .update(cx_a, |call, cx| call.accept_incoming(cx))
5642        .await
5643        .unwrap();
5644    executor.run_until_parked();
5645    assert_eq!(
5646        contacts(&client_a, cx_a),
5647        [
5648            ("user_b".to_string(), "online", "free"),
5649            ("user_c".to_string(), "online", "busy")
5650        ]
5651    );
5652    assert_eq!(
5653        contacts(&client_b, cx_b),
5654        [
5655            ("user_a".to_string(), "online", "busy"),
5656            ("user_c".to_string(), "online", "busy"),
5657            ("user_d".to_string(), "online", "free")
5658        ]
5659    );
5660    assert_eq!(
5661        contacts(&client_c, cx_c),
5662        [
5663            ("user_a".to_string(), "online", "busy"),
5664            ("user_b".to_string(), "online", "free")
5665        ]
5666    );
5667    assert_eq!(
5668        contacts(&client_d, cx_d),
5669        [("user_b".to_string(), "online", "free")]
5670    );
5671
5672    active_call_a
5673        .update(cx_a, |call, cx| {
5674            call.invite(client_b.user_id().unwrap(), None, cx)
5675        })
5676        .await
5677        .unwrap();
5678    executor.run_until_parked();
5679    assert_eq!(
5680        contacts(&client_a, cx_a),
5681        [
5682            ("user_b".to_string(), "online", "busy"),
5683            ("user_c".to_string(), "online", "busy")
5684        ]
5685    );
5686    assert_eq!(
5687        contacts(&client_b, cx_b),
5688        [
5689            ("user_a".to_string(), "online", "busy"),
5690            ("user_c".to_string(), "online", "busy"),
5691            ("user_d".to_string(), "online", "free")
5692        ]
5693    );
5694    assert_eq!(
5695        contacts(&client_c, cx_c),
5696        [
5697            ("user_a".to_string(), "online", "busy"),
5698            ("user_b".to_string(), "online", "busy")
5699        ]
5700    );
5701    assert_eq!(
5702        contacts(&client_d, cx_d),
5703        [("user_b".to_string(), "online", "busy")]
5704    );
5705
5706    active_call_a
5707        .update(cx_a, |call, cx| call.hang_up(cx))
5708        .await
5709        .unwrap();
5710    executor.run_until_parked();
5711    assert_eq!(
5712        contacts(&client_a, cx_a),
5713        [
5714            ("user_b".to_string(), "online", "free"),
5715            ("user_c".to_string(), "online", "free")
5716        ]
5717    );
5718    assert_eq!(
5719        contacts(&client_b, cx_b),
5720        [
5721            ("user_a".to_string(), "online", "free"),
5722            ("user_c".to_string(), "online", "free"),
5723            ("user_d".to_string(), "online", "free")
5724        ]
5725    );
5726    assert_eq!(
5727        contacts(&client_c, cx_c),
5728        [
5729            ("user_a".to_string(), "online", "free"),
5730            ("user_b".to_string(), "online", "free")
5731        ]
5732    );
5733    assert_eq!(
5734        contacts(&client_d, cx_d),
5735        [("user_b".to_string(), "online", "free")]
5736    );
5737
5738    active_call_a
5739        .update(cx_a, |call, cx| {
5740            call.invite(client_b.user_id().unwrap(), None, cx)
5741        })
5742        .await
5743        .unwrap();
5744    executor.run_until_parked();
5745    assert_eq!(
5746        contacts(&client_a, cx_a),
5747        [
5748            ("user_b".to_string(), "online", "busy"),
5749            ("user_c".to_string(), "online", "free")
5750        ]
5751    );
5752    assert_eq!(
5753        contacts(&client_b, cx_b),
5754        [
5755            ("user_a".to_string(), "online", "busy"),
5756            ("user_c".to_string(), "online", "free"),
5757            ("user_d".to_string(), "online", "free")
5758        ]
5759    );
5760    assert_eq!(
5761        contacts(&client_c, cx_c),
5762        [
5763            ("user_a".to_string(), "online", "busy"),
5764            ("user_b".to_string(), "online", "busy")
5765        ]
5766    );
5767    assert_eq!(
5768        contacts(&client_d, cx_d),
5769        [("user_b".to_string(), "online", "busy")]
5770    );
5771
5772    server.forbid_connections();
5773    server.disconnect_client(client_a.peer_id().unwrap());
5774    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5775    assert_eq!(contacts(&client_a, cx_a), []);
5776    assert_eq!(
5777        contacts(&client_b, cx_b),
5778        [
5779            ("user_a".to_string(), "offline", "free"),
5780            ("user_c".to_string(), "online", "free"),
5781            ("user_d".to_string(), "online", "free")
5782        ]
5783    );
5784    assert_eq!(
5785        contacts(&client_c, cx_c),
5786        [
5787            ("user_a".to_string(), "offline", "free"),
5788            ("user_b".to_string(), "online", "free")
5789        ]
5790    );
5791    assert_eq!(
5792        contacts(&client_d, cx_d),
5793        [("user_b".to_string(), "online", "free")]
5794    );
5795
5796    // Test removing a contact
5797    client_b
5798        .user_store()
5799        .update(cx_b, |store, cx| {
5800            store.remove_contact(client_c.user_id().unwrap(), cx)
5801        })
5802        .await
5803        .unwrap();
5804    executor.run_until_parked();
5805    assert_eq!(
5806        contacts(&client_b, cx_b),
5807        [
5808            ("user_a".to_string(), "offline", "free"),
5809            ("user_d".to_string(), "online", "free")
5810        ]
5811    );
5812    assert_eq!(
5813        contacts(&client_c, cx_c),
5814        [("user_a".to_string(), "offline", "free"),]
5815    );
5816
5817    fn contacts(
5818        client: &TestClient,
5819        cx: &TestAppContext,
5820    ) -> Vec<(String, &'static str, &'static str)> {
5821        client.user_store().read_with(cx, |store, _| {
5822            store
5823                .contacts()
5824                .iter()
5825                .map(|contact| {
5826                    (
5827                        contact.user.github_login.clone(),
5828                        if contact.online { "online" } else { "offline" },
5829                        if contact.busy { "busy" } else { "free" },
5830                    )
5831                })
5832                .collect()
5833        })
5834    }
5835}
5836
5837#[gpui::test(iterations = 10)]
5838async fn test_contact_requests(
5839    executor: BackgroundExecutor,
5840    cx_a: &mut TestAppContext,
5841    cx_a2: &mut TestAppContext,
5842    cx_b: &mut TestAppContext,
5843    cx_b2: &mut TestAppContext,
5844    cx_c: &mut TestAppContext,
5845    cx_c2: &mut TestAppContext,
5846) {
5847    // Connect to a server as 3 clients.
5848    let mut server = TestServer::start(executor.clone()).await;
5849    let client_a = server.create_client(cx_a, "user_a").await;
5850    let client_a2 = server.create_client(cx_a2, "user_a").await;
5851    let client_b = server.create_client(cx_b, "user_b").await;
5852    let client_b2 = server.create_client(cx_b2, "user_b").await;
5853    let client_c = server.create_client(cx_c, "user_c").await;
5854    let client_c2 = server.create_client(cx_c2, "user_c").await;
5855
5856    assert_eq!(client_a.user_id().unwrap(), client_a2.user_id().unwrap());
5857    assert_eq!(client_b.user_id().unwrap(), client_b2.user_id().unwrap());
5858    assert_eq!(client_c.user_id().unwrap(), client_c2.user_id().unwrap());
5859
5860    // User A and User C request that user B become their contact.
5861    client_a
5862        .user_store()
5863        .update(cx_a, |store, cx| {
5864            store.request_contact(client_b.user_id().unwrap(), cx)
5865        })
5866        .await
5867        .unwrap();
5868    client_c
5869        .user_store()
5870        .update(cx_c, |store, cx| {
5871            store.request_contact(client_b.user_id().unwrap(), cx)
5872        })
5873        .await
5874        .unwrap();
5875    executor.run_until_parked();
5876
5877    // All users see the pending request appear in all their clients.
5878    assert_eq!(
5879        client_a.summarize_contacts(cx_a).outgoing_requests,
5880        &["user_b"]
5881    );
5882    assert_eq!(
5883        client_a2.summarize_contacts(cx_a2).outgoing_requests,
5884        &["user_b"]
5885    );
5886    assert_eq!(
5887        client_b.summarize_contacts(cx_b).incoming_requests,
5888        &["user_a", "user_c"]
5889    );
5890    assert_eq!(
5891        client_b2.summarize_contacts(cx_b2).incoming_requests,
5892        &["user_a", "user_c"]
5893    );
5894    assert_eq!(
5895        client_c.summarize_contacts(cx_c).outgoing_requests,
5896        &["user_b"]
5897    );
5898    assert_eq!(
5899        client_c2.summarize_contacts(cx_c2).outgoing_requests,
5900        &["user_b"]
5901    );
5902
5903    // Contact requests are present upon connecting (tested here via disconnect/reconnect)
5904    disconnect_and_reconnect(&client_a, cx_a).await;
5905    disconnect_and_reconnect(&client_b, cx_b).await;
5906    disconnect_and_reconnect(&client_c, cx_c).await;
5907    executor.run_until_parked();
5908    assert_eq!(
5909        client_a.summarize_contacts(cx_a).outgoing_requests,
5910        &["user_b"]
5911    );
5912    assert_eq!(
5913        client_b.summarize_contacts(cx_b).incoming_requests,
5914        &["user_a", "user_c"]
5915    );
5916    assert_eq!(
5917        client_c.summarize_contacts(cx_c).outgoing_requests,
5918        &["user_b"]
5919    );
5920
5921    // User B accepts the request from user A.
5922    client_b
5923        .user_store()
5924        .update(cx_b, |store, cx| {
5925            store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
5926        })
5927        .await
5928        .unwrap();
5929
5930    executor.run_until_parked();
5931
5932    // User B sees user A as their contact now in all client, and the incoming request from them is removed.
5933    let contacts_b = client_b.summarize_contacts(cx_b);
5934    assert_eq!(contacts_b.current, &["user_a"]);
5935    assert_eq!(contacts_b.incoming_requests, &["user_c"]);
5936    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
5937    assert_eq!(contacts_b2.current, &["user_a"]);
5938    assert_eq!(contacts_b2.incoming_requests, &["user_c"]);
5939
5940    // User A sees user B as their contact now in all clients, and the outgoing request to them is removed.
5941    let contacts_a = client_a.summarize_contacts(cx_a);
5942    assert_eq!(contacts_a.current, &["user_b"]);
5943    assert!(contacts_a.outgoing_requests.is_empty());
5944    let contacts_a2 = client_a2.summarize_contacts(cx_a2);
5945    assert_eq!(contacts_a2.current, &["user_b"]);
5946    assert!(contacts_a2.outgoing_requests.is_empty());
5947
5948    // Contacts are present upon connecting (tested here via disconnect/reconnect)
5949    disconnect_and_reconnect(&client_a, cx_a).await;
5950    disconnect_and_reconnect(&client_b, cx_b).await;
5951    disconnect_and_reconnect(&client_c, cx_c).await;
5952    executor.run_until_parked();
5953    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
5954    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
5955    assert_eq!(
5956        client_b.summarize_contacts(cx_b).incoming_requests,
5957        &["user_c"]
5958    );
5959    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
5960    assert_eq!(
5961        client_c.summarize_contacts(cx_c).outgoing_requests,
5962        &["user_b"]
5963    );
5964
5965    // User B rejects the request from user C.
5966    client_b
5967        .user_store()
5968        .update(cx_b, |store, cx| {
5969            store.respond_to_contact_request(client_c.user_id().unwrap(), false, cx)
5970        })
5971        .await
5972        .unwrap();
5973
5974    executor.run_until_parked();
5975
5976    // User B doesn't see user C as their contact, and the incoming request from them is removed.
5977    let contacts_b = client_b.summarize_contacts(cx_b);
5978    assert_eq!(contacts_b.current, &["user_a"]);
5979    assert!(contacts_b.incoming_requests.is_empty());
5980    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
5981    assert_eq!(contacts_b2.current, &["user_a"]);
5982    assert!(contacts_b2.incoming_requests.is_empty());
5983
5984    // User C doesn't see user B as their contact, and the outgoing request to them is removed.
5985    let contacts_c = client_c.summarize_contacts(cx_c);
5986    assert!(contacts_c.current.is_empty());
5987    assert!(contacts_c.outgoing_requests.is_empty());
5988    let contacts_c2 = client_c2.summarize_contacts(cx_c2);
5989    assert!(contacts_c2.current.is_empty());
5990    assert!(contacts_c2.outgoing_requests.is_empty());
5991
5992    // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect)
5993    disconnect_and_reconnect(&client_a, cx_a).await;
5994    disconnect_and_reconnect(&client_b, cx_b).await;
5995    disconnect_and_reconnect(&client_c, cx_c).await;
5996    executor.run_until_parked();
5997    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
5998    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
5999    assert!(client_b
6000        .summarize_contacts(cx_b)
6001        .incoming_requests
6002        .is_empty());
6003    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
6004    assert!(client_c
6005        .summarize_contacts(cx_c)
6006        .outgoing_requests
6007        .is_empty());
6008
6009    async fn disconnect_and_reconnect(client: &TestClient, cx: &mut TestAppContext) {
6010        client.disconnect(&cx.to_async());
6011        client.clear_contacts(cx).await;
6012        client
6013            .authenticate_and_connect(false, &cx.to_async())
6014            .await
6015            .unwrap();
6016    }
6017}
6018
6019#[gpui::test(iterations = 10)]
6020async fn test_join_call_after_screen_was_shared(
6021    executor: BackgroundExecutor,
6022    cx_a: &mut TestAppContext,
6023    cx_b: &mut TestAppContext,
6024) {
6025    let mut server = TestServer::start(executor.clone()).await;
6026
6027    let client_a = server.create_client(cx_a, "user_a").await;
6028    let client_b = server.create_client(cx_b, "user_b").await;
6029    server
6030        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6031        .await;
6032
6033    let active_call_a = cx_a.read(ActiveCall::global);
6034    let active_call_b = cx_b.read(ActiveCall::global);
6035
6036    // Call users B and C from client A.
6037    active_call_a
6038        .update(cx_a, |call, cx| {
6039            call.invite(client_b.user_id().unwrap(), None, cx)
6040        })
6041        .await
6042        .unwrap();
6043
6044    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
6045    executor.run_until_parked();
6046    assert_eq!(
6047        room_participants(&room_a, cx_a),
6048        RoomParticipants {
6049            remote: Default::default(),
6050            pending: vec!["user_b".to_string()]
6051        }
6052    );
6053
6054    // User B receives the call.
6055
6056    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
6057    let call_b = incoming_call_b.next().await.unwrap().unwrap();
6058    assert_eq!(call_b.calling_user.github_login, "user_a");
6059
6060    // User A shares their screen
6061    let display = MacOSDisplay::new();
6062    active_call_a
6063        .update(cx_a, |call, cx| {
6064            call.room().unwrap().update(cx, |room, cx| {
6065                room.set_display_sources(vec![display.clone()]);
6066                room.share_screen(cx)
6067            })
6068        })
6069        .await
6070        .unwrap();
6071
6072    client_b.user_store().update(cx_b, |user_store, _| {
6073        user_store.clear_cache();
6074    });
6075
6076    // User B joins the room
6077    active_call_b
6078        .update(cx_b, |call, cx| call.accept_incoming(cx))
6079        .await
6080        .unwrap();
6081
6082    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
6083    assert!(incoming_call_b.next().await.unwrap().is_none());
6084
6085    executor.run_until_parked();
6086    assert_eq!(
6087        room_participants(&room_a, cx_a),
6088        RoomParticipants {
6089            remote: vec!["user_b".to_string()],
6090            pending: vec![],
6091        }
6092    );
6093    assert_eq!(
6094        room_participants(&room_b, cx_b),
6095        RoomParticipants {
6096            remote: vec!["user_a".to_string()],
6097            pending: vec![],
6098        }
6099    );
6100
6101    // Ensure User B sees User A's screenshare.
6102
6103    room_b.read_with(cx_b, |room, _| {
6104        assert_eq!(
6105            room.remote_participants()
6106                .get(&client_a.user_id().unwrap())
6107                .unwrap()
6108                .video_tracks
6109                .len(),
6110            1
6111        );
6112    });
6113}
6114
6115#[gpui::test]
6116async fn test_right_click_menu_behind_collab_panel(cx: &mut TestAppContext) {
6117    let mut server = TestServer::start(cx.executor().clone()).await;
6118    let client_a = server.create_client(cx, "user_a").await;
6119    let (_workspace_a, cx) = client_a.build_test_workspace(cx).await;
6120
6121    cx.simulate_resize(size(px(300.), px(300.)));
6122
6123    cx.simulate_keystrokes("cmd-n cmd-n cmd-n");
6124    cx.update(|cx| cx.refresh());
6125
6126    let tab_bounds = cx.debug_bounds("TAB-2").unwrap();
6127    let new_tab_button_bounds = cx.debug_bounds("ICON-Plus").unwrap();
6128
6129    assert!(
6130        tab_bounds.intersects(&new_tab_button_bounds),
6131        "Tab should overlap with the new tab button, if this is failing check if there's been a redesign!"
6132    );
6133
6134    cx.simulate_event(MouseDownEvent {
6135        button: MouseButton::Right,
6136        position: new_tab_button_bounds.center(),
6137        modifiers: Modifiers::default(),
6138        click_count: 1,
6139        first_mouse: false,
6140    });
6141
6142    // regression test that the right click menu for tabs does not open.
6143    assert!(cx.debug_bounds("MENU_ITEM-Close").is_none());
6144
6145    let tab_bounds = cx.debug_bounds("TAB-1").unwrap();
6146    cx.simulate_event(MouseDownEvent {
6147        button: MouseButton::Right,
6148        position: tab_bounds.center(),
6149        modifiers: Modifiers::default(),
6150        click_count: 1,
6151        first_mouse: false,
6152    });
6153    assert!(cx.debug_bounds("MENU_ITEM-Close").is_some());
6154}
6155
6156#[gpui::test]
6157async fn test_pane_split_left(cx: &mut TestAppContext) {
6158    let (_, client) = TestServer::start1(cx).await;
6159    let (workspace, cx) = client.build_test_workspace(cx).await;
6160
6161    cx.simulate_keystrokes("cmd-n");
6162    workspace.update(cx, |workspace, cx| {
6163        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 1);
6164    });
6165    cx.simulate_keystrokes("cmd-k left");
6166    workspace.update(cx, |workspace, cx| {
6167        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 2);
6168    });
6169    cx.simulate_keystrokes("cmd-k");
6170    // sleep for longer than the timeout in keyboard shortcut handling
6171    // to verify that it doesn't fire in this case.
6172    cx.executor().advance_clock(Duration::from_secs(2));
6173    cx.simulate_keystrokes("left");
6174    workspace.update(cx, |workspace, cx| {
6175        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 2);
6176    });
6177}
6178
6179#[gpui::test]
6180async fn test_join_after_restart(cx1: &mut TestAppContext, cx2: &mut TestAppContext) {
6181    let (mut server, client) = TestServer::start1(cx1).await;
6182    let channel1 = server.make_public_channel("channel1", &client, cx1).await;
6183    let channel2 = server.make_public_channel("channel2", &client, cx1).await;
6184
6185    join_channel(channel1, &client, cx1).await.unwrap();
6186    drop(client);
6187
6188    let client2 = server.create_client(cx2, "user_a").await;
6189    join_channel(channel2, &client2, cx2).await.unwrap();
6190}
6191
6192#[gpui::test]
6193async fn test_preview_tabs(cx: &mut TestAppContext) {
6194    let (_server, client) = TestServer::start1(cx).await;
6195    let (workspace, cx) = client.build_test_workspace(cx).await;
6196    let project = workspace.update(cx, |workspace, _| workspace.project().clone());
6197
6198    let worktree_id = project.update(cx, |project, cx| {
6199        project.worktrees(cx).next().unwrap().read(cx).id()
6200    });
6201
6202    let path_1 = ProjectPath {
6203        worktree_id,
6204        path: Path::new("1.txt").into(),
6205    };
6206    let path_2 = ProjectPath {
6207        worktree_id,
6208        path: Path::new("2.js").into(),
6209    };
6210    let path_3 = ProjectPath {
6211        worktree_id,
6212        path: Path::new("3.rs").into(),
6213    };
6214
6215    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
6216
6217    let get_path = |pane: &Pane, idx: usize, cx: &AppContext| {
6218        pane.item_for_index(idx).unwrap().project_path(cx).unwrap()
6219    };
6220
6221    // Opening item 3 as a "permanent" tab
6222    workspace
6223        .update(cx, |workspace, cx| {
6224            workspace.open_path(path_3.clone(), None, false, cx)
6225        })
6226        .await
6227        .unwrap();
6228
6229    pane.update(cx, |pane, cx| {
6230        assert_eq!(pane.items_len(), 1);
6231        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6232        assert_eq!(pane.preview_item_id(), None);
6233
6234        assert!(!pane.can_navigate_backward());
6235        assert!(!pane.can_navigate_forward());
6236    });
6237
6238    // Open item 1 as preview
6239    workspace
6240        .update(cx, |workspace, cx| {
6241            workspace.open_path_preview(path_1.clone(), None, true, true, cx)
6242        })
6243        .await
6244        .unwrap();
6245
6246    pane.update(cx, |pane, cx| {
6247        assert_eq!(pane.items_len(), 2);
6248        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6249        assert_eq!(get_path(pane, 1, cx), path_1.clone());
6250        assert_eq!(
6251            pane.preview_item_id(),
6252            Some(pane.items().nth(1).unwrap().item_id())
6253        );
6254
6255        assert!(pane.can_navigate_backward());
6256        assert!(!pane.can_navigate_forward());
6257    });
6258
6259    // Open item 2 as preview
6260    workspace
6261        .update(cx, |workspace, cx| {
6262            workspace.open_path_preview(path_2.clone(), None, true, true, cx)
6263        })
6264        .await
6265        .unwrap();
6266
6267    pane.update(cx, |pane, cx| {
6268        assert_eq!(pane.items_len(), 2);
6269        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6270        assert_eq!(get_path(pane, 1, cx), path_2.clone());
6271        assert_eq!(
6272            pane.preview_item_id(),
6273            Some(pane.items().nth(1).unwrap().item_id())
6274        );
6275
6276        assert!(pane.can_navigate_backward());
6277        assert!(!pane.can_navigate_forward());
6278    });
6279
6280    // Going back should show item 1 as preview
6281    workspace
6282        .update(cx, |workspace, cx| workspace.go_back(pane.downgrade(), cx))
6283        .await
6284        .unwrap();
6285
6286    pane.update(cx, |pane, cx| {
6287        assert_eq!(pane.items_len(), 2);
6288        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6289        assert_eq!(get_path(pane, 1, cx), path_1.clone());
6290        assert_eq!(
6291            pane.preview_item_id(),
6292            Some(pane.items().nth(1).unwrap().item_id())
6293        );
6294
6295        assert!(pane.can_navigate_backward());
6296        assert!(pane.can_navigate_forward());
6297    });
6298
6299    // Closing item 1
6300    pane.update(cx, |pane, cx| {
6301        pane.close_item_by_id(
6302            pane.active_item().unwrap().item_id(),
6303            workspace::SaveIntent::Skip,
6304            cx,
6305        )
6306    })
6307    .await
6308    .unwrap();
6309
6310    pane.update(cx, |pane, cx| {
6311        assert_eq!(pane.items_len(), 1);
6312        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6313        assert_eq!(pane.preview_item_id(), None);
6314
6315        assert!(pane.can_navigate_backward());
6316        assert!(!pane.can_navigate_forward());
6317    });
6318
6319    // Going back should show item 1 as preview
6320    workspace
6321        .update(cx, |workspace, cx| workspace.go_back(pane.downgrade(), cx))
6322        .await
6323        .unwrap();
6324
6325    pane.update(cx, |pane, cx| {
6326        assert_eq!(pane.items_len(), 2);
6327        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6328        assert_eq!(get_path(pane, 1, cx), path_1.clone());
6329        assert_eq!(
6330            pane.preview_item_id(),
6331            Some(pane.items().nth(1).unwrap().item_id())
6332        );
6333
6334        assert!(pane.can_navigate_backward());
6335        assert!(pane.can_navigate_forward());
6336    });
6337
6338    // Close permanent tab
6339    pane.update(cx, |pane, cx| {
6340        let id = pane.items().next().unwrap().item_id();
6341        pane.close_item_by_id(id, workspace::SaveIntent::Skip, cx)
6342    })
6343    .await
6344    .unwrap();
6345
6346    pane.update(cx, |pane, cx| {
6347        assert_eq!(pane.items_len(), 1);
6348        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6349        assert_eq!(
6350            pane.preview_item_id(),
6351            Some(pane.items().next().unwrap().item_id())
6352        );
6353
6354        assert!(pane.can_navigate_backward());
6355        assert!(pane.can_navigate_forward());
6356    });
6357
6358    // Split pane to the right
6359    pane.update(cx, |pane, cx| {
6360        pane.split(workspace::SplitDirection::Right, cx);
6361    });
6362
6363    let right_pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
6364
6365    pane.update(cx, |pane, cx| {
6366        assert_eq!(pane.items_len(), 1);
6367        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6368        assert_eq!(
6369            pane.preview_item_id(),
6370            Some(pane.items().next().unwrap().item_id())
6371        );
6372
6373        assert!(pane.can_navigate_backward());
6374        assert!(pane.can_navigate_forward());
6375    });
6376
6377    right_pane.update(cx, |pane, cx| {
6378        assert_eq!(pane.items_len(), 1);
6379        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6380        assert_eq!(pane.preview_item_id(), None);
6381
6382        assert!(!pane.can_navigate_backward());
6383        assert!(!pane.can_navigate_forward());
6384    });
6385
6386    // Open item 2 as preview in right pane
6387    workspace
6388        .update(cx, |workspace, cx| {
6389            workspace.open_path_preview(path_2.clone(), None, true, true, cx)
6390        })
6391        .await
6392        .unwrap();
6393
6394    pane.update(cx, |pane, cx| {
6395        assert_eq!(pane.items_len(), 1);
6396        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6397        assert_eq!(
6398            pane.preview_item_id(),
6399            Some(pane.items().next().unwrap().item_id())
6400        );
6401
6402        assert!(pane.can_navigate_backward());
6403        assert!(pane.can_navigate_forward());
6404    });
6405
6406    right_pane.update(cx, |pane, cx| {
6407        assert_eq!(pane.items_len(), 2);
6408        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6409        assert_eq!(get_path(pane, 1, cx), path_2.clone());
6410        assert_eq!(
6411            pane.preview_item_id(),
6412            Some(pane.items().nth(1).unwrap().item_id())
6413        );
6414
6415        assert!(pane.can_navigate_backward());
6416        assert!(!pane.can_navigate_forward());
6417    });
6418
6419    // Focus left pane
6420    workspace.update(cx, |workspace, cx| {
6421        workspace.activate_pane_in_direction(workspace::SplitDirection::Left, cx)
6422    });
6423
6424    // Open item 2 as preview in left pane
6425    workspace
6426        .update(cx, |workspace, cx| {
6427            workspace.open_path_preview(path_2.clone(), None, true, true, cx)
6428        })
6429        .await
6430        .unwrap();
6431
6432    pane.update(cx, |pane, cx| {
6433        assert_eq!(pane.items_len(), 1);
6434        assert_eq!(get_path(pane, 0, cx), path_2.clone());
6435        assert_eq!(
6436            pane.preview_item_id(),
6437            Some(pane.items().next().unwrap().item_id())
6438        );
6439
6440        assert!(pane.can_navigate_backward());
6441        assert!(!pane.can_navigate_forward());
6442    });
6443
6444    right_pane.update(cx, |pane, cx| {
6445        assert_eq!(pane.items_len(), 2);
6446        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6447        assert_eq!(get_path(pane, 1, cx), path_2.clone());
6448        assert_eq!(
6449            pane.preview_item_id(),
6450            Some(pane.items().nth(1).unwrap().item_id())
6451        );
6452
6453        assert!(pane.can_navigate_backward());
6454        assert!(!pane.can_navigate_forward());
6455    });
6456}
6457
6458#[gpui::test(iterations = 10)]
6459async fn test_context_collaboration_with_reconnect(
6460    executor: BackgroundExecutor,
6461    cx_a: &mut TestAppContext,
6462    cx_b: &mut TestAppContext,
6463) {
6464    let mut server = TestServer::start(executor.clone()).await;
6465    let client_a = server.create_client(cx_a, "user_a").await;
6466    let client_b = server.create_client(cx_b, "user_b").await;
6467    server
6468        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6469        .await;
6470    let active_call_a = cx_a.read(ActiveCall::global);
6471
6472    client_a.fs().insert_tree("/a", Default::default()).await;
6473    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
6474    let project_id = active_call_a
6475        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6476        .await
6477        .unwrap();
6478    let project_b = client_b.join_remote_project(project_id, cx_b).await;
6479
6480    // Client A sees that a guest has joined.
6481    executor.run_until_parked();
6482
6483    project_a.read_with(cx_a, |project, _| {
6484        assert_eq!(project.collaborators().len(), 1);
6485    });
6486    project_b.read_with(cx_b, |project, _| {
6487        assert_eq!(project.collaborators().len(), 1);
6488    });
6489
6490    let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
6491    let context_store_a = cx_a
6492        .update(|cx| ContextStore::new(project_a.clone(), prompt_builder.clone(), cx))
6493        .await
6494        .unwrap();
6495    let context_store_b = cx_b
6496        .update(|cx| ContextStore::new(project_b.clone(), prompt_builder.clone(), cx))
6497        .await
6498        .unwrap();
6499
6500    // Client A creates a new context.
6501    let context_a = context_store_a.update(cx_a, |store, cx| store.create(cx));
6502    executor.run_until_parked();
6503
6504    // Client B retrieves host's contexts and joins one.
6505    let context_b = context_store_b
6506        .update(cx_b, |store, cx| {
6507            let host_contexts = store.host_contexts().to_vec();
6508            assert_eq!(host_contexts.len(), 1);
6509            store.open_remote_context(host_contexts[0].id.clone(), cx)
6510        })
6511        .await
6512        .unwrap();
6513
6514    // Host and guest make changes
6515    context_a.update(cx_a, |context, cx| {
6516        context.buffer().update(cx, |buffer, cx| {
6517            buffer.edit([(0..0, "Host change\n")], None, cx)
6518        })
6519    });
6520    context_b.update(cx_b, |context, cx| {
6521        context.buffer().update(cx, |buffer, cx| {
6522            buffer.edit([(0..0, "Guest change\n")], None, cx)
6523        })
6524    });
6525    executor.run_until_parked();
6526    assert_eq!(
6527        context_a.read_with(cx_a, |context, cx| context.buffer().read(cx).text()),
6528        "Guest change\nHost change\n"
6529    );
6530    assert_eq!(
6531        context_b.read_with(cx_b, |context, cx| context.buffer().read(cx).text()),
6532        "Guest change\nHost change\n"
6533    );
6534
6535    // Disconnect client A and make some changes while disconnected.
6536    server.disconnect_client(client_a.peer_id().unwrap());
6537    server.forbid_connections();
6538    context_a.update(cx_a, |context, cx| {
6539        context.buffer().update(cx, |buffer, cx| {
6540            buffer.edit([(0..0, "Host offline change\n")], None, cx)
6541        })
6542    });
6543    context_b.update(cx_b, |context, cx| {
6544        context.buffer().update(cx, |buffer, cx| {
6545            buffer.edit([(0..0, "Guest offline change\n")], None, cx)
6546        })
6547    });
6548    executor.run_until_parked();
6549    assert_eq!(
6550        context_a.read_with(cx_a, |context, cx| context.buffer().read(cx).text()),
6551        "Host offline change\nGuest change\nHost change\n"
6552    );
6553    assert_eq!(
6554        context_b.read_with(cx_b, |context, cx| context.buffer().read(cx).text()),
6555        "Guest offline change\nGuest change\nHost change\n"
6556    );
6557
6558    // Allow client A to reconnect and verify that contexts converge.
6559    server.allow_connections();
6560    executor.advance_clock(RECEIVE_TIMEOUT);
6561    assert_eq!(
6562        context_a.read_with(cx_a, |context, cx| context.buffer().read(cx).text()),
6563        "Guest offline change\nHost offline change\nGuest change\nHost change\n"
6564    );
6565    assert_eq!(
6566        context_b.read_with(cx_b, |context, cx| context.buffer().read(cx).text()),
6567        "Guest offline change\nHost offline change\nGuest change\nHost change\n"
6568    );
6569
6570    // Client A disconnects without being able to reconnect. Context B becomes readonly.
6571    server.forbid_connections();
6572    server.disconnect_client(client_a.peer_id().unwrap());
6573    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
6574    context_b.read_with(cx_b, |context, cx| {
6575        assert!(context.buffer().read(cx).read_only());
6576    });
6577}
6578
6579#[gpui::test]
6580async fn test_remote_git_branches(
6581    executor: BackgroundExecutor,
6582    cx_a: &mut TestAppContext,
6583    cx_b: &mut TestAppContext,
6584) {
6585    let mut server = TestServer::start(executor.clone()).await;
6586    let client_a = server.create_client(cx_a, "user_a").await;
6587    let client_b = server.create_client(cx_b, "user_b").await;
6588    server
6589        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6590        .await;
6591    let active_call_a = cx_a.read(ActiveCall::global);
6592
6593    client_a
6594        .fs()
6595        .insert_tree("/project", serde_json::json!({ ".git":{} }))
6596        .await;
6597    let branches = ["main", "dev", "feature-1"];
6598    client_a
6599        .fs()
6600        .insert_branches(Path::new("/project/.git"), &branches);
6601
6602    let (project_a, worktree_id) = client_a.build_local_project("/project", cx_a).await;
6603    let project_id = active_call_a
6604        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6605        .await
6606        .unwrap();
6607    let project_b = client_b.join_remote_project(project_id, cx_b).await;
6608
6609    let root_path = ProjectPath::root_path(worktree_id);
6610    // Client A sees that a guest has joined.
6611    executor.run_until_parked();
6612
6613    let branches_b = cx_b
6614        .update(|cx| project_b.update(cx, |project, cx| project.branches(root_path.clone(), cx)))
6615        .await
6616        .unwrap();
6617
6618    let new_branch = branches[2];
6619
6620    let branches_b = branches_b
6621        .into_iter()
6622        .map(|branch| branch.name)
6623        .collect::<Vec<_>>();
6624
6625    assert_eq!(&branches_b, &branches);
6626
6627    cx_b.update(|cx| {
6628        project_b.update(cx, |project, cx| {
6629            project.update_or_create_branch(root_path.clone(), new_branch.to_string(), cx)
6630        })
6631    })
6632    .await
6633    .unwrap();
6634
6635    executor.run_until_parked();
6636
6637    let host_branch = cx_a.update(|cx| {
6638        project_a.update(cx, |project, cx| {
6639            project.worktree_store().update(cx, |worktree_store, cx| {
6640                worktree_store
6641                    .current_branch(root_path.clone(), cx)
6642                    .unwrap()
6643            })
6644        })
6645    });
6646
6647    assert_eq!(host_branch.as_ref(), branches[2]);
6648
6649    // Also try creating a new branch
6650    cx_b.update(|cx| {
6651        project_b.update(cx, |project, cx| {
6652            project.update_or_create_branch(root_path.clone(), "totally-new-branch".to_string(), cx)
6653        })
6654    })
6655    .await
6656    .unwrap();
6657
6658    executor.run_until_parked();
6659
6660    let host_branch = cx_a.update(|cx| {
6661        project_a.update(cx, |project, cx| {
6662            project.worktree_store().update(cx, |worktree_store, cx| {
6663                worktree_store.current_branch(root_path, cx).unwrap()
6664            })
6665        })
6666    });
6667
6668    assert_eq!(host_branch.as_ref(), "totally-new-branch");
6669}