integration_tests.rs

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