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    deterministic.run_until_parked();
2575    
2576    worktree_a.read_with(cx_a, |worktree, _| {
2577        assert_eq!(
2578            worktree
2579                .paths()
2580                .map(|p| p.to_string_lossy())
2581                .collect::<Vec<_>>(),
2582            ["a.txt", "b.txt", "d.txt", "f.txt"]
2583        );
2584    });
2585    worktree_b.read_with(cx_b, |worktree, _| {
2586        assert_eq!(
2587            worktree
2588                .paths()
2589                .map(|p| p.to_string_lossy())
2590                .collect::<Vec<_>>(),
2591            ["a.txt", "b.txt", "d.txt", "f.txt"]
2592        );
2593    });
2594
2595    project_b
2596        .update(cx_b, |project, cx| {
2597            project.delete_entry(entry.id, cx).unwrap()
2598        })
2599        .await
2600        .unwrap();
2601    worktree_a.read_with(cx_a, |worktree, _| {
2602        assert_eq!(
2603            worktree
2604                .paths()
2605                .map(|p| p.to_string_lossy())
2606                .collect::<Vec<_>>(),
2607            ["a.txt", "b.txt", "f.txt"]
2608        );
2609    });
2610    worktree_b.read_with(cx_b, |worktree, _| {
2611        assert_eq!(
2612            worktree
2613                .paths()
2614                .map(|p| p.to_string_lossy())
2615                .collect::<Vec<_>>(),
2616            ["a.txt", "b.txt", "f.txt"]
2617        );
2618    });
2619}
2620
2621#[gpui::test(iterations = 10)]
2622async fn test_buffer_conflict_after_save(
2623    deterministic: Arc<Deterministic>,
2624    cx_a: &mut TestAppContext,
2625    cx_b: &mut TestAppContext,
2626) {
2627    deterministic.forbid_parking();
2628    let mut server = TestServer::start(&deterministic).await;
2629    let client_a = server.create_client(cx_a, "user_a").await;
2630    let client_b = server.create_client(cx_b, "user_b").await;
2631    server
2632        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2633        .await;
2634    let active_call_a = cx_a.read(ActiveCall::global);
2635
2636    client_a
2637        .fs
2638        .insert_tree(
2639            "/dir",
2640            json!({
2641                "a.txt": "a-contents",
2642            }),
2643        )
2644        .await;
2645    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2646    let project_id = active_call_a
2647        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2648        .await
2649        .unwrap();
2650    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2651
2652    // Open a buffer as client B
2653    let buffer_b = project_b
2654        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2655        .await
2656        .unwrap();
2657
2658    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "world ")], None, cx));
2659    buffer_b.read_with(cx_b, |buf, _| {
2660        assert!(buf.is_dirty());
2661        assert!(!buf.has_conflict());
2662    });
2663
2664    buffer_b.update(cx_b, |buf, cx| buf.save(cx)).await.unwrap();
2665    cx_a.foreground().forbid_parking();
2666    buffer_b.read_with(cx_b, |buffer_b, _| assert!(!buffer_b.is_dirty()));
2667    buffer_b.read_with(cx_b, |buf, _| {
2668        assert!(!buf.has_conflict());
2669    });
2670
2671    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "hello ")], None, cx));
2672    buffer_b.read_with(cx_b, |buf, _| {
2673        assert!(buf.is_dirty());
2674        assert!(!buf.has_conflict());
2675    });
2676}
2677
2678#[gpui::test(iterations = 10)]
2679async fn test_buffer_reloading(
2680    deterministic: Arc<Deterministic>,
2681    cx_a: &mut TestAppContext,
2682    cx_b: &mut TestAppContext,
2683) {
2684    deterministic.forbid_parking();
2685    let mut server = TestServer::start(&deterministic).await;
2686    let client_a = server.create_client(cx_a, "user_a").await;
2687    let client_b = server.create_client(cx_b, "user_b").await;
2688    server
2689        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2690        .await;
2691    let active_call_a = cx_a.read(ActiveCall::global);
2692
2693    client_a
2694        .fs
2695        .insert_tree(
2696            "/dir",
2697            json!({
2698                "a.txt": "a\nb\nc",
2699            }),
2700        )
2701        .await;
2702    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2703    let project_id = active_call_a
2704        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2705        .await
2706        .unwrap();
2707    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2708
2709    // Open a buffer as client B
2710    let buffer_b = project_b
2711        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2712        .await
2713        .unwrap();
2714    buffer_b.read_with(cx_b, |buf, _| {
2715        assert!(!buf.is_dirty());
2716        assert!(!buf.has_conflict());
2717        assert_eq!(buf.line_ending(), LineEnding::Unix);
2718    });
2719
2720    let new_contents = Rope::from("d\ne\nf");
2721    client_a
2722        .fs
2723        .save("/dir/a.txt".as_ref(), &new_contents, LineEnding::Windows)
2724        .await
2725        .unwrap();
2726    cx_a.foreground().run_until_parked();
2727    buffer_b.read_with(cx_b, |buf, _| {
2728        assert_eq!(buf.text(), new_contents.to_string());
2729        assert!(!buf.is_dirty());
2730        assert!(!buf.has_conflict());
2731        assert_eq!(buf.line_ending(), LineEnding::Windows);
2732    });
2733}
2734
2735#[gpui::test(iterations = 10)]
2736async fn test_editing_while_guest_opens_buffer(
2737    deterministic: Arc<Deterministic>,
2738    cx_a: &mut TestAppContext,
2739    cx_b: &mut TestAppContext,
2740) {
2741    deterministic.forbid_parking();
2742    let mut server = TestServer::start(&deterministic).await;
2743    let client_a = server.create_client(cx_a, "user_a").await;
2744    let client_b = server.create_client(cx_b, "user_b").await;
2745    server
2746        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2747        .await;
2748    let active_call_a = cx_a.read(ActiveCall::global);
2749
2750    client_a
2751        .fs
2752        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
2753        .await;
2754    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2755    let project_id = active_call_a
2756        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2757        .await
2758        .unwrap();
2759    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2760
2761    // Open a buffer as client A
2762    let buffer_a = project_a
2763        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2764        .await
2765        .unwrap();
2766
2767    // Start opening the same buffer as client B
2768    let buffer_b = cx_b
2769        .background()
2770        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)));
2771
2772    // Edit the buffer as client A while client B is still opening it.
2773    cx_b.background().simulate_random_delay().await;
2774    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "X")], None, cx));
2775    cx_b.background().simulate_random_delay().await;
2776    buffer_a.update(cx_a, |buf, cx| buf.edit([(1..1, "Y")], None, cx));
2777
2778    let text = buffer_a.read_with(cx_a, |buf, _| buf.text());
2779    let buffer_b = buffer_b.await.unwrap();
2780    cx_a.foreground().run_until_parked();
2781    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), text));
2782}
2783
2784#[gpui::test(iterations = 10)]
2785async fn test_leaving_worktree_while_opening_buffer(
2786    deterministic: Arc<Deterministic>,
2787    cx_a: &mut TestAppContext,
2788    cx_b: &mut TestAppContext,
2789) {
2790    deterministic.forbid_parking();
2791    let mut server = TestServer::start(&deterministic).await;
2792    let client_a = server.create_client(cx_a, "user_a").await;
2793    let client_b = server.create_client(cx_b, "user_b").await;
2794    server
2795        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2796        .await;
2797    let active_call_a = cx_a.read(ActiveCall::global);
2798
2799    client_a
2800        .fs
2801        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
2802        .await;
2803    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2804    let project_id = active_call_a
2805        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2806        .await
2807        .unwrap();
2808    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2809
2810    // See that a guest has joined as client A.
2811    cx_a.foreground().run_until_parked();
2812    project_a.read_with(cx_a, |p, _| assert_eq!(p.collaborators().len(), 1));
2813
2814    // Begin opening a buffer as client B, but leave the project before the open completes.
2815    let buffer_b = cx_b
2816        .background()
2817        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)));
2818    cx_b.update(|_| drop(project_b));
2819    drop(buffer_b);
2820
2821    // See that the guest has left.
2822    cx_a.foreground().run_until_parked();
2823    project_a.read_with(cx_a, |p, _| assert!(p.collaborators().is_empty()));
2824}
2825
2826#[gpui::test(iterations = 10)]
2827async fn test_canceling_buffer_opening(
2828    deterministic: Arc<Deterministic>,
2829    cx_a: &mut TestAppContext,
2830    cx_b: &mut TestAppContext,
2831) {
2832    deterministic.forbid_parking();
2833
2834    let mut server = TestServer::start(&deterministic).await;
2835    let client_a = server.create_client(cx_a, "user_a").await;
2836    let client_b = server.create_client(cx_b, "user_b").await;
2837    server
2838        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2839        .await;
2840    let active_call_a = cx_a.read(ActiveCall::global);
2841
2842    client_a
2843        .fs
2844        .insert_tree(
2845            "/dir",
2846            json!({
2847                "a.txt": "abc",
2848            }),
2849        )
2850        .await;
2851    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2852    let project_id = active_call_a
2853        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2854        .await
2855        .unwrap();
2856    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2857
2858    let buffer_a = project_a
2859        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2860        .await
2861        .unwrap();
2862
2863    // Open a buffer as client B but cancel after a random amount of time.
2864    let buffer_b = project_b.update(cx_b, |p, cx| p.open_buffer_by_id(buffer_a.id() as u64, cx));
2865    deterministic.simulate_random_delay().await;
2866    drop(buffer_b);
2867
2868    // Try opening the same buffer again as client B, and ensure we can
2869    // still do it despite the cancellation above.
2870    let buffer_b = project_b
2871        .update(cx_b, |p, cx| p.open_buffer_by_id(buffer_a.id() as u64, cx))
2872        .await
2873        .unwrap();
2874    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "abc"));
2875}
2876
2877#[gpui::test(iterations = 10)]
2878async fn test_leaving_project(
2879    deterministic: Arc<Deterministic>,
2880    cx_a: &mut TestAppContext,
2881    cx_b: &mut TestAppContext,
2882    cx_c: &mut TestAppContext,
2883) {
2884    deterministic.forbid_parking();
2885    let mut server = TestServer::start(&deterministic).await;
2886    let client_a = server.create_client(cx_a, "user_a").await;
2887    let client_b = server.create_client(cx_b, "user_b").await;
2888    let client_c = server.create_client(cx_c, "user_c").await;
2889    server
2890        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
2891        .await;
2892    let active_call_a = cx_a.read(ActiveCall::global);
2893
2894    client_a
2895        .fs
2896        .insert_tree(
2897            "/a",
2898            json!({
2899                "a.txt": "a-contents",
2900                "b.txt": "b-contents",
2901            }),
2902        )
2903        .await;
2904    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
2905    let project_id = active_call_a
2906        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2907        .await
2908        .unwrap();
2909    let project_b1 = client_b.build_remote_project(project_id, cx_b).await;
2910    let project_c = client_c.build_remote_project(project_id, cx_c).await;
2911
2912    // Client A sees that a guest has joined.
2913    deterministic.run_until_parked();
2914    project_a.read_with(cx_a, |project, _| {
2915        assert_eq!(project.collaborators().len(), 2);
2916    });
2917    project_b1.read_with(cx_b, |project, _| {
2918        assert_eq!(project.collaborators().len(), 2);
2919    });
2920    project_c.read_with(cx_c, |project, _| {
2921        assert_eq!(project.collaborators().len(), 2);
2922    });
2923
2924    // Client B opens a buffer.
2925    let buffer_b1 = project_b1
2926        .update(cx_b, |project, cx| {
2927            let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
2928            project.open_buffer((worktree_id, "a.txt"), cx)
2929        })
2930        .await
2931        .unwrap();
2932    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
2933
2934    // Drop client B's project and ensure client A and client C observe client B leaving.
2935    cx_b.update(|_| drop(project_b1));
2936    deterministic.run_until_parked();
2937    project_a.read_with(cx_a, |project, _| {
2938        assert_eq!(project.collaborators().len(), 1);
2939    });
2940    project_c.read_with(cx_c, |project, _| {
2941        assert_eq!(project.collaborators().len(), 1);
2942    });
2943
2944    // Client B re-joins the project and can open buffers as before.
2945    let project_b2 = client_b.build_remote_project(project_id, cx_b).await;
2946    deterministic.run_until_parked();
2947    project_a.read_with(cx_a, |project, _| {
2948        assert_eq!(project.collaborators().len(), 2);
2949    });
2950    project_b2.read_with(cx_b, |project, _| {
2951        assert_eq!(project.collaborators().len(), 2);
2952    });
2953    project_c.read_with(cx_c, |project, _| {
2954        assert_eq!(project.collaborators().len(), 2);
2955    });
2956
2957    let buffer_b2 = project_b2
2958        .update(cx_b, |project, cx| {
2959            let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
2960            project.open_buffer((worktree_id, "a.txt"), cx)
2961        })
2962        .await
2963        .unwrap();
2964    buffer_b2.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
2965
2966    // Drop client B's connection and ensure client A and client C observe client B leaving.
2967    client_b.disconnect(&cx_b.to_async()).unwrap();
2968    deterministic.advance_clock(RECONNECT_TIMEOUT);
2969    project_a.read_with(cx_a, |project, _| {
2970        assert_eq!(project.collaborators().len(), 1);
2971    });
2972    project_b2.read_with(cx_b, |project, _| {
2973        assert!(project.is_read_only());
2974    });
2975    project_c.read_with(cx_c, |project, _| {
2976        assert_eq!(project.collaborators().len(), 1);
2977    });
2978
2979    // Client B can't join the project, unless they re-join the room.
2980    cx_b.spawn(|cx| {
2981        Project::remote(
2982            project_id,
2983            client_b.client.clone(),
2984            client_b.user_store.clone(),
2985            client_b.language_registry.clone(),
2986            FakeFs::new(cx.background()),
2987            cx,
2988        )
2989    })
2990    .await
2991    .unwrap_err();
2992
2993    // Simulate connection loss for client C and ensure client A observes client C leaving the project.
2994    client_c.wait_for_current_user(cx_c).await;
2995    server.forbid_connections();
2996    server.disconnect_client(client_c.peer_id().unwrap());
2997    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
2998    deterministic.run_until_parked();
2999    project_a.read_with(cx_a, |project, _| {
3000        assert_eq!(project.collaborators().len(), 0);
3001    });
3002    project_b2.read_with(cx_b, |project, _| {
3003        assert!(project.is_read_only());
3004    });
3005    project_c.read_with(cx_c, |project, _| {
3006        assert!(project.is_read_only());
3007    });
3008}
3009
3010#[gpui::test(iterations = 10)]
3011async fn test_collaborating_with_diagnostics(
3012    deterministic: Arc<Deterministic>,
3013    cx_a: &mut TestAppContext,
3014    cx_b: &mut TestAppContext,
3015    cx_c: &mut TestAppContext,
3016) {
3017    deterministic.forbid_parking();
3018    let mut server = TestServer::start(&deterministic).await;
3019    let client_a = server.create_client(cx_a, "user_a").await;
3020    let client_b = server.create_client(cx_b, "user_b").await;
3021    let client_c = server.create_client(cx_c, "user_c").await;
3022    server
3023        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3024        .await;
3025    let active_call_a = cx_a.read(ActiveCall::global);
3026
3027    // Set up a fake language server.
3028    let mut language = Language::new(
3029        LanguageConfig {
3030            name: "Rust".into(),
3031            path_suffixes: vec!["rs".to_string()],
3032            ..Default::default()
3033        },
3034        Some(tree_sitter_rust::language()),
3035    );
3036    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
3037    client_a.language_registry.add(Arc::new(language));
3038
3039    // Share a project as client A
3040    client_a
3041        .fs
3042        .insert_tree(
3043            "/a",
3044            json!({
3045                "a.rs": "let one = two",
3046                "other.rs": "",
3047            }),
3048        )
3049        .await;
3050    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3051
3052    // Cause the language server to start.
3053    let _buffer = project_a
3054        .update(cx_a, |project, cx| {
3055            project.open_buffer(
3056                ProjectPath {
3057                    worktree_id,
3058                    path: Path::new("other.rs").into(),
3059                },
3060                cx,
3061            )
3062        })
3063        .await
3064        .unwrap();
3065
3066    // Simulate a language server reporting errors for a file.
3067    let mut fake_language_server = fake_language_servers.next().await.unwrap();
3068    fake_language_server
3069        .receive_notification::<lsp::notification::DidOpenTextDocument>()
3070        .await;
3071    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3072        lsp::PublishDiagnosticsParams {
3073            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3074            version: None,
3075            diagnostics: vec![lsp::Diagnostic {
3076                severity: Some(lsp::DiagnosticSeverity::WARNING),
3077                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3078                message: "message 0".to_string(),
3079                ..Default::default()
3080            }],
3081        },
3082    );
3083
3084    // Client A shares the project and, simultaneously, the language server
3085    // publishes a diagnostic. This is done to ensure that the server always
3086    // observes the latest diagnostics for a worktree.
3087    let project_id = active_call_a
3088        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3089        .await
3090        .unwrap();
3091    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3092        lsp::PublishDiagnosticsParams {
3093            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3094            version: None,
3095            diagnostics: vec![lsp::Diagnostic {
3096                severity: Some(lsp::DiagnosticSeverity::ERROR),
3097                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3098                message: "message 1".to_string(),
3099                ..Default::default()
3100            }],
3101        },
3102    );
3103
3104    // Join the worktree as client B.
3105    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3106
3107    // Wait for server to see the diagnostics update.
3108    deterministic.run_until_parked();
3109
3110    // Ensure client B observes the new diagnostics.
3111    project_b.read_with(cx_b, |project, cx| {
3112        assert_eq!(
3113            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
3114            &[(
3115                ProjectPath {
3116                    worktree_id,
3117                    path: Arc::from(Path::new("a.rs")),
3118                },
3119                DiagnosticSummary {
3120                    error_count: 1,
3121                    warning_count: 0,
3122                    ..Default::default()
3123                },
3124            )]
3125        )
3126    });
3127
3128    // Join project as client C and observe the diagnostics.
3129    let project_c = client_c.build_remote_project(project_id, cx_c).await;
3130    let project_c_diagnostic_summaries =
3131        Rc::new(RefCell::new(project_c.read_with(cx_c, |project, cx| {
3132            project.diagnostic_summaries(cx).collect::<Vec<_>>()
3133        })));
3134    project_c.update(cx_c, |_, cx| {
3135        let summaries = project_c_diagnostic_summaries.clone();
3136        cx.subscribe(&project_c, {
3137            move |p, _, event, cx| {
3138                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
3139                    *summaries.borrow_mut() = p.diagnostic_summaries(cx).collect();
3140                }
3141            }
3142        })
3143        .detach();
3144    });
3145
3146    deterministic.run_until_parked();
3147    assert_eq!(
3148        project_c_diagnostic_summaries.borrow().as_slice(),
3149        &[(
3150            ProjectPath {
3151                worktree_id,
3152                path: Arc::from(Path::new("a.rs")),
3153            },
3154            DiagnosticSummary {
3155                error_count: 1,
3156                warning_count: 0,
3157                ..Default::default()
3158            },
3159        )]
3160    );
3161
3162    // Simulate a language server reporting more errors for a file.
3163    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3164        lsp::PublishDiagnosticsParams {
3165            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3166            version: None,
3167            diagnostics: vec![
3168                lsp::Diagnostic {
3169                    severity: Some(lsp::DiagnosticSeverity::ERROR),
3170                    range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3171                    message: "message 1".to_string(),
3172                    ..Default::default()
3173                },
3174                lsp::Diagnostic {
3175                    severity: Some(lsp::DiagnosticSeverity::WARNING),
3176                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 13)),
3177                    message: "message 2".to_string(),
3178                    ..Default::default()
3179                },
3180            ],
3181        },
3182    );
3183
3184    // Clients B and C get the updated summaries
3185    deterministic.run_until_parked();
3186    project_b.read_with(cx_b, |project, cx| {
3187        assert_eq!(
3188            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
3189            [(
3190                ProjectPath {
3191                    worktree_id,
3192                    path: Arc::from(Path::new("a.rs")),
3193                },
3194                DiagnosticSummary {
3195                    error_count: 1,
3196                    warning_count: 1,
3197                    ..Default::default()
3198                },
3199            )]
3200        );
3201    });
3202    project_c.read_with(cx_c, |project, cx| {
3203        assert_eq!(
3204            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
3205            [(
3206                ProjectPath {
3207                    worktree_id,
3208                    path: Arc::from(Path::new("a.rs")),
3209                },
3210                DiagnosticSummary {
3211                    error_count: 1,
3212                    warning_count: 1,
3213                    ..Default::default()
3214                },
3215            )]
3216        );
3217    });
3218
3219    // Open the file with the errors on client B. They should be present.
3220    let buffer_b = cx_b
3221        .background()
3222        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
3223        .await
3224        .unwrap();
3225
3226    buffer_b.read_with(cx_b, |buffer, _| {
3227        assert_eq!(
3228            buffer
3229                .snapshot()
3230                .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
3231                .collect::<Vec<_>>(),
3232            &[
3233                DiagnosticEntry {
3234                    range: Point::new(0, 4)..Point::new(0, 7),
3235                    diagnostic: Diagnostic {
3236                        group_id: 2,
3237                        message: "message 1".to_string(),
3238                        severity: lsp::DiagnosticSeverity::ERROR,
3239                        is_primary: true,
3240                        ..Default::default()
3241                    }
3242                },
3243                DiagnosticEntry {
3244                    range: Point::new(0, 10)..Point::new(0, 13),
3245                    diagnostic: Diagnostic {
3246                        group_id: 3,
3247                        severity: lsp::DiagnosticSeverity::WARNING,
3248                        message: "message 2".to_string(),
3249                        is_primary: true,
3250                        ..Default::default()
3251                    }
3252                }
3253            ]
3254        );
3255    });
3256
3257    // Simulate a language server reporting no errors for a file.
3258    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3259        lsp::PublishDiagnosticsParams {
3260            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3261            version: None,
3262            diagnostics: vec![],
3263        },
3264    );
3265    deterministic.run_until_parked();
3266    project_a.read_with(cx_a, |project, cx| {
3267        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
3268    });
3269    project_b.read_with(cx_b, |project, cx| {
3270        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
3271    });
3272    project_c.read_with(cx_c, |project, cx| {
3273        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
3274    });
3275}
3276
3277#[gpui::test(iterations = 10)]
3278async fn test_collaborating_with_completion(
3279    deterministic: Arc<Deterministic>,
3280    cx_a: &mut TestAppContext,
3281    cx_b: &mut TestAppContext,
3282) {
3283    deterministic.forbid_parking();
3284    let mut server = TestServer::start(&deterministic).await;
3285    let client_a = server.create_client(cx_a, "user_a").await;
3286    let client_b = server.create_client(cx_b, "user_b").await;
3287    server
3288        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3289        .await;
3290    let active_call_a = cx_a.read(ActiveCall::global);
3291
3292    // Set up a fake language server.
3293    let mut language = Language::new(
3294        LanguageConfig {
3295            name: "Rust".into(),
3296            path_suffixes: vec!["rs".to_string()],
3297            ..Default::default()
3298        },
3299        Some(tree_sitter_rust::language()),
3300    );
3301    let mut fake_language_servers = language
3302        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3303            capabilities: lsp::ServerCapabilities {
3304                completion_provider: Some(lsp::CompletionOptions {
3305                    trigger_characters: Some(vec![".".to_string()]),
3306                    ..Default::default()
3307                }),
3308                ..Default::default()
3309            },
3310            ..Default::default()
3311        }))
3312        .await;
3313    client_a.language_registry.add(Arc::new(language));
3314
3315    client_a
3316        .fs
3317        .insert_tree(
3318            "/a",
3319            json!({
3320                "main.rs": "fn main() { a }",
3321                "other.rs": "",
3322            }),
3323        )
3324        .await;
3325    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3326    let project_id = active_call_a
3327        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3328        .await
3329        .unwrap();
3330    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3331
3332    // Open a file in an editor as the guest.
3333    let buffer_b = project_b
3334        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
3335        .await
3336        .unwrap();
3337    let (_, window_b) = cx_b.add_window(|_| EmptyView);
3338    let editor_b = cx_b.add_view(&window_b, |cx| {
3339        Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx)
3340    });
3341
3342    let fake_language_server = fake_language_servers.next().await.unwrap();
3343    cx_a.foreground().run_until_parked();
3344    buffer_b.read_with(cx_b, |buffer, _| {
3345        assert!(!buffer.completion_triggers().is_empty())
3346    });
3347
3348    // Type a completion trigger character as the guest.
3349    editor_b.update(cx_b, |editor, cx| {
3350        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
3351        editor.handle_input(".", cx);
3352        cx.focus(&editor_b);
3353    });
3354
3355    // Receive a completion request as the host's language server.
3356    // Return some completions from the host's language server.
3357    cx_a.foreground().start_waiting();
3358    fake_language_server
3359        .handle_request::<lsp::request::Completion, _, _>(|params, _| async move {
3360            assert_eq!(
3361                params.text_document_position.text_document.uri,
3362                lsp::Url::from_file_path("/a/main.rs").unwrap(),
3363            );
3364            assert_eq!(
3365                params.text_document_position.position,
3366                lsp::Position::new(0, 14),
3367            );
3368
3369            Ok(Some(lsp::CompletionResponse::Array(vec![
3370                lsp::CompletionItem {
3371                    label: "first_method(…)".into(),
3372                    detail: Some("fn(&mut self, B) -> C".into()),
3373                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
3374                        new_text: "first_method($1)".to_string(),
3375                        range: lsp::Range::new(
3376                            lsp::Position::new(0, 14),
3377                            lsp::Position::new(0, 14),
3378                        ),
3379                    })),
3380                    insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
3381                    ..Default::default()
3382                },
3383                lsp::CompletionItem {
3384                    label: "second_method(…)".into(),
3385                    detail: Some("fn(&mut self, C) -> D<E>".into()),
3386                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
3387                        new_text: "second_method()".to_string(),
3388                        range: lsp::Range::new(
3389                            lsp::Position::new(0, 14),
3390                            lsp::Position::new(0, 14),
3391                        ),
3392                    })),
3393                    insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
3394                    ..Default::default()
3395                },
3396            ])))
3397        })
3398        .next()
3399        .await
3400        .unwrap();
3401    cx_a.foreground().finish_waiting();
3402
3403    // Open the buffer on the host.
3404    let buffer_a = project_a
3405        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
3406        .await
3407        .unwrap();
3408    cx_a.foreground().run_until_parked();
3409    buffer_a.read_with(cx_a, |buffer, _| {
3410        assert_eq!(buffer.text(), "fn main() { a. }")
3411    });
3412
3413    // Confirm a completion on the guest.
3414    editor_b.read_with(cx_b, |editor, _| assert!(editor.context_menu_visible()));
3415    editor_b.update(cx_b, |editor, cx| {
3416        editor.confirm_completion(&ConfirmCompletion { item_ix: Some(0) }, cx);
3417        assert_eq!(editor.text(cx), "fn main() { a.first_method() }");
3418    });
3419
3420    // Return a resolved completion from the host's language server.
3421    // The resolved completion has an additional text edit.
3422    fake_language_server.handle_request::<lsp::request::ResolveCompletionItem, _, _>(
3423        |params, _| async move {
3424            assert_eq!(params.label, "first_method(…)");
3425            Ok(lsp::CompletionItem {
3426                label: "first_method(…)".into(),
3427                detail: Some("fn(&mut self, B) -> C".into()),
3428                text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
3429                    new_text: "first_method($1)".to_string(),
3430                    range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
3431                })),
3432                additional_text_edits: Some(vec![lsp::TextEdit {
3433                    new_text: "use d::SomeTrait;\n".to_string(),
3434                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
3435                }]),
3436                insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
3437                ..Default::default()
3438            })
3439        },
3440    );
3441
3442    // The additional edit is applied.
3443    cx_a.foreground().run_until_parked();
3444    buffer_a.read_with(cx_a, |buffer, _| {
3445        assert_eq!(
3446            buffer.text(),
3447            "use d::SomeTrait;\nfn main() { a.first_method() }"
3448        );
3449    });
3450    buffer_b.read_with(cx_b, |buffer, _| {
3451        assert_eq!(
3452            buffer.text(),
3453            "use d::SomeTrait;\nfn main() { a.first_method() }"
3454        );
3455    });
3456}
3457
3458#[gpui::test(iterations = 10)]
3459async fn test_reloading_buffer_manually(
3460    deterministic: Arc<Deterministic>,
3461    cx_a: &mut TestAppContext,
3462    cx_b: &mut TestAppContext,
3463) {
3464    deterministic.forbid_parking();
3465    let mut server = TestServer::start(&deterministic).await;
3466    let client_a = server.create_client(cx_a, "user_a").await;
3467    let client_b = server.create_client(cx_b, "user_b").await;
3468    server
3469        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3470        .await;
3471    let active_call_a = cx_a.read(ActiveCall::global);
3472
3473    client_a
3474        .fs
3475        .insert_tree("/a", json!({ "a.rs": "let one = 1;" }))
3476        .await;
3477    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3478    let buffer_a = project_a
3479        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
3480        .await
3481        .unwrap();
3482    let project_id = active_call_a
3483        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3484        .await
3485        .unwrap();
3486
3487    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3488
3489    let buffer_b = cx_b
3490        .background()
3491        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
3492        .await
3493        .unwrap();
3494    buffer_b.update(cx_b, |buffer, cx| {
3495        buffer.edit([(4..7, "six")], None, cx);
3496        buffer.edit([(10..11, "6")], None, cx);
3497        assert_eq!(buffer.text(), "let six = 6;");
3498        assert!(buffer.is_dirty());
3499        assert!(!buffer.has_conflict());
3500    });
3501    cx_a.foreground().run_until_parked();
3502    buffer_a.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "let six = 6;"));
3503
3504    client_a
3505        .fs
3506        .save(
3507            "/a/a.rs".as_ref(),
3508            &Rope::from("let seven = 7;"),
3509            LineEnding::Unix,
3510        )
3511        .await
3512        .unwrap();
3513    cx_a.foreground().run_until_parked();
3514    buffer_a.read_with(cx_a, |buffer, _| assert!(buffer.has_conflict()));
3515    buffer_b.read_with(cx_b, |buffer, _| assert!(buffer.has_conflict()));
3516
3517    project_b
3518        .update(cx_b, |project, cx| {
3519            project.reload_buffers(HashSet::from_iter([buffer_b.clone()]), true, cx)
3520        })
3521        .await
3522        .unwrap();
3523    buffer_a.read_with(cx_a, |buffer, _| {
3524        assert_eq!(buffer.text(), "let seven = 7;");
3525        assert!(!buffer.is_dirty());
3526        assert!(!buffer.has_conflict());
3527    });
3528    buffer_b.read_with(cx_b, |buffer, _| {
3529        assert_eq!(buffer.text(), "let seven = 7;");
3530        assert!(!buffer.is_dirty());
3531        assert!(!buffer.has_conflict());
3532    });
3533
3534    buffer_a.update(cx_a, |buffer, cx| {
3535        // Undoing on the host is a no-op when the reload was initiated by the guest.
3536        buffer.undo(cx);
3537        assert_eq!(buffer.text(), "let seven = 7;");
3538        assert!(!buffer.is_dirty());
3539        assert!(!buffer.has_conflict());
3540    });
3541    buffer_b.update(cx_b, |buffer, cx| {
3542        // Undoing on the guest rolls back the buffer to before it was reloaded but the conflict gets cleared.
3543        buffer.undo(cx);
3544        assert_eq!(buffer.text(), "let six = 6;");
3545        assert!(buffer.is_dirty());
3546        assert!(!buffer.has_conflict());
3547    });
3548}
3549
3550#[gpui::test(iterations = 10)]
3551async fn test_formatting_buffer(
3552    deterministic: Arc<Deterministic>,
3553    cx_a: &mut TestAppContext,
3554    cx_b: &mut TestAppContext,
3555) {
3556    use project::FormatTrigger;
3557
3558    let mut server = TestServer::start(&deterministic).await;
3559    let client_a = server.create_client(cx_a, "user_a").await;
3560    let client_b = server.create_client(cx_b, "user_b").await;
3561    server
3562        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3563        .await;
3564    let active_call_a = cx_a.read(ActiveCall::global);
3565
3566    // Set up a fake language server.
3567    let mut language = Language::new(
3568        LanguageConfig {
3569            name: "Rust".into(),
3570            path_suffixes: vec!["rs".to_string()],
3571            ..Default::default()
3572        },
3573        Some(tree_sitter_rust::language()),
3574    );
3575    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
3576    client_a.language_registry.add(Arc::new(language));
3577
3578    // Here we insert a fake tree with a directory that exists on disk. This is needed
3579    // because later we'll invoke a command, which requires passing a working directory
3580    // that points to a valid location on disk.
3581    let directory = env::current_dir().unwrap();
3582    client_a
3583        .fs
3584        .insert_tree(&directory, json!({ "a.rs": "let one = \"two\"" }))
3585        .await;
3586    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
3587    let project_id = active_call_a
3588        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3589        .await
3590        .unwrap();
3591    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3592
3593    let buffer_b = cx_b
3594        .background()
3595        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
3596        .await
3597        .unwrap();
3598
3599    let fake_language_server = fake_language_servers.next().await.unwrap();
3600    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
3601        Ok(Some(vec![
3602            lsp::TextEdit {
3603                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 4)),
3604                new_text: "h".to_string(),
3605            },
3606            lsp::TextEdit {
3607                range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 7)),
3608                new_text: "y".to_string(),
3609            },
3610        ]))
3611    });
3612
3613    project_b
3614        .update(cx_b, |project, cx| {
3615            project.format(
3616                HashSet::from_iter([buffer_b.clone()]),
3617                true,
3618                FormatTrigger::Save,
3619                cx,
3620            )
3621        })
3622        .await
3623        .unwrap();
3624    assert_eq!(
3625        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
3626        "let honey = \"two\""
3627    );
3628
3629    // Ensure buffer can be formatted using an external command. Notice how the
3630    // host's configuration is honored as opposed to using the guest's settings.
3631    cx_a.update(|cx| {
3632        cx.update_global(|settings: &mut Settings, _| {
3633            settings.editor_defaults.formatter = Some(Formatter::External {
3634                command: "awk".to_string(),
3635                arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()],
3636            });
3637        });
3638    });
3639    project_b
3640        .update(cx_b, |project, cx| {
3641            project.format(
3642                HashSet::from_iter([buffer_b.clone()]),
3643                true,
3644                FormatTrigger::Save,
3645                cx,
3646            )
3647        })
3648        .await
3649        .unwrap();
3650    assert_eq!(
3651        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
3652        format!("let honey = \"{}/a.rs\"\n", directory.to_str().unwrap())
3653    );
3654}
3655
3656#[gpui::test(iterations = 10)]
3657async fn test_definition(
3658    deterministic: Arc<Deterministic>,
3659    cx_a: &mut TestAppContext,
3660    cx_b: &mut TestAppContext,
3661) {
3662    deterministic.forbid_parking();
3663    let mut server = TestServer::start(&deterministic).await;
3664    let client_a = server.create_client(cx_a, "user_a").await;
3665    let client_b = server.create_client(cx_b, "user_b").await;
3666    server
3667        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3668        .await;
3669    let active_call_a = cx_a.read(ActiveCall::global);
3670
3671    // Set up a fake language server.
3672    let mut language = Language::new(
3673        LanguageConfig {
3674            name: "Rust".into(),
3675            path_suffixes: vec!["rs".to_string()],
3676            ..Default::default()
3677        },
3678        Some(tree_sitter_rust::language()),
3679    );
3680    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
3681    client_a.language_registry.add(Arc::new(language));
3682
3683    client_a
3684        .fs
3685        .insert_tree(
3686            "/root",
3687            json!({
3688                "dir-1": {
3689                    "a.rs": "const ONE: usize = b::TWO + b::THREE;",
3690                },
3691                "dir-2": {
3692                    "b.rs": "const TWO: c::T2 = 2;\nconst THREE: usize = 3;",
3693                    "c.rs": "type T2 = usize;",
3694                }
3695            }),
3696        )
3697        .await;
3698    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
3699    let project_id = active_call_a
3700        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3701        .await
3702        .unwrap();
3703    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3704
3705    // Open the file on client B.
3706    let buffer_b = cx_b
3707        .background()
3708        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
3709        .await
3710        .unwrap();
3711
3712    // Request the definition of a symbol as the guest.
3713    let fake_language_server = fake_language_servers.next().await.unwrap();
3714    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
3715        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
3716            lsp::Location::new(
3717                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
3718                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
3719            ),
3720        )))
3721    });
3722
3723    let definitions_1 = project_b
3724        .update(cx_b, |p, cx| p.definition(&buffer_b, 23, cx))
3725        .await
3726        .unwrap();
3727    cx_b.read(|cx| {
3728        assert_eq!(definitions_1.len(), 1);
3729        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
3730        let target_buffer = definitions_1[0].target.buffer.read(cx);
3731        assert_eq!(
3732            target_buffer.text(),
3733            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
3734        );
3735        assert_eq!(
3736            definitions_1[0].target.range.to_point(target_buffer),
3737            Point::new(0, 6)..Point::new(0, 9)
3738        );
3739    });
3740
3741    // Try getting more definitions for the same buffer, ensuring the buffer gets reused from
3742    // the previous call to `definition`.
3743    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
3744        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
3745            lsp::Location::new(
3746                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
3747                lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)),
3748            ),
3749        )))
3750    });
3751
3752    let definitions_2 = project_b
3753        .update(cx_b, |p, cx| p.definition(&buffer_b, 33, cx))
3754        .await
3755        .unwrap();
3756    cx_b.read(|cx| {
3757        assert_eq!(definitions_2.len(), 1);
3758        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
3759        let target_buffer = definitions_2[0].target.buffer.read(cx);
3760        assert_eq!(
3761            target_buffer.text(),
3762            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
3763        );
3764        assert_eq!(
3765            definitions_2[0].target.range.to_point(target_buffer),
3766            Point::new(1, 6)..Point::new(1, 11)
3767        );
3768    });
3769    assert_eq!(
3770        definitions_1[0].target.buffer,
3771        definitions_2[0].target.buffer
3772    );
3773
3774    fake_language_server.handle_request::<lsp::request::GotoTypeDefinition, _, _>(
3775        |req, _| async move {
3776            assert_eq!(
3777                req.text_document_position_params.position,
3778                lsp::Position::new(0, 7)
3779            );
3780            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
3781                lsp::Location::new(
3782                    lsp::Url::from_file_path("/root/dir-2/c.rs").unwrap(),
3783                    lsp::Range::new(lsp::Position::new(0, 5), lsp::Position::new(0, 7)),
3784                ),
3785            )))
3786        },
3787    );
3788
3789    let type_definitions = project_b
3790        .update(cx_b, |p, cx| p.type_definition(&buffer_b, 7, cx))
3791        .await
3792        .unwrap();
3793    cx_b.read(|cx| {
3794        assert_eq!(type_definitions.len(), 1);
3795        let target_buffer = type_definitions[0].target.buffer.read(cx);
3796        assert_eq!(target_buffer.text(), "type T2 = usize;");
3797        assert_eq!(
3798            type_definitions[0].target.range.to_point(target_buffer),
3799            Point::new(0, 5)..Point::new(0, 7)
3800        );
3801    });
3802}
3803
3804#[gpui::test(iterations = 10)]
3805async fn test_references(
3806    deterministic: Arc<Deterministic>,
3807    cx_a: &mut TestAppContext,
3808    cx_b: &mut TestAppContext,
3809) {
3810    deterministic.forbid_parking();
3811    let mut server = TestServer::start(&deterministic).await;
3812    let client_a = server.create_client(cx_a, "user_a").await;
3813    let client_b = server.create_client(cx_b, "user_b").await;
3814    server
3815        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3816        .await;
3817    let active_call_a = cx_a.read(ActiveCall::global);
3818
3819    // Set up a fake language server.
3820    let mut language = Language::new(
3821        LanguageConfig {
3822            name: "Rust".into(),
3823            path_suffixes: vec!["rs".to_string()],
3824            ..Default::default()
3825        },
3826        Some(tree_sitter_rust::language()),
3827    );
3828    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
3829    client_a.language_registry.add(Arc::new(language));
3830
3831    client_a
3832        .fs
3833        .insert_tree(
3834            "/root",
3835            json!({
3836                "dir-1": {
3837                    "one.rs": "const ONE: usize = 1;",
3838                    "two.rs": "const TWO: usize = one::ONE + one::ONE;",
3839                },
3840                "dir-2": {
3841                    "three.rs": "const THREE: usize = two::TWO + one::ONE;",
3842                }
3843            }),
3844        )
3845        .await;
3846    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
3847    let project_id = active_call_a
3848        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3849        .await
3850        .unwrap();
3851    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3852
3853    // Open the file on client B.
3854    let buffer_b = cx_b
3855        .background()
3856        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
3857        .await
3858        .unwrap();
3859
3860    // Request references to a symbol as the guest.
3861    let fake_language_server = fake_language_servers.next().await.unwrap();
3862    fake_language_server.handle_request::<lsp::request::References, _, _>(|params, _| async move {
3863        assert_eq!(
3864            params.text_document_position.text_document.uri.as_str(),
3865            "file:///root/dir-1/one.rs"
3866        );
3867        Ok(Some(vec![
3868            lsp::Location {
3869                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
3870                range: lsp::Range::new(lsp::Position::new(0, 24), lsp::Position::new(0, 27)),
3871            },
3872            lsp::Location {
3873                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
3874                range: lsp::Range::new(lsp::Position::new(0, 35), lsp::Position::new(0, 38)),
3875            },
3876            lsp::Location {
3877                uri: lsp::Url::from_file_path("/root/dir-2/three.rs").unwrap(),
3878                range: lsp::Range::new(lsp::Position::new(0, 37), lsp::Position::new(0, 40)),
3879            },
3880        ]))
3881    });
3882
3883    let references = project_b
3884        .update(cx_b, |p, cx| p.references(&buffer_b, 7, cx))
3885        .await
3886        .unwrap();
3887    cx_b.read(|cx| {
3888        assert_eq!(references.len(), 3);
3889        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
3890
3891        let two_buffer = references[0].buffer.read(cx);
3892        let three_buffer = references[2].buffer.read(cx);
3893        assert_eq!(
3894            two_buffer.file().unwrap().path().as_ref(),
3895            Path::new("two.rs")
3896        );
3897        assert_eq!(references[1].buffer, references[0].buffer);
3898        assert_eq!(
3899            three_buffer.file().unwrap().full_path(cx),
3900            Path::new("/root/dir-2/three.rs")
3901        );
3902
3903        assert_eq!(references[0].range.to_offset(two_buffer), 24..27);
3904        assert_eq!(references[1].range.to_offset(two_buffer), 35..38);
3905        assert_eq!(references[2].range.to_offset(three_buffer), 37..40);
3906    });
3907}
3908
3909#[gpui::test(iterations = 10)]
3910async fn test_project_search(
3911    deterministic: Arc<Deterministic>,
3912    cx_a: &mut TestAppContext,
3913    cx_b: &mut TestAppContext,
3914) {
3915    deterministic.forbid_parking();
3916    let mut server = TestServer::start(&deterministic).await;
3917    let client_a = server.create_client(cx_a, "user_a").await;
3918    let client_b = server.create_client(cx_b, "user_b").await;
3919    server
3920        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3921        .await;
3922    let active_call_a = cx_a.read(ActiveCall::global);
3923
3924    client_a
3925        .fs
3926        .insert_tree(
3927            "/root",
3928            json!({
3929                "dir-1": {
3930                    "a": "hello world",
3931                    "b": "goodnight moon",
3932                    "c": "a world of goo",
3933                    "d": "world champion of clown world",
3934                },
3935                "dir-2": {
3936                    "e": "disney world is fun",
3937                }
3938            }),
3939        )
3940        .await;
3941    let (project_a, _) = client_a.build_local_project("/root/dir-1", cx_a).await;
3942    let (worktree_2, _) = project_a
3943        .update(cx_a, |p, cx| {
3944            p.find_or_create_local_worktree("/root/dir-2", true, cx)
3945        })
3946        .await
3947        .unwrap();
3948    worktree_2
3949        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
3950        .await;
3951    let project_id = active_call_a
3952        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3953        .await
3954        .unwrap();
3955
3956    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3957
3958    // Perform a search as the guest.
3959    let results = project_b
3960        .update(cx_b, |project, cx| {
3961            project.search(SearchQuery::text("world", false, false), cx)
3962        })
3963        .await
3964        .unwrap();
3965
3966    let mut ranges_by_path = results
3967        .into_iter()
3968        .map(|(buffer, ranges)| {
3969            buffer.read_with(cx_b, |buffer, cx| {
3970                let path = buffer.file().unwrap().full_path(cx);
3971                let offset_ranges = ranges
3972                    .into_iter()
3973                    .map(|range| range.to_offset(buffer))
3974                    .collect::<Vec<_>>();
3975                (path, offset_ranges)
3976            })
3977        })
3978        .collect::<Vec<_>>();
3979    ranges_by_path.sort_by_key(|(path, _)| path.clone());
3980
3981    assert_eq!(
3982        ranges_by_path,
3983        &[
3984            (PathBuf::from("dir-1/a"), vec![6..11]),
3985            (PathBuf::from("dir-1/c"), vec![2..7]),
3986            (PathBuf::from("dir-1/d"), vec![0..5, 24..29]),
3987            (PathBuf::from("dir-2/e"), vec![7..12]),
3988        ]
3989    );
3990}
3991
3992#[gpui::test(iterations = 10)]
3993async fn test_document_highlights(
3994    deterministic: Arc<Deterministic>,
3995    cx_a: &mut TestAppContext,
3996    cx_b: &mut TestAppContext,
3997) {
3998    deterministic.forbid_parking();
3999    let mut server = TestServer::start(&deterministic).await;
4000    let client_a = server.create_client(cx_a, "user_a").await;
4001    let client_b = server.create_client(cx_b, "user_b").await;
4002    server
4003        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4004        .await;
4005    let active_call_a = cx_a.read(ActiveCall::global);
4006
4007    client_a
4008        .fs
4009        .insert_tree(
4010            "/root-1",
4011            json!({
4012                "main.rs": "fn double(number: i32) -> i32 { number + number }",
4013            }),
4014        )
4015        .await;
4016
4017    // Set up a fake language server.
4018    let mut language = Language::new(
4019        LanguageConfig {
4020            name: "Rust".into(),
4021            path_suffixes: vec!["rs".to_string()],
4022            ..Default::default()
4023        },
4024        Some(tree_sitter_rust::language()),
4025    );
4026    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4027    client_a.language_registry.add(Arc::new(language));
4028
4029    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4030    let project_id = active_call_a
4031        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4032        .await
4033        .unwrap();
4034    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4035
4036    // Open the file on client B.
4037    let buffer_b = cx_b
4038        .background()
4039        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
4040        .await
4041        .unwrap();
4042
4043    // Request document highlights as the guest.
4044    let fake_language_server = fake_language_servers.next().await.unwrap();
4045    fake_language_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
4046        |params, _| async move {
4047            assert_eq!(
4048                params
4049                    .text_document_position_params
4050                    .text_document
4051                    .uri
4052                    .as_str(),
4053                "file:///root-1/main.rs"
4054            );
4055            assert_eq!(
4056                params.text_document_position_params.position,
4057                lsp::Position::new(0, 34)
4058            );
4059            Ok(Some(vec![
4060                lsp::DocumentHighlight {
4061                    kind: Some(lsp::DocumentHighlightKind::WRITE),
4062                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 16)),
4063                },
4064                lsp::DocumentHighlight {
4065                    kind: Some(lsp::DocumentHighlightKind::READ),
4066                    range: lsp::Range::new(lsp::Position::new(0, 32), lsp::Position::new(0, 38)),
4067                },
4068                lsp::DocumentHighlight {
4069                    kind: Some(lsp::DocumentHighlightKind::READ),
4070                    range: lsp::Range::new(lsp::Position::new(0, 41), lsp::Position::new(0, 47)),
4071                },
4072            ]))
4073        },
4074    );
4075
4076    let highlights = project_b
4077        .update(cx_b, |p, cx| p.document_highlights(&buffer_b, 34, cx))
4078        .await
4079        .unwrap();
4080    buffer_b.read_with(cx_b, |buffer, _| {
4081        let snapshot = buffer.snapshot();
4082
4083        let highlights = highlights
4084            .into_iter()
4085            .map(|highlight| (highlight.kind, highlight.range.to_offset(&snapshot)))
4086            .collect::<Vec<_>>();
4087        assert_eq!(
4088            highlights,
4089            &[
4090                (lsp::DocumentHighlightKind::WRITE, 10..16),
4091                (lsp::DocumentHighlightKind::READ, 32..38),
4092                (lsp::DocumentHighlightKind::READ, 41..47)
4093            ]
4094        )
4095    });
4096}
4097
4098#[gpui::test(iterations = 10)]
4099async fn test_lsp_hover(
4100    deterministic: Arc<Deterministic>,
4101    cx_a: &mut TestAppContext,
4102    cx_b: &mut TestAppContext,
4103) {
4104    deterministic.forbid_parking();
4105    let mut server = TestServer::start(&deterministic).await;
4106    let client_a = server.create_client(cx_a, "user_a").await;
4107    let client_b = server.create_client(cx_b, "user_b").await;
4108    server
4109        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4110        .await;
4111    let active_call_a = cx_a.read(ActiveCall::global);
4112
4113    client_a
4114        .fs
4115        .insert_tree(
4116            "/root-1",
4117            json!({
4118                "main.rs": "use std::collections::HashMap;",
4119            }),
4120        )
4121        .await;
4122
4123    // Set up a fake language server.
4124    let mut language = Language::new(
4125        LanguageConfig {
4126            name: "Rust".into(),
4127            path_suffixes: vec!["rs".to_string()],
4128            ..Default::default()
4129        },
4130        Some(tree_sitter_rust::language()),
4131    );
4132    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4133    client_a.language_registry.add(Arc::new(language));
4134
4135    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4136    let project_id = active_call_a
4137        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4138        .await
4139        .unwrap();
4140    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4141
4142    // Open the file as the guest
4143    let buffer_b = cx_b
4144        .background()
4145        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
4146        .await
4147        .unwrap();
4148
4149    // Request hover information as the guest.
4150    let fake_language_server = fake_language_servers.next().await.unwrap();
4151    fake_language_server.handle_request::<lsp::request::HoverRequest, _, _>(
4152        |params, _| async move {
4153            assert_eq!(
4154                params
4155                    .text_document_position_params
4156                    .text_document
4157                    .uri
4158                    .as_str(),
4159                "file:///root-1/main.rs"
4160            );
4161            assert_eq!(
4162                params.text_document_position_params.position,
4163                lsp::Position::new(0, 22)
4164            );
4165            Ok(Some(lsp::Hover {
4166                contents: lsp::HoverContents::Array(vec![
4167                    lsp::MarkedString::String("Test hover content.".to_string()),
4168                    lsp::MarkedString::LanguageString(lsp::LanguageString {
4169                        language: "Rust".to_string(),
4170                        value: "let foo = 42;".to_string(),
4171                    }),
4172                ]),
4173                range: Some(lsp::Range::new(
4174                    lsp::Position::new(0, 22),
4175                    lsp::Position::new(0, 29),
4176                )),
4177            }))
4178        },
4179    );
4180
4181    let hover_info = project_b
4182        .update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
4183        .await
4184        .unwrap()
4185        .unwrap();
4186    buffer_b.read_with(cx_b, |buffer, _| {
4187        let snapshot = buffer.snapshot();
4188        assert_eq!(hover_info.range.unwrap().to_offset(&snapshot), 22..29);
4189        assert_eq!(
4190            hover_info.contents,
4191            vec![
4192                project::HoverBlock {
4193                    text: "Test hover content.".to_string(),
4194                    language: None,
4195                },
4196                project::HoverBlock {
4197                    text: "let foo = 42;".to_string(),
4198                    language: Some("Rust".to_string()),
4199                }
4200            ]
4201        );
4202    });
4203}
4204
4205#[gpui::test(iterations = 10)]
4206async fn test_project_symbols(
4207    deterministic: Arc<Deterministic>,
4208    cx_a: &mut TestAppContext,
4209    cx_b: &mut TestAppContext,
4210) {
4211    deterministic.forbid_parking();
4212    let mut server = TestServer::start(&deterministic).await;
4213    let client_a = server.create_client(cx_a, "user_a").await;
4214    let client_b = server.create_client(cx_b, "user_b").await;
4215    server
4216        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4217        .await;
4218    let active_call_a = cx_a.read(ActiveCall::global);
4219
4220    // Set up a fake language server.
4221    let mut language = Language::new(
4222        LanguageConfig {
4223            name: "Rust".into(),
4224            path_suffixes: vec!["rs".to_string()],
4225            ..Default::default()
4226        },
4227        Some(tree_sitter_rust::language()),
4228    );
4229    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4230    client_a.language_registry.add(Arc::new(language));
4231
4232    client_a
4233        .fs
4234        .insert_tree(
4235            "/code",
4236            json!({
4237                "crate-1": {
4238                    "one.rs": "const ONE: usize = 1;",
4239                },
4240                "crate-2": {
4241                    "two.rs": "const TWO: usize = 2; const THREE: usize = 3;",
4242                },
4243                "private": {
4244                    "passwords.txt": "the-password",
4245                }
4246            }),
4247        )
4248        .await;
4249    let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await;
4250    let project_id = active_call_a
4251        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4252        .await
4253        .unwrap();
4254    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4255
4256    // Cause the language server to start.
4257    let _buffer = cx_b
4258        .background()
4259        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
4260        .await
4261        .unwrap();
4262
4263    let fake_language_server = fake_language_servers.next().await.unwrap();
4264    fake_language_server.handle_request::<lsp::request::WorkspaceSymbol, _, _>(|_, _| async move {
4265        #[allow(deprecated)]
4266        Ok(Some(vec![lsp::SymbolInformation {
4267            name: "TWO".into(),
4268            location: lsp::Location {
4269                uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
4270                range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4271            },
4272            kind: lsp::SymbolKind::CONSTANT,
4273            tags: None,
4274            container_name: None,
4275            deprecated: None,
4276        }]))
4277    });
4278
4279    // Request the definition of a symbol as the guest.
4280    let symbols = project_b
4281        .update(cx_b, |p, cx| p.symbols("two", cx))
4282        .await
4283        .unwrap();
4284    assert_eq!(symbols.len(), 1);
4285    assert_eq!(symbols[0].name, "TWO");
4286
4287    // Open one of the returned symbols.
4288    let buffer_b_2 = project_b
4289        .update(cx_b, |project, cx| {
4290            project.open_buffer_for_symbol(&symbols[0], cx)
4291        })
4292        .await
4293        .unwrap();
4294    buffer_b_2.read_with(cx_b, |buffer, _| {
4295        assert_eq!(
4296            buffer.file().unwrap().path().as_ref(),
4297            Path::new("../crate-2/two.rs")
4298        );
4299    });
4300
4301    // Attempt to craft a symbol and violate host's privacy by opening an arbitrary file.
4302    let mut fake_symbol = symbols[0].clone();
4303    fake_symbol.path.path = Path::new("/code/secrets").into();
4304    let error = project_b
4305        .update(cx_b, |project, cx| {
4306            project.open_buffer_for_symbol(&fake_symbol, cx)
4307        })
4308        .await
4309        .unwrap_err();
4310    assert!(error.to_string().contains("invalid symbol signature"));
4311}
4312
4313#[gpui::test(iterations = 10)]
4314async fn test_open_buffer_while_getting_definition_pointing_to_it(
4315    deterministic: Arc<Deterministic>,
4316    cx_a: &mut TestAppContext,
4317    cx_b: &mut TestAppContext,
4318    mut rng: StdRng,
4319) {
4320    deterministic.forbid_parking();
4321    let mut server = TestServer::start(&deterministic).await;
4322    let client_a = server.create_client(cx_a, "user_a").await;
4323    let client_b = server.create_client(cx_b, "user_b").await;
4324    server
4325        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4326        .await;
4327    let active_call_a = cx_a.read(ActiveCall::global);
4328
4329    // Set up a fake language server.
4330    let mut language = Language::new(
4331        LanguageConfig {
4332            name: "Rust".into(),
4333            path_suffixes: vec!["rs".to_string()],
4334            ..Default::default()
4335        },
4336        Some(tree_sitter_rust::language()),
4337    );
4338    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4339    client_a.language_registry.add(Arc::new(language));
4340
4341    client_a
4342        .fs
4343        .insert_tree(
4344            "/root",
4345            json!({
4346                "a.rs": "const ONE: usize = b::TWO;",
4347                "b.rs": "const TWO: usize = 2",
4348            }),
4349        )
4350        .await;
4351    let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await;
4352    let project_id = active_call_a
4353        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4354        .await
4355        .unwrap();
4356    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4357
4358    let buffer_b1 = cx_b
4359        .background()
4360        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4361        .await
4362        .unwrap();
4363
4364    let fake_language_server = fake_language_servers.next().await.unwrap();
4365    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4366        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4367            lsp::Location::new(
4368                lsp::Url::from_file_path("/root/b.rs").unwrap(),
4369                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4370            ),
4371        )))
4372    });
4373
4374    let definitions;
4375    let buffer_b2;
4376    if rng.gen() {
4377        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
4378        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
4379    } else {
4380        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
4381        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
4382    }
4383
4384    let buffer_b2 = buffer_b2.await.unwrap();
4385    let definitions = definitions.await.unwrap();
4386    assert_eq!(definitions.len(), 1);
4387    assert_eq!(definitions[0].target.buffer, buffer_b2);
4388}
4389
4390#[gpui::test(iterations = 10)]
4391async fn test_collaborating_with_code_actions(
4392    deterministic: Arc<Deterministic>,
4393    cx_a: &mut TestAppContext,
4394    cx_b: &mut TestAppContext,
4395) {
4396    deterministic.forbid_parking();
4397    cx_b.update(editor::init);
4398    let mut server = TestServer::start(&deterministic).await;
4399    let client_a = server.create_client(cx_a, "user_a").await;
4400    let client_b = server.create_client(cx_b, "user_b").await;
4401    server
4402        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4403        .await;
4404    let active_call_a = cx_a.read(ActiveCall::global);
4405
4406    // Set up a fake language server.
4407    let mut language = Language::new(
4408        LanguageConfig {
4409            name: "Rust".into(),
4410            path_suffixes: vec!["rs".to_string()],
4411            ..Default::default()
4412        },
4413        Some(tree_sitter_rust::language()),
4414    );
4415    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4416    client_a.language_registry.add(Arc::new(language));
4417
4418    client_a
4419        .fs
4420        .insert_tree(
4421            "/a",
4422            json!({
4423                "main.rs": "mod other;\nfn main() { let foo = other::foo(); }",
4424                "other.rs": "pub fn foo() -> usize { 4 }",
4425            }),
4426        )
4427        .await;
4428    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
4429    let project_id = active_call_a
4430        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4431        .await
4432        .unwrap();
4433
4434    // Join the project as client B.
4435    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4436    let (_window_b, workspace_b) = cx_b.add_window(|cx| {
4437        Workspace::new(
4438            Default::default(),
4439            0,
4440            project_b.clone(),
4441            |_, _| unimplemented!(),
4442            cx,
4443        )
4444    });
4445    let editor_b = workspace_b
4446        .update(cx_b, |workspace, cx| {
4447            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
4448        })
4449        .await
4450        .unwrap()
4451        .downcast::<Editor>()
4452        .unwrap();
4453
4454    let mut fake_language_server = fake_language_servers.next().await.unwrap();
4455    fake_language_server
4456        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
4457            assert_eq!(
4458                params.text_document.uri,
4459                lsp::Url::from_file_path("/a/main.rs").unwrap(),
4460            );
4461            assert_eq!(params.range.start, lsp::Position::new(0, 0));
4462            assert_eq!(params.range.end, lsp::Position::new(0, 0));
4463            Ok(None)
4464        })
4465        .next()
4466        .await;
4467
4468    // Move cursor to a location that contains code actions.
4469    editor_b.update(cx_b, |editor, cx| {
4470        editor.change_selections(None, cx, |s| {
4471            s.select_ranges([Point::new(1, 31)..Point::new(1, 31)])
4472        });
4473        cx.focus(&editor_b);
4474    });
4475
4476    fake_language_server
4477        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
4478            assert_eq!(
4479                params.text_document.uri,
4480                lsp::Url::from_file_path("/a/main.rs").unwrap(),
4481            );
4482            assert_eq!(params.range.start, lsp::Position::new(1, 31));
4483            assert_eq!(params.range.end, lsp::Position::new(1, 31));
4484
4485            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
4486                lsp::CodeAction {
4487                    title: "Inline into all callers".to_string(),
4488                    edit: Some(lsp::WorkspaceEdit {
4489                        changes: Some(
4490                            [
4491                                (
4492                                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
4493                                    vec![lsp::TextEdit::new(
4494                                        lsp::Range::new(
4495                                            lsp::Position::new(1, 22),
4496                                            lsp::Position::new(1, 34),
4497                                        ),
4498                                        "4".to_string(),
4499                                    )],
4500                                ),
4501                                (
4502                                    lsp::Url::from_file_path("/a/other.rs").unwrap(),
4503                                    vec![lsp::TextEdit::new(
4504                                        lsp::Range::new(
4505                                            lsp::Position::new(0, 0),
4506                                            lsp::Position::new(0, 27),
4507                                        ),
4508                                        "".to_string(),
4509                                    )],
4510                                ),
4511                            ]
4512                            .into_iter()
4513                            .collect(),
4514                        ),
4515                        ..Default::default()
4516                    }),
4517                    data: Some(json!({
4518                        "codeActionParams": {
4519                            "range": {
4520                                "start": {"line": 1, "column": 31},
4521                                "end": {"line": 1, "column": 31},
4522                            }
4523                        }
4524                    })),
4525                    ..Default::default()
4526                },
4527            )]))
4528        })
4529        .next()
4530        .await;
4531
4532    // Toggle code actions and wait for them to display.
4533    editor_b.update(cx_b, |editor, cx| {
4534        editor.toggle_code_actions(
4535            &ToggleCodeActions {
4536                deployed_from_indicator: false,
4537            },
4538            cx,
4539        );
4540    });
4541    cx_a.foreground().run_until_parked();
4542    editor_b.read_with(cx_b, |editor, _| assert!(editor.context_menu_visible()));
4543
4544    fake_language_server.remove_request_handler::<lsp::request::CodeActionRequest>();
4545
4546    // Confirming the code action will trigger a resolve request.
4547    let confirm_action = workspace_b
4548        .update(cx_b, |workspace, cx| {
4549            Editor::confirm_code_action(workspace, &ConfirmCodeAction { item_ix: Some(0) }, cx)
4550        })
4551        .unwrap();
4552    fake_language_server.handle_request::<lsp::request::CodeActionResolveRequest, _, _>(
4553        |_, _| async move {
4554            Ok(lsp::CodeAction {
4555                title: "Inline into all callers".to_string(),
4556                edit: Some(lsp::WorkspaceEdit {
4557                    changes: Some(
4558                        [
4559                            (
4560                                lsp::Url::from_file_path("/a/main.rs").unwrap(),
4561                                vec![lsp::TextEdit::new(
4562                                    lsp::Range::new(
4563                                        lsp::Position::new(1, 22),
4564                                        lsp::Position::new(1, 34),
4565                                    ),
4566                                    "4".to_string(),
4567                                )],
4568                            ),
4569                            (
4570                                lsp::Url::from_file_path("/a/other.rs").unwrap(),
4571                                vec![lsp::TextEdit::new(
4572                                    lsp::Range::new(
4573                                        lsp::Position::new(0, 0),
4574                                        lsp::Position::new(0, 27),
4575                                    ),
4576                                    "".to_string(),
4577                                )],
4578                            ),
4579                        ]
4580                        .into_iter()
4581                        .collect(),
4582                    ),
4583                    ..Default::default()
4584                }),
4585                ..Default::default()
4586            })
4587        },
4588    );
4589
4590    // After the action is confirmed, an editor containing both modified files is opened.
4591    confirm_action.await.unwrap();
4592    let code_action_editor = workspace_b.read_with(cx_b, |workspace, cx| {
4593        workspace
4594            .active_item(cx)
4595            .unwrap()
4596            .downcast::<Editor>()
4597            .unwrap()
4598    });
4599    code_action_editor.update(cx_b, |editor, cx| {
4600        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
4601        editor.undo(&Undo, cx);
4602        assert_eq!(
4603            editor.text(cx),
4604            "mod other;\nfn main() { let foo = other::foo(); }\npub fn foo() -> usize { 4 }"
4605        );
4606        editor.redo(&Redo, cx);
4607        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
4608    });
4609}
4610
4611#[gpui::test(iterations = 10)]
4612async fn test_collaborating_with_renames(
4613    deterministic: Arc<Deterministic>,
4614    cx_a: &mut TestAppContext,
4615    cx_b: &mut TestAppContext,
4616) {
4617    deterministic.forbid_parking();
4618    cx_b.update(editor::init);
4619    let mut server = TestServer::start(&deterministic).await;
4620    let client_a = server.create_client(cx_a, "user_a").await;
4621    let client_b = server.create_client(cx_b, "user_b").await;
4622    server
4623        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4624        .await;
4625    let active_call_a = cx_a.read(ActiveCall::global);
4626
4627    // Set up a fake language server.
4628    let mut language = Language::new(
4629        LanguageConfig {
4630            name: "Rust".into(),
4631            path_suffixes: vec!["rs".to_string()],
4632            ..Default::default()
4633        },
4634        Some(tree_sitter_rust::language()),
4635    );
4636    let mut fake_language_servers = language
4637        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4638            capabilities: lsp::ServerCapabilities {
4639                rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
4640                    prepare_provider: Some(true),
4641                    work_done_progress_options: Default::default(),
4642                })),
4643                ..Default::default()
4644            },
4645            ..Default::default()
4646        }))
4647        .await;
4648    client_a.language_registry.add(Arc::new(language));
4649
4650    client_a
4651        .fs
4652        .insert_tree(
4653            "/dir",
4654            json!({
4655                "one.rs": "const ONE: usize = 1;",
4656                "two.rs": "const TWO: usize = one::ONE + one::ONE;"
4657            }),
4658        )
4659        .await;
4660    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
4661    let project_id = active_call_a
4662        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4663        .await
4664        .unwrap();
4665    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4666
4667    let (_window_b, workspace_b) = cx_b.add_window(|cx| {
4668        Workspace::new(
4669            Default::default(),
4670            0,
4671            project_b.clone(),
4672            |_, _| unimplemented!(),
4673            cx,
4674        )
4675    });
4676    let editor_b = workspace_b
4677        .update(cx_b, |workspace, cx| {
4678            workspace.open_path((worktree_id, "one.rs"), None, true, cx)
4679        })
4680        .await
4681        .unwrap()
4682        .downcast::<Editor>()
4683        .unwrap();
4684    let fake_language_server = fake_language_servers.next().await.unwrap();
4685
4686    // Move cursor to a location that can be renamed.
4687    let prepare_rename = editor_b.update(cx_b, |editor, cx| {
4688        editor.change_selections(None, cx, |s| s.select_ranges([7..7]));
4689        editor.rename(&Rename, cx).unwrap()
4690    });
4691
4692    fake_language_server
4693        .handle_request::<lsp::request::PrepareRenameRequest, _, _>(|params, _| async move {
4694            assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs");
4695            assert_eq!(params.position, lsp::Position::new(0, 7));
4696            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
4697                lsp::Position::new(0, 6),
4698                lsp::Position::new(0, 9),
4699            ))))
4700        })
4701        .next()
4702        .await
4703        .unwrap();
4704    prepare_rename.await.unwrap();
4705    editor_b.update(cx_b, |editor, cx| {
4706        let rename = editor.pending_rename().unwrap();
4707        let buffer = editor.buffer().read(cx).snapshot(cx);
4708        assert_eq!(
4709            rename.range.start.to_offset(&buffer)..rename.range.end.to_offset(&buffer),
4710            6..9
4711        );
4712        rename.editor.update(cx, |rename_editor, cx| {
4713            rename_editor.buffer().update(cx, |rename_buffer, cx| {
4714                rename_buffer.edit([(0..3, "THREE")], None, cx);
4715            });
4716        });
4717    });
4718
4719    let confirm_rename = workspace_b.update(cx_b, |workspace, cx| {
4720        Editor::confirm_rename(workspace, &ConfirmRename, cx).unwrap()
4721    });
4722    fake_language_server
4723        .handle_request::<lsp::request::Rename, _, _>(|params, _| async move {
4724            assert_eq!(
4725                params.text_document_position.text_document.uri.as_str(),
4726                "file:///dir/one.rs"
4727            );
4728            assert_eq!(
4729                params.text_document_position.position,
4730                lsp::Position::new(0, 6)
4731            );
4732            assert_eq!(params.new_name, "THREE");
4733            Ok(Some(lsp::WorkspaceEdit {
4734                changes: Some(
4735                    [
4736                        (
4737                            lsp::Url::from_file_path("/dir/one.rs").unwrap(),
4738                            vec![lsp::TextEdit::new(
4739                                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4740                                "THREE".to_string(),
4741                            )],
4742                        ),
4743                        (
4744                            lsp::Url::from_file_path("/dir/two.rs").unwrap(),
4745                            vec![
4746                                lsp::TextEdit::new(
4747                                    lsp::Range::new(
4748                                        lsp::Position::new(0, 24),
4749                                        lsp::Position::new(0, 27),
4750                                    ),
4751                                    "THREE".to_string(),
4752                                ),
4753                                lsp::TextEdit::new(
4754                                    lsp::Range::new(
4755                                        lsp::Position::new(0, 35),
4756                                        lsp::Position::new(0, 38),
4757                                    ),
4758                                    "THREE".to_string(),
4759                                ),
4760                            ],
4761                        ),
4762                    ]
4763                    .into_iter()
4764                    .collect(),
4765                ),
4766                ..Default::default()
4767            }))
4768        })
4769        .next()
4770        .await
4771        .unwrap();
4772    confirm_rename.await.unwrap();
4773
4774    let rename_editor = workspace_b.read_with(cx_b, |workspace, cx| {
4775        workspace
4776            .active_item(cx)
4777            .unwrap()
4778            .downcast::<Editor>()
4779            .unwrap()
4780    });
4781    rename_editor.update(cx_b, |editor, cx| {
4782        assert_eq!(
4783            editor.text(cx),
4784            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
4785        );
4786        editor.undo(&Undo, cx);
4787        assert_eq!(
4788            editor.text(cx),
4789            "const ONE: usize = 1;\nconst TWO: usize = one::ONE + one::ONE;"
4790        );
4791        editor.redo(&Redo, cx);
4792        assert_eq!(
4793            editor.text(cx),
4794            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
4795        );
4796    });
4797
4798    // Ensure temporary rename edits cannot be undone/redone.
4799    editor_b.update(cx_b, |editor, cx| {
4800        editor.undo(&Undo, cx);
4801        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
4802        editor.undo(&Undo, cx);
4803        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
4804        editor.redo(&Redo, cx);
4805        assert_eq!(editor.text(cx), "const THREE: usize = 1;");
4806    })
4807}
4808
4809#[gpui::test(iterations = 10)]
4810async fn test_language_server_statuses(
4811    deterministic: Arc<Deterministic>,
4812    cx_a: &mut TestAppContext,
4813    cx_b: &mut TestAppContext,
4814) {
4815    deterministic.forbid_parking();
4816
4817    cx_b.update(editor::init);
4818    let mut server = TestServer::start(&deterministic).await;
4819    let client_a = server.create_client(cx_a, "user_a").await;
4820    let client_b = server.create_client(cx_b, "user_b").await;
4821    server
4822        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4823        .await;
4824    let active_call_a = cx_a.read(ActiveCall::global);
4825
4826    // Set up a fake language server.
4827    let mut language = Language::new(
4828        LanguageConfig {
4829            name: "Rust".into(),
4830            path_suffixes: vec!["rs".to_string()],
4831            ..Default::default()
4832        },
4833        Some(tree_sitter_rust::language()),
4834    );
4835    let mut fake_language_servers = language
4836        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4837            name: "the-language-server",
4838            ..Default::default()
4839        }))
4840        .await;
4841    client_a.language_registry.add(Arc::new(language));
4842
4843    client_a
4844        .fs
4845        .insert_tree(
4846            "/dir",
4847            json!({
4848                "main.rs": "const ONE: usize = 1;",
4849            }),
4850        )
4851        .await;
4852    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
4853
4854    let _buffer_a = project_a
4855        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
4856        .await
4857        .unwrap();
4858
4859    let fake_language_server = fake_language_servers.next().await.unwrap();
4860    fake_language_server.start_progress("the-token").await;
4861    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
4862        token: lsp::NumberOrString::String("the-token".to_string()),
4863        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
4864            lsp::WorkDoneProgressReport {
4865                message: Some("the-message".to_string()),
4866                ..Default::default()
4867            },
4868        )),
4869    });
4870    deterministic.run_until_parked();
4871    project_a.read_with(cx_a, |project, _| {
4872        let status = project.language_server_statuses().next().unwrap();
4873        assert_eq!(status.name, "the-language-server");
4874        assert_eq!(status.pending_work.len(), 1);
4875        assert_eq!(
4876            status.pending_work["the-token"].message.as_ref().unwrap(),
4877            "the-message"
4878        );
4879    });
4880
4881    let project_id = active_call_a
4882        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4883        .await
4884        .unwrap();
4885    deterministic.run_until_parked();
4886    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4887    project_b.read_with(cx_b, |project, _| {
4888        let status = project.language_server_statuses().next().unwrap();
4889        assert_eq!(status.name, "the-language-server");
4890    });
4891
4892    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
4893        token: lsp::NumberOrString::String("the-token".to_string()),
4894        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
4895            lsp::WorkDoneProgressReport {
4896                message: Some("the-message-2".to_string()),
4897                ..Default::default()
4898            },
4899        )),
4900    });
4901    deterministic.run_until_parked();
4902    project_a.read_with(cx_a, |project, _| {
4903        let status = project.language_server_statuses().next().unwrap();
4904        assert_eq!(status.name, "the-language-server");
4905        assert_eq!(status.pending_work.len(), 1);
4906        assert_eq!(
4907            status.pending_work["the-token"].message.as_ref().unwrap(),
4908            "the-message-2"
4909        );
4910    });
4911    project_b.read_with(cx_b, |project, _| {
4912        let status = project.language_server_statuses().next().unwrap();
4913        assert_eq!(status.name, "the-language-server");
4914        assert_eq!(status.pending_work.len(), 1);
4915        assert_eq!(
4916            status.pending_work["the-token"].message.as_ref().unwrap(),
4917            "the-message-2"
4918        );
4919    });
4920}
4921
4922#[gpui::test(iterations = 10)]
4923async fn test_contacts(
4924    deterministic: Arc<Deterministic>,
4925    cx_a: &mut TestAppContext,
4926    cx_b: &mut TestAppContext,
4927    cx_c: &mut TestAppContext,
4928    cx_d: &mut TestAppContext,
4929) {
4930    deterministic.forbid_parking();
4931    let mut server = TestServer::start(&deterministic).await;
4932    let client_a = server.create_client(cx_a, "user_a").await;
4933    let client_b = server.create_client(cx_b, "user_b").await;
4934    let client_c = server.create_client(cx_c, "user_c").await;
4935    let client_d = server.create_client(cx_d, "user_d").await;
4936    server
4937        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
4938        .await;
4939    let active_call_a = cx_a.read(ActiveCall::global);
4940    let active_call_b = cx_b.read(ActiveCall::global);
4941    let active_call_c = cx_c.read(ActiveCall::global);
4942    let _active_call_d = cx_d.read(ActiveCall::global);
4943
4944    deterministic.run_until_parked();
4945    assert_eq!(
4946        contacts(&client_a, cx_a),
4947        [
4948            ("user_b".to_string(), "online", "free"),
4949            ("user_c".to_string(), "online", "free")
4950        ]
4951    );
4952    assert_eq!(
4953        contacts(&client_b, cx_b),
4954        [
4955            ("user_a".to_string(), "online", "free"),
4956            ("user_c".to_string(), "online", "free")
4957        ]
4958    );
4959    assert_eq!(
4960        contacts(&client_c, cx_c),
4961        [
4962            ("user_a".to_string(), "online", "free"),
4963            ("user_b".to_string(), "online", "free")
4964        ]
4965    );
4966    assert_eq!(contacts(&client_d, cx_d), []);
4967
4968    server.disconnect_client(client_c.peer_id().unwrap());
4969    server.forbid_connections();
4970    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
4971    assert_eq!(
4972        contacts(&client_a, cx_a),
4973        [
4974            ("user_b".to_string(), "online", "free"),
4975            ("user_c".to_string(), "offline", "free")
4976        ]
4977    );
4978    assert_eq!(
4979        contacts(&client_b, cx_b),
4980        [
4981            ("user_a".to_string(), "online", "free"),
4982            ("user_c".to_string(), "offline", "free")
4983        ]
4984    );
4985    assert_eq!(contacts(&client_c, cx_c), []);
4986    assert_eq!(contacts(&client_d, cx_d), []);
4987
4988    server.allow_connections();
4989    client_c
4990        .authenticate_and_connect(false, &cx_c.to_async())
4991        .await
4992        .unwrap();
4993
4994    deterministic.run_until_parked();
4995    assert_eq!(
4996        contacts(&client_a, cx_a),
4997        [
4998            ("user_b".to_string(), "online", "free"),
4999            ("user_c".to_string(), "online", "free")
5000        ]
5001    );
5002    assert_eq!(
5003        contacts(&client_b, cx_b),
5004        [
5005            ("user_a".to_string(), "online", "free"),
5006            ("user_c".to_string(), "online", "free")
5007        ]
5008    );
5009    assert_eq!(
5010        contacts(&client_c, cx_c),
5011        [
5012            ("user_a".to_string(), "online", "free"),
5013            ("user_b".to_string(), "online", "free")
5014        ]
5015    );
5016    assert_eq!(contacts(&client_d, cx_d), []);
5017
5018    active_call_a
5019        .update(cx_a, |call, cx| {
5020            call.invite(client_b.user_id().unwrap(), None, cx)
5021        })
5022        .await
5023        .unwrap();
5024    deterministic.run_until_parked();
5025    assert_eq!(
5026        contacts(&client_a, cx_a),
5027        [
5028            ("user_b".to_string(), "online", "busy"),
5029            ("user_c".to_string(), "online", "free")
5030        ]
5031    );
5032    assert_eq!(
5033        contacts(&client_b, cx_b),
5034        [
5035            ("user_a".to_string(), "online", "busy"),
5036            ("user_c".to_string(), "online", "free")
5037        ]
5038    );
5039    assert_eq!(
5040        contacts(&client_c, cx_c),
5041        [
5042            ("user_a".to_string(), "online", "busy"),
5043            ("user_b".to_string(), "online", "busy")
5044        ]
5045    );
5046    assert_eq!(contacts(&client_d, cx_d), []);
5047
5048    // Client B and client D become contacts while client B is being called.
5049    server
5050        .make_contacts(&mut [(&client_b, cx_b), (&client_d, cx_d)])
5051        .await;
5052    deterministic.run_until_parked();
5053    assert_eq!(
5054        contacts(&client_a, cx_a),
5055        [
5056            ("user_b".to_string(), "online", "busy"),
5057            ("user_c".to_string(), "online", "free")
5058        ]
5059    );
5060    assert_eq!(
5061        contacts(&client_b, cx_b),
5062        [
5063            ("user_a".to_string(), "online", "busy"),
5064            ("user_c".to_string(), "online", "free"),
5065            ("user_d".to_string(), "online", "free"),
5066        ]
5067    );
5068    assert_eq!(
5069        contacts(&client_c, cx_c),
5070        [
5071            ("user_a".to_string(), "online", "busy"),
5072            ("user_b".to_string(), "online", "busy")
5073        ]
5074    );
5075    assert_eq!(
5076        contacts(&client_d, cx_d),
5077        [("user_b".to_string(), "online", "busy")]
5078    );
5079
5080    active_call_b.update(cx_b, |call, _| call.decline_incoming().unwrap());
5081    deterministic.run_until_parked();
5082    assert_eq!(
5083        contacts(&client_a, cx_a),
5084        [
5085            ("user_b".to_string(), "online", "free"),
5086            ("user_c".to_string(), "online", "free")
5087        ]
5088    );
5089    assert_eq!(
5090        contacts(&client_b, cx_b),
5091        [
5092            ("user_a".to_string(), "online", "free"),
5093            ("user_c".to_string(), "online", "free"),
5094            ("user_d".to_string(), "online", "free")
5095        ]
5096    );
5097    assert_eq!(
5098        contacts(&client_c, cx_c),
5099        [
5100            ("user_a".to_string(), "online", "free"),
5101            ("user_b".to_string(), "online", "free")
5102        ]
5103    );
5104    assert_eq!(
5105        contacts(&client_d, cx_d),
5106        [("user_b".to_string(), "online", "free")]
5107    );
5108
5109    active_call_c
5110        .update(cx_c, |call, cx| {
5111            call.invite(client_a.user_id().unwrap(), None, cx)
5112        })
5113        .await
5114        .unwrap();
5115    deterministic.run_until_parked();
5116    assert_eq!(
5117        contacts(&client_a, cx_a),
5118        [
5119            ("user_b".to_string(), "online", "free"),
5120            ("user_c".to_string(), "online", "busy")
5121        ]
5122    );
5123    assert_eq!(
5124        contacts(&client_b, cx_b),
5125        [
5126            ("user_a".to_string(), "online", "busy"),
5127            ("user_c".to_string(), "online", "busy"),
5128            ("user_d".to_string(), "online", "free")
5129        ]
5130    );
5131    assert_eq!(
5132        contacts(&client_c, cx_c),
5133        [
5134            ("user_a".to_string(), "online", "busy"),
5135            ("user_b".to_string(), "online", "free")
5136        ]
5137    );
5138    assert_eq!(
5139        contacts(&client_d, cx_d),
5140        [("user_b".to_string(), "online", "free")]
5141    );
5142
5143    active_call_a
5144        .update(cx_a, |call, cx| call.accept_incoming(cx))
5145        .await
5146        .unwrap();
5147    deterministic.run_until_parked();
5148    assert_eq!(
5149        contacts(&client_a, cx_a),
5150        [
5151            ("user_b".to_string(), "online", "free"),
5152            ("user_c".to_string(), "online", "busy")
5153        ]
5154    );
5155    assert_eq!(
5156        contacts(&client_b, cx_b),
5157        [
5158            ("user_a".to_string(), "online", "busy"),
5159            ("user_c".to_string(), "online", "busy"),
5160            ("user_d".to_string(), "online", "free")
5161        ]
5162    );
5163    assert_eq!(
5164        contacts(&client_c, cx_c),
5165        [
5166            ("user_a".to_string(), "online", "busy"),
5167            ("user_b".to_string(), "online", "free")
5168        ]
5169    );
5170    assert_eq!(
5171        contacts(&client_d, cx_d),
5172        [("user_b".to_string(), "online", "free")]
5173    );
5174
5175    active_call_a
5176        .update(cx_a, |call, cx| {
5177            call.invite(client_b.user_id().unwrap(), None, cx)
5178        })
5179        .await
5180        .unwrap();
5181    deterministic.run_until_parked();
5182    assert_eq!(
5183        contacts(&client_a, cx_a),
5184        [
5185            ("user_b".to_string(), "online", "busy"),
5186            ("user_c".to_string(), "online", "busy")
5187        ]
5188    );
5189    assert_eq!(
5190        contacts(&client_b, cx_b),
5191        [
5192            ("user_a".to_string(), "online", "busy"),
5193            ("user_c".to_string(), "online", "busy"),
5194            ("user_d".to_string(), "online", "free")
5195        ]
5196    );
5197    assert_eq!(
5198        contacts(&client_c, cx_c),
5199        [
5200            ("user_a".to_string(), "online", "busy"),
5201            ("user_b".to_string(), "online", "busy")
5202        ]
5203    );
5204    assert_eq!(
5205        contacts(&client_d, cx_d),
5206        [("user_b".to_string(), "online", "busy")]
5207    );
5208
5209    active_call_a.update(cx_a, |call, cx| call.hang_up(cx).unwrap());
5210    deterministic.run_until_parked();
5211    assert_eq!(
5212        contacts(&client_a, cx_a),
5213        [
5214            ("user_b".to_string(), "online", "free"),
5215            ("user_c".to_string(), "online", "free")
5216        ]
5217    );
5218    assert_eq!(
5219        contacts(&client_b, cx_b),
5220        [
5221            ("user_a".to_string(), "online", "free"),
5222            ("user_c".to_string(), "online", "free"),
5223            ("user_d".to_string(), "online", "free")
5224        ]
5225    );
5226    assert_eq!(
5227        contacts(&client_c, cx_c),
5228        [
5229            ("user_a".to_string(), "online", "free"),
5230            ("user_b".to_string(), "online", "free")
5231        ]
5232    );
5233    assert_eq!(
5234        contacts(&client_d, cx_d),
5235        [("user_b".to_string(), "online", "free")]
5236    );
5237
5238    active_call_a
5239        .update(cx_a, |call, cx| {
5240            call.invite(client_b.user_id().unwrap(), None, cx)
5241        })
5242        .await
5243        .unwrap();
5244    deterministic.run_until_parked();
5245    assert_eq!(
5246        contacts(&client_a, cx_a),
5247        [
5248            ("user_b".to_string(), "online", "busy"),
5249            ("user_c".to_string(), "online", "free")
5250        ]
5251    );
5252    assert_eq!(
5253        contacts(&client_b, cx_b),
5254        [
5255            ("user_a".to_string(), "online", "busy"),
5256            ("user_c".to_string(), "online", "free"),
5257            ("user_d".to_string(), "online", "free")
5258        ]
5259    );
5260    assert_eq!(
5261        contacts(&client_c, cx_c),
5262        [
5263            ("user_a".to_string(), "online", "busy"),
5264            ("user_b".to_string(), "online", "busy")
5265        ]
5266    );
5267    assert_eq!(
5268        contacts(&client_d, cx_d),
5269        [("user_b".to_string(), "online", "busy")]
5270    );
5271
5272    server.forbid_connections();
5273    server.disconnect_client(client_a.peer_id().unwrap());
5274    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5275    assert_eq!(contacts(&client_a, cx_a), []);
5276    assert_eq!(
5277        contacts(&client_b, cx_b),
5278        [
5279            ("user_a".to_string(), "offline", "free"),
5280            ("user_c".to_string(), "online", "free"),
5281            ("user_d".to_string(), "online", "free")
5282        ]
5283    );
5284    assert_eq!(
5285        contacts(&client_c, cx_c),
5286        [
5287            ("user_a".to_string(), "offline", "free"),
5288            ("user_b".to_string(), "online", "free")
5289        ]
5290    );
5291    assert_eq!(
5292        contacts(&client_d, cx_d),
5293        [("user_b".to_string(), "online", "free")]
5294    );
5295
5296    // Test removing a contact
5297    client_b
5298        .user_store
5299        .update(cx_b, |store, cx| {
5300            store.remove_contact(client_c.user_id().unwrap(), cx)
5301        })
5302        .await
5303        .unwrap();
5304    deterministic.run_until_parked();
5305    assert_eq!(
5306        contacts(&client_b, cx_b),
5307        [
5308            ("user_a".to_string(), "offline", "free"),
5309            ("user_d".to_string(), "online", "free")
5310        ]
5311    );
5312    assert_eq!(
5313        contacts(&client_c, cx_c),
5314        [("user_a".to_string(), "offline", "free"),]
5315    );
5316
5317    fn contacts(
5318        client: &TestClient,
5319        cx: &TestAppContext,
5320    ) -> Vec<(String, &'static str, &'static str)> {
5321        client.user_store.read_with(cx, |store, _| {
5322            store
5323                .contacts()
5324                .iter()
5325                .map(|contact| {
5326                    (
5327                        contact.user.github_login.clone(),
5328                        if contact.online { "online" } else { "offline" },
5329                        if contact.busy { "busy" } else { "free" },
5330                    )
5331                })
5332                .collect()
5333        })
5334    }
5335}
5336
5337#[gpui::test(iterations = 10)]
5338async fn test_contact_requests(
5339    deterministic: Arc<Deterministic>,
5340    cx_a: &mut TestAppContext,
5341    cx_a2: &mut TestAppContext,
5342    cx_b: &mut TestAppContext,
5343    cx_b2: &mut TestAppContext,
5344    cx_c: &mut TestAppContext,
5345    cx_c2: &mut TestAppContext,
5346) {
5347    deterministic.forbid_parking();
5348
5349    // Connect to a server as 3 clients.
5350    let mut server = TestServer::start(&deterministic).await;
5351    let client_a = server.create_client(cx_a, "user_a").await;
5352    let client_a2 = server.create_client(cx_a2, "user_a").await;
5353    let client_b = server.create_client(cx_b, "user_b").await;
5354    let client_b2 = server.create_client(cx_b2, "user_b").await;
5355    let client_c = server.create_client(cx_c, "user_c").await;
5356    let client_c2 = server.create_client(cx_c2, "user_c").await;
5357
5358    assert_eq!(client_a.user_id().unwrap(), client_a2.user_id().unwrap());
5359    assert_eq!(client_b.user_id().unwrap(), client_b2.user_id().unwrap());
5360    assert_eq!(client_c.user_id().unwrap(), client_c2.user_id().unwrap());
5361
5362    // User A and User C request that user B become their contact.
5363    client_a
5364        .user_store
5365        .update(cx_a, |store, cx| {
5366            store.request_contact(client_b.user_id().unwrap(), cx)
5367        })
5368        .await
5369        .unwrap();
5370    client_c
5371        .user_store
5372        .update(cx_c, |store, cx| {
5373            store.request_contact(client_b.user_id().unwrap(), cx)
5374        })
5375        .await
5376        .unwrap();
5377    deterministic.run_until_parked();
5378
5379    // All users see the pending request appear in all their clients.
5380    assert_eq!(
5381        client_a.summarize_contacts(cx_a).outgoing_requests,
5382        &["user_b"]
5383    );
5384    assert_eq!(
5385        client_a2.summarize_contacts(cx_a2).outgoing_requests,
5386        &["user_b"]
5387    );
5388    assert_eq!(
5389        client_b.summarize_contacts(cx_b).incoming_requests,
5390        &["user_a", "user_c"]
5391    );
5392    assert_eq!(
5393        client_b2.summarize_contacts(cx_b2).incoming_requests,
5394        &["user_a", "user_c"]
5395    );
5396    assert_eq!(
5397        client_c.summarize_contacts(cx_c).outgoing_requests,
5398        &["user_b"]
5399    );
5400    assert_eq!(
5401        client_c2.summarize_contacts(cx_c2).outgoing_requests,
5402        &["user_b"]
5403    );
5404
5405    // Contact requests are present upon connecting (tested here via disconnect/reconnect)
5406    disconnect_and_reconnect(&client_a, cx_a).await;
5407    disconnect_and_reconnect(&client_b, cx_b).await;
5408    disconnect_and_reconnect(&client_c, cx_c).await;
5409    deterministic.run_until_parked();
5410    assert_eq!(
5411        client_a.summarize_contacts(cx_a).outgoing_requests,
5412        &["user_b"]
5413    );
5414    assert_eq!(
5415        client_b.summarize_contacts(cx_b).incoming_requests,
5416        &["user_a", "user_c"]
5417    );
5418    assert_eq!(
5419        client_c.summarize_contacts(cx_c).outgoing_requests,
5420        &["user_b"]
5421    );
5422
5423    // User B accepts the request from user A.
5424    client_b
5425        .user_store
5426        .update(cx_b, |store, cx| {
5427            store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
5428        })
5429        .await
5430        .unwrap();
5431
5432    deterministic.run_until_parked();
5433
5434    // User B sees user A as their contact now in all client, and the incoming request from them is removed.
5435    let contacts_b = client_b.summarize_contacts(cx_b);
5436    assert_eq!(contacts_b.current, &["user_a"]);
5437    assert_eq!(contacts_b.incoming_requests, &["user_c"]);
5438    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
5439    assert_eq!(contacts_b2.current, &["user_a"]);
5440    assert_eq!(contacts_b2.incoming_requests, &["user_c"]);
5441
5442    // User A sees user B as their contact now in all clients, and the outgoing request to them is removed.
5443    let contacts_a = client_a.summarize_contacts(cx_a);
5444    assert_eq!(contacts_a.current, &["user_b"]);
5445    assert!(contacts_a.outgoing_requests.is_empty());
5446    let contacts_a2 = client_a2.summarize_contacts(cx_a2);
5447    assert_eq!(contacts_a2.current, &["user_b"]);
5448    assert!(contacts_a2.outgoing_requests.is_empty());
5449
5450    // Contacts are present upon connecting (tested here via disconnect/reconnect)
5451    disconnect_and_reconnect(&client_a, cx_a).await;
5452    disconnect_and_reconnect(&client_b, cx_b).await;
5453    disconnect_and_reconnect(&client_c, cx_c).await;
5454    deterministic.run_until_parked();
5455    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
5456    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
5457    assert_eq!(
5458        client_b.summarize_contacts(cx_b).incoming_requests,
5459        &["user_c"]
5460    );
5461    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
5462    assert_eq!(
5463        client_c.summarize_contacts(cx_c).outgoing_requests,
5464        &["user_b"]
5465    );
5466
5467    // User B rejects the request from user C.
5468    client_b
5469        .user_store
5470        .update(cx_b, |store, cx| {
5471            store.respond_to_contact_request(client_c.user_id().unwrap(), false, cx)
5472        })
5473        .await
5474        .unwrap();
5475
5476    deterministic.run_until_parked();
5477
5478    // User B doesn't see user C as their contact, and the incoming request from them is removed.
5479    let contacts_b = client_b.summarize_contacts(cx_b);
5480    assert_eq!(contacts_b.current, &["user_a"]);
5481    assert!(contacts_b.incoming_requests.is_empty());
5482    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
5483    assert_eq!(contacts_b2.current, &["user_a"]);
5484    assert!(contacts_b2.incoming_requests.is_empty());
5485
5486    // User C doesn't see user B as their contact, and the outgoing request to them is removed.
5487    let contacts_c = client_c.summarize_contacts(cx_c);
5488    assert!(contacts_c.current.is_empty());
5489    assert!(contacts_c.outgoing_requests.is_empty());
5490    let contacts_c2 = client_c2.summarize_contacts(cx_c2);
5491    assert!(contacts_c2.current.is_empty());
5492    assert!(contacts_c2.outgoing_requests.is_empty());
5493
5494    // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect)
5495    disconnect_and_reconnect(&client_a, cx_a).await;
5496    disconnect_and_reconnect(&client_b, cx_b).await;
5497    disconnect_and_reconnect(&client_c, cx_c).await;
5498    deterministic.run_until_parked();
5499    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
5500    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
5501    assert!(client_b
5502        .summarize_contacts(cx_b)
5503        .incoming_requests
5504        .is_empty());
5505    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
5506    assert!(client_c
5507        .summarize_contacts(cx_c)
5508        .outgoing_requests
5509        .is_empty());
5510
5511    async fn disconnect_and_reconnect(client: &TestClient, cx: &mut TestAppContext) {
5512        client.disconnect(&cx.to_async()).unwrap();
5513        client.clear_contacts(cx).await;
5514        client
5515            .authenticate_and_connect(false, &cx.to_async())
5516            .await
5517            .unwrap();
5518    }
5519}
5520
5521#[gpui::test(iterations = 10)]
5522async fn test_following(
5523    deterministic: Arc<Deterministic>,
5524    cx_a: &mut TestAppContext,
5525    cx_b: &mut TestAppContext,
5526) {
5527    deterministic.forbid_parking();
5528    cx_a.update(editor::init);
5529    cx_b.update(editor::init);
5530
5531    let mut server = TestServer::start(&deterministic).await;
5532    let client_a = server.create_client(cx_a, "user_a").await;
5533    let client_b = server.create_client(cx_b, "user_b").await;
5534    server
5535        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5536        .await;
5537    let active_call_a = cx_a.read(ActiveCall::global);
5538    let active_call_b = cx_b.read(ActiveCall::global);
5539
5540    client_a
5541        .fs
5542        .insert_tree(
5543            "/a",
5544            json!({
5545                "1.txt": "one\none\none",
5546                "2.txt": "two\ntwo\ntwo",
5547                "3.txt": "three\nthree\nthree",
5548            }),
5549        )
5550        .await;
5551    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
5552    active_call_a
5553        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
5554        .await
5555        .unwrap();
5556
5557    let project_id = active_call_a
5558        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5559        .await
5560        .unwrap();
5561    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5562    active_call_b
5563        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
5564        .await
5565        .unwrap();
5566
5567    // Client A opens some editors.
5568    let workspace_a = client_a.build_workspace(&project_a, cx_a);
5569    let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
5570    let editor_a1 = workspace_a
5571        .update(cx_a, |workspace, cx| {
5572            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
5573        })
5574        .await
5575        .unwrap()
5576        .downcast::<Editor>()
5577        .unwrap();
5578    let editor_a2 = workspace_a
5579        .update(cx_a, |workspace, cx| {
5580            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
5581        })
5582        .await
5583        .unwrap()
5584        .downcast::<Editor>()
5585        .unwrap();
5586
5587    // Client B opens an editor.
5588    let workspace_b = client_b.build_workspace(&project_b, cx_b);
5589    let editor_b1 = workspace_b
5590        .update(cx_b, |workspace, cx| {
5591            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
5592        })
5593        .await
5594        .unwrap()
5595        .downcast::<Editor>()
5596        .unwrap();
5597
5598    let client_a_id = project_b.read_with(cx_b, |project, _| {
5599        project.collaborators().values().next().unwrap().peer_id
5600    });
5601    let client_b_id = project_a.read_with(cx_a, |project, _| {
5602        project.collaborators().values().next().unwrap().peer_id
5603    });
5604
5605    // When client B starts following client A, all visible view states are replicated to client B.
5606    editor_a1.update(cx_a, |editor, cx| {
5607        editor.change_selections(None, cx, |s| s.select_ranges([0..1]))
5608    });
5609    editor_a2.update(cx_a, |editor, cx| {
5610        editor.change_selections(None, cx, |s| s.select_ranges([2..3]))
5611    });
5612    workspace_b
5613        .update(cx_b, |workspace, cx| {
5614            workspace
5615                .toggle_follow(&ToggleFollow(client_a_id), cx)
5616                .unwrap()
5617        })
5618        .await
5619        .unwrap();
5620
5621    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
5622        workspace
5623            .active_item(cx)
5624            .unwrap()
5625            .downcast::<Editor>()
5626            .unwrap()
5627    });
5628    assert_eq!(
5629        cx_b.read(|cx| editor_b2.project_path(cx)),
5630        Some((worktree_id, "2.txt").into())
5631    );
5632    assert_eq!(
5633        editor_b2.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
5634        vec![2..3]
5635    );
5636    assert_eq!(
5637        editor_b1.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
5638        vec![0..1]
5639    );
5640
5641    // When client A activates a different editor, client B does so as well.
5642    workspace_a.update(cx_a, |workspace, cx| {
5643        workspace.activate_item(&editor_a1, cx)
5644    });
5645    deterministic.run_until_parked();
5646    workspace_b.read_with(cx_b, |workspace, cx| {
5647        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
5648    });
5649
5650    // When client A opens a multibuffer, client B does so as well.
5651    let multibuffer_a = cx_a.add_model(|cx| {
5652        let buffer_a1 = project_a.update(cx, |project, cx| {
5653            project
5654                .get_open_buffer(&(worktree_id, "1.txt").into(), cx)
5655                .unwrap()
5656        });
5657        let buffer_a2 = project_a.update(cx, |project, cx| {
5658            project
5659                .get_open_buffer(&(worktree_id, "2.txt").into(), cx)
5660                .unwrap()
5661        });
5662        let mut result = MultiBuffer::new(0);
5663        result.push_excerpts(
5664            buffer_a1,
5665            [ExcerptRange {
5666                context: 0..3,
5667                primary: None,
5668            }],
5669            cx,
5670        );
5671        result.push_excerpts(
5672            buffer_a2,
5673            [ExcerptRange {
5674                context: 4..7,
5675                primary: None,
5676            }],
5677            cx,
5678        );
5679        result
5680    });
5681    let multibuffer_editor_a = workspace_a.update(cx_a, |workspace, cx| {
5682        let editor =
5683            cx.add_view(|cx| Editor::for_multibuffer(multibuffer_a, Some(project_a.clone()), cx));
5684        workspace.add_item(Box::new(editor.clone()), cx);
5685        editor
5686    });
5687    deterministic.run_until_parked();
5688    let multibuffer_editor_b = workspace_b.read_with(cx_b, |workspace, cx| {
5689        workspace
5690            .active_item(cx)
5691            .unwrap()
5692            .downcast::<Editor>()
5693            .unwrap()
5694    });
5695    assert_eq!(
5696        multibuffer_editor_a.read_with(cx_a, |editor, cx| editor.text(cx)),
5697        multibuffer_editor_b.read_with(cx_b, |editor, cx| editor.text(cx)),
5698    );
5699
5700    // When client A navigates back and forth, client B does so as well.
5701    workspace_a
5702        .update(cx_a, |workspace, cx| {
5703            workspace::Pane::go_back(workspace, None, cx)
5704        })
5705        .await;
5706    deterministic.run_until_parked();
5707    workspace_b.read_with(cx_b, |workspace, cx| {
5708        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
5709    });
5710
5711    workspace_a
5712        .update(cx_a, |workspace, cx| {
5713            workspace::Pane::go_back(workspace, None, cx)
5714        })
5715        .await;
5716    deterministic.run_until_parked();
5717    workspace_b.read_with(cx_b, |workspace, cx| {
5718        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b2.id());
5719    });
5720
5721    workspace_a
5722        .update(cx_a, |workspace, cx| {
5723            workspace::Pane::go_forward(workspace, None, cx)
5724        })
5725        .await;
5726    deterministic.run_until_parked();
5727    workspace_b.read_with(cx_b, |workspace, cx| {
5728        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
5729    });
5730
5731    // Changes to client A's editor are reflected on client B.
5732    editor_a1.update(cx_a, |editor, cx| {
5733        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2]));
5734    });
5735    deterministic.run_until_parked();
5736    editor_b1.read_with(cx_b, |editor, cx| {
5737        assert_eq!(editor.selections.ranges(cx), &[1..1, 2..2]);
5738    });
5739
5740    editor_a1.update(cx_a, |editor, cx| editor.set_text("TWO", cx));
5741    deterministic.run_until_parked();
5742    editor_b1.read_with(cx_b, |editor, cx| assert_eq!(editor.text(cx), "TWO"));
5743
5744    editor_a1.update(cx_a, |editor, cx| {
5745        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
5746        editor.set_scroll_position(vec2f(0., 100.), cx);
5747    });
5748    deterministic.run_until_parked();
5749    editor_b1.read_with(cx_b, |editor, cx| {
5750        assert_eq!(editor.selections.ranges(cx), &[3..3]);
5751    });
5752
5753    // After unfollowing, client B stops receiving updates from client A.
5754    workspace_b.update(cx_b, |workspace, cx| {
5755        workspace.unfollow(&workspace.active_pane().clone(), cx)
5756    });
5757    workspace_a.update(cx_a, |workspace, cx| {
5758        workspace.activate_item(&editor_a2, cx)
5759    });
5760    deterministic.run_until_parked();
5761    assert_eq!(
5762        workspace_b.read_with(cx_b, |workspace, cx| workspace
5763            .active_item(cx)
5764            .unwrap()
5765            .id()),
5766        editor_b1.id()
5767    );
5768
5769    // Client A starts following client B.
5770    workspace_a
5771        .update(cx_a, |workspace, cx| {
5772            workspace
5773                .toggle_follow(&ToggleFollow(client_b_id), cx)
5774                .unwrap()
5775        })
5776        .await
5777        .unwrap();
5778    assert_eq!(
5779        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
5780        Some(client_b_id)
5781    );
5782    assert_eq!(
5783        workspace_a.read_with(cx_a, |workspace, cx| workspace
5784            .active_item(cx)
5785            .unwrap()
5786            .id()),
5787        editor_a1.id()
5788    );
5789
5790    // Client B activates an external window, which causes a new screen-sharing item to be added to the pane.
5791    let display = MacOSDisplay::new();
5792    active_call_b
5793        .update(cx_b, |call, cx| call.set_location(None, cx))
5794        .await
5795        .unwrap();
5796    active_call_b
5797        .update(cx_b, |call, cx| {
5798            call.room().unwrap().update(cx, |room, cx| {
5799                room.set_display_sources(vec![display.clone()]);
5800                room.share_screen(cx)
5801            })
5802        })
5803        .await
5804        .unwrap();
5805    deterministic.run_until_parked();
5806    let shared_screen = workspace_a.read_with(cx_a, |workspace, cx| {
5807        workspace
5808            .active_item(cx)
5809            .unwrap()
5810            .downcast::<SharedScreen>()
5811            .unwrap()
5812    });
5813
5814    // Client B activates Zed again, which causes the previous editor to become focused again.
5815    active_call_b
5816        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
5817        .await
5818        .unwrap();
5819    deterministic.run_until_parked();
5820    workspace_a.read_with(cx_a, |workspace, cx| {
5821        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_a1.id())
5822    });
5823
5824    // Client B activates a multibuffer that was created by following client A. Client A returns to that multibuffer.
5825    workspace_b.update(cx_b, |workspace, cx| {
5826        workspace.activate_item(&multibuffer_editor_b, cx)
5827    });
5828    deterministic.run_until_parked();
5829    workspace_a.read_with(cx_a, |workspace, cx| {
5830        assert_eq!(
5831            workspace.active_item(cx).unwrap().id(),
5832            multibuffer_editor_a.id()
5833        )
5834    });
5835
5836    // Client B activates an external window again, and the previously-opened screen-sharing item
5837    // gets activated.
5838    active_call_b
5839        .update(cx_b, |call, cx| call.set_location(None, cx))
5840        .await
5841        .unwrap();
5842    deterministic.run_until_parked();
5843    assert_eq!(
5844        workspace_a.read_with(cx_a, |workspace, cx| workspace
5845            .active_item(cx)
5846            .unwrap()
5847            .id()),
5848        shared_screen.id()
5849    );
5850
5851    // Following interrupts when client B disconnects.
5852    client_b.disconnect(&cx_b.to_async()).unwrap();
5853    deterministic.advance_clock(RECONNECT_TIMEOUT);
5854    assert_eq!(
5855        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
5856        None
5857    );
5858}
5859
5860#[gpui::test]
5861async fn test_following_tab_order(
5862    deterministic: Arc<Deterministic>,
5863    cx_a: &mut TestAppContext,
5864    cx_b: &mut TestAppContext,
5865) {
5866    cx_a.update(editor::init);
5867    cx_b.update(editor::init);
5868
5869    let mut server = TestServer::start(&deterministic).await;
5870    let client_a = server.create_client(cx_a, "user_a").await;
5871    let client_b = server.create_client(cx_b, "user_b").await;
5872    server
5873        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5874        .await;
5875    let active_call_a = cx_a.read(ActiveCall::global);
5876    let active_call_b = cx_b.read(ActiveCall::global);
5877
5878    client_a
5879        .fs
5880        .insert_tree(
5881            "/a",
5882            json!({
5883                "1.txt": "one",
5884                "2.txt": "two",
5885                "3.txt": "three",
5886            }),
5887        )
5888        .await;
5889    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
5890    active_call_a
5891        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
5892        .await
5893        .unwrap();
5894
5895    let project_id = active_call_a
5896        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5897        .await
5898        .unwrap();
5899    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5900    active_call_b
5901        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
5902        .await
5903        .unwrap();
5904
5905    let workspace_a = client_a.build_workspace(&project_a, cx_a);
5906    let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
5907
5908    let workspace_b = client_b.build_workspace(&project_b, cx_b);
5909    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
5910
5911    let client_b_id = project_a.read_with(cx_a, |project, _| {
5912        project.collaborators().values().next().unwrap().peer_id
5913    });
5914
5915    //Open 1, 3 in that order on client A
5916    workspace_a
5917        .update(cx_a, |workspace, cx| {
5918            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
5919        })
5920        .await
5921        .unwrap();
5922    workspace_a
5923        .update(cx_a, |workspace, cx| {
5924            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
5925        })
5926        .await
5927        .unwrap();
5928
5929    let pane_paths = |pane: &ViewHandle<workspace::Pane>, cx: &mut TestAppContext| {
5930        pane.update(cx, |pane, cx| {
5931            pane.items()
5932                .map(|item| {
5933                    item.project_path(cx)
5934                        .unwrap()
5935                        .path
5936                        .to_str()
5937                        .unwrap()
5938                        .to_owned()
5939                })
5940                .collect::<Vec<_>>()
5941        })
5942    };
5943
5944    //Verify that the tabs opened in the order we expect
5945    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt"]);
5946
5947    //Follow client B as client A
5948    workspace_a
5949        .update(cx_a, |workspace, cx| {
5950            workspace
5951                .toggle_follow(&ToggleFollow(client_b_id), cx)
5952                .unwrap()
5953        })
5954        .await
5955        .unwrap();
5956
5957    //Open just 2 on client B
5958    workspace_b
5959        .update(cx_b, |workspace, cx| {
5960            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
5961        })
5962        .await
5963        .unwrap();
5964    deterministic.run_until_parked();
5965
5966    // Verify that newly opened followed file is at the end
5967    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt", "2.txt"]);
5968
5969    //Open just 1 on client B
5970    workspace_b
5971        .update(cx_b, |workspace, cx| {
5972            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
5973        })
5974        .await
5975        .unwrap();
5976    assert_eq!(&pane_paths(&pane_b, cx_b), &["2.txt", "1.txt"]);
5977    deterministic.run_until_parked();
5978
5979    // Verify that following into 1 did not reorder
5980    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt", "2.txt"]);
5981}
5982
5983#[gpui::test(iterations = 10)]
5984async fn test_peers_following_each_other(
5985    deterministic: Arc<Deterministic>,
5986    cx_a: &mut TestAppContext,
5987    cx_b: &mut TestAppContext,
5988) {
5989    deterministic.forbid_parking();
5990    cx_a.update(editor::init);
5991    cx_b.update(editor::init);
5992
5993    let mut server = TestServer::start(&deterministic).await;
5994    let client_a = server.create_client(cx_a, "user_a").await;
5995    let client_b = server.create_client(cx_b, "user_b").await;
5996    server
5997        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5998        .await;
5999    let active_call_a = cx_a.read(ActiveCall::global);
6000    let active_call_b = cx_b.read(ActiveCall::global);
6001
6002    // Client A shares a project.
6003    client_a
6004        .fs
6005        .insert_tree(
6006            "/a",
6007            json!({
6008                "1.txt": "one",
6009                "2.txt": "two",
6010                "3.txt": "three",
6011                "4.txt": "four",
6012            }),
6013        )
6014        .await;
6015    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
6016    active_call_a
6017        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
6018        .await
6019        .unwrap();
6020    let project_id = active_call_a
6021        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6022        .await
6023        .unwrap();
6024
6025    // Client B joins the project.
6026    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6027    active_call_b
6028        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6029        .await
6030        .unwrap();
6031
6032    // Client A opens some editors.
6033    let workspace_a = client_a.build_workspace(&project_a, cx_a);
6034    let pane_a1 = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
6035    let _editor_a1 = workspace_a
6036        .update(cx_a, |workspace, cx| {
6037            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6038        })
6039        .await
6040        .unwrap()
6041        .downcast::<Editor>()
6042        .unwrap();
6043
6044    // Client B opens an editor.
6045    let workspace_b = client_b.build_workspace(&project_b, cx_b);
6046    let pane_b1 = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
6047    let _editor_b1 = workspace_b
6048        .update(cx_b, |workspace, cx| {
6049            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
6050        })
6051        .await
6052        .unwrap()
6053        .downcast::<Editor>()
6054        .unwrap();
6055
6056    // Clients A and B follow each other in split panes
6057    workspace_a.update(cx_a, |workspace, cx| {
6058        workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
6059        let pane_a1 = pane_a1.clone();
6060        cx.defer(move |workspace, _| {
6061            assert_ne!(*workspace.active_pane(), pane_a1);
6062        });
6063    });
6064    workspace_a
6065        .update(cx_a, |workspace, cx| {
6066            let leader_id = *project_a.read(cx).collaborators().keys().next().unwrap();
6067            workspace
6068                .toggle_follow(&workspace::ToggleFollow(leader_id), cx)
6069                .unwrap()
6070        })
6071        .await
6072        .unwrap();
6073    workspace_b.update(cx_b, |workspace, cx| {
6074        workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
6075        let pane_b1 = pane_b1.clone();
6076        cx.defer(move |workspace, _| {
6077            assert_ne!(*workspace.active_pane(), pane_b1);
6078        });
6079    });
6080    workspace_b
6081        .update(cx_b, |workspace, cx| {
6082            let leader_id = *project_b.read(cx).collaborators().keys().next().unwrap();
6083            workspace
6084                .toggle_follow(&workspace::ToggleFollow(leader_id), cx)
6085                .unwrap()
6086        })
6087        .await
6088        .unwrap();
6089
6090    workspace_a.update(cx_a, |workspace, cx| {
6091        workspace.activate_next_pane(cx);
6092    });
6093    // Wait for focus effects to be fully flushed
6094    workspace_a.update(cx_a, |workspace, _| {
6095        assert_eq!(*workspace.active_pane(), pane_a1);
6096    });
6097
6098    workspace_a
6099        .update(cx_a, |workspace, cx| {
6100            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
6101        })
6102        .await
6103        .unwrap();
6104    workspace_b.update(cx_b, |workspace, cx| {
6105        workspace.activate_next_pane(cx);
6106    });
6107
6108    workspace_b
6109        .update(cx_b, |workspace, cx| {
6110            assert_eq!(*workspace.active_pane(), pane_b1);
6111            workspace.open_path((worktree_id, "4.txt"), None, true, cx)
6112        })
6113        .await
6114        .unwrap();
6115    cx_a.foreground().run_until_parked();
6116
6117    // Ensure leader updates don't change the active pane of followers
6118    workspace_a.read_with(cx_a, |workspace, _| {
6119        assert_eq!(*workspace.active_pane(), pane_a1);
6120    });
6121    workspace_b.read_with(cx_b, |workspace, _| {
6122        assert_eq!(*workspace.active_pane(), pane_b1);
6123    });
6124
6125    // Ensure peers following each other doesn't cause an infinite loop.
6126    assert_eq!(
6127        workspace_a.read_with(cx_a, |workspace, cx| workspace
6128            .active_item(cx)
6129            .unwrap()
6130            .project_path(cx)),
6131        Some((worktree_id, "3.txt").into())
6132    );
6133    workspace_a.update(cx_a, |workspace, cx| {
6134        assert_eq!(
6135            workspace.active_item(cx).unwrap().project_path(cx),
6136            Some((worktree_id, "3.txt").into())
6137        );
6138        workspace.activate_next_pane(cx);
6139    });
6140
6141    workspace_a.update(cx_a, |workspace, cx| {
6142        assert_eq!(
6143            workspace.active_item(cx).unwrap().project_path(cx),
6144            Some((worktree_id, "4.txt").into())
6145        );
6146    });
6147
6148    workspace_b.update(cx_b, |workspace, cx| {
6149        assert_eq!(
6150            workspace.active_item(cx).unwrap().project_path(cx),
6151            Some((worktree_id, "4.txt").into())
6152        );
6153        workspace.activate_next_pane(cx);
6154    });
6155
6156    workspace_b.update(cx_b, |workspace, cx| {
6157        assert_eq!(
6158            workspace.active_item(cx).unwrap().project_path(cx),
6159            Some((worktree_id, "3.txt").into())
6160        );
6161    });
6162}
6163
6164#[gpui::test(iterations = 10)]
6165async fn test_auto_unfollowing(
6166    deterministic: Arc<Deterministic>,
6167    cx_a: &mut TestAppContext,
6168    cx_b: &mut TestAppContext,
6169) {
6170    deterministic.forbid_parking();
6171    cx_a.update(editor::init);
6172    cx_b.update(editor::init);
6173
6174    // 2 clients connect to a server.
6175    let mut server = TestServer::start(&deterministic).await;
6176    let client_a = server.create_client(cx_a, "user_a").await;
6177    let client_b = server.create_client(cx_b, "user_b").await;
6178    server
6179        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6180        .await;
6181    let active_call_a = cx_a.read(ActiveCall::global);
6182    let active_call_b = cx_b.read(ActiveCall::global);
6183
6184    // Client A shares a project.
6185    client_a
6186        .fs
6187        .insert_tree(
6188            "/a",
6189            json!({
6190                "1.txt": "one",
6191                "2.txt": "two",
6192                "3.txt": "three",
6193            }),
6194        )
6195        .await;
6196    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
6197    active_call_a
6198        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
6199        .await
6200        .unwrap();
6201
6202    let project_id = active_call_a
6203        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6204        .await
6205        .unwrap();
6206    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6207    active_call_b
6208        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6209        .await
6210        .unwrap();
6211
6212    // Client A opens some editors.
6213    let workspace_a = client_a.build_workspace(&project_a, cx_a);
6214    let _editor_a1 = workspace_a
6215        .update(cx_a, |workspace, cx| {
6216            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6217        })
6218        .await
6219        .unwrap()
6220        .downcast::<Editor>()
6221        .unwrap();
6222
6223    // Client B starts following client A.
6224    let workspace_b = client_b.build_workspace(&project_b, cx_b);
6225    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
6226    let leader_id = project_b.read_with(cx_b, |project, _| {
6227        project.collaborators().values().next().unwrap().peer_id
6228    });
6229    workspace_b
6230        .update(cx_b, |workspace, cx| {
6231            workspace
6232                .toggle_follow(&ToggleFollow(leader_id), cx)
6233                .unwrap()
6234        })
6235        .await
6236        .unwrap();
6237    assert_eq!(
6238        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6239        Some(leader_id)
6240    );
6241    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
6242        workspace
6243            .active_item(cx)
6244            .unwrap()
6245            .downcast::<Editor>()
6246            .unwrap()
6247    });
6248
6249    // When client B moves, it automatically stops following client A.
6250    editor_b2.update(cx_b, |editor, cx| editor.move_right(&editor::MoveRight, cx));
6251    assert_eq!(
6252        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6253        None
6254    );
6255
6256    workspace_b
6257        .update(cx_b, |workspace, cx| {
6258            workspace
6259                .toggle_follow(&ToggleFollow(leader_id), cx)
6260                .unwrap()
6261        })
6262        .await
6263        .unwrap();
6264    assert_eq!(
6265        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6266        Some(leader_id)
6267    );
6268
6269    // When client B edits, it automatically stops following client A.
6270    editor_b2.update(cx_b, |editor, cx| editor.insert("X", cx));
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 scrolls, it automatically stops following client A.
6290    editor_b2.update(cx_b, |editor, cx| {
6291        editor.set_scroll_position(vec2f(0., 3.), cx)
6292    });
6293    assert_eq!(
6294        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6295        None
6296    );
6297
6298    workspace_b
6299        .update(cx_b, |workspace, cx| {
6300            workspace
6301                .toggle_follow(&ToggleFollow(leader_id), cx)
6302                .unwrap()
6303        })
6304        .await
6305        .unwrap();
6306    assert_eq!(
6307        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6308        Some(leader_id)
6309    );
6310
6311    // When client B activates a different pane, it continues following client A in the original pane.
6312    workspace_b.update(cx_b, |workspace, cx| {
6313        workspace.split_pane(pane_b.clone(), SplitDirection::Right, cx)
6314    });
6315    assert_eq!(
6316        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6317        Some(leader_id)
6318    );
6319
6320    workspace_b.update(cx_b, |workspace, cx| workspace.activate_next_pane(cx));
6321    assert_eq!(
6322        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6323        Some(leader_id)
6324    );
6325
6326    // When client B activates a different item in the original pane, it automatically stops following client A.
6327    workspace_b
6328        .update(cx_b, |workspace, cx| {
6329            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
6330        })
6331        .await
6332        .unwrap();
6333    assert_eq!(
6334        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6335        None
6336    );
6337}
6338
6339#[gpui::test(iterations = 10)]
6340async fn test_peers_simultaneously_following_each_other(
6341    deterministic: Arc<Deterministic>,
6342    cx_a: &mut TestAppContext,
6343    cx_b: &mut TestAppContext,
6344) {
6345    deterministic.forbid_parking();
6346    cx_a.update(editor::init);
6347    cx_b.update(editor::init);
6348
6349    let mut server = TestServer::start(&deterministic).await;
6350    let client_a = server.create_client(cx_a, "user_a").await;
6351    let client_b = server.create_client(cx_b, "user_b").await;
6352    server
6353        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6354        .await;
6355    let active_call_a = cx_a.read(ActiveCall::global);
6356
6357    client_a.fs.insert_tree("/a", json!({})).await;
6358    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
6359    let workspace_a = client_a.build_workspace(&project_a, cx_a);
6360    let project_id = active_call_a
6361        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6362        .await
6363        .unwrap();
6364
6365    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6366    let workspace_b = client_b.build_workspace(&project_b, cx_b);
6367
6368    deterministic.run_until_parked();
6369    let client_a_id = project_b.read_with(cx_b, |project, _| {
6370        project.collaborators().values().next().unwrap().peer_id
6371    });
6372    let client_b_id = project_a.read_with(cx_a, |project, _| {
6373        project.collaborators().values().next().unwrap().peer_id
6374    });
6375
6376    let a_follow_b = workspace_a.update(cx_a, |workspace, cx| {
6377        workspace
6378            .toggle_follow(&ToggleFollow(client_b_id), cx)
6379            .unwrap()
6380    });
6381    let b_follow_a = workspace_b.update(cx_b, |workspace, cx| {
6382        workspace
6383            .toggle_follow(&ToggleFollow(client_a_id), cx)
6384            .unwrap()
6385    });
6386
6387    futures::try_join!(a_follow_b, b_follow_a).unwrap();
6388    workspace_a.read_with(cx_a, |workspace, _| {
6389        assert_eq!(
6390            workspace.leader_for_pane(workspace.active_pane()),
6391            Some(client_b_id)
6392        );
6393    });
6394    workspace_b.read_with(cx_b, |workspace, _| {
6395        assert_eq!(
6396            workspace.leader_for_pane(workspace.active_pane()),
6397            Some(client_a_id)
6398        );
6399    });
6400}
6401
6402#[derive(Debug, Eq, PartialEq)]
6403struct RoomParticipants {
6404    remote: Vec<String>,
6405    pending: Vec<String>,
6406}
6407
6408fn room_participants(room: &ModelHandle<Room>, cx: &mut TestAppContext) -> RoomParticipants {
6409    room.read_with(cx, |room, _| {
6410        let mut remote = room
6411            .remote_participants()
6412            .iter()
6413            .map(|(_, participant)| participant.user.github_login.clone())
6414            .collect::<Vec<_>>();
6415        let mut pending = room
6416            .pending_participants()
6417            .iter()
6418            .map(|user| user.github_login.clone())
6419            .collect::<Vec<_>>();
6420        remote.sort();
6421        pending.sort();
6422        RoomParticipants { remote, pending }
6423    })
6424}