integration_tests.rs

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