integration_tests.rs

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