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