integration_tests.rs

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