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(false, 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(false, 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(false, 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(false, 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(false, 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!(
3848            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
3849            []
3850        )
3851    });
3852
3853    project_b.read_with(cx_b, |project, cx| {
3854        assert_eq!(
3855            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
3856            []
3857        )
3858    });
3859
3860    project_c.read_with(cx_c, |project, cx| {
3861        assert_eq!(
3862            project.diagnostic_summaries(false, cx).collect::<Vec<_>>(),
3863            []
3864        )
3865    });
3866}
3867
3868#[gpui::test(iterations = 10)]
3869async fn test_collaborating_with_lsp_progress_updates_and_diagnostics_ordering(
3870    executor: BackgroundExecutor,
3871    cx_a: &mut TestAppContext,
3872    cx_b: &mut TestAppContext,
3873) {
3874    let mut server = TestServer::start(executor.clone()).await;
3875    let client_a = server.create_client(cx_a, "user_a").await;
3876    let client_b = server.create_client(cx_b, "user_b").await;
3877    server
3878        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3879        .await;
3880
3881    // Set up a fake language server.
3882    let mut language = Language::new(
3883        LanguageConfig {
3884            name: "Rust".into(),
3885            path_suffixes: vec!["rs".to_string()],
3886            ..Default::default()
3887        },
3888        Some(tree_sitter_rust::language()),
3889    );
3890    let mut fake_language_servers = language
3891        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3892            disk_based_diagnostics_progress_token: Some("the-disk-based-token".into()),
3893            disk_based_diagnostics_sources: vec!["the-disk-based-diagnostics-source".into()],
3894            ..Default::default()
3895        }))
3896        .await;
3897    client_a.language_registry().add(Arc::new(language));
3898
3899    let file_names = &["one.rs", "two.rs", "three.rs", "four.rs", "five.rs"];
3900    client_a
3901        .fs()
3902        .insert_tree(
3903            "/test",
3904            json!({
3905                "one.rs": "const ONE: usize = 1;",
3906                "two.rs": "const TWO: usize = 2;",
3907                "three.rs": "const THREE: usize = 3;",
3908                "four.rs": "const FOUR: usize = 3;",
3909                "five.rs": "const FIVE: usize = 3;",
3910            }),
3911        )
3912        .await;
3913
3914    let (project_a, worktree_id) = client_a.build_local_project("/test", cx_a).await;
3915
3916    // Share a project as client A
3917    let active_call_a = cx_a.read(ActiveCall::global);
3918    let project_id = active_call_a
3919        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3920        .await
3921        .unwrap();
3922
3923    // Join the project as client B and open all three files.
3924    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3925    let guest_buffers = futures::future::try_join_all(file_names.iter().map(|file_name| {
3926        project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, file_name), cx))
3927    }))
3928    .await
3929    .unwrap();
3930
3931    // Simulate a language server reporting errors for a file.
3932    let fake_language_server = fake_language_servers.next().await.unwrap();
3933    fake_language_server
3934        .request::<lsp::request::WorkDoneProgressCreate>(lsp::WorkDoneProgressCreateParams {
3935            token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
3936        })
3937        .await
3938        .unwrap();
3939    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
3940        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
3941        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin(
3942            lsp::WorkDoneProgressBegin {
3943                title: "Progress Began".into(),
3944                ..Default::default()
3945            },
3946        )),
3947    });
3948    for file_name in file_names {
3949        fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3950            lsp::PublishDiagnosticsParams {
3951                uri: lsp::Url::from_file_path(Path::new("/test").join(file_name)).unwrap(),
3952                version: None,
3953                diagnostics: vec![lsp::Diagnostic {
3954                    severity: Some(lsp::DiagnosticSeverity::WARNING),
3955                    source: Some("the-disk-based-diagnostics-source".into()),
3956                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
3957                    message: "message one".to_string(),
3958                    ..Default::default()
3959                }],
3960            },
3961        );
3962    }
3963    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
3964        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
3965        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End(
3966            lsp::WorkDoneProgressEnd { message: None },
3967        )),
3968    });
3969
3970    // When the "disk base diagnostics finished" message is received, the buffers'
3971    // diagnostics are expected to be present.
3972    let disk_based_diagnostics_finished = Arc::new(AtomicBool::new(false));
3973    project_b.update(cx_b, {
3974        let project_b = project_b.clone();
3975        let disk_based_diagnostics_finished = disk_based_diagnostics_finished.clone();
3976        move |_, cx| {
3977            cx.subscribe(&project_b, move |_, _, event, cx| {
3978                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
3979                    disk_based_diagnostics_finished.store(true, SeqCst);
3980                    for buffer in &guest_buffers {
3981                        assert_eq!(
3982                            buffer
3983                                .read(cx)
3984                                .snapshot()
3985                                .diagnostics_in_range::<_, usize>(0..5, false)
3986                                .count(),
3987                            1,
3988                            "expected a diagnostic for buffer {:?}",
3989                            buffer.read(cx).file().unwrap().path(),
3990                        );
3991                    }
3992                }
3993            })
3994            .detach();
3995        }
3996    });
3997
3998    executor.run_until_parked();
3999    assert!(disk_based_diagnostics_finished.load(SeqCst));
4000}
4001
4002#[gpui::test(iterations = 10)]
4003async fn test_reloading_buffer_manually(
4004    executor: BackgroundExecutor,
4005    cx_a: &mut TestAppContext,
4006    cx_b: &mut TestAppContext,
4007) {
4008    let mut server = TestServer::start(executor.clone()).await;
4009    let client_a = server.create_client(cx_a, "user_a").await;
4010    let client_b = server.create_client(cx_b, "user_b").await;
4011    server
4012        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4013        .await;
4014    let active_call_a = cx_a.read(ActiveCall::global);
4015
4016    client_a
4017        .fs()
4018        .insert_tree("/a", json!({ "a.rs": "let one = 1;" }))
4019        .await;
4020    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
4021    let buffer_a = project_a
4022        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
4023        .await
4024        .unwrap();
4025    let project_id = active_call_a
4026        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4027        .await
4028        .unwrap();
4029
4030    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4031
4032    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4033    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4034    buffer_b.update(cx_b, |buffer, cx| {
4035        buffer.edit([(4..7, "six")], None, cx);
4036        buffer.edit([(10..11, "6")], None, cx);
4037        assert_eq!(buffer.text(), "let six = 6;");
4038        assert!(buffer.is_dirty());
4039        assert!(!buffer.has_conflict());
4040    });
4041    executor.run_until_parked();
4042
4043    buffer_a.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "let six = 6;"));
4044
4045    client_a
4046        .fs()
4047        .save(
4048            "/a/a.rs".as_ref(),
4049            &Rope::from("let seven = 7;"),
4050            LineEnding::Unix,
4051        )
4052        .await
4053        .unwrap();
4054    executor.run_until_parked();
4055
4056    buffer_a.read_with(cx_a, |buffer, _| assert!(buffer.has_conflict()));
4057
4058    buffer_b.read_with(cx_b, |buffer, _| assert!(buffer.has_conflict()));
4059
4060    project_b
4061        .update(cx_b, |project, cx| {
4062            project.reload_buffers(HashSet::from_iter([buffer_b.clone()]), true, cx)
4063        })
4064        .await
4065        .unwrap();
4066
4067    buffer_a.read_with(cx_a, |buffer, _| {
4068        assert_eq!(buffer.text(), "let seven = 7;");
4069        assert!(!buffer.is_dirty());
4070        assert!(!buffer.has_conflict());
4071    });
4072
4073    buffer_b.read_with(cx_b, |buffer, _| {
4074        assert_eq!(buffer.text(), "let seven = 7;");
4075        assert!(!buffer.is_dirty());
4076        assert!(!buffer.has_conflict());
4077    });
4078
4079    buffer_a.update(cx_a, |buffer, cx| {
4080        // Undoing on the host is a no-op when the reload was initiated by the guest.
4081        buffer.undo(cx);
4082        assert_eq!(buffer.text(), "let seven = 7;");
4083        assert!(!buffer.is_dirty());
4084        assert!(!buffer.has_conflict());
4085    });
4086    buffer_b.update(cx_b, |buffer, cx| {
4087        // Undoing on the guest rolls back the buffer to before it was reloaded but the conflict gets cleared.
4088        buffer.undo(cx);
4089        assert_eq!(buffer.text(), "let six = 6;");
4090        assert!(buffer.is_dirty());
4091        assert!(!buffer.has_conflict());
4092    });
4093}
4094
4095#[gpui::test(iterations = 10)]
4096async fn test_formatting_buffer(
4097    executor: BackgroundExecutor,
4098    cx_a: &mut TestAppContext,
4099    cx_b: &mut TestAppContext,
4100) {
4101    executor.allow_parking();
4102    let mut server = TestServer::start(executor.clone()).await;
4103    let client_a = server.create_client(cx_a, "user_a").await;
4104    let client_b = server.create_client(cx_b, "user_b").await;
4105    server
4106        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4107        .await;
4108    let active_call_a = cx_a.read(ActiveCall::global);
4109
4110    // Set up a fake language server.
4111    let mut language = Language::new(
4112        LanguageConfig {
4113            name: "Rust".into(),
4114            path_suffixes: vec!["rs".to_string()],
4115            ..Default::default()
4116        },
4117        Some(tree_sitter_rust::language()),
4118    );
4119    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4120    client_a.language_registry().add(Arc::new(language));
4121
4122    // Here we insert a fake tree with a directory that exists on disk. This is needed
4123    // because later we'll invoke a command, which requires passing a working directory
4124    // that points to a valid location on disk.
4125    let directory = env::current_dir().unwrap();
4126    client_a
4127        .fs()
4128        .insert_tree(&directory, json!({ "a.rs": "let one = \"two\"" }))
4129        .await;
4130    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
4131    let project_id = active_call_a
4132        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4133        .await
4134        .unwrap();
4135    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4136
4137    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4138    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4139
4140    let fake_language_server = fake_language_servers.next().await.unwrap();
4141    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
4142        Ok(Some(vec![
4143            lsp::TextEdit {
4144                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 4)),
4145                new_text: "h".to_string(),
4146            },
4147            lsp::TextEdit {
4148                range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 7)),
4149                new_text: "y".to_string(),
4150            },
4151        ]))
4152    });
4153
4154    project_b
4155        .update(cx_b, |project, cx| {
4156            project.format(
4157                HashSet::from_iter([buffer_b.clone()]),
4158                true,
4159                FormatTrigger::Save,
4160                cx,
4161            )
4162        })
4163        .await
4164        .unwrap();
4165
4166    // The edits from the LSP are applied, and a final newline is added.
4167    assert_eq!(
4168        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4169        "let honey = \"two\"\n"
4170    );
4171
4172    // Ensure buffer can be formatted using an external command. Notice how the
4173    // host's configuration is honored as opposed to using the guest's settings.
4174    cx_a.update(|cx| {
4175        cx.update_global(|store: &mut SettingsStore, cx| {
4176            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4177                file.defaults.formatter = Some(Formatter::External {
4178                    command: "awk".into(),
4179                    arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()].into(),
4180                });
4181            });
4182        });
4183    });
4184    project_b
4185        .update(cx_b, |project, cx| {
4186            project.format(
4187                HashSet::from_iter([buffer_b.clone()]),
4188                true,
4189                FormatTrigger::Save,
4190                cx,
4191            )
4192        })
4193        .await
4194        .unwrap();
4195    assert_eq!(
4196        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4197        format!("let honey = \"{}/a.rs\"\n", directory.to_str().unwrap())
4198    );
4199}
4200
4201#[gpui::test(iterations = 10)]
4202async fn test_prettier_formatting_buffer(
4203    executor: BackgroundExecutor,
4204    cx_a: &mut TestAppContext,
4205    cx_b: &mut TestAppContext,
4206) {
4207    let mut server = TestServer::start(executor.clone()).await;
4208    let client_a = server.create_client(cx_a, "user_a").await;
4209    let client_b = server.create_client(cx_b, "user_b").await;
4210    server
4211        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4212        .await;
4213    let active_call_a = cx_a.read(ActiveCall::global);
4214
4215    // Set up a fake language server.
4216    let mut language = Language::new(
4217        LanguageConfig {
4218            name: "Rust".into(),
4219            path_suffixes: vec!["rs".to_string()],
4220            prettier_parser_name: Some("test_parser".to_string()),
4221            ..Default::default()
4222        },
4223        Some(tree_sitter_rust::language()),
4224    );
4225    let test_plugin = "test_plugin";
4226    let mut fake_language_servers = language
4227        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4228            prettier_plugins: vec![test_plugin],
4229            ..Default::default()
4230        }))
4231        .await;
4232    let language = Arc::new(language);
4233    client_a.language_registry().add(Arc::clone(&language));
4234
4235    // Here we insert a fake tree with a directory that exists on disk. This is needed
4236    // because later we'll invoke a command, which requires passing a working directory
4237    // that points to a valid location on disk.
4238    let directory = env::current_dir().unwrap();
4239    let buffer_text = "let one = \"two\"";
4240    client_a
4241        .fs()
4242        .insert_tree(&directory, json!({ "a.rs": buffer_text }))
4243        .await;
4244    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
4245    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
4246    let open_buffer = project_a.update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4247    let buffer_a = cx_a.executor().spawn(open_buffer).await.unwrap();
4248
4249    let project_id = active_call_a
4250        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4251        .await
4252        .unwrap();
4253    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4254    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4255    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4256
4257    cx_a.update(|cx| {
4258        cx.update_global(|store: &mut SettingsStore, cx| {
4259            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4260                file.defaults.formatter = Some(Formatter::Auto);
4261            });
4262        });
4263    });
4264    cx_b.update(|cx| {
4265        cx.update_global(|store: &mut SettingsStore, cx| {
4266            store.update_user_settings::<AllLanguageSettings>(cx, |file| {
4267                file.defaults.formatter = Some(Formatter::LanguageServer);
4268            });
4269        });
4270    });
4271    let fake_language_server = fake_language_servers.next().await.unwrap();
4272    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
4273        panic!(
4274            "Unexpected: prettier should be preferred since it's enabled and language supports it"
4275        )
4276    });
4277
4278    project_b
4279        .update(cx_b, |project, cx| {
4280            project.format(
4281                HashSet::from_iter([buffer_b.clone()]),
4282                true,
4283                FormatTrigger::Save,
4284                cx,
4285            )
4286        })
4287        .await
4288        .unwrap();
4289
4290    executor.run_until_parked();
4291    assert_eq!(
4292        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4293        buffer_text.to_string() + "\n" + prettier_format_suffix,
4294        "Prettier formatting was not applied to client buffer after client's request"
4295    );
4296
4297    project_a
4298        .update(cx_a, |project, cx| {
4299            project.format(
4300                HashSet::from_iter([buffer_a.clone()]),
4301                true,
4302                FormatTrigger::Manual,
4303                cx,
4304            )
4305        })
4306        .await
4307        .unwrap();
4308
4309    executor.run_until_parked();
4310    assert_eq!(
4311        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4312        buffer_text.to_string() + "\n" + prettier_format_suffix + "\n" + prettier_format_suffix,
4313        "Prettier formatting was not applied to client buffer after host's request"
4314    );
4315}
4316
4317#[gpui::test(iterations = 10)]
4318async fn test_definition(
4319    executor: BackgroundExecutor,
4320    cx_a: &mut TestAppContext,
4321    cx_b: &mut TestAppContext,
4322) {
4323    let mut server = TestServer::start(executor.clone()).await;
4324    let client_a = server.create_client(cx_a, "user_a").await;
4325    let client_b = server.create_client(cx_b, "user_b").await;
4326    server
4327        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4328        .await;
4329    let active_call_a = cx_a.read(ActiveCall::global);
4330
4331    // Set up a fake language server.
4332    let mut language = Language::new(
4333        LanguageConfig {
4334            name: "Rust".into(),
4335            path_suffixes: vec!["rs".to_string()],
4336            ..Default::default()
4337        },
4338        Some(tree_sitter_rust::language()),
4339    );
4340    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4341    client_a.language_registry().add(Arc::new(language));
4342
4343    client_a
4344        .fs()
4345        .insert_tree(
4346            "/root",
4347            json!({
4348                "dir-1": {
4349                    "a.rs": "const ONE: usize = b::TWO + b::THREE;",
4350                },
4351                "dir-2": {
4352                    "b.rs": "const TWO: c::T2 = 2;\nconst THREE: usize = 3;",
4353                    "c.rs": "type T2 = usize;",
4354                }
4355            }),
4356        )
4357        .await;
4358    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4359    let project_id = active_call_a
4360        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4361        .await
4362        .unwrap();
4363    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4364
4365    // Open the file on client B.
4366    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
4367    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4368
4369    // Request the definition of a symbol as the guest.
4370    let fake_language_server = fake_language_servers.next().await.unwrap();
4371    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4372        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4373            lsp::Location::new(
4374                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4375                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4376            ),
4377        )))
4378    });
4379
4380    let definitions_1 = project_b
4381        .update(cx_b, |p, cx| p.definition(&buffer_b, 23, cx))
4382        .await
4383        .unwrap();
4384    cx_b.read(|cx| {
4385        assert_eq!(definitions_1.len(), 1);
4386        assert_eq!(project_b.read(cx).worktrees().count(), 2);
4387        let target_buffer = definitions_1[0].target.buffer.read(cx);
4388        assert_eq!(
4389            target_buffer.text(),
4390            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4391        );
4392        assert_eq!(
4393            definitions_1[0].target.range.to_point(target_buffer),
4394            Point::new(0, 6)..Point::new(0, 9)
4395        );
4396    });
4397
4398    // Try getting more definitions for the same buffer, ensuring the buffer gets reused from
4399    // the previous call to `definition`.
4400    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4401        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4402            lsp::Location::new(
4403                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4404                lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)),
4405            ),
4406        )))
4407    });
4408
4409    let definitions_2 = project_b
4410        .update(cx_b, |p, cx| p.definition(&buffer_b, 33, cx))
4411        .await
4412        .unwrap();
4413    cx_b.read(|cx| {
4414        assert_eq!(definitions_2.len(), 1);
4415        assert_eq!(project_b.read(cx).worktrees().count(), 2);
4416        let target_buffer = definitions_2[0].target.buffer.read(cx);
4417        assert_eq!(
4418            target_buffer.text(),
4419            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4420        );
4421        assert_eq!(
4422            definitions_2[0].target.range.to_point(target_buffer),
4423            Point::new(1, 6)..Point::new(1, 11)
4424        );
4425    });
4426    assert_eq!(
4427        definitions_1[0].target.buffer,
4428        definitions_2[0].target.buffer
4429    );
4430
4431    fake_language_server.handle_request::<lsp::request::GotoTypeDefinition, _, _>(
4432        |req, _| async move {
4433            assert_eq!(
4434                req.text_document_position_params.position,
4435                lsp::Position::new(0, 7)
4436            );
4437            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4438                lsp::Location::new(
4439                    lsp::Url::from_file_path("/root/dir-2/c.rs").unwrap(),
4440                    lsp::Range::new(lsp::Position::new(0, 5), lsp::Position::new(0, 7)),
4441                ),
4442            )))
4443        },
4444    );
4445
4446    let type_definitions = project_b
4447        .update(cx_b, |p, cx| p.type_definition(&buffer_b, 7, cx))
4448        .await
4449        .unwrap();
4450    cx_b.read(|cx| {
4451        assert_eq!(type_definitions.len(), 1);
4452        let target_buffer = type_definitions[0].target.buffer.read(cx);
4453        assert_eq!(target_buffer.text(), "type T2 = usize;");
4454        assert_eq!(
4455            type_definitions[0].target.range.to_point(target_buffer),
4456            Point::new(0, 5)..Point::new(0, 7)
4457        );
4458    });
4459}
4460
4461#[gpui::test(iterations = 10)]
4462async fn test_references(
4463    executor: BackgroundExecutor,
4464    cx_a: &mut TestAppContext,
4465    cx_b: &mut TestAppContext,
4466) {
4467    let mut server = TestServer::start(executor.clone()).await;
4468    let client_a = server.create_client(cx_a, "user_a").await;
4469    let client_b = server.create_client(cx_b, "user_b").await;
4470    server
4471        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4472        .await;
4473    let active_call_a = cx_a.read(ActiveCall::global);
4474
4475    // Set up a fake language server.
4476    let mut language = Language::new(
4477        LanguageConfig {
4478            name: "Rust".into(),
4479            path_suffixes: vec!["rs".to_string()],
4480            ..Default::default()
4481        },
4482        Some(tree_sitter_rust::language()),
4483    );
4484    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4485    client_a.language_registry().add(Arc::new(language));
4486
4487    client_a
4488        .fs()
4489        .insert_tree(
4490            "/root",
4491            json!({
4492                "dir-1": {
4493                    "one.rs": "const ONE: usize = 1;",
4494                    "two.rs": "const TWO: usize = one::ONE + one::ONE;",
4495                },
4496                "dir-2": {
4497                    "three.rs": "const THREE: usize = two::TWO + one::ONE;",
4498                }
4499            }),
4500        )
4501        .await;
4502    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4503    let project_id = active_call_a
4504        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4505        .await
4506        .unwrap();
4507    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4508
4509    // Open the file on client B.
4510    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx));
4511    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4512
4513    // Request references to a symbol as the guest.
4514    let fake_language_server = fake_language_servers.next().await.unwrap();
4515    fake_language_server.handle_request::<lsp::request::References, _, _>(|params, _| async move {
4516        assert_eq!(
4517            params.text_document_position.text_document.uri.as_str(),
4518            "file:///root/dir-1/one.rs"
4519        );
4520        Ok(Some(vec![
4521            lsp::Location {
4522                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4523                range: lsp::Range::new(lsp::Position::new(0, 24), lsp::Position::new(0, 27)),
4524            },
4525            lsp::Location {
4526                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4527                range: lsp::Range::new(lsp::Position::new(0, 35), lsp::Position::new(0, 38)),
4528            },
4529            lsp::Location {
4530                uri: lsp::Url::from_file_path("/root/dir-2/three.rs").unwrap(),
4531                range: lsp::Range::new(lsp::Position::new(0, 37), lsp::Position::new(0, 40)),
4532            },
4533        ]))
4534    });
4535
4536    let references = project_b
4537        .update(cx_b, |p, cx| p.references(&buffer_b, 7, cx))
4538        .await
4539        .unwrap();
4540    cx_b.read(|cx| {
4541        assert_eq!(references.len(), 3);
4542        assert_eq!(project_b.read(cx).worktrees().count(), 2);
4543
4544        let two_buffer = references[0].buffer.read(cx);
4545        let three_buffer = references[2].buffer.read(cx);
4546        assert_eq!(
4547            two_buffer.file().unwrap().path().as_ref(),
4548            Path::new("two.rs")
4549        );
4550        assert_eq!(references[1].buffer, references[0].buffer);
4551        assert_eq!(
4552            three_buffer.file().unwrap().full_path(cx),
4553            Path::new("/root/dir-2/three.rs")
4554        );
4555
4556        assert_eq!(references[0].range.to_offset(two_buffer), 24..27);
4557        assert_eq!(references[1].range.to_offset(two_buffer), 35..38);
4558        assert_eq!(references[2].range.to_offset(three_buffer), 37..40);
4559    });
4560}
4561
4562#[gpui::test(iterations = 10)]
4563async fn test_project_search(
4564    executor: BackgroundExecutor,
4565    cx_a: &mut TestAppContext,
4566    cx_b: &mut TestAppContext,
4567) {
4568    let mut server = TestServer::start(executor.clone()).await;
4569    let client_a = server.create_client(cx_a, "user_a").await;
4570    let client_b = server.create_client(cx_b, "user_b").await;
4571    server
4572        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4573        .await;
4574    let active_call_a = cx_a.read(ActiveCall::global);
4575
4576    client_a
4577        .fs()
4578        .insert_tree(
4579            "/root",
4580            json!({
4581                "dir-1": {
4582                    "a": "hello world",
4583                    "b": "goodnight moon",
4584                    "c": "a world of goo",
4585                    "d": "world champion of clown world",
4586                },
4587                "dir-2": {
4588                    "e": "disney world is fun",
4589                }
4590            }),
4591        )
4592        .await;
4593    let (project_a, _) = client_a.build_local_project("/root/dir-1", cx_a).await;
4594    let (worktree_2, _) = project_a
4595        .update(cx_a, |p, cx| {
4596            p.find_or_create_local_worktree("/root/dir-2", true, cx)
4597        })
4598        .await
4599        .unwrap();
4600    worktree_2
4601        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
4602        .await;
4603    let project_id = active_call_a
4604        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4605        .await
4606        .unwrap();
4607
4608    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4609
4610    // Perform a search as the guest.
4611    let mut results = HashMap::default();
4612    let mut search_rx = project_b.update(cx_b, |project, cx| {
4613        project.search(
4614            SearchQuery::text("world", false, false, false, Vec::new(), Vec::new()).unwrap(),
4615            cx,
4616        )
4617    });
4618    while let Some((buffer, ranges)) = search_rx.next().await {
4619        results.entry(buffer).or_insert(ranges);
4620    }
4621
4622    let mut ranges_by_path = results
4623        .into_iter()
4624        .map(|(buffer, ranges)| {
4625            buffer.read_with(cx_b, |buffer, cx| {
4626                let path = buffer.file().unwrap().full_path(cx);
4627                let offset_ranges = ranges
4628                    .into_iter()
4629                    .map(|range| range.to_offset(buffer))
4630                    .collect::<Vec<_>>();
4631                (path, offset_ranges)
4632            })
4633        })
4634        .collect::<Vec<_>>();
4635    ranges_by_path.sort_by_key(|(path, _)| path.clone());
4636
4637    assert_eq!(
4638        ranges_by_path,
4639        &[
4640            (PathBuf::from("dir-1/a"), vec![6..11]),
4641            (PathBuf::from("dir-1/c"), vec![2..7]),
4642            (PathBuf::from("dir-1/d"), vec![0..5, 24..29]),
4643            (PathBuf::from("dir-2/e"), vec![7..12]),
4644        ]
4645    );
4646}
4647
4648#[gpui::test(iterations = 10)]
4649async fn test_document_highlights(
4650    executor: BackgroundExecutor,
4651    cx_a: &mut TestAppContext,
4652    cx_b: &mut TestAppContext,
4653) {
4654    let mut server = TestServer::start(executor.clone()).await;
4655    let client_a = server.create_client(cx_a, "user_a").await;
4656    let client_b = server.create_client(cx_b, "user_b").await;
4657    server
4658        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4659        .await;
4660    let active_call_a = cx_a.read(ActiveCall::global);
4661
4662    client_a
4663        .fs()
4664        .insert_tree(
4665            "/root-1",
4666            json!({
4667                "main.rs": "fn double(number: i32) -> i32 { number + number }",
4668            }),
4669        )
4670        .await;
4671
4672    // Set up a fake language server.
4673    let mut language = Language::new(
4674        LanguageConfig {
4675            name: "Rust".into(),
4676            path_suffixes: vec!["rs".to_string()],
4677            ..Default::default()
4678        },
4679        Some(tree_sitter_rust::language()),
4680    );
4681    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4682    client_a.language_registry().add(Arc::new(language));
4683
4684    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4685    let project_id = active_call_a
4686        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4687        .await
4688        .unwrap();
4689    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4690
4691    // Open the file on client B.
4692    let open_b = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx));
4693    let buffer_b = cx_b.executor().spawn(open_b).await.unwrap();
4694
4695    // Request document highlights as the guest.
4696    let fake_language_server = fake_language_servers.next().await.unwrap();
4697    fake_language_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
4698        |params, _| async move {
4699            assert_eq!(
4700                params
4701                    .text_document_position_params
4702                    .text_document
4703                    .uri
4704                    .as_str(),
4705                "file:///root-1/main.rs"
4706            );
4707            assert_eq!(
4708                params.text_document_position_params.position,
4709                lsp::Position::new(0, 34)
4710            );
4711            Ok(Some(vec![
4712                lsp::DocumentHighlight {
4713                    kind: Some(lsp::DocumentHighlightKind::WRITE),
4714                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 16)),
4715                },
4716                lsp::DocumentHighlight {
4717                    kind: Some(lsp::DocumentHighlightKind::READ),
4718                    range: lsp::Range::new(lsp::Position::new(0, 32), lsp::Position::new(0, 38)),
4719                },
4720                lsp::DocumentHighlight {
4721                    kind: Some(lsp::DocumentHighlightKind::READ),
4722                    range: lsp::Range::new(lsp::Position::new(0, 41), lsp::Position::new(0, 47)),
4723                },
4724            ]))
4725        },
4726    );
4727
4728    let highlights = project_b
4729        .update(cx_b, |p, cx| p.document_highlights(&buffer_b, 34, cx))
4730        .await
4731        .unwrap();
4732
4733    buffer_b.read_with(cx_b, |buffer, _| {
4734        let snapshot = buffer.snapshot();
4735
4736        let highlights = highlights
4737            .into_iter()
4738            .map(|highlight| (highlight.kind, highlight.range.to_offset(&snapshot)))
4739            .collect::<Vec<_>>();
4740        assert_eq!(
4741            highlights,
4742            &[
4743                (lsp::DocumentHighlightKind::WRITE, 10..16),
4744                (lsp::DocumentHighlightKind::READ, 32..38),
4745                (lsp::DocumentHighlightKind::READ, 41..47)
4746            ]
4747        )
4748    });
4749}
4750
4751#[gpui::test(iterations = 10)]
4752async fn test_lsp_hover(
4753    executor: BackgroundExecutor,
4754    cx_a: &mut TestAppContext,
4755    cx_b: &mut TestAppContext,
4756) {
4757    let mut server = TestServer::start(executor.clone()).await;
4758    let client_a = server.create_client(cx_a, "user_a").await;
4759    let client_b = server.create_client(cx_b, "user_b").await;
4760    server
4761        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4762        .await;
4763    let active_call_a = cx_a.read(ActiveCall::global);
4764
4765    client_a
4766        .fs()
4767        .insert_tree(
4768            "/root-1",
4769            json!({
4770                "main.rs": "use std::collections::HashMap;",
4771            }),
4772        )
4773        .await;
4774
4775    // Set up a fake language server.
4776    let mut language = Language::new(
4777        LanguageConfig {
4778            name: "Rust".into(),
4779            path_suffixes: vec!["rs".to_string()],
4780            ..Default::default()
4781        },
4782        Some(tree_sitter_rust::language()),
4783    );
4784    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4785    client_a.language_registry().add(Arc::new(language));
4786
4787    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4788    let project_id = active_call_a
4789        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4790        .await
4791        .unwrap();
4792    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4793
4794    // Open the file as the guest
4795    let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx));
4796    let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
4797
4798    // Request hover information as the guest.
4799    let fake_language_server = fake_language_servers.next().await.unwrap();
4800    fake_language_server.handle_request::<lsp::request::HoverRequest, _, _>(
4801        |params, _| async move {
4802            assert_eq!(
4803                params
4804                    .text_document_position_params
4805                    .text_document
4806                    .uri
4807                    .as_str(),
4808                "file:///root-1/main.rs"
4809            );
4810            assert_eq!(
4811                params.text_document_position_params.position,
4812                lsp::Position::new(0, 22)
4813            );
4814            Ok(Some(lsp::Hover {
4815                contents: lsp::HoverContents::Array(vec![
4816                    lsp::MarkedString::String("Test hover content.".to_string()),
4817                    lsp::MarkedString::LanguageString(lsp::LanguageString {
4818                        language: "Rust".to_string(),
4819                        value: "let foo = 42;".to_string(),
4820                    }),
4821                ]),
4822                range: Some(lsp::Range::new(
4823                    lsp::Position::new(0, 22),
4824                    lsp::Position::new(0, 29),
4825                )),
4826            }))
4827        },
4828    );
4829
4830    let hover_info = project_b
4831        .update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
4832        .await
4833        .unwrap()
4834        .unwrap();
4835
4836    buffer_b.read_with(cx_b, |buffer, _| {
4837        let snapshot = buffer.snapshot();
4838        assert_eq!(hover_info.range.unwrap().to_offset(&snapshot), 22..29);
4839        assert_eq!(
4840            hover_info.contents,
4841            vec![
4842                project::HoverBlock {
4843                    text: "Test hover content.".to_string(),
4844                    kind: HoverBlockKind::Markdown,
4845                },
4846                project::HoverBlock {
4847                    text: "let foo = 42;".to_string(),
4848                    kind: HoverBlockKind::Code {
4849                        language: "Rust".to_string()
4850                    },
4851                }
4852            ]
4853        );
4854    });
4855}
4856
4857#[gpui::test(iterations = 10)]
4858async fn test_project_symbols(
4859    executor: BackgroundExecutor,
4860    cx_a: &mut TestAppContext,
4861    cx_b: &mut TestAppContext,
4862) {
4863    let mut server = TestServer::start(executor.clone()).await;
4864    let client_a = server.create_client(cx_a, "user_a").await;
4865    let client_b = server.create_client(cx_b, "user_b").await;
4866    server
4867        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4868        .await;
4869    let active_call_a = cx_a.read(ActiveCall::global);
4870
4871    // Set up a fake language server.
4872    let mut language = Language::new(
4873        LanguageConfig {
4874            name: "Rust".into(),
4875            path_suffixes: vec!["rs".to_string()],
4876            ..Default::default()
4877        },
4878        Some(tree_sitter_rust::language()),
4879    );
4880    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4881    client_a.language_registry().add(Arc::new(language));
4882
4883    client_a
4884        .fs()
4885        .insert_tree(
4886            "/code",
4887            json!({
4888                "crate-1": {
4889                    "one.rs": "const ONE: usize = 1;",
4890                },
4891                "crate-2": {
4892                    "two.rs": "const TWO: usize = 2; const THREE: usize = 3;",
4893                },
4894                "private": {
4895                    "passwords.txt": "the-password",
4896                }
4897            }),
4898        )
4899        .await;
4900    let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await;
4901    let project_id = active_call_a
4902        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4903        .await
4904        .unwrap();
4905    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4906
4907    // Cause the language server to start.
4908    let open_buffer_task =
4909        project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx));
4910    let _buffer = cx_b.executor().spawn(open_buffer_task).await.unwrap();
4911
4912    let fake_language_server = fake_language_servers.next().await.unwrap();
4913    fake_language_server.handle_request::<lsp::WorkspaceSymbolRequest, _, _>(|_, _| async move {
4914        Ok(Some(lsp::WorkspaceSymbolResponse::Flat(vec![
4915            #[allow(deprecated)]
4916            lsp::SymbolInformation {
4917                name: "TWO".into(),
4918                location: lsp::Location {
4919                    uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
4920                    range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4921                },
4922                kind: lsp::SymbolKind::CONSTANT,
4923                tags: None,
4924                container_name: None,
4925                deprecated: None,
4926            },
4927        ])))
4928    });
4929
4930    // Request the definition of a symbol as the guest.
4931    let symbols = project_b
4932        .update(cx_b, |p, cx| p.symbols("two", cx))
4933        .await
4934        .unwrap();
4935    assert_eq!(symbols.len(), 1);
4936    assert_eq!(symbols[0].name, "TWO");
4937
4938    // Open one of the returned symbols.
4939    let buffer_b_2 = project_b
4940        .update(cx_b, |project, cx| {
4941            project.open_buffer_for_symbol(&symbols[0], cx)
4942        })
4943        .await
4944        .unwrap();
4945
4946    buffer_b_2.read_with(cx_b, |buffer, _| {
4947        assert_eq!(
4948            buffer.file().unwrap().path().as_ref(),
4949            Path::new("../crate-2/two.rs")
4950        );
4951    });
4952
4953    // Attempt to craft a symbol and violate host's privacy by opening an arbitrary file.
4954    let mut fake_symbol = symbols[0].clone();
4955    fake_symbol.path.path = Path::new("/code/secrets").into();
4956    let error = project_b
4957        .update(cx_b, |project, cx| {
4958            project.open_buffer_for_symbol(&fake_symbol, cx)
4959        })
4960        .await
4961        .unwrap_err();
4962    assert!(error.to_string().contains("invalid symbol signature"));
4963}
4964
4965#[gpui::test(iterations = 10)]
4966async fn test_open_buffer_while_getting_definition_pointing_to_it(
4967    executor: BackgroundExecutor,
4968    cx_a: &mut TestAppContext,
4969    cx_b: &mut TestAppContext,
4970    mut rng: StdRng,
4971) {
4972    let mut server = TestServer::start(executor.clone()).await;
4973    let client_a = server.create_client(cx_a, "user_a").await;
4974    let client_b = server.create_client(cx_b, "user_b").await;
4975    server
4976        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4977        .await;
4978    let active_call_a = cx_a.read(ActiveCall::global);
4979
4980    // Set up a fake language server.
4981    let mut language = Language::new(
4982        LanguageConfig {
4983            name: "Rust".into(),
4984            path_suffixes: vec!["rs".to_string()],
4985            ..Default::default()
4986        },
4987        Some(tree_sitter_rust::language()),
4988    );
4989    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4990    client_a.language_registry().add(Arc::new(language));
4991
4992    client_a
4993        .fs()
4994        .insert_tree(
4995            "/root",
4996            json!({
4997                "a.rs": "const ONE: usize = b::TWO;",
4998                "b.rs": "const TWO: usize = 2",
4999            }),
5000        )
5001        .await;
5002    let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await;
5003    let project_id = active_call_a
5004        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5005        .await
5006        .unwrap();
5007    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5008
5009    let open_buffer_task = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
5010    let buffer_b1 = cx_b.executor().spawn(open_buffer_task).await.unwrap();
5011
5012    let fake_language_server = fake_language_servers.next().await.unwrap();
5013    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
5014        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
5015            lsp::Location::new(
5016                lsp::Url::from_file_path("/root/b.rs").unwrap(),
5017                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5018            ),
5019        )))
5020    });
5021
5022    let definitions;
5023    let buffer_b2;
5024    if rng.gen() {
5025        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5026        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5027    } else {
5028        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
5029        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
5030    }
5031
5032    let buffer_b2 = buffer_b2.await.unwrap();
5033    let definitions = definitions.await.unwrap();
5034    assert_eq!(definitions.len(), 1);
5035    assert_eq!(definitions[0].target.buffer, buffer_b2);
5036}
5037
5038#[gpui::test(iterations = 10)]
5039async fn test_contacts(
5040    executor: BackgroundExecutor,
5041    cx_a: &mut TestAppContext,
5042    cx_b: &mut TestAppContext,
5043    cx_c: &mut TestAppContext,
5044    cx_d: &mut TestAppContext,
5045) {
5046    let mut server = TestServer::start(executor.clone()).await;
5047    let client_a = server.create_client(cx_a, "user_a").await;
5048    let client_b = server.create_client(cx_b, "user_b").await;
5049    let client_c = server.create_client(cx_c, "user_c").await;
5050    let client_d = server.create_client(cx_d, "user_d").await;
5051    server
5052        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
5053        .await;
5054    let active_call_a = cx_a.read(ActiveCall::global);
5055    let active_call_b = cx_b.read(ActiveCall::global);
5056    let active_call_c = cx_c.read(ActiveCall::global);
5057    let _active_call_d = cx_d.read(ActiveCall::global);
5058
5059    executor.run_until_parked();
5060    assert_eq!(
5061        contacts(&client_a, cx_a),
5062        [
5063            ("user_b".to_string(), "online", "free"),
5064            ("user_c".to_string(), "online", "free")
5065        ]
5066    );
5067    assert_eq!(
5068        contacts(&client_b, cx_b),
5069        [
5070            ("user_a".to_string(), "online", "free"),
5071            ("user_c".to_string(), "online", "free")
5072        ]
5073    );
5074    assert_eq!(
5075        contacts(&client_c, cx_c),
5076        [
5077            ("user_a".to_string(), "online", "free"),
5078            ("user_b".to_string(), "online", "free")
5079        ]
5080    );
5081    assert_eq!(contacts(&client_d, cx_d), []);
5082
5083    server.disconnect_client(client_c.peer_id().unwrap());
5084    server.forbid_connections();
5085    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5086    assert_eq!(
5087        contacts(&client_a, cx_a),
5088        [
5089            ("user_b".to_string(), "online", "free"),
5090            ("user_c".to_string(), "offline", "free")
5091        ]
5092    );
5093    assert_eq!(
5094        contacts(&client_b, cx_b),
5095        [
5096            ("user_a".to_string(), "online", "free"),
5097            ("user_c".to_string(), "offline", "free")
5098        ]
5099    );
5100    assert_eq!(contacts(&client_c, cx_c), []);
5101    assert_eq!(contacts(&client_d, cx_d), []);
5102
5103    server.allow_connections();
5104    client_c
5105        .authenticate_and_connect(false, &cx_c.to_async())
5106        .await
5107        .unwrap();
5108
5109    executor.run_until_parked();
5110    assert_eq!(
5111        contacts(&client_a, cx_a),
5112        [
5113            ("user_b".to_string(), "online", "free"),
5114            ("user_c".to_string(), "online", "free")
5115        ]
5116    );
5117    assert_eq!(
5118        contacts(&client_b, cx_b),
5119        [
5120            ("user_a".to_string(), "online", "free"),
5121            ("user_c".to_string(), "online", "free")
5122        ]
5123    );
5124    assert_eq!(
5125        contacts(&client_c, cx_c),
5126        [
5127            ("user_a".to_string(), "online", "free"),
5128            ("user_b".to_string(), "online", "free")
5129        ]
5130    );
5131    assert_eq!(contacts(&client_d, cx_d), []);
5132
5133    active_call_a
5134        .update(cx_a, |call, cx| {
5135            call.invite(client_b.user_id().unwrap(), None, cx)
5136        })
5137        .await
5138        .unwrap();
5139    executor.run_until_parked();
5140    assert_eq!(
5141        contacts(&client_a, cx_a),
5142        [
5143            ("user_b".to_string(), "online", "busy"),
5144            ("user_c".to_string(), "online", "free")
5145        ]
5146    );
5147    assert_eq!(
5148        contacts(&client_b, cx_b),
5149        [
5150            ("user_a".to_string(), "online", "busy"),
5151            ("user_c".to_string(), "online", "free")
5152        ]
5153    );
5154    assert_eq!(
5155        contacts(&client_c, cx_c),
5156        [
5157            ("user_a".to_string(), "online", "busy"),
5158            ("user_b".to_string(), "online", "busy")
5159        ]
5160    );
5161    assert_eq!(contacts(&client_d, cx_d), []);
5162
5163    // Client B and client D become contacts while client B is being called.
5164    server
5165        .make_contacts(&mut [(&client_b, cx_b), (&client_d, cx_d)])
5166        .await;
5167    executor.run_until_parked();
5168    assert_eq!(
5169        contacts(&client_a, cx_a),
5170        [
5171            ("user_b".to_string(), "online", "busy"),
5172            ("user_c".to_string(), "online", "free")
5173        ]
5174    );
5175    assert_eq!(
5176        contacts(&client_b, cx_b),
5177        [
5178            ("user_a".to_string(), "online", "busy"),
5179            ("user_c".to_string(), "online", "free"),
5180            ("user_d".to_string(), "online", "free"),
5181        ]
5182    );
5183    assert_eq!(
5184        contacts(&client_c, cx_c),
5185        [
5186            ("user_a".to_string(), "online", "busy"),
5187            ("user_b".to_string(), "online", "busy")
5188        ]
5189    );
5190    assert_eq!(
5191        contacts(&client_d, cx_d),
5192        [("user_b".to_string(), "online", "busy")]
5193    );
5194
5195    active_call_b.update(cx_b, |call, cx| call.decline_incoming(cx).unwrap());
5196    executor.run_until_parked();
5197    assert_eq!(
5198        contacts(&client_a, cx_a),
5199        [
5200            ("user_b".to_string(), "online", "free"),
5201            ("user_c".to_string(), "online", "free")
5202        ]
5203    );
5204    assert_eq!(
5205        contacts(&client_b, cx_b),
5206        [
5207            ("user_a".to_string(), "online", "free"),
5208            ("user_c".to_string(), "online", "free"),
5209            ("user_d".to_string(), "online", "free")
5210        ]
5211    );
5212    assert_eq!(
5213        contacts(&client_c, cx_c),
5214        [
5215            ("user_a".to_string(), "online", "free"),
5216            ("user_b".to_string(), "online", "free")
5217        ]
5218    );
5219    assert_eq!(
5220        contacts(&client_d, cx_d),
5221        [("user_b".to_string(), "online", "free")]
5222    );
5223
5224    active_call_c
5225        .update(cx_c, |call, cx| {
5226            call.invite(client_a.user_id().unwrap(), None, cx)
5227        })
5228        .await
5229        .unwrap();
5230    executor.run_until_parked();
5231    assert_eq!(
5232        contacts(&client_a, cx_a),
5233        [
5234            ("user_b".to_string(), "online", "free"),
5235            ("user_c".to_string(), "online", "busy")
5236        ]
5237    );
5238    assert_eq!(
5239        contacts(&client_b, cx_b),
5240        [
5241            ("user_a".to_string(), "online", "busy"),
5242            ("user_c".to_string(), "online", "busy"),
5243            ("user_d".to_string(), "online", "free")
5244        ]
5245    );
5246    assert_eq!(
5247        contacts(&client_c, cx_c),
5248        [
5249            ("user_a".to_string(), "online", "busy"),
5250            ("user_b".to_string(), "online", "free")
5251        ]
5252    );
5253    assert_eq!(
5254        contacts(&client_d, cx_d),
5255        [("user_b".to_string(), "online", "free")]
5256    );
5257
5258    active_call_a
5259        .update(cx_a, |call, cx| call.accept_incoming(cx))
5260        .await
5261        .unwrap();
5262    executor.run_until_parked();
5263    assert_eq!(
5264        contacts(&client_a, cx_a),
5265        [
5266            ("user_b".to_string(), "online", "free"),
5267            ("user_c".to_string(), "online", "busy")
5268        ]
5269    );
5270    assert_eq!(
5271        contacts(&client_b, cx_b),
5272        [
5273            ("user_a".to_string(), "online", "busy"),
5274            ("user_c".to_string(), "online", "busy"),
5275            ("user_d".to_string(), "online", "free")
5276        ]
5277    );
5278    assert_eq!(
5279        contacts(&client_c, cx_c),
5280        [
5281            ("user_a".to_string(), "online", "busy"),
5282            ("user_b".to_string(), "online", "free")
5283        ]
5284    );
5285    assert_eq!(
5286        contacts(&client_d, cx_d),
5287        [("user_b".to_string(), "online", "free")]
5288    );
5289
5290    active_call_a
5291        .update(cx_a, |call, cx| {
5292            call.invite(client_b.user_id().unwrap(), None, cx)
5293        })
5294        .await
5295        .unwrap();
5296    executor.run_until_parked();
5297    assert_eq!(
5298        contacts(&client_a, cx_a),
5299        [
5300            ("user_b".to_string(), "online", "busy"),
5301            ("user_c".to_string(), "online", "busy")
5302        ]
5303    );
5304    assert_eq!(
5305        contacts(&client_b, cx_b),
5306        [
5307            ("user_a".to_string(), "online", "busy"),
5308            ("user_c".to_string(), "online", "busy"),
5309            ("user_d".to_string(), "online", "free")
5310        ]
5311    );
5312    assert_eq!(
5313        contacts(&client_c, cx_c),
5314        [
5315            ("user_a".to_string(), "online", "busy"),
5316            ("user_b".to_string(), "online", "busy")
5317        ]
5318    );
5319    assert_eq!(
5320        contacts(&client_d, cx_d),
5321        [("user_b".to_string(), "online", "busy")]
5322    );
5323
5324    active_call_a
5325        .update(cx_a, |call, cx| call.hang_up(cx))
5326        .await
5327        .unwrap();
5328    executor.run_until_parked();
5329    assert_eq!(
5330        contacts(&client_a, cx_a),
5331        [
5332            ("user_b".to_string(), "online", "free"),
5333            ("user_c".to_string(), "online", "free")
5334        ]
5335    );
5336    assert_eq!(
5337        contacts(&client_b, cx_b),
5338        [
5339            ("user_a".to_string(), "online", "free"),
5340            ("user_c".to_string(), "online", "free"),
5341            ("user_d".to_string(), "online", "free")
5342        ]
5343    );
5344    assert_eq!(
5345        contacts(&client_c, cx_c),
5346        [
5347            ("user_a".to_string(), "online", "free"),
5348            ("user_b".to_string(), "online", "free")
5349        ]
5350    );
5351    assert_eq!(
5352        contacts(&client_d, cx_d),
5353        [("user_b".to_string(), "online", "free")]
5354    );
5355
5356    active_call_a
5357        .update(cx_a, |call, cx| {
5358            call.invite(client_b.user_id().unwrap(), None, cx)
5359        })
5360        .await
5361        .unwrap();
5362    executor.run_until_parked();
5363    assert_eq!(
5364        contacts(&client_a, cx_a),
5365        [
5366            ("user_b".to_string(), "online", "busy"),
5367            ("user_c".to_string(), "online", "free")
5368        ]
5369    );
5370    assert_eq!(
5371        contacts(&client_b, cx_b),
5372        [
5373            ("user_a".to_string(), "online", "busy"),
5374            ("user_c".to_string(), "online", "free"),
5375            ("user_d".to_string(), "online", "free")
5376        ]
5377    );
5378    assert_eq!(
5379        contacts(&client_c, cx_c),
5380        [
5381            ("user_a".to_string(), "online", "busy"),
5382            ("user_b".to_string(), "online", "busy")
5383        ]
5384    );
5385    assert_eq!(
5386        contacts(&client_d, cx_d),
5387        [("user_b".to_string(), "online", "busy")]
5388    );
5389
5390    server.forbid_connections();
5391    server.disconnect_client(client_a.peer_id().unwrap());
5392    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5393    assert_eq!(contacts(&client_a, cx_a), []);
5394    assert_eq!(
5395        contacts(&client_b, cx_b),
5396        [
5397            ("user_a".to_string(), "offline", "free"),
5398            ("user_c".to_string(), "online", "free"),
5399            ("user_d".to_string(), "online", "free")
5400        ]
5401    );
5402    assert_eq!(
5403        contacts(&client_c, cx_c),
5404        [
5405            ("user_a".to_string(), "offline", "free"),
5406            ("user_b".to_string(), "online", "free")
5407        ]
5408    );
5409    assert_eq!(
5410        contacts(&client_d, cx_d),
5411        [("user_b".to_string(), "online", "free")]
5412    );
5413
5414    // Test removing a contact
5415    client_b
5416        .user_store()
5417        .update(cx_b, |store, cx| {
5418            store.remove_contact(client_c.user_id().unwrap(), cx)
5419        })
5420        .await
5421        .unwrap();
5422    executor.run_until_parked();
5423    assert_eq!(
5424        contacts(&client_b, cx_b),
5425        [
5426            ("user_a".to_string(), "offline", "free"),
5427            ("user_d".to_string(), "online", "free")
5428        ]
5429    );
5430    assert_eq!(
5431        contacts(&client_c, cx_c),
5432        [("user_a".to_string(), "offline", "free"),]
5433    );
5434
5435    fn contacts(
5436        client: &TestClient,
5437        cx: &TestAppContext,
5438    ) -> Vec<(String, &'static str, &'static str)> {
5439        client.user_store().read_with(cx, |store, _| {
5440            store
5441                .contacts()
5442                .iter()
5443                .map(|contact| {
5444                    (
5445                        contact.user.github_login.clone(),
5446                        if contact.online { "online" } else { "offline" },
5447                        if contact.busy { "busy" } else { "free" },
5448                    )
5449                })
5450                .collect()
5451        })
5452    }
5453}
5454
5455#[gpui::test(iterations = 10)]
5456async fn test_contact_requests(
5457    executor: BackgroundExecutor,
5458    cx_a: &mut TestAppContext,
5459    cx_a2: &mut TestAppContext,
5460    cx_b: &mut TestAppContext,
5461    cx_b2: &mut TestAppContext,
5462    cx_c: &mut TestAppContext,
5463    cx_c2: &mut TestAppContext,
5464) {
5465    // Connect to a server as 3 clients.
5466    let mut server = TestServer::start(executor.clone()).await;
5467    let client_a = server.create_client(cx_a, "user_a").await;
5468    let client_a2 = server.create_client(cx_a2, "user_a").await;
5469    let client_b = server.create_client(cx_b, "user_b").await;
5470    let client_b2 = server.create_client(cx_b2, "user_b").await;
5471    let client_c = server.create_client(cx_c, "user_c").await;
5472    let client_c2 = server.create_client(cx_c2, "user_c").await;
5473
5474    assert_eq!(client_a.user_id().unwrap(), client_a2.user_id().unwrap());
5475    assert_eq!(client_b.user_id().unwrap(), client_b2.user_id().unwrap());
5476    assert_eq!(client_c.user_id().unwrap(), client_c2.user_id().unwrap());
5477
5478    // User A and User C request that user B become their contact.
5479    client_a
5480        .user_store()
5481        .update(cx_a, |store, cx| {
5482            store.request_contact(client_b.user_id().unwrap(), cx)
5483        })
5484        .await
5485        .unwrap();
5486    client_c
5487        .user_store()
5488        .update(cx_c, |store, cx| {
5489            store.request_contact(client_b.user_id().unwrap(), cx)
5490        })
5491        .await
5492        .unwrap();
5493    executor.run_until_parked();
5494
5495    // All users see the pending request appear in all their clients.
5496    assert_eq!(
5497        client_a.summarize_contacts(cx_a).outgoing_requests,
5498        &["user_b"]
5499    );
5500    assert_eq!(
5501        client_a2.summarize_contacts(cx_a2).outgoing_requests,
5502        &["user_b"]
5503    );
5504    assert_eq!(
5505        client_b.summarize_contacts(cx_b).incoming_requests,
5506        &["user_a", "user_c"]
5507    );
5508    assert_eq!(
5509        client_b2.summarize_contacts(cx_b2).incoming_requests,
5510        &["user_a", "user_c"]
5511    );
5512    assert_eq!(
5513        client_c.summarize_contacts(cx_c).outgoing_requests,
5514        &["user_b"]
5515    );
5516    assert_eq!(
5517        client_c2.summarize_contacts(cx_c2).outgoing_requests,
5518        &["user_b"]
5519    );
5520
5521    // Contact requests are present upon connecting (tested here via disconnect/reconnect)
5522    disconnect_and_reconnect(&client_a, cx_a).await;
5523    disconnect_and_reconnect(&client_b, cx_b).await;
5524    disconnect_and_reconnect(&client_c, cx_c).await;
5525    executor.run_until_parked();
5526    assert_eq!(
5527        client_a.summarize_contacts(cx_a).outgoing_requests,
5528        &["user_b"]
5529    );
5530    assert_eq!(
5531        client_b.summarize_contacts(cx_b).incoming_requests,
5532        &["user_a", "user_c"]
5533    );
5534    assert_eq!(
5535        client_c.summarize_contacts(cx_c).outgoing_requests,
5536        &["user_b"]
5537    );
5538
5539    // User B accepts the request from user A.
5540    client_b
5541        .user_store()
5542        .update(cx_b, |store, cx| {
5543            store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
5544        })
5545        .await
5546        .unwrap();
5547
5548    executor.run_until_parked();
5549
5550    // User B sees user A as their contact now in all client, and the incoming request from them is removed.
5551    let contacts_b = client_b.summarize_contacts(cx_b);
5552    assert_eq!(contacts_b.current, &["user_a"]);
5553    assert_eq!(contacts_b.incoming_requests, &["user_c"]);
5554    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
5555    assert_eq!(contacts_b2.current, &["user_a"]);
5556    assert_eq!(contacts_b2.incoming_requests, &["user_c"]);
5557
5558    // User A sees user B as their contact now in all clients, and the outgoing request to them is removed.
5559    let contacts_a = client_a.summarize_contacts(cx_a);
5560    assert_eq!(contacts_a.current, &["user_b"]);
5561    assert!(contacts_a.outgoing_requests.is_empty());
5562    let contacts_a2 = client_a2.summarize_contacts(cx_a2);
5563    assert_eq!(contacts_a2.current, &["user_b"]);
5564    assert!(contacts_a2.outgoing_requests.is_empty());
5565
5566    // Contacts are present upon connecting (tested here via disconnect/reconnect)
5567    disconnect_and_reconnect(&client_a, cx_a).await;
5568    disconnect_and_reconnect(&client_b, cx_b).await;
5569    disconnect_and_reconnect(&client_c, cx_c).await;
5570    executor.run_until_parked();
5571    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
5572    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
5573    assert_eq!(
5574        client_b.summarize_contacts(cx_b).incoming_requests,
5575        &["user_c"]
5576    );
5577    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
5578    assert_eq!(
5579        client_c.summarize_contacts(cx_c).outgoing_requests,
5580        &["user_b"]
5581    );
5582
5583    // User B rejects the request from user C.
5584    client_b
5585        .user_store()
5586        .update(cx_b, |store, cx| {
5587            store.respond_to_contact_request(client_c.user_id().unwrap(), false, cx)
5588        })
5589        .await
5590        .unwrap();
5591
5592    executor.run_until_parked();
5593
5594    // User B doesn't see user C as their contact, and the incoming request from them is removed.
5595    let contacts_b = client_b.summarize_contacts(cx_b);
5596    assert_eq!(contacts_b.current, &["user_a"]);
5597    assert!(contacts_b.incoming_requests.is_empty());
5598    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
5599    assert_eq!(contacts_b2.current, &["user_a"]);
5600    assert!(contacts_b2.incoming_requests.is_empty());
5601
5602    // User C doesn't see user B as their contact, and the outgoing request to them is removed.
5603    let contacts_c = client_c.summarize_contacts(cx_c);
5604    assert!(contacts_c.current.is_empty());
5605    assert!(contacts_c.outgoing_requests.is_empty());
5606    let contacts_c2 = client_c2.summarize_contacts(cx_c2);
5607    assert!(contacts_c2.current.is_empty());
5608    assert!(contacts_c2.outgoing_requests.is_empty());
5609
5610    // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect)
5611    disconnect_and_reconnect(&client_a, cx_a).await;
5612    disconnect_and_reconnect(&client_b, cx_b).await;
5613    disconnect_and_reconnect(&client_c, cx_c).await;
5614    executor.run_until_parked();
5615    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
5616    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
5617    assert!(client_b
5618        .summarize_contacts(cx_b)
5619        .incoming_requests
5620        .is_empty());
5621    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
5622    assert!(client_c
5623        .summarize_contacts(cx_c)
5624        .outgoing_requests
5625        .is_empty());
5626
5627    async fn disconnect_and_reconnect(client: &TestClient, cx: &mut TestAppContext) {
5628        client.disconnect(&cx.to_async());
5629        client.clear_contacts(cx).await;
5630        client
5631            .authenticate_and_connect(false, &cx.to_async())
5632            .await
5633            .unwrap();
5634    }
5635}
5636
5637#[gpui::test(iterations = 10)]
5638async fn test_join_call_after_screen_was_shared(
5639    executor: BackgroundExecutor,
5640    cx_a: &mut TestAppContext,
5641    cx_b: &mut TestAppContext,
5642) {
5643    let mut server = TestServer::start(executor.clone()).await;
5644
5645    let client_a = server.create_client(cx_a, "user_a").await;
5646    let client_b = server.create_client(cx_b, "user_b").await;
5647    server
5648        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5649        .await;
5650
5651    let active_call_a = cx_a.read(ActiveCall::global);
5652    let active_call_b = cx_b.read(ActiveCall::global);
5653
5654    // Call users B and C from client A.
5655    active_call_a
5656        .update(cx_a, |call, cx| {
5657            call.invite(client_b.user_id().unwrap(), None, cx)
5658        })
5659        .await
5660        .unwrap();
5661
5662    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
5663    executor.run_until_parked();
5664    assert_eq!(
5665        room_participants(&room_a, cx_a),
5666        RoomParticipants {
5667            remote: Default::default(),
5668            pending: vec!["user_b".to_string()]
5669        }
5670    );
5671
5672    // User B receives the call.
5673
5674    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
5675    let call_b = incoming_call_b.next().await.unwrap().unwrap();
5676    assert_eq!(call_b.calling_user.github_login, "user_a");
5677
5678    // User A shares their screen
5679    let display = MacOSDisplay::new();
5680    active_call_a
5681        .update(cx_a, |call, cx| {
5682            call.room().unwrap().update(cx, |room, cx| {
5683                room.set_display_sources(vec![display.clone()]);
5684                room.share_screen(cx)
5685            })
5686        })
5687        .await
5688        .unwrap();
5689
5690    client_b.user_store().update(cx_b, |user_store, _| {
5691        user_store.clear_cache();
5692    });
5693
5694    // User B joins the room
5695    active_call_b
5696        .update(cx_b, |call, cx| call.accept_incoming(cx))
5697        .await
5698        .unwrap();
5699
5700    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
5701    assert!(incoming_call_b.next().await.unwrap().is_none());
5702
5703    executor.run_until_parked();
5704    assert_eq!(
5705        room_participants(&room_a, cx_a),
5706        RoomParticipants {
5707            remote: vec!["user_b".to_string()],
5708            pending: vec![],
5709        }
5710    );
5711    assert_eq!(
5712        room_participants(&room_b, cx_b),
5713        RoomParticipants {
5714            remote: vec!["user_a".to_string()],
5715            pending: vec![],
5716        }
5717    );
5718
5719    // Ensure User B sees User A's screenshare.
5720
5721    room_b.read_with(cx_b, |room, _| {
5722        assert_eq!(
5723            room.remote_participants()
5724                .get(&client_a.user_id().unwrap())
5725                .unwrap()
5726                .video_tracks
5727                .len(),
5728            1
5729        );
5730    });
5731}