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