integration_tests.rs

   1use crate::{
   2    rpc::{CLEANUP_TIMEOUT, RECONNECT_TIMEOUT},
   3    tests::{
   4        channel_id, following_tests::join_channel, room_participants, rust_lang, RoomParticipants,
   5        TestClient, TestServer,
   6    },
   7};
   8use anyhow::{anyhow, Result};
   9use assistant::{ContextStore, PromptBuilder};
  10use call::{room, ActiveCall, ParticipantLocation, Room};
  11use client::{User, RECEIVE_TIMEOUT};
  12use collections::{HashMap, HashSet};
  13use fs::{FakeFs, Fs as _, RemoveOptions};
  14use futures::{channel::mpsc, StreamExt as _};
  15use git::repository::GitFileStatus;
  16use gpui::{
  17    px, size, AppContext, BackgroundExecutor, Model, Modifiers, MouseButton, MouseDownEvent,
  18    TestAppContext, UpdateGlobal,
  19};
  20use language::{
  21    language_settings::{
  22        AllLanguageSettings, Formatter, FormatterList, PrettierSettings, SelectedFormatter,
  23    },
  24    tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language, LanguageConfig,
  25    LanguageMatcher, LineEnding, OffsetRangeExt, Point, Rope,
  26};
  27use live_kit_client::MacOSDisplay;
  28use lsp::LanguageServerId;
  29use parking_lot::Mutex;
  30use project::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::{LocalSettingsKind, 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                (
3332                    Path::new("").into(),
3333                    LocalSettingsKind::Settings,
3334                    r#"{"tab_size":2}"#.to_string()
3335                ),
3336                (
3337                    Path::new("a").into(),
3338                    LocalSettingsKind::Settings,
3339                    r#"{"tab_size":8}"#.to_string()
3340                ),
3341            ]
3342        )
3343    });
3344
3345    // As client A, update a settings file. As Client B, see the changed settings.
3346    client_a
3347        .fs()
3348        .insert_file("/dir/.zed/settings.json", r#"{}"#.into())
3349        .await;
3350    executor.run_until_parked();
3351    cx_b.read(|cx| {
3352        let store = cx.global::<SettingsStore>();
3353        assert_eq!(
3354            store
3355                .local_settings(worktree_b.read(cx).id())
3356                .collect::<Vec<_>>(),
3357            &[
3358                (
3359                    Path::new("").into(),
3360                    LocalSettingsKind::Settings,
3361                    r#"{}"#.to_string()
3362                ),
3363                (
3364                    Path::new("a").into(),
3365                    LocalSettingsKind::Settings,
3366                    r#"{"tab_size":8}"#.to_string()
3367                ),
3368            ]
3369        )
3370    });
3371
3372    // As client A, create and remove some settings files. As client B, see the changed settings.
3373    client_a
3374        .fs()
3375        .remove_file("/dir/.zed/settings.json".as_ref(), Default::default())
3376        .await
3377        .unwrap();
3378    client_a
3379        .fs()
3380        .create_dir("/dir/b/.zed".as_ref())
3381        .await
3382        .unwrap();
3383    client_a
3384        .fs()
3385        .insert_file("/dir/b/.zed/settings.json", r#"{"tab_size": 4}"#.into())
3386        .await;
3387    executor.run_until_parked();
3388    cx_b.read(|cx| {
3389        let store = cx.global::<SettingsStore>();
3390        assert_eq!(
3391            store
3392                .local_settings(worktree_b.read(cx).id())
3393                .collect::<Vec<_>>(),
3394            &[
3395                (
3396                    Path::new("a").into(),
3397                    LocalSettingsKind::Settings,
3398                    r#"{"tab_size":8}"#.to_string()
3399                ),
3400                (
3401                    Path::new("b").into(),
3402                    LocalSettingsKind::Settings,
3403                    r#"{"tab_size":4}"#.to_string()
3404                ),
3405            ]
3406        )
3407    });
3408
3409    // As client B, disconnect.
3410    server.forbid_connections();
3411    server.disconnect_client(client_b.peer_id().unwrap());
3412
3413    // As client A, change and remove settings files while client B is disconnected.
3414    client_a
3415        .fs()
3416        .insert_file("/dir/a/.zed/settings.json", r#"{"hard_tabs":true}"#.into())
3417        .await;
3418    client_a
3419        .fs()
3420        .remove_file("/dir/b/.zed/settings.json".as_ref(), Default::default())
3421        .await
3422        .unwrap();
3423    executor.run_until_parked();
3424
3425    // As client B, reconnect and see the changed settings.
3426    server.allow_connections();
3427    executor.advance_clock(RECEIVE_TIMEOUT);
3428    cx_b.read(|cx| {
3429        let store = cx.global::<SettingsStore>();
3430        assert_eq!(
3431            store
3432                .local_settings(worktree_b.read(cx).id())
3433                .collect::<Vec<_>>(),
3434            &[(
3435                Path::new("a").into(),
3436                LocalSettingsKind::Settings,
3437                r#"{"hard_tabs":true}"#.to_string()
3438            ),]
3439        )
3440    });
3441}
3442
3443#[gpui::test(iterations = 10)]
3444async fn test_buffer_conflict_after_save(
3445    executor: BackgroundExecutor,
3446    cx_a: &mut TestAppContext,
3447    cx_b: &mut TestAppContext,
3448) {
3449    let mut server = TestServer::start(executor.clone()).await;
3450    let client_a = server.create_client(cx_a, "user_a").await;
3451    let client_b = server.create_client(cx_b, "user_b").await;
3452    server
3453        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3454        .await;
3455    let active_call_a = cx_a.read(ActiveCall::global);
3456
3457    client_a
3458        .fs()
3459        .insert_tree(
3460            "/dir",
3461            json!({
3462                "a.txt": "a-contents",
3463            }),
3464        )
3465        .await;
3466    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3467    let project_id = active_call_a
3468        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3469        .await
3470        .unwrap();
3471    let project_b = client_b.join_remote_project(project_id, cx_b).await;
3472
3473    // Open a buffer as client B
3474    let buffer_b = project_b
3475        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3476        .await
3477        .unwrap();
3478
3479    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "world ")], None, cx));
3480
3481    buffer_b.read_with(cx_b, |buf, _| {
3482        assert!(buf.is_dirty());
3483        assert!(!buf.has_conflict());
3484    });
3485
3486    project_b
3487        .update(cx_b, |project, cx| {
3488            project.save_buffer(buffer_b.clone(), cx)
3489        })
3490        .await
3491        .unwrap();
3492
3493    buffer_b.read_with(cx_b, |buffer_b, _| assert!(!buffer_b.is_dirty()));
3494
3495    buffer_b.read_with(cx_b, |buf, _| {
3496        assert!(!buf.has_conflict());
3497    });
3498
3499    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "hello ")], None, cx));
3500
3501    buffer_b.read_with(cx_b, |buf, _| {
3502        assert!(buf.is_dirty());
3503        assert!(!buf.has_conflict());
3504    });
3505}
3506
3507#[gpui::test(iterations = 10)]
3508async fn test_buffer_reloading(
3509    executor: BackgroundExecutor,
3510    cx_a: &mut TestAppContext,
3511    cx_b: &mut TestAppContext,
3512) {
3513    let mut server = TestServer::start(executor.clone()).await;
3514    let client_a = server.create_client(cx_a, "user_a").await;
3515    let client_b = server.create_client(cx_b, "user_b").await;
3516    server
3517        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3518        .await;
3519    let active_call_a = cx_a.read(ActiveCall::global);
3520
3521    client_a
3522        .fs()
3523        .insert_tree(
3524            "/dir",
3525            json!({
3526                "a.txt": "a\nb\nc",
3527            }),
3528        )
3529        .await;
3530    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3531    let project_id = active_call_a
3532        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3533        .await
3534        .unwrap();
3535    let project_b = client_b.join_remote_project(project_id, cx_b).await;
3536
3537    // Open a buffer as client B
3538    let buffer_b = project_b
3539        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3540        .await
3541        .unwrap();
3542
3543    buffer_b.read_with(cx_b, |buf, _| {
3544        assert!(!buf.is_dirty());
3545        assert!(!buf.has_conflict());
3546        assert_eq!(buf.line_ending(), LineEnding::Unix);
3547    });
3548
3549    let new_contents = Rope::from("d\ne\nf");
3550    client_a
3551        .fs()
3552        .save("/dir/a.txt".as_ref(), &new_contents, LineEnding::Windows)
3553        .await
3554        .unwrap();
3555
3556    executor.run_until_parked();
3557
3558    buffer_b.read_with(cx_b, |buf, _| {
3559        assert_eq!(buf.text(), new_contents.to_string());
3560        assert!(!buf.is_dirty());
3561        assert!(!buf.has_conflict());
3562        assert_eq!(buf.line_ending(), LineEnding::Windows);
3563    });
3564}
3565
3566#[gpui::test(iterations = 10)]
3567async fn test_editing_while_guest_opens_buffer(
3568    executor: BackgroundExecutor,
3569    cx_a: &mut TestAppContext,
3570    cx_b: &mut TestAppContext,
3571) {
3572    let mut server = TestServer::start(executor.clone()).await;
3573    let client_a = server.create_client(cx_a, "user_a").await;
3574    let client_b = server.create_client(cx_b, "user_b").await;
3575    server
3576        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3577        .await;
3578    let active_call_a = cx_a.read(ActiveCall::global);
3579
3580    client_a
3581        .fs()
3582        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3583        .await;
3584    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3585    let project_id = active_call_a
3586        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3587        .await
3588        .unwrap();
3589    let project_b = client_b.join_remote_project(project_id, cx_b).await;
3590
3591    // Open a buffer as client A
3592    let buffer_a = project_a
3593        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3594        .await
3595        .unwrap();
3596
3597    // Start opening the same buffer as client B
3598    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx));
3599    let buffer_b = cx_b.executor().spawn(open_buffer);
3600
3601    // Edit the buffer as client A while client B is still opening it.
3602    cx_b.executor().simulate_random_delay().await;
3603    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "X")], None, cx));
3604    cx_b.executor().simulate_random_delay().await;
3605    buffer_a.update(cx_a, |buf, cx| buf.edit([(1..1, "Y")], None, cx));
3606
3607    let text = buffer_a.read_with(cx_a, |buf, _| buf.text());
3608    let buffer_b = buffer_b.await.unwrap();
3609    executor.run_until_parked();
3610
3611    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), text));
3612}
3613
3614#[gpui::test(iterations = 10)]
3615async fn test_leaving_worktree_while_opening_buffer(
3616    executor: BackgroundExecutor,
3617    cx_a: &mut TestAppContext,
3618    cx_b: &mut TestAppContext,
3619) {
3620    let mut server = TestServer::start(executor.clone()).await;
3621    let client_a = server.create_client(cx_a, "user_a").await;
3622    let client_b = server.create_client(cx_b, "user_b").await;
3623    server
3624        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3625        .await;
3626    let active_call_a = cx_a.read(ActiveCall::global);
3627
3628    client_a
3629        .fs()
3630        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3631        .await;
3632    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3633    let project_id = active_call_a
3634        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3635        .await
3636        .unwrap();
3637    let project_b = client_b.join_remote_project(project_id, cx_b).await;
3638
3639    // See that a guest has joined as client A.
3640    executor.run_until_parked();
3641
3642    project_a.read_with(cx_a, |p, _| assert_eq!(p.collaborators().len(), 1));
3643
3644    // Begin opening a buffer as client B, but leave the project before the open completes.
3645    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx));
3646    let buffer_b = cx_b.executor().spawn(open_buffer);
3647    cx_b.update(|_| drop(project_b));
3648    drop(buffer_b);
3649
3650    // See that the guest has left.
3651    executor.run_until_parked();
3652
3653    project_a.read_with(cx_a, |p, _| assert!(p.collaborators().is_empty()));
3654}
3655
3656#[gpui::test(iterations = 10)]
3657async fn test_canceling_buffer_opening(
3658    executor: BackgroundExecutor,
3659    cx_a: &mut TestAppContext,
3660    cx_b: &mut TestAppContext,
3661) {
3662    let mut server = TestServer::start(executor.clone()).await;
3663    let client_a = server.create_client(cx_a, "user_a").await;
3664    let client_b = server.create_client(cx_b, "user_b").await;
3665    server
3666        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3667        .await;
3668    let active_call_a = cx_a.read(ActiveCall::global);
3669
3670    client_a
3671        .fs()
3672        .insert_tree(
3673            "/dir",
3674            json!({
3675                "a.txt": "abc",
3676            }),
3677        )
3678        .await;
3679    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3680    let project_id = active_call_a
3681        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3682        .await
3683        .unwrap();
3684    let project_b = client_b.join_remote_project(project_id, cx_b).await;
3685
3686    let buffer_a = project_a
3687        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3688        .await
3689        .unwrap();
3690
3691    // Open a buffer as client B but cancel after a random amount of time.
3692    let buffer_b = project_b.update(cx_b, |p, cx| {
3693        p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3694    });
3695    executor.simulate_random_delay().await;
3696    drop(buffer_b);
3697
3698    // Try opening the same buffer again as client B, and ensure we can
3699    // still do it despite the cancellation above.
3700    let buffer_b = project_b
3701        .update(cx_b, |p, cx| {
3702            p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3703        })
3704        .await
3705        .unwrap();
3706
3707    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "abc"));
3708}
3709
3710#[gpui::test(iterations = 10)]
3711async fn test_leaving_project(
3712    executor: BackgroundExecutor,
3713    cx_a: &mut TestAppContext,
3714    cx_b: &mut TestAppContext,
3715    cx_c: &mut TestAppContext,
3716) {
3717    let mut server = TestServer::start(executor.clone()).await;
3718    let client_a = server.create_client(cx_a, "user_a").await;
3719    let client_b = server.create_client(cx_b, "user_b").await;
3720    let client_c = server.create_client(cx_c, "user_c").await;
3721    server
3722        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3723        .await;
3724    let active_call_a = cx_a.read(ActiveCall::global);
3725
3726    client_a
3727        .fs()
3728        .insert_tree(
3729            "/a",
3730            json!({
3731                "a.txt": "a-contents",
3732                "b.txt": "b-contents",
3733            }),
3734        )
3735        .await;
3736    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
3737    let project_id = active_call_a
3738        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3739        .await
3740        .unwrap();
3741    let project_b1 = client_b.join_remote_project(project_id, cx_b).await;
3742    let project_c = client_c.join_remote_project(project_id, cx_c).await;
3743
3744    // Client A sees that a guest has joined.
3745    executor.run_until_parked();
3746
3747    project_a.read_with(cx_a, |project, _| {
3748        assert_eq!(project.collaborators().len(), 2);
3749    });
3750
3751    project_b1.read_with(cx_b, |project, _| {
3752        assert_eq!(project.collaborators().len(), 2);
3753    });
3754
3755    project_c.read_with(cx_c, |project, _| {
3756        assert_eq!(project.collaborators().len(), 2);
3757    });
3758
3759    // Client B opens a buffer.
3760    let buffer_b1 = project_b1
3761        .update(cx_b, |project, cx| {
3762            let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
3763            project.open_buffer((worktree_id, "a.txt"), cx)
3764        })
3765        .await
3766        .unwrap();
3767
3768    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3769
3770    // Drop client B's project and ensure client A and client C observe client B leaving.
3771    cx_b.update(|_| drop(project_b1));
3772    executor.run_until_parked();
3773
3774    project_a.read_with(cx_a, |project, _| {
3775        assert_eq!(project.collaborators().len(), 1);
3776    });
3777
3778    project_c.read_with(cx_c, |project, _| {
3779        assert_eq!(project.collaborators().len(), 1);
3780    });
3781
3782    // Client B re-joins the project and can open buffers as before.
3783    let project_b2 = client_b.join_remote_project(project_id, cx_b).await;
3784    executor.run_until_parked();
3785
3786    project_a.read_with(cx_a, |project, _| {
3787        assert_eq!(project.collaborators().len(), 2);
3788    });
3789
3790    project_b2.read_with(cx_b, |project, _| {
3791        assert_eq!(project.collaborators().len(), 2);
3792    });
3793
3794    project_c.read_with(cx_c, |project, _| {
3795        assert_eq!(project.collaborators().len(), 2);
3796    });
3797
3798    let buffer_b2 = project_b2
3799        .update(cx_b, |project, cx| {
3800            let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
3801            project.open_buffer((worktree_id, "a.txt"), cx)
3802        })
3803        .await
3804        .unwrap();
3805
3806    buffer_b2.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3807
3808    project_a.read_with(cx_a, |project, _| {
3809        assert_eq!(project.collaborators().len(), 2);
3810    });
3811
3812    // Drop client B's connection and ensure client A and client C observe client B leaving.
3813    client_b.disconnect(&cx_b.to_async());
3814    executor.advance_clock(RECONNECT_TIMEOUT);
3815
3816    project_a.read_with(cx_a, |project, _| {
3817        assert_eq!(project.collaborators().len(), 1);
3818    });
3819
3820    project_b2.read_with(cx_b, |project, cx| {
3821        assert!(project.is_disconnected(cx));
3822    });
3823
3824    project_c.read_with(cx_c, |project, _| {
3825        assert_eq!(project.collaborators().len(), 1);
3826    });
3827
3828    // Client B can't join the project, unless they re-join the room.
3829    cx_b.spawn(|cx| {
3830        Project::in_room(
3831            project_id,
3832            client_b.app_state.client.clone(),
3833            client_b.user_store().clone(),
3834            client_b.language_registry().clone(),
3835            FakeFs::new(cx.background_executor().clone()),
3836            cx,
3837        )
3838    })
3839    .await
3840    .unwrap_err();
3841
3842    // Simulate connection loss for client C and ensure client A observes client C leaving the project.
3843    client_c.wait_for_current_user(cx_c).await;
3844    server.forbid_connections();
3845    server.disconnect_client(client_c.peer_id().unwrap());
3846    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
3847    executor.run_until_parked();
3848
3849    project_a.read_with(cx_a, |project, _| {
3850        assert_eq!(project.collaborators().len(), 0);
3851    });
3852
3853    project_b2.read_with(cx_b, |project, cx| {
3854        assert!(project.is_disconnected(cx));
3855    });
3856
3857    project_c.read_with(cx_c, |project, cx| {
3858        assert!(project.is_disconnected(cx));
3859    });
3860}
3861
3862#[gpui::test(iterations = 10)]
3863async fn test_collaborating_with_diagnostics(
3864    executor: BackgroundExecutor,
3865    cx_a: &mut TestAppContext,
3866    cx_b: &mut TestAppContext,
3867    cx_c: &mut TestAppContext,
3868) {
3869    let mut server = TestServer::start(executor.clone()).await;
3870    let client_a = server.create_client(cx_a, "user_a").await;
3871    let client_b = server.create_client(cx_b, "user_b").await;
3872    let client_c = server.create_client(cx_c, "user_c").await;
3873    server
3874        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3875        .await;
3876    let active_call_a = cx_a.read(ActiveCall::global);
3877
3878    client_a.language_registry().add(Arc::new(Language::new(
3879        LanguageConfig {
3880            name: "Rust".into(),
3881            matcher: LanguageMatcher {
3882                path_suffixes: vec!["rs".to_string()],
3883                ..Default::default()
3884            },
3885            ..Default::default()
3886        },
3887        Some(tree_sitter_rust::LANGUAGE.into()),
3888    )));
3889    let mut fake_language_servers = client_a
3890        .language_registry()
3891        .register_fake_lsp("Rust", Default::default());
3892
3893    // Share a project as client A
3894    client_a
3895        .fs()
3896        .insert_tree(
3897            "/a",
3898            json!({
3899                "a.rs": "let one = two",
3900                "other.rs": "",
3901            }),
3902        )
3903        .await;
3904    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3905
3906    // Cause the language server to start.
3907    let _buffer = project_a
3908        .update(cx_a, |project, cx| {
3909            project.open_buffer(
3910                ProjectPath {
3911                    worktree_id,
3912                    path: Path::new("other.rs").into(),
3913                },
3914                cx,
3915            )
3916        })
3917        .await
3918        .unwrap();
3919
3920    // Simulate a language server reporting errors for a file.
3921    let mut fake_language_server = fake_language_servers.next().await.unwrap();
3922    fake_language_server
3923        .receive_notification::<lsp::notification::DidOpenTextDocument>()
3924        .await;
3925    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3926        lsp::PublishDiagnosticsParams {
3927            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3928            version: None,
3929            diagnostics: vec![lsp::Diagnostic {
3930                severity: Some(lsp::DiagnosticSeverity::WARNING),
3931                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3932                message: "message 0".to_string(),
3933                ..Default::default()
3934            }],
3935        },
3936    );
3937
3938    // Client A shares the project and, simultaneously, the language server
3939    // publishes a diagnostic. This is done to ensure that the server always
3940    // observes the latest diagnostics for a worktree.
3941    let project_id = active_call_a
3942        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3943        .await
3944        .unwrap();
3945    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3946        lsp::PublishDiagnosticsParams {
3947            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3948            version: None,
3949            diagnostics: vec![lsp::Diagnostic {
3950                severity: Some(lsp::DiagnosticSeverity::ERROR),
3951                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3952                message: "message 1".to_string(),
3953                ..Default::default()
3954            }],
3955        },
3956    );
3957
3958    // Join the worktree as client B.
3959    let project_b = client_b.join_remote_project(project_id, cx_b).await;
3960
3961    // Wait for server to see the diagnostics update.
3962    executor.run_until_parked();
3963
3964    // Ensure client B observes the new diagnostics.
3965
3966    project_b.read_with(cx_b, |project, cx| {
3967        assert_eq!(
3968            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
3969            &[(
3970                ProjectPath {
3971                    worktree_id,
3972                    path: Arc::from(Path::new("a.rs")),
3973                },
3974                LanguageServerId(0),
3975                DiagnosticSummary {
3976                    error_count: 1,
3977                    warning_count: 0,
3978                },
3979            )]
3980        )
3981    });
3982
3983    // Join project as client C and observe the diagnostics.
3984    let project_c = client_c.join_remote_project(project_id, cx_c).await;
3985    executor.run_until_parked();
3986    let project_c_diagnostic_summaries =
3987        Rc::new(RefCell::new(project_c.read_with(cx_c, |project, cx| {
3988            project.diagnostic_summaries(false, cx).collect::<Vec<_>>()
3989        })));
3990    project_c.update(cx_c, |_, cx| {
3991        let summaries = project_c_diagnostic_summaries.clone();
3992        cx.subscribe(&project_c, {
3993            move |p, _, event, cx| {
3994                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
3995                    *summaries.borrow_mut() = p.diagnostic_summaries(false, cx).collect();
3996                }
3997            }
3998        })
3999        .detach();
4000    });
4001
4002    executor.run_until_parked();
4003    assert_eq!(
4004        project_c_diagnostic_summaries.borrow().as_slice(),
4005        &[(
4006            ProjectPath {
4007                worktree_id,
4008                path: Arc::from(Path::new("a.rs")),
4009            },
4010            LanguageServerId(0),
4011            DiagnosticSummary {
4012                error_count: 1,
4013                warning_count: 0,
4014            },
4015        )]
4016    );
4017
4018    // Simulate a language server reporting more errors for a file.
4019    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
4020        lsp::PublishDiagnosticsParams {
4021            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
4022            version: None,
4023            diagnostics: vec![
4024                lsp::Diagnostic {
4025                    severity: Some(lsp::DiagnosticSeverity::ERROR),
4026                    range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
4027                    message: "message 1".to_string(),
4028                    ..Default::default()
4029                },
4030                lsp::Diagnostic {
4031                    severity: Some(lsp::DiagnosticSeverity::WARNING),
4032                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 13)),
4033                    message: "message 2".to_string(),
4034                    ..Default::default()
4035                },
4036            ],
4037        },
4038    );
4039
4040    // Clients B and C get the updated summaries
4041    executor.run_until_parked();
4042
4043    project_b.read_with(cx_b, |project, cx| {
4044        assert_eq!(
4045            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4046            [(
4047                ProjectPath {
4048                    worktree_id,
4049                    path: Arc::from(Path::new("a.rs")),
4050                },
4051                LanguageServerId(0),
4052                DiagnosticSummary {
4053                    error_count: 1,
4054                    warning_count: 1,
4055                },
4056            )]
4057        );
4058    });
4059
4060    project_c.read_with(cx_c, |project, cx| {
4061        assert_eq!(
4062            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4063            [(
4064                ProjectPath {
4065                    worktree_id,
4066                    path: Arc::from(Path::new("a.rs")),
4067                },
4068                LanguageServerId(0),
4069                DiagnosticSummary {
4070                    error_count: 1,
4071                    warning_count: 1,
4072                },
4073            )]
4074        );
4075    });
4076
4077    // Open the file with the errors on client B. They should be present.
4078    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4079    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4080
4081    buffer_b.read_with(cx_b, |buffer, _| {
4082        assert_eq!(
4083            buffer
4084                .snapshot()
4085                .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
4086                .collect::<Vec<_>>(),
4087            &[
4088                DiagnosticEntry {
4089                    range: Point::new(0, 4)..Point::new(0, 7),
4090                    diagnostic: Diagnostic {
4091                        group_id: 2,
4092                        message: "message 1".to_string(),
4093                        severity: lsp::DiagnosticSeverity::ERROR,
4094                        is_primary: true,
4095                        ..Default::default()
4096                    }
4097                },
4098                DiagnosticEntry {
4099                    range: Point::new(0, 10)..Point::new(0, 13),
4100                    diagnostic: Diagnostic {
4101                        group_id: 3,
4102                        severity: lsp::DiagnosticSeverity::WARNING,
4103                        message: "message 2".to_string(),
4104                        is_primary: true,
4105                        ..Default::default()
4106                    }
4107                }
4108            ]
4109        );
4110    });
4111
4112    // Simulate a language server reporting no errors for a file.
4113    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
4114        lsp::PublishDiagnosticsParams {
4115            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
4116            version: None,
4117            diagnostics: vec![],
4118        },
4119    );
4120    executor.run_until_parked();
4121
4122    project_a.read_with(cx_a, |project, cx| {
4123        assert_eq!(
4124            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4125            []
4126        )
4127    });
4128
4129    project_b.read_with(cx_b, |project, cx| {
4130        assert_eq!(
4131            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4132            []
4133        )
4134    });
4135
4136    project_c.read_with(cx_c, |project, cx| {
4137        assert_eq!(
4138            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
4139            []
4140        )
4141    });
4142}
4143
4144#[gpui::test(iterations = 10)]
4145async fn test_collaborating_with_lsp_progress_updates_and_diagnostics_ordering(
4146    executor: BackgroundExecutor,
4147    cx_a: &mut TestAppContext,
4148    cx_b: &mut TestAppContext,
4149) {
4150    let mut server = TestServer::start(executor.clone()).await;
4151    let client_a = server.create_client(cx_a, "user_a").await;
4152    let client_b = server.create_client(cx_b, "user_b").await;
4153    server
4154        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4155        .await;
4156
4157    client_a.language_registry().add(rust_lang());
4158    let mut fake_language_servers = client_a.language_registry().register_fake_lsp(
4159        "Rust",
4160        FakeLspAdapter {
4161            disk_based_diagnostics_progress_token: Some("the-disk-based-token".into()),
4162            disk_based_diagnostics_sources: vec!["the-disk-based-diagnostics-source".into()],
4163            ..Default::default()
4164        },
4165    );
4166
4167    let file_names = &["one.rs", "two.rs", "three.rs", "four.rs", "five.rs"];
4168    client_a
4169        .fs()
4170        .insert_tree(
4171            "/test",
4172            json!({
4173                "one.rs": "const ONE: usize = 1;",
4174                "two.rs": "const TWO: usize = 2;",
4175                "three.rs": "const THREE: usize = 3;",
4176                "four.rs": "const FOUR: usize = 3;",
4177                "five.rs": "const FIVE: usize = 3;",
4178            }),
4179        )
4180        .await;
4181
4182    let (project_a, worktree_id) = client_a.build_local_project("/test", cx_a).await;
4183
4184    // Share a project as client A
4185    let active_call_a = cx_a.read(ActiveCall::global);
4186    let project_id = active_call_a
4187        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4188        .await
4189        .unwrap();
4190
4191    // Join the project as client B and open all three files.
4192    let project_b = client_b.join_remote_project(project_id, cx_b).await;
4193    let guest_buffers = futures::future::try_join_all(file_names.iter().map(|file_name| {
4194        project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, file_name), cx))
4195    }))
4196    .await
4197    .unwrap();
4198
4199    // Simulate a language server reporting errors for a file.
4200    let fake_language_server = fake_language_servers.next().await.unwrap();
4201    fake_language_server
4202        .request::<lsp::request::WorkDoneProgressCreate>(lsp::WorkDoneProgressCreateParams {
4203            token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4204        })
4205        .await
4206        .unwrap();
4207    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
4208        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4209        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin(
4210            lsp::WorkDoneProgressBegin {
4211                title: "Progress Began".into(),
4212                ..Default::default()
4213            },
4214        )),
4215    });
4216    for file_name in file_names {
4217        fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
4218            lsp::PublishDiagnosticsParams {
4219                uri: lsp::Url::from_file_path(Path::new("/test").join(file_name)).unwrap(),
4220                version: None,
4221                diagnostics: vec![lsp::Diagnostic {
4222                    severity: Some(lsp::DiagnosticSeverity::WARNING),
4223                    source: Some("the-disk-based-diagnostics-source".into()),
4224                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
4225                    message: "message one".to_string(),
4226                    ..Default::default()
4227                }],
4228            },
4229        );
4230    }
4231    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
4232        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
4233        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End(
4234            lsp::WorkDoneProgressEnd { message: None },
4235        )),
4236    });
4237
4238    // When the "disk base diagnostics finished" message is received, the buffers'
4239    // diagnostics are expected to be present.
4240    let disk_based_diagnostics_finished = Arc::new(AtomicBool::new(false));
4241    project_b.update(cx_b, {
4242        let project_b = project_b.clone();
4243        let disk_based_diagnostics_finished = disk_based_diagnostics_finished.clone();
4244        move |_, cx| {
4245            cx.subscribe(&project_b, move |_, _, event, cx| {
4246                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
4247                    disk_based_diagnostics_finished.store(true, SeqCst);
4248                    for buffer in &guest_buffers {
4249                        assert_eq!(
4250                            buffer
4251                                .read(cx)
4252                                .snapshot()
4253                                .diagnostics_in_range::<_, usize>(0..5, false)
4254                                .count(),
4255                            1,
4256                            "expected a diagnostic for buffer {:?}",
4257                            buffer.read(cx).file().unwrap().path(),
4258                        );
4259                    }
4260                }
4261            })
4262            .detach();
4263        }
4264    });
4265
4266    executor.run_until_parked();
4267    assert!(disk_based_diagnostics_finished.load(SeqCst));
4268}
4269
4270#[gpui::test(iterations = 10)]
4271async fn test_reloading_buffer_manually(
4272    executor: BackgroundExecutor,
4273    cx_a: &mut TestAppContext,
4274    cx_b: &mut TestAppContext,
4275) {
4276    let mut server = TestServer::start(executor.clone()).await;
4277    let client_a = server.create_client(cx_a, "user_a").await;
4278    let client_b = server.create_client(cx_b, "user_b").await;
4279    server
4280        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4281        .await;
4282    let active_call_a = cx_a.read(ActiveCall::global);
4283
4284    client_a
4285        .fs()
4286        .insert_tree("/a", json!({ "a.rs": "let one = 1;" }))
4287        .await;
4288    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
4289    let buffer_a = project_a
4290        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
4291        .await
4292        .unwrap();
4293    let project_id = active_call_a
4294        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4295        .await
4296        .unwrap();
4297
4298    let project_b = client_b.join_remote_project(project_id, cx_b).await;
4299
4300    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4301    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4302    buffer_b.update(cx_b, |buffer, cx| {
4303        buffer.edit([(4..7, "six")], None, cx);
4304        buffer.edit([(10..11, "6")], None, cx);
4305        assert_eq!(buffer.text(), "let six = 6;");
4306        assert!(buffer.is_dirty());
4307        assert!(!buffer.has_conflict());
4308    });
4309    executor.run_until_parked();
4310
4311    buffer_a.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "let six = 6;"));
4312
4313    client_a
4314        .fs()
4315        .save(
4316            "/a/a.rs".as_ref(),
4317            &Rope::from("let seven = 7;"),
4318            LineEnding::Unix,
4319        )
4320        .await
4321        .unwrap();
4322    executor.run_until_parked();
4323
4324    buffer_a.read_with(cx_a, |buffer, _| assert!(buffer.has_conflict()));
4325
4326    buffer_b.read_with(cx_b, |buffer, _| assert!(buffer.has_conflict()));
4327
4328    project_b
4329        .update(cx_b, |project, cx| {
4330            project.reload_buffers(HashSet::from_iter([buffer_b.clone()]), true, cx)
4331        })
4332        .await
4333        .unwrap();
4334
4335    buffer_a.read_with(cx_a, |buffer, _| {
4336        assert_eq!(buffer.text(), "let seven = 7;");
4337        assert!(!buffer.is_dirty());
4338        assert!(!buffer.has_conflict());
4339    });
4340
4341    buffer_b.read_with(cx_b, |buffer, _| {
4342        assert_eq!(buffer.text(), "let seven = 7;");
4343        assert!(!buffer.is_dirty());
4344        assert!(!buffer.has_conflict());
4345    });
4346
4347    buffer_a.update(cx_a, |buffer, cx| {
4348        // Undoing on the host is a no-op when the reload was initiated by the guest.
4349        buffer.undo(cx);
4350        assert_eq!(buffer.text(), "let seven = 7;");
4351        assert!(!buffer.is_dirty());
4352        assert!(!buffer.has_conflict());
4353    });
4354    buffer_b.update(cx_b, |buffer, cx| {
4355        // Undoing on the guest rolls back the buffer to before it was reloaded but the conflict gets cleared.
4356        buffer.undo(cx);
4357        assert_eq!(buffer.text(), "let six = 6;");
4358        assert!(buffer.is_dirty());
4359        assert!(!buffer.has_conflict());
4360    });
4361}
4362
4363#[gpui::test(iterations = 10)]
4364async fn test_formatting_buffer(
4365    executor: BackgroundExecutor,
4366    cx_a: &mut TestAppContext,
4367    cx_b: &mut TestAppContext,
4368) {
4369    executor.allow_parking();
4370    let mut server = TestServer::start(executor.clone()).await;
4371    let client_a = server.create_client(cx_a, "user_a").await;
4372    let client_b = server.create_client(cx_b, "user_b").await;
4373    server
4374        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4375        .await;
4376    let active_call_a = cx_a.read(ActiveCall::global);
4377
4378    client_a.language_registry().add(rust_lang());
4379    let mut fake_language_servers = client_a
4380        .language_registry()
4381        .register_fake_lsp("Rust", FakeLspAdapter::default());
4382
4383    // Here we insert a fake tree with a directory that exists on disk. This is needed
4384    // because later we'll invoke a command, which requires passing a working directory
4385    // that points to a valid location on disk.
4386    let directory = env::current_dir().unwrap();
4387    client_a
4388        .fs()
4389        .insert_tree(&directory, json!({ "a.rs": "let one = \"two\"" }))
4390        .await;
4391    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
4392    let project_id = active_call_a
4393        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4394        .await
4395        .unwrap();
4396    let project_b = client_b.join_remote_project(project_id, cx_b).await;
4397
4398    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4399    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4400
4401    let fake_language_server = fake_language_servers.next().await.unwrap();
4402    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
4403        Ok(Some(vec![
4404            lsp::TextEdit {
4405                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 4)),
4406                new_text: "h".to_string(),
4407            },
4408            lsp::TextEdit {
4409                range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 7)),
4410                new_text: "y".to_string(),
4411            },
4412        ]))
4413    });
4414
4415    project_b
4416        .update(cx_b, |project, cx| {
4417            project.format(
4418                HashSet::from_iter([buffer_b.clone()]),
4419                true,
4420                FormatTrigger::Save,
4421                FormatTarget::Buffer,
4422                cx,
4423            )
4424        })
4425        .await
4426        .unwrap();
4427
4428    // The edits from the LSP are applied, and a final newline is added.
4429    assert_eq!(
4430        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4431        "let honey = \"two\"\n"
4432    );
4433
4434    // Ensure buffer can be formatted using an external command. Notice how the
4435    // host's configuration is honored as opposed to using the guest's settings.
4436    cx_a.update(|cx| {
4437        SettingsStore::update_global(cx, |store, cx| {
4438            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4439                file.defaults.formatter = Some(SelectedFormatter::List(FormatterList(
4440                    vec![Formatter::External {
4441                        command: "awk".into(),
4442                        arguments: Some(vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()].into()),
4443                    }]
4444                    .into(),
4445                )));
4446            });
4447        });
4448    });
4449    project_b
4450        .update(cx_b, |project, cx| {
4451            project.format(
4452                HashSet::from_iter([buffer_b.clone()]),
4453                true,
4454                FormatTrigger::Save,
4455                FormatTarget::Buffer,
4456                cx,
4457            )
4458        })
4459        .await
4460        .unwrap();
4461    assert_eq!(
4462        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4463        format!("let honey = \"{}/a.rs\"\n", directory.to_str().unwrap())
4464    );
4465}
4466
4467#[gpui::test(iterations = 10)]
4468async fn test_prettier_formatting_buffer(
4469    executor: BackgroundExecutor,
4470    cx_a: &mut TestAppContext,
4471    cx_b: &mut TestAppContext,
4472) {
4473    let mut server = TestServer::start(executor.clone()).await;
4474    let client_a = server.create_client(cx_a, "user_a").await;
4475    let client_b = server.create_client(cx_b, "user_b").await;
4476    server
4477        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4478        .await;
4479    let active_call_a = cx_a.read(ActiveCall::global);
4480
4481    let test_plugin = "test_plugin";
4482
4483    client_a.language_registry().add(Arc::new(Language::new(
4484        LanguageConfig {
4485            name: "TypeScript".into(),
4486            matcher: LanguageMatcher {
4487                path_suffixes: vec!["ts".to_string()],
4488                ..Default::default()
4489            },
4490            ..Default::default()
4491        },
4492        Some(tree_sitter_rust::LANGUAGE.into()),
4493    )));
4494    let mut fake_language_servers = client_a.language_registry().register_fake_lsp(
4495        "TypeScript",
4496        FakeLspAdapter {
4497            prettier_plugins: vec![test_plugin],
4498            ..Default::default()
4499        },
4500    );
4501
4502    // Here we insert a fake tree with a directory that exists on disk. This is needed
4503    // because later we'll invoke a command, which requires passing a working directory
4504    // that points to a valid location on disk.
4505    let directory = env::current_dir().unwrap();
4506    let buffer_text = "let one = \"two\"";
4507    client_a
4508        .fs()
4509        .insert_tree(&directory, json!({ "a.ts": buffer_text }))
4510        .await;
4511    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
4512    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
4513    let open_buffer = project_a.update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.ts"), cx));
4514    let buffer_a = cx_a.executor().spawn(open_buffer).await.unwrap();
4515
4516    let project_id = active_call_a
4517        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4518        .await
4519        .unwrap();
4520    let project_b = client_b.join_remote_project(project_id, cx_b).await;
4521    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.ts"), cx));
4522    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4523
4524    cx_a.update(|cx| {
4525        SettingsStore::update_global(cx, |store, cx| {
4526            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4527                file.defaults.formatter = Some(SelectedFormatter::Auto);
4528                file.defaults.prettier = Some(PrettierSettings {
4529                    allowed: true,
4530                    ..PrettierSettings::default()
4531                });
4532            });
4533        });
4534    });
4535    cx_b.update(|cx| {
4536        SettingsStore::update_global(cx, |store, cx| {
4537            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4538                file.defaults.formatter = Some(SelectedFormatter::List(FormatterList(
4539                    vec![Formatter::LanguageServer { name: None }].into(),
4540                )));
4541                file.defaults.prettier = Some(PrettierSettings {
4542                    allowed: true,
4543                    ..PrettierSettings::default()
4544                });
4545            });
4546        });
4547    });
4548    let fake_language_server = fake_language_servers.next().await.unwrap();
4549    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
4550        panic!(
4551            "Unexpected: prettier should be preferred since it's enabled and language supports it"
4552        )
4553    });
4554
4555    project_b
4556        .update(cx_b, |project, cx| {
4557            project.format(
4558                HashSet::from_iter([buffer_b.clone()]),
4559                true,
4560                FormatTrigger::Save,
4561                FormatTarget::Buffer,
4562                cx,
4563            )
4564        })
4565        .await
4566        .unwrap();
4567
4568    executor.run_until_parked();
4569    assert_eq!(
4570        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4571        buffer_text.to_string() + "\n" + prettier_format_suffix,
4572        "Prettier formatting was not applied to client buffer after client's request"
4573    );
4574
4575    project_a
4576        .update(cx_a, |project, cx| {
4577            project.format(
4578                HashSet::from_iter([buffer_a.clone()]),
4579                true,
4580                FormatTrigger::Manual,
4581                FormatTarget::Buffer,
4582                cx,
4583            )
4584        })
4585        .await
4586        .unwrap();
4587
4588    executor.run_until_parked();
4589    assert_eq!(
4590        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4591        buffer_text.to_string() + "\n" + prettier_format_suffix + "\n" + prettier_format_suffix,
4592        "Prettier formatting was not applied to client buffer after host's request"
4593    );
4594}
4595
4596#[gpui::test(iterations = 10)]
4597async fn test_definition(
4598    executor: BackgroundExecutor,
4599    cx_a: &mut TestAppContext,
4600    cx_b: &mut TestAppContext,
4601) {
4602    let mut server = TestServer::start(executor.clone()).await;
4603    let client_a = server.create_client(cx_a, "user_a").await;
4604    let client_b = server.create_client(cx_b, "user_b").await;
4605    server
4606        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4607        .await;
4608    let active_call_a = cx_a.read(ActiveCall::global);
4609
4610    let mut fake_language_servers = client_a
4611        .language_registry()
4612        .register_fake_lsp("Rust", Default::default());
4613    client_a.language_registry().add(rust_lang());
4614
4615    client_a
4616        .fs()
4617        .insert_tree(
4618            "/root",
4619            json!({
4620                "dir-1": {
4621                    "a.rs": "const ONE: usize = b::TWO + b::THREE;",
4622                },
4623                "dir-2": {
4624                    "b.rs": "const TWO: c::T2 = 2;\nconst THREE: usize = 3;",
4625                    "c.rs": "type T2 = usize;",
4626                }
4627            }),
4628        )
4629        .await;
4630    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4631    let project_id = active_call_a
4632        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4633        .await
4634        .unwrap();
4635    let project_b = client_b.join_remote_project(project_id, cx_b).await;
4636
4637    // Open the file on client B.
4638    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4639    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4640
4641    // Request the definition of a symbol as the guest.
4642    let fake_language_server = fake_language_servers.next().await.unwrap();
4643    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4644        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4645            lsp::Location::new(
4646                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4647                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4648            ),
4649        )))
4650    });
4651
4652    let definitions_1 = project_b
4653        .update(cx_b, |p, cx| p.definition(&buffer_b, 23, cx))
4654        .await
4655        .unwrap();
4656    cx_b.read(|cx| {
4657        assert_eq!(definitions_1.len(), 1);
4658        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4659        let target_buffer = definitions_1[0].target.buffer.read(cx);
4660        assert_eq!(
4661            target_buffer.text(),
4662            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4663        );
4664        assert_eq!(
4665            definitions_1[0].target.range.to_point(target_buffer),
4666            Point::new(0, 6)..Point::new(0, 9)
4667        );
4668    });
4669
4670    // Try getting more definitions for the same buffer, ensuring the buffer gets reused from
4671    // the previous call to `definition`.
4672    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4673        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4674            lsp::Location::new(
4675                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4676                lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)),
4677            ),
4678        )))
4679    });
4680
4681    let definitions_2 = project_b
4682        .update(cx_b, |p, cx| p.definition(&buffer_b, 33, cx))
4683        .await
4684        .unwrap();
4685    cx_b.read(|cx| {
4686        assert_eq!(definitions_2.len(), 1);
4687        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4688        let target_buffer = definitions_2[0].target.buffer.read(cx);
4689        assert_eq!(
4690            target_buffer.text(),
4691            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4692        );
4693        assert_eq!(
4694            definitions_2[0].target.range.to_point(target_buffer),
4695            Point::new(1, 6)..Point::new(1, 11)
4696        );
4697    });
4698    assert_eq!(
4699        definitions_1[0].target.buffer,
4700        definitions_2[0].target.buffer
4701    );
4702
4703    fake_language_server.handle_request::<lsp::request::GotoTypeDefinition, _, _>(
4704        |req, _| async move {
4705            assert_eq!(
4706                req.text_document_position_params.position,
4707                lsp::Position::new(0, 7)
4708            );
4709            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4710                lsp::Location::new(
4711                    lsp::Url::from_file_path("/root/dir-2/c.rs").unwrap(),
4712                    lsp::Range::new(lsp::Position::new(0, 5), lsp::Position::new(0, 7)),
4713                ),
4714            )))
4715        },
4716    );
4717
4718    let type_definitions = project_b
4719        .update(cx_b, |p, cx| p.type_definition(&buffer_b, 7, cx))
4720        .await
4721        .unwrap();
4722    cx_b.read(|cx| {
4723        assert_eq!(type_definitions.len(), 1);
4724        let target_buffer = type_definitions[0].target.buffer.read(cx);
4725        assert_eq!(target_buffer.text(), "type T2 = usize;");
4726        assert_eq!(
4727            type_definitions[0].target.range.to_point(target_buffer),
4728            Point::new(0, 5)..Point::new(0, 7)
4729        );
4730    });
4731}
4732
4733#[gpui::test(iterations = 10)]
4734async fn test_references(
4735    executor: BackgroundExecutor,
4736    cx_a: &mut TestAppContext,
4737    cx_b: &mut TestAppContext,
4738) {
4739    let mut server = TestServer::start(executor.clone()).await;
4740    let client_a = server.create_client(cx_a, "user_a").await;
4741    let client_b = server.create_client(cx_b, "user_b").await;
4742    server
4743        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4744        .await;
4745    let active_call_a = cx_a.read(ActiveCall::global);
4746
4747    client_a.language_registry().add(rust_lang());
4748    let mut fake_language_servers = client_a.language_registry().register_fake_lsp(
4749        "Rust",
4750        FakeLspAdapter {
4751            name: "my-fake-lsp-adapter",
4752            capabilities: lsp::ServerCapabilities {
4753                references_provider: Some(lsp::OneOf::Left(true)),
4754                ..Default::default()
4755            },
4756            ..Default::default()
4757        },
4758    );
4759
4760    client_a
4761        .fs()
4762        .insert_tree(
4763            "/root",
4764            json!({
4765                "dir-1": {
4766                    "one.rs": "const ONE: usize = 1;",
4767                    "two.rs": "const TWO: usize = one::ONE + one::ONE;",
4768                },
4769                "dir-2": {
4770                    "three.rs": "const THREE: usize = two::TWO + one::ONE;",
4771                }
4772            }),
4773        )
4774        .await;
4775    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4776    let project_id = active_call_a
4777        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4778        .await
4779        .unwrap();
4780    let project_b = client_b.join_remote_project(project_id, cx_b).await;
4781
4782    // Open the file on client B.
4783    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx));
4784    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4785
4786    // Request references to a symbol as the guest.
4787    let fake_language_server = fake_language_servers.next().await.unwrap();
4788    let (lsp_response_tx, rx) = mpsc::unbounded::<Result<Option<Vec<lsp::Location>>>>();
4789    fake_language_server.handle_request::<lsp::request::References, _, _>({
4790        let rx = Arc::new(Mutex::new(Some(rx)));
4791        move |params, _| {
4792            assert_eq!(
4793                params.text_document_position.text_document.uri.as_str(),
4794                "file:///root/dir-1/one.rs"
4795            );
4796            let rx = rx.clone();
4797            async move {
4798                let mut response_rx = rx.lock().take().unwrap();
4799                let result = response_rx.next().await.unwrap();
4800                *rx.lock() = Some(response_rx);
4801                result
4802            }
4803        }
4804    });
4805
4806    let references = project_b.update(cx_b, |p, cx| p.references(&buffer_b, 7, cx));
4807
4808    // User is informed that a request is pending.
4809    executor.run_until_parked();
4810    project_b.read_with(cx_b, |project, cx| {
4811        let status = project.language_server_statuses(cx).next().unwrap().1;
4812        assert_eq!(status.name, "my-fake-lsp-adapter");
4813        assert_eq!(
4814            status.pending_work.values().next().unwrap().message,
4815            Some("Finding references...".into())
4816        );
4817    });
4818
4819    // Cause the language server to respond.
4820    lsp_response_tx
4821        .unbounded_send(Ok(Some(vec![
4822            lsp::Location {
4823                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4824                range: lsp::Range::new(lsp::Position::new(0, 24), lsp::Position::new(0, 27)),
4825            },
4826            lsp::Location {
4827                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4828                range: lsp::Range::new(lsp::Position::new(0, 35), lsp::Position::new(0, 38)),
4829            },
4830            lsp::Location {
4831                uri: lsp::Url::from_file_path("/root/dir-2/three.rs").unwrap(),
4832                range: lsp::Range::new(lsp::Position::new(0, 37), lsp::Position::new(0, 40)),
4833            },
4834        ])))
4835        .unwrap();
4836
4837    let references = references.await.unwrap();
4838    executor.run_until_parked();
4839    project_b.read_with(cx_b, |project, cx| {
4840        // User is informed that a request is no longer pending.
4841        let status = project.language_server_statuses(cx).next().unwrap().1;
4842        assert!(status.pending_work.is_empty());
4843
4844        assert_eq!(references.len(), 3);
4845        assert_eq!(project.worktrees(cx).count(), 2);
4846
4847        let two_buffer = references[0].buffer.read(cx);
4848        let three_buffer = references[2].buffer.read(cx);
4849        assert_eq!(
4850            two_buffer.file().unwrap().path().as_ref(),
4851            Path::new("two.rs")
4852        );
4853        assert_eq!(references[1].buffer, references[0].buffer);
4854        assert_eq!(
4855            three_buffer.file().unwrap().full_path(cx),
4856            Path::new("/root/dir-2/three.rs")
4857        );
4858
4859        assert_eq!(references[0].range.to_offset(two_buffer), 24..27);
4860        assert_eq!(references[1].range.to_offset(two_buffer), 35..38);
4861        assert_eq!(references[2].range.to_offset(three_buffer), 37..40);
4862    });
4863
4864    let references = project_b.update(cx_b, |p, cx| p.references(&buffer_b, 7, cx));
4865
4866    // User is informed that a request is pending.
4867    executor.run_until_parked();
4868    project_b.read_with(cx_b, |project, cx| {
4869        let status = project.language_server_statuses(cx).next().unwrap().1;
4870        assert_eq!(status.name, "my-fake-lsp-adapter");
4871        assert_eq!(
4872            status.pending_work.values().next().unwrap().message,
4873            Some("Finding references...".into())
4874        );
4875    });
4876
4877    // Cause the LSP request to fail.
4878    lsp_response_tx
4879        .unbounded_send(Err(anyhow!("can't find references")))
4880        .unwrap();
4881    references.await.unwrap_err();
4882
4883    // User is informed that the request is no longer pending.
4884    executor.run_until_parked();
4885    project_b.read_with(cx_b, |project, cx| {
4886        let status = project.language_server_statuses(cx).next().unwrap().1;
4887        assert!(status.pending_work.is_empty());
4888    });
4889}
4890
4891#[gpui::test(iterations = 10)]
4892async fn test_project_search(
4893    executor: BackgroundExecutor,
4894    cx_a: &mut TestAppContext,
4895    cx_b: &mut TestAppContext,
4896) {
4897    let mut server = TestServer::start(executor.clone()).await;
4898    let client_a = server.create_client(cx_a, "user_a").await;
4899    let client_b = server.create_client(cx_b, "user_b").await;
4900    server
4901        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4902        .await;
4903    let active_call_a = cx_a.read(ActiveCall::global);
4904
4905    client_a
4906        .fs()
4907        .insert_tree(
4908            "/root",
4909            json!({
4910                "dir-1": {
4911                    "a": "hello world",
4912                    "b": "goodnight moon",
4913                    "c": "a world of goo",
4914                    "d": "world champion of clown world",
4915                },
4916                "dir-2": {
4917                    "e": "disney world is fun",
4918                }
4919            }),
4920        )
4921        .await;
4922    let (project_a, _) = client_a.build_local_project("/root/dir-1", cx_a).await;
4923    let (worktree_2, _) = project_a
4924        .update(cx_a, |p, cx| {
4925            p.find_or_create_worktree("/root/dir-2", true, cx)
4926        })
4927        .await
4928        .unwrap();
4929    worktree_2
4930        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
4931        .await;
4932    let project_id = active_call_a
4933        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4934        .await
4935        .unwrap();
4936
4937    let project_b = client_b.join_remote_project(project_id, cx_b).await;
4938
4939    // Perform a search as the guest.
4940    let mut results = HashMap::default();
4941    let mut search_rx = project_b.update(cx_b, |project, cx| {
4942        project.search(
4943            SearchQuery::text(
4944                "world",
4945                false,
4946                false,
4947                false,
4948                Default::default(),
4949                Default::default(),
4950                None,
4951            )
4952            .unwrap(),
4953            cx,
4954        )
4955    });
4956    while let Some(result) = search_rx.next().await {
4957        match result {
4958            SearchResult::Buffer { buffer, ranges } => {
4959                results.entry(buffer).or_insert(ranges);
4960            }
4961            SearchResult::LimitReached => {
4962                panic!("Unexpectedly reached search limit in tests. If you do want to assert limit-reached, change this panic call.")
4963            }
4964        };
4965    }
4966
4967    let mut ranges_by_path = results
4968        .into_iter()
4969        .map(|(buffer, ranges)| {
4970            buffer.read_with(cx_b, |buffer, cx| {
4971                let path = buffer.file().unwrap().full_path(cx);
4972                let offset_ranges = ranges
4973                    .into_iter()
4974                    .map(|range| range.to_offset(buffer))
4975                    .collect::<Vec<_>>();
4976                (path, offset_ranges)
4977            })
4978        })
4979        .collect::<Vec<_>>();
4980    ranges_by_path.sort_by_key(|(path, _)| path.clone());
4981
4982    assert_eq!(
4983        ranges_by_path,
4984        &[
4985            (PathBuf::from("dir-1/a"), vec![6..11]),
4986            (PathBuf::from("dir-1/c"), vec![2..7]),
4987            (PathBuf::from("dir-1/d"), vec![0..5, 24..29]),
4988            (PathBuf::from("dir-2/e"), vec![7..12]),
4989        ]
4990    );
4991}
4992
4993#[gpui::test(iterations = 10)]
4994async fn test_document_highlights(
4995    executor: BackgroundExecutor,
4996    cx_a: &mut TestAppContext,
4997    cx_b: &mut TestAppContext,
4998) {
4999    let mut server = TestServer::start(executor.clone()).await;
5000    let client_a = server.create_client(cx_a, "user_a").await;
5001    let client_b = server.create_client(cx_b, "user_b").await;
5002    server
5003        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5004        .await;
5005    let active_call_a = cx_a.read(ActiveCall::global);
5006
5007    client_a
5008        .fs()
5009        .insert_tree(
5010            "/root-1",
5011            json!({
5012                "main.rs": "fn double(number: i32) -> i32 { number + number }",
5013            }),
5014        )
5015        .await;
5016
5017    let mut fake_language_servers = client_a
5018        .language_registry()
5019        .register_fake_lsp("Rust", Default::default());
5020    client_a.language_registry().add(rust_lang());
5021
5022    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
5023    let project_id = active_call_a
5024        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5025        .await
5026        .unwrap();
5027    let project_b = client_b.join_remote_project(project_id, cx_b).await;
5028
5029    // Open the file on client B.
5030    let open_b = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx));
5031    let buffer_b = cx_b.executor().spawn(open_b).await.unwrap();
5032
5033    // Request document highlights as the guest.
5034    let fake_language_server = fake_language_servers.next().await.unwrap();
5035    fake_language_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
5036        |params, _| async move {
5037            assert_eq!(
5038                params
5039                    .text_document_position_params
5040                    .text_document
5041                    .uri
5042                    .as_str(),
5043                "file:///root-1/main.rs"
5044            );
5045            assert_eq!(
5046                params.text_document_position_params.position,
5047                lsp::Position::new(0, 34)
5048            );
5049            Ok(Some(vec![
5050                lsp::DocumentHighlight {
5051                    kind: Some(lsp::DocumentHighlightKind::WRITE),
5052                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 16)),
5053                },
5054                lsp::DocumentHighlight {
5055                    kind: Some(lsp::DocumentHighlightKind::READ),
5056                    range: lsp::Range::new(lsp::Position::new(0, 32), lsp::Position::new(0, 38)),
5057                },
5058                lsp::DocumentHighlight {
5059                    kind: Some(lsp::DocumentHighlightKind::READ),
5060                    range: lsp::Range::new(lsp::Position::new(0, 41), lsp::Position::new(0, 47)),
5061                },
5062            ]))
5063        },
5064    );
5065
5066    let highlights = project_b
5067        .update(cx_b, |p, cx| p.document_highlights(&buffer_b, 34, cx))
5068        .await
5069        .unwrap();
5070
5071    buffer_b.read_with(cx_b, |buffer, _| {
5072        let snapshot = buffer.snapshot();
5073
5074        let highlights = highlights
5075            .into_iter()
5076            .map(|highlight| (highlight.kind, highlight.range.to_offset(&snapshot)))
5077            .collect::<Vec<_>>();
5078        assert_eq!(
5079            highlights,
5080            &[
5081                (lsp::DocumentHighlightKind::WRITE, 10..16),
5082                (lsp::DocumentHighlightKind::READ, 32..38),
5083                (lsp::DocumentHighlightKind::READ, 41..47)
5084            ]
5085        )
5086    });
5087}
5088
5089#[gpui::test(iterations = 10)]
5090async fn test_lsp_hover(
5091    executor: BackgroundExecutor,
5092    cx_a: &mut TestAppContext,
5093    cx_b: &mut TestAppContext,
5094) {
5095    let mut server = TestServer::start(executor.clone()).await;
5096    let client_a = server.create_client(cx_a, "user_a").await;
5097    let client_b = server.create_client(cx_b, "user_b").await;
5098    server
5099        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5100        .await;
5101    let active_call_a = cx_a.read(ActiveCall::global);
5102
5103    client_a
5104        .fs()
5105        .insert_tree(
5106            "/root-1",
5107            json!({
5108                "main.rs": "use std::collections::HashMap;",
5109            }),
5110        )
5111        .await;
5112
5113    client_a.language_registry().add(rust_lang());
5114    let language_server_names = ["rust-analyzer", "CrabLang-ls"];
5115    let mut language_servers = [
5116        client_a.language_registry().register_fake_lsp(
5117            "Rust",
5118            FakeLspAdapter {
5119                name: "rust-analyzer",
5120                capabilities: lsp::ServerCapabilities {
5121                    hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
5122                    ..lsp::ServerCapabilities::default()
5123                },
5124                ..FakeLspAdapter::default()
5125            },
5126        ),
5127        client_a.language_registry().register_fake_lsp(
5128            "Rust",
5129            FakeLspAdapter {
5130                name: "CrabLang-ls",
5131                capabilities: lsp::ServerCapabilities {
5132                    hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
5133                    ..lsp::ServerCapabilities::default()
5134                },
5135                ..FakeLspAdapter::default()
5136            },
5137        ),
5138    ];
5139
5140    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
5141    let project_id = active_call_a
5142        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5143        .await
5144        .unwrap();
5145    let project_b = client_b.join_remote_project(project_id, cx_b).await;
5146
5147    // Open the file as the guest
5148    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx));
5149    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
5150
5151    let mut servers_with_hover_requests = HashMap::default();
5152    for i in 0..language_server_names.len() {
5153        let new_server = language_servers[i].next().await.unwrap_or_else(|| {
5154            panic!(
5155                "Failed to get language server #{i} with name {}",
5156                &language_server_names[i]
5157            )
5158        });
5159        let new_server_name = new_server.server.name();
5160        assert!(
5161            !servers_with_hover_requests.contains_key(new_server_name),
5162            "Unexpected: initialized server with the same name twice. Name: `{new_server_name}`"
5163        );
5164        let new_server_name = new_server_name.to_string();
5165        match new_server_name.as_str() {
5166            "CrabLang-ls" => {
5167                servers_with_hover_requests.insert(
5168                    new_server_name.clone(),
5169                    new_server.handle_request::<lsp::request::HoverRequest, _, _>(
5170                        move |params, _| {
5171                            assert_eq!(
5172                                params
5173                                    .text_document_position_params
5174                                    .text_document
5175                                    .uri
5176                                    .as_str(),
5177                                "file:///root-1/main.rs"
5178                            );
5179                            let name = new_server_name.clone();
5180                            async move {
5181                                Ok(Some(lsp::Hover {
5182                                    contents: lsp::HoverContents::Scalar(
5183                                        lsp::MarkedString::String(format!("{name} hover")),
5184                                    ),
5185                                    range: None,
5186                                }))
5187                            }
5188                        },
5189                    ),
5190                );
5191            }
5192            "rust-analyzer" => {
5193                servers_with_hover_requests.insert(
5194                    new_server_name.clone(),
5195                    new_server.handle_request::<lsp::request::HoverRequest, _, _>(
5196                        |params, _| async move {
5197                            assert_eq!(
5198                                params
5199                                    .text_document_position_params
5200                                    .text_document
5201                                    .uri
5202                                    .as_str(),
5203                                "file:///root-1/main.rs"
5204                            );
5205                            assert_eq!(
5206                                params.text_document_position_params.position,
5207                                lsp::Position::new(0, 22)
5208                            );
5209                            Ok(Some(lsp::Hover {
5210                                contents: lsp::HoverContents::Array(vec![
5211                                    lsp::MarkedString::String("Test hover content.".to_string()),
5212                                    lsp::MarkedString::LanguageString(lsp::LanguageString {
5213                                        language: "Rust".to_string(),
5214                                        value: "let foo = 42;".to_string(),
5215                                    }),
5216                                ]),
5217                                range: Some(lsp::Range::new(
5218                                    lsp::Position::new(0, 22),
5219                                    lsp::Position::new(0, 29),
5220                                )),
5221                            }))
5222                        },
5223                    ),
5224                );
5225            }
5226            unexpected => panic!("Unexpected server name: {unexpected}"),
5227        }
5228    }
5229
5230    // Request hover information as the guest.
5231    let mut hovers = project_b
5232        .update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
5233        .await;
5234    assert_eq!(
5235        hovers.len(),
5236        2,
5237        "Expected two hovers from both language servers, but got: {hovers:?}"
5238    );
5239
5240    let _: Vec<()> = futures::future::join_all(servers_with_hover_requests.into_values().map(
5241        |mut hover_request| async move {
5242            hover_request
5243                .next()
5244                .await
5245                .expect("All hover requests should have been triggered")
5246        },
5247    ))
5248    .await;
5249
5250    hovers.sort_by_key(|hover| hover.contents.len());
5251    let first_hover = hovers.first().cloned().unwrap();
5252    assert_eq!(
5253        first_hover.contents,
5254        vec![project::HoverBlock {
5255            text: "CrabLang-ls hover".to_string(),
5256            kind: HoverBlockKind::Markdown,
5257        },]
5258    );
5259    let second_hover = hovers.last().cloned().unwrap();
5260    assert_eq!(
5261        second_hover.contents,
5262        vec![
5263            project::HoverBlock {
5264                text: "Test hover content.".to_string(),
5265                kind: HoverBlockKind::Markdown,
5266            },
5267            project::HoverBlock {
5268                text: "let foo = 42;".to_string(),
5269                kind: HoverBlockKind::Code {
5270                    language: "Rust".to_string()
5271                },
5272            }
5273        ]
5274    );
5275    buffer_b.read_with(cx_b, |buffer, _| {
5276        let snapshot = buffer.snapshot();
5277        assert_eq!(second_hover.range.unwrap().to_offset(&snapshot), 22..29);
5278    });
5279}
5280
5281#[gpui::test(iterations = 10)]
5282async fn test_project_symbols(
5283    executor: BackgroundExecutor,
5284    cx_a: &mut TestAppContext,
5285    cx_b: &mut TestAppContext,
5286) {
5287    let mut server = TestServer::start(executor.clone()).await;
5288    let client_a = server.create_client(cx_a, "user_a").await;
5289    let client_b = server.create_client(cx_b, "user_b").await;
5290    server
5291        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5292        .await;
5293    let active_call_a = cx_a.read(ActiveCall::global);
5294
5295    client_a.language_registry().add(rust_lang());
5296    let mut fake_language_servers = client_a
5297        .language_registry()
5298        .register_fake_lsp("Rust", Default::default());
5299
5300    client_a
5301        .fs()
5302        .insert_tree(
5303            "/code",
5304            json!({
5305                "crate-1": {
5306                    "one.rs": "const ONE: usize = 1;",
5307                },
5308                "crate-2": {
5309                    "two.rs": "const TWO: usize = 2; const THREE: usize = 3;",
5310                },
5311                "private": {
5312                    "passwords.txt": "the-password",
5313                }
5314            }),
5315        )
5316        .await;
5317    let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await;
5318    let project_id = active_call_a
5319        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5320        .await
5321        .unwrap();
5322    let project_b = client_b.join_remote_project(project_id, cx_b).await;
5323
5324    // Cause the language server to start.
5325    let open_buffer_task =
5326        project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx));
5327    let _buffer = cx_b.executor().spawn(open_buffer_task).await.unwrap();
5328
5329    let fake_language_server = fake_language_servers.next().await.unwrap();
5330    fake_language_server.handle_request::<lsp::WorkspaceSymbolRequest, _, _>(|_, _| async move {
5331        Ok(Some(lsp::WorkspaceSymbolResponse::Flat(vec![
5332            #[allow(deprecated)]
5333            lsp::SymbolInformation {
5334                name: "TWO".into(),
5335                location: lsp::Location {
5336                    uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
5337                    range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5338                },
5339                kind: lsp::SymbolKind::CONSTANT,
5340                tags: None,
5341                container_name: None,
5342                deprecated: None,
5343            },
5344        ])))
5345    });
5346
5347    // Request the definition of a symbol as the guest.
5348    let symbols = project_b
5349        .update(cx_b, |p, cx| p.symbols("two", cx))
5350        .await
5351        .unwrap();
5352    assert_eq!(symbols.len(), 1);
5353    assert_eq!(symbols[0].name, "TWO");
5354
5355    // Open one of the returned symbols.
5356    let buffer_b_2 = project_b
5357        .update(cx_b, |project, cx| {
5358            project.open_buffer_for_symbol(&symbols[0], cx)
5359        })
5360        .await
5361        .unwrap();
5362
5363    buffer_b_2.read_with(cx_b, |buffer, cx| {
5364        assert_eq!(
5365            buffer.file().unwrap().full_path(cx),
5366            Path::new("/code/crate-2/two.rs")
5367        );
5368    });
5369
5370    // Attempt to craft a symbol and violate host's privacy by opening an arbitrary file.
5371    let mut fake_symbol = symbols[0].clone();
5372    fake_symbol.path.path = Path::new("/code/secrets").into();
5373    let error = project_b
5374        .update(cx_b, |project, cx| {
5375            project.open_buffer_for_symbol(&fake_symbol, cx)
5376        })
5377        .await
5378        .unwrap_err();
5379    assert!(error.to_string().contains("invalid symbol signature"));
5380}
5381
5382#[gpui::test(iterations = 10)]
5383async fn test_open_buffer_while_getting_definition_pointing_to_it(
5384    executor: BackgroundExecutor,
5385    cx_a: &mut TestAppContext,
5386    cx_b: &mut TestAppContext,
5387    mut rng: StdRng,
5388) {
5389    let mut server = TestServer::start(executor.clone()).await;
5390    let client_a = server.create_client(cx_a, "user_a").await;
5391    let client_b = server.create_client(cx_b, "user_b").await;
5392    server
5393        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5394        .await;
5395    let active_call_a = cx_a.read(ActiveCall::global);
5396
5397    client_a.language_registry().add(rust_lang());
5398    let mut fake_language_servers = client_a
5399        .language_registry()
5400        .register_fake_lsp("Rust", Default::default());
5401
5402    client_a
5403        .fs()
5404        .insert_tree(
5405            "/root",
5406            json!({
5407                "a.rs": "const ONE: usize = b::TWO;",
5408                "b.rs": "const TWO: usize = 2",
5409            }),
5410        )
5411        .await;
5412    let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await;
5413    let project_id = active_call_a
5414        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5415        .await
5416        .unwrap();
5417    let project_b = client_b.join_remote_project(project_id, cx_b).await;
5418
5419    let open_buffer_task = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
5420    let buffer_b1 = cx_b.executor().spawn(open_buffer_task).await.unwrap();
5421
5422    let fake_language_server = fake_language_servers.next().await.unwrap();
5423    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
5424        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
5425            lsp::Location::new(
5426                lsp::Url::from_file_path("/root/b.rs").unwrap(),
5427                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5428            ),
5429        )))
5430    });
5431
5432    let definitions;
5433    let buffer_b2;
5434    if rng.gen() {
5435        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5436        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5437    } else {
5438        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5439        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5440    }
5441
5442    let buffer_b2 = buffer_b2.await.unwrap();
5443    let definitions = definitions.await.unwrap();
5444    assert_eq!(definitions.len(), 1);
5445    assert_eq!(definitions[0].target.buffer, buffer_b2);
5446}
5447
5448#[gpui::test(iterations = 10)]
5449async fn test_contacts(
5450    executor: BackgroundExecutor,
5451    cx_a: &mut TestAppContext,
5452    cx_b: &mut TestAppContext,
5453    cx_c: &mut TestAppContext,
5454    cx_d: &mut TestAppContext,
5455) {
5456    let mut server = TestServer::start(executor.clone()).await;
5457    let client_a = server.create_client(cx_a, "user_a").await;
5458    let client_b = server.create_client(cx_b, "user_b").await;
5459    let client_c = server.create_client(cx_c, "user_c").await;
5460    let client_d = server.create_client(cx_d, "user_d").await;
5461    server
5462        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
5463        .await;
5464    let active_call_a = cx_a.read(ActiveCall::global);
5465    let active_call_b = cx_b.read(ActiveCall::global);
5466    let active_call_c = cx_c.read(ActiveCall::global);
5467    let _active_call_d = cx_d.read(ActiveCall::global);
5468
5469    executor.run_until_parked();
5470    assert_eq!(
5471        contacts(&client_a, cx_a),
5472        [
5473            ("user_b".to_string(), "online", "free"),
5474            ("user_c".to_string(), "online", "free")
5475        ]
5476    );
5477    assert_eq!(
5478        contacts(&client_b, cx_b),
5479        [
5480            ("user_a".to_string(), "online", "free"),
5481            ("user_c".to_string(), "online", "free")
5482        ]
5483    );
5484    assert_eq!(
5485        contacts(&client_c, cx_c),
5486        [
5487            ("user_a".to_string(), "online", "free"),
5488            ("user_b".to_string(), "online", "free")
5489        ]
5490    );
5491    assert_eq!(contacts(&client_d, cx_d), []);
5492
5493    server.disconnect_client(client_c.peer_id().unwrap());
5494    server.forbid_connections();
5495    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5496    assert_eq!(
5497        contacts(&client_a, cx_a),
5498        [
5499            ("user_b".to_string(), "online", "free"),
5500            ("user_c".to_string(), "offline", "free")
5501        ]
5502    );
5503    assert_eq!(
5504        contacts(&client_b, cx_b),
5505        [
5506            ("user_a".to_string(), "online", "free"),
5507            ("user_c".to_string(), "offline", "free")
5508        ]
5509    );
5510    assert_eq!(contacts(&client_c, cx_c), []);
5511    assert_eq!(contacts(&client_d, cx_d), []);
5512
5513    server.allow_connections();
5514    client_c
5515        .authenticate_and_connect(false, &cx_c.to_async())
5516        .await
5517        .unwrap();
5518
5519    executor.run_until_parked();
5520    assert_eq!(
5521        contacts(&client_a, cx_a),
5522        [
5523            ("user_b".to_string(), "online", "free"),
5524            ("user_c".to_string(), "online", "free")
5525        ]
5526    );
5527    assert_eq!(
5528        contacts(&client_b, cx_b),
5529        [
5530            ("user_a".to_string(), "online", "free"),
5531            ("user_c".to_string(), "online", "free")
5532        ]
5533    );
5534    assert_eq!(
5535        contacts(&client_c, cx_c),
5536        [
5537            ("user_a".to_string(), "online", "free"),
5538            ("user_b".to_string(), "online", "free")
5539        ]
5540    );
5541    assert_eq!(contacts(&client_d, cx_d), []);
5542
5543    active_call_a
5544        .update(cx_a, |call, cx| {
5545            call.invite(client_b.user_id().unwrap(), None, cx)
5546        })
5547        .await
5548        .unwrap();
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        ]
5563    );
5564    assert_eq!(
5565        contacts(&client_c, cx_c),
5566        [
5567            ("user_a".to_string(), "online", "busy"),
5568            ("user_b".to_string(), "online", "busy")
5569        ]
5570    );
5571    assert_eq!(contacts(&client_d, cx_d), []);
5572
5573    // Client B and client D become contacts while client B is being called.
5574    server
5575        .make_contacts(&mut [(&client_b, cx_b), (&client_d, cx_d)])
5576        .await;
5577    executor.run_until_parked();
5578    assert_eq!(
5579        contacts(&client_a, cx_a),
5580        [
5581            ("user_b".to_string(), "online", "busy"),
5582            ("user_c".to_string(), "online", "free")
5583        ]
5584    );
5585    assert_eq!(
5586        contacts(&client_b, cx_b),
5587        [
5588            ("user_a".to_string(), "online", "busy"),
5589            ("user_c".to_string(), "online", "free"),
5590            ("user_d".to_string(), "online", "free"),
5591        ]
5592    );
5593    assert_eq!(
5594        contacts(&client_c, cx_c),
5595        [
5596            ("user_a".to_string(), "online", "busy"),
5597            ("user_b".to_string(), "online", "busy")
5598        ]
5599    );
5600    assert_eq!(
5601        contacts(&client_d, cx_d),
5602        [("user_b".to_string(), "online", "busy")]
5603    );
5604
5605    active_call_b.update(cx_b, |call, cx| call.decline_incoming(cx).unwrap());
5606    executor.run_until_parked();
5607    assert_eq!(
5608        contacts(&client_a, cx_a),
5609        [
5610            ("user_b".to_string(), "online", "free"),
5611            ("user_c".to_string(), "online", "free")
5612        ]
5613    );
5614    assert_eq!(
5615        contacts(&client_b, cx_b),
5616        [
5617            ("user_a".to_string(), "online", "free"),
5618            ("user_c".to_string(), "online", "free"),
5619            ("user_d".to_string(), "online", "free")
5620        ]
5621    );
5622    assert_eq!(
5623        contacts(&client_c, cx_c),
5624        [
5625            ("user_a".to_string(), "online", "free"),
5626            ("user_b".to_string(), "online", "free")
5627        ]
5628    );
5629    assert_eq!(
5630        contacts(&client_d, cx_d),
5631        [("user_b".to_string(), "online", "free")]
5632    );
5633
5634    active_call_c
5635        .update(cx_c, |call, cx| {
5636            call.invite(client_a.user_id().unwrap(), None, cx)
5637        })
5638        .await
5639        .unwrap();
5640    executor.run_until_parked();
5641    assert_eq!(
5642        contacts(&client_a, cx_a),
5643        [
5644            ("user_b".to_string(), "online", "free"),
5645            ("user_c".to_string(), "online", "busy")
5646        ]
5647    );
5648    assert_eq!(
5649        contacts(&client_b, cx_b),
5650        [
5651            ("user_a".to_string(), "online", "busy"),
5652            ("user_c".to_string(), "online", "busy"),
5653            ("user_d".to_string(), "online", "free")
5654        ]
5655    );
5656    assert_eq!(
5657        contacts(&client_c, cx_c),
5658        [
5659            ("user_a".to_string(), "online", "busy"),
5660            ("user_b".to_string(), "online", "free")
5661        ]
5662    );
5663    assert_eq!(
5664        contacts(&client_d, cx_d),
5665        [("user_b".to_string(), "online", "free")]
5666    );
5667
5668    active_call_a
5669        .update(cx_a, |call, cx| call.accept_incoming(cx))
5670        .await
5671        .unwrap();
5672    executor.run_until_parked();
5673    assert_eq!(
5674        contacts(&client_a, cx_a),
5675        [
5676            ("user_b".to_string(), "online", "free"),
5677            ("user_c".to_string(), "online", "busy")
5678        ]
5679    );
5680    assert_eq!(
5681        contacts(&client_b, cx_b),
5682        [
5683            ("user_a".to_string(), "online", "busy"),
5684            ("user_c".to_string(), "online", "busy"),
5685            ("user_d".to_string(), "online", "free")
5686        ]
5687    );
5688    assert_eq!(
5689        contacts(&client_c, cx_c),
5690        [
5691            ("user_a".to_string(), "online", "busy"),
5692            ("user_b".to_string(), "online", "free")
5693        ]
5694    );
5695    assert_eq!(
5696        contacts(&client_d, cx_d),
5697        [("user_b".to_string(), "online", "free")]
5698    );
5699
5700    active_call_a
5701        .update(cx_a, |call, cx| {
5702            call.invite(client_b.user_id().unwrap(), None, cx)
5703        })
5704        .await
5705        .unwrap();
5706    executor.run_until_parked();
5707    assert_eq!(
5708        contacts(&client_a, cx_a),
5709        [
5710            ("user_b".to_string(), "online", "busy"),
5711            ("user_c".to_string(), "online", "busy")
5712        ]
5713    );
5714    assert_eq!(
5715        contacts(&client_b, cx_b),
5716        [
5717            ("user_a".to_string(), "online", "busy"),
5718            ("user_c".to_string(), "online", "busy"),
5719            ("user_d".to_string(), "online", "free")
5720        ]
5721    );
5722    assert_eq!(
5723        contacts(&client_c, cx_c),
5724        [
5725            ("user_a".to_string(), "online", "busy"),
5726            ("user_b".to_string(), "online", "busy")
5727        ]
5728    );
5729    assert_eq!(
5730        contacts(&client_d, cx_d),
5731        [("user_b".to_string(), "online", "busy")]
5732    );
5733
5734    active_call_a
5735        .update(cx_a, |call, cx| call.hang_up(cx))
5736        .await
5737        .unwrap();
5738    executor.run_until_parked();
5739    assert_eq!(
5740        contacts(&client_a, cx_a),
5741        [
5742            ("user_b".to_string(), "online", "free"),
5743            ("user_c".to_string(), "online", "free")
5744        ]
5745    );
5746    assert_eq!(
5747        contacts(&client_b, cx_b),
5748        [
5749            ("user_a".to_string(), "online", "free"),
5750            ("user_c".to_string(), "online", "free"),
5751            ("user_d".to_string(), "online", "free")
5752        ]
5753    );
5754    assert_eq!(
5755        contacts(&client_c, cx_c),
5756        [
5757            ("user_a".to_string(), "online", "free"),
5758            ("user_b".to_string(), "online", "free")
5759        ]
5760    );
5761    assert_eq!(
5762        contacts(&client_d, cx_d),
5763        [("user_b".to_string(), "online", "free")]
5764    );
5765
5766    active_call_a
5767        .update(cx_a, |call, cx| {
5768            call.invite(client_b.user_id().unwrap(), None, cx)
5769        })
5770        .await
5771        .unwrap();
5772    executor.run_until_parked();
5773    assert_eq!(
5774        contacts(&client_a, cx_a),
5775        [
5776            ("user_b".to_string(), "online", "busy"),
5777            ("user_c".to_string(), "online", "free")
5778        ]
5779    );
5780    assert_eq!(
5781        contacts(&client_b, cx_b),
5782        [
5783            ("user_a".to_string(), "online", "busy"),
5784            ("user_c".to_string(), "online", "free"),
5785            ("user_d".to_string(), "online", "free")
5786        ]
5787    );
5788    assert_eq!(
5789        contacts(&client_c, cx_c),
5790        [
5791            ("user_a".to_string(), "online", "busy"),
5792            ("user_b".to_string(), "online", "busy")
5793        ]
5794    );
5795    assert_eq!(
5796        contacts(&client_d, cx_d),
5797        [("user_b".to_string(), "online", "busy")]
5798    );
5799
5800    server.forbid_connections();
5801    server.disconnect_client(client_a.peer_id().unwrap());
5802    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5803    assert_eq!(contacts(&client_a, cx_a), []);
5804    assert_eq!(
5805        contacts(&client_b, cx_b),
5806        [
5807            ("user_a".to_string(), "offline", "free"),
5808            ("user_c".to_string(), "online", "free"),
5809            ("user_d".to_string(), "online", "free")
5810        ]
5811    );
5812    assert_eq!(
5813        contacts(&client_c, cx_c),
5814        [
5815            ("user_a".to_string(), "offline", "free"),
5816            ("user_b".to_string(), "online", "free")
5817        ]
5818    );
5819    assert_eq!(
5820        contacts(&client_d, cx_d),
5821        [("user_b".to_string(), "online", "free")]
5822    );
5823
5824    // Test removing a contact
5825    client_b
5826        .user_store()
5827        .update(cx_b, |store, cx| {
5828            store.remove_contact(client_c.user_id().unwrap(), cx)
5829        })
5830        .await
5831        .unwrap();
5832    executor.run_until_parked();
5833    assert_eq!(
5834        contacts(&client_b, cx_b),
5835        [
5836            ("user_a".to_string(), "offline", "free"),
5837            ("user_d".to_string(), "online", "free")
5838        ]
5839    );
5840    assert_eq!(
5841        contacts(&client_c, cx_c),
5842        [("user_a".to_string(), "offline", "free"),]
5843    );
5844
5845    fn contacts(
5846        client: &TestClient,
5847        cx: &TestAppContext,
5848    ) -> Vec<(String, &'static str, &'static str)> {
5849        client.user_store().read_with(cx, |store, _| {
5850            store
5851                .contacts()
5852                .iter()
5853                .map(|contact| {
5854                    (
5855                        contact.user.github_login.clone(),
5856                        if contact.online { "online" } else { "offline" },
5857                        if contact.busy { "busy" } else { "free" },
5858                    )
5859                })
5860                .collect()
5861        })
5862    }
5863}
5864
5865#[gpui::test(iterations = 10)]
5866async fn test_contact_requests(
5867    executor: BackgroundExecutor,
5868    cx_a: &mut TestAppContext,
5869    cx_a2: &mut TestAppContext,
5870    cx_b: &mut TestAppContext,
5871    cx_b2: &mut TestAppContext,
5872    cx_c: &mut TestAppContext,
5873    cx_c2: &mut TestAppContext,
5874) {
5875    // Connect to a server as 3 clients.
5876    let mut server = TestServer::start(executor.clone()).await;
5877    let client_a = server.create_client(cx_a, "user_a").await;
5878    let client_a2 = server.create_client(cx_a2, "user_a").await;
5879    let client_b = server.create_client(cx_b, "user_b").await;
5880    let client_b2 = server.create_client(cx_b2, "user_b").await;
5881    let client_c = server.create_client(cx_c, "user_c").await;
5882    let client_c2 = server.create_client(cx_c2, "user_c").await;
5883
5884    assert_eq!(client_a.user_id().unwrap(), client_a2.user_id().unwrap());
5885    assert_eq!(client_b.user_id().unwrap(), client_b2.user_id().unwrap());
5886    assert_eq!(client_c.user_id().unwrap(), client_c2.user_id().unwrap());
5887
5888    // User A and User C request that user B become their contact.
5889    client_a
5890        .user_store()
5891        .update(cx_a, |store, cx| {
5892            store.request_contact(client_b.user_id().unwrap(), cx)
5893        })
5894        .await
5895        .unwrap();
5896    client_c
5897        .user_store()
5898        .update(cx_c, |store, cx| {
5899            store.request_contact(client_b.user_id().unwrap(), cx)
5900        })
5901        .await
5902        .unwrap();
5903    executor.run_until_parked();
5904
5905    // All users see the pending request appear in all their clients.
5906    assert_eq!(
5907        client_a.summarize_contacts(cx_a).outgoing_requests,
5908        &["user_b"]
5909    );
5910    assert_eq!(
5911        client_a2.summarize_contacts(cx_a2).outgoing_requests,
5912        &["user_b"]
5913    );
5914    assert_eq!(
5915        client_b.summarize_contacts(cx_b).incoming_requests,
5916        &["user_a", "user_c"]
5917    );
5918    assert_eq!(
5919        client_b2.summarize_contacts(cx_b2).incoming_requests,
5920        &["user_a", "user_c"]
5921    );
5922    assert_eq!(
5923        client_c.summarize_contacts(cx_c).outgoing_requests,
5924        &["user_b"]
5925    );
5926    assert_eq!(
5927        client_c2.summarize_contacts(cx_c2).outgoing_requests,
5928        &["user_b"]
5929    );
5930
5931    // Contact requests are present upon connecting (tested here via disconnect/reconnect)
5932    disconnect_and_reconnect(&client_a, cx_a).await;
5933    disconnect_and_reconnect(&client_b, cx_b).await;
5934    disconnect_and_reconnect(&client_c, cx_c).await;
5935    executor.run_until_parked();
5936    assert_eq!(
5937        client_a.summarize_contacts(cx_a).outgoing_requests,
5938        &["user_b"]
5939    );
5940    assert_eq!(
5941        client_b.summarize_contacts(cx_b).incoming_requests,
5942        &["user_a", "user_c"]
5943    );
5944    assert_eq!(
5945        client_c.summarize_contacts(cx_c).outgoing_requests,
5946        &["user_b"]
5947    );
5948
5949    // User B accepts the request from user A.
5950    client_b
5951        .user_store()
5952        .update(cx_b, |store, cx| {
5953            store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
5954        })
5955        .await
5956        .unwrap();
5957
5958    executor.run_until_parked();
5959
5960    // User B sees user A as their contact now in all client, and the incoming request from them is removed.
5961    let contacts_b = client_b.summarize_contacts(cx_b);
5962    assert_eq!(contacts_b.current, &["user_a"]);
5963    assert_eq!(contacts_b.incoming_requests, &["user_c"]);
5964    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
5965    assert_eq!(contacts_b2.current, &["user_a"]);
5966    assert_eq!(contacts_b2.incoming_requests, &["user_c"]);
5967
5968    // User A sees user B as their contact now in all clients, and the outgoing request to them is removed.
5969    let contacts_a = client_a.summarize_contacts(cx_a);
5970    assert_eq!(contacts_a.current, &["user_b"]);
5971    assert!(contacts_a.outgoing_requests.is_empty());
5972    let contacts_a2 = client_a2.summarize_contacts(cx_a2);
5973    assert_eq!(contacts_a2.current, &["user_b"]);
5974    assert!(contacts_a2.outgoing_requests.is_empty());
5975
5976    // Contacts are present upon connecting (tested here via disconnect/reconnect)
5977    disconnect_and_reconnect(&client_a, cx_a).await;
5978    disconnect_and_reconnect(&client_b, cx_b).await;
5979    disconnect_and_reconnect(&client_c, cx_c).await;
5980    executor.run_until_parked();
5981    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
5982    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
5983    assert_eq!(
5984        client_b.summarize_contacts(cx_b).incoming_requests,
5985        &["user_c"]
5986    );
5987    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
5988    assert_eq!(
5989        client_c.summarize_contacts(cx_c).outgoing_requests,
5990        &["user_b"]
5991    );
5992
5993    // User B rejects the request from user C.
5994    client_b
5995        .user_store()
5996        .update(cx_b, |store, cx| {
5997            store.respond_to_contact_request(client_c.user_id().unwrap(), false, cx)
5998        })
5999        .await
6000        .unwrap();
6001
6002    executor.run_until_parked();
6003
6004    // User B doesn't see user C as their contact, and the incoming request from them is removed.
6005    let contacts_b = client_b.summarize_contacts(cx_b);
6006    assert_eq!(contacts_b.current, &["user_a"]);
6007    assert!(contacts_b.incoming_requests.is_empty());
6008    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
6009    assert_eq!(contacts_b2.current, &["user_a"]);
6010    assert!(contacts_b2.incoming_requests.is_empty());
6011
6012    // User C doesn't see user B as their contact, and the outgoing request to them is removed.
6013    let contacts_c = client_c.summarize_contacts(cx_c);
6014    assert!(contacts_c.current.is_empty());
6015    assert!(contacts_c.outgoing_requests.is_empty());
6016    let contacts_c2 = client_c2.summarize_contacts(cx_c2);
6017    assert!(contacts_c2.current.is_empty());
6018    assert!(contacts_c2.outgoing_requests.is_empty());
6019
6020    // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect)
6021    disconnect_and_reconnect(&client_a, cx_a).await;
6022    disconnect_and_reconnect(&client_b, cx_b).await;
6023    disconnect_and_reconnect(&client_c, cx_c).await;
6024    executor.run_until_parked();
6025    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
6026    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
6027    assert!(client_b
6028        .summarize_contacts(cx_b)
6029        .incoming_requests
6030        .is_empty());
6031    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
6032    assert!(client_c
6033        .summarize_contacts(cx_c)
6034        .outgoing_requests
6035        .is_empty());
6036
6037    async fn disconnect_and_reconnect(client: &TestClient, cx: &mut TestAppContext) {
6038        client.disconnect(&cx.to_async());
6039        client.clear_contacts(cx).await;
6040        client
6041            .authenticate_and_connect(false, &cx.to_async())
6042            .await
6043            .unwrap();
6044    }
6045}
6046
6047#[gpui::test(iterations = 10)]
6048async fn test_join_call_after_screen_was_shared(
6049    executor: BackgroundExecutor,
6050    cx_a: &mut TestAppContext,
6051    cx_b: &mut TestAppContext,
6052) {
6053    let mut server = TestServer::start(executor.clone()).await;
6054
6055    let client_a = server.create_client(cx_a, "user_a").await;
6056    let client_b = server.create_client(cx_b, "user_b").await;
6057    server
6058        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6059        .await;
6060
6061    let active_call_a = cx_a.read(ActiveCall::global);
6062    let active_call_b = cx_b.read(ActiveCall::global);
6063
6064    // Call users B and C from client A.
6065    active_call_a
6066        .update(cx_a, |call, cx| {
6067            call.invite(client_b.user_id().unwrap(), None, cx)
6068        })
6069        .await
6070        .unwrap();
6071
6072    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
6073    executor.run_until_parked();
6074    assert_eq!(
6075        room_participants(&room_a, cx_a),
6076        RoomParticipants {
6077            remote: Default::default(),
6078            pending: vec!["user_b".to_string()]
6079        }
6080    );
6081
6082    // User B receives the call.
6083
6084    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
6085    let call_b = incoming_call_b.next().await.unwrap().unwrap();
6086    assert_eq!(call_b.calling_user.github_login, "user_a");
6087
6088    // User A shares their screen
6089    let display = MacOSDisplay::new();
6090    active_call_a
6091        .update(cx_a, |call, cx| {
6092            call.room().unwrap().update(cx, |room, cx| {
6093                room.set_display_sources(vec![display.clone()]);
6094                room.share_screen(cx)
6095            })
6096        })
6097        .await
6098        .unwrap();
6099
6100    client_b.user_store().update(cx_b, |user_store, _| {
6101        user_store.clear_cache();
6102    });
6103
6104    // User B joins the room
6105    active_call_b
6106        .update(cx_b, |call, cx| call.accept_incoming(cx))
6107        .await
6108        .unwrap();
6109
6110    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
6111    assert!(incoming_call_b.next().await.unwrap().is_none());
6112
6113    executor.run_until_parked();
6114    assert_eq!(
6115        room_participants(&room_a, cx_a),
6116        RoomParticipants {
6117            remote: vec!["user_b".to_string()],
6118            pending: vec![],
6119        }
6120    );
6121    assert_eq!(
6122        room_participants(&room_b, cx_b),
6123        RoomParticipants {
6124            remote: vec!["user_a".to_string()],
6125            pending: vec![],
6126        }
6127    );
6128
6129    // Ensure User B sees User A's screenshare.
6130
6131    room_b.read_with(cx_b, |room, _| {
6132        assert_eq!(
6133            room.remote_participants()
6134                .get(&client_a.user_id().unwrap())
6135                .unwrap()
6136                .video_tracks
6137                .len(),
6138            1
6139        );
6140    });
6141}
6142
6143#[gpui::test]
6144async fn test_right_click_menu_behind_collab_panel(cx: &mut TestAppContext) {
6145    let mut server = TestServer::start(cx.executor().clone()).await;
6146    let client_a = server.create_client(cx, "user_a").await;
6147    let (_workspace_a, cx) = client_a.build_test_workspace(cx).await;
6148
6149    cx.simulate_resize(size(px(300.), px(300.)));
6150
6151    cx.simulate_keystrokes("cmd-n cmd-n cmd-n");
6152    cx.update(|cx| cx.refresh());
6153
6154    let tab_bounds = cx.debug_bounds("TAB-2").unwrap();
6155    let new_tab_button_bounds = cx.debug_bounds("ICON-Plus").unwrap();
6156
6157    assert!(
6158        tab_bounds.intersects(&new_tab_button_bounds),
6159        "Tab should overlap with the new tab button, if this is failing check if there's been a redesign!"
6160    );
6161
6162    cx.simulate_event(MouseDownEvent {
6163        button: MouseButton::Right,
6164        position: new_tab_button_bounds.center(),
6165        modifiers: Modifiers::default(),
6166        click_count: 1,
6167        first_mouse: false,
6168    });
6169
6170    // regression test that the right click menu for tabs does not open.
6171    assert!(cx.debug_bounds("MENU_ITEM-Close").is_none());
6172
6173    let tab_bounds = cx.debug_bounds("TAB-1").unwrap();
6174    cx.simulate_event(MouseDownEvent {
6175        button: MouseButton::Right,
6176        position: tab_bounds.center(),
6177        modifiers: Modifiers::default(),
6178        click_count: 1,
6179        first_mouse: false,
6180    });
6181    assert!(cx.debug_bounds("MENU_ITEM-Close").is_some());
6182}
6183
6184#[gpui::test]
6185async fn test_pane_split_left(cx: &mut TestAppContext) {
6186    let (_, client) = TestServer::start1(cx).await;
6187    let (workspace, cx) = client.build_test_workspace(cx).await;
6188
6189    cx.simulate_keystrokes("cmd-n");
6190    workspace.update(cx, |workspace, cx| {
6191        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 1);
6192    });
6193    cx.simulate_keystrokes("cmd-k left");
6194    workspace.update(cx, |workspace, cx| {
6195        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 2);
6196    });
6197    cx.simulate_keystrokes("cmd-k");
6198    // sleep for longer than the timeout in keyboard shortcut handling
6199    // to verify that it doesn't fire in this case.
6200    cx.executor().advance_clock(Duration::from_secs(2));
6201    cx.simulate_keystrokes("left");
6202    workspace.update(cx, |workspace, cx| {
6203        assert!(workspace.items(cx).collect::<Vec<_>>().len() == 2);
6204    });
6205}
6206
6207#[gpui::test]
6208async fn test_join_after_restart(cx1: &mut TestAppContext, cx2: &mut TestAppContext) {
6209    let (mut server, client) = TestServer::start1(cx1).await;
6210    let channel1 = server.make_public_channel("channel1", &client, cx1).await;
6211    let channel2 = server.make_public_channel("channel2", &client, cx1).await;
6212
6213    join_channel(channel1, &client, cx1).await.unwrap();
6214    drop(client);
6215
6216    let client2 = server.create_client(cx2, "user_a").await;
6217    join_channel(channel2, &client2, cx2).await.unwrap();
6218}
6219
6220#[gpui::test]
6221async fn test_preview_tabs(cx: &mut TestAppContext) {
6222    let (_server, client) = TestServer::start1(cx).await;
6223    let (workspace, cx) = client.build_test_workspace(cx).await;
6224    let project = workspace.update(cx, |workspace, _| workspace.project().clone());
6225
6226    let worktree_id = project.update(cx, |project, cx| {
6227        project.worktrees(cx).next().unwrap().read(cx).id()
6228    });
6229
6230    let path_1 = ProjectPath {
6231        worktree_id,
6232        path: Path::new("1.txt").into(),
6233    };
6234    let path_2 = ProjectPath {
6235        worktree_id,
6236        path: Path::new("2.js").into(),
6237    };
6238    let path_3 = ProjectPath {
6239        worktree_id,
6240        path: Path::new("3.rs").into(),
6241    };
6242
6243    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
6244
6245    let get_path = |pane: &Pane, idx: usize, cx: &AppContext| {
6246        pane.item_for_index(idx).unwrap().project_path(cx).unwrap()
6247    };
6248
6249    // Opening item 3 as a "permanent" tab
6250    workspace
6251        .update(cx, |workspace, cx| {
6252            workspace.open_path(path_3.clone(), None, false, cx)
6253        })
6254        .await
6255        .unwrap();
6256
6257    pane.update(cx, |pane, cx| {
6258        assert_eq!(pane.items_len(), 1);
6259        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6260        assert_eq!(pane.preview_item_id(), None);
6261
6262        assert!(!pane.can_navigate_backward());
6263        assert!(!pane.can_navigate_forward());
6264    });
6265
6266    // Open item 1 as preview
6267    workspace
6268        .update(cx, |workspace, cx| {
6269            workspace.open_path_preview(path_1.clone(), None, true, true, cx)
6270        })
6271        .await
6272        .unwrap();
6273
6274    pane.update(cx, |pane, cx| {
6275        assert_eq!(pane.items_len(), 2);
6276        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6277        assert_eq!(get_path(pane, 1, cx), path_1.clone());
6278        assert_eq!(
6279            pane.preview_item_id(),
6280            Some(pane.items().nth(1).unwrap().item_id())
6281        );
6282
6283        assert!(pane.can_navigate_backward());
6284        assert!(!pane.can_navigate_forward());
6285    });
6286
6287    // Open item 2 as preview
6288    workspace
6289        .update(cx, |workspace, cx| {
6290            workspace.open_path_preview(path_2.clone(), None, true, true, cx)
6291        })
6292        .await
6293        .unwrap();
6294
6295    pane.update(cx, |pane, cx| {
6296        assert_eq!(pane.items_len(), 2);
6297        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6298        assert_eq!(get_path(pane, 1, cx), path_2.clone());
6299        assert_eq!(
6300            pane.preview_item_id(),
6301            Some(pane.items().nth(1).unwrap().item_id())
6302        );
6303
6304        assert!(pane.can_navigate_backward());
6305        assert!(!pane.can_navigate_forward());
6306    });
6307
6308    // Going back should show item 1 as preview
6309    workspace
6310        .update(cx, |workspace, cx| workspace.go_back(pane.downgrade(), cx))
6311        .await
6312        .unwrap();
6313
6314    pane.update(cx, |pane, cx| {
6315        assert_eq!(pane.items_len(), 2);
6316        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6317        assert_eq!(get_path(pane, 1, cx), path_1.clone());
6318        assert_eq!(
6319            pane.preview_item_id(),
6320            Some(pane.items().nth(1).unwrap().item_id())
6321        );
6322
6323        assert!(pane.can_navigate_backward());
6324        assert!(pane.can_navigate_forward());
6325    });
6326
6327    // Closing item 1
6328    pane.update(cx, |pane, cx| {
6329        pane.close_item_by_id(
6330            pane.active_item().unwrap().item_id(),
6331            workspace::SaveIntent::Skip,
6332            cx,
6333        )
6334    })
6335    .await
6336    .unwrap();
6337
6338    pane.update(cx, |pane, cx| {
6339        assert_eq!(pane.items_len(), 1);
6340        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6341        assert_eq!(pane.preview_item_id(), None);
6342
6343        assert!(pane.can_navigate_backward());
6344        assert!(!pane.can_navigate_forward());
6345    });
6346
6347    // Going back should show item 1 as preview
6348    workspace
6349        .update(cx, |workspace, cx| workspace.go_back(pane.downgrade(), cx))
6350        .await
6351        .unwrap();
6352
6353    pane.update(cx, |pane, cx| {
6354        assert_eq!(pane.items_len(), 2);
6355        assert_eq!(get_path(pane, 0, cx), path_3.clone());
6356        assert_eq!(get_path(pane, 1, cx), path_1.clone());
6357        assert_eq!(
6358            pane.preview_item_id(),
6359            Some(pane.items().nth(1).unwrap().item_id())
6360        );
6361
6362        assert!(pane.can_navigate_backward());
6363        assert!(pane.can_navigate_forward());
6364    });
6365
6366    // Close permanent tab
6367    pane.update(cx, |pane, cx| {
6368        let id = pane.items().next().unwrap().item_id();
6369        pane.close_item_by_id(id, workspace::SaveIntent::Skip, cx)
6370    })
6371    .await
6372    .unwrap();
6373
6374    pane.update(cx, |pane, cx| {
6375        assert_eq!(pane.items_len(), 1);
6376        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6377        assert_eq!(
6378            pane.preview_item_id(),
6379            Some(pane.items().next().unwrap().item_id())
6380        );
6381
6382        assert!(pane.can_navigate_backward());
6383        assert!(pane.can_navigate_forward());
6384    });
6385
6386    // Split pane to the right
6387    pane.update(cx, |pane, cx| {
6388        pane.split(workspace::SplitDirection::Right, cx);
6389    });
6390
6391    let right_pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
6392
6393    pane.update(cx, |pane, cx| {
6394        assert_eq!(pane.items_len(), 1);
6395        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6396        assert_eq!(
6397            pane.preview_item_id(),
6398            Some(pane.items().next().unwrap().item_id())
6399        );
6400
6401        assert!(pane.can_navigate_backward());
6402        assert!(pane.can_navigate_forward());
6403    });
6404
6405    right_pane.update(cx, |pane, cx| {
6406        assert_eq!(pane.items_len(), 1);
6407        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6408        assert_eq!(pane.preview_item_id(), None);
6409
6410        assert!(!pane.can_navigate_backward());
6411        assert!(!pane.can_navigate_forward());
6412    });
6413
6414    // Open item 2 as preview in right pane
6415    workspace
6416        .update(cx, |workspace, cx| {
6417            workspace.open_path_preview(path_2.clone(), None, true, true, cx)
6418        })
6419        .await
6420        .unwrap();
6421
6422    pane.update(cx, |pane, cx| {
6423        assert_eq!(pane.items_len(), 1);
6424        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6425        assert_eq!(
6426            pane.preview_item_id(),
6427            Some(pane.items().next().unwrap().item_id())
6428        );
6429
6430        assert!(pane.can_navigate_backward());
6431        assert!(pane.can_navigate_forward());
6432    });
6433
6434    right_pane.update(cx, |pane, cx| {
6435        assert_eq!(pane.items_len(), 2);
6436        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6437        assert_eq!(get_path(pane, 1, cx), path_2.clone());
6438        assert_eq!(
6439            pane.preview_item_id(),
6440            Some(pane.items().nth(1).unwrap().item_id())
6441        );
6442
6443        assert!(pane.can_navigate_backward());
6444        assert!(!pane.can_navigate_forward());
6445    });
6446
6447    // Focus left pane
6448    workspace.update(cx, |workspace, cx| {
6449        workspace.activate_pane_in_direction(workspace::SplitDirection::Left, cx)
6450    });
6451
6452    // Open item 2 as preview in left pane
6453    workspace
6454        .update(cx, |workspace, cx| {
6455            workspace.open_path_preview(path_2.clone(), None, true, true, cx)
6456        })
6457        .await
6458        .unwrap();
6459
6460    pane.update(cx, |pane, cx| {
6461        assert_eq!(pane.items_len(), 1);
6462        assert_eq!(get_path(pane, 0, cx), path_2.clone());
6463        assert_eq!(
6464            pane.preview_item_id(),
6465            Some(pane.items().next().unwrap().item_id())
6466        );
6467
6468        assert!(pane.can_navigate_backward());
6469        assert!(!pane.can_navigate_forward());
6470    });
6471
6472    right_pane.update(cx, |pane, cx| {
6473        assert_eq!(pane.items_len(), 2);
6474        assert_eq!(get_path(pane, 0, cx), path_1.clone());
6475        assert_eq!(get_path(pane, 1, cx), path_2.clone());
6476        assert_eq!(
6477            pane.preview_item_id(),
6478            Some(pane.items().nth(1).unwrap().item_id())
6479        );
6480
6481        assert!(pane.can_navigate_backward());
6482        assert!(!pane.can_navigate_forward());
6483    });
6484}
6485
6486#[gpui::test(iterations = 10)]
6487async fn test_context_collaboration_with_reconnect(
6488    executor: BackgroundExecutor,
6489    cx_a: &mut TestAppContext,
6490    cx_b: &mut TestAppContext,
6491) {
6492    let mut server = TestServer::start(executor.clone()).await;
6493    let client_a = server.create_client(cx_a, "user_a").await;
6494    let client_b = server.create_client(cx_b, "user_b").await;
6495    server
6496        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6497        .await;
6498    let active_call_a = cx_a.read(ActiveCall::global);
6499
6500    client_a.fs().insert_tree("/a", Default::default()).await;
6501    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
6502    let project_id = active_call_a
6503        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6504        .await
6505        .unwrap();
6506    let project_b = client_b.join_remote_project(project_id, cx_b).await;
6507
6508    // Client A sees that a guest has joined.
6509    executor.run_until_parked();
6510
6511    project_a.read_with(cx_a, |project, _| {
6512        assert_eq!(project.collaborators().len(), 1);
6513    });
6514    project_b.read_with(cx_b, |project, _| {
6515        assert_eq!(project.collaborators().len(), 1);
6516    });
6517
6518    let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
6519    let context_store_a = cx_a
6520        .update(|cx| ContextStore::new(project_a.clone(), prompt_builder.clone(), cx))
6521        .await
6522        .unwrap();
6523    let context_store_b = cx_b
6524        .update(|cx| ContextStore::new(project_b.clone(), prompt_builder.clone(), cx))
6525        .await
6526        .unwrap();
6527
6528    // Client A creates a new context.
6529    let context_a = context_store_a.update(cx_a, |store, cx| store.create(cx));
6530    executor.run_until_parked();
6531
6532    // Client B retrieves host's contexts and joins one.
6533    let context_b = context_store_b
6534        .update(cx_b, |store, cx| {
6535            let host_contexts = store.host_contexts().to_vec();
6536            assert_eq!(host_contexts.len(), 1);
6537            store.open_remote_context(host_contexts[0].id.clone(), cx)
6538        })
6539        .await
6540        .unwrap();
6541
6542    // Host and guest make changes
6543    context_a.update(cx_a, |context, cx| {
6544        context.buffer().update(cx, |buffer, cx| {
6545            buffer.edit([(0..0, "Host change\n")], None, cx)
6546        })
6547    });
6548    context_b.update(cx_b, |context, cx| {
6549        context.buffer().update(cx, |buffer, cx| {
6550            buffer.edit([(0..0, "Guest change\n")], None, cx)
6551        })
6552    });
6553    executor.run_until_parked();
6554    assert_eq!(
6555        context_a.read_with(cx_a, |context, cx| context.buffer().read(cx).text()),
6556        "Guest change\nHost change\n"
6557    );
6558    assert_eq!(
6559        context_b.read_with(cx_b, |context, cx| context.buffer().read(cx).text()),
6560        "Guest change\nHost change\n"
6561    );
6562
6563    // Disconnect client A and make some changes while disconnected.
6564    server.disconnect_client(client_a.peer_id().unwrap());
6565    server.forbid_connections();
6566    context_a.update(cx_a, |context, cx| {
6567        context.buffer().update(cx, |buffer, cx| {
6568            buffer.edit([(0..0, "Host offline change\n")], None, cx)
6569        })
6570    });
6571    context_b.update(cx_b, |context, cx| {
6572        context.buffer().update(cx, |buffer, cx| {
6573            buffer.edit([(0..0, "Guest offline change\n")], None, cx)
6574        })
6575    });
6576    executor.run_until_parked();
6577    assert_eq!(
6578        context_a.read_with(cx_a, |context, cx| context.buffer().read(cx).text()),
6579        "Host offline change\nGuest change\nHost change\n"
6580    );
6581    assert_eq!(
6582        context_b.read_with(cx_b, |context, cx| context.buffer().read(cx).text()),
6583        "Guest offline change\nGuest change\nHost change\n"
6584    );
6585
6586    // Allow client A to reconnect and verify that contexts converge.
6587    server.allow_connections();
6588    executor.advance_clock(RECEIVE_TIMEOUT);
6589    assert_eq!(
6590        context_a.read_with(cx_a, |context, cx| context.buffer().read(cx).text()),
6591        "Guest offline change\nHost offline change\nGuest change\nHost change\n"
6592    );
6593    assert_eq!(
6594        context_b.read_with(cx_b, |context, cx| context.buffer().read(cx).text()),
6595        "Guest offline change\nHost offline change\nGuest change\nHost change\n"
6596    );
6597
6598    // Client A disconnects without being able to reconnect. Context B becomes readonly.
6599    server.forbid_connections();
6600    server.disconnect_client(client_a.peer_id().unwrap());
6601    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
6602    context_b.read_with(cx_b, |context, cx| {
6603        assert!(context.buffer().read(cx).read_only());
6604    });
6605}