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