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