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
3069    // As client B, join that project and observe the local settings.
3070    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3071
3072    let worktree_b = project_b.read_with(cx_b, |project, _| project.worktrees().next().unwrap());
3073    executor.run_until_parked();
3074    cx_b.read(|cx| {
3075        let store = cx.global::<SettingsStore>();
3076        assert_eq!(
3077            store
3078                .local_settings(worktree_b.read(cx).id().to_usize())
3079                .collect::<Vec<_>>(),
3080            &[
3081                (Path::new("").into(), r#"{"tab_size":2}"#.to_string()),
3082                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3083            ]
3084        )
3085    });
3086
3087    // As client A, update a settings file. As Client B, see the changed settings.
3088    client_a
3089        .fs()
3090        .insert_file("/dir/.zed/settings.json", r#"{}"#.into())
3091        .await;
3092    executor.run_until_parked();
3093    cx_b.read(|cx| {
3094        let store = cx.global::<SettingsStore>();
3095        assert_eq!(
3096            store
3097                .local_settings(worktree_b.read(cx).id().to_usize())
3098                .collect::<Vec<_>>(),
3099            &[
3100                (Path::new("").into(), r#"{}"#.to_string()),
3101                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3102            ]
3103        )
3104    });
3105
3106    // As client A, create and remove some settings files. As client B, see the changed settings.
3107    client_a
3108        .fs()
3109        .remove_file("/dir/.zed/settings.json".as_ref(), Default::default())
3110        .await
3111        .unwrap();
3112    client_a
3113        .fs()
3114        .create_dir("/dir/b/.zed".as_ref())
3115        .await
3116        .unwrap();
3117    client_a
3118        .fs()
3119        .insert_file("/dir/b/.zed/settings.json", r#"{"tab_size": 4}"#.into())
3120        .await;
3121    executor.run_until_parked();
3122    cx_b.read(|cx| {
3123        let store = cx.global::<SettingsStore>();
3124        assert_eq!(
3125            store
3126                .local_settings(worktree_b.read(cx).id().to_usize())
3127                .collect::<Vec<_>>(),
3128            &[
3129                (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
3130                (Path::new("b").into(), r#"{"tab_size":4}"#.to_string()),
3131            ]
3132        )
3133    });
3134
3135    // As client B, disconnect.
3136    server.forbid_connections();
3137    server.disconnect_client(client_b.peer_id().unwrap());
3138
3139    // As client A, change and remove settings files while client B is disconnected.
3140    client_a
3141        .fs()
3142        .insert_file("/dir/a/.zed/settings.json", r#"{"hard_tabs":true}"#.into())
3143        .await;
3144    client_a
3145        .fs()
3146        .remove_file("/dir/b/.zed/settings.json".as_ref(), Default::default())
3147        .await
3148        .unwrap();
3149    executor.run_until_parked();
3150
3151    // As client B, reconnect and see the changed settings.
3152    server.allow_connections();
3153    executor.advance_clock(RECEIVE_TIMEOUT);
3154    cx_b.read(|cx| {
3155        let store = cx.global::<SettingsStore>();
3156        assert_eq!(
3157            store
3158                .local_settings(worktree_b.read(cx).id().to_usize())
3159                .collect::<Vec<_>>(),
3160            &[(Path::new("a").into(), r#"{"hard_tabs":true}"#.to_string()),]
3161        )
3162    });
3163}
3164
3165#[gpui::test(iterations = 10)]
3166async fn test_buffer_conflict_after_save(
3167    executor: BackgroundExecutor,
3168    cx_a: &mut TestAppContext,
3169    cx_b: &mut TestAppContext,
3170) {
3171    let mut server = TestServer::start(executor.clone()).await;
3172    let client_a = server.create_client(cx_a, "user_a").await;
3173    let client_b = server.create_client(cx_b, "user_b").await;
3174    server
3175        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3176        .await;
3177    let active_call_a = cx_a.read(ActiveCall::global);
3178
3179    client_a
3180        .fs()
3181        .insert_tree(
3182            "/dir",
3183            json!({
3184                "a.txt": "a-contents",
3185            }),
3186        )
3187        .await;
3188    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3189    let project_id = active_call_a
3190        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3191        .await
3192        .unwrap();
3193    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3194
3195    // Open a buffer as client B
3196    let buffer_b = project_b
3197        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3198        .await
3199        .unwrap();
3200
3201    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "world ")], None, cx));
3202
3203    buffer_b.read_with(cx_b, |buf, _| {
3204        assert!(buf.is_dirty());
3205        assert!(!buf.has_conflict());
3206    });
3207
3208    project_b
3209        .update(cx_b, |project, cx| {
3210            project.save_buffer(buffer_b.clone(), cx)
3211        })
3212        .await
3213        .unwrap();
3214
3215    buffer_b.read_with(cx_b, |buffer_b, _| assert!(!buffer_b.is_dirty()));
3216
3217    buffer_b.read_with(cx_b, |buf, _| {
3218        assert!(!buf.has_conflict());
3219    });
3220
3221    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "hello ")], None, cx));
3222
3223    buffer_b.read_with(cx_b, |buf, _| {
3224        assert!(buf.is_dirty());
3225        assert!(!buf.has_conflict());
3226    });
3227}
3228
3229#[gpui::test(iterations = 10)]
3230async fn test_buffer_reloading(
3231    executor: BackgroundExecutor,
3232    cx_a: &mut TestAppContext,
3233    cx_b: &mut TestAppContext,
3234) {
3235    let mut server = TestServer::start(executor.clone()).await;
3236    let client_a = server.create_client(cx_a, "user_a").await;
3237    let client_b = server.create_client(cx_b, "user_b").await;
3238    server
3239        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3240        .await;
3241    let active_call_a = cx_a.read(ActiveCall::global);
3242
3243    client_a
3244        .fs()
3245        .insert_tree(
3246            "/dir",
3247            json!({
3248                "a.txt": "a\nb\nc",
3249            }),
3250        )
3251        .await;
3252    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3253    let project_id = active_call_a
3254        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3255        .await
3256        .unwrap();
3257    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3258
3259    // Open a buffer as client B
3260    let buffer_b = project_b
3261        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3262        .await
3263        .unwrap();
3264
3265    buffer_b.read_with(cx_b, |buf, _| {
3266        assert!(!buf.is_dirty());
3267        assert!(!buf.has_conflict());
3268        assert_eq!(buf.line_ending(), LineEnding::Unix);
3269    });
3270
3271    let new_contents = Rope::from("d\ne\nf");
3272    client_a
3273        .fs()
3274        .save("/dir/a.txt".as_ref(), &new_contents, LineEnding::Windows)
3275        .await
3276        .unwrap();
3277
3278    executor.run_until_parked();
3279
3280    buffer_b.read_with(cx_b, |buf, _| {
3281        assert_eq!(buf.text(), new_contents.to_string());
3282        assert!(!buf.is_dirty());
3283        assert!(!buf.has_conflict());
3284        assert_eq!(buf.line_ending(), LineEnding::Windows);
3285    });
3286}
3287
3288#[gpui::test(iterations = 10)]
3289async fn test_editing_while_guest_opens_buffer(
3290    executor: BackgroundExecutor,
3291    cx_a: &mut TestAppContext,
3292    cx_b: &mut TestAppContext,
3293) {
3294    let mut server = TestServer::start(executor.clone()).await;
3295    let client_a = server.create_client(cx_a, "user_a").await;
3296    let client_b = server.create_client(cx_b, "user_b").await;
3297    server
3298        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3299        .await;
3300    let active_call_a = cx_a.read(ActiveCall::global);
3301
3302    client_a
3303        .fs()
3304        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3305        .await;
3306    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3307    let project_id = active_call_a
3308        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3309        .await
3310        .unwrap();
3311    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3312
3313    // Open a buffer as client A
3314    let buffer_a = project_a
3315        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3316        .await
3317        .unwrap();
3318
3319    // Start opening the same buffer as client B
3320    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx));
3321    let buffer_b = cx_b.executor().spawn(open_buffer);
3322
3323    // Edit the buffer as client A while client B is still opening it.
3324    cx_b.executor().simulate_random_delay().await;
3325    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "X")], None, cx));
3326    cx_b.executor().simulate_random_delay().await;
3327    buffer_a.update(cx_a, |buf, cx| buf.edit([(1..1, "Y")], None, cx));
3328
3329    let text = buffer_a.read_with(cx_a, |buf, _| buf.text());
3330    let buffer_b = buffer_b.await.unwrap();
3331    executor.run_until_parked();
3332
3333    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), text));
3334}
3335
3336#[gpui::test(iterations = 10)]
3337async fn test_leaving_worktree_while_opening_buffer(
3338    executor: BackgroundExecutor,
3339    cx_a: &mut TestAppContext,
3340    cx_b: &mut TestAppContext,
3341) {
3342    let mut server = TestServer::start(executor.clone()).await;
3343    let client_a = server.create_client(cx_a, "user_a").await;
3344    let client_b = server.create_client(cx_b, "user_b").await;
3345    server
3346        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3347        .await;
3348    let active_call_a = cx_a.read(ActiveCall::global);
3349
3350    client_a
3351        .fs()
3352        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3353        .await;
3354    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3355    let project_id = active_call_a
3356        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3357        .await
3358        .unwrap();
3359    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3360
3361    // See that a guest has joined as client A.
3362    executor.run_until_parked();
3363
3364    project_a.read_with(cx_a, |p, _| assert_eq!(p.collaborators().len(), 1));
3365
3366    // Begin opening a buffer as client B, but leave the project before the open completes.
3367    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx));
3368    let buffer_b = cx_b.executor().spawn(open_buffer);
3369    cx_b.update(|_| drop(project_b));
3370    drop(buffer_b);
3371
3372    // See that the guest has left.
3373    executor.run_until_parked();
3374
3375    project_a.read_with(cx_a, |p, _| assert!(p.collaborators().is_empty()));
3376}
3377
3378#[gpui::test(iterations = 10)]
3379async fn test_canceling_buffer_opening(
3380    executor: BackgroundExecutor,
3381    cx_a: &mut TestAppContext,
3382    cx_b: &mut TestAppContext,
3383) {
3384    let mut server = TestServer::start(executor.clone()).await;
3385    let client_a = server.create_client(cx_a, "user_a").await;
3386    let client_b = server.create_client(cx_b, "user_b").await;
3387    server
3388        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3389        .await;
3390    let active_call_a = cx_a.read(ActiveCall::global);
3391
3392    client_a
3393        .fs()
3394        .insert_tree(
3395            "/dir",
3396            json!({
3397                "a.txt": "abc",
3398            }),
3399        )
3400        .await;
3401    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3402    let project_id = active_call_a
3403        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3404        .await
3405        .unwrap();
3406    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3407
3408    let buffer_a = project_a
3409        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3410        .await
3411        .unwrap();
3412
3413    // Open a buffer as client B but cancel after a random amount of time.
3414    let buffer_b = project_b.update(cx_b, |p, cx| {
3415        p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3416    });
3417    executor.simulate_random_delay().await;
3418    drop(buffer_b);
3419
3420    // Try opening the same buffer again as client B, and ensure we can
3421    // still do it despite the cancellation above.
3422    let buffer_b = project_b
3423        .update(cx_b, |p, cx| {
3424            p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3425        })
3426        .await
3427        .unwrap();
3428
3429    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "abc"));
3430}
3431
3432#[gpui::test(iterations = 10)]
3433async fn test_leaving_project(
3434    executor: BackgroundExecutor,
3435    cx_a: &mut TestAppContext,
3436    cx_b: &mut TestAppContext,
3437    cx_c: &mut TestAppContext,
3438) {
3439    let mut server = TestServer::start(executor.clone()).await;
3440    let client_a = server.create_client(cx_a, "user_a").await;
3441    let client_b = server.create_client(cx_b, "user_b").await;
3442    let client_c = server.create_client(cx_c, "user_c").await;
3443    server
3444        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3445        .await;
3446    let active_call_a = cx_a.read(ActiveCall::global);
3447
3448    client_a
3449        .fs()
3450        .insert_tree(
3451            "/a",
3452            json!({
3453                "a.txt": "a-contents",
3454                "b.txt": "b-contents",
3455            }),
3456        )
3457        .await;
3458    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
3459    let project_id = active_call_a
3460        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3461        .await
3462        .unwrap();
3463    let project_b1 = client_b.build_remote_project(project_id, cx_b).await;
3464    let project_c = client_c.build_remote_project(project_id, cx_c).await;
3465
3466    // Client A sees that a guest has joined.
3467    executor.run_until_parked();
3468
3469    project_a.read_with(cx_a, |project, _| {
3470        assert_eq!(project.collaborators().len(), 2);
3471    });
3472
3473    project_b1.read_with(cx_b, |project, _| {
3474        assert_eq!(project.collaborators().len(), 2);
3475    });
3476
3477    project_c.read_with(cx_c, |project, _| {
3478        assert_eq!(project.collaborators().len(), 2);
3479    });
3480
3481    // Client B opens a buffer.
3482    let buffer_b1 = project_b1
3483        .update(cx_b, |project, cx| {
3484            let worktree_id = project.worktrees().next().unwrap().read(cx).id();
3485            project.open_buffer((worktree_id, "a.txt"), cx)
3486        })
3487        .await
3488        .unwrap();
3489
3490    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3491
3492    // Drop client B's project and ensure client A and client C observe client B leaving.
3493    cx_b.update(|_| drop(project_b1));
3494    executor.run_until_parked();
3495
3496    project_a.read_with(cx_a, |project, _| {
3497        assert_eq!(project.collaborators().len(), 1);
3498    });
3499
3500    project_c.read_with(cx_c, |project, _| {
3501        assert_eq!(project.collaborators().len(), 1);
3502    });
3503
3504    // Client B re-joins the project and can open buffers as before.
3505    let project_b2 = client_b.build_remote_project(project_id, cx_b).await;
3506    executor.run_until_parked();
3507
3508    project_a.read_with(cx_a, |project, _| {
3509        assert_eq!(project.collaborators().len(), 2);
3510    });
3511
3512    project_b2.read_with(cx_b, |project, _| {
3513        assert_eq!(project.collaborators().len(), 2);
3514    });
3515
3516    project_c.read_with(cx_c, |project, _| {
3517        assert_eq!(project.collaborators().len(), 2);
3518    });
3519
3520    let buffer_b2 = project_b2
3521        .update(cx_b, |project, cx| {
3522            let worktree_id = project.worktrees().next().unwrap().read(cx).id();
3523            project.open_buffer((worktree_id, "a.txt"), cx)
3524        })
3525        .await
3526        .unwrap();
3527
3528    buffer_b2.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3529
3530    // Drop client B's connection and ensure client A and client C observe client B leaving.
3531    client_b.disconnect(&cx_b.to_async());
3532    executor.advance_clock(RECONNECT_TIMEOUT);
3533
3534    project_a.read_with(cx_a, |project, _| {
3535        assert_eq!(project.collaborators().len(), 1);
3536    });
3537
3538    project_b2.read_with(cx_b, |project, _| {
3539        assert!(project.is_disconnected());
3540    });
3541
3542    project_c.read_with(cx_c, |project, _| {
3543        assert_eq!(project.collaborators().len(), 1);
3544    });
3545
3546    // Client B can't join the project, unless they re-join the room.
3547    cx_b.spawn(|cx| {
3548        Project::remote(
3549            project_id,
3550            client_b.app_state.client.clone(),
3551            client_b.user_store().clone(),
3552            client_b.language_registry().clone(),
3553            FakeFs::new(cx.background_executor().clone()),
3554            ChannelRole::Member,
3555            cx,
3556        )
3557    })
3558    .await
3559    .unwrap_err();
3560
3561    // Simulate connection loss for client C and ensure client A observes client C leaving the project.
3562    client_c.wait_for_current_user(cx_c).await;
3563    server.forbid_connections();
3564    server.disconnect_client(client_c.peer_id().unwrap());
3565    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
3566    executor.run_until_parked();
3567
3568    project_a.read_with(cx_a, |project, _| {
3569        assert_eq!(project.collaborators().len(), 0);
3570    });
3571
3572    project_b2.read_with(cx_b, |project, _| {
3573        assert!(project.is_disconnected());
3574    });
3575
3576    project_c.read_with(cx_c, |project, _| {
3577        assert!(project.is_disconnected());
3578    });
3579}
3580
3581#[gpui::test(iterations = 10)]
3582async fn test_collaborating_with_diagnostics(
3583    executor: BackgroundExecutor,
3584    cx_a: &mut TestAppContext,
3585    cx_b: &mut TestAppContext,
3586    cx_c: &mut TestAppContext,
3587) {
3588    let mut server = TestServer::start(executor.clone()).await;
3589    let client_a = server.create_client(cx_a, "user_a").await;
3590    let client_b = server.create_client(cx_b, "user_b").await;
3591    let client_c = server.create_client(cx_c, "user_c").await;
3592    server
3593        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3594        .await;
3595    let active_call_a = cx_a.read(ActiveCall::global);
3596
3597    // Set up a fake language server.
3598    let mut language = Language::new(
3599        LanguageConfig {
3600            name: "Rust".into(),
3601            path_suffixes: vec!["rs".to_string()],
3602            ..Default::default()
3603        },
3604        Some(tree_sitter_rust::language()),
3605    );
3606    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
3607    client_a.language_registry().add(Arc::new(language));
3608
3609    // Share a project as client A
3610    client_a
3611        .fs()
3612        .insert_tree(
3613            "/a",
3614            json!({
3615                "a.rs": "let one = two",
3616                "other.rs": "",
3617            }),
3618        )
3619        .await;
3620    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3621
3622    // Cause the language server to start.
3623    let _buffer = project_a
3624        .update(cx_a, |project, cx| {
3625            project.open_buffer(
3626                ProjectPath {
3627                    worktree_id,
3628                    path: Path::new("other.rs").into(),
3629                },
3630                cx,
3631            )
3632        })
3633        .await
3634        .unwrap();
3635
3636    // Simulate a language server reporting errors for a file.
3637    let mut fake_language_server = fake_language_servers.next().await.unwrap();
3638    fake_language_server
3639        .receive_notification::<lsp::notification::DidOpenTextDocument>()
3640        .await;
3641    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3642        lsp::PublishDiagnosticsParams {
3643            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3644            version: None,
3645            diagnostics: vec![lsp::Diagnostic {
3646                severity: Some(lsp::DiagnosticSeverity::WARNING),
3647                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3648                message: "message 0".to_string(),
3649                ..Default::default()
3650            }],
3651        },
3652    );
3653
3654    // Client A shares the project and, simultaneously, the language server
3655    // publishes a diagnostic. This is done to ensure that the server always
3656    // observes the latest diagnostics for a worktree.
3657    let project_id = active_call_a
3658        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3659        .await
3660        .unwrap();
3661    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3662        lsp::PublishDiagnosticsParams {
3663            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3664            version: None,
3665            diagnostics: vec![lsp::Diagnostic {
3666                severity: Some(lsp::DiagnosticSeverity::ERROR),
3667                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3668                message: "message 1".to_string(),
3669                ..Default::default()
3670            }],
3671        },
3672    );
3673
3674    // Join the worktree as client B.
3675    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3676
3677    // Wait for server to see the diagnostics update.
3678    executor.run_until_parked();
3679
3680    // Ensure client B observes the new diagnostics.
3681
3682    project_b.read_with(cx_b, |project, cx| {
3683        assert_eq!(
3684            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
3685            &[(
3686                ProjectPath {
3687                    worktree_id,
3688                    path: Arc::from(Path::new("a.rs")),
3689                },
3690                LanguageServerId(0),
3691                DiagnosticSummary {
3692                    error_count: 1,
3693                    warning_count: 0,
3694                    ..Default::default()
3695                },
3696            )]
3697        )
3698    });
3699
3700    // Join project as client C and observe the diagnostics.
3701    let project_c = client_c.build_remote_project(project_id, cx_c).await;
3702    let project_c_diagnostic_summaries =
3703        Rc::new(RefCell::new(project_c.read_with(cx_c, |project, cx| {
3704            project.diagnostic_summaries(false, cx).collect::<Vec<_>>()
3705        })));
3706    project_c.update(cx_c, |_, cx| {
3707        let summaries = project_c_diagnostic_summaries.clone();
3708        cx.subscribe(&project_c, {
3709            move |p, _, event, cx| {
3710                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
3711                    *summaries.borrow_mut() = p.diagnostic_summaries(false, cx).collect();
3712                }
3713            }
3714        })
3715        .detach();
3716    });
3717
3718    executor.run_until_parked();
3719    assert_eq!(
3720        project_c_diagnostic_summaries.borrow().as_slice(),
3721        &[(
3722            ProjectPath {
3723                worktree_id,
3724                path: Arc::from(Path::new("a.rs")),
3725            },
3726            LanguageServerId(0),
3727            DiagnosticSummary {
3728                error_count: 1,
3729                warning_count: 0,
3730                ..Default::default()
3731            },
3732        )]
3733    );
3734
3735    // Simulate a language server reporting more errors for a file.
3736    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3737        lsp::PublishDiagnosticsParams {
3738            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3739            version: None,
3740            diagnostics: vec![
3741                lsp::Diagnostic {
3742                    severity: Some(lsp::DiagnosticSeverity::ERROR),
3743                    range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3744                    message: "message 1".to_string(),
3745                    ..Default::default()
3746                },
3747                lsp::Diagnostic {
3748                    severity: Some(lsp::DiagnosticSeverity::WARNING),
3749                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 13)),
3750                    message: "message 2".to_string(),
3751                    ..Default::default()
3752                },
3753            ],
3754        },
3755    );
3756
3757    // Clients B and C get the updated summaries
3758    executor.run_until_parked();
3759
3760    project_b.read_with(cx_b, |project, cx| {
3761        assert_eq!(
3762            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
3763            [(
3764                ProjectPath {
3765                    worktree_id,
3766                    path: Arc::from(Path::new("a.rs")),
3767                },
3768                LanguageServerId(0),
3769                DiagnosticSummary {
3770                    error_count: 1,
3771                    warning_count: 1,
3772                },
3773            )]
3774        );
3775    });
3776
3777    project_c.read_with(cx_c, |project, cx| {
3778        assert_eq!(
3779            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
3780            [(
3781                ProjectPath {
3782                    worktree_id,
3783                    path: Arc::from(Path::new("a.rs")),
3784                },
3785                LanguageServerId(0),
3786                DiagnosticSummary {
3787                    error_count: 1,
3788                    warning_count: 1,
3789                },
3790            )]
3791        );
3792    });
3793
3794    // Open the file with the errors on client B. They should be present.
3795    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
3796    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
3797
3798    buffer_b.read_with(cx_b, |buffer, _| {
3799        assert_eq!(
3800            buffer
3801                .snapshot()
3802                .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
3803                .collect::<Vec<_>>(),
3804            &[
3805                DiagnosticEntry {
3806                    range: Point::new(0, 4)..Point::new(0, 7),
3807                    diagnostic: Diagnostic {
3808                        group_id: 2,
3809                        message: "message 1".to_string(),
3810                        severity: lsp::DiagnosticSeverity::ERROR,
3811                        is_primary: true,
3812                        ..Default::default()
3813                    }
3814                },
3815                DiagnosticEntry {
3816                    range: Point::new(0, 10)..Point::new(0, 13),
3817                    diagnostic: Diagnostic {
3818                        group_id: 3,
3819                        severity: lsp::DiagnosticSeverity::WARNING,
3820                        message: "message 2".to_string(),
3821                        is_primary: true,
3822                        ..Default::default()
3823                    }
3824                }
3825            ]
3826        );
3827    });
3828
3829    // Simulate a language server reporting no errors for a file.
3830    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3831        lsp::PublishDiagnosticsParams {
3832            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3833            version: None,
3834            diagnostics: vec![],
3835        },
3836    );
3837    executor.run_until_parked();
3838
3839    project_a.read_with(cx_a, |project, cx| {
3840        assert_eq!(
3841            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
3842            []
3843        )
3844    });
3845
3846    project_b.read_with(cx_b, |project, cx| {
3847        assert_eq!(
3848            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
3849            []
3850        )
3851    });
3852
3853    project_c.read_with(cx_c, |project, cx| {
3854        assert_eq!(
3855            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
3856            []
3857        )
3858    });
3859}
3860
3861#[gpui::test(iterations = 10)]
3862async fn test_collaborating_with_lsp_progress_updates_and_diagnostics_ordering(
3863    executor: BackgroundExecutor,
3864    cx_a: &mut TestAppContext,
3865    cx_b: &mut TestAppContext,
3866) {
3867    let mut server = TestServer::start(executor.clone()).await;
3868    let client_a = server.create_client(cx_a, "user_a").await;
3869    let client_b = server.create_client(cx_b, "user_b").await;
3870    server
3871        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3872        .await;
3873
3874    // Set up a fake language server.
3875    let mut language = Language::new(
3876        LanguageConfig {
3877            name: "Rust".into(),
3878            path_suffixes: vec!["rs".to_string()],
3879            ..Default::default()
3880        },
3881        Some(tree_sitter_rust::language()),
3882    );
3883    let mut fake_language_servers = language
3884        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3885            disk_based_diagnostics_progress_token: Some("the-disk-based-token".into()),
3886            disk_based_diagnostics_sources: vec!["the-disk-based-diagnostics-source".into()],
3887            ..Default::default()
3888        }))
3889        .await;
3890    client_a.language_registry().add(Arc::new(language));
3891
3892    let file_names = &["one.rs", "two.rs", "three.rs", "four.rs", "five.rs"];
3893    client_a
3894        .fs()
3895        .insert_tree(
3896            "/test",
3897            json!({
3898                "one.rs": "const ONE: usize = 1;",
3899                "two.rs": "const TWO: usize = 2;",
3900                "three.rs": "const THREE: usize = 3;",
3901                "four.rs": "const FOUR: usize = 3;",
3902                "five.rs": "const FIVE: usize = 3;",
3903            }),
3904        )
3905        .await;
3906
3907    let (project_a, worktree_id) = client_a.build_local_project("/test", cx_a).await;
3908
3909    // Share a project as client A
3910    let active_call_a = cx_a.read(ActiveCall::global);
3911    let project_id = active_call_a
3912        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3913        .await
3914        .unwrap();
3915
3916    // Join the project as client B and open all three files.
3917    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3918    let guest_buffers = futures::future::try_join_all(file_names.iter().map(|file_name| {
3919        project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, file_name), cx))
3920    }))
3921    .await
3922    .unwrap();
3923
3924    // Simulate a language server reporting errors for a file.
3925    let fake_language_server = fake_language_servers.next().await.unwrap();
3926    fake_language_server
3927        .request::<lsp::request::WorkDoneProgressCreate>(lsp::WorkDoneProgressCreateParams {
3928            token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
3929        })
3930        .await
3931        .unwrap();
3932    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
3933        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
3934        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin(
3935            lsp::WorkDoneProgressBegin {
3936                title: "Progress Began".into(),
3937                ..Default::default()
3938            },
3939        )),
3940    });
3941    for file_name in file_names {
3942        fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3943            lsp::PublishDiagnosticsParams {
3944                uri: lsp::Url::from_file_path(Path::new("/test").join(file_name)).unwrap(),
3945                version: None,
3946                diagnostics: vec![lsp::Diagnostic {
3947                    severity: Some(lsp::DiagnosticSeverity::WARNING),
3948                    source: Some("the-disk-based-diagnostics-source".into()),
3949                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
3950                    message: "message one".to_string(),
3951                    ..Default::default()
3952                }],
3953            },
3954        );
3955    }
3956    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
3957        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
3958        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End(
3959            lsp::WorkDoneProgressEnd { message: None },
3960        )),
3961    });
3962
3963    // When the "disk base diagnostics finished" message is received, the buffers'
3964    // diagnostics are expected to be present.
3965    let disk_based_diagnostics_finished = Arc::new(AtomicBool::new(false));
3966    project_b.update(cx_b, {
3967        let project_b = project_b.clone();
3968        let disk_based_diagnostics_finished = disk_based_diagnostics_finished.clone();
3969        move |_, cx| {
3970            cx.subscribe(&project_b, move |_, _, event, cx| {
3971                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
3972                    disk_based_diagnostics_finished.store(true, SeqCst);
3973                    for buffer in &guest_buffers {
3974                        assert_eq!(
3975                            buffer
3976                                .read(cx)
3977                                .snapshot()
3978                                .diagnostics_in_range::<_, usize>(0..5, false)
3979                                .count(),
3980                            1,
3981                            "expected a diagnostic for buffer {:?}",
3982                            buffer.read(cx).file().unwrap().path(),
3983                        );
3984                    }
3985                }
3986            })
3987            .detach();
3988        }
3989    });
3990
3991    executor.run_until_parked();
3992    assert!(disk_based_diagnostics_finished.load(SeqCst));
3993}
3994
3995#[gpui::test(iterations = 10)]
3996async fn test_reloading_buffer_manually(
3997    executor: BackgroundExecutor,
3998    cx_a: &mut TestAppContext,
3999    cx_b: &mut TestAppContext,
4000) {
4001    let mut server = TestServer::start(executor.clone()).await;
4002    let client_a = server.create_client(cx_a, "user_a").await;
4003    let client_b = server.create_client(cx_b, "user_b").await;
4004    server
4005        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4006        .await;
4007    let active_call_a = cx_a.read(ActiveCall::global);
4008
4009    client_a
4010        .fs()
4011        .insert_tree("/a", json!({ "a.rs": "let one = 1;" }))
4012        .await;
4013    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
4014    let buffer_a = project_a
4015        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
4016        .await
4017        .unwrap();
4018    let project_id = active_call_a
4019        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4020        .await
4021        .unwrap();
4022
4023    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4024
4025    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4026    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4027    buffer_b.update(cx_b, |buffer, cx| {
4028        buffer.edit([(4..7, "six")], None, cx);
4029        buffer.edit([(10..11, "6")], None, cx);
4030        assert_eq!(buffer.text(), "let six = 6;");
4031        assert!(buffer.is_dirty());
4032        assert!(!buffer.has_conflict());
4033    });
4034    executor.run_until_parked();
4035
4036    buffer_a.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "let six = 6;"));
4037
4038    client_a
4039        .fs()
4040        .save(
4041            "/a/a.rs".as_ref(),
4042            &Rope::from("let seven = 7;"),
4043            LineEnding::Unix,
4044        )
4045        .await
4046        .unwrap();
4047    executor.run_until_parked();
4048
4049    buffer_a.read_with(cx_a, |buffer, _| assert!(buffer.has_conflict()));
4050
4051    buffer_b.read_with(cx_b, |buffer, _| assert!(buffer.has_conflict()));
4052
4053    project_b
4054        .update(cx_b, |project, cx| {
4055            project.reload_buffers(HashSet::from_iter([buffer_b.clone()]), true, cx)
4056        })
4057        .await
4058        .unwrap();
4059
4060    buffer_a.read_with(cx_a, |buffer, _| {
4061        assert_eq!(buffer.text(), "let seven = 7;");
4062        assert!(!buffer.is_dirty());
4063        assert!(!buffer.has_conflict());
4064    });
4065
4066    buffer_b.read_with(cx_b, |buffer, _| {
4067        assert_eq!(buffer.text(), "let seven = 7;");
4068        assert!(!buffer.is_dirty());
4069        assert!(!buffer.has_conflict());
4070    });
4071
4072    buffer_a.update(cx_a, |buffer, cx| {
4073        // Undoing on the host is a no-op when the reload was initiated by the guest.
4074        buffer.undo(cx);
4075        assert_eq!(buffer.text(), "let seven = 7;");
4076        assert!(!buffer.is_dirty());
4077        assert!(!buffer.has_conflict());
4078    });
4079    buffer_b.update(cx_b, |buffer, cx| {
4080        // Undoing on the guest rolls back the buffer to before it was reloaded but the conflict gets cleared.
4081        buffer.undo(cx);
4082        assert_eq!(buffer.text(), "let six = 6;");
4083        assert!(buffer.is_dirty());
4084        assert!(!buffer.has_conflict());
4085    });
4086}
4087
4088#[gpui::test(iterations = 10)]
4089async fn test_formatting_buffer(
4090    executor: BackgroundExecutor,
4091    cx_a: &mut TestAppContext,
4092    cx_b: &mut TestAppContext,
4093) {
4094    executor.allow_parking();
4095    let mut server = TestServer::start(executor.clone()).await;
4096    let client_a = server.create_client(cx_a, "user_a").await;
4097    let client_b = server.create_client(cx_b, "user_b").await;
4098    server
4099        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4100        .await;
4101    let active_call_a = cx_a.read(ActiveCall::global);
4102
4103    // Set up a fake language server.
4104    let mut language = Language::new(
4105        LanguageConfig {
4106            name: "Rust".into(),
4107            path_suffixes: vec!["rs".to_string()],
4108            ..Default::default()
4109        },
4110        Some(tree_sitter_rust::language()),
4111    );
4112    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4113    client_a.language_registry().add(Arc::new(language));
4114
4115    // Here we insert a fake tree with a directory that exists on disk. This is needed
4116    // because later we'll invoke a command, which requires passing a working directory
4117    // that points to a valid location on disk.
4118    let directory = env::current_dir().unwrap();
4119    client_a
4120        .fs()
4121        .insert_tree(&directory, json!({ "a.rs": "let one = \"two\"" }))
4122        .await;
4123    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
4124    let project_id = active_call_a
4125        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4126        .await
4127        .unwrap();
4128    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4129
4130    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4131    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4132
4133    let fake_language_server = fake_language_servers.next().await.unwrap();
4134    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
4135        Ok(Some(vec![
4136            lsp::TextEdit {
4137                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 4)),
4138                new_text: "h".to_string(),
4139            },
4140            lsp::TextEdit {
4141                range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 7)),
4142                new_text: "y".to_string(),
4143            },
4144        ]))
4145    });
4146
4147    project_b
4148        .update(cx_b, |project, cx| {
4149            project.format(
4150                HashSet::from_iter([buffer_b.clone()]),
4151                true,
4152                FormatTrigger::Save,
4153                cx,
4154            )
4155        })
4156        .await
4157        .unwrap();
4158
4159    // The edits from the LSP are applied, and a final newline is added.
4160    assert_eq!(
4161        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4162        "let honey = \"two\"\n"
4163    );
4164
4165    // Ensure buffer can be formatted using an external command. Notice how the
4166    // host's configuration is honored as opposed to using the guest's settings.
4167    cx_a.update(|cx| {
4168        cx.update_global(|store: &mut SettingsStore, cx| {
4169            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4170                file.defaults.formatter = Some(Formatter::External {
4171                    command: "awk".into(),
4172                    arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()].into(),
4173                });
4174            });
4175        });
4176    });
4177    project_b
4178        .update(cx_b, |project, cx| {
4179            project.format(
4180                HashSet::from_iter([buffer_b.clone()]),
4181                true,
4182                FormatTrigger::Save,
4183                cx,
4184            )
4185        })
4186        .await
4187        .unwrap();
4188    assert_eq!(
4189        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4190        format!("let honey = \"{}/a.rs\"\n", directory.to_str().unwrap())
4191    );
4192}
4193
4194#[gpui::test(iterations = 10)]
4195async fn test_prettier_formatting_buffer(
4196    executor: BackgroundExecutor,
4197    cx_a: &mut TestAppContext,
4198    cx_b: &mut TestAppContext,
4199) {
4200    let mut server = TestServer::start(executor.clone()).await;
4201    let client_a = server.create_client(cx_a, "user_a").await;
4202    let client_b = server.create_client(cx_b, "user_b").await;
4203    server
4204        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4205        .await;
4206    let active_call_a = cx_a.read(ActiveCall::global);
4207
4208    // Set up a fake language server.
4209    let mut language = Language::new(
4210        LanguageConfig {
4211            name: "Rust".into(),
4212            path_suffixes: vec!["rs".to_string()],
4213            prettier_parser_name: Some("test_parser".to_string()),
4214            ..Default::default()
4215        },
4216        Some(tree_sitter_rust::language()),
4217    );
4218    let test_plugin = "test_plugin";
4219    let mut fake_language_servers = language
4220        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4221            prettier_plugins: vec![test_plugin],
4222            ..Default::default()
4223        }))
4224        .await;
4225    let language = Arc::new(language);
4226    client_a.language_registry().add(Arc::clone(&language));
4227
4228    // Here we insert a fake tree with a directory that exists on disk. This is needed
4229    // because later we'll invoke a command, which requires passing a working directory
4230    // that points to a valid location on disk.
4231    let directory = env::current_dir().unwrap();
4232    let buffer_text = "let one = \"two\"";
4233    client_a
4234        .fs()
4235        .insert_tree(&directory, json!({ "a.rs": buffer_text }))
4236        .await;
4237    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
4238    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
4239    let open_buffer = project_a.update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4240    let buffer_a = cx_a.executor().spawn(open_buffer).await.unwrap();
4241
4242    let project_id = active_call_a
4243        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4244        .await
4245        .unwrap();
4246    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4247    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4248    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4249
4250    cx_a.update(|cx| {
4251        cx.update_global(|store: &mut SettingsStore, cx| {
4252            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4253                file.defaults.formatter = Some(Formatter::Auto);
4254            });
4255        });
4256    });
4257    cx_b.update(|cx| {
4258        cx.update_global(|store: &mut SettingsStore, cx| {
4259            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4260                file.defaults.formatter = Some(Formatter::LanguageServer);
4261            });
4262        });
4263    });
4264    let fake_language_server = fake_language_servers.next().await.unwrap();
4265    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
4266        panic!(
4267            "Unexpected: prettier should be preferred since it's enabled and language supports it"
4268        )
4269    });
4270
4271    project_b
4272        .update(cx_b, |project, cx| {
4273            project.format(
4274                HashSet::from_iter([buffer_b.clone()]),
4275                true,
4276                FormatTrigger::Save,
4277                cx,
4278            )
4279        })
4280        .await
4281        .unwrap();
4282
4283    executor.run_until_parked();
4284    assert_eq!(
4285        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4286        buffer_text.to_string() + "\n" + prettier_format_suffix,
4287        "Prettier formatting was not applied to client buffer after client's request"
4288    );
4289
4290    project_a
4291        .update(cx_a, |project, cx| {
4292            project.format(
4293                HashSet::from_iter([buffer_a.clone()]),
4294                true,
4295                FormatTrigger::Manual,
4296                cx,
4297            )
4298        })
4299        .await
4300        .unwrap();
4301
4302    executor.run_until_parked();
4303    assert_eq!(
4304        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4305        buffer_text.to_string() + "\n" + prettier_format_suffix + "\n" + prettier_format_suffix,
4306        "Prettier formatting was not applied to client buffer after host's request"
4307    );
4308}
4309
4310#[gpui::test(iterations = 10)]
4311async fn test_definition(
4312    executor: BackgroundExecutor,
4313    cx_a: &mut TestAppContext,
4314    cx_b: &mut TestAppContext,
4315) {
4316    let mut server = TestServer::start(executor.clone()).await;
4317    let client_a = server.create_client(cx_a, "user_a").await;
4318    let client_b = server.create_client(cx_b, "user_b").await;
4319    server
4320        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4321        .await;
4322    let active_call_a = cx_a.read(ActiveCall::global);
4323
4324    // Set up a fake language server.
4325    let mut language = Language::new(
4326        LanguageConfig {
4327            name: "Rust".into(),
4328            path_suffixes: vec!["rs".to_string()],
4329            ..Default::default()
4330        },
4331        Some(tree_sitter_rust::language()),
4332    );
4333    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4334    client_a.language_registry().add(Arc::new(language));
4335
4336    client_a
4337        .fs()
4338        .insert_tree(
4339            "/root",
4340            json!({
4341                "dir-1": {
4342                    "a.rs": "const ONE: usize = b::TWO + b::THREE;",
4343                },
4344                "dir-2": {
4345                    "b.rs": "const TWO: c::T2 = 2;\nconst THREE: usize = 3;",
4346                    "c.rs": "type T2 = usize;",
4347                }
4348            }),
4349        )
4350        .await;
4351    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4352    let project_id = active_call_a
4353        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4354        .await
4355        .unwrap();
4356    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4357
4358    // Open the file on client B.
4359    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4360    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4361
4362    // Request the definition of a symbol as the guest.
4363    let fake_language_server = fake_language_servers.next().await.unwrap();
4364    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4365        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4366            lsp::Location::new(
4367                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4368                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4369            ),
4370        )))
4371    });
4372
4373    let definitions_1 = project_b
4374        .update(cx_b, |p, cx| p.definition(&buffer_b, 23, cx))
4375        .await
4376        .unwrap();
4377    cx_b.read(|cx| {
4378        assert_eq!(definitions_1.len(), 1);
4379        assert_eq!(project_b.read(cx).worktrees().count(), 2);
4380        let target_buffer = definitions_1[0].target.buffer.read(cx);
4381        assert_eq!(
4382            target_buffer.text(),
4383            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4384        );
4385        assert_eq!(
4386            definitions_1[0].target.range.to_point(target_buffer),
4387            Point::new(0, 6)..Point::new(0, 9)
4388        );
4389    });
4390
4391    // Try getting more definitions for the same buffer, ensuring the buffer gets reused from
4392    // the previous call to `definition`.
4393    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4394        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4395            lsp::Location::new(
4396                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4397                lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)),
4398            ),
4399        )))
4400    });
4401
4402    let definitions_2 = project_b
4403        .update(cx_b, |p, cx| p.definition(&buffer_b, 33, cx))
4404        .await
4405        .unwrap();
4406    cx_b.read(|cx| {
4407        assert_eq!(definitions_2.len(), 1);
4408        assert_eq!(project_b.read(cx).worktrees().count(), 2);
4409        let target_buffer = definitions_2[0].target.buffer.read(cx);
4410        assert_eq!(
4411            target_buffer.text(),
4412            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4413        );
4414        assert_eq!(
4415            definitions_2[0].target.range.to_point(target_buffer),
4416            Point::new(1, 6)..Point::new(1, 11)
4417        );
4418    });
4419    assert_eq!(
4420        definitions_1[0].target.buffer,
4421        definitions_2[0].target.buffer
4422    );
4423
4424    fake_language_server.handle_request::<lsp::request::GotoTypeDefinition, _, _>(
4425        |req, _| async move {
4426            assert_eq!(
4427                req.text_document_position_params.position,
4428                lsp::Position::new(0, 7)
4429            );
4430            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4431                lsp::Location::new(
4432                    lsp::Url::from_file_path("/root/dir-2/c.rs").unwrap(),
4433                    lsp::Range::new(lsp::Position::new(0, 5), lsp::Position::new(0, 7)),
4434                ),
4435            )))
4436        },
4437    );
4438
4439    let type_definitions = project_b
4440        .update(cx_b, |p, cx| p.type_definition(&buffer_b, 7, cx))
4441        .await
4442        .unwrap();
4443    cx_b.read(|cx| {
4444        assert_eq!(type_definitions.len(), 1);
4445        let target_buffer = type_definitions[0].target.buffer.read(cx);
4446        assert_eq!(target_buffer.text(), "type T2 = usize;");
4447        assert_eq!(
4448            type_definitions[0].target.range.to_point(target_buffer),
4449            Point::new(0, 5)..Point::new(0, 7)
4450        );
4451    });
4452}
4453
4454#[gpui::test(iterations = 10)]
4455async fn test_references(
4456    executor: BackgroundExecutor,
4457    cx_a: &mut TestAppContext,
4458    cx_b: &mut TestAppContext,
4459) {
4460    let mut server = TestServer::start(executor.clone()).await;
4461    let client_a = server.create_client(cx_a, "user_a").await;
4462    let client_b = server.create_client(cx_b, "user_b").await;
4463    server
4464        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4465        .await;
4466    let active_call_a = cx_a.read(ActiveCall::global);
4467
4468    // Set up a fake language server.
4469    let mut language = Language::new(
4470        LanguageConfig {
4471            name: "Rust".into(),
4472            path_suffixes: vec!["rs".to_string()],
4473            ..Default::default()
4474        },
4475        Some(tree_sitter_rust::language()),
4476    );
4477    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4478    client_a.language_registry().add(Arc::new(language));
4479
4480    client_a
4481        .fs()
4482        .insert_tree(
4483            "/root",
4484            json!({
4485                "dir-1": {
4486                    "one.rs": "const ONE: usize = 1;",
4487                    "two.rs": "const TWO: usize = one::ONE + one::ONE;",
4488                },
4489                "dir-2": {
4490                    "three.rs": "const THREE: usize = two::TWO + one::ONE;",
4491                }
4492            }),
4493        )
4494        .await;
4495    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4496    let project_id = active_call_a
4497        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4498        .await
4499        .unwrap();
4500    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4501
4502    // Open the file on client B.
4503    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx));
4504    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4505
4506    // Request references to a symbol as the guest.
4507    let fake_language_server = fake_language_servers.next().await.unwrap();
4508    fake_language_server.handle_request::<lsp::request::References, _, _>(|params, _| async move {
4509        assert_eq!(
4510            params.text_document_position.text_document.uri.as_str(),
4511            "file:///root/dir-1/one.rs"
4512        );
4513        Ok(Some(vec![
4514            lsp::Location {
4515                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4516                range: lsp::Range::new(lsp::Position::new(0, 24), lsp::Position::new(0, 27)),
4517            },
4518            lsp::Location {
4519                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4520                range: lsp::Range::new(lsp::Position::new(0, 35), lsp::Position::new(0, 38)),
4521            },
4522            lsp::Location {
4523                uri: lsp::Url::from_file_path("/root/dir-2/three.rs").unwrap(),
4524                range: lsp::Range::new(lsp::Position::new(0, 37), lsp::Position::new(0, 40)),
4525            },
4526        ]))
4527    });
4528
4529    let references = project_b
4530        .update(cx_b, |p, cx| p.references(&buffer_b, 7, cx))
4531        .await
4532        .unwrap();
4533    cx_b.read(|cx| {
4534        assert_eq!(references.len(), 3);
4535        assert_eq!(project_b.read(cx).worktrees().count(), 2);
4536
4537        let two_buffer = references[0].buffer.read(cx);
4538        let three_buffer = references[2].buffer.read(cx);
4539        assert_eq!(
4540            two_buffer.file().unwrap().path().as_ref(),
4541            Path::new("two.rs")
4542        );
4543        assert_eq!(references[1].buffer, references[0].buffer);
4544        assert_eq!(
4545            three_buffer.file().unwrap().full_path(cx),
4546            Path::new("/root/dir-2/three.rs")
4547        );
4548
4549        assert_eq!(references[0].range.to_offset(two_buffer), 24..27);
4550        assert_eq!(references[1].range.to_offset(two_buffer), 35..38);
4551        assert_eq!(references[2].range.to_offset(three_buffer), 37..40);
4552    });
4553}
4554
4555#[gpui::test(iterations = 10)]
4556async fn test_project_search(
4557    executor: BackgroundExecutor,
4558    cx_a: &mut TestAppContext,
4559    cx_b: &mut TestAppContext,
4560) {
4561    let mut server = TestServer::start(executor.clone()).await;
4562    let client_a = server.create_client(cx_a, "user_a").await;
4563    let client_b = server.create_client(cx_b, "user_b").await;
4564    server
4565        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4566        .await;
4567    let active_call_a = cx_a.read(ActiveCall::global);
4568
4569    client_a
4570        .fs()
4571        .insert_tree(
4572            "/root",
4573            json!({
4574                "dir-1": {
4575                    "a": "hello world",
4576                    "b": "goodnight moon",
4577                    "c": "a world of goo",
4578                    "d": "world champion of clown world",
4579                },
4580                "dir-2": {
4581                    "e": "disney world is fun",
4582                }
4583            }),
4584        )
4585        .await;
4586    let (project_a, _) = client_a.build_local_project("/root/dir-1", cx_a).await;
4587    let (worktree_2, _) = project_a
4588        .update(cx_a, |p, cx| {
4589            p.find_or_create_local_worktree("/root/dir-2", true, cx)
4590        })
4591        .await
4592        .unwrap();
4593    worktree_2
4594        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
4595        .await;
4596    let project_id = active_call_a
4597        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4598        .await
4599        .unwrap();
4600
4601    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4602
4603    // Perform a search as the guest.
4604    let mut results = HashMap::default();
4605    let mut search_rx = project_b.update(cx_b, |project, cx| {
4606        project.search(
4607            SearchQuery::text("world", false, false, false, Vec::new(), Vec::new()).unwrap(),
4608            cx,
4609        )
4610    });
4611    while let Some((buffer, ranges)) = search_rx.next().await {
4612        results.entry(buffer).or_insert(ranges);
4613    }
4614
4615    let mut ranges_by_path = results
4616        .into_iter()
4617        .map(|(buffer, ranges)| {
4618            buffer.read_with(cx_b, |buffer, cx| {
4619                let path = buffer.file().unwrap().full_path(cx);
4620                let offset_ranges = ranges
4621                    .into_iter()
4622                    .map(|range| range.to_offset(buffer))
4623                    .collect::<Vec<_>>();
4624                (path, offset_ranges)
4625            })
4626        })
4627        .collect::<Vec<_>>();
4628    ranges_by_path.sort_by_key(|(path, _)| path.clone());
4629
4630    assert_eq!(
4631        ranges_by_path,
4632        &[
4633            (PathBuf::from("dir-1/a"), vec![6..11]),
4634            (PathBuf::from("dir-1/c"), vec![2..7]),
4635            (PathBuf::from("dir-1/d"), vec![0..5, 24..29]),
4636            (PathBuf::from("dir-2/e"), vec![7..12]),
4637        ]
4638    );
4639}
4640
4641#[gpui::test(iterations = 10)]
4642async fn test_document_highlights(
4643    executor: BackgroundExecutor,
4644    cx_a: &mut TestAppContext,
4645    cx_b: &mut TestAppContext,
4646) {
4647    let mut server = TestServer::start(executor.clone()).await;
4648    let client_a = server.create_client(cx_a, "user_a").await;
4649    let client_b = server.create_client(cx_b, "user_b").await;
4650    server
4651        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4652        .await;
4653    let active_call_a = cx_a.read(ActiveCall::global);
4654
4655    client_a
4656        .fs()
4657        .insert_tree(
4658            "/root-1",
4659            json!({
4660                "main.rs": "fn double(number: i32) -> i32 { number + number }",
4661            }),
4662        )
4663        .await;
4664
4665    // Set up a fake language server.
4666    let mut language = Language::new(
4667        LanguageConfig {
4668            name: "Rust".into(),
4669            path_suffixes: vec!["rs".to_string()],
4670            ..Default::default()
4671        },
4672        Some(tree_sitter_rust::language()),
4673    );
4674    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4675    client_a.language_registry().add(Arc::new(language));
4676
4677    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4678    let project_id = active_call_a
4679        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4680        .await
4681        .unwrap();
4682    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4683
4684    // Open the file on client B.
4685    let open_b = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx));
4686    let buffer_b = cx_b.executor().spawn(open_b).await.unwrap();
4687
4688    // Request document highlights as the guest.
4689    let fake_language_server = fake_language_servers.next().await.unwrap();
4690    fake_language_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
4691        |params, _| async move {
4692            assert_eq!(
4693                params
4694                    .text_document_position_params
4695                    .text_document
4696                    .uri
4697                    .as_str(),
4698                "file:///root-1/main.rs"
4699            );
4700            assert_eq!(
4701                params.text_document_position_params.position,
4702                lsp::Position::new(0, 34)
4703            );
4704            Ok(Some(vec![
4705                lsp::DocumentHighlight {
4706                    kind: Some(lsp::DocumentHighlightKind::WRITE),
4707                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 16)),
4708                },
4709                lsp::DocumentHighlight {
4710                    kind: Some(lsp::DocumentHighlightKind::READ),
4711                    range: lsp::Range::new(lsp::Position::new(0, 32), lsp::Position::new(0, 38)),
4712                },
4713                lsp::DocumentHighlight {
4714                    kind: Some(lsp::DocumentHighlightKind::READ),
4715                    range: lsp::Range::new(lsp::Position::new(0, 41), lsp::Position::new(0, 47)),
4716                },
4717            ]))
4718        },
4719    );
4720
4721    let highlights = project_b
4722        .update(cx_b, |p, cx| p.document_highlights(&buffer_b, 34, cx))
4723        .await
4724        .unwrap();
4725
4726    buffer_b.read_with(cx_b, |buffer, _| {
4727        let snapshot = buffer.snapshot();
4728
4729        let highlights = highlights
4730            .into_iter()
4731            .map(|highlight| (highlight.kind, highlight.range.to_offset(&snapshot)))
4732            .collect::<Vec<_>>();
4733        assert_eq!(
4734            highlights,
4735            &[
4736                (lsp::DocumentHighlightKind::WRITE, 10..16),
4737                (lsp::DocumentHighlightKind::READ, 32..38),
4738                (lsp::DocumentHighlightKind::READ, 41..47)
4739            ]
4740        )
4741    });
4742}
4743
4744#[gpui::test(iterations = 10)]
4745async fn test_lsp_hover(
4746    executor: BackgroundExecutor,
4747    cx_a: &mut TestAppContext,
4748    cx_b: &mut TestAppContext,
4749) {
4750    let mut server = TestServer::start(executor.clone()).await;
4751    let client_a = server.create_client(cx_a, "user_a").await;
4752    let client_b = server.create_client(cx_b, "user_b").await;
4753    server
4754        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4755        .await;
4756    let active_call_a = cx_a.read(ActiveCall::global);
4757
4758    client_a
4759        .fs()
4760        .insert_tree(
4761            "/root-1",
4762            json!({
4763                "main.rs": "use std::collections::HashMap;",
4764            }),
4765        )
4766        .await;
4767
4768    // Set up a fake language server.
4769    let mut language = Language::new(
4770        LanguageConfig {
4771            name: "Rust".into(),
4772            path_suffixes: vec!["rs".to_string()],
4773            ..Default::default()
4774        },
4775        Some(tree_sitter_rust::language()),
4776    );
4777    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4778    client_a.language_registry().add(Arc::new(language));
4779
4780    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4781    let project_id = active_call_a
4782        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4783        .await
4784        .unwrap();
4785    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4786
4787    // Open the file as the guest
4788    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx));
4789    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4790
4791    // Request hover information as the guest.
4792    let fake_language_server = fake_language_servers.next().await.unwrap();
4793    fake_language_server.handle_request::<lsp::request::HoverRequest, _, _>(
4794        |params, _| async move {
4795            assert_eq!(
4796                params
4797                    .text_document_position_params
4798                    .text_document
4799                    .uri
4800                    .as_str(),
4801                "file:///root-1/main.rs"
4802            );
4803            assert_eq!(
4804                params.text_document_position_params.position,
4805                lsp::Position::new(0, 22)
4806            );
4807            Ok(Some(lsp::Hover {
4808                contents: lsp::HoverContents::Array(vec![
4809                    lsp::MarkedString::String("Test hover content.".to_string()),
4810                    lsp::MarkedString::LanguageString(lsp::LanguageString {
4811                        language: "Rust".to_string(),
4812                        value: "let foo = 42;".to_string(),
4813                    }),
4814                ]),
4815                range: Some(lsp::Range::new(
4816                    lsp::Position::new(0, 22),
4817                    lsp::Position::new(0, 29),
4818                )),
4819            }))
4820        },
4821    );
4822
4823    let hover_info = project_b
4824        .update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
4825        .await
4826        .unwrap()
4827        .unwrap();
4828
4829    buffer_b.read_with(cx_b, |buffer, _| {
4830        let snapshot = buffer.snapshot();
4831        assert_eq!(hover_info.range.unwrap().to_offset(&snapshot), 22..29);
4832        assert_eq!(
4833            hover_info.contents,
4834            vec![
4835                project::HoverBlock {
4836                    text: "Test hover content.".to_string(),
4837                    kind: HoverBlockKind::Markdown,
4838                },
4839                project::HoverBlock {
4840                    text: "let foo = 42;".to_string(),
4841                    kind: HoverBlockKind::Code {
4842                        language: "Rust".to_string()
4843                    },
4844                }
4845            ]
4846        );
4847    });
4848}
4849
4850#[gpui::test(iterations = 10)]
4851async fn test_project_symbols(
4852    executor: BackgroundExecutor,
4853    cx_a: &mut TestAppContext,
4854    cx_b: &mut TestAppContext,
4855) {
4856    let mut server = TestServer::start(executor.clone()).await;
4857    let client_a = server.create_client(cx_a, "user_a").await;
4858    let client_b = server.create_client(cx_b, "user_b").await;
4859    server
4860        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4861        .await;
4862    let active_call_a = cx_a.read(ActiveCall::global);
4863
4864    // Set up a fake language server.
4865    let mut language = Language::new(
4866        LanguageConfig {
4867            name: "Rust".into(),
4868            path_suffixes: vec!["rs".to_string()],
4869            ..Default::default()
4870        },
4871        Some(tree_sitter_rust::language()),
4872    );
4873    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4874    client_a.language_registry().add(Arc::new(language));
4875
4876    client_a
4877        .fs()
4878        .insert_tree(
4879            "/code",
4880            json!({
4881                "crate-1": {
4882                    "one.rs": "const ONE: usize = 1;",
4883                },
4884                "crate-2": {
4885                    "two.rs": "const TWO: usize = 2; const THREE: usize = 3;",
4886                },
4887                "private": {
4888                    "passwords.txt": "the-password",
4889                }
4890            }),
4891        )
4892        .await;
4893    let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await;
4894    let project_id = active_call_a
4895        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4896        .await
4897        .unwrap();
4898    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4899
4900    // Cause the language server to start.
4901    let open_buffer_task =
4902        project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx));
4903    let _buffer = cx_b.executor().spawn(open_buffer_task).await.unwrap();
4904
4905    let fake_language_server = fake_language_servers.next().await.unwrap();
4906    fake_language_server.handle_request::<lsp::WorkspaceSymbolRequest, _, _>(|_, _| async move {
4907        Ok(Some(lsp::WorkspaceSymbolResponse::Flat(vec![
4908            #[allow(deprecated)]
4909            lsp::SymbolInformation {
4910                name: "TWO".into(),
4911                location: lsp::Location {
4912                    uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
4913                    range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4914                },
4915                kind: lsp::SymbolKind::CONSTANT,
4916                tags: None,
4917                container_name: None,
4918                deprecated: None,
4919            },
4920        ])))
4921    });
4922
4923    // Request the definition of a symbol as the guest.
4924    let symbols = project_b
4925        .update(cx_b, |p, cx| p.symbols("two", cx))
4926        .await
4927        .unwrap();
4928    assert_eq!(symbols.len(), 1);
4929    assert_eq!(symbols[0].name, "TWO");
4930
4931    // Open one of the returned symbols.
4932    let buffer_b_2 = project_b
4933        .update(cx_b, |project, cx| {
4934            project.open_buffer_for_symbol(&symbols[0], cx)
4935        })
4936        .await
4937        .unwrap();
4938
4939    buffer_b_2.read_with(cx_b, |buffer, cx| {
4940        assert_eq!(
4941            buffer.file().unwrap().full_path(cx),
4942            Path::new("/code/crate-2/two.rs")
4943        );
4944    });
4945
4946    // Attempt to craft a symbol and violate host's privacy by opening an arbitrary file.
4947    let mut fake_symbol = symbols[0].clone();
4948    fake_symbol.path.path = Path::new("/code/secrets").into();
4949    let error = project_b
4950        .update(cx_b, |project, cx| {
4951            project.open_buffer_for_symbol(&fake_symbol, cx)
4952        })
4953        .await
4954        .unwrap_err();
4955    assert!(error.to_string().contains("invalid symbol signature"));
4956}
4957
4958#[gpui::test(iterations = 10)]
4959async fn test_open_buffer_while_getting_definition_pointing_to_it(
4960    executor: BackgroundExecutor,
4961    cx_a: &mut TestAppContext,
4962    cx_b: &mut TestAppContext,
4963    mut rng: StdRng,
4964) {
4965    let mut server = TestServer::start(executor.clone()).await;
4966    let client_a = server.create_client(cx_a, "user_a").await;
4967    let client_b = server.create_client(cx_b, "user_b").await;
4968    server
4969        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4970        .await;
4971    let active_call_a = cx_a.read(ActiveCall::global);
4972
4973    // Set up a fake language server.
4974    let mut language = Language::new(
4975        LanguageConfig {
4976            name: "Rust".into(),
4977            path_suffixes: vec!["rs".to_string()],
4978            ..Default::default()
4979        },
4980        Some(tree_sitter_rust::language()),
4981    );
4982    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4983    client_a.language_registry().add(Arc::new(language));
4984
4985    client_a
4986        .fs()
4987        .insert_tree(
4988            "/root",
4989            json!({
4990                "a.rs": "const ONE: usize = b::TWO;",
4991                "b.rs": "const TWO: usize = 2",
4992            }),
4993        )
4994        .await;
4995    let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await;
4996    let project_id = active_call_a
4997        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4998        .await
4999        .unwrap();
5000    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5001
5002    let open_buffer_task = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
5003    let buffer_b1 = cx_b.executor().spawn(open_buffer_task).await.unwrap();
5004
5005    let fake_language_server = fake_language_servers.next().await.unwrap();
5006    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
5007        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
5008            lsp::Location::new(
5009                lsp::Url::from_file_path("/root/b.rs").unwrap(),
5010                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5011            ),
5012        )))
5013    });
5014
5015    let definitions;
5016    let buffer_b2;
5017    if rng.gen() {
5018        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5019        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5020    } else {
5021        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5022        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5023    }
5024
5025    let buffer_b2 = buffer_b2.await.unwrap();
5026    let definitions = definitions.await.unwrap();
5027    assert_eq!(definitions.len(), 1);
5028    assert_eq!(definitions[0].target.buffer, buffer_b2);
5029}
5030
5031#[gpui::test(iterations = 10)]
5032async fn test_contacts(
5033    executor: BackgroundExecutor,
5034    cx_a: &mut TestAppContext,
5035    cx_b: &mut TestAppContext,
5036    cx_c: &mut TestAppContext,
5037    cx_d: &mut TestAppContext,
5038) {
5039    let mut server = TestServer::start(executor.clone()).await;
5040    let client_a = server.create_client(cx_a, "user_a").await;
5041    let client_b = server.create_client(cx_b, "user_b").await;
5042    let client_c = server.create_client(cx_c, "user_c").await;
5043    let client_d = server.create_client(cx_d, "user_d").await;
5044    server
5045        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
5046        .await;
5047    let active_call_a = cx_a.read(ActiveCall::global);
5048    let active_call_b = cx_b.read(ActiveCall::global);
5049    let active_call_c = cx_c.read(ActiveCall::global);
5050    let _active_call_d = cx_d.read(ActiveCall::global);
5051
5052    executor.run_until_parked();
5053    assert_eq!(
5054        contacts(&client_a, cx_a),
5055        [
5056            ("user_b".to_string(), "online", "free"),
5057            ("user_c".to_string(), "online", "free")
5058        ]
5059    );
5060    assert_eq!(
5061        contacts(&client_b, cx_b),
5062        [
5063            ("user_a".to_string(), "online", "free"),
5064            ("user_c".to_string(), "online", "free")
5065        ]
5066    );
5067    assert_eq!(
5068        contacts(&client_c, cx_c),
5069        [
5070            ("user_a".to_string(), "online", "free"),
5071            ("user_b".to_string(), "online", "free")
5072        ]
5073    );
5074    assert_eq!(contacts(&client_d, cx_d), []);
5075
5076    server.disconnect_client(client_c.peer_id().unwrap());
5077    server.forbid_connections();
5078    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5079    assert_eq!(
5080        contacts(&client_a, cx_a),
5081        [
5082            ("user_b".to_string(), "online", "free"),
5083            ("user_c".to_string(), "offline", "free")
5084        ]
5085    );
5086    assert_eq!(
5087        contacts(&client_b, cx_b),
5088        [
5089            ("user_a".to_string(), "online", "free"),
5090            ("user_c".to_string(), "offline", "free")
5091        ]
5092    );
5093    assert_eq!(contacts(&client_c, cx_c), []);
5094    assert_eq!(contacts(&client_d, cx_d), []);
5095
5096    server.allow_connections();
5097    client_c
5098        .authenticate_and_connect(false, &cx_c.to_async())
5099        .await
5100        .unwrap();
5101
5102    executor.run_until_parked();
5103    assert_eq!(
5104        contacts(&client_a, cx_a),
5105        [
5106            ("user_b".to_string(), "online", "free"),
5107            ("user_c".to_string(), "online", "free")
5108        ]
5109    );
5110    assert_eq!(
5111        contacts(&client_b, cx_b),
5112        [
5113            ("user_a".to_string(), "online", "free"),
5114            ("user_c".to_string(), "online", "free")
5115        ]
5116    );
5117    assert_eq!(
5118        contacts(&client_c, cx_c),
5119        [
5120            ("user_a".to_string(), "online", "free"),
5121            ("user_b".to_string(), "online", "free")
5122        ]
5123    );
5124    assert_eq!(contacts(&client_d, cx_d), []);
5125
5126    active_call_a
5127        .update(cx_a, |call, cx| {
5128            call.invite(client_b.user_id().unwrap(), None, cx)
5129        })
5130        .await
5131        .unwrap();
5132    executor.run_until_parked();
5133    assert_eq!(
5134        contacts(&client_a, cx_a),
5135        [
5136            ("user_b".to_string(), "online", "busy"),
5137            ("user_c".to_string(), "online", "free")
5138        ]
5139    );
5140    assert_eq!(
5141        contacts(&client_b, cx_b),
5142        [
5143            ("user_a".to_string(), "online", "busy"),
5144            ("user_c".to_string(), "online", "free")
5145        ]
5146    );
5147    assert_eq!(
5148        contacts(&client_c, cx_c),
5149        [
5150            ("user_a".to_string(), "online", "busy"),
5151            ("user_b".to_string(), "online", "busy")
5152        ]
5153    );
5154    assert_eq!(contacts(&client_d, cx_d), []);
5155
5156    // Client B and client D become contacts while client B is being called.
5157    server
5158        .make_contacts(&mut [(&client_b, cx_b), (&client_d, cx_d)])
5159        .await;
5160    executor.run_until_parked();
5161    assert_eq!(
5162        contacts(&client_a, cx_a),
5163        [
5164            ("user_b".to_string(), "online", "busy"),
5165            ("user_c".to_string(), "online", "free")
5166        ]
5167    );
5168    assert_eq!(
5169        contacts(&client_b, cx_b),
5170        [
5171            ("user_a".to_string(), "online", "busy"),
5172            ("user_c".to_string(), "online", "free"),
5173            ("user_d".to_string(), "online", "free"),
5174        ]
5175    );
5176    assert_eq!(
5177        contacts(&client_c, cx_c),
5178        [
5179            ("user_a".to_string(), "online", "busy"),
5180            ("user_b".to_string(), "online", "busy")
5181        ]
5182    );
5183    assert_eq!(
5184        contacts(&client_d, cx_d),
5185        [("user_b".to_string(), "online", "busy")]
5186    );
5187
5188    active_call_b.update(cx_b, |call, cx| call.decline_incoming(cx).unwrap());
5189    executor.run_until_parked();
5190    assert_eq!(
5191        contacts(&client_a, cx_a),
5192        [
5193            ("user_b".to_string(), "online", "free"),
5194            ("user_c".to_string(), "online", "free")
5195        ]
5196    );
5197    assert_eq!(
5198        contacts(&client_b, cx_b),
5199        [
5200            ("user_a".to_string(), "online", "free"),
5201            ("user_c".to_string(), "online", "free"),
5202            ("user_d".to_string(), "online", "free")
5203        ]
5204    );
5205    assert_eq!(
5206        contacts(&client_c, cx_c),
5207        [
5208            ("user_a".to_string(), "online", "free"),
5209            ("user_b".to_string(), "online", "free")
5210        ]
5211    );
5212    assert_eq!(
5213        contacts(&client_d, cx_d),
5214        [("user_b".to_string(), "online", "free")]
5215    );
5216
5217    active_call_c
5218        .update(cx_c, |call, cx| {
5219            call.invite(client_a.user_id().unwrap(), None, cx)
5220        })
5221        .await
5222        .unwrap();
5223    executor.run_until_parked();
5224    assert_eq!(
5225        contacts(&client_a, cx_a),
5226        [
5227            ("user_b".to_string(), "online", "free"),
5228            ("user_c".to_string(), "online", "busy")
5229        ]
5230    );
5231    assert_eq!(
5232        contacts(&client_b, cx_b),
5233        [
5234            ("user_a".to_string(), "online", "busy"),
5235            ("user_c".to_string(), "online", "busy"),
5236            ("user_d".to_string(), "online", "free")
5237        ]
5238    );
5239    assert_eq!(
5240        contacts(&client_c, cx_c),
5241        [
5242            ("user_a".to_string(), "online", "busy"),
5243            ("user_b".to_string(), "online", "free")
5244        ]
5245    );
5246    assert_eq!(
5247        contacts(&client_d, cx_d),
5248        [("user_b".to_string(), "online", "free")]
5249    );
5250
5251    active_call_a
5252        .update(cx_a, |call, cx| call.accept_incoming(cx))
5253        .await
5254        .unwrap();
5255    executor.run_until_parked();
5256    assert_eq!(
5257        contacts(&client_a, cx_a),
5258        [
5259            ("user_b".to_string(), "online", "free"),
5260            ("user_c".to_string(), "online", "busy")
5261        ]
5262    );
5263    assert_eq!(
5264        contacts(&client_b, cx_b),
5265        [
5266            ("user_a".to_string(), "online", "busy"),
5267            ("user_c".to_string(), "online", "busy"),
5268            ("user_d".to_string(), "online", "free")
5269        ]
5270    );
5271    assert_eq!(
5272        contacts(&client_c, cx_c),
5273        [
5274            ("user_a".to_string(), "online", "busy"),
5275            ("user_b".to_string(), "online", "free")
5276        ]
5277    );
5278    assert_eq!(
5279        contacts(&client_d, cx_d),
5280        [("user_b".to_string(), "online", "free")]
5281    );
5282
5283    active_call_a
5284        .update(cx_a, |call, cx| {
5285            call.invite(client_b.user_id().unwrap(), None, cx)
5286        })
5287        .await
5288        .unwrap();
5289    executor.run_until_parked();
5290    assert_eq!(
5291        contacts(&client_a, cx_a),
5292        [
5293            ("user_b".to_string(), "online", "busy"),
5294            ("user_c".to_string(), "online", "busy")
5295        ]
5296    );
5297    assert_eq!(
5298        contacts(&client_b, cx_b),
5299        [
5300            ("user_a".to_string(), "online", "busy"),
5301            ("user_c".to_string(), "online", "busy"),
5302            ("user_d".to_string(), "online", "free")
5303        ]
5304    );
5305    assert_eq!(
5306        contacts(&client_c, cx_c),
5307        [
5308            ("user_a".to_string(), "online", "busy"),
5309            ("user_b".to_string(), "online", "busy")
5310        ]
5311    );
5312    assert_eq!(
5313        contacts(&client_d, cx_d),
5314        [("user_b".to_string(), "online", "busy")]
5315    );
5316
5317    active_call_a
5318        .update(cx_a, |call, cx| call.hang_up(cx))
5319        .await
5320        .unwrap();
5321    executor.run_until_parked();
5322    assert_eq!(
5323        contacts(&client_a, cx_a),
5324        [
5325            ("user_b".to_string(), "online", "free"),
5326            ("user_c".to_string(), "online", "free")
5327        ]
5328    );
5329    assert_eq!(
5330        contacts(&client_b, cx_b),
5331        [
5332            ("user_a".to_string(), "online", "free"),
5333            ("user_c".to_string(), "online", "free"),
5334            ("user_d".to_string(), "online", "free")
5335        ]
5336    );
5337    assert_eq!(
5338        contacts(&client_c, cx_c),
5339        [
5340            ("user_a".to_string(), "online", "free"),
5341            ("user_b".to_string(), "online", "free")
5342        ]
5343    );
5344    assert_eq!(
5345        contacts(&client_d, cx_d),
5346        [("user_b".to_string(), "online", "free")]
5347    );
5348
5349    active_call_a
5350        .update(cx_a, |call, cx| {
5351            call.invite(client_b.user_id().unwrap(), None, cx)
5352        })
5353        .await
5354        .unwrap();
5355    executor.run_until_parked();
5356    assert_eq!(
5357        contacts(&client_a, cx_a),
5358        [
5359            ("user_b".to_string(), "online", "busy"),
5360            ("user_c".to_string(), "online", "free")
5361        ]
5362    );
5363    assert_eq!(
5364        contacts(&client_b, cx_b),
5365        [
5366            ("user_a".to_string(), "online", "busy"),
5367            ("user_c".to_string(), "online", "free"),
5368            ("user_d".to_string(), "online", "free")
5369        ]
5370    );
5371    assert_eq!(
5372        contacts(&client_c, cx_c),
5373        [
5374            ("user_a".to_string(), "online", "busy"),
5375            ("user_b".to_string(), "online", "busy")
5376        ]
5377    );
5378    assert_eq!(
5379        contacts(&client_d, cx_d),
5380        [("user_b".to_string(), "online", "busy")]
5381    );
5382
5383    server.forbid_connections();
5384    server.disconnect_client(client_a.peer_id().unwrap());
5385    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5386    assert_eq!(contacts(&client_a, cx_a), []);
5387    assert_eq!(
5388        contacts(&client_b, cx_b),
5389        [
5390            ("user_a".to_string(), "offline", "free"),
5391            ("user_c".to_string(), "online", "free"),
5392            ("user_d".to_string(), "online", "free")
5393        ]
5394    );
5395    assert_eq!(
5396        contacts(&client_c, cx_c),
5397        [
5398            ("user_a".to_string(), "offline", "free"),
5399            ("user_b".to_string(), "online", "free")
5400        ]
5401    );
5402    assert_eq!(
5403        contacts(&client_d, cx_d),
5404        [("user_b".to_string(), "online", "free")]
5405    );
5406
5407    // Test removing a contact
5408    client_b
5409        .user_store()
5410        .update(cx_b, |store, cx| {
5411            store.remove_contact(client_c.user_id().unwrap(), cx)
5412        })
5413        .await
5414        .unwrap();
5415    executor.run_until_parked();
5416    assert_eq!(
5417        contacts(&client_b, cx_b),
5418        [
5419            ("user_a".to_string(), "offline", "free"),
5420            ("user_d".to_string(), "online", "free")
5421        ]
5422    );
5423    assert_eq!(
5424        contacts(&client_c, cx_c),
5425        [("user_a".to_string(), "offline", "free"),]
5426    );
5427
5428    fn contacts(
5429        client: &TestClient,
5430        cx: &TestAppContext,
5431    ) -> Vec<(String, &'static str, &'static str)> {
5432        client.user_store().read_with(cx, |store, _| {
5433            store
5434                .contacts()
5435                .iter()
5436                .map(|contact| {
5437                    (
5438                        contact.user.github_login.clone(),
5439                        if contact.online { "online" } else { "offline" },
5440                        if contact.busy { "busy" } else { "free" },
5441                    )
5442                })
5443                .collect()
5444        })
5445    }
5446}
5447
5448#[gpui::test(iterations = 10)]
5449async fn test_contact_requests(
5450    executor: BackgroundExecutor,
5451    cx_a: &mut TestAppContext,
5452    cx_a2: &mut TestAppContext,
5453    cx_b: &mut TestAppContext,
5454    cx_b2: &mut TestAppContext,
5455    cx_c: &mut TestAppContext,
5456    cx_c2: &mut TestAppContext,
5457) {
5458    // Connect to a server as 3 clients.
5459    let mut server = TestServer::start(executor.clone()).await;
5460    let client_a = server.create_client(cx_a, "user_a").await;
5461    let client_a2 = server.create_client(cx_a2, "user_a").await;
5462    let client_b = server.create_client(cx_b, "user_b").await;
5463    let client_b2 = server.create_client(cx_b2, "user_b").await;
5464    let client_c = server.create_client(cx_c, "user_c").await;
5465    let client_c2 = server.create_client(cx_c2, "user_c").await;
5466
5467    assert_eq!(client_a.user_id().unwrap(), client_a2.user_id().unwrap());
5468    assert_eq!(client_b.user_id().unwrap(), client_b2.user_id().unwrap());
5469    assert_eq!(client_c.user_id().unwrap(), client_c2.user_id().unwrap());
5470
5471    // User A and User C request that user B become their contact.
5472    client_a
5473        .user_store()
5474        .update(cx_a, |store, cx| {
5475            store.request_contact(client_b.user_id().unwrap(), cx)
5476        })
5477        .await
5478        .unwrap();
5479    client_c
5480        .user_store()
5481        .update(cx_c, |store, cx| {
5482            store.request_contact(client_b.user_id().unwrap(), cx)
5483        })
5484        .await
5485        .unwrap();
5486    executor.run_until_parked();
5487
5488    // All users see the pending request appear in all their clients.
5489    assert_eq!(
5490        client_a.summarize_contacts(cx_a).outgoing_requests,
5491        &["user_b"]
5492    );
5493    assert_eq!(
5494        client_a2.summarize_contacts(cx_a2).outgoing_requests,
5495        &["user_b"]
5496    );
5497    assert_eq!(
5498        client_b.summarize_contacts(cx_b).incoming_requests,
5499        &["user_a", "user_c"]
5500    );
5501    assert_eq!(
5502        client_b2.summarize_contacts(cx_b2).incoming_requests,
5503        &["user_a", "user_c"]
5504    );
5505    assert_eq!(
5506        client_c.summarize_contacts(cx_c).outgoing_requests,
5507        &["user_b"]
5508    );
5509    assert_eq!(
5510        client_c2.summarize_contacts(cx_c2).outgoing_requests,
5511        &["user_b"]
5512    );
5513
5514    // Contact requests are present upon connecting (tested here via disconnect/reconnect)
5515    disconnect_and_reconnect(&client_a, cx_a).await;
5516    disconnect_and_reconnect(&client_b, cx_b).await;
5517    disconnect_and_reconnect(&client_c, cx_c).await;
5518    executor.run_until_parked();
5519    assert_eq!(
5520        client_a.summarize_contacts(cx_a).outgoing_requests,
5521        &["user_b"]
5522    );
5523    assert_eq!(
5524        client_b.summarize_contacts(cx_b).incoming_requests,
5525        &["user_a", "user_c"]
5526    );
5527    assert_eq!(
5528        client_c.summarize_contacts(cx_c).outgoing_requests,
5529        &["user_b"]
5530    );
5531
5532    // User B accepts the request from user A.
5533    client_b
5534        .user_store()
5535        .update(cx_b, |store, cx| {
5536            store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
5537        })
5538        .await
5539        .unwrap();
5540
5541    executor.run_until_parked();
5542
5543    // User B sees user A as their contact now in all client, and the incoming request from them is removed.
5544    let contacts_b = client_b.summarize_contacts(cx_b);
5545    assert_eq!(contacts_b.current, &["user_a"]);
5546    assert_eq!(contacts_b.incoming_requests, &["user_c"]);
5547    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
5548    assert_eq!(contacts_b2.current, &["user_a"]);
5549    assert_eq!(contacts_b2.incoming_requests, &["user_c"]);
5550
5551    // User A sees user B as their contact now in all clients, and the outgoing request to them is removed.
5552    let contacts_a = client_a.summarize_contacts(cx_a);
5553    assert_eq!(contacts_a.current, &["user_b"]);
5554    assert!(contacts_a.outgoing_requests.is_empty());
5555    let contacts_a2 = client_a2.summarize_contacts(cx_a2);
5556    assert_eq!(contacts_a2.current, &["user_b"]);
5557    assert!(contacts_a2.outgoing_requests.is_empty());
5558
5559    // Contacts are present upon connecting (tested here via disconnect/reconnect)
5560    disconnect_and_reconnect(&client_a, cx_a).await;
5561    disconnect_and_reconnect(&client_b, cx_b).await;
5562    disconnect_and_reconnect(&client_c, cx_c).await;
5563    executor.run_until_parked();
5564    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
5565    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
5566    assert_eq!(
5567        client_b.summarize_contacts(cx_b).incoming_requests,
5568        &["user_c"]
5569    );
5570    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
5571    assert_eq!(
5572        client_c.summarize_contacts(cx_c).outgoing_requests,
5573        &["user_b"]
5574    );
5575
5576    // User B rejects the request from user C.
5577    client_b
5578        .user_store()
5579        .update(cx_b, |store, cx| {
5580            store.respond_to_contact_request(client_c.user_id().unwrap(), false, cx)
5581        })
5582        .await
5583        .unwrap();
5584
5585    executor.run_until_parked();
5586
5587    // User B doesn't see user C as their contact, and the incoming request from them is removed.
5588    let contacts_b = client_b.summarize_contacts(cx_b);
5589    assert_eq!(contacts_b.current, &["user_a"]);
5590    assert!(contacts_b.incoming_requests.is_empty());
5591    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
5592    assert_eq!(contacts_b2.current, &["user_a"]);
5593    assert!(contacts_b2.incoming_requests.is_empty());
5594
5595    // User C doesn't see user B as their contact, and the outgoing request to them is removed.
5596    let contacts_c = client_c.summarize_contacts(cx_c);
5597    assert!(contacts_c.current.is_empty());
5598    assert!(contacts_c.outgoing_requests.is_empty());
5599    let contacts_c2 = client_c2.summarize_contacts(cx_c2);
5600    assert!(contacts_c2.current.is_empty());
5601    assert!(contacts_c2.outgoing_requests.is_empty());
5602
5603    // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect)
5604    disconnect_and_reconnect(&client_a, cx_a).await;
5605    disconnect_and_reconnect(&client_b, cx_b).await;
5606    disconnect_and_reconnect(&client_c, cx_c).await;
5607    executor.run_until_parked();
5608    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
5609    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
5610    assert!(client_b
5611        .summarize_contacts(cx_b)
5612        .incoming_requests
5613        .is_empty());
5614    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
5615    assert!(client_c
5616        .summarize_contacts(cx_c)
5617        .outgoing_requests
5618        .is_empty());
5619
5620    async fn disconnect_and_reconnect(client: &TestClient, cx: &mut TestAppContext) {
5621        client.disconnect(&cx.to_async());
5622        client.clear_contacts(cx).await;
5623        client
5624            .authenticate_and_connect(false, &cx.to_async())
5625            .await
5626            .unwrap();
5627    }
5628}
5629
5630#[gpui::test(iterations = 10)]
5631async fn test_join_call_after_screen_was_shared(
5632    executor: BackgroundExecutor,
5633    cx_a: &mut TestAppContext,
5634    cx_b: &mut TestAppContext,
5635) {
5636    let mut server = TestServer::start(executor.clone()).await;
5637
5638    let client_a = server.create_client(cx_a, "user_a").await;
5639    let client_b = server.create_client(cx_b, "user_b").await;
5640    server
5641        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5642        .await;
5643
5644    let active_call_a = cx_a.read(ActiveCall::global);
5645    let active_call_b = cx_b.read(ActiveCall::global);
5646
5647    // Call users B and C from client A.
5648    active_call_a
5649        .update(cx_a, |call, cx| {
5650            call.invite(client_b.user_id().unwrap(), None, cx)
5651        })
5652        .await
5653        .unwrap();
5654
5655    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
5656    executor.run_until_parked();
5657    assert_eq!(
5658        room_participants(&room_a, cx_a),
5659        RoomParticipants {
5660            remote: Default::default(),
5661            pending: vec!["user_b".to_string()]
5662        }
5663    );
5664
5665    // User B receives the call.
5666
5667    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
5668    let call_b = incoming_call_b.next().await.unwrap().unwrap();
5669    assert_eq!(call_b.calling_user.github_login, "user_a");
5670
5671    // User A shares their screen
5672    let display = MacOSDisplay::new();
5673    active_call_a
5674        .update(cx_a, |call, cx| {
5675            call.room().unwrap().update(cx, |room, cx| {
5676                room.set_display_sources(vec![display.clone()]);
5677                room.share_screen(cx)
5678            })
5679        })
5680        .await
5681        .unwrap();
5682
5683    client_b.user_store().update(cx_b, |user_store, _| {
5684        user_store.clear_cache();
5685    });
5686
5687    // User B joins the room
5688    active_call_b
5689        .update(cx_b, |call, cx| call.accept_incoming(cx))
5690        .await
5691        .unwrap();
5692
5693    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
5694    assert!(incoming_call_b.next().await.unwrap().is_none());
5695
5696    executor.run_until_parked();
5697    assert_eq!(
5698        room_participants(&room_a, cx_a),
5699        RoomParticipants {
5700            remote: vec!["user_b".to_string()],
5701            pending: vec![],
5702        }
5703    );
5704    assert_eq!(
5705        room_participants(&room_b, cx_b),
5706        RoomParticipants {
5707            remote: vec!["user_a".to_string()],
5708            pending: vec![],
5709        }
5710    );
5711
5712    // Ensure User B sees User A's screenshare.
5713
5714    room_b.read_with(cx_b, |room, _| {
5715        assert_eq!(
5716            room.remote_participants()
5717                .get(&client_a.user_id().unwrap())
5718                .unwrap()
5719                .video_tracks
5720                .len(),
5721            1
5722        );
5723    });
5724}