integration_tests.rs

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