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