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