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