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, AppContext, ModelHandle,
  17    TestAppContext, 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]
2608async fn test_git_branch_name(
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            ".git": {},
2628            }),
2629        )
2630        .await;
2631
2632    let (project_local, _worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2633    let project_id = active_call_a
2634        .update(cx_a, |call, cx| {
2635            call.share_project(project_local.clone(), cx)
2636        })
2637        .await
2638        .unwrap();
2639
2640    let project_remote = client_b.build_remote_project(project_id, cx_b).await;
2641    client_a
2642        .fs
2643        .as_fake()
2644        .set_branch_name(Path::new("/dir/.git"), Some("branch-1"))
2645        .await;
2646
2647    // Wait for it to catch up to the new branch
2648    deterministic.run_until_parked();
2649
2650    #[track_caller]
2651    fn assert_branch(branch_name: Option<impl Into<String>>, project: &Project, cx: &AppContext) {
2652        let branch_name = branch_name.map(Into::into);
2653        let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
2654        assert_eq!(worktrees.len(), 1);
2655        let worktree = worktrees[0].clone();
2656        let root_entry = worktree.read(cx).snapshot().root_git_entry().unwrap();
2657        assert_eq!(root_entry.branch(), branch_name.map(Into::into));
2658    }
2659
2660    // Smoke test branch reading
2661    project_local.read_with(cx_a, |project, cx| {
2662        assert_branch(Some("branch-1"), project, cx)
2663    });
2664    project_remote.read_with(cx_b, |project, cx| {
2665        assert_branch(Some("branch-1"), project, cx)
2666    });
2667
2668    client_a
2669        .fs
2670        .as_fake()
2671        .set_branch_name(Path::new("/dir/.git"), Some("branch-2"))
2672        .await;
2673
2674    // Wait for buffer_local_a to receive it
2675    deterministic.run_until_parked();
2676
2677    // Smoke test branch reading
2678    project_local.read_with(cx_a, |project, cx| {
2679        assert_branch(Some("branch-2"), project, cx)
2680    });
2681    project_remote.read_with(cx_b, |project, cx| {
2682        assert_branch(Some("branch-2"), project, cx)
2683    });
2684}
2685
2686#[gpui::test(iterations = 10)]
2687async fn test_fs_operations(
2688    deterministic: Arc<Deterministic>,
2689    cx_a: &mut TestAppContext,
2690    cx_b: &mut TestAppContext,
2691) {
2692    deterministic.forbid_parking();
2693    let mut server = TestServer::start(&deterministic).await;
2694    let client_a = server.create_client(cx_a, "user_a").await;
2695    let client_b = server.create_client(cx_b, "user_b").await;
2696    server
2697        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2698        .await;
2699    let active_call_a = cx_a.read(ActiveCall::global);
2700
2701    client_a
2702        .fs
2703        .insert_tree(
2704            "/dir",
2705            json!({
2706                "a.txt": "a-contents",
2707                "b.txt": "b-contents",
2708            }),
2709        )
2710        .await;
2711    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2712    let project_id = active_call_a
2713        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2714        .await
2715        .unwrap();
2716    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2717
2718    let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
2719    let worktree_b = project_b.read_with(cx_b, |project, cx| project.worktrees(cx).next().unwrap());
2720
2721    let entry = project_b
2722        .update(cx_b, |project, cx| {
2723            project
2724                .create_entry((worktree_id, "c.txt"), false, cx)
2725                .unwrap()
2726        })
2727        .await
2728        .unwrap();
2729    worktree_a.read_with(cx_a, |worktree, _| {
2730        assert_eq!(
2731            worktree
2732                .paths()
2733                .map(|p| p.to_string_lossy())
2734                .collect::<Vec<_>>(),
2735            ["a.txt", "b.txt", "c.txt"]
2736        );
2737    });
2738    worktree_b.read_with(cx_b, |worktree, _| {
2739        assert_eq!(
2740            worktree
2741                .paths()
2742                .map(|p| p.to_string_lossy())
2743                .collect::<Vec<_>>(),
2744            ["a.txt", "b.txt", "c.txt"]
2745        );
2746    });
2747
2748    project_b
2749        .update(cx_b, |project, cx| {
2750            project.rename_entry(entry.id, Path::new("d.txt"), cx)
2751        })
2752        .unwrap()
2753        .await
2754        .unwrap();
2755    worktree_a.read_with(cx_a, |worktree, _| {
2756        assert_eq!(
2757            worktree
2758                .paths()
2759                .map(|p| p.to_string_lossy())
2760                .collect::<Vec<_>>(),
2761            ["a.txt", "b.txt", "d.txt"]
2762        );
2763    });
2764    worktree_b.read_with(cx_b, |worktree, _| {
2765        assert_eq!(
2766            worktree
2767                .paths()
2768                .map(|p| p.to_string_lossy())
2769                .collect::<Vec<_>>(),
2770            ["a.txt", "b.txt", "d.txt"]
2771        );
2772    });
2773
2774    let dir_entry = project_b
2775        .update(cx_b, |project, cx| {
2776            project
2777                .create_entry((worktree_id, "DIR"), true, cx)
2778                .unwrap()
2779        })
2780        .await
2781        .unwrap();
2782    worktree_a.read_with(cx_a, |worktree, _| {
2783        assert_eq!(
2784            worktree
2785                .paths()
2786                .map(|p| p.to_string_lossy())
2787                .collect::<Vec<_>>(),
2788            ["DIR", "a.txt", "b.txt", "d.txt"]
2789        );
2790    });
2791    worktree_b.read_with(cx_b, |worktree, _| {
2792        assert_eq!(
2793            worktree
2794                .paths()
2795                .map(|p| p.to_string_lossy())
2796                .collect::<Vec<_>>(),
2797            ["DIR", "a.txt", "b.txt", "d.txt"]
2798        );
2799    });
2800
2801    project_b
2802        .update(cx_b, |project, cx| {
2803            project
2804                .create_entry((worktree_id, "DIR/e.txt"), false, cx)
2805                .unwrap()
2806        })
2807        .await
2808        .unwrap();
2809    project_b
2810        .update(cx_b, |project, cx| {
2811            project
2812                .create_entry((worktree_id, "DIR/SUBDIR"), true, cx)
2813                .unwrap()
2814        })
2815        .await
2816        .unwrap();
2817    project_b
2818        .update(cx_b, |project, cx| {
2819            project
2820                .create_entry((worktree_id, "DIR/SUBDIR/f.txt"), false, cx)
2821                .unwrap()
2822        })
2823        .await
2824        .unwrap();
2825    worktree_a.read_with(cx_a, |worktree, _| {
2826        assert_eq!(
2827            worktree
2828                .paths()
2829                .map(|p| p.to_string_lossy())
2830                .collect::<Vec<_>>(),
2831            [
2832                "DIR",
2833                "DIR/SUBDIR",
2834                "DIR/SUBDIR/f.txt",
2835                "DIR/e.txt",
2836                "a.txt",
2837                "b.txt",
2838                "d.txt"
2839            ]
2840        );
2841    });
2842    worktree_b.read_with(cx_b, |worktree, _| {
2843        assert_eq!(
2844            worktree
2845                .paths()
2846                .map(|p| p.to_string_lossy())
2847                .collect::<Vec<_>>(),
2848            [
2849                "DIR",
2850                "DIR/SUBDIR",
2851                "DIR/SUBDIR/f.txt",
2852                "DIR/e.txt",
2853                "a.txt",
2854                "b.txt",
2855                "d.txt"
2856            ]
2857        );
2858    });
2859
2860    project_b
2861        .update(cx_b, |project, cx| {
2862            project
2863                .copy_entry(entry.id, Path::new("f.txt"), cx)
2864                .unwrap()
2865        })
2866        .await
2867        .unwrap();
2868    worktree_a.read_with(cx_a, |worktree, _| {
2869        assert_eq!(
2870            worktree
2871                .paths()
2872                .map(|p| p.to_string_lossy())
2873                .collect::<Vec<_>>(),
2874            [
2875                "DIR",
2876                "DIR/SUBDIR",
2877                "DIR/SUBDIR/f.txt",
2878                "DIR/e.txt",
2879                "a.txt",
2880                "b.txt",
2881                "d.txt",
2882                "f.txt"
2883            ]
2884        );
2885    });
2886    worktree_b.read_with(cx_b, |worktree, _| {
2887        assert_eq!(
2888            worktree
2889                .paths()
2890                .map(|p| p.to_string_lossy())
2891                .collect::<Vec<_>>(),
2892            [
2893                "DIR",
2894                "DIR/SUBDIR",
2895                "DIR/SUBDIR/f.txt",
2896                "DIR/e.txt",
2897                "a.txt",
2898                "b.txt",
2899                "d.txt",
2900                "f.txt"
2901            ]
2902        );
2903    });
2904
2905    project_b
2906        .update(cx_b, |project, cx| {
2907            project.delete_entry(dir_entry.id, cx).unwrap()
2908        })
2909        .await
2910        .unwrap();
2911    deterministic.run_until_parked();
2912
2913    worktree_a.read_with(cx_a, |worktree, _| {
2914        assert_eq!(
2915            worktree
2916                .paths()
2917                .map(|p| p.to_string_lossy())
2918                .collect::<Vec<_>>(),
2919            ["a.txt", "b.txt", "d.txt", "f.txt"]
2920        );
2921    });
2922    worktree_b.read_with(cx_b, |worktree, _| {
2923        assert_eq!(
2924            worktree
2925                .paths()
2926                .map(|p| p.to_string_lossy())
2927                .collect::<Vec<_>>(),
2928            ["a.txt", "b.txt", "d.txt", "f.txt"]
2929        );
2930    });
2931
2932    project_b
2933        .update(cx_b, |project, cx| {
2934            project.delete_entry(entry.id, cx).unwrap()
2935        })
2936        .await
2937        .unwrap();
2938    worktree_a.read_with(cx_a, |worktree, _| {
2939        assert_eq!(
2940            worktree
2941                .paths()
2942                .map(|p| p.to_string_lossy())
2943                .collect::<Vec<_>>(),
2944            ["a.txt", "b.txt", "f.txt"]
2945        );
2946    });
2947    worktree_b.read_with(cx_b, |worktree, _| {
2948        assert_eq!(
2949            worktree
2950                .paths()
2951                .map(|p| p.to_string_lossy())
2952                .collect::<Vec<_>>(),
2953            ["a.txt", "b.txt", "f.txt"]
2954        );
2955    });
2956}
2957
2958#[gpui::test(iterations = 10)]
2959async fn test_buffer_conflict_after_save(
2960    deterministic: Arc<Deterministic>,
2961    cx_a: &mut TestAppContext,
2962    cx_b: &mut TestAppContext,
2963) {
2964    deterministic.forbid_parking();
2965    let mut server = TestServer::start(&deterministic).await;
2966    let client_a = server.create_client(cx_a, "user_a").await;
2967    let client_b = server.create_client(cx_b, "user_b").await;
2968    server
2969        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2970        .await;
2971    let active_call_a = cx_a.read(ActiveCall::global);
2972
2973    client_a
2974        .fs
2975        .insert_tree(
2976            "/dir",
2977            json!({
2978                "a.txt": "a-contents",
2979            }),
2980        )
2981        .await;
2982    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2983    let project_id = active_call_a
2984        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2985        .await
2986        .unwrap();
2987    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2988
2989    // Open a buffer as client B
2990    let buffer_b = project_b
2991        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2992        .await
2993        .unwrap();
2994
2995    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "world ")], None, cx));
2996    buffer_b.read_with(cx_b, |buf, _| {
2997        assert!(buf.is_dirty());
2998        assert!(!buf.has_conflict());
2999    });
3000
3001    project_b
3002        .update(cx_b, |project, cx| {
3003            project.save_buffer(buffer_b.clone(), cx)
3004        })
3005        .await
3006        .unwrap();
3007    cx_a.foreground().forbid_parking();
3008    buffer_b.read_with(cx_b, |buffer_b, _| assert!(!buffer_b.is_dirty()));
3009    buffer_b.read_with(cx_b, |buf, _| {
3010        assert!(!buf.has_conflict());
3011    });
3012
3013    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "hello ")], None, cx));
3014    buffer_b.read_with(cx_b, |buf, _| {
3015        assert!(buf.is_dirty());
3016        assert!(!buf.has_conflict());
3017    });
3018}
3019
3020#[gpui::test(iterations = 10)]
3021async fn test_buffer_reloading(
3022    deterministic: Arc<Deterministic>,
3023    cx_a: &mut TestAppContext,
3024    cx_b: &mut TestAppContext,
3025) {
3026    deterministic.forbid_parking();
3027    let mut server = TestServer::start(&deterministic).await;
3028    let client_a = server.create_client(cx_a, "user_a").await;
3029    let client_b = server.create_client(cx_b, "user_b").await;
3030    server
3031        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3032        .await;
3033    let active_call_a = cx_a.read(ActiveCall::global);
3034
3035    client_a
3036        .fs
3037        .insert_tree(
3038            "/dir",
3039            json!({
3040                "a.txt": "a\nb\nc",
3041            }),
3042        )
3043        .await;
3044    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3045    let project_id = active_call_a
3046        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3047        .await
3048        .unwrap();
3049    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3050
3051    // Open a buffer as client B
3052    let buffer_b = project_b
3053        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3054        .await
3055        .unwrap();
3056    buffer_b.read_with(cx_b, |buf, _| {
3057        assert!(!buf.is_dirty());
3058        assert!(!buf.has_conflict());
3059        assert_eq!(buf.line_ending(), LineEnding::Unix);
3060    });
3061
3062    let new_contents = Rope::from("d\ne\nf");
3063    client_a
3064        .fs
3065        .save("/dir/a.txt".as_ref(), &new_contents, LineEnding::Windows)
3066        .await
3067        .unwrap();
3068    cx_a.foreground().run_until_parked();
3069    buffer_b.read_with(cx_b, |buf, _| {
3070        assert_eq!(buf.text(), new_contents.to_string());
3071        assert!(!buf.is_dirty());
3072        assert!(!buf.has_conflict());
3073        assert_eq!(buf.line_ending(), LineEnding::Windows);
3074    });
3075}
3076
3077#[gpui::test(iterations = 10)]
3078async fn test_editing_while_guest_opens_buffer(
3079    deterministic: Arc<Deterministic>,
3080    cx_a: &mut TestAppContext,
3081    cx_b: &mut TestAppContext,
3082) {
3083    deterministic.forbid_parking();
3084    let mut server = TestServer::start(&deterministic).await;
3085    let client_a = server.create_client(cx_a, "user_a").await;
3086    let client_b = server.create_client(cx_b, "user_b").await;
3087    server
3088        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3089        .await;
3090    let active_call_a = cx_a.read(ActiveCall::global);
3091
3092    client_a
3093        .fs
3094        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3095        .await;
3096    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3097    let project_id = active_call_a
3098        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3099        .await
3100        .unwrap();
3101    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3102
3103    // Open a buffer as client A
3104    let buffer_a = project_a
3105        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3106        .await
3107        .unwrap();
3108
3109    // Start opening the same buffer as client B
3110    let buffer_b = cx_b
3111        .background()
3112        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)));
3113
3114    // Edit the buffer as client A while client B is still opening it.
3115    cx_b.background().simulate_random_delay().await;
3116    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "X")], None, cx));
3117    cx_b.background().simulate_random_delay().await;
3118    buffer_a.update(cx_a, |buf, cx| buf.edit([(1..1, "Y")], None, cx));
3119
3120    let text = buffer_a.read_with(cx_a, |buf, _| buf.text());
3121    let buffer_b = buffer_b.await.unwrap();
3122    cx_a.foreground().run_until_parked();
3123    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), text));
3124}
3125
3126#[gpui::test]
3127async fn test_newline_above_or_below_does_not_move_guest_cursor(
3128    deterministic: Arc<Deterministic>,
3129    cx_a: &mut TestAppContext,
3130    cx_b: &mut TestAppContext,
3131) {
3132    deterministic.forbid_parking();
3133    let mut server = TestServer::start(&deterministic).await;
3134    let client_a = server.create_client(cx_a, "user_a").await;
3135    let client_b = server.create_client(cx_b, "user_b").await;
3136    server
3137        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3138        .await;
3139    let active_call_a = cx_a.read(ActiveCall::global);
3140
3141    client_a
3142        .fs
3143        .insert_tree("/dir", json!({ "a.txt": "Some text\n" }))
3144        .await;
3145    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3146    let project_id = active_call_a
3147        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3148        .await
3149        .unwrap();
3150
3151    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3152
3153    // Open a buffer as client A
3154    let buffer_a = project_a
3155        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3156        .await
3157        .unwrap();
3158    let (window_a, _) = cx_a.add_window(|_| EmptyView);
3159    let editor_a = cx_a.add_view(window_a, |cx| {
3160        Editor::for_buffer(buffer_a, Some(project_a), cx)
3161    });
3162    let mut editor_cx_a = EditorTestContext {
3163        cx: cx_a,
3164        window_id: window_a,
3165        editor: editor_a,
3166    };
3167
3168    // Open a buffer as client B
3169    let buffer_b = project_b
3170        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3171        .await
3172        .unwrap();
3173    let (window_b, _) = cx_b.add_window(|_| EmptyView);
3174    let editor_b = cx_b.add_view(window_b, |cx| {
3175        Editor::for_buffer(buffer_b, Some(project_b), cx)
3176    });
3177    let mut editor_cx_b = EditorTestContext {
3178        cx: cx_b,
3179        window_id: window_b,
3180        editor: editor_b,
3181    };
3182
3183    // Test newline above
3184    editor_cx_a.set_selections_state(indoc! {"
3185        Some textˇ
3186    "});
3187    editor_cx_b.set_selections_state(indoc! {"
3188        Some textˇ
3189    "});
3190    editor_cx_a.update_editor(|editor, cx| editor.newline_above(&editor::NewlineAbove, cx));
3191    deterministic.run_until_parked();
3192    editor_cx_a.assert_editor_state(indoc! {"
3193        ˇ
3194        Some text
3195    "});
3196    editor_cx_b.assert_editor_state(indoc! {"
3197
3198        Some textˇ
3199    "});
3200
3201    // Test newline below
3202    editor_cx_a.set_selections_state(indoc! {"
3203
3204        Some textˇ
3205    "});
3206    editor_cx_b.set_selections_state(indoc! {"
3207
3208        Some textˇ
3209    "});
3210    editor_cx_a.update_editor(|editor, cx| editor.newline_below(&editor::NewlineBelow, cx));
3211    deterministic.run_until_parked();
3212    editor_cx_a.assert_editor_state(indoc! {"
3213
3214        Some text
3215        ˇ
3216    "});
3217    editor_cx_b.assert_editor_state(indoc! {"
3218
3219        Some textˇ
3220
3221    "});
3222}
3223
3224#[gpui::test(iterations = 10)]
3225async fn test_leaving_worktree_while_opening_buffer(
3226    deterministic: Arc<Deterministic>,
3227    cx_a: &mut TestAppContext,
3228    cx_b: &mut TestAppContext,
3229) {
3230    deterministic.forbid_parking();
3231    let mut server = TestServer::start(&deterministic).await;
3232    let client_a = server.create_client(cx_a, "user_a").await;
3233    let client_b = server.create_client(cx_b, "user_b").await;
3234    server
3235        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3236        .await;
3237    let active_call_a = cx_a.read(ActiveCall::global);
3238
3239    client_a
3240        .fs
3241        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3242        .await;
3243    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3244    let project_id = active_call_a
3245        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3246        .await
3247        .unwrap();
3248    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3249
3250    // See that a guest has joined as client A.
3251    cx_a.foreground().run_until_parked();
3252    project_a.read_with(cx_a, |p, _| assert_eq!(p.collaborators().len(), 1));
3253
3254    // Begin opening a buffer as client B, but leave the project before the open completes.
3255    let buffer_b = cx_b
3256        .background()
3257        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)));
3258    cx_b.update(|_| drop(project_b));
3259    drop(buffer_b);
3260
3261    // See that the guest has left.
3262    cx_a.foreground().run_until_parked();
3263    project_a.read_with(cx_a, |p, _| assert!(p.collaborators().is_empty()));
3264}
3265
3266#[gpui::test(iterations = 10)]
3267async fn test_canceling_buffer_opening(
3268    deterministic: Arc<Deterministic>,
3269    cx_a: &mut TestAppContext,
3270    cx_b: &mut TestAppContext,
3271) {
3272    deterministic.forbid_parking();
3273
3274    let mut server = TestServer::start(&deterministic).await;
3275    let client_a = server.create_client(cx_a, "user_a").await;
3276    let client_b = server.create_client(cx_b, "user_b").await;
3277    server
3278        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3279        .await;
3280    let active_call_a = cx_a.read(ActiveCall::global);
3281
3282    client_a
3283        .fs
3284        .insert_tree(
3285            "/dir",
3286            json!({
3287                "a.txt": "abc",
3288            }),
3289        )
3290        .await;
3291    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3292    let project_id = active_call_a
3293        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3294        .await
3295        .unwrap();
3296    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3297
3298    let buffer_a = project_a
3299        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3300        .await
3301        .unwrap();
3302
3303    // Open a buffer as client B but cancel after a random amount of time.
3304    let buffer_b = project_b.update(cx_b, |p, cx| {
3305        p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3306    });
3307    deterministic.simulate_random_delay().await;
3308    drop(buffer_b);
3309
3310    // Try opening the same buffer again as client B, and ensure we can
3311    // still do it despite the cancellation above.
3312    let buffer_b = project_b
3313        .update(cx_b, |p, cx| {
3314            p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx)
3315        })
3316        .await
3317        .unwrap();
3318    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "abc"));
3319}
3320
3321#[gpui::test(iterations = 10)]
3322async fn test_leaving_project(
3323    deterministic: Arc<Deterministic>,
3324    cx_a: &mut TestAppContext,
3325    cx_b: &mut TestAppContext,
3326    cx_c: &mut TestAppContext,
3327) {
3328    deterministic.forbid_parking();
3329    let mut server = TestServer::start(&deterministic).await;
3330    let client_a = server.create_client(cx_a, "user_a").await;
3331    let client_b = server.create_client(cx_b, "user_b").await;
3332    let client_c = server.create_client(cx_c, "user_c").await;
3333    server
3334        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3335        .await;
3336    let active_call_a = cx_a.read(ActiveCall::global);
3337
3338    client_a
3339        .fs
3340        .insert_tree(
3341            "/a",
3342            json!({
3343                "a.txt": "a-contents",
3344                "b.txt": "b-contents",
3345            }),
3346        )
3347        .await;
3348    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
3349    let project_id = active_call_a
3350        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3351        .await
3352        .unwrap();
3353    let project_b1 = client_b.build_remote_project(project_id, cx_b).await;
3354    let project_c = client_c.build_remote_project(project_id, cx_c).await;
3355
3356    // Client A sees that a guest has joined.
3357    deterministic.run_until_parked();
3358    project_a.read_with(cx_a, |project, _| {
3359        assert_eq!(project.collaborators().len(), 2);
3360    });
3361    project_b1.read_with(cx_b, |project, _| {
3362        assert_eq!(project.collaborators().len(), 2);
3363    });
3364    project_c.read_with(cx_c, |project, _| {
3365        assert_eq!(project.collaborators().len(), 2);
3366    });
3367
3368    // Client B opens a buffer.
3369    let buffer_b1 = project_b1
3370        .update(cx_b, |project, cx| {
3371            let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
3372            project.open_buffer((worktree_id, "a.txt"), cx)
3373        })
3374        .await
3375        .unwrap();
3376    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3377
3378    // Drop client B's project and ensure client A and client C observe client B leaving.
3379    cx_b.update(|_| drop(project_b1));
3380    deterministic.run_until_parked();
3381    project_a.read_with(cx_a, |project, _| {
3382        assert_eq!(project.collaborators().len(), 1);
3383    });
3384    project_c.read_with(cx_c, |project, _| {
3385        assert_eq!(project.collaborators().len(), 1);
3386    });
3387
3388    // Client B re-joins the project and can open buffers as before.
3389    let project_b2 = client_b.build_remote_project(project_id, cx_b).await;
3390    deterministic.run_until_parked();
3391    project_a.read_with(cx_a, |project, _| {
3392        assert_eq!(project.collaborators().len(), 2);
3393    });
3394    project_b2.read_with(cx_b, |project, _| {
3395        assert_eq!(project.collaborators().len(), 2);
3396    });
3397    project_c.read_with(cx_c, |project, _| {
3398        assert_eq!(project.collaborators().len(), 2);
3399    });
3400
3401    let buffer_b2 = project_b2
3402        .update(cx_b, |project, cx| {
3403            let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
3404            project.open_buffer((worktree_id, "a.txt"), cx)
3405        })
3406        .await
3407        .unwrap();
3408    buffer_b2.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3409
3410    // Drop client B's connection and ensure client A and client C observe client B leaving.
3411    client_b.disconnect(&cx_b.to_async());
3412    deterministic.advance_clock(RECONNECT_TIMEOUT);
3413    project_a.read_with(cx_a, |project, _| {
3414        assert_eq!(project.collaborators().len(), 1);
3415    });
3416    project_b2.read_with(cx_b, |project, _| {
3417        assert!(project.is_read_only());
3418    });
3419    project_c.read_with(cx_c, |project, _| {
3420        assert_eq!(project.collaborators().len(), 1);
3421    });
3422
3423    // Client B can't join the project, unless they re-join the room.
3424    cx_b.spawn(|cx| {
3425        Project::remote(
3426            project_id,
3427            client_b.client.clone(),
3428            client_b.user_store.clone(),
3429            client_b.language_registry.clone(),
3430            FakeFs::new(cx.background()),
3431            cx,
3432        )
3433    })
3434    .await
3435    .unwrap_err();
3436
3437    // Simulate connection loss for client C and ensure client A observes client C leaving the project.
3438    client_c.wait_for_current_user(cx_c).await;
3439    server.forbid_connections();
3440    server.disconnect_client(client_c.peer_id().unwrap());
3441    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
3442    deterministic.run_until_parked();
3443    project_a.read_with(cx_a, |project, _| {
3444        assert_eq!(project.collaborators().len(), 0);
3445    });
3446    project_b2.read_with(cx_b, |project, _| {
3447        assert!(project.is_read_only());
3448    });
3449    project_c.read_with(cx_c, |project, _| {
3450        assert!(project.is_read_only());
3451    });
3452}
3453
3454#[gpui::test(iterations = 10)]
3455async fn test_collaborating_with_diagnostics(
3456    deterministic: Arc<Deterministic>,
3457    cx_a: &mut TestAppContext,
3458    cx_b: &mut TestAppContext,
3459    cx_c: &mut TestAppContext,
3460) {
3461    deterministic.forbid_parking();
3462    let mut server = TestServer::start(&deterministic).await;
3463    let client_a = server.create_client(cx_a, "user_a").await;
3464    let client_b = server.create_client(cx_b, "user_b").await;
3465    let client_c = server.create_client(cx_c, "user_c").await;
3466    server
3467        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3468        .await;
3469    let active_call_a = cx_a.read(ActiveCall::global);
3470
3471    // Set up a fake language server.
3472    let mut language = Language::new(
3473        LanguageConfig {
3474            name: "Rust".into(),
3475            path_suffixes: vec!["rs".to_string()],
3476            ..Default::default()
3477        },
3478        Some(tree_sitter_rust::language()),
3479    );
3480    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
3481    client_a.language_registry.add(Arc::new(language));
3482
3483    // Share a project as client A
3484    client_a
3485        .fs
3486        .insert_tree(
3487            "/a",
3488            json!({
3489                "a.rs": "let one = two",
3490                "other.rs": "",
3491            }),
3492        )
3493        .await;
3494    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3495
3496    // Cause the language server to start.
3497    let _buffer = project_a
3498        .update(cx_a, |project, cx| {
3499            project.open_buffer(
3500                ProjectPath {
3501                    worktree_id,
3502                    path: Path::new("other.rs").into(),
3503                },
3504                cx,
3505            )
3506        })
3507        .await
3508        .unwrap();
3509
3510    // Simulate a language server reporting errors for a file.
3511    let mut fake_language_server = fake_language_servers.next().await.unwrap();
3512    fake_language_server
3513        .receive_notification::<lsp::notification::DidOpenTextDocument>()
3514        .await;
3515    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3516        lsp::PublishDiagnosticsParams {
3517            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3518            version: None,
3519            diagnostics: vec![lsp::Diagnostic {
3520                severity: Some(lsp::DiagnosticSeverity::WARNING),
3521                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3522                message: "message 0".to_string(),
3523                ..Default::default()
3524            }],
3525        },
3526    );
3527
3528    // Client A shares the project and, simultaneously, the language server
3529    // publishes a diagnostic. This is done to ensure that the server always
3530    // observes the latest diagnostics for a worktree.
3531    let project_id = active_call_a
3532        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3533        .await
3534        .unwrap();
3535    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3536        lsp::PublishDiagnosticsParams {
3537            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3538            version: None,
3539            diagnostics: vec![lsp::Diagnostic {
3540                severity: Some(lsp::DiagnosticSeverity::ERROR),
3541                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3542                message: "message 1".to_string(),
3543                ..Default::default()
3544            }],
3545        },
3546    );
3547
3548    // Join the worktree as client B.
3549    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3550
3551    // Wait for server to see the diagnostics update.
3552    deterministic.run_until_parked();
3553
3554    // Ensure client B observes the new diagnostics.
3555    project_b.read_with(cx_b, |project, cx| {
3556        assert_eq!(
3557            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
3558            &[(
3559                ProjectPath {
3560                    worktree_id,
3561                    path: Arc::from(Path::new("a.rs")),
3562                },
3563                LanguageServerId(0),
3564                DiagnosticSummary {
3565                    error_count: 1,
3566                    warning_count: 0,
3567                    ..Default::default()
3568                },
3569            )]
3570        )
3571    });
3572
3573    // Join project as client C and observe the diagnostics.
3574    let project_c = client_c.build_remote_project(project_id, cx_c).await;
3575    let project_c_diagnostic_summaries =
3576        Rc::new(RefCell::new(project_c.read_with(cx_c, |project, cx| {
3577            project.diagnostic_summaries(cx).collect::<Vec<_>>()
3578        })));
3579    project_c.update(cx_c, |_, cx| {
3580        let summaries = project_c_diagnostic_summaries.clone();
3581        cx.subscribe(&project_c, {
3582            move |p, _, event, cx| {
3583                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
3584                    *summaries.borrow_mut() = p.diagnostic_summaries(cx).collect();
3585                }
3586            }
3587        })
3588        .detach();
3589    });
3590
3591    deterministic.run_until_parked();
3592    assert_eq!(
3593        project_c_diagnostic_summaries.borrow().as_slice(),
3594        &[(
3595            ProjectPath {
3596                worktree_id,
3597                path: Arc::from(Path::new("a.rs")),
3598            },
3599            LanguageServerId(0),
3600            DiagnosticSummary {
3601                error_count: 1,
3602                warning_count: 0,
3603                ..Default::default()
3604            },
3605        )]
3606    );
3607
3608    // Simulate a language server reporting more errors for a file.
3609    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3610        lsp::PublishDiagnosticsParams {
3611            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3612            version: None,
3613            diagnostics: vec![
3614                lsp::Diagnostic {
3615                    severity: Some(lsp::DiagnosticSeverity::ERROR),
3616                    range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3617                    message: "message 1".to_string(),
3618                    ..Default::default()
3619                },
3620                lsp::Diagnostic {
3621                    severity: Some(lsp::DiagnosticSeverity::WARNING),
3622                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 13)),
3623                    message: "message 2".to_string(),
3624                    ..Default::default()
3625                },
3626            ],
3627        },
3628    );
3629
3630    // Clients B and C get the updated summaries
3631    deterministic.run_until_parked();
3632    project_b.read_with(cx_b, |project, cx| {
3633        assert_eq!(
3634            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
3635            [(
3636                ProjectPath {
3637                    worktree_id,
3638                    path: Arc::from(Path::new("a.rs")),
3639                },
3640                LanguageServerId(0),
3641                DiagnosticSummary {
3642                    error_count: 1,
3643                    warning_count: 1,
3644                },
3645            )]
3646        );
3647    });
3648    project_c.read_with(cx_c, |project, cx| {
3649        assert_eq!(
3650            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
3651            [(
3652                ProjectPath {
3653                    worktree_id,
3654                    path: Arc::from(Path::new("a.rs")),
3655                },
3656                LanguageServerId(0),
3657                DiagnosticSummary {
3658                    error_count: 1,
3659                    warning_count: 1,
3660                },
3661            )]
3662        );
3663    });
3664
3665    // Open the file with the errors on client B. They should be present.
3666    let buffer_b = cx_b
3667        .background()
3668        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
3669        .await
3670        .unwrap();
3671
3672    buffer_b.read_with(cx_b, |buffer, _| {
3673        assert_eq!(
3674            buffer
3675                .snapshot()
3676                .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
3677                .collect::<Vec<_>>(),
3678            &[
3679                DiagnosticEntry {
3680                    range: Point::new(0, 4)..Point::new(0, 7),
3681                    diagnostic: Diagnostic {
3682                        group_id: 2,
3683                        message: "message 1".to_string(),
3684                        severity: lsp::DiagnosticSeverity::ERROR,
3685                        is_primary: true,
3686                        ..Default::default()
3687                    }
3688                },
3689                DiagnosticEntry {
3690                    range: Point::new(0, 10)..Point::new(0, 13),
3691                    diagnostic: Diagnostic {
3692                        group_id: 3,
3693                        severity: lsp::DiagnosticSeverity::WARNING,
3694                        message: "message 2".to_string(),
3695                        is_primary: true,
3696                        ..Default::default()
3697                    }
3698                }
3699            ]
3700        );
3701    });
3702
3703    // Simulate a language server reporting no errors for a file.
3704    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3705        lsp::PublishDiagnosticsParams {
3706            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3707            version: None,
3708            diagnostics: vec![],
3709        },
3710    );
3711    deterministic.run_until_parked();
3712    project_a.read_with(cx_a, |project, cx| {
3713        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
3714    });
3715    project_b.read_with(cx_b, |project, cx| {
3716        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
3717    });
3718    project_c.read_with(cx_c, |project, cx| {
3719        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
3720    });
3721}
3722
3723#[gpui::test(iterations = 10)]
3724async fn test_collaborating_with_lsp_progress_updates_and_diagnostics_ordering(
3725    deterministic: Arc<Deterministic>,
3726    cx_a: &mut TestAppContext,
3727    cx_b: &mut TestAppContext,
3728) {
3729    deterministic.forbid_parking();
3730    let mut server = TestServer::start(&deterministic).await;
3731    let client_a = server.create_client(cx_a, "user_a").await;
3732    let client_b = server.create_client(cx_b, "user_b").await;
3733    server
3734        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3735        .await;
3736
3737    // Set up a fake language server.
3738    let mut language = Language::new(
3739        LanguageConfig {
3740            name: "Rust".into(),
3741            path_suffixes: vec!["rs".to_string()],
3742            ..Default::default()
3743        },
3744        Some(tree_sitter_rust::language()),
3745    );
3746    let mut fake_language_servers = language
3747        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3748            disk_based_diagnostics_progress_token: Some("the-disk-based-token".into()),
3749            disk_based_diagnostics_sources: vec!["the-disk-based-diagnostics-source".into()],
3750            ..Default::default()
3751        }))
3752        .await;
3753    client_a.language_registry.add(Arc::new(language));
3754
3755    let file_names = &["one.rs", "two.rs", "three.rs", "four.rs", "five.rs"];
3756    client_a
3757        .fs
3758        .insert_tree(
3759            "/test",
3760            json!({
3761                "one.rs": "const ONE: usize = 1;",
3762                "two.rs": "const TWO: usize = 2;",
3763                "three.rs": "const THREE: usize = 3;",
3764                "four.rs": "const FOUR: usize = 3;",
3765                "five.rs": "const FIVE: usize = 3;",
3766            }),
3767        )
3768        .await;
3769
3770    let (project_a, worktree_id) = client_a.build_local_project("/test", cx_a).await;
3771
3772    // Share a project as client A
3773    let active_call_a = cx_a.read(ActiveCall::global);
3774    let project_id = active_call_a
3775        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3776        .await
3777        .unwrap();
3778
3779    // Join the project as client B and open all three files.
3780    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3781    let guest_buffers = futures::future::try_join_all(file_names.iter().map(|file_name| {
3782        project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, file_name), cx))
3783    }))
3784    .await
3785    .unwrap();
3786
3787    // Simulate a language server reporting errors for a file.
3788    let fake_language_server = fake_language_servers.next().await.unwrap();
3789    fake_language_server
3790        .request::<lsp::request::WorkDoneProgressCreate>(lsp::WorkDoneProgressCreateParams {
3791            token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
3792        })
3793        .await
3794        .unwrap();
3795    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
3796        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
3797        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin(
3798            lsp::WorkDoneProgressBegin {
3799                title: "Progress Began".into(),
3800                ..Default::default()
3801            },
3802        )),
3803    });
3804    for file_name in file_names {
3805        fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3806            lsp::PublishDiagnosticsParams {
3807                uri: lsp::Url::from_file_path(Path::new("/test").join(file_name)).unwrap(),
3808                version: None,
3809                diagnostics: vec![lsp::Diagnostic {
3810                    severity: Some(lsp::DiagnosticSeverity::WARNING),
3811                    source: Some("the-disk-based-diagnostics-source".into()),
3812                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
3813                    message: "message one".to_string(),
3814                    ..Default::default()
3815                }],
3816            },
3817        );
3818    }
3819    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
3820        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
3821        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End(
3822            lsp::WorkDoneProgressEnd { message: None },
3823        )),
3824    });
3825
3826    // When the "disk base diagnostics finished" message is received, the buffers'
3827    // diagnostics are expected to be present.
3828    let disk_based_diagnostics_finished = Arc::new(AtomicBool::new(false));
3829    project_b.update(cx_b, {
3830        let project_b = project_b.clone();
3831        let disk_based_diagnostics_finished = disk_based_diagnostics_finished.clone();
3832        move |_, cx| {
3833            cx.subscribe(&project_b, move |_, _, event, cx| {
3834                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
3835                    disk_based_diagnostics_finished.store(true, SeqCst);
3836                    for buffer in &guest_buffers {
3837                        assert_eq!(
3838                            buffer
3839                                .read(cx)
3840                                .snapshot()
3841                                .diagnostics_in_range::<_, usize>(0..5, false)
3842                                .count(),
3843                            1,
3844                            "expected a diagnostic for buffer {:?}",
3845                            buffer.read(cx).file().unwrap().path(),
3846                        );
3847                    }
3848                }
3849            })
3850            .detach();
3851        }
3852    });
3853
3854    deterministic.run_until_parked();
3855    assert!(disk_based_diagnostics_finished.load(SeqCst));
3856}
3857
3858#[gpui::test(iterations = 10)]
3859async fn test_collaborating_with_completion(
3860    deterministic: Arc<Deterministic>,
3861    cx_a: &mut TestAppContext,
3862    cx_b: &mut TestAppContext,
3863) {
3864    deterministic.forbid_parking();
3865    let mut server = TestServer::start(&deterministic).await;
3866    let client_a = server.create_client(cx_a, "user_a").await;
3867    let client_b = server.create_client(cx_b, "user_b").await;
3868    server
3869        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3870        .await;
3871    let active_call_a = cx_a.read(ActiveCall::global);
3872
3873    // Set up a fake language server.
3874    let mut language = Language::new(
3875        LanguageConfig {
3876            name: "Rust".into(),
3877            path_suffixes: vec!["rs".to_string()],
3878            ..Default::default()
3879        },
3880        Some(tree_sitter_rust::language()),
3881    );
3882    let mut fake_language_servers = language
3883        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3884            capabilities: lsp::ServerCapabilities {
3885                completion_provider: Some(lsp::CompletionOptions {
3886                    trigger_characters: Some(vec![".".to_string()]),
3887                    ..Default::default()
3888                }),
3889                ..Default::default()
3890            },
3891            ..Default::default()
3892        }))
3893        .await;
3894    client_a.language_registry.add(Arc::new(language));
3895
3896    client_a
3897        .fs
3898        .insert_tree(
3899            "/a",
3900            json!({
3901                "main.rs": "fn main() { a }",
3902                "other.rs": "",
3903            }),
3904        )
3905        .await;
3906    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3907    let project_id = active_call_a
3908        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3909        .await
3910        .unwrap();
3911    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3912
3913    // Open a file in an editor as the guest.
3914    let buffer_b = project_b
3915        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
3916        .await
3917        .unwrap();
3918    let (window_b, _) = cx_b.add_window(|_| EmptyView);
3919    let editor_b = cx_b.add_view(window_b, |cx| {
3920        Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx)
3921    });
3922
3923    let fake_language_server = fake_language_servers.next().await.unwrap();
3924    cx_a.foreground().run_until_parked();
3925    buffer_b.read_with(cx_b, |buffer, _| {
3926        assert!(!buffer.completion_triggers().is_empty())
3927    });
3928
3929    // Type a completion trigger character as the guest.
3930    editor_b.update(cx_b, |editor, cx| {
3931        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
3932        editor.handle_input(".", cx);
3933        cx.focus(&editor_b);
3934    });
3935
3936    // Receive a completion request as the host's language server.
3937    // Return some completions from the host's language server.
3938    cx_a.foreground().start_waiting();
3939    fake_language_server
3940        .handle_request::<lsp::request::Completion, _, _>(|params, _| async move {
3941            assert_eq!(
3942                params.text_document_position.text_document.uri,
3943                lsp::Url::from_file_path("/a/main.rs").unwrap(),
3944            );
3945            assert_eq!(
3946                params.text_document_position.position,
3947                lsp::Position::new(0, 14),
3948            );
3949
3950            Ok(Some(lsp::CompletionResponse::Array(vec![
3951                lsp::CompletionItem {
3952                    label: "first_method(…)".into(),
3953                    detail: Some("fn(&mut self, B) -> C".into()),
3954                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
3955                        new_text: "first_method($1)".to_string(),
3956                        range: lsp::Range::new(
3957                            lsp::Position::new(0, 14),
3958                            lsp::Position::new(0, 14),
3959                        ),
3960                    })),
3961                    insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
3962                    ..Default::default()
3963                },
3964                lsp::CompletionItem {
3965                    label: "second_method(…)".into(),
3966                    detail: Some("fn(&mut self, C) -> D<E>".into()),
3967                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
3968                        new_text: "second_method()".to_string(),
3969                        range: lsp::Range::new(
3970                            lsp::Position::new(0, 14),
3971                            lsp::Position::new(0, 14),
3972                        ),
3973                    })),
3974                    insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
3975                    ..Default::default()
3976                },
3977            ])))
3978        })
3979        .next()
3980        .await
3981        .unwrap();
3982    cx_a.foreground().finish_waiting();
3983
3984    // Open the buffer on the host.
3985    let buffer_a = project_a
3986        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
3987        .await
3988        .unwrap();
3989    cx_a.foreground().run_until_parked();
3990    buffer_a.read_with(cx_a, |buffer, _| {
3991        assert_eq!(buffer.text(), "fn main() { a. }")
3992    });
3993
3994    // Confirm a completion on the guest.
3995    editor_b.read_with(cx_b, |editor, _| assert!(editor.context_menu_visible()));
3996    editor_b.update(cx_b, |editor, cx| {
3997        editor.confirm_completion(&ConfirmCompletion { item_ix: Some(0) }, cx);
3998        assert_eq!(editor.text(cx), "fn main() { a.first_method() }");
3999    });
4000
4001    // Return a resolved completion from the host's language server.
4002    // The resolved completion has an additional text edit.
4003    fake_language_server.handle_request::<lsp::request::ResolveCompletionItem, _, _>(
4004        |params, _| async move {
4005            assert_eq!(params.label, "first_method(…)");
4006            Ok(lsp::CompletionItem {
4007                label: "first_method(…)".into(),
4008                detail: Some("fn(&mut self, B) -> C".into()),
4009                text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
4010                    new_text: "first_method($1)".to_string(),
4011                    range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
4012                })),
4013                additional_text_edits: Some(vec![lsp::TextEdit {
4014                    new_text: "use d::SomeTrait;\n".to_string(),
4015                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
4016                }]),
4017                insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
4018                ..Default::default()
4019            })
4020        },
4021    );
4022
4023    // The additional edit is applied.
4024    cx_a.foreground().run_until_parked();
4025    buffer_a.read_with(cx_a, |buffer, _| {
4026        assert_eq!(
4027            buffer.text(),
4028            "use d::SomeTrait;\nfn main() { a.first_method() }"
4029        );
4030    });
4031    buffer_b.read_with(cx_b, |buffer, _| {
4032        assert_eq!(
4033            buffer.text(),
4034            "use d::SomeTrait;\nfn main() { a.first_method() }"
4035        );
4036    });
4037}
4038
4039#[gpui::test(iterations = 10)]
4040async fn test_reloading_buffer_manually(
4041    deterministic: Arc<Deterministic>,
4042    cx_a: &mut TestAppContext,
4043    cx_b: &mut TestAppContext,
4044) {
4045    deterministic.forbid_parking();
4046    let mut server = TestServer::start(&deterministic).await;
4047    let client_a = server.create_client(cx_a, "user_a").await;
4048    let client_b = server.create_client(cx_b, "user_b").await;
4049    server
4050        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4051        .await;
4052    let active_call_a = cx_a.read(ActiveCall::global);
4053
4054    client_a
4055        .fs
4056        .insert_tree("/a", json!({ "a.rs": "let one = 1;" }))
4057        .await;
4058    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
4059    let buffer_a = project_a
4060        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
4061        .await
4062        .unwrap();
4063    let project_id = active_call_a
4064        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4065        .await
4066        .unwrap();
4067
4068    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4069
4070    let buffer_b = cx_b
4071        .background()
4072        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4073        .await
4074        .unwrap();
4075    buffer_b.update(cx_b, |buffer, cx| {
4076        buffer.edit([(4..7, "six")], None, cx);
4077        buffer.edit([(10..11, "6")], None, cx);
4078        assert_eq!(buffer.text(), "let six = 6;");
4079        assert!(buffer.is_dirty());
4080        assert!(!buffer.has_conflict());
4081    });
4082    cx_a.foreground().run_until_parked();
4083    buffer_a.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "let six = 6;"));
4084
4085    client_a
4086        .fs
4087        .save(
4088            "/a/a.rs".as_ref(),
4089            &Rope::from("let seven = 7;"),
4090            LineEnding::Unix,
4091        )
4092        .await
4093        .unwrap();
4094    cx_a.foreground().run_until_parked();
4095    buffer_a.read_with(cx_a, |buffer, _| assert!(buffer.has_conflict()));
4096    buffer_b.read_with(cx_b, |buffer, _| assert!(buffer.has_conflict()));
4097
4098    project_b
4099        .update(cx_b, |project, cx| {
4100            project.reload_buffers(HashSet::from_iter([buffer_b.clone()]), true, cx)
4101        })
4102        .await
4103        .unwrap();
4104    buffer_a.read_with(cx_a, |buffer, _| {
4105        assert_eq!(buffer.text(), "let seven = 7;");
4106        assert!(!buffer.is_dirty());
4107        assert!(!buffer.has_conflict());
4108    });
4109    buffer_b.read_with(cx_b, |buffer, _| {
4110        assert_eq!(buffer.text(), "let seven = 7;");
4111        assert!(!buffer.is_dirty());
4112        assert!(!buffer.has_conflict());
4113    });
4114
4115    buffer_a.update(cx_a, |buffer, cx| {
4116        // Undoing on the host is a no-op when the reload was initiated by the guest.
4117        buffer.undo(cx);
4118        assert_eq!(buffer.text(), "let seven = 7;");
4119        assert!(!buffer.is_dirty());
4120        assert!(!buffer.has_conflict());
4121    });
4122    buffer_b.update(cx_b, |buffer, cx| {
4123        // Undoing on the guest rolls back the buffer to before it was reloaded but the conflict gets cleared.
4124        buffer.undo(cx);
4125        assert_eq!(buffer.text(), "let six = 6;");
4126        assert!(buffer.is_dirty());
4127        assert!(!buffer.has_conflict());
4128    });
4129}
4130
4131#[gpui::test(iterations = 10)]
4132async fn test_formatting_buffer(
4133    deterministic: Arc<Deterministic>,
4134    cx_a: &mut TestAppContext,
4135    cx_b: &mut TestAppContext,
4136) {
4137    use project::FormatTrigger;
4138
4139    let mut server = TestServer::start(&deterministic).await;
4140    let client_a = server.create_client(cx_a, "user_a").await;
4141    let client_b = server.create_client(cx_b, "user_b").await;
4142    server
4143        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4144        .await;
4145    let active_call_a = cx_a.read(ActiveCall::global);
4146
4147    // Set up a fake language server.
4148    let mut language = Language::new(
4149        LanguageConfig {
4150            name: "Rust".into(),
4151            path_suffixes: vec!["rs".to_string()],
4152            ..Default::default()
4153        },
4154        Some(tree_sitter_rust::language()),
4155    );
4156    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4157    client_a.language_registry.add(Arc::new(language));
4158
4159    // Here we insert a fake tree with a directory that exists on disk. This is needed
4160    // because later we'll invoke a command, which requires passing a working directory
4161    // that points to a valid location on disk.
4162    let directory = env::current_dir().unwrap();
4163    client_a
4164        .fs
4165        .insert_tree(&directory, json!({ "a.rs": "let one = \"two\"" }))
4166        .await;
4167    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
4168    let project_id = active_call_a
4169        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4170        .await
4171        .unwrap();
4172    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4173
4174    let buffer_b = cx_b
4175        .background()
4176        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4177        .await
4178        .unwrap();
4179
4180    let fake_language_server = fake_language_servers.next().await.unwrap();
4181    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
4182        Ok(Some(vec![
4183            lsp::TextEdit {
4184                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 4)),
4185                new_text: "h".to_string(),
4186            },
4187            lsp::TextEdit {
4188                range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 7)),
4189                new_text: "y".to_string(),
4190            },
4191        ]))
4192    });
4193
4194    project_b
4195        .update(cx_b, |project, cx| {
4196            project.format(
4197                HashSet::from_iter([buffer_b.clone()]),
4198                true,
4199                FormatTrigger::Save,
4200                cx,
4201            )
4202        })
4203        .await
4204        .unwrap();
4205
4206    // The edits from the LSP are applied, and a final newline is added.
4207    assert_eq!(
4208        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4209        "let honey = \"two\"\n"
4210    );
4211
4212    // Ensure buffer can be formatted using an external command. Notice how the
4213    // host's configuration is honored as opposed to using the guest's settings.
4214    cx_a.update(|cx| {
4215        cx.update_global(|settings: &mut Settings, _| {
4216            settings.editor_defaults.formatter = Some(Formatter::External {
4217                command: "awk".to_string(),
4218                arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()],
4219            });
4220        });
4221    });
4222    project_b
4223        .update(cx_b, |project, cx| {
4224            project.format(
4225                HashSet::from_iter([buffer_b.clone()]),
4226                true,
4227                FormatTrigger::Save,
4228                cx,
4229            )
4230        })
4231        .await
4232        .unwrap();
4233    assert_eq!(
4234        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4235        format!("let honey = \"{}/a.rs\"\n", directory.to_str().unwrap())
4236    );
4237}
4238
4239#[gpui::test(iterations = 10)]
4240async fn test_definition(
4241    deterministic: Arc<Deterministic>,
4242    cx_a: &mut TestAppContext,
4243    cx_b: &mut TestAppContext,
4244) {
4245    deterministic.forbid_parking();
4246    let mut server = TestServer::start(&deterministic).await;
4247    let client_a = server.create_client(cx_a, "user_a").await;
4248    let client_b = server.create_client(cx_b, "user_b").await;
4249    server
4250        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4251        .await;
4252    let active_call_a = cx_a.read(ActiveCall::global);
4253
4254    // Set up a fake language server.
4255    let mut language = Language::new(
4256        LanguageConfig {
4257            name: "Rust".into(),
4258            path_suffixes: vec!["rs".to_string()],
4259            ..Default::default()
4260        },
4261        Some(tree_sitter_rust::language()),
4262    );
4263    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4264    client_a.language_registry.add(Arc::new(language));
4265
4266    client_a
4267        .fs
4268        .insert_tree(
4269            "/root",
4270            json!({
4271                "dir-1": {
4272                    "a.rs": "const ONE: usize = b::TWO + b::THREE;",
4273                },
4274                "dir-2": {
4275                    "b.rs": "const TWO: c::T2 = 2;\nconst THREE: usize = 3;",
4276                    "c.rs": "type T2 = usize;",
4277                }
4278            }),
4279        )
4280        .await;
4281    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4282    let project_id = active_call_a
4283        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4284        .await
4285        .unwrap();
4286    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4287
4288    // Open the file on client B.
4289    let buffer_b = cx_b
4290        .background()
4291        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4292        .await
4293        .unwrap();
4294
4295    // Request the definition of a symbol as the guest.
4296    let fake_language_server = fake_language_servers.next().await.unwrap();
4297    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4298        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4299            lsp::Location::new(
4300                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4301                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4302            ),
4303        )))
4304    });
4305
4306    let definitions_1 = project_b
4307        .update(cx_b, |p, cx| p.definition(&buffer_b, 23, cx))
4308        .await
4309        .unwrap();
4310    cx_b.read(|cx| {
4311        assert_eq!(definitions_1.len(), 1);
4312        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4313        let target_buffer = definitions_1[0].target.buffer.read(cx);
4314        assert_eq!(
4315            target_buffer.text(),
4316            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4317        );
4318        assert_eq!(
4319            definitions_1[0].target.range.to_point(target_buffer),
4320            Point::new(0, 6)..Point::new(0, 9)
4321        );
4322    });
4323
4324    // Try getting more definitions for the same buffer, ensuring the buffer gets reused from
4325    // the previous call to `definition`.
4326    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4327        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4328            lsp::Location::new(
4329                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4330                lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)),
4331            ),
4332        )))
4333    });
4334
4335    let definitions_2 = project_b
4336        .update(cx_b, |p, cx| p.definition(&buffer_b, 33, cx))
4337        .await
4338        .unwrap();
4339    cx_b.read(|cx| {
4340        assert_eq!(definitions_2.len(), 1);
4341        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4342        let target_buffer = definitions_2[0].target.buffer.read(cx);
4343        assert_eq!(
4344            target_buffer.text(),
4345            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4346        );
4347        assert_eq!(
4348            definitions_2[0].target.range.to_point(target_buffer),
4349            Point::new(1, 6)..Point::new(1, 11)
4350        );
4351    });
4352    assert_eq!(
4353        definitions_1[0].target.buffer,
4354        definitions_2[0].target.buffer
4355    );
4356
4357    fake_language_server.handle_request::<lsp::request::GotoTypeDefinition, _, _>(
4358        |req, _| async move {
4359            assert_eq!(
4360                req.text_document_position_params.position,
4361                lsp::Position::new(0, 7)
4362            );
4363            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4364                lsp::Location::new(
4365                    lsp::Url::from_file_path("/root/dir-2/c.rs").unwrap(),
4366                    lsp::Range::new(lsp::Position::new(0, 5), lsp::Position::new(0, 7)),
4367                ),
4368            )))
4369        },
4370    );
4371
4372    let type_definitions = project_b
4373        .update(cx_b, |p, cx| p.type_definition(&buffer_b, 7, cx))
4374        .await
4375        .unwrap();
4376    cx_b.read(|cx| {
4377        assert_eq!(type_definitions.len(), 1);
4378        let target_buffer = type_definitions[0].target.buffer.read(cx);
4379        assert_eq!(target_buffer.text(), "type T2 = usize;");
4380        assert_eq!(
4381            type_definitions[0].target.range.to_point(target_buffer),
4382            Point::new(0, 5)..Point::new(0, 7)
4383        );
4384    });
4385}
4386
4387#[gpui::test(iterations = 10)]
4388async fn test_references(
4389    deterministic: Arc<Deterministic>,
4390    cx_a: &mut TestAppContext,
4391    cx_b: &mut TestAppContext,
4392) {
4393    deterministic.forbid_parking();
4394    let mut server = TestServer::start(&deterministic).await;
4395    let client_a = server.create_client(cx_a, "user_a").await;
4396    let client_b = server.create_client(cx_b, "user_b").await;
4397    server
4398        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4399        .await;
4400    let active_call_a = cx_a.read(ActiveCall::global);
4401
4402    // Set up a fake language server.
4403    let mut language = Language::new(
4404        LanguageConfig {
4405            name: "Rust".into(),
4406            path_suffixes: vec!["rs".to_string()],
4407            ..Default::default()
4408        },
4409        Some(tree_sitter_rust::language()),
4410    );
4411    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4412    client_a.language_registry.add(Arc::new(language));
4413
4414    client_a
4415        .fs
4416        .insert_tree(
4417            "/root",
4418            json!({
4419                "dir-1": {
4420                    "one.rs": "const ONE: usize = 1;",
4421                    "two.rs": "const TWO: usize = one::ONE + one::ONE;",
4422                },
4423                "dir-2": {
4424                    "three.rs": "const THREE: usize = two::TWO + one::ONE;",
4425                }
4426            }),
4427        )
4428        .await;
4429    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4430    let project_id = active_call_a
4431        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4432        .await
4433        .unwrap();
4434    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4435
4436    // Open the file on client B.
4437    let buffer_b = cx_b
4438        .background()
4439        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
4440        .await
4441        .unwrap();
4442
4443    // Request references to a symbol as the guest.
4444    let fake_language_server = fake_language_servers.next().await.unwrap();
4445    fake_language_server.handle_request::<lsp::request::References, _, _>(|params, _| async move {
4446        assert_eq!(
4447            params.text_document_position.text_document.uri.as_str(),
4448            "file:///root/dir-1/one.rs"
4449        );
4450        Ok(Some(vec![
4451            lsp::Location {
4452                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4453                range: lsp::Range::new(lsp::Position::new(0, 24), lsp::Position::new(0, 27)),
4454            },
4455            lsp::Location {
4456                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4457                range: lsp::Range::new(lsp::Position::new(0, 35), lsp::Position::new(0, 38)),
4458            },
4459            lsp::Location {
4460                uri: lsp::Url::from_file_path("/root/dir-2/three.rs").unwrap(),
4461                range: lsp::Range::new(lsp::Position::new(0, 37), lsp::Position::new(0, 40)),
4462            },
4463        ]))
4464    });
4465
4466    let references = project_b
4467        .update(cx_b, |p, cx| p.references(&buffer_b, 7, cx))
4468        .await
4469        .unwrap();
4470    cx_b.read(|cx| {
4471        assert_eq!(references.len(), 3);
4472        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4473
4474        let two_buffer = references[0].buffer.read(cx);
4475        let three_buffer = references[2].buffer.read(cx);
4476        assert_eq!(
4477            two_buffer.file().unwrap().path().as_ref(),
4478            Path::new("two.rs")
4479        );
4480        assert_eq!(references[1].buffer, references[0].buffer);
4481        assert_eq!(
4482            three_buffer.file().unwrap().full_path(cx),
4483            Path::new("/root/dir-2/three.rs")
4484        );
4485
4486        assert_eq!(references[0].range.to_offset(two_buffer), 24..27);
4487        assert_eq!(references[1].range.to_offset(two_buffer), 35..38);
4488        assert_eq!(references[2].range.to_offset(three_buffer), 37..40);
4489    });
4490}
4491
4492#[gpui::test(iterations = 10)]
4493async fn test_project_search(
4494    deterministic: Arc<Deterministic>,
4495    cx_a: &mut TestAppContext,
4496    cx_b: &mut TestAppContext,
4497) {
4498    deterministic.forbid_parking();
4499    let mut server = TestServer::start(&deterministic).await;
4500    let client_a = server.create_client(cx_a, "user_a").await;
4501    let client_b = server.create_client(cx_b, "user_b").await;
4502    server
4503        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4504        .await;
4505    let active_call_a = cx_a.read(ActiveCall::global);
4506
4507    client_a
4508        .fs
4509        .insert_tree(
4510            "/root",
4511            json!({
4512                "dir-1": {
4513                    "a": "hello world",
4514                    "b": "goodnight moon",
4515                    "c": "a world of goo",
4516                    "d": "world champion of clown world",
4517                },
4518                "dir-2": {
4519                    "e": "disney world is fun",
4520                }
4521            }),
4522        )
4523        .await;
4524    let (project_a, _) = client_a.build_local_project("/root/dir-1", cx_a).await;
4525    let (worktree_2, _) = project_a
4526        .update(cx_a, |p, cx| {
4527            p.find_or_create_local_worktree("/root/dir-2", true, cx)
4528        })
4529        .await
4530        .unwrap();
4531    worktree_2
4532        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
4533        .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
4539    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4540
4541    // Perform a search as the guest.
4542    let results = project_b
4543        .update(cx_b, |project, cx| {
4544            project.search(SearchQuery::text("world", false, false), cx)
4545        })
4546        .await
4547        .unwrap();
4548
4549    let mut ranges_by_path = results
4550        .into_iter()
4551        .map(|(buffer, ranges)| {
4552            buffer.read_with(cx_b, |buffer, cx| {
4553                let path = buffer.file().unwrap().full_path(cx);
4554                let offset_ranges = ranges
4555                    .into_iter()
4556                    .map(|range| range.to_offset(buffer))
4557                    .collect::<Vec<_>>();
4558                (path, offset_ranges)
4559            })
4560        })
4561        .collect::<Vec<_>>();
4562    ranges_by_path.sort_by_key(|(path, _)| path.clone());
4563
4564    assert_eq!(
4565        ranges_by_path,
4566        &[
4567            (PathBuf::from("dir-1/a"), vec![6..11]),
4568            (PathBuf::from("dir-1/c"), vec![2..7]),
4569            (PathBuf::from("dir-1/d"), vec![0..5, 24..29]),
4570            (PathBuf::from("dir-2/e"), vec![7..12]),
4571        ]
4572    );
4573}
4574
4575#[gpui::test(iterations = 10)]
4576async fn test_document_highlights(
4577    deterministic: Arc<Deterministic>,
4578    cx_a: &mut TestAppContext,
4579    cx_b: &mut TestAppContext,
4580) {
4581    deterministic.forbid_parking();
4582    let mut server = TestServer::start(&deterministic).await;
4583    let client_a = server.create_client(cx_a, "user_a").await;
4584    let client_b = server.create_client(cx_b, "user_b").await;
4585    server
4586        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4587        .await;
4588    let active_call_a = cx_a.read(ActiveCall::global);
4589
4590    client_a
4591        .fs
4592        .insert_tree(
4593            "/root-1",
4594            json!({
4595                "main.rs": "fn double(number: i32) -> i32 { number + number }",
4596            }),
4597        )
4598        .await;
4599
4600    // Set up a fake language server.
4601    let mut language = Language::new(
4602        LanguageConfig {
4603            name: "Rust".into(),
4604            path_suffixes: vec!["rs".to_string()],
4605            ..Default::default()
4606        },
4607        Some(tree_sitter_rust::language()),
4608    );
4609    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4610    client_a.language_registry.add(Arc::new(language));
4611
4612    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4613    let project_id = active_call_a
4614        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4615        .await
4616        .unwrap();
4617    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4618
4619    // Open the file on client B.
4620    let buffer_b = cx_b
4621        .background()
4622        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
4623        .await
4624        .unwrap();
4625
4626    // Request document highlights as the guest.
4627    let fake_language_server = fake_language_servers.next().await.unwrap();
4628    fake_language_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
4629        |params, _| async move {
4630            assert_eq!(
4631                params
4632                    .text_document_position_params
4633                    .text_document
4634                    .uri
4635                    .as_str(),
4636                "file:///root-1/main.rs"
4637            );
4638            assert_eq!(
4639                params.text_document_position_params.position,
4640                lsp::Position::new(0, 34)
4641            );
4642            Ok(Some(vec![
4643                lsp::DocumentHighlight {
4644                    kind: Some(lsp::DocumentHighlightKind::WRITE),
4645                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 16)),
4646                },
4647                lsp::DocumentHighlight {
4648                    kind: Some(lsp::DocumentHighlightKind::READ),
4649                    range: lsp::Range::new(lsp::Position::new(0, 32), lsp::Position::new(0, 38)),
4650                },
4651                lsp::DocumentHighlight {
4652                    kind: Some(lsp::DocumentHighlightKind::READ),
4653                    range: lsp::Range::new(lsp::Position::new(0, 41), lsp::Position::new(0, 47)),
4654                },
4655            ]))
4656        },
4657    );
4658
4659    let highlights = project_b
4660        .update(cx_b, |p, cx| p.document_highlights(&buffer_b, 34, cx))
4661        .await
4662        .unwrap();
4663    buffer_b.read_with(cx_b, |buffer, _| {
4664        let snapshot = buffer.snapshot();
4665
4666        let highlights = highlights
4667            .into_iter()
4668            .map(|highlight| (highlight.kind, highlight.range.to_offset(&snapshot)))
4669            .collect::<Vec<_>>();
4670        assert_eq!(
4671            highlights,
4672            &[
4673                (lsp::DocumentHighlightKind::WRITE, 10..16),
4674                (lsp::DocumentHighlightKind::READ, 32..38),
4675                (lsp::DocumentHighlightKind::READ, 41..47)
4676            ]
4677        )
4678    });
4679}
4680
4681#[gpui::test(iterations = 10)]
4682async fn test_lsp_hover(
4683    deterministic: Arc<Deterministic>,
4684    cx_a: &mut TestAppContext,
4685    cx_b: &mut TestAppContext,
4686) {
4687    deterministic.forbid_parking();
4688    let mut server = TestServer::start(&deterministic).await;
4689    let client_a = server.create_client(cx_a, "user_a").await;
4690    let client_b = server.create_client(cx_b, "user_b").await;
4691    server
4692        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4693        .await;
4694    let active_call_a = cx_a.read(ActiveCall::global);
4695
4696    client_a
4697        .fs
4698        .insert_tree(
4699            "/root-1",
4700            json!({
4701                "main.rs": "use std::collections::HashMap;",
4702            }),
4703        )
4704        .await;
4705
4706    // Set up a fake language server.
4707    let mut language = Language::new(
4708        LanguageConfig {
4709            name: "Rust".into(),
4710            path_suffixes: vec!["rs".to_string()],
4711            ..Default::default()
4712        },
4713        Some(tree_sitter_rust::language()),
4714    );
4715    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4716    client_a.language_registry.add(Arc::new(language));
4717
4718    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4719    let project_id = active_call_a
4720        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4721        .await
4722        .unwrap();
4723    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4724
4725    // Open the file as the guest
4726    let buffer_b = cx_b
4727        .background()
4728        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
4729        .await
4730        .unwrap();
4731
4732    // Request hover information as the guest.
4733    let fake_language_server = fake_language_servers.next().await.unwrap();
4734    fake_language_server.handle_request::<lsp::request::HoverRequest, _, _>(
4735        |params, _| async move {
4736            assert_eq!(
4737                params
4738                    .text_document_position_params
4739                    .text_document
4740                    .uri
4741                    .as_str(),
4742                "file:///root-1/main.rs"
4743            );
4744            assert_eq!(
4745                params.text_document_position_params.position,
4746                lsp::Position::new(0, 22)
4747            );
4748            Ok(Some(lsp::Hover {
4749                contents: lsp::HoverContents::Array(vec![
4750                    lsp::MarkedString::String("Test hover content.".to_string()),
4751                    lsp::MarkedString::LanguageString(lsp::LanguageString {
4752                        language: "Rust".to_string(),
4753                        value: "let foo = 42;".to_string(),
4754                    }),
4755                ]),
4756                range: Some(lsp::Range::new(
4757                    lsp::Position::new(0, 22),
4758                    lsp::Position::new(0, 29),
4759                )),
4760            }))
4761        },
4762    );
4763
4764    let hover_info = project_b
4765        .update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
4766        .await
4767        .unwrap()
4768        .unwrap();
4769    buffer_b.read_with(cx_b, |buffer, _| {
4770        let snapshot = buffer.snapshot();
4771        assert_eq!(hover_info.range.unwrap().to_offset(&snapshot), 22..29);
4772        assert_eq!(
4773            hover_info.contents,
4774            vec![
4775                project::HoverBlock {
4776                    text: "Test hover content.".to_string(),
4777                    kind: HoverBlockKind::Markdown,
4778                },
4779                project::HoverBlock {
4780                    text: "let foo = 42;".to_string(),
4781                    kind: HoverBlockKind::Code {
4782                        language: "Rust".to_string()
4783                    },
4784                }
4785            ]
4786        );
4787    });
4788}
4789
4790#[gpui::test(iterations = 10)]
4791async fn test_project_symbols(
4792    deterministic: Arc<Deterministic>,
4793    cx_a: &mut TestAppContext,
4794    cx_b: &mut TestAppContext,
4795) {
4796    deterministic.forbid_parking();
4797    let mut server = TestServer::start(&deterministic).await;
4798    let client_a = server.create_client(cx_a, "user_a").await;
4799    let client_b = server.create_client(cx_b, "user_b").await;
4800    server
4801        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4802        .await;
4803    let active_call_a = cx_a.read(ActiveCall::global);
4804
4805    // Set up a fake language server.
4806    let mut language = Language::new(
4807        LanguageConfig {
4808            name: "Rust".into(),
4809            path_suffixes: vec!["rs".to_string()],
4810            ..Default::default()
4811        },
4812        Some(tree_sitter_rust::language()),
4813    );
4814    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4815    client_a.language_registry.add(Arc::new(language));
4816
4817    client_a
4818        .fs
4819        .insert_tree(
4820            "/code",
4821            json!({
4822                "crate-1": {
4823                    "one.rs": "const ONE: usize = 1;",
4824                },
4825                "crate-2": {
4826                    "two.rs": "const TWO: usize = 2; const THREE: usize = 3;",
4827                },
4828                "private": {
4829                    "passwords.txt": "the-password",
4830                }
4831            }),
4832        )
4833        .await;
4834    let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await;
4835    let project_id = active_call_a
4836        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4837        .await
4838        .unwrap();
4839    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4840
4841    // Cause the language server to start.
4842    let _buffer = cx_b
4843        .background()
4844        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
4845        .await
4846        .unwrap();
4847
4848    let fake_language_server = fake_language_servers.next().await.unwrap();
4849    fake_language_server.handle_request::<lsp::request::WorkspaceSymbol, _, _>(|_, _| async move {
4850        #[allow(deprecated)]
4851        Ok(Some(vec![lsp::SymbolInformation {
4852            name: "TWO".into(),
4853            location: lsp::Location {
4854                uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
4855                range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4856            },
4857            kind: lsp::SymbolKind::CONSTANT,
4858            tags: None,
4859            container_name: None,
4860            deprecated: None,
4861        }]))
4862    });
4863
4864    // Request the definition of a symbol as the guest.
4865    let symbols = project_b
4866        .update(cx_b, |p, cx| p.symbols("two", cx))
4867        .await
4868        .unwrap();
4869    assert_eq!(symbols.len(), 1);
4870    assert_eq!(symbols[0].name, "TWO");
4871
4872    // Open one of the returned symbols.
4873    let buffer_b_2 = project_b
4874        .update(cx_b, |project, cx| {
4875            project.open_buffer_for_symbol(&symbols[0], cx)
4876        })
4877        .await
4878        .unwrap();
4879    buffer_b_2.read_with(cx_b, |buffer, _| {
4880        assert_eq!(
4881            buffer.file().unwrap().path().as_ref(),
4882            Path::new("../crate-2/two.rs")
4883        );
4884    });
4885
4886    // Attempt to craft a symbol and violate host's privacy by opening an arbitrary file.
4887    let mut fake_symbol = symbols[0].clone();
4888    fake_symbol.path.path = Path::new("/code/secrets").into();
4889    let error = project_b
4890        .update(cx_b, |project, cx| {
4891            project.open_buffer_for_symbol(&fake_symbol, cx)
4892        })
4893        .await
4894        .unwrap_err();
4895    assert!(error.to_string().contains("invalid symbol signature"));
4896}
4897
4898#[gpui::test(iterations = 10)]
4899async fn test_open_buffer_while_getting_definition_pointing_to_it(
4900    deterministic: Arc<Deterministic>,
4901    cx_a: &mut TestAppContext,
4902    cx_b: &mut TestAppContext,
4903    mut rng: StdRng,
4904) {
4905    deterministic.forbid_parking();
4906    let mut server = TestServer::start(&deterministic).await;
4907    let client_a = server.create_client(cx_a, "user_a").await;
4908    let client_b = server.create_client(cx_b, "user_b").await;
4909    server
4910        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4911        .await;
4912    let active_call_a = cx_a.read(ActiveCall::global);
4913
4914    // Set up a fake language server.
4915    let mut language = Language::new(
4916        LanguageConfig {
4917            name: "Rust".into(),
4918            path_suffixes: vec!["rs".to_string()],
4919            ..Default::default()
4920        },
4921        Some(tree_sitter_rust::language()),
4922    );
4923    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4924    client_a.language_registry.add(Arc::new(language));
4925
4926    client_a
4927        .fs
4928        .insert_tree(
4929            "/root",
4930            json!({
4931                "a.rs": "const ONE: usize = b::TWO;",
4932                "b.rs": "const TWO: usize = 2",
4933            }),
4934        )
4935        .await;
4936    let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await;
4937    let project_id = active_call_a
4938        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4939        .await
4940        .unwrap();
4941    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4942
4943    let buffer_b1 = cx_b
4944        .background()
4945        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4946        .await
4947        .unwrap();
4948
4949    let fake_language_server = fake_language_servers.next().await.unwrap();
4950    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4951        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4952            lsp::Location::new(
4953                lsp::Url::from_file_path("/root/b.rs").unwrap(),
4954                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4955            ),
4956        )))
4957    });
4958
4959    let definitions;
4960    let buffer_b2;
4961    if rng.gen() {
4962        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
4963        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
4964    } else {
4965        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
4966        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
4967    }
4968
4969    let buffer_b2 = buffer_b2.await.unwrap();
4970    let definitions = definitions.await.unwrap();
4971    assert_eq!(definitions.len(), 1);
4972    assert_eq!(definitions[0].target.buffer, buffer_b2);
4973}
4974
4975#[gpui::test(iterations = 10)]
4976async fn test_collaborating_with_code_actions(
4977    deterministic: Arc<Deterministic>,
4978    cx_a: &mut TestAppContext,
4979    cx_b: &mut TestAppContext,
4980) {
4981    deterministic.forbid_parking();
4982    cx_b.update(editor::init);
4983    let mut server = TestServer::start(&deterministic).await;
4984    let client_a = server.create_client(cx_a, "user_a").await;
4985    let client_b = server.create_client(cx_b, "user_b").await;
4986    server
4987        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4988        .await;
4989    let active_call_a = cx_a.read(ActiveCall::global);
4990
4991    // Set up a fake language server.
4992    let mut language = Language::new(
4993        LanguageConfig {
4994            name: "Rust".into(),
4995            path_suffixes: vec!["rs".to_string()],
4996            ..Default::default()
4997        },
4998        Some(tree_sitter_rust::language()),
4999    );
5000    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
5001    client_a.language_registry.add(Arc::new(language));
5002
5003    client_a
5004        .fs
5005        .insert_tree(
5006            "/a",
5007            json!({
5008                "main.rs": "mod other;\nfn main() { let foo = other::foo(); }",
5009                "other.rs": "pub fn foo() -> usize { 4 }",
5010            }),
5011        )
5012        .await;
5013    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
5014    let project_id = active_call_a
5015        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5016        .await
5017        .unwrap();
5018
5019    // Join the project as client B.
5020    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5021    let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx));
5022    let editor_b = workspace_b
5023        .update(cx_b, |workspace, cx| {
5024            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
5025        })
5026        .await
5027        .unwrap()
5028        .downcast::<Editor>()
5029        .unwrap();
5030
5031    let mut fake_language_server = fake_language_servers.next().await.unwrap();
5032    fake_language_server
5033        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
5034            assert_eq!(
5035                params.text_document.uri,
5036                lsp::Url::from_file_path("/a/main.rs").unwrap(),
5037            );
5038            assert_eq!(params.range.start, lsp::Position::new(0, 0));
5039            assert_eq!(params.range.end, lsp::Position::new(0, 0));
5040            Ok(None)
5041        })
5042        .next()
5043        .await;
5044
5045    // Move cursor to a location that contains code actions.
5046    editor_b.update(cx_b, |editor, cx| {
5047        editor.change_selections(None, cx, |s| {
5048            s.select_ranges([Point::new(1, 31)..Point::new(1, 31)])
5049        });
5050        cx.focus(&editor_b);
5051    });
5052
5053    fake_language_server
5054        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
5055            assert_eq!(
5056                params.text_document.uri,
5057                lsp::Url::from_file_path("/a/main.rs").unwrap(),
5058            );
5059            assert_eq!(params.range.start, lsp::Position::new(1, 31));
5060            assert_eq!(params.range.end, lsp::Position::new(1, 31));
5061
5062            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
5063                lsp::CodeAction {
5064                    title: "Inline into all callers".to_string(),
5065                    edit: Some(lsp::WorkspaceEdit {
5066                        changes: Some(
5067                            [
5068                                (
5069                                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
5070                                    vec![lsp::TextEdit::new(
5071                                        lsp::Range::new(
5072                                            lsp::Position::new(1, 22),
5073                                            lsp::Position::new(1, 34),
5074                                        ),
5075                                        "4".to_string(),
5076                                    )],
5077                                ),
5078                                (
5079                                    lsp::Url::from_file_path("/a/other.rs").unwrap(),
5080                                    vec![lsp::TextEdit::new(
5081                                        lsp::Range::new(
5082                                            lsp::Position::new(0, 0),
5083                                            lsp::Position::new(0, 27),
5084                                        ),
5085                                        "".to_string(),
5086                                    )],
5087                                ),
5088                            ]
5089                            .into_iter()
5090                            .collect(),
5091                        ),
5092                        ..Default::default()
5093                    }),
5094                    data: Some(json!({
5095                        "codeActionParams": {
5096                            "range": {
5097                                "start": {"line": 1, "column": 31},
5098                                "end": {"line": 1, "column": 31},
5099                            }
5100                        }
5101                    })),
5102                    ..Default::default()
5103                },
5104            )]))
5105        })
5106        .next()
5107        .await;
5108
5109    // Toggle code actions and wait for them to display.
5110    editor_b.update(cx_b, |editor, cx| {
5111        editor.toggle_code_actions(
5112            &ToggleCodeActions {
5113                deployed_from_indicator: false,
5114            },
5115            cx,
5116        );
5117    });
5118    cx_a.foreground().run_until_parked();
5119    editor_b.read_with(cx_b, |editor, _| assert!(editor.context_menu_visible()));
5120
5121    fake_language_server.remove_request_handler::<lsp::request::CodeActionRequest>();
5122
5123    // Confirming the code action will trigger a resolve request.
5124    let confirm_action = workspace_b
5125        .update(cx_b, |workspace, cx| {
5126            Editor::confirm_code_action(workspace, &ConfirmCodeAction { item_ix: Some(0) }, cx)
5127        })
5128        .unwrap();
5129    fake_language_server.handle_request::<lsp::request::CodeActionResolveRequest, _, _>(
5130        |_, _| async move {
5131            Ok(lsp::CodeAction {
5132                title: "Inline into all callers".to_string(),
5133                edit: Some(lsp::WorkspaceEdit {
5134                    changes: Some(
5135                        [
5136                            (
5137                                lsp::Url::from_file_path("/a/main.rs").unwrap(),
5138                                vec![lsp::TextEdit::new(
5139                                    lsp::Range::new(
5140                                        lsp::Position::new(1, 22),
5141                                        lsp::Position::new(1, 34),
5142                                    ),
5143                                    "4".to_string(),
5144                                )],
5145                            ),
5146                            (
5147                                lsp::Url::from_file_path("/a/other.rs").unwrap(),
5148                                vec![lsp::TextEdit::new(
5149                                    lsp::Range::new(
5150                                        lsp::Position::new(0, 0),
5151                                        lsp::Position::new(0, 27),
5152                                    ),
5153                                    "".to_string(),
5154                                )],
5155                            ),
5156                        ]
5157                        .into_iter()
5158                        .collect(),
5159                    ),
5160                    ..Default::default()
5161                }),
5162                ..Default::default()
5163            })
5164        },
5165    );
5166
5167    // After the action is confirmed, an editor containing both modified files is opened.
5168    confirm_action.await.unwrap();
5169    let code_action_editor = workspace_b.read_with(cx_b, |workspace, cx| {
5170        workspace
5171            .active_item(cx)
5172            .unwrap()
5173            .downcast::<Editor>()
5174            .unwrap()
5175    });
5176    code_action_editor.update(cx_b, |editor, cx| {
5177        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
5178        editor.undo(&Undo, cx);
5179        assert_eq!(
5180            editor.text(cx),
5181            "mod other;\nfn main() { let foo = other::foo(); }\npub fn foo() -> usize { 4 }"
5182        );
5183        editor.redo(&Redo, cx);
5184        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
5185    });
5186}
5187
5188#[gpui::test(iterations = 10)]
5189async fn test_collaborating_with_renames(
5190    deterministic: Arc<Deterministic>,
5191    cx_a: &mut TestAppContext,
5192    cx_b: &mut TestAppContext,
5193) {
5194    deterministic.forbid_parking();
5195    cx_b.update(editor::init);
5196    let mut server = TestServer::start(&deterministic).await;
5197    let client_a = server.create_client(cx_a, "user_a").await;
5198    let client_b = server.create_client(cx_b, "user_b").await;
5199    server
5200        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5201        .await;
5202    let active_call_a = cx_a.read(ActiveCall::global);
5203
5204    // Set up a fake language server.
5205    let mut language = Language::new(
5206        LanguageConfig {
5207            name: "Rust".into(),
5208            path_suffixes: vec!["rs".to_string()],
5209            ..Default::default()
5210        },
5211        Some(tree_sitter_rust::language()),
5212    );
5213    let mut fake_language_servers = language
5214        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
5215            capabilities: lsp::ServerCapabilities {
5216                rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
5217                    prepare_provider: Some(true),
5218                    work_done_progress_options: Default::default(),
5219                })),
5220                ..Default::default()
5221            },
5222            ..Default::default()
5223        }))
5224        .await;
5225    client_a.language_registry.add(Arc::new(language));
5226
5227    client_a
5228        .fs
5229        .insert_tree(
5230            "/dir",
5231            json!({
5232                "one.rs": "const ONE: usize = 1;",
5233                "two.rs": "const TWO: usize = one::ONE + one::ONE;"
5234            }),
5235        )
5236        .await;
5237    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
5238    let project_id = active_call_a
5239        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5240        .await
5241        .unwrap();
5242    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5243
5244    let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx));
5245    let editor_b = workspace_b
5246        .update(cx_b, |workspace, cx| {
5247            workspace.open_path((worktree_id, "one.rs"), None, true, cx)
5248        })
5249        .await
5250        .unwrap()
5251        .downcast::<Editor>()
5252        .unwrap();
5253    let fake_language_server = fake_language_servers.next().await.unwrap();
5254
5255    // Move cursor to a location that can be renamed.
5256    let prepare_rename = editor_b.update(cx_b, |editor, cx| {
5257        editor.change_selections(None, cx, |s| s.select_ranges([7..7]));
5258        editor.rename(&Rename, cx).unwrap()
5259    });
5260
5261    fake_language_server
5262        .handle_request::<lsp::request::PrepareRenameRequest, _, _>(|params, _| async move {
5263            assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs");
5264            assert_eq!(params.position, lsp::Position::new(0, 7));
5265            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
5266                lsp::Position::new(0, 6),
5267                lsp::Position::new(0, 9),
5268            ))))
5269        })
5270        .next()
5271        .await
5272        .unwrap();
5273    prepare_rename.await.unwrap();
5274    editor_b.update(cx_b, |editor, cx| {
5275        let rename = editor.pending_rename().unwrap();
5276        let buffer = editor.buffer().read(cx).snapshot(cx);
5277        assert_eq!(
5278            rename.range.start.to_offset(&buffer)..rename.range.end.to_offset(&buffer),
5279            6..9
5280        );
5281        rename.editor.update(cx, |rename_editor, cx| {
5282            rename_editor.buffer().update(cx, |rename_buffer, cx| {
5283                rename_buffer.edit([(0..3, "THREE")], None, cx);
5284            });
5285        });
5286    });
5287
5288    let confirm_rename = workspace_b.update(cx_b, |workspace, cx| {
5289        Editor::confirm_rename(workspace, &ConfirmRename, cx).unwrap()
5290    });
5291    fake_language_server
5292        .handle_request::<lsp::request::Rename, _, _>(|params, _| async move {
5293            assert_eq!(
5294                params.text_document_position.text_document.uri.as_str(),
5295                "file:///dir/one.rs"
5296            );
5297            assert_eq!(
5298                params.text_document_position.position,
5299                lsp::Position::new(0, 6)
5300            );
5301            assert_eq!(params.new_name, "THREE");
5302            Ok(Some(lsp::WorkspaceEdit {
5303                changes: Some(
5304                    [
5305                        (
5306                            lsp::Url::from_file_path("/dir/one.rs").unwrap(),
5307                            vec![lsp::TextEdit::new(
5308                                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5309                                "THREE".to_string(),
5310                            )],
5311                        ),
5312                        (
5313                            lsp::Url::from_file_path("/dir/two.rs").unwrap(),
5314                            vec![
5315                                lsp::TextEdit::new(
5316                                    lsp::Range::new(
5317                                        lsp::Position::new(0, 24),
5318                                        lsp::Position::new(0, 27),
5319                                    ),
5320                                    "THREE".to_string(),
5321                                ),
5322                                lsp::TextEdit::new(
5323                                    lsp::Range::new(
5324                                        lsp::Position::new(0, 35),
5325                                        lsp::Position::new(0, 38),
5326                                    ),
5327                                    "THREE".to_string(),
5328                                ),
5329                            ],
5330                        ),
5331                    ]
5332                    .into_iter()
5333                    .collect(),
5334                ),
5335                ..Default::default()
5336            }))
5337        })
5338        .next()
5339        .await
5340        .unwrap();
5341    confirm_rename.await.unwrap();
5342
5343    let rename_editor = workspace_b.read_with(cx_b, |workspace, cx| {
5344        workspace
5345            .active_item(cx)
5346            .unwrap()
5347            .downcast::<Editor>()
5348            .unwrap()
5349    });
5350    rename_editor.update(cx_b, |editor, cx| {
5351        assert_eq!(
5352            editor.text(cx),
5353            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
5354        );
5355        editor.undo(&Undo, cx);
5356        assert_eq!(
5357            editor.text(cx),
5358            "const ONE: usize = 1;\nconst TWO: usize = one::ONE + one::ONE;"
5359        );
5360        editor.redo(&Redo, cx);
5361        assert_eq!(
5362            editor.text(cx),
5363            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
5364        );
5365    });
5366
5367    // Ensure temporary rename edits cannot be undone/redone.
5368    editor_b.update(cx_b, |editor, cx| {
5369        editor.undo(&Undo, cx);
5370        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
5371        editor.undo(&Undo, cx);
5372        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
5373        editor.redo(&Redo, cx);
5374        assert_eq!(editor.text(cx), "const THREE: usize = 1;");
5375    })
5376}
5377
5378#[gpui::test(iterations = 10)]
5379async fn test_language_server_statuses(
5380    deterministic: Arc<Deterministic>,
5381    cx_a: &mut TestAppContext,
5382    cx_b: &mut TestAppContext,
5383) {
5384    deterministic.forbid_parking();
5385
5386    cx_b.update(editor::init);
5387    let mut server = TestServer::start(&deterministic).await;
5388    let client_a = server.create_client(cx_a, "user_a").await;
5389    let client_b = server.create_client(cx_b, "user_b").await;
5390    server
5391        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5392        .await;
5393    let active_call_a = cx_a.read(ActiveCall::global);
5394
5395    // Set up a fake language server.
5396    let mut language = Language::new(
5397        LanguageConfig {
5398            name: "Rust".into(),
5399            path_suffixes: vec!["rs".to_string()],
5400            ..Default::default()
5401        },
5402        Some(tree_sitter_rust::language()),
5403    );
5404    let mut fake_language_servers = language
5405        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
5406            name: "the-language-server",
5407            ..Default::default()
5408        }))
5409        .await;
5410    client_a.language_registry.add(Arc::new(language));
5411
5412    client_a
5413        .fs
5414        .insert_tree(
5415            "/dir",
5416            json!({
5417                "main.rs": "const ONE: usize = 1;",
5418            }),
5419        )
5420        .await;
5421    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
5422
5423    let _buffer_a = project_a
5424        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
5425        .await
5426        .unwrap();
5427
5428    let fake_language_server = fake_language_servers.next().await.unwrap();
5429    fake_language_server.start_progress("the-token").await;
5430    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
5431        token: lsp::NumberOrString::String("the-token".to_string()),
5432        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
5433            lsp::WorkDoneProgressReport {
5434                message: Some("the-message".to_string()),
5435                ..Default::default()
5436            },
5437        )),
5438    });
5439    deterministic.run_until_parked();
5440    project_a.read_with(cx_a, |project, _| {
5441        let status = project.language_server_statuses().next().unwrap();
5442        assert_eq!(status.name, "the-language-server");
5443        assert_eq!(status.pending_work.len(), 1);
5444        assert_eq!(
5445            status.pending_work["the-token"].message.as_ref().unwrap(),
5446            "the-message"
5447        );
5448    });
5449
5450    let project_id = active_call_a
5451        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5452        .await
5453        .unwrap();
5454    deterministic.run_until_parked();
5455    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5456    project_b.read_with(cx_b, |project, _| {
5457        let status = project.language_server_statuses().next().unwrap();
5458        assert_eq!(status.name, "the-language-server");
5459    });
5460
5461    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
5462        token: lsp::NumberOrString::String("the-token".to_string()),
5463        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
5464            lsp::WorkDoneProgressReport {
5465                message: Some("the-message-2".to_string()),
5466                ..Default::default()
5467            },
5468        )),
5469    });
5470    deterministic.run_until_parked();
5471    project_a.read_with(cx_a, |project, _| {
5472        let status = project.language_server_statuses().next().unwrap();
5473        assert_eq!(status.name, "the-language-server");
5474        assert_eq!(status.pending_work.len(), 1);
5475        assert_eq!(
5476            status.pending_work["the-token"].message.as_ref().unwrap(),
5477            "the-message-2"
5478        );
5479    });
5480    project_b.read_with(cx_b, |project, _| {
5481        let status = project.language_server_statuses().next().unwrap();
5482        assert_eq!(status.name, "the-language-server");
5483        assert_eq!(status.pending_work.len(), 1);
5484        assert_eq!(
5485            status.pending_work["the-token"].message.as_ref().unwrap(),
5486            "the-message-2"
5487        );
5488    });
5489}
5490
5491#[gpui::test(iterations = 10)]
5492async fn test_contacts(
5493    deterministic: Arc<Deterministic>,
5494    cx_a: &mut TestAppContext,
5495    cx_b: &mut TestAppContext,
5496    cx_c: &mut TestAppContext,
5497    cx_d: &mut TestAppContext,
5498) {
5499    deterministic.forbid_parking();
5500    let mut server = TestServer::start(&deterministic).await;
5501    let client_a = server.create_client(cx_a, "user_a").await;
5502    let client_b = server.create_client(cx_b, "user_b").await;
5503    let client_c = server.create_client(cx_c, "user_c").await;
5504    let client_d = server.create_client(cx_d, "user_d").await;
5505    server
5506        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
5507        .await;
5508    let active_call_a = cx_a.read(ActiveCall::global);
5509    let active_call_b = cx_b.read(ActiveCall::global);
5510    let active_call_c = cx_c.read(ActiveCall::global);
5511    let _active_call_d = cx_d.read(ActiveCall::global);
5512
5513    deterministic.run_until_parked();
5514    assert_eq!(
5515        contacts(&client_a, cx_a),
5516        [
5517            ("user_b".to_string(), "online", "free"),
5518            ("user_c".to_string(), "online", "free")
5519        ]
5520    );
5521    assert_eq!(
5522        contacts(&client_b, cx_b),
5523        [
5524            ("user_a".to_string(), "online", "free"),
5525            ("user_c".to_string(), "online", "free")
5526        ]
5527    );
5528    assert_eq!(
5529        contacts(&client_c, cx_c),
5530        [
5531            ("user_a".to_string(), "online", "free"),
5532            ("user_b".to_string(), "online", "free")
5533        ]
5534    );
5535    assert_eq!(contacts(&client_d, cx_d), []);
5536
5537    server.disconnect_client(client_c.peer_id().unwrap());
5538    server.forbid_connections();
5539    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5540    assert_eq!(
5541        contacts(&client_a, cx_a),
5542        [
5543            ("user_b".to_string(), "online", "free"),
5544            ("user_c".to_string(), "offline", "free")
5545        ]
5546    );
5547    assert_eq!(
5548        contacts(&client_b, cx_b),
5549        [
5550            ("user_a".to_string(), "online", "free"),
5551            ("user_c".to_string(), "offline", "free")
5552        ]
5553    );
5554    assert_eq!(contacts(&client_c, cx_c), []);
5555    assert_eq!(contacts(&client_d, cx_d), []);
5556
5557    server.allow_connections();
5558    client_c
5559        .authenticate_and_connect(false, &cx_c.to_async())
5560        .await
5561        .unwrap();
5562
5563    deterministic.run_until_parked();
5564    assert_eq!(
5565        contacts(&client_a, cx_a),
5566        [
5567            ("user_b".to_string(), "online", "free"),
5568            ("user_c".to_string(), "online", "free")
5569        ]
5570    );
5571    assert_eq!(
5572        contacts(&client_b, cx_b),
5573        [
5574            ("user_a".to_string(), "online", "free"),
5575            ("user_c".to_string(), "online", "free")
5576        ]
5577    );
5578    assert_eq!(
5579        contacts(&client_c, cx_c),
5580        [
5581            ("user_a".to_string(), "online", "free"),
5582            ("user_b".to_string(), "online", "free")
5583        ]
5584    );
5585    assert_eq!(contacts(&client_d, cx_d), []);
5586
5587    active_call_a
5588        .update(cx_a, |call, cx| {
5589            call.invite(client_b.user_id().unwrap(), None, cx)
5590        })
5591        .await
5592        .unwrap();
5593    deterministic.run_until_parked();
5594    assert_eq!(
5595        contacts(&client_a, cx_a),
5596        [
5597            ("user_b".to_string(), "online", "busy"),
5598            ("user_c".to_string(), "online", "free")
5599        ]
5600    );
5601    assert_eq!(
5602        contacts(&client_b, cx_b),
5603        [
5604            ("user_a".to_string(), "online", "busy"),
5605            ("user_c".to_string(), "online", "free")
5606        ]
5607    );
5608    assert_eq!(
5609        contacts(&client_c, cx_c),
5610        [
5611            ("user_a".to_string(), "online", "busy"),
5612            ("user_b".to_string(), "online", "busy")
5613        ]
5614    );
5615    assert_eq!(contacts(&client_d, cx_d), []);
5616
5617    // Client B and client D become contacts while client B is being called.
5618    server
5619        .make_contacts(&mut [(&client_b, cx_b), (&client_d, cx_d)])
5620        .await;
5621    deterministic.run_until_parked();
5622    assert_eq!(
5623        contacts(&client_a, cx_a),
5624        [
5625            ("user_b".to_string(), "online", "busy"),
5626            ("user_c".to_string(), "online", "free")
5627        ]
5628    );
5629    assert_eq!(
5630        contacts(&client_b, cx_b),
5631        [
5632            ("user_a".to_string(), "online", "busy"),
5633            ("user_c".to_string(), "online", "free"),
5634            ("user_d".to_string(), "online", "free"),
5635        ]
5636    );
5637    assert_eq!(
5638        contacts(&client_c, cx_c),
5639        [
5640            ("user_a".to_string(), "online", "busy"),
5641            ("user_b".to_string(), "online", "busy")
5642        ]
5643    );
5644    assert_eq!(
5645        contacts(&client_d, cx_d),
5646        [("user_b".to_string(), "online", "busy")]
5647    );
5648
5649    active_call_b.update(cx_b, |call, _| call.decline_incoming().unwrap());
5650    deterministic.run_until_parked();
5651    assert_eq!(
5652        contacts(&client_a, cx_a),
5653        [
5654            ("user_b".to_string(), "online", "free"),
5655            ("user_c".to_string(), "online", "free")
5656        ]
5657    );
5658    assert_eq!(
5659        contacts(&client_b, cx_b),
5660        [
5661            ("user_a".to_string(), "online", "free"),
5662            ("user_c".to_string(), "online", "free"),
5663            ("user_d".to_string(), "online", "free")
5664        ]
5665    );
5666    assert_eq!(
5667        contacts(&client_c, cx_c),
5668        [
5669            ("user_a".to_string(), "online", "free"),
5670            ("user_b".to_string(), "online", "free")
5671        ]
5672    );
5673    assert_eq!(
5674        contacts(&client_d, cx_d),
5675        [("user_b".to_string(), "online", "free")]
5676    );
5677
5678    active_call_c
5679        .update(cx_c, |call, cx| {
5680            call.invite(client_a.user_id().unwrap(), None, cx)
5681        })
5682        .await
5683        .unwrap();
5684    deterministic.run_until_parked();
5685    assert_eq!(
5686        contacts(&client_a, cx_a),
5687        [
5688            ("user_b".to_string(), "online", "free"),
5689            ("user_c".to_string(), "online", "busy")
5690        ]
5691    );
5692    assert_eq!(
5693        contacts(&client_b, cx_b),
5694        [
5695            ("user_a".to_string(), "online", "busy"),
5696            ("user_c".to_string(), "online", "busy"),
5697            ("user_d".to_string(), "online", "free")
5698        ]
5699    );
5700    assert_eq!(
5701        contacts(&client_c, cx_c),
5702        [
5703            ("user_a".to_string(), "online", "busy"),
5704            ("user_b".to_string(), "online", "free")
5705        ]
5706    );
5707    assert_eq!(
5708        contacts(&client_d, cx_d),
5709        [("user_b".to_string(), "online", "free")]
5710    );
5711
5712    active_call_a
5713        .update(cx_a, |call, cx| call.accept_incoming(cx))
5714        .await
5715        .unwrap();
5716    deterministic.run_until_parked();
5717    assert_eq!(
5718        contacts(&client_a, cx_a),
5719        [
5720            ("user_b".to_string(), "online", "free"),
5721            ("user_c".to_string(), "online", "busy")
5722        ]
5723    );
5724    assert_eq!(
5725        contacts(&client_b, cx_b),
5726        [
5727            ("user_a".to_string(), "online", "busy"),
5728            ("user_c".to_string(), "online", "busy"),
5729            ("user_d".to_string(), "online", "free")
5730        ]
5731    );
5732    assert_eq!(
5733        contacts(&client_c, cx_c),
5734        [
5735            ("user_a".to_string(), "online", "busy"),
5736            ("user_b".to_string(), "online", "free")
5737        ]
5738    );
5739    assert_eq!(
5740        contacts(&client_d, cx_d),
5741        [("user_b".to_string(), "online", "free")]
5742    );
5743
5744    active_call_a
5745        .update(cx_a, |call, cx| {
5746            call.invite(client_b.user_id().unwrap(), None, cx)
5747        })
5748        .await
5749        .unwrap();
5750    deterministic.run_until_parked();
5751    assert_eq!(
5752        contacts(&client_a, cx_a),
5753        [
5754            ("user_b".to_string(), "online", "busy"),
5755            ("user_c".to_string(), "online", "busy")
5756        ]
5757    );
5758    assert_eq!(
5759        contacts(&client_b, cx_b),
5760        [
5761            ("user_a".to_string(), "online", "busy"),
5762            ("user_c".to_string(), "online", "busy"),
5763            ("user_d".to_string(), "online", "free")
5764        ]
5765    );
5766    assert_eq!(
5767        contacts(&client_c, cx_c),
5768        [
5769            ("user_a".to_string(), "online", "busy"),
5770            ("user_b".to_string(), "online", "busy")
5771        ]
5772    );
5773    assert_eq!(
5774        contacts(&client_d, cx_d),
5775        [("user_b".to_string(), "online", "busy")]
5776    );
5777
5778    active_call_a
5779        .update(cx_a, |call, cx| call.hang_up(cx))
5780        .await
5781        .unwrap();
5782    deterministic.run_until_parked();
5783    assert_eq!(
5784        contacts(&client_a, cx_a),
5785        [
5786            ("user_b".to_string(), "online", "free"),
5787            ("user_c".to_string(), "online", "free")
5788        ]
5789    );
5790    assert_eq!(
5791        contacts(&client_b, cx_b),
5792        [
5793            ("user_a".to_string(), "online", "free"),
5794            ("user_c".to_string(), "online", "free"),
5795            ("user_d".to_string(), "online", "free")
5796        ]
5797    );
5798    assert_eq!(
5799        contacts(&client_c, cx_c),
5800        [
5801            ("user_a".to_string(), "online", "free"),
5802            ("user_b".to_string(), "online", "free")
5803        ]
5804    );
5805    assert_eq!(
5806        contacts(&client_d, cx_d),
5807        [("user_b".to_string(), "online", "free")]
5808    );
5809
5810    active_call_a
5811        .update(cx_a, |call, cx| {
5812            call.invite(client_b.user_id().unwrap(), None, cx)
5813        })
5814        .await
5815        .unwrap();
5816    deterministic.run_until_parked();
5817    assert_eq!(
5818        contacts(&client_a, cx_a),
5819        [
5820            ("user_b".to_string(), "online", "busy"),
5821            ("user_c".to_string(), "online", "free")
5822        ]
5823    );
5824    assert_eq!(
5825        contacts(&client_b, cx_b),
5826        [
5827            ("user_a".to_string(), "online", "busy"),
5828            ("user_c".to_string(), "online", "free"),
5829            ("user_d".to_string(), "online", "free")
5830        ]
5831    );
5832    assert_eq!(
5833        contacts(&client_c, cx_c),
5834        [
5835            ("user_a".to_string(), "online", "busy"),
5836            ("user_b".to_string(), "online", "busy")
5837        ]
5838    );
5839    assert_eq!(
5840        contacts(&client_d, cx_d),
5841        [("user_b".to_string(), "online", "busy")]
5842    );
5843
5844    server.forbid_connections();
5845    server.disconnect_client(client_a.peer_id().unwrap());
5846    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5847    assert_eq!(contacts(&client_a, cx_a), []);
5848    assert_eq!(
5849        contacts(&client_b, cx_b),
5850        [
5851            ("user_a".to_string(), "offline", "free"),
5852            ("user_c".to_string(), "online", "free"),
5853            ("user_d".to_string(), "online", "free")
5854        ]
5855    );
5856    assert_eq!(
5857        contacts(&client_c, cx_c),
5858        [
5859            ("user_a".to_string(), "offline", "free"),
5860            ("user_b".to_string(), "online", "free")
5861        ]
5862    );
5863    assert_eq!(
5864        contacts(&client_d, cx_d),
5865        [("user_b".to_string(), "online", "free")]
5866    );
5867
5868    // Test removing a contact
5869    client_b
5870        .user_store
5871        .update(cx_b, |store, cx| {
5872            store.remove_contact(client_c.user_id().unwrap(), cx)
5873        })
5874        .await
5875        .unwrap();
5876    deterministic.run_until_parked();
5877    assert_eq!(
5878        contacts(&client_b, cx_b),
5879        [
5880            ("user_a".to_string(), "offline", "free"),
5881            ("user_d".to_string(), "online", "free")
5882        ]
5883    );
5884    assert_eq!(
5885        contacts(&client_c, cx_c),
5886        [("user_a".to_string(), "offline", "free"),]
5887    );
5888
5889    fn contacts(
5890        client: &TestClient,
5891        cx: &TestAppContext,
5892    ) -> Vec<(String, &'static str, &'static str)> {
5893        client.user_store.read_with(cx, |store, _| {
5894            store
5895                .contacts()
5896                .iter()
5897                .map(|contact| {
5898                    (
5899                        contact.user.github_login.clone(),
5900                        if contact.online { "online" } else { "offline" },
5901                        if contact.busy { "busy" } else { "free" },
5902                    )
5903                })
5904                .collect()
5905        })
5906    }
5907}
5908
5909#[gpui::test(iterations = 10)]
5910async fn test_contact_requests(
5911    deterministic: Arc<Deterministic>,
5912    cx_a: &mut TestAppContext,
5913    cx_a2: &mut TestAppContext,
5914    cx_b: &mut TestAppContext,
5915    cx_b2: &mut TestAppContext,
5916    cx_c: &mut TestAppContext,
5917    cx_c2: &mut TestAppContext,
5918) {
5919    deterministic.forbid_parking();
5920
5921    // Connect to a server as 3 clients.
5922    let mut server = TestServer::start(&deterministic).await;
5923    let client_a = server.create_client(cx_a, "user_a").await;
5924    let client_a2 = server.create_client(cx_a2, "user_a").await;
5925    let client_b = server.create_client(cx_b, "user_b").await;
5926    let client_b2 = server.create_client(cx_b2, "user_b").await;
5927    let client_c = server.create_client(cx_c, "user_c").await;
5928    let client_c2 = server.create_client(cx_c2, "user_c").await;
5929
5930    assert_eq!(client_a.user_id().unwrap(), client_a2.user_id().unwrap());
5931    assert_eq!(client_b.user_id().unwrap(), client_b2.user_id().unwrap());
5932    assert_eq!(client_c.user_id().unwrap(), client_c2.user_id().unwrap());
5933
5934    // User A and User C request that user B become their contact.
5935    client_a
5936        .user_store
5937        .update(cx_a, |store, cx| {
5938            store.request_contact(client_b.user_id().unwrap(), cx)
5939        })
5940        .await
5941        .unwrap();
5942    client_c
5943        .user_store
5944        .update(cx_c, |store, cx| {
5945            store.request_contact(client_b.user_id().unwrap(), cx)
5946        })
5947        .await
5948        .unwrap();
5949    deterministic.run_until_parked();
5950
5951    // All users see the pending request appear in all their clients.
5952    assert_eq!(
5953        client_a.summarize_contacts(cx_a).outgoing_requests,
5954        &["user_b"]
5955    );
5956    assert_eq!(
5957        client_a2.summarize_contacts(cx_a2).outgoing_requests,
5958        &["user_b"]
5959    );
5960    assert_eq!(
5961        client_b.summarize_contacts(cx_b).incoming_requests,
5962        &["user_a", "user_c"]
5963    );
5964    assert_eq!(
5965        client_b2.summarize_contacts(cx_b2).incoming_requests,
5966        &["user_a", "user_c"]
5967    );
5968    assert_eq!(
5969        client_c.summarize_contacts(cx_c).outgoing_requests,
5970        &["user_b"]
5971    );
5972    assert_eq!(
5973        client_c2.summarize_contacts(cx_c2).outgoing_requests,
5974        &["user_b"]
5975    );
5976
5977    // Contact requests are present upon connecting (tested here via disconnect/reconnect)
5978    disconnect_and_reconnect(&client_a, cx_a).await;
5979    disconnect_and_reconnect(&client_b, cx_b).await;
5980    disconnect_and_reconnect(&client_c, cx_c).await;
5981    deterministic.run_until_parked();
5982    assert_eq!(
5983        client_a.summarize_contacts(cx_a).outgoing_requests,
5984        &["user_b"]
5985    );
5986    assert_eq!(
5987        client_b.summarize_contacts(cx_b).incoming_requests,
5988        &["user_a", "user_c"]
5989    );
5990    assert_eq!(
5991        client_c.summarize_contacts(cx_c).outgoing_requests,
5992        &["user_b"]
5993    );
5994
5995    // User B accepts the request from user A.
5996    client_b
5997        .user_store
5998        .update(cx_b, |store, cx| {
5999            store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
6000        })
6001        .await
6002        .unwrap();
6003
6004    deterministic.run_until_parked();
6005
6006    // User B sees user A as their contact now in all client, and the incoming request from them is removed.
6007    let contacts_b = client_b.summarize_contacts(cx_b);
6008    assert_eq!(contacts_b.current, &["user_a"]);
6009    assert_eq!(contacts_b.incoming_requests, &["user_c"]);
6010    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
6011    assert_eq!(contacts_b2.current, &["user_a"]);
6012    assert_eq!(contacts_b2.incoming_requests, &["user_c"]);
6013
6014    // User A sees user B as their contact now in all clients, and the outgoing request to them is removed.
6015    let contacts_a = client_a.summarize_contacts(cx_a);
6016    assert_eq!(contacts_a.current, &["user_b"]);
6017    assert!(contacts_a.outgoing_requests.is_empty());
6018    let contacts_a2 = client_a2.summarize_contacts(cx_a2);
6019    assert_eq!(contacts_a2.current, &["user_b"]);
6020    assert!(contacts_a2.outgoing_requests.is_empty());
6021
6022    // Contacts are present upon connecting (tested here via disconnect/reconnect)
6023    disconnect_and_reconnect(&client_a, cx_a).await;
6024    disconnect_and_reconnect(&client_b, cx_b).await;
6025    disconnect_and_reconnect(&client_c, cx_c).await;
6026    deterministic.run_until_parked();
6027    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
6028    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
6029    assert_eq!(
6030        client_b.summarize_contacts(cx_b).incoming_requests,
6031        &["user_c"]
6032    );
6033    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
6034    assert_eq!(
6035        client_c.summarize_contacts(cx_c).outgoing_requests,
6036        &["user_b"]
6037    );
6038
6039    // User B rejects the request from user C.
6040    client_b
6041        .user_store
6042        .update(cx_b, |store, cx| {
6043            store.respond_to_contact_request(client_c.user_id().unwrap(), false, cx)
6044        })
6045        .await
6046        .unwrap();
6047
6048    deterministic.run_until_parked();
6049
6050    // User B doesn't see user C as their contact, and the incoming request from them is removed.
6051    let contacts_b = client_b.summarize_contacts(cx_b);
6052    assert_eq!(contacts_b.current, &["user_a"]);
6053    assert!(contacts_b.incoming_requests.is_empty());
6054    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
6055    assert_eq!(contacts_b2.current, &["user_a"]);
6056    assert!(contacts_b2.incoming_requests.is_empty());
6057
6058    // User C doesn't see user B as their contact, and the outgoing request to them is removed.
6059    let contacts_c = client_c.summarize_contacts(cx_c);
6060    assert!(contacts_c.current.is_empty());
6061    assert!(contacts_c.outgoing_requests.is_empty());
6062    let contacts_c2 = client_c2.summarize_contacts(cx_c2);
6063    assert!(contacts_c2.current.is_empty());
6064    assert!(contacts_c2.outgoing_requests.is_empty());
6065
6066    // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect)
6067    disconnect_and_reconnect(&client_a, cx_a).await;
6068    disconnect_and_reconnect(&client_b, cx_b).await;
6069    disconnect_and_reconnect(&client_c, cx_c).await;
6070    deterministic.run_until_parked();
6071    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
6072    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
6073    assert!(client_b
6074        .summarize_contacts(cx_b)
6075        .incoming_requests
6076        .is_empty());
6077    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
6078    assert!(client_c
6079        .summarize_contacts(cx_c)
6080        .outgoing_requests
6081        .is_empty());
6082
6083    async fn disconnect_and_reconnect(client: &TestClient, cx: &mut TestAppContext) {
6084        client.disconnect(&cx.to_async());
6085        client.clear_contacts(cx).await;
6086        client
6087            .authenticate_and_connect(false, &cx.to_async())
6088            .await
6089            .unwrap();
6090    }
6091}
6092
6093#[gpui::test(iterations = 10)]
6094async fn test_basic_following(
6095    deterministic: Arc<Deterministic>,
6096    cx_a: &mut TestAppContext,
6097    cx_b: &mut TestAppContext,
6098    cx_c: &mut TestAppContext,
6099    cx_d: &mut TestAppContext,
6100) {
6101    deterministic.forbid_parking();
6102    cx_a.update(editor::init);
6103    cx_b.update(editor::init);
6104
6105    let mut server = TestServer::start(&deterministic).await;
6106    let client_a = server.create_client(cx_a, "user_a").await;
6107    let client_b = server.create_client(cx_b, "user_b").await;
6108    let client_c = server.create_client(cx_c, "user_c").await;
6109    let client_d = server.create_client(cx_d, "user_d").await;
6110    server
6111        .create_room(&mut [
6112            (&client_a, cx_a),
6113            (&client_b, cx_b),
6114            (&client_c, cx_c),
6115            (&client_d, cx_d),
6116        ])
6117        .await;
6118    let active_call_a = cx_a.read(ActiveCall::global);
6119    let active_call_b = cx_b.read(ActiveCall::global);
6120
6121    client_a
6122        .fs
6123        .insert_tree(
6124            "/a",
6125            json!({
6126                "1.txt": "one\none\none",
6127                "2.txt": "two\ntwo\ntwo",
6128                "3.txt": "three\nthree\nthree",
6129            }),
6130        )
6131        .await;
6132    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
6133    active_call_a
6134        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
6135        .await
6136        .unwrap();
6137
6138    let project_id = active_call_a
6139        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6140        .await
6141        .unwrap();
6142    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6143    active_call_b
6144        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6145        .await
6146        .unwrap();
6147
6148    let workspace_a = client_a.build_workspace(&project_a, cx_a);
6149    let workspace_b = client_b.build_workspace(&project_b, cx_b);
6150
6151    // Client A opens some editors.
6152    let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
6153    let editor_a1 = workspace_a
6154        .update(cx_a, |workspace, cx| {
6155            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6156        })
6157        .await
6158        .unwrap()
6159        .downcast::<Editor>()
6160        .unwrap();
6161    let editor_a2 = workspace_a
6162        .update(cx_a, |workspace, cx| {
6163            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
6164        })
6165        .await
6166        .unwrap()
6167        .downcast::<Editor>()
6168        .unwrap();
6169
6170    // Client B opens an editor.
6171    let editor_b1 = workspace_b
6172        .update(cx_b, |workspace, cx| {
6173            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6174        })
6175        .await
6176        .unwrap()
6177        .downcast::<Editor>()
6178        .unwrap();
6179
6180    let peer_id_a = client_a.peer_id().unwrap();
6181    let peer_id_b = client_b.peer_id().unwrap();
6182    let peer_id_c = client_c.peer_id().unwrap();
6183    let peer_id_d = client_d.peer_id().unwrap();
6184
6185    // Client A updates their selections in those editors
6186    editor_a1.update(cx_a, |editor, cx| {
6187        editor.handle_input("a", cx);
6188        editor.handle_input("b", cx);
6189        editor.handle_input("c", cx);
6190        editor.select_left(&Default::default(), cx);
6191        assert_eq!(editor.selections.ranges(cx), vec![3..2]);
6192    });
6193    editor_a2.update(cx_a, |editor, cx| {
6194        editor.handle_input("d", cx);
6195        editor.handle_input("e", cx);
6196        editor.select_left(&Default::default(), cx);
6197        assert_eq!(editor.selections.ranges(cx), vec![2..1]);
6198    });
6199
6200    // When client B starts following client A, all visible view states are replicated to client B.
6201    workspace_b
6202        .update(cx_b, |workspace, cx| {
6203            workspace.toggle_follow(peer_id_a, cx).unwrap()
6204        })
6205        .await
6206        .unwrap();
6207
6208    cx_c.foreground().run_until_parked();
6209    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
6210        workspace
6211            .active_item(cx)
6212            .unwrap()
6213            .downcast::<Editor>()
6214            .unwrap()
6215    });
6216    assert_eq!(
6217        cx_b.read(|cx| editor_b2.project_path(cx)),
6218        Some((worktree_id, "2.txt").into())
6219    );
6220    assert_eq!(
6221        editor_b2.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
6222        vec![2..1]
6223    );
6224    assert_eq!(
6225        editor_b1.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
6226        vec![3..2]
6227    );
6228
6229    cx_c.foreground().run_until_parked();
6230    let active_call_c = cx_c.read(ActiveCall::global);
6231    let project_c = client_c.build_remote_project(project_id, cx_c).await;
6232    let workspace_c = client_c.build_workspace(&project_c, cx_c);
6233    active_call_c
6234        .update(cx_c, |call, cx| call.set_location(Some(&project_c), cx))
6235        .await
6236        .unwrap();
6237    drop(project_c);
6238
6239    // Client C also follows client A.
6240    workspace_c
6241        .update(cx_c, |workspace, cx| {
6242            workspace.toggle_follow(peer_id_a, cx).unwrap()
6243        })
6244        .await
6245        .unwrap();
6246
6247    cx_d.foreground().run_until_parked();
6248    let active_call_d = cx_d.read(ActiveCall::global);
6249    let project_d = client_d.build_remote_project(project_id, cx_d).await;
6250    let workspace_d = client_d.build_workspace(&project_d, cx_d);
6251    active_call_d
6252        .update(cx_d, |call, cx| call.set_location(Some(&project_d), cx))
6253        .await
6254        .unwrap();
6255    drop(project_d);
6256
6257    // All clients see that clients B and C are following client A.
6258    cx_c.foreground().run_until_parked();
6259    for (name, active_call, cx) in [
6260        ("A", &active_call_a, &cx_a),
6261        ("B", &active_call_b, &cx_b),
6262        ("C", &active_call_c, &cx_c),
6263        ("D", &active_call_d, &cx_d),
6264    ] {
6265        active_call.read_with(*cx, |call, cx| {
6266            let room = call.room().unwrap().read(cx);
6267            assert_eq!(
6268                room.followers_for(peer_id_a, project_id),
6269                &[peer_id_b, peer_id_c],
6270                "checking followers for A as {name}"
6271            );
6272        });
6273    }
6274
6275    // Client C unfollows client A.
6276    workspace_c.update(cx_c, |workspace, cx| {
6277        workspace.toggle_follow(peer_id_a, cx);
6278    });
6279
6280    // All clients see that clients B is following client A.
6281    cx_c.foreground().run_until_parked();
6282    for (name, active_call, cx) in [
6283        ("A", &active_call_a, &cx_a),
6284        ("B", &active_call_b, &cx_b),
6285        ("C", &active_call_c, &cx_c),
6286        ("D", &active_call_d, &cx_d),
6287    ] {
6288        active_call.read_with(*cx, |call, cx| {
6289            let room = call.room().unwrap().read(cx);
6290            assert_eq!(
6291                room.followers_for(peer_id_a, project_id),
6292                &[peer_id_b],
6293                "checking followers for A as {name}"
6294            );
6295        });
6296    }
6297
6298    // Client C re-follows client A.
6299    workspace_c.update(cx_c, |workspace, cx| {
6300        workspace.toggle_follow(peer_id_a, cx);
6301    });
6302
6303    // All clients see that clients B and C are following client A.
6304    cx_c.foreground().run_until_parked();
6305    for (name, active_call, cx) in [
6306        ("A", &active_call_a, &cx_a),
6307        ("B", &active_call_b, &cx_b),
6308        ("C", &active_call_c, &cx_c),
6309        ("D", &active_call_d, &cx_d),
6310    ] {
6311        active_call.read_with(*cx, |call, cx| {
6312            let room = call.room().unwrap().read(cx);
6313            assert_eq!(
6314                room.followers_for(peer_id_a, project_id),
6315                &[peer_id_b, peer_id_c],
6316                "checking followers for A as {name}"
6317            );
6318        });
6319    }
6320
6321    // Client D follows client C.
6322    workspace_d
6323        .update(cx_d, |workspace, cx| {
6324            workspace.toggle_follow(peer_id_c, cx).unwrap()
6325        })
6326        .await
6327        .unwrap();
6328
6329    // All clients see that D is following C
6330    cx_d.foreground().run_until_parked();
6331    for (name, active_call, cx) in [
6332        ("A", &active_call_a, &cx_a),
6333        ("B", &active_call_b, &cx_b),
6334        ("C", &active_call_c, &cx_c),
6335        ("D", &active_call_d, &cx_d),
6336    ] {
6337        active_call.read_with(*cx, |call, cx| {
6338            let room = call.room().unwrap().read(cx);
6339            assert_eq!(
6340                room.followers_for(peer_id_c, project_id),
6341                &[peer_id_d],
6342                "checking followers for C as {name}"
6343            );
6344        });
6345    }
6346
6347    // Client C closes the project.
6348    cx_c.drop_last(workspace_c);
6349
6350    // Clients A and B see that client B is following A, and client C is not present in the followers.
6351    cx_c.foreground().run_until_parked();
6352    for (name, active_call, cx) in [("A", &active_call_a, &cx_a), ("B", &active_call_b, &cx_b)] {
6353        active_call.read_with(*cx, |call, cx| {
6354            let room = call.room().unwrap().read(cx);
6355            assert_eq!(
6356                room.followers_for(peer_id_a, project_id),
6357                &[peer_id_b],
6358                "checking followers for A as {name}"
6359            );
6360        });
6361    }
6362
6363    // All clients see that no-one is following C
6364    for (name, active_call, cx) in [
6365        ("A", &active_call_a, &cx_a),
6366        ("B", &active_call_b, &cx_b),
6367        ("C", &active_call_c, &cx_c),
6368        ("D", &active_call_d, &cx_d),
6369    ] {
6370        active_call.read_with(*cx, |call, cx| {
6371            let room = call.room().unwrap().read(cx);
6372            assert_eq!(
6373                room.followers_for(peer_id_c, project_id),
6374                &[],
6375                "checking followers for C as {name}"
6376            );
6377        });
6378    }
6379
6380    // When client A activates a different editor, client B does so as well.
6381    workspace_a.update(cx_a, |workspace, cx| {
6382        workspace.activate_item(&editor_a1, cx)
6383    });
6384    deterministic.run_until_parked();
6385    workspace_b.read_with(cx_b, |workspace, cx| {
6386        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
6387    });
6388
6389    // When client A opens a multibuffer, client B does so as well.
6390    let multibuffer_a = cx_a.add_model(|cx| {
6391        let buffer_a1 = project_a.update(cx, |project, cx| {
6392            project
6393                .get_open_buffer(&(worktree_id, "1.txt").into(), cx)
6394                .unwrap()
6395        });
6396        let buffer_a2 = project_a.update(cx, |project, cx| {
6397            project
6398                .get_open_buffer(&(worktree_id, "2.txt").into(), cx)
6399                .unwrap()
6400        });
6401        let mut result = MultiBuffer::new(0);
6402        result.push_excerpts(
6403            buffer_a1,
6404            [ExcerptRange {
6405                context: 0..3,
6406                primary: None,
6407            }],
6408            cx,
6409        );
6410        result.push_excerpts(
6411            buffer_a2,
6412            [ExcerptRange {
6413                context: 4..7,
6414                primary: None,
6415            }],
6416            cx,
6417        );
6418        result
6419    });
6420    let multibuffer_editor_a = workspace_a.update(cx_a, |workspace, cx| {
6421        let editor =
6422            cx.add_view(|cx| Editor::for_multibuffer(multibuffer_a, Some(project_a.clone()), cx));
6423        workspace.add_item(Box::new(editor.clone()), cx);
6424        editor
6425    });
6426    deterministic.run_until_parked();
6427    let multibuffer_editor_b = workspace_b.read_with(cx_b, |workspace, cx| {
6428        workspace
6429            .active_item(cx)
6430            .unwrap()
6431            .downcast::<Editor>()
6432            .unwrap()
6433    });
6434    assert_eq!(
6435        multibuffer_editor_a.read_with(cx_a, |editor, cx| editor.text(cx)),
6436        multibuffer_editor_b.read_with(cx_b, |editor, cx| editor.text(cx)),
6437    );
6438
6439    // When client A navigates back and forth, client B does so as well.
6440    workspace_a
6441        .update(cx_a, |workspace, cx| {
6442            workspace::Pane::go_back(workspace, None, cx)
6443        })
6444        .await
6445        .unwrap();
6446    deterministic.run_until_parked();
6447    workspace_b.read_with(cx_b, |workspace, cx| {
6448        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
6449    });
6450
6451    workspace_a
6452        .update(cx_a, |workspace, cx| {
6453            workspace::Pane::go_back(workspace, None, cx)
6454        })
6455        .await
6456        .unwrap();
6457    deterministic.run_until_parked();
6458    workspace_b.read_with(cx_b, |workspace, cx| {
6459        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b2.id());
6460    });
6461
6462    workspace_a
6463        .update(cx_a, |workspace, cx| {
6464            workspace::Pane::go_forward(workspace, None, cx)
6465        })
6466        .await
6467        .unwrap();
6468    deterministic.run_until_parked();
6469    workspace_b.read_with(cx_b, |workspace, cx| {
6470        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
6471    });
6472
6473    // Changes to client A's editor are reflected on client B.
6474    editor_a1.update(cx_a, |editor, cx| {
6475        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2]));
6476    });
6477    deterministic.run_until_parked();
6478    editor_b1.read_with(cx_b, |editor, cx| {
6479        assert_eq!(editor.selections.ranges(cx), &[1..1, 2..2]);
6480    });
6481
6482    editor_a1.update(cx_a, |editor, cx| editor.set_text("TWO", cx));
6483    deterministic.run_until_parked();
6484    editor_b1.read_with(cx_b, |editor, cx| assert_eq!(editor.text(cx), "TWO"));
6485
6486    editor_a1.update(cx_a, |editor, cx| {
6487        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
6488        editor.set_scroll_position(vec2f(0., 100.), cx);
6489    });
6490    deterministic.run_until_parked();
6491    editor_b1.read_with(cx_b, |editor, cx| {
6492        assert_eq!(editor.selections.ranges(cx), &[3..3]);
6493    });
6494
6495    // After unfollowing, client B stops receiving updates from client A.
6496    workspace_b.update(cx_b, |workspace, cx| {
6497        workspace.unfollow(&workspace.active_pane().clone(), cx)
6498    });
6499    workspace_a.update(cx_a, |workspace, cx| {
6500        workspace.activate_item(&editor_a2, cx)
6501    });
6502    deterministic.run_until_parked();
6503    assert_eq!(
6504        workspace_b.read_with(cx_b, |workspace, cx| workspace
6505            .active_item(cx)
6506            .unwrap()
6507            .id()),
6508        editor_b1.id()
6509    );
6510
6511    // Client A starts following client B.
6512    workspace_a
6513        .update(cx_a, |workspace, cx| {
6514            workspace.toggle_follow(peer_id_b, cx).unwrap()
6515        })
6516        .await
6517        .unwrap();
6518    assert_eq!(
6519        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
6520        Some(peer_id_b)
6521    );
6522    assert_eq!(
6523        workspace_a.read_with(cx_a, |workspace, cx| workspace
6524            .active_item(cx)
6525            .unwrap()
6526            .id()),
6527        editor_a1.id()
6528    );
6529
6530    // Client B activates an external window, which causes a new screen-sharing item to be added to the pane.
6531    let display = MacOSDisplay::new();
6532    active_call_b
6533        .update(cx_b, |call, cx| call.set_location(None, cx))
6534        .await
6535        .unwrap();
6536    active_call_b
6537        .update(cx_b, |call, cx| {
6538            call.room().unwrap().update(cx, |room, cx| {
6539                room.set_display_sources(vec![display.clone()]);
6540                room.share_screen(cx)
6541            })
6542        })
6543        .await
6544        .unwrap();
6545    deterministic.run_until_parked();
6546    let shared_screen = workspace_a.read_with(cx_a, |workspace, cx| {
6547        workspace
6548            .active_item(cx)
6549            .unwrap()
6550            .downcast::<SharedScreen>()
6551            .unwrap()
6552    });
6553
6554    // Client B activates Zed again, which causes the previous editor to become focused again.
6555    active_call_b
6556        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6557        .await
6558        .unwrap();
6559    deterministic.run_until_parked();
6560    workspace_a.read_with(cx_a, |workspace, cx| {
6561        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_a1.id())
6562    });
6563
6564    // Client B activates a multibuffer that was created by following client A. Client A returns to that multibuffer.
6565    workspace_b.update(cx_b, |workspace, cx| {
6566        workspace.activate_item(&multibuffer_editor_b, cx)
6567    });
6568    deterministic.run_until_parked();
6569    workspace_a.read_with(cx_a, |workspace, cx| {
6570        assert_eq!(
6571            workspace.active_item(cx).unwrap().id(),
6572            multibuffer_editor_a.id()
6573        )
6574    });
6575
6576    // Client B activates an external window again, and the previously-opened screen-sharing item
6577    // gets activated.
6578    active_call_b
6579        .update(cx_b, |call, cx| call.set_location(None, cx))
6580        .await
6581        .unwrap();
6582    deterministic.run_until_parked();
6583    assert_eq!(
6584        workspace_a.read_with(cx_a, |workspace, cx| workspace
6585            .active_item(cx)
6586            .unwrap()
6587            .id()),
6588        shared_screen.id()
6589    );
6590
6591    // Following interrupts when client B disconnects.
6592    client_b.disconnect(&cx_b.to_async());
6593    deterministic.advance_clock(RECONNECT_TIMEOUT);
6594    assert_eq!(
6595        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
6596        None
6597    );
6598}
6599
6600#[gpui::test(iterations = 10)]
6601async fn test_join_call_after_screen_was_shared(
6602    deterministic: Arc<Deterministic>,
6603    cx_a: &mut TestAppContext,
6604    cx_b: &mut TestAppContext,
6605) {
6606    deterministic.forbid_parking();
6607    let mut server = TestServer::start(&deterministic).await;
6608
6609    let client_a = server.create_client(cx_a, "user_a").await;
6610    let client_b = server.create_client(cx_b, "user_b").await;
6611    server
6612        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6613        .await;
6614
6615    let active_call_a = cx_a.read(ActiveCall::global);
6616    let active_call_b = cx_b.read(ActiveCall::global);
6617
6618    // Call users B and C from client A.
6619    active_call_a
6620        .update(cx_a, |call, cx| {
6621            call.invite(client_b.user_id().unwrap(), None, cx)
6622        })
6623        .await
6624        .unwrap();
6625    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
6626    deterministic.run_until_parked();
6627    assert_eq!(
6628        room_participants(&room_a, cx_a),
6629        RoomParticipants {
6630            remote: Default::default(),
6631            pending: vec!["user_b".to_string()]
6632        }
6633    );
6634
6635    // User B receives the call.
6636    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
6637    let call_b = incoming_call_b.next().await.unwrap().unwrap();
6638    assert_eq!(call_b.calling_user.github_login, "user_a");
6639
6640    // User A shares their screen
6641    let display = MacOSDisplay::new();
6642    active_call_a
6643        .update(cx_a, |call, cx| {
6644            call.room().unwrap().update(cx, |room, cx| {
6645                room.set_display_sources(vec![display.clone()]);
6646                room.share_screen(cx)
6647            })
6648        })
6649        .await
6650        .unwrap();
6651
6652    client_b.user_store.update(cx_b, |user_store, _| {
6653        user_store.clear_cache();
6654    });
6655
6656    // User B joins the room
6657    active_call_b
6658        .update(cx_b, |call, cx| call.accept_incoming(cx))
6659        .await
6660        .unwrap();
6661    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
6662    assert!(incoming_call_b.next().await.unwrap().is_none());
6663
6664    deterministic.run_until_parked();
6665    assert_eq!(
6666        room_participants(&room_a, cx_a),
6667        RoomParticipants {
6668            remote: vec!["user_b".to_string()],
6669            pending: vec![],
6670        }
6671    );
6672    assert_eq!(
6673        room_participants(&room_b, cx_b),
6674        RoomParticipants {
6675            remote: vec!["user_a".to_string()],
6676            pending: vec![],
6677        }
6678    );
6679
6680    // Ensure User B sees User A's screenshare.
6681    room_b.read_with(cx_b, |room, _| {
6682        assert_eq!(
6683            room.remote_participants()
6684                .get(&client_a.user_id().unwrap())
6685                .unwrap()
6686                .tracks
6687                .len(),
6688            1
6689        );
6690    });
6691}
6692
6693#[gpui::test]
6694async fn test_following_tab_order(
6695    deterministic: Arc<Deterministic>,
6696    cx_a: &mut TestAppContext,
6697    cx_b: &mut TestAppContext,
6698) {
6699    cx_a.update(editor::init);
6700    cx_b.update(editor::init);
6701
6702    let mut server = TestServer::start(&deterministic).await;
6703    let client_a = server.create_client(cx_a, "user_a").await;
6704    let client_b = server.create_client(cx_b, "user_b").await;
6705    server
6706        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6707        .await;
6708    let active_call_a = cx_a.read(ActiveCall::global);
6709    let active_call_b = cx_b.read(ActiveCall::global);
6710
6711    client_a
6712        .fs
6713        .insert_tree(
6714            "/a",
6715            json!({
6716                "1.txt": "one",
6717                "2.txt": "two",
6718                "3.txt": "three",
6719            }),
6720        )
6721        .await;
6722    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
6723    active_call_a
6724        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
6725        .await
6726        .unwrap();
6727
6728    let project_id = active_call_a
6729        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6730        .await
6731        .unwrap();
6732    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6733    active_call_b
6734        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6735        .await
6736        .unwrap();
6737
6738    let workspace_a = client_a.build_workspace(&project_a, cx_a);
6739    let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
6740
6741    let workspace_b = client_b.build_workspace(&project_b, cx_b);
6742    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
6743
6744    let client_b_id = project_a.read_with(cx_a, |project, _| {
6745        project.collaborators().values().next().unwrap().peer_id
6746    });
6747
6748    //Open 1, 3 in that order on client A
6749    workspace_a
6750        .update(cx_a, |workspace, cx| {
6751            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6752        })
6753        .await
6754        .unwrap();
6755    workspace_a
6756        .update(cx_a, |workspace, cx| {
6757            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
6758        })
6759        .await
6760        .unwrap();
6761
6762    let pane_paths = |pane: &ViewHandle<workspace::Pane>, cx: &mut TestAppContext| {
6763        pane.update(cx, |pane, cx| {
6764            pane.items()
6765                .map(|item| {
6766                    item.project_path(cx)
6767                        .unwrap()
6768                        .path
6769                        .to_str()
6770                        .unwrap()
6771                        .to_owned()
6772                })
6773                .collect::<Vec<_>>()
6774        })
6775    };
6776
6777    //Verify that the tabs opened in the order we expect
6778    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt"]);
6779
6780    //Follow client B as client A
6781    workspace_a
6782        .update(cx_a, |workspace, cx| {
6783            workspace.toggle_follow(client_b_id, cx).unwrap()
6784        })
6785        .await
6786        .unwrap();
6787
6788    //Open just 2 on client B
6789    workspace_b
6790        .update(cx_b, |workspace, cx| {
6791            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
6792        })
6793        .await
6794        .unwrap();
6795    deterministic.run_until_parked();
6796
6797    // Verify that newly opened followed file is at the end
6798    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt", "2.txt"]);
6799
6800    //Open just 1 on client B
6801    workspace_b
6802        .update(cx_b, |workspace, cx| {
6803            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6804        })
6805        .await
6806        .unwrap();
6807    assert_eq!(&pane_paths(&pane_b, cx_b), &["2.txt", "1.txt"]);
6808    deterministic.run_until_parked();
6809
6810    // Verify that following into 1 did not reorder
6811    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt", "2.txt"]);
6812}
6813
6814#[gpui::test(iterations = 10)]
6815async fn test_peers_following_each_other(
6816    deterministic: Arc<Deterministic>,
6817    cx_a: &mut TestAppContext,
6818    cx_b: &mut TestAppContext,
6819) {
6820    deterministic.forbid_parking();
6821    cx_a.update(editor::init);
6822    cx_b.update(editor::init);
6823
6824    let mut server = TestServer::start(&deterministic).await;
6825    let client_a = server.create_client(cx_a, "user_a").await;
6826    let client_b = server.create_client(cx_b, "user_b").await;
6827    server
6828        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6829        .await;
6830    let active_call_a = cx_a.read(ActiveCall::global);
6831    let active_call_b = cx_b.read(ActiveCall::global);
6832
6833    // Client A shares a project.
6834    client_a
6835        .fs
6836        .insert_tree(
6837            "/a",
6838            json!({
6839                "1.txt": "one",
6840                "2.txt": "two",
6841                "3.txt": "three",
6842                "4.txt": "four",
6843            }),
6844        )
6845        .await;
6846    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
6847    active_call_a
6848        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
6849        .await
6850        .unwrap();
6851    let project_id = active_call_a
6852        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6853        .await
6854        .unwrap();
6855
6856    // Client B joins the project.
6857    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6858    active_call_b
6859        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6860        .await
6861        .unwrap();
6862
6863    // Client A opens some editors.
6864    let workspace_a = client_a.build_workspace(&project_a, cx_a);
6865    let pane_a1 = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
6866    let _editor_a1 = workspace_a
6867        .update(cx_a, |workspace, cx| {
6868            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6869        })
6870        .await
6871        .unwrap()
6872        .downcast::<Editor>()
6873        .unwrap();
6874
6875    // Client B opens an editor.
6876    let workspace_b = client_b.build_workspace(&project_b, cx_b);
6877    let pane_b1 = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
6878    let _editor_b1 = workspace_b
6879        .update(cx_b, |workspace, cx| {
6880            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
6881        })
6882        .await
6883        .unwrap()
6884        .downcast::<Editor>()
6885        .unwrap();
6886
6887    // Clients A and B follow each other in split panes
6888    workspace_a.update(cx_a, |workspace, cx| {
6889        workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
6890    });
6891    workspace_a
6892        .update(cx_a, |workspace, cx| {
6893            assert_ne!(*workspace.active_pane(), pane_a1);
6894            let leader_id = *project_a.read(cx).collaborators().keys().next().unwrap();
6895            workspace.toggle_follow(leader_id, cx).unwrap()
6896        })
6897        .await
6898        .unwrap();
6899    workspace_b.update(cx_b, |workspace, cx| {
6900        workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
6901    });
6902    workspace_b
6903        .update(cx_b, |workspace, cx| {
6904            assert_ne!(*workspace.active_pane(), pane_b1);
6905            let leader_id = *project_b.read(cx).collaborators().keys().next().unwrap();
6906            workspace.toggle_follow(leader_id, cx).unwrap()
6907        })
6908        .await
6909        .unwrap();
6910
6911    workspace_a.update(cx_a, |workspace, cx| {
6912        workspace.activate_next_pane(cx);
6913    });
6914    // Wait for focus effects to be fully flushed
6915    workspace_a.update(cx_a, |workspace, _| {
6916        assert_eq!(*workspace.active_pane(), pane_a1);
6917    });
6918
6919    workspace_a
6920        .update(cx_a, |workspace, cx| {
6921            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
6922        })
6923        .await
6924        .unwrap();
6925    workspace_b.update(cx_b, |workspace, cx| {
6926        workspace.activate_next_pane(cx);
6927    });
6928
6929    workspace_b
6930        .update(cx_b, |workspace, cx| {
6931            assert_eq!(*workspace.active_pane(), pane_b1);
6932            workspace.open_path((worktree_id, "4.txt"), None, true, cx)
6933        })
6934        .await
6935        .unwrap();
6936    cx_a.foreground().run_until_parked();
6937
6938    // Ensure leader updates don't change the active pane of followers
6939    workspace_a.read_with(cx_a, |workspace, _| {
6940        assert_eq!(*workspace.active_pane(), pane_a1);
6941    });
6942    workspace_b.read_with(cx_b, |workspace, _| {
6943        assert_eq!(*workspace.active_pane(), pane_b1);
6944    });
6945
6946    // Ensure peers following each other doesn't cause an infinite loop.
6947    assert_eq!(
6948        workspace_a.read_with(cx_a, |workspace, cx| workspace
6949            .active_item(cx)
6950            .unwrap()
6951            .project_path(cx)),
6952        Some((worktree_id, "3.txt").into())
6953    );
6954    workspace_a.update(cx_a, |workspace, cx| {
6955        assert_eq!(
6956            workspace.active_item(cx).unwrap().project_path(cx),
6957            Some((worktree_id, "3.txt").into())
6958        );
6959        workspace.activate_next_pane(cx);
6960    });
6961
6962    workspace_a.update(cx_a, |workspace, cx| {
6963        assert_eq!(
6964            workspace.active_item(cx).unwrap().project_path(cx),
6965            Some((worktree_id, "4.txt").into())
6966        );
6967    });
6968
6969    workspace_b.update(cx_b, |workspace, cx| {
6970        assert_eq!(
6971            workspace.active_item(cx).unwrap().project_path(cx),
6972            Some((worktree_id, "4.txt").into())
6973        );
6974        workspace.activate_next_pane(cx);
6975    });
6976
6977    workspace_b.update(cx_b, |workspace, cx| {
6978        assert_eq!(
6979            workspace.active_item(cx).unwrap().project_path(cx),
6980            Some((worktree_id, "3.txt").into())
6981        );
6982    });
6983}
6984
6985#[gpui::test(iterations = 10)]
6986async fn test_auto_unfollowing(
6987    deterministic: Arc<Deterministic>,
6988    cx_a: &mut TestAppContext,
6989    cx_b: &mut TestAppContext,
6990) {
6991    deterministic.forbid_parking();
6992    cx_a.update(editor::init);
6993    cx_b.update(editor::init);
6994
6995    // 2 clients connect to a server.
6996    let mut server = TestServer::start(&deterministic).await;
6997    let client_a = server.create_client(cx_a, "user_a").await;
6998    let client_b = server.create_client(cx_b, "user_b").await;
6999    server
7000        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7001        .await;
7002    let active_call_a = cx_a.read(ActiveCall::global);
7003    let active_call_b = cx_b.read(ActiveCall::global);
7004
7005    // Client A shares a project.
7006    client_a
7007        .fs
7008        .insert_tree(
7009            "/a",
7010            json!({
7011                "1.txt": "one",
7012                "2.txt": "two",
7013                "3.txt": "three",
7014            }),
7015        )
7016        .await;
7017    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
7018    active_call_a
7019        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
7020        .await
7021        .unwrap();
7022
7023    let project_id = active_call_a
7024        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7025        .await
7026        .unwrap();
7027    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7028    active_call_b
7029        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
7030        .await
7031        .unwrap();
7032
7033    // Client A opens some editors.
7034    let workspace_a = client_a.build_workspace(&project_a, cx_a);
7035    let _editor_a1 = workspace_a
7036        .update(cx_a, |workspace, cx| {
7037            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
7038        })
7039        .await
7040        .unwrap()
7041        .downcast::<Editor>()
7042        .unwrap();
7043
7044    // Client B starts following client A.
7045    let workspace_b = client_b.build_workspace(&project_b, cx_b);
7046    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
7047    let leader_id = project_b.read_with(cx_b, |project, _| {
7048        project.collaborators().values().next().unwrap().peer_id
7049    });
7050    workspace_b
7051        .update(cx_b, |workspace, cx| {
7052            workspace.toggle_follow(leader_id, cx).unwrap()
7053        })
7054        .await
7055        .unwrap();
7056    assert_eq!(
7057        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7058        Some(leader_id)
7059    );
7060    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
7061        workspace
7062            .active_item(cx)
7063            .unwrap()
7064            .downcast::<Editor>()
7065            .unwrap()
7066    });
7067
7068    // When client B moves, it automatically stops following client A.
7069    editor_b2.update(cx_b, |editor, cx| editor.move_right(&editor::MoveRight, cx));
7070    assert_eq!(
7071        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7072        None
7073    );
7074
7075    workspace_b
7076        .update(cx_b, |workspace, cx| {
7077            workspace.toggle_follow(leader_id, cx).unwrap()
7078        })
7079        .await
7080        .unwrap();
7081    assert_eq!(
7082        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7083        Some(leader_id)
7084    );
7085
7086    // When client B edits, it automatically stops following client A.
7087    editor_b2.update(cx_b, |editor, cx| editor.insert("X", cx));
7088    assert_eq!(
7089        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7090        None
7091    );
7092
7093    workspace_b
7094        .update(cx_b, |workspace, cx| {
7095            workspace.toggle_follow(leader_id, cx).unwrap()
7096        })
7097        .await
7098        .unwrap();
7099    assert_eq!(
7100        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7101        Some(leader_id)
7102    );
7103
7104    // When client B scrolls, it automatically stops following client A.
7105    editor_b2.update(cx_b, |editor, cx| {
7106        editor.set_scroll_position(vec2f(0., 3.), cx)
7107    });
7108    assert_eq!(
7109        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7110        None
7111    );
7112
7113    workspace_b
7114        .update(cx_b, |workspace, cx| {
7115            workspace.toggle_follow(leader_id, cx).unwrap()
7116        })
7117        .await
7118        .unwrap();
7119    assert_eq!(
7120        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7121        Some(leader_id)
7122    );
7123
7124    // When client B activates a different pane, it continues following client A in the original pane.
7125    workspace_b.update(cx_b, |workspace, cx| {
7126        workspace.split_pane(pane_b.clone(), SplitDirection::Right, cx)
7127    });
7128    assert_eq!(
7129        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7130        Some(leader_id)
7131    );
7132
7133    workspace_b.update(cx_b, |workspace, cx| workspace.activate_next_pane(cx));
7134    assert_eq!(
7135        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7136        Some(leader_id)
7137    );
7138
7139    // When client B activates a different item in the original pane, it automatically stops following client A.
7140    workspace_b
7141        .update(cx_b, |workspace, cx| {
7142            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
7143        })
7144        .await
7145        .unwrap();
7146    assert_eq!(
7147        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7148        None
7149    );
7150}
7151
7152#[gpui::test(iterations = 10)]
7153async fn test_peers_simultaneously_following_each_other(
7154    deterministic: Arc<Deterministic>,
7155    cx_a: &mut TestAppContext,
7156    cx_b: &mut TestAppContext,
7157) {
7158    deterministic.forbid_parking();
7159    cx_a.update(editor::init);
7160    cx_b.update(editor::init);
7161
7162    let mut server = TestServer::start(&deterministic).await;
7163    let client_a = server.create_client(cx_a, "user_a").await;
7164    let client_b = server.create_client(cx_b, "user_b").await;
7165    server
7166        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7167        .await;
7168    let active_call_a = cx_a.read(ActiveCall::global);
7169
7170    client_a.fs.insert_tree("/a", json!({})).await;
7171    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
7172    let workspace_a = client_a.build_workspace(&project_a, cx_a);
7173    let project_id = active_call_a
7174        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7175        .await
7176        .unwrap();
7177
7178    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7179    let workspace_b = client_b.build_workspace(&project_b, cx_b);
7180
7181    deterministic.run_until_parked();
7182    let client_a_id = project_b.read_with(cx_b, |project, _| {
7183        project.collaborators().values().next().unwrap().peer_id
7184    });
7185    let client_b_id = project_a.read_with(cx_a, |project, _| {
7186        project.collaborators().values().next().unwrap().peer_id
7187    });
7188
7189    let a_follow_b = workspace_a.update(cx_a, |workspace, cx| {
7190        workspace.toggle_follow(client_b_id, cx).unwrap()
7191    });
7192    let b_follow_a = workspace_b.update(cx_b, |workspace, cx| {
7193        workspace.toggle_follow(client_a_id, cx).unwrap()
7194    });
7195
7196    futures::try_join!(a_follow_b, b_follow_a).unwrap();
7197    workspace_a.read_with(cx_a, |workspace, _| {
7198        assert_eq!(
7199            workspace.leader_for_pane(workspace.active_pane()),
7200            Some(client_b_id)
7201        );
7202    });
7203    workspace_b.read_with(cx_b, |workspace, _| {
7204        assert_eq!(
7205            workspace.leader_for_pane(workspace.active_pane()),
7206            Some(client_a_id)
7207        );
7208    });
7209}
7210
7211#[derive(Debug, Eq, PartialEq)]
7212struct RoomParticipants {
7213    remote: Vec<String>,
7214    pending: Vec<String>,
7215}
7216
7217fn room_participants(room: &ModelHandle<Room>, cx: &mut TestAppContext) -> RoomParticipants {
7218    room.read_with(cx, |room, _| {
7219        let mut remote = room
7220            .remote_participants()
7221            .iter()
7222            .map(|(_, participant)| participant.user.github_login.clone())
7223            .collect::<Vec<_>>();
7224        let mut pending = room
7225            .pending_participants()
7226            .iter()
7227            .map(|user| user.github_login.clone())
7228            .collect::<Vec<_>>();
7229        remote.sort();
7230        pending.sort();
7231        RoomParticipants { remote, pending }
7232    })
7233}