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