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