integration_tests.rs

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