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