integration_tests.rs

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