integration_tests.rs

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