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