integration_tests.rs

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