integration_tests.rs

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