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