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, HoverBlockKind, 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 (window_id_b, workspace_b) =
1477        cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx));
1478    let editor_b = workspace_b
1479        .update(cx_b, |workspace, cx| {
1480            workspace.open_path((worktree_id, "b.txt"), None, true, cx)
1481        })
1482        .await
1483        .unwrap()
1484        .downcast::<Editor>()
1485        .unwrap();
1486    assert!(cx_b
1487        .read_window(window_id_b, |cx| editor_b.is_focused(cx))
1488        .unwrap());
1489    editor_b.update(cx_b, |editor, cx| editor.insert("X", cx));
1490    assert!(cx_b.is_window_edited(workspace_b.window_id()));
1491
1492    // Drop client A's connection. Collaborators should disappear and the project should not be shown as shared.
1493    server.forbid_connections();
1494    server.disconnect_client(client_a.peer_id().unwrap());
1495    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
1496    project_a.read_with(cx_a, |project, _| project.collaborators().is_empty());
1497    project_a.read_with(cx_a, |project, _| assert!(!project.is_shared()));
1498    project_b.read_with(cx_b, |project, _| project.is_read_only());
1499    assert!(worktree_a.read_with(cx_a, |tree, _| !tree.as_local().unwrap().is_shared()));
1500
1501    // Ensure client B's edited state is reset and that the whole window is blurred.
1502    cx_b.read_window(window_id_b, |cx| {
1503        assert_eq!(cx.focused_view_id(), None);
1504    });
1505    assert!(!cx_b.is_window_edited(workspace_b.window_id()));
1506
1507    // Ensure client B is not prompted to save edits when closing window after disconnecting.
1508    let can_close = workspace_b
1509        .update(cx_b, |workspace, cx| workspace.prepare_to_close(true, cx))
1510        .await
1511        .unwrap();
1512    assert!(can_close);
1513
1514    // Allow client A to reconnect to the server.
1515    server.allow_connections();
1516    deterministic.advance_clock(RECEIVE_TIMEOUT);
1517
1518    // Client B calls client A again after they reconnected.
1519    let active_call_b = cx_b.read(ActiveCall::global);
1520    active_call_b
1521        .update(cx_b, |call, cx| {
1522            call.invite(client_a.user_id().unwrap(), None, cx)
1523        })
1524        .await
1525        .unwrap();
1526    deterministic.run_until_parked();
1527    active_call_a
1528        .update(cx_a, |call, cx| call.accept_incoming(cx))
1529        .await
1530        .unwrap();
1531
1532    active_call_a
1533        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
1534        .await
1535        .unwrap();
1536
1537    // Drop client A's connection again. We should still unshare it successfully.
1538    server.forbid_connections();
1539    server.disconnect_client(client_a.peer_id().unwrap());
1540    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
1541    project_a.read_with(cx_a, |project, _| assert!(!project.is_shared()));
1542}
1543
1544#[gpui::test(iterations = 10)]
1545async fn test_project_reconnect(
1546    deterministic: Arc<Deterministic>,
1547    cx_a: &mut TestAppContext,
1548    cx_b: &mut TestAppContext,
1549) {
1550    cx_b.update(editor::init);
1551    deterministic.forbid_parking();
1552    let mut server = TestServer::start(&deterministic).await;
1553    let client_a = server.create_client(cx_a, "user_a").await;
1554    let client_b = server.create_client(cx_b, "user_b").await;
1555    server
1556        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
1557        .await;
1558
1559    client_a
1560        .fs
1561        .insert_tree(
1562            "/root-1",
1563            json!({
1564                "dir1": {
1565                    "a.txt": "a",
1566                    "b.txt": "b",
1567                    "subdir1": {
1568                        "c.txt": "c",
1569                        "d.txt": "d",
1570                        "e.txt": "e",
1571                    }
1572                },
1573                "dir2": {
1574                    "v.txt": "v",
1575                },
1576                "dir3": {
1577                    "w.txt": "w",
1578                    "x.txt": "x",
1579                    "y.txt": "y",
1580                },
1581                "dir4": {
1582                    "z.txt": "z",
1583                },
1584            }),
1585        )
1586        .await;
1587    client_a
1588        .fs
1589        .insert_tree(
1590            "/root-2",
1591            json!({
1592                "2.txt": "2",
1593            }),
1594        )
1595        .await;
1596    client_a
1597        .fs
1598        .insert_tree(
1599            "/root-3",
1600            json!({
1601                "3.txt": "3",
1602            }),
1603        )
1604        .await;
1605
1606    let active_call_a = cx_a.read(ActiveCall::global);
1607    let (project_a1, _) = client_a.build_local_project("/root-1/dir1", cx_a).await;
1608    let (project_a2, _) = client_a.build_local_project("/root-2", cx_a).await;
1609    let (project_a3, _) = client_a.build_local_project("/root-3", cx_a).await;
1610    let worktree_a1 =
1611        project_a1.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
1612    let project1_id = active_call_a
1613        .update(cx_a, |call, cx| call.share_project(project_a1.clone(), cx))
1614        .await
1615        .unwrap();
1616    let project2_id = active_call_a
1617        .update(cx_a, |call, cx| call.share_project(project_a2.clone(), cx))
1618        .await
1619        .unwrap();
1620    let project3_id = active_call_a
1621        .update(cx_a, |call, cx| call.share_project(project_a3.clone(), cx))
1622        .await
1623        .unwrap();
1624
1625    let project_b1 = client_b.build_remote_project(project1_id, cx_b).await;
1626    let project_b2 = client_b.build_remote_project(project2_id, cx_b).await;
1627    let project_b3 = client_b.build_remote_project(project3_id, cx_b).await;
1628    deterministic.run_until_parked();
1629
1630    let worktree1_id = worktree_a1.read_with(cx_a, |worktree, _| {
1631        assert!(worktree.as_local().unwrap().is_shared());
1632        worktree.id()
1633    });
1634    let (worktree_a2, _) = project_a1
1635        .update(cx_a, |p, cx| {
1636            p.find_or_create_local_worktree("/root-1/dir2", true, cx)
1637        })
1638        .await
1639        .unwrap();
1640    deterministic.run_until_parked();
1641    let worktree2_id = worktree_a2.read_with(cx_a, |tree, _| {
1642        assert!(tree.as_local().unwrap().is_shared());
1643        tree.id()
1644    });
1645    deterministic.run_until_parked();
1646    project_b1.read_with(cx_b, |project, cx| {
1647        assert!(project.worktree_for_id(worktree2_id, cx).is_some())
1648    });
1649
1650    let buffer_a1 = project_a1
1651        .update(cx_a, |p, cx| p.open_buffer((worktree1_id, "a.txt"), cx))
1652        .await
1653        .unwrap();
1654    let buffer_b1 = project_b1
1655        .update(cx_b, |p, cx| p.open_buffer((worktree1_id, "a.txt"), cx))
1656        .await
1657        .unwrap();
1658
1659    // Drop client A's connection.
1660    server.forbid_connections();
1661    server.disconnect_client(client_a.peer_id().unwrap());
1662    deterministic.advance_clock(RECEIVE_TIMEOUT);
1663    project_a1.read_with(cx_a, |project, _| {
1664        assert!(project.is_shared());
1665        assert_eq!(project.collaborators().len(), 1);
1666    });
1667    project_b1.read_with(cx_b, |project, _| {
1668        assert!(!project.is_read_only());
1669        assert_eq!(project.collaborators().len(), 1);
1670    });
1671    worktree_a1.read_with(cx_a, |tree, _| {
1672        assert!(tree.as_local().unwrap().is_shared())
1673    });
1674
1675    // While client A is disconnected, add and remove files from client A's project.
1676    client_a
1677        .fs
1678        .insert_tree(
1679            "/root-1/dir1/subdir2",
1680            json!({
1681                "f.txt": "f-contents",
1682                "g.txt": "g-contents",
1683                "h.txt": "h-contents",
1684                "i.txt": "i-contents",
1685            }),
1686        )
1687        .await;
1688    client_a
1689        .fs
1690        .remove_dir(
1691            "/root-1/dir1/subdir1".as_ref(),
1692            RemoveOptions {
1693                recursive: true,
1694                ..Default::default()
1695            },
1696        )
1697        .await
1698        .unwrap();
1699
1700    // While client A is disconnected, add and remove worktrees from client A's project.
1701    project_a1.update(cx_a, |project, cx| {
1702        project.remove_worktree(worktree2_id, cx)
1703    });
1704    let (worktree_a3, _) = project_a1
1705        .update(cx_a, |p, cx| {
1706            p.find_or_create_local_worktree("/root-1/dir3", true, cx)
1707        })
1708        .await
1709        .unwrap();
1710    worktree_a3
1711        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
1712        .await;
1713    let worktree3_id = worktree_a3.read_with(cx_a, |tree, _| {
1714        assert!(!tree.as_local().unwrap().is_shared());
1715        tree.id()
1716    });
1717    deterministic.run_until_parked();
1718
1719    // While client A is disconnected, close project 2
1720    cx_a.update(|_| drop(project_a2));
1721
1722    // While client A is disconnected, mutate a buffer on both the host and the guest.
1723    buffer_a1.update(cx_a, |buf, cx| buf.edit([(0..0, "W")], None, cx));
1724    buffer_b1.update(cx_b, |buf, cx| buf.edit([(1..1, "Z")], None, cx));
1725    deterministic.run_until_parked();
1726
1727    // Client A reconnects. Their project is re-shared, and client B re-joins it.
1728    server.allow_connections();
1729    client_a
1730        .authenticate_and_connect(false, &cx_a.to_async())
1731        .await
1732        .unwrap();
1733    deterministic.run_until_parked();
1734    project_a1.read_with(cx_a, |project, cx| {
1735        assert!(project.is_shared());
1736        assert!(worktree_a1.read(cx).as_local().unwrap().is_shared());
1737        assert_eq!(
1738            worktree_a1
1739                .read(cx)
1740                .snapshot()
1741                .paths()
1742                .map(|p| p.to_str().unwrap())
1743                .collect::<Vec<_>>(),
1744            vec![
1745                "a.txt",
1746                "b.txt",
1747                "subdir2",
1748                "subdir2/f.txt",
1749                "subdir2/g.txt",
1750                "subdir2/h.txt",
1751                "subdir2/i.txt"
1752            ]
1753        );
1754        assert!(worktree_a3.read(cx).as_local().unwrap().is_shared());
1755        assert_eq!(
1756            worktree_a3
1757                .read(cx)
1758                .snapshot()
1759                .paths()
1760                .map(|p| p.to_str().unwrap())
1761                .collect::<Vec<_>>(),
1762            vec!["w.txt", "x.txt", "y.txt"]
1763        );
1764    });
1765    project_b1.read_with(cx_b, |project, cx| {
1766        assert!(!project.is_read_only());
1767        assert_eq!(
1768            project
1769                .worktree_for_id(worktree1_id, cx)
1770                .unwrap()
1771                .read(cx)
1772                .snapshot()
1773                .paths()
1774                .map(|p| p.to_str().unwrap())
1775                .collect::<Vec<_>>(),
1776            vec![
1777                "a.txt",
1778                "b.txt",
1779                "subdir2",
1780                "subdir2/f.txt",
1781                "subdir2/g.txt",
1782                "subdir2/h.txt",
1783                "subdir2/i.txt"
1784            ]
1785        );
1786        assert!(project.worktree_for_id(worktree2_id, cx).is_none());
1787        assert_eq!(
1788            project
1789                .worktree_for_id(worktree3_id, cx)
1790                .unwrap()
1791                .read(cx)
1792                .snapshot()
1793                .paths()
1794                .map(|p| p.to_str().unwrap())
1795                .collect::<Vec<_>>(),
1796            vec!["w.txt", "x.txt", "y.txt"]
1797        );
1798    });
1799    project_b2.read_with(cx_b, |project, _| assert!(project.is_read_only()));
1800    project_b3.read_with(cx_b, |project, _| assert!(!project.is_read_only()));
1801    buffer_a1.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "WaZ"));
1802    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "WaZ"));
1803
1804    // Drop client B's connection.
1805    server.forbid_connections();
1806    server.disconnect_client(client_b.peer_id().unwrap());
1807    deterministic.advance_clock(RECEIVE_TIMEOUT);
1808
1809    // While client B is disconnected, add and remove files from client A's project
1810    client_a
1811        .fs
1812        .insert_file("/root-1/dir1/subdir2/j.txt", "j-contents".into())
1813        .await;
1814    client_a
1815        .fs
1816        .remove_file("/root-1/dir1/subdir2/i.txt".as_ref(), Default::default())
1817        .await
1818        .unwrap();
1819
1820    // While client B is disconnected, add and remove worktrees from client A's project.
1821    let (worktree_a4, _) = project_a1
1822        .update(cx_a, |p, cx| {
1823            p.find_or_create_local_worktree("/root-1/dir4", true, cx)
1824        })
1825        .await
1826        .unwrap();
1827    deterministic.run_until_parked();
1828    let worktree4_id = worktree_a4.read_with(cx_a, |tree, _| {
1829        assert!(tree.as_local().unwrap().is_shared());
1830        tree.id()
1831    });
1832    project_a1.update(cx_a, |project, cx| {
1833        project.remove_worktree(worktree3_id, cx)
1834    });
1835    deterministic.run_until_parked();
1836
1837    // While client B is disconnected, mutate a buffer on both the host and the guest.
1838    buffer_a1.update(cx_a, |buf, cx| buf.edit([(1..1, "X")], None, cx));
1839    buffer_b1.update(cx_b, |buf, cx| buf.edit([(2..2, "Y")], None, cx));
1840    deterministic.run_until_parked();
1841
1842    // While disconnected, close project 3
1843    cx_a.update(|_| drop(project_a3));
1844
1845    // Client B reconnects. They re-join the room and the remaining shared project.
1846    server.allow_connections();
1847    client_b
1848        .authenticate_and_connect(false, &cx_b.to_async())
1849        .await
1850        .unwrap();
1851    deterministic.run_until_parked();
1852    project_b1.read_with(cx_b, |project, cx| {
1853        assert!(!project.is_read_only());
1854        assert_eq!(
1855            project
1856                .worktree_for_id(worktree1_id, cx)
1857                .unwrap()
1858                .read(cx)
1859                .snapshot()
1860                .paths()
1861                .map(|p| p.to_str().unwrap())
1862                .collect::<Vec<_>>(),
1863            vec![
1864                "a.txt",
1865                "b.txt",
1866                "subdir2",
1867                "subdir2/f.txt",
1868                "subdir2/g.txt",
1869                "subdir2/h.txt",
1870                "subdir2/j.txt"
1871            ]
1872        );
1873        assert!(project.worktree_for_id(worktree2_id, cx).is_none());
1874        assert_eq!(
1875            project
1876                .worktree_for_id(worktree4_id, cx)
1877                .unwrap()
1878                .read(cx)
1879                .snapshot()
1880                .paths()
1881                .map(|p| p.to_str().unwrap())
1882                .collect::<Vec<_>>(),
1883            vec!["z.txt"]
1884        );
1885    });
1886    project_b3.read_with(cx_b, |project, _| assert!(project.is_read_only()));
1887    buffer_a1.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "WXaYZ"));
1888    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "WXaYZ"));
1889}
1890
1891#[gpui::test(iterations = 10)]
1892async fn test_active_call_events(
1893    deterministic: Arc<Deterministic>,
1894    cx_a: &mut TestAppContext,
1895    cx_b: &mut TestAppContext,
1896) {
1897    deterministic.forbid_parking();
1898    let mut server = TestServer::start(&deterministic).await;
1899    let client_a = server.create_client(cx_a, "user_a").await;
1900    let client_b = server.create_client(cx_b, "user_b").await;
1901    client_a.fs.insert_tree("/a", json!({})).await;
1902    client_b.fs.insert_tree("/b", json!({})).await;
1903
1904    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
1905    let (project_b, _) = client_b.build_local_project("/b", cx_b).await;
1906
1907    server
1908        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
1909        .await;
1910    let active_call_a = cx_a.read(ActiveCall::global);
1911    let active_call_b = cx_b.read(ActiveCall::global);
1912
1913    let events_a = active_call_events(cx_a);
1914    let events_b = active_call_events(cx_b);
1915
1916    let project_a_id = active_call_a
1917        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
1918        .await
1919        .unwrap();
1920    deterministic.run_until_parked();
1921    assert_eq!(mem::take(&mut *events_a.borrow_mut()), vec![]);
1922    assert_eq!(
1923        mem::take(&mut *events_b.borrow_mut()),
1924        vec![room::Event::RemoteProjectShared {
1925            owner: Arc::new(User {
1926                id: client_a.user_id().unwrap(),
1927                github_login: "user_a".to_string(),
1928                avatar: None,
1929            }),
1930            project_id: project_a_id,
1931            worktree_root_names: vec!["a".to_string()],
1932        }]
1933    );
1934
1935    let project_b_id = active_call_b
1936        .update(cx_b, |call, cx| call.share_project(project_b.clone(), cx))
1937        .await
1938        .unwrap();
1939    deterministic.run_until_parked();
1940    assert_eq!(
1941        mem::take(&mut *events_a.borrow_mut()),
1942        vec![room::Event::RemoteProjectShared {
1943            owner: Arc::new(User {
1944                id: client_b.user_id().unwrap(),
1945                github_login: "user_b".to_string(),
1946                avatar: None,
1947            }),
1948            project_id: project_b_id,
1949            worktree_root_names: vec!["b".to_string()]
1950        }]
1951    );
1952    assert_eq!(mem::take(&mut *events_b.borrow_mut()), vec![]);
1953
1954    // Sharing a project twice is idempotent.
1955    let project_b_id_2 = active_call_b
1956        .update(cx_b, |call, cx| call.share_project(project_b.clone(), cx))
1957        .await
1958        .unwrap();
1959    assert_eq!(project_b_id_2, project_b_id);
1960    deterministic.run_until_parked();
1961    assert_eq!(mem::take(&mut *events_a.borrow_mut()), vec![]);
1962    assert_eq!(mem::take(&mut *events_b.borrow_mut()), vec![]);
1963}
1964
1965fn active_call_events(cx: &mut TestAppContext) -> Rc<RefCell<Vec<room::Event>>> {
1966    let events = Rc::new(RefCell::new(Vec::new()));
1967    let active_call = cx.read(ActiveCall::global);
1968    cx.update({
1969        let events = events.clone();
1970        |cx| {
1971            cx.subscribe(&active_call, move |_, event, _| {
1972                events.borrow_mut().push(event.clone())
1973            })
1974            .detach()
1975        }
1976    });
1977    events
1978}
1979
1980#[gpui::test(iterations = 10)]
1981async fn test_room_location(
1982    deterministic: Arc<Deterministic>,
1983    cx_a: &mut TestAppContext,
1984    cx_b: &mut TestAppContext,
1985) {
1986    deterministic.forbid_parking();
1987    let mut server = TestServer::start(&deterministic).await;
1988    let client_a = server.create_client(cx_a, "user_a").await;
1989    let client_b = server.create_client(cx_b, "user_b").await;
1990    client_a.fs.insert_tree("/a", json!({})).await;
1991    client_b.fs.insert_tree("/b", json!({})).await;
1992
1993    let active_call_a = cx_a.read(ActiveCall::global);
1994    let active_call_b = cx_b.read(ActiveCall::global);
1995
1996    let a_notified = Rc::new(Cell::new(false));
1997    cx_a.update({
1998        let notified = a_notified.clone();
1999        |cx| {
2000            cx.observe(&active_call_a, move |_, _| notified.set(true))
2001                .detach()
2002        }
2003    });
2004
2005    let b_notified = Rc::new(Cell::new(false));
2006    cx_b.update({
2007        let b_notified = b_notified.clone();
2008        |cx| {
2009            cx.observe(&active_call_b, move |_, _| b_notified.set(true))
2010                .detach()
2011        }
2012    });
2013
2014    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
2015    active_call_a
2016        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
2017        .await
2018        .unwrap();
2019    let (project_b, _) = client_b.build_local_project("/b", cx_b).await;
2020
2021    server
2022        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2023        .await;
2024    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
2025    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
2026    deterministic.run_until_parked();
2027    assert!(a_notified.take());
2028    assert_eq!(
2029        participant_locations(&room_a, cx_a),
2030        vec![("user_b".to_string(), ParticipantLocation::External)]
2031    );
2032    assert!(b_notified.take());
2033    assert_eq!(
2034        participant_locations(&room_b, cx_b),
2035        vec![("user_a".to_string(), ParticipantLocation::UnsharedProject)]
2036    );
2037
2038    let project_a_id = active_call_a
2039        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2040        .await
2041        .unwrap();
2042    deterministic.run_until_parked();
2043    assert!(a_notified.take());
2044    assert_eq!(
2045        participant_locations(&room_a, cx_a),
2046        vec![("user_b".to_string(), ParticipantLocation::External)]
2047    );
2048    assert!(b_notified.take());
2049    assert_eq!(
2050        participant_locations(&room_b, cx_b),
2051        vec![(
2052            "user_a".to_string(),
2053            ParticipantLocation::SharedProject {
2054                project_id: project_a_id
2055            }
2056        )]
2057    );
2058
2059    let project_b_id = active_call_b
2060        .update(cx_b, |call, cx| call.share_project(project_b.clone(), cx))
2061        .await
2062        .unwrap();
2063    deterministic.run_until_parked();
2064    assert!(a_notified.take());
2065    assert_eq!(
2066        participant_locations(&room_a, cx_a),
2067        vec![("user_b".to_string(), ParticipantLocation::External)]
2068    );
2069    assert!(b_notified.take());
2070    assert_eq!(
2071        participant_locations(&room_b, cx_b),
2072        vec![(
2073            "user_a".to_string(),
2074            ParticipantLocation::SharedProject {
2075                project_id: project_a_id
2076            }
2077        )]
2078    );
2079
2080    active_call_b
2081        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
2082        .await
2083        .unwrap();
2084    deterministic.run_until_parked();
2085    assert!(a_notified.take());
2086    assert_eq!(
2087        participant_locations(&room_a, cx_a),
2088        vec![(
2089            "user_b".to_string(),
2090            ParticipantLocation::SharedProject {
2091                project_id: project_b_id
2092            }
2093        )]
2094    );
2095    assert!(b_notified.take());
2096    assert_eq!(
2097        participant_locations(&room_b, cx_b),
2098        vec![(
2099            "user_a".to_string(),
2100            ParticipantLocation::SharedProject {
2101                project_id: project_a_id
2102            }
2103        )]
2104    );
2105
2106    active_call_b
2107        .update(cx_b, |call, cx| call.set_location(None, cx))
2108        .await
2109        .unwrap();
2110    deterministic.run_until_parked();
2111    assert!(a_notified.take());
2112    assert_eq!(
2113        participant_locations(&room_a, cx_a),
2114        vec![("user_b".to_string(), ParticipantLocation::External)]
2115    );
2116    assert!(b_notified.take());
2117    assert_eq!(
2118        participant_locations(&room_b, cx_b),
2119        vec![(
2120            "user_a".to_string(),
2121            ParticipantLocation::SharedProject {
2122                project_id: project_a_id
2123            }
2124        )]
2125    );
2126
2127    fn participant_locations(
2128        room: &ModelHandle<Room>,
2129        cx: &TestAppContext,
2130    ) -> Vec<(String, ParticipantLocation)> {
2131        room.read_with(cx, |room, _| {
2132            room.remote_participants()
2133                .values()
2134                .map(|participant| {
2135                    (
2136                        participant.user.github_login.to_string(),
2137                        participant.location,
2138                    )
2139                })
2140                .collect()
2141        })
2142    }
2143}
2144
2145#[gpui::test(iterations = 10)]
2146async fn test_propagate_saves_and_fs_changes(
2147    deterministic: Arc<Deterministic>,
2148    cx_a: &mut TestAppContext,
2149    cx_b: &mut TestAppContext,
2150    cx_c: &mut TestAppContext,
2151) {
2152    deterministic.forbid_parking();
2153    let mut server = TestServer::start(&deterministic).await;
2154    let client_a = server.create_client(cx_a, "user_a").await;
2155    let client_b = server.create_client(cx_b, "user_b").await;
2156    let client_c = server.create_client(cx_c, "user_c").await;
2157
2158    server
2159        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
2160        .await;
2161    let active_call_a = cx_a.read(ActiveCall::global);
2162
2163    let rust = Arc::new(Language::new(
2164        LanguageConfig {
2165            name: "Rust".into(),
2166            path_suffixes: vec!["rs".to_string()],
2167            ..Default::default()
2168        },
2169        Some(tree_sitter_rust::language()),
2170    ));
2171    let javascript = Arc::new(Language::new(
2172        LanguageConfig {
2173            name: "JavaScript".into(),
2174            path_suffixes: vec!["js".to_string()],
2175            ..Default::default()
2176        },
2177        Some(tree_sitter_rust::language()),
2178    ));
2179    for client in [&client_a, &client_b, &client_c] {
2180        client.language_registry.add(rust.clone());
2181        client.language_registry.add(javascript.clone());
2182    }
2183
2184    client_a
2185        .fs
2186        .insert_tree(
2187            "/a",
2188            json!({
2189                "file1.rs": "",
2190                "file2": ""
2191            }),
2192        )
2193        .await;
2194    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
2195    let worktree_a = project_a.read_with(cx_a, |p, cx| p.worktrees(cx).next().unwrap());
2196    let project_id = active_call_a
2197        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2198        .await
2199        .unwrap();
2200
2201    // Join that worktree as clients B and C.
2202    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2203    let project_c = client_c.build_remote_project(project_id, cx_c).await;
2204    let worktree_b = project_b.read_with(cx_b, |p, cx| p.worktrees(cx).next().unwrap());
2205    let worktree_c = project_c.read_with(cx_c, |p, cx| p.worktrees(cx).next().unwrap());
2206
2207    // Open and edit a buffer as both guests B and C.
2208    let buffer_b = project_b
2209        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "file1.rs"), cx))
2210        .await
2211        .unwrap();
2212    let buffer_c = project_c
2213        .update(cx_c, |p, cx| p.open_buffer((worktree_id, "file1.rs"), cx))
2214        .await
2215        .unwrap();
2216    buffer_b.read_with(cx_b, |buffer, _| {
2217        assert_eq!(&*buffer.language().unwrap().name(), "Rust");
2218    });
2219    buffer_c.read_with(cx_c, |buffer, _| {
2220        assert_eq!(&*buffer.language().unwrap().name(), "Rust");
2221    });
2222    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "i-am-b, ")], None, cx));
2223    buffer_c.update(cx_c, |buf, cx| buf.edit([(0..0, "i-am-c, ")], None, cx));
2224
2225    // Open and edit that buffer as the host.
2226    let buffer_a = project_a
2227        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "file1.rs"), cx))
2228        .await
2229        .unwrap();
2230
2231    deterministic.run_until_parked();
2232    buffer_a.read_with(cx_a, |buf, _| assert_eq!(buf.text(), "i-am-c, i-am-b, "));
2233    buffer_a.update(cx_a, |buf, cx| {
2234        buf.edit([(buf.len()..buf.len(), "i-am-a")], None, cx)
2235    });
2236
2237    deterministic.run_until_parked();
2238    buffer_a.read_with(cx_a, |buf, _| {
2239        assert_eq!(buf.text(), "i-am-c, i-am-b, i-am-a");
2240    });
2241    buffer_b.read_with(cx_b, |buf, _| {
2242        assert_eq!(buf.text(), "i-am-c, i-am-b, i-am-a");
2243    });
2244    buffer_c.read_with(cx_c, |buf, _| {
2245        assert_eq!(buf.text(), "i-am-c, i-am-b, i-am-a");
2246    });
2247
2248    // Edit the buffer as the host and concurrently save as guest B.
2249    let save_b = project_b.update(cx_b, |project, cx| {
2250        project.save_buffer(buffer_b.clone(), cx)
2251    });
2252    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "hi-a, ")], None, cx));
2253    save_b.await.unwrap();
2254    assert_eq!(
2255        client_a.fs.load("/a/file1.rs".as_ref()).await.unwrap(),
2256        "hi-a, i-am-c, i-am-b, i-am-a"
2257    );
2258
2259    deterministic.run_until_parked();
2260    buffer_a.read_with(cx_a, |buf, _| assert!(!buf.is_dirty()));
2261    buffer_b.read_with(cx_b, |buf, _| assert!(!buf.is_dirty()));
2262    buffer_c.read_with(cx_c, |buf, _| assert!(!buf.is_dirty()));
2263
2264    // Make changes on host's file system, see those changes on guest worktrees.
2265    client_a
2266        .fs
2267        .rename(
2268            "/a/file1.rs".as_ref(),
2269            "/a/file1.js".as_ref(),
2270            Default::default(),
2271        )
2272        .await
2273        .unwrap();
2274    client_a
2275        .fs
2276        .rename("/a/file2".as_ref(), "/a/file3".as_ref(), Default::default())
2277        .await
2278        .unwrap();
2279    client_a.fs.insert_file("/a/file4", "4".into()).await;
2280    deterministic.run_until_parked();
2281
2282    worktree_a.read_with(cx_a, |tree, _| {
2283        assert_eq!(
2284            tree.paths()
2285                .map(|p| p.to_string_lossy())
2286                .collect::<Vec<_>>(),
2287            ["file1.js", "file3", "file4"]
2288        )
2289    });
2290    worktree_b.read_with(cx_b, |tree, _| {
2291        assert_eq!(
2292            tree.paths()
2293                .map(|p| p.to_string_lossy())
2294                .collect::<Vec<_>>(),
2295            ["file1.js", "file3", "file4"]
2296        )
2297    });
2298    worktree_c.read_with(cx_c, |tree, _| {
2299        assert_eq!(
2300            tree.paths()
2301                .map(|p| p.to_string_lossy())
2302                .collect::<Vec<_>>(),
2303            ["file1.js", "file3", "file4"]
2304        )
2305    });
2306
2307    // Ensure buffer files are updated as well.
2308    buffer_a.read_with(cx_a, |buffer, _| {
2309        assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
2310        assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
2311    });
2312    buffer_b.read_with(cx_b, |buffer, _| {
2313        assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
2314        assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
2315    });
2316    buffer_c.read_with(cx_c, |buffer, _| {
2317        assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
2318        assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
2319    });
2320
2321    let new_buffer_a = project_a
2322        .update(cx_a, |p, cx| p.create_buffer("", None, cx))
2323        .unwrap();
2324    let new_buffer_id = new_buffer_a.read_with(cx_a, |buffer, _| buffer.remote_id());
2325    let new_buffer_b = project_b
2326        .update(cx_b, |p, cx| p.open_buffer_by_id(new_buffer_id, cx))
2327        .await
2328        .unwrap();
2329    new_buffer_b.read_with(cx_b, |buffer, _| {
2330        assert!(buffer.file().is_none());
2331    });
2332
2333    new_buffer_a.update(cx_a, |buffer, cx| {
2334        buffer.edit([(0..0, "ok")], None, cx);
2335    });
2336    project_a
2337        .update(cx_a, |project, cx| {
2338            project.save_buffer_as(new_buffer_a.clone(), "/a/file3.rs".into(), cx)
2339        })
2340        .await
2341        .unwrap();
2342
2343    deterministic.run_until_parked();
2344    new_buffer_b.read_with(cx_b, |buffer_b, _| {
2345        assert_eq!(
2346            buffer_b.file().unwrap().path().as_ref(),
2347            Path::new("file3.rs")
2348        );
2349
2350        new_buffer_a.read_with(cx_a, |buffer_a, _| {
2351            assert_eq!(buffer_b.saved_mtime(), buffer_a.saved_mtime());
2352            assert_eq!(buffer_b.saved_version(), buffer_a.saved_version());
2353        });
2354    });
2355}
2356
2357#[gpui::test(iterations = 10)]
2358async fn test_git_diff_base_change(
2359    deterministic: Arc<Deterministic>,
2360    cx_a: &mut TestAppContext,
2361    cx_b: &mut TestAppContext,
2362) {
2363    deterministic.forbid_parking();
2364    let mut server = TestServer::start(&deterministic).await;
2365    let client_a = server.create_client(cx_a, "user_a").await;
2366    let client_b = server.create_client(cx_b, "user_b").await;
2367    server
2368        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2369        .await;
2370    let active_call_a = cx_a.read(ActiveCall::global);
2371
2372    client_a
2373        .fs
2374        .insert_tree(
2375            "/dir",
2376            json!({
2377            ".git": {},
2378            "sub": {
2379                ".git": {},
2380                "b.txt": "
2381                    one
2382                    two
2383                    three
2384                ".unindent(),
2385            },
2386            "a.txt": "
2387                    one
2388                    two
2389                    three
2390                ".unindent(),
2391            }),
2392        )
2393        .await;
2394
2395    let (project_local, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2396    let project_id = active_call_a
2397        .update(cx_a, |call, cx| {
2398            call.share_project(project_local.clone(), cx)
2399        })
2400        .await
2401        .unwrap();
2402
2403    let project_remote = client_b.build_remote_project(project_id, cx_b).await;
2404
2405    let diff_base = "
2406        one
2407        three
2408    "
2409    .unindent();
2410
2411    let new_diff_base = "
2412        one
2413        two
2414    "
2415    .unindent();
2416
2417    client_a
2418        .fs
2419        .as_fake()
2420        .set_index_for_repo(
2421            Path::new("/dir/.git"),
2422            &[(Path::new("a.txt"), diff_base.clone())],
2423        )
2424        .await;
2425
2426    // Create the buffer
2427    let buffer_local_a = project_local
2428        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2429        .await
2430        .unwrap();
2431
2432    // Wait for it to catch up to the new diff
2433    deterministic.run_until_parked();
2434
2435    // Smoke test diffing
2436    buffer_local_a.read_with(cx_a, |buffer, _| {
2437        assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
2438        git::diff::assert_hunks(
2439            buffer.snapshot().git_diff_hunks_in_row_range(0..4, false),
2440            &buffer,
2441            &diff_base,
2442            &[(1..2, "", "two\n")],
2443        );
2444    });
2445
2446    // Create remote buffer
2447    let buffer_remote_a = project_remote
2448        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2449        .await
2450        .unwrap();
2451
2452    // Wait remote buffer to catch up to the new diff
2453    deterministic.run_until_parked();
2454
2455    // Smoke test diffing
2456    buffer_remote_a.read_with(cx_b, |buffer, _| {
2457        assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
2458        git::diff::assert_hunks(
2459            buffer.snapshot().git_diff_hunks_in_row_range(0..4, false),
2460            &buffer,
2461            &diff_base,
2462            &[(1..2, "", "two\n")],
2463        );
2464    });
2465
2466    client_a
2467        .fs
2468        .as_fake()
2469        .set_index_for_repo(
2470            Path::new("/dir/.git"),
2471            &[(Path::new("a.txt"), new_diff_base.clone())],
2472        )
2473        .await;
2474
2475    // Wait for buffer_local_a to receive it
2476    deterministic.run_until_parked();
2477
2478    // Smoke test new diffing
2479    buffer_local_a.read_with(cx_a, |buffer, _| {
2480        assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
2481
2482        git::diff::assert_hunks(
2483            buffer.snapshot().git_diff_hunks_in_row_range(0..4, false),
2484            &buffer,
2485            &diff_base,
2486            &[(2..3, "", "three\n")],
2487        );
2488    });
2489
2490    // Smoke test B
2491    buffer_remote_a.read_with(cx_b, |buffer, _| {
2492        assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
2493        git::diff::assert_hunks(
2494            buffer.snapshot().git_diff_hunks_in_row_range(0..4, false),
2495            &buffer,
2496            &diff_base,
2497            &[(2..3, "", "three\n")],
2498        );
2499    });
2500
2501    //Nested git dir
2502
2503    let diff_base = "
2504        one
2505        three
2506    "
2507    .unindent();
2508
2509    let new_diff_base = "
2510        one
2511        two
2512    "
2513    .unindent();
2514
2515    client_a
2516        .fs
2517        .as_fake()
2518        .set_index_for_repo(
2519            Path::new("/dir/sub/.git"),
2520            &[(Path::new("b.txt"), diff_base.clone())],
2521        )
2522        .await;
2523
2524    // Create the buffer
2525    let buffer_local_b = project_local
2526        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "sub/b.txt"), cx))
2527        .await
2528        .unwrap();
2529
2530    // Wait for it to catch up to the new diff
2531    deterministic.run_until_parked();
2532
2533    // Smoke test diffing
2534    buffer_local_b.read_with(cx_a, |buffer, _| {
2535        assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
2536        git::diff::assert_hunks(
2537            buffer.snapshot().git_diff_hunks_in_row_range(0..4, false),
2538            &buffer,
2539            &diff_base,
2540            &[(1..2, "", "two\n")],
2541        );
2542    });
2543
2544    // Create remote buffer
2545    let buffer_remote_b = project_remote
2546        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "sub/b.txt"), cx))
2547        .await
2548        .unwrap();
2549
2550    // Wait remote buffer to catch up to the new diff
2551    deterministic.run_until_parked();
2552
2553    // Smoke test diffing
2554    buffer_remote_b.read_with(cx_b, |buffer, _| {
2555        assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
2556        git::diff::assert_hunks(
2557            buffer.snapshot().git_diff_hunks_in_row_range(0..4, false),
2558            &buffer,
2559            &diff_base,
2560            &[(1..2, "", "two\n")],
2561        );
2562    });
2563
2564    client_a
2565        .fs
2566        .as_fake()
2567        .set_index_for_repo(
2568            Path::new("/dir/sub/.git"),
2569            &[(Path::new("b.txt"), new_diff_base.clone())],
2570        )
2571        .await;
2572
2573    // Wait for buffer_local_b to receive it
2574    deterministic.run_until_parked();
2575
2576    // Smoke test new diffing
2577    buffer_local_b.read_with(cx_a, |buffer, _| {
2578        assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
2579        println!("{:?}", buffer.as_rope().to_string());
2580        println!("{:?}", buffer.diff_base());
2581        println!(
2582            "{:?}",
2583            buffer
2584                .snapshot()
2585                .git_diff_hunks_in_row_range(0..4, false)
2586                .collect::<Vec<_>>()
2587        );
2588
2589        git::diff::assert_hunks(
2590            buffer.snapshot().git_diff_hunks_in_row_range(0..4, false),
2591            &buffer,
2592            &diff_base,
2593            &[(2..3, "", "three\n")],
2594        );
2595    });
2596
2597    // Smoke test B
2598    buffer_remote_b.read_with(cx_b, |buffer, _| {
2599        assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
2600        git::diff::assert_hunks(
2601            buffer.snapshot().git_diff_hunks_in_row_range(0..4, false),
2602            &buffer,
2603            &diff_base,
2604            &[(2..3, "", "three\n")],
2605        );
2606    });
2607}
2608
2609#[gpui::test(iterations = 10)]
2610async fn test_fs_operations(
2611    deterministic: Arc<Deterministic>,
2612    cx_a: &mut TestAppContext,
2613    cx_b: &mut TestAppContext,
2614) {
2615    deterministic.forbid_parking();
2616    let mut server = TestServer::start(&deterministic).await;
2617    let client_a = server.create_client(cx_a, "user_a").await;
2618    let client_b = server.create_client(cx_b, "user_b").await;
2619    server
2620        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2621        .await;
2622    let active_call_a = cx_a.read(ActiveCall::global);
2623
2624    client_a
2625        .fs
2626        .insert_tree(
2627            "/dir",
2628            json!({
2629                "a.txt": "a-contents",
2630                "b.txt": "b-contents",
2631            }),
2632        )
2633        .await;
2634    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2635    let project_id = active_call_a
2636        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2637        .await
2638        .unwrap();
2639    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2640
2641    let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
2642    let worktree_b = project_b.read_with(cx_b, |project, cx| project.worktrees(cx).next().unwrap());
2643
2644    let entry = project_b
2645        .update(cx_b, |project, cx| {
2646            project
2647                .create_entry((worktree_id, "c.txt"), false, cx)
2648                .unwrap()
2649        })
2650        .await
2651        .unwrap();
2652    worktree_a.read_with(cx_a, |worktree, _| {
2653        assert_eq!(
2654            worktree
2655                .paths()
2656                .map(|p| p.to_string_lossy())
2657                .collect::<Vec<_>>(),
2658            ["a.txt", "b.txt", "c.txt"]
2659        );
2660    });
2661    worktree_b.read_with(cx_b, |worktree, _| {
2662        assert_eq!(
2663            worktree
2664                .paths()
2665                .map(|p| p.to_string_lossy())
2666                .collect::<Vec<_>>(),
2667            ["a.txt", "b.txt", "c.txt"]
2668        );
2669    });
2670
2671    project_b
2672        .update(cx_b, |project, cx| {
2673            project.rename_entry(entry.id, Path::new("d.txt"), cx)
2674        })
2675        .unwrap()
2676        .await
2677        .unwrap();
2678    worktree_a.read_with(cx_a, |worktree, _| {
2679        assert_eq!(
2680            worktree
2681                .paths()
2682                .map(|p| p.to_string_lossy())
2683                .collect::<Vec<_>>(),
2684            ["a.txt", "b.txt", "d.txt"]
2685        );
2686    });
2687    worktree_b.read_with(cx_b, |worktree, _| {
2688        assert_eq!(
2689            worktree
2690                .paths()
2691                .map(|p| p.to_string_lossy())
2692                .collect::<Vec<_>>(),
2693            ["a.txt", "b.txt", "d.txt"]
2694        );
2695    });
2696
2697    let dir_entry = project_b
2698        .update(cx_b, |project, cx| {
2699            project
2700                .create_entry((worktree_id, "DIR"), true, cx)
2701                .unwrap()
2702        })
2703        .await
2704        .unwrap();
2705    worktree_a.read_with(cx_a, |worktree, _| {
2706        assert_eq!(
2707            worktree
2708                .paths()
2709                .map(|p| p.to_string_lossy())
2710                .collect::<Vec<_>>(),
2711            ["DIR", "a.txt", "b.txt", "d.txt"]
2712        );
2713    });
2714    worktree_b.read_with(cx_b, |worktree, _| {
2715        assert_eq!(
2716            worktree
2717                .paths()
2718                .map(|p| p.to_string_lossy())
2719                .collect::<Vec<_>>(),
2720            ["DIR", "a.txt", "b.txt", "d.txt"]
2721        );
2722    });
2723
2724    project_b
2725        .update(cx_b, |project, cx| {
2726            project
2727                .create_entry((worktree_id, "DIR/e.txt"), false, cx)
2728                .unwrap()
2729        })
2730        .await
2731        .unwrap();
2732    project_b
2733        .update(cx_b, |project, cx| {
2734            project
2735                .create_entry((worktree_id, "DIR/SUBDIR"), true, cx)
2736                .unwrap()
2737        })
2738        .await
2739        .unwrap();
2740    project_b
2741        .update(cx_b, |project, cx| {
2742            project
2743                .create_entry((worktree_id, "DIR/SUBDIR/f.txt"), false, cx)
2744                .unwrap()
2745        })
2746        .await
2747        .unwrap();
2748    worktree_a.read_with(cx_a, |worktree, _| {
2749        assert_eq!(
2750            worktree
2751                .paths()
2752                .map(|p| p.to_string_lossy())
2753                .collect::<Vec<_>>(),
2754            [
2755                "DIR",
2756                "DIR/SUBDIR",
2757                "DIR/SUBDIR/f.txt",
2758                "DIR/e.txt",
2759                "a.txt",
2760                "b.txt",
2761                "d.txt"
2762            ]
2763        );
2764    });
2765    worktree_b.read_with(cx_b, |worktree, _| {
2766        assert_eq!(
2767            worktree
2768                .paths()
2769                .map(|p| p.to_string_lossy())
2770                .collect::<Vec<_>>(),
2771            [
2772                "DIR",
2773                "DIR/SUBDIR",
2774                "DIR/SUBDIR/f.txt",
2775                "DIR/e.txt",
2776                "a.txt",
2777                "b.txt",
2778                "d.txt"
2779            ]
2780        );
2781    });
2782
2783    project_b
2784        .update(cx_b, |project, cx| {
2785            project
2786                .copy_entry(entry.id, Path::new("f.txt"), cx)
2787                .unwrap()
2788        })
2789        .await
2790        .unwrap();
2791    worktree_a.read_with(cx_a, |worktree, _| {
2792        assert_eq!(
2793            worktree
2794                .paths()
2795                .map(|p| p.to_string_lossy())
2796                .collect::<Vec<_>>(),
2797            [
2798                "DIR",
2799                "DIR/SUBDIR",
2800                "DIR/SUBDIR/f.txt",
2801                "DIR/e.txt",
2802                "a.txt",
2803                "b.txt",
2804                "d.txt",
2805                "f.txt"
2806            ]
2807        );
2808    });
2809    worktree_b.read_with(cx_b, |worktree, _| {
2810        assert_eq!(
2811            worktree
2812                .paths()
2813                .map(|p| p.to_string_lossy())
2814                .collect::<Vec<_>>(),
2815            [
2816                "DIR",
2817                "DIR/SUBDIR",
2818                "DIR/SUBDIR/f.txt",
2819                "DIR/e.txt",
2820                "a.txt",
2821                "b.txt",
2822                "d.txt",
2823                "f.txt"
2824            ]
2825        );
2826    });
2827
2828    project_b
2829        .update(cx_b, |project, cx| {
2830            project.delete_entry(dir_entry.id, cx).unwrap()
2831        })
2832        .await
2833        .unwrap();
2834    deterministic.run_until_parked();
2835
2836    worktree_a.read_with(cx_a, |worktree, _| {
2837        assert_eq!(
2838            worktree
2839                .paths()
2840                .map(|p| p.to_string_lossy())
2841                .collect::<Vec<_>>(),
2842            ["a.txt", "b.txt", "d.txt", "f.txt"]
2843        );
2844    });
2845    worktree_b.read_with(cx_b, |worktree, _| {
2846        assert_eq!(
2847            worktree
2848                .paths()
2849                .map(|p| p.to_string_lossy())
2850                .collect::<Vec<_>>(),
2851            ["a.txt", "b.txt", "d.txt", "f.txt"]
2852        );
2853    });
2854
2855    project_b
2856        .update(cx_b, |project, cx| {
2857            project.delete_entry(entry.id, cx).unwrap()
2858        })
2859        .await
2860        .unwrap();
2861    worktree_a.read_with(cx_a, |worktree, _| {
2862        assert_eq!(
2863            worktree
2864                .paths()
2865                .map(|p| p.to_string_lossy())
2866                .collect::<Vec<_>>(),
2867            ["a.txt", "b.txt", "f.txt"]
2868        );
2869    });
2870    worktree_b.read_with(cx_b, |worktree, _| {
2871        assert_eq!(
2872            worktree
2873                .paths()
2874                .map(|p| p.to_string_lossy())
2875                .collect::<Vec<_>>(),
2876            ["a.txt", "b.txt", "f.txt"]
2877        );
2878    });
2879}
2880
2881#[gpui::test(iterations = 10)]
2882async fn test_buffer_conflict_after_save(
2883    deterministic: Arc<Deterministic>,
2884    cx_a: &mut TestAppContext,
2885    cx_b: &mut TestAppContext,
2886) {
2887    deterministic.forbid_parking();
2888    let mut server = TestServer::start(&deterministic).await;
2889    let client_a = server.create_client(cx_a, "user_a").await;
2890    let client_b = server.create_client(cx_b, "user_b").await;
2891    server
2892        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2893        .await;
2894    let active_call_a = cx_a.read(ActiveCall::global);
2895
2896    client_a
2897        .fs
2898        .insert_tree(
2899            "/dir",
2900            json!({
2901                "a.txt": "a-contents",
2902            }),
2903        )
2904        .await;
2905    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2906    let project_id = active_call_a
2907        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2908        .await
2909        .unwrap();
2910    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2911
2912    // Open a buffer as client B
2913    let buffer_b = project_b
2914        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2915        .await
2916        .unwrap();
2917
2918    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "world ")], None, cx));
2919    buffer_b.read_with(cx_b, |buf, _| {
2920        assert!(buf.is_dirty());
2921        assert!(!buf.has_conflict());
2922    });
2923
2924    project_b
2925        .update(cx_b, |project, cx| {
2926            project.save_buffer(buffer_b.clone(), cx)
2927        })
2928        .await
2929        .unwrap();
2930    cx_a.foreground().forbid_parking();
2931    buffer_b.read_with(cx_b, |buffer_b, _| assert!(!buffer_b.is_dirty()));
2932    buffer_b.read_with(cx_b, |buf, _| {
2933        assert!(!buf.has_conflict());
2934    });
2935
2936    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "hello ")], None, cx));
2937    buffer_b.read_with(cx_b, |buf, _| {
2938        assert!(buf.is_dirty());
2939        assert!(!buf.has_conflict());
2940    });
2941}
2942
2943#[gpui::test(iterations = 10)]
2944async fn test_buffer_reloading(
2945    deterministic: Arc<Deterministic>,
2946    cx_a: &mut TestAppContext,
2947    cx_b: &mut TestAppContext,
2948) {
2949    deterministic.forbid_parking();
2950    let mut server = TestServer::start(&deterministic).await;
2951    let client_a = server.create_client(cx_a, "user_a").await;
2952    let client_b = server.create_client(cx_b, "user_b").await;
2953    server
2954        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2955        .await;
2956    let active_call_a = cx_a.read(ActiveCall::global);
2957
2958    client_a
2959        .fs
2960        .insert_tree(
2961            "/dir",
2962            json!({
2963                "a.txt": "a\nb\nc",
2964            }),
2965        )
2966        .await;
2967    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2968    let project_id = active_call_a
2969        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2970        .await
2971        .unwrap();
2972    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2973
2974    // Open a buffer as client B
2975    let buffer_b = project_b
2976        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2977        .await
2978        .unwrap();
2979    buffer_b.read_with(cx_b, |buf, _| {
2980        assert!(!buf.is_dirty());
2981        assert!(!buf.has_conflict());
2982        assert_eq!(buf.line_ending(), LineEnding::Unix);
2983    });
2984
2985    let new_contents = Rope::from("d\ne\nf");
2986    client_a
2987        .fs
2988        .save("/dir/a.txt".as_ref(), &new_contents, LineEnding::Windows)
2989        .await
2990        .unwrap();
2991    cx_a.foreground().run_until_parked();
2992    buffer_b.read_with(cx_b, |buf, _| {
2993        assert_eq!(buf.text(), new_contents.to_string());
2994        assert!(!buf.is_dirty());
2995        assert!(!buf.has_conflict());
2996        assert_eq!(buf.line_ending(), LineEnding::Windows);
2997    });
2998}
2999
3000#[gpui::test(iterations = 10)]
3001async fn test_editing_while_guest_opens_buffer(
3002    deterministic: Arc<Deterministic>,
3003    cx_a: &mut TestAppContext,
3004    cx_b: &mut TestAppContext,
3005) {
3006    deterministic.forbid_parking();
3007    let mut server = TestServer::start(&deterministic).await;
3008    let client_a = server.create_client(cx_a, "user_a").await;
3009    let client_b = server.create_client(cx_b, "user_b").await;
3010    server
3011        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3012        .await;
3013    let active_call_a = cx_a.read(ActiveCall::global);
3014
3015    client_a
3016        .fs
3017        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3018        .await;
3019    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3020    let project_id = active_call_a
3021        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3022        .await
3023        .unwrap();
3024    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3025
3026    // Open a buffer as client A
3027    let buffer_a = project_a
3028        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3029        .await
3030        .unwrap();
3031
3032    // Start opening the same buffer as client B
3033    let buffer_b = cx_b
3034        .background()
3035        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)));
3036
3037    // Edit the buffer as client A while client B is still opening it.
3038    cx_b.background().simulate_random_delay().await;
3039    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "X")], None, cx));
3040    cx_b.background().simulate_random_delay().await;
3041    buffer_a.update(cx_a, |buf, cx| buf.edit([(1..1, "Y")], None, cx));
3042
3043    let text = buffer_a.read_with(cx_a, |buf, _| buf.text());
3044    let buffer_b = buffer_b.await.unwrap();
3045    cx_a.foreground().run_until_parked();
3046    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), text));
3047}
3048
3049#[gpui::test]
3050async fn test_newline_above_or_below_does_not_move_guest_cursor(
3051    deterministic: Arc<Deterministic>,
3052    cx_a: &mut TestAppContext,
3053    cx_b: &mut TestAppContext,
3054) {
3055    deterministic.forbid_parking();
3056    let mut server = TestServer::start(&deterministic).await;
3057    let client_a = server.create_client(cx_a, "user_a").await;
3058    let client_b = server.create_client(cx_b, "user_b").await;
3059    server
3060        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3061        .await;
3062    let active_call_a = cx_a.read(ActiveCall::global);
3063
3064    client_a
3065        .fs
3066        .insert_tree("/dir", json!({ "a.txt": "Some text\n" }))
3067        .await;
3068    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3069    let project_id = active_call_a
3070        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3071        .await
3072        .unwrap();
3073
3074    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3075
3076    // Open a buffer as client A
3077    let buffer_a = project_a
3078        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3079        .await
3080        .unwrap();
3081    let (_, window_a) = cx_a.add_window(|_| EmptyView);
3082    let editor_a = cx_a.add_view(&window_a, |cx| {
3083        Editor::for_buffer(buffer_a, Some(project_a), cx)
3084    });
3085    let mut editor_cx_a = EditorTestContext {
3086        cx: cx_a,
3087        window_id: window_a.id(),
3088        editor: editor_a,
3089    };
3090
3091    // Open a buffer as client B
3092    let buffer_b = project_b
3093        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3094        .await
3095        .unwrap();
3096    let (_, window_b) = cx_b.add_window(|_| EmptyView);
3097    let editor_b = cx_b.add_view(&window_b, |cx| {
3098        Editor::for_buffer(buffer_b, Some(project_b), cx)
3099    });
3100    let mut editor_cx_b = EditorTestContext {
3101        cx: cx_b,
3102        window_id: window_b.id(),
3103        editor: editor_b,
3104    };
3105
3106    // Test newline above
3107    editor_cx_a.set_selections_state(indoc! {"
3108        Some textˇ
3109    "});
3110    editor_cx_b.set_selections_state(indoc! {"
3111        Some textˇ
3112    "});
3113    editor_cx_a.update_editor(|editor, cx| editor.newline_above(&editor::NewlineAbove, cx));
3114    deterministic.run_until_parked();
3115    editor_cx_a.assert_editor_state(indoc! {"
3116        ˇ
3117        Some text
3118    "});
3119    editor_cx_b.assert_editor_state(indoc! {"
3120
3121        Some textˇ
3122    "});
3123
3124    // Test newline below
3125    editor_cx_a.set_selections_state(indoc! {"
3126
3127        Some textˇ
3128    "});
3129    editor_cx_b.set_selections_state(indoc! {"
3130
3131        Some textˇ
3132    "});
3133    editor_cx_a.update_editor(|editor, cx| editor.newline_below(&editor::NewlineBelow, cx));
3134    deterministic.run_until_parked();
3135    editor_cx_a.assert_editor_state(indoc! {"
3136
3137        Some text
3138        ˇ
3139    "});
3140    editor_cx_b.assert_editor_state(indoc! {"
3141
3142        Some textˇ
3143
3144    "});
3145}
3146
3147#[gpui::test(iterations = 10)]
3148async fn test_leaving_worktree_while_opening_buffer(
3149    deterministic: Arc<Deterministic>,
3150    cx_a: &mut TestAppContext,
3151    cx_b: &mut TestAppContext,
3152) {
3153    deterministic.forbid_parking();
3154    let mut server = TestServer::start(&deterministic).await;
3155    let client_a = server.create_client(cx_a, "user_a").await;
3156    let client_b = server.create_client(cx_b, "user_b").await;
3157    server
3158        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3159        .await;
3160    let active_call_a = cx_a.read(ActiveCall::global);
3161
3162    client_a
3163        .fs
3164        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3165        .await;
3166    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3167    let project_id = active_call_a
3168        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3169        .await
3170        .unwrap();
3171    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3172
3173    // See that a guest has joined as client A.
3174    cx_a.foreground().run_until_parked();
3175    project_a.read_with(cx_a, |p, _| assert_eq!(p.collaborators().len(), 1));
3176
3177    // Begin opening a buffer as client B, but leave the project before the open completes.
3178    let buffer_b = cx_b
3179        .background()
3180        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)));
3181    cx_b.update(|_| drop(project_b));
3182    drop(buffer_b);
3183
3184    // See that the guest has left.
3185    cx_a.foreground().run_until_parked();
3186    project_a.read_with(cx_a, |p, _| assert!(p.collaborators().is_empty()));
3187}
3188
3189#[gpui::test(iterations = 10)]
3190async fn test_canceling_buffer_opening(
3191    deterministic: Arc<Deterministic>,
3192    cx_a: &mut TestAppContext,
3193    cx_b: &mut TestAppContext,
3194) {
3195    deterministic.forbid_parking();
3196
3197    let mut server = TestServer::start(&deterministic).await;
3198    let client_a = server.create_client(cx_a, "user_a").await;
3199    let client_b = server.create_client(cx_b, "user_b").await;
3200    server
3201        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3202        .await;
3203    let active_call_a = cx_a.read(ActiveCall::global);
3204
3205    client_a
3206        .fs
3207        .insert_tree(
3208            "/dir",
3209            json!({
3210                "a.txt": "abc",
3211            }),
3212        )
3213        .await;
3214    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3215    let project_id = active_call_a
3216        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3217        .await
3218        .unwrap();
3219    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3220
3221    let buffer_a = project_a
3222        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3223        .await
3224        .unwrap();
3225
3226    // Open a buffer as client B but cancel after a random amount of time.
3227    let buffer_b = project_b.update(cx_b, |p, cx| p.open_buffer_by_id(buffer_a.id() as u64, cx));
3228    deterministic.simulate_random_delay().await;
3229    drop(buffer_b);
3230
3231    // Try opening the same buffer again as client B, and ensure we can
3232    // still do it despite the cancellation above.
3233    let buffer_b = project_b
3234        .update(cx_b, |p, cx| p.open_buffer_by_id(buffer_a.id() as u64, cx))
3235        .await
3236        .unwrap();
3237    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "abc"));
3238}
3239
3240#[gpui::test(iterations = 10)]
3241async fn test_leaving_project(
3242    deterministic: Arc<Deterministic>,
3243    cx_a: &mut TestAppContext,
3244    cx_b: &mut TestAppContext,
3245    cx_c: &mut TestAppContext,
3246) {
3247    deterministic.forbid_parking();
3248    let mut server = TestServer::start(&deterministic).await;
3249    let client_a = server.create_client(cx_a, "user_a").await;
3250    let client_b = server.create_client(cx_b, "user_b").await;
3251    let client_c = server.create_client(cx_c, "user_c").await;
3252    server
3253        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3254        .await;
3255    let active_call_a = cx_a.read(ActiveCall::global);
3256
3257    client_a
3258        .fs
3259        .insert_tree(
3260            "/a",
3261            json!({
3262                "a.txt": "a-contents",
3263                "b.txt": "b-contents",
3264            }),
3265        )
3266        .await;
3267    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
3268    let project_id = active_call_a
3269        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3270        .await
3271        .unwrap();
3272    let project_b1 = client_b.build_remote_project(project_id, cx_b).await;
3273    let project_c = client_c.build_remote_project(project_id, cx_c).await;
3274
3275    // Client A sees that a guest has joined.
3276    deterministic.run_until_parked();
3277    project_a.read_with(cx_a, |project, _| {
3278        assert_eq!(project.collaborators().len(), 2);
3279    });
3280    project_b1.read_with(cx_b, |project, _| {
3281        assert_eq!(project.collaborators().len(), 2);
3282    });
3283    project_c.read_with(cx_c, |project, _| {
3284        assert_eq!(project.collaborators().len(), 2);
3285    });
3286
3287    // Client B opens a buffer.
3288    let buffer_b1 = project_b1
3289        .update(cx_b, |project, cx| {
3290            let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
3291            project.open_buffer((worktree_id, "a.txt"), cx)
3292        })
3293        .await
3294        .unwrap();
3295    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3296
3297    // Drop client B's project and ensure client A and client C observe client B leaving.
3298    cx_b.update(|_| drop(project_b1));
3299    deterministic.run_until_parked();
3300    project_a.read_with(cx_a, |project, _| {
3301        assert_eq!(project.collaborators().len(), 1);
3302    });
3303    project_c.read_with(cx_c, |project, _| {
3304        assert_eq!(project.collaborators().len(), 1);
3305    });
3306
3307    // Client B re-joins the project and can open buffers as before.
3308    let project_b2 = client_b.build_remote_project(project_id, cx_b).await;
3309    deterministic.run_until_parked();
3310    project_a.read_with(cx_a, |project, _| {
3311        assert_eq!(project.collaborators().len(), 2);
3312    });
3313    project_b2.read_with(cx_b, |project, _| {
3314        assert_eq!(project.collaborators().len(), 2);
3315    });
3316    project_c.read_with(cx_c, |project, _| {
3317        assert_eq!(project.collaborators().len(), 2);
3318    });
3319
3320    let buffer_b2 = project_b2
3321        .update(cx_b, |project, cx| {
3322            let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
3323            project.open_buffer((worktree_id, "a.txt"), cx)
3324        })
3325        .await
3326        .unwrap();
3327    buffer_b2.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3328
3329    // Drop client B's connection and ensure client A and client C observe client B leaving.
3330    client_b.disconnect(&cx_b.to_async());
3331    deterministic.advance_clock(RECONNECT_TIMEOUT);
3332    project_a.read_with(cx_a, |project, _| {
3333        assert_eq!(project.collaborators().len(), 1);
3334    });
3335    project_b2.read_with(cx_b, |project, _| {
3336        assert!(project.is_read_only());
3337    });
3338    project_c.read_with(cx_c, |project, _| {
3339        assert_eq!(project.collaborators().len(), 1);
3340    });
3341
3342    // Client B can't join the project, unless they re-join the room.
3343    cx_b.spawn(|cx| {
3344        Project::remote(
3345            project_id,
3346            client_b.client.clone(),
3347            client_b.user_store.clone(),
3348            client_b.language_registry.clone(),
3349            FakeFs::new(cx.background()),
3350            cx,
3351        )
3352    })
3353    .await
3354    .unwrap_err();
3355
3356    // Simulate connection loss for client C and ensure client A observes client C leaving the project.
3357    client_c.wait_for_current_user(cx_c).await;
3358    server.forbid_connections();
3359    server.disconnect_client(client_c.peer_id().unwrap());
3360    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
3361    deterministic.run_until_parked();
3362    project_a.read_with(cx_a, |project, _| {
3363        assert_eq!(project.collaborators().len(), 0);
3364    });
3365    project_b2.read_with(cx_b, |project, _| {
3366        assert!(project.is_read_only());
3367    });
3368    project_c.read_with(cx_c, |project, _| {
3369        assert!(project.is_read_only());
3370    });
3371}
3372
3373#[gpui::test(iterations = 10)]
3374async fn test_collaborating_with_diagnostics(
3375    deterministic: Arc<Deterministic>,
3376    cx_a: &mut TestAppContext,
3377    cx_b: &mut TestAppContext,
3378    cx_c: &mut TestAppContext,
3379) {
3380    deterministic.forbid_parking();
3381    let mut server = TestServer::start(&deterministic).await;
3382    let client_a = server.create_client(cx_a, "user_a").await;
3383    let client_b = server.create_client(cx_b, "user_b").await;
3384    let client_c = server.create_client(cx_c, "user_c").await;
3385    server
3386        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3387        .await;
3388    let active_call_a = cx_a.read(ActiveCall::global);
3389
3390    // Set up a fake language server.
3391    let mut language = Language::new(
3392        LanguageConfig {
3393            name: "Rust".into(),
3394            path_suffixes: vec!["rs".to_string()],
3395            ..Default::default()
3396        },
3397        Some(tree_sitter_rust::language()),
3398    );
3399    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
3400    client_a.language_registry.add(Arc::new(language));
3401
3402    // Share a project as client A
3403    client_a
3404        .fs
3405        .insert_tree(
3406            "/a",
3407            json!({
3408                "a.rs": "let one = two",
3409                "other.rs": "",
3410            }),
3411        )
3412        .await;
3413    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3414
3415    // Cause the language server to start.
3416    let _buffer = project_a
3417        .update(cx_a, |project, cx| {
3418            project.open_buffer(
3419                ProjectPath {
3420                    worktree_id,
3421                    path: Path::new("other.rs").into(),
3422                },
3423                cx,
3424            )
3425        })
3426        .await
3427        .unwrap();
3428
3429    // Simulate a language server reporting errors for a file.
3430    let mut fake_language_server = fake_language_servers.next().await.unwrap();
3431    fake_language_server
3432        .receive_notification::<lsp::notification::DidOpenTextDocument>()
3433        .await;
3434    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3435        lsp::PublishDiagnosticsParams {
3436            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3437            version: None,
3438            diagnostics: vec![lsp::Diagnostic {
3439                severity: Some(lsp::DiagnosticSeverity::WARNING),
3440                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3441                message: "message 0".to_string(),
3442                ..Default::default()
3443            }],
3444        },
3445    );
3446
3447    // Client A shares the project and, simultaneously, the language server
3448    // publishes a diagnostic. This is done to ensure that the server always
3449    // observes the latest diagnostics for a worktree.
3450    let project_id = active_call_a
3451        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3452        .await
3453        .unwrap();
3454    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3455        lsp::PublishDiagnosticsParams {
3456            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3457            version: None,
3458            diagnostics: vec![lsp::Diagnostic {
3459                severity: Some(lsp::DiagnosticSeverity::ERROR),
3460                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3461                message: "message 1".to_string(),
3462                ..Default::default()
3463            }],
3464        },
3465    );
3466
3467    // Join the worktree as client B.
3468    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3469
3470    // Wait for server to see the diagnostics update.
3471    deterministic.run_until_parked();
3472
3473    // Ensure client B observes the new diagnostics.
3474    project_b.read_with(cx_b, |project, cx| {
3475        assert_eq!(
3476            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
3477            &[(
3478                ProjectPath {
3479                    worktree_id,
3480                    path: Arc::from(Path::new("a.rs")),
3481                },
3482                LanguageServerId(0),
3483                DiagnosticSummary {
3484                    error_count: 1,
3485                    warning_count: 0,
3486                    ..Default::default()
3487                },
3488            )]
3489        )
3490    });
3491
3492    // Join project as client C and observe the diagnostics.
3493    let project_c = client_c.build_remote_project(project_id, cx_c).await;
3494    let project_c_diagnostic_summaries =
3495        Rc::new(RefCell::new(project_c.read_with(cx_c, |project, cx| {
3496            project.diagnostic_summaries(cx).collect::<Vec<_>>()
3497        })));
3498    project_c.update(cx_c, |_, cx| {
3499        let summaries = project_c_diagnostic_summaries.clone();
3500        cx.subscribe(&project_c, {
3501            move |p, _, event, cx| {
3502                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
3503                    *summaries.borrow_mut() = p.diagnostic_summaries(cx).collect();
3504                }
3505            }
3506        })
3507        .detach();
3508    });
3509
3510    deterministic.run_until_parked();
3511    assert_eq!(
3512        project_c_diagnostic_summaries.borrow().as_slice(),
3513        &[(
3514            ProjectPath {
3515                worktree_id,
3516                path: Arc::from(Path::new("a.rs")),
3517            },
3518            LanguageServerId(0),
3519            DiagnosticSummary {
3520                error_count: 1,
3521                warning_count: 0,
3522                ..Default::default()
3523            },
3524        )]
3525    );
3526
3527    // Simulate a language server reporting more errors for a file.
3528    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3529        lsp::PublishDiagnosticsParams {
3530            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3531            version: None,
3532            diagnostics: vec![
3533                lsp::Diagnostic {
3534                    severity: Some(lsp::DiagnosticSeverity::ERROR),
3535                    range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3536                    message: "message 1".to_string(),
3537                    ..Default::default()
3538                },
3539                lsp::Diagnostic {
3540                    severity: Some(lsp::DiagnosticSeverity::WARNING),
3541                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 13)),
3542                    message: "message 2".to_string(),
3543                    ..Default::default()
3544                },
3545            ],
3546        },
3547    );
3548
3549    // Clients B and C get the updated summaries
3550    deterministic.run_until_parked();
3551    project_b.read_with(cx_b, |project, cx| {
3552        assert_eq!(
3553            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
3554            [(
3555                ProjectPath {
3556                    worktree_id,
3557                    path: Arc::from(Path::new("a.rs")),
3558                },
3559                LanguageServerId(0),
3560                DiagnosticSummary {
3561                    error_count: 1,
3562                    warning_count: 1,
3563                },
3564            )]
3565        );
3566    });
3567    project_c.read_with(cx_c, |project, cx| {
3568        assert_eq!(
3569            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
3570            [(
3571                ProjectPath {
3572                    worktree_id,
3573                    path: Arc::from(Path::new("a.rs")),
3574                },
3575                LanguageServerId(0),
3576                DiagnosticSummary {
3577                    error_count: 1,
3578                    warning_count: 1,
3579                },
3580            )]
3581        );
3582    });
3583
3584    // Open the file with the errors on client B. They should be present.
3585    let buffer_b = cx_b
3586        .background()
3587        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
3588        .await
3589        .unwrap();
3590
3591    buffer_b.read_with(cx_b, |buffer, _| {
3592        assert_eq!(
3593            buffer
3594                .snapshot()
3595                .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
3596                .collect::<Vec<_>>(),
3597            &[
3598                DiagnosticEntry {
3599                    range: Point::new(0, 4)..Point::new(0, 7),
3600                    diagnostic: Diagnostic {
3601                        group_id: 2,
3602                        message: "message 1".to_string(),
3603                        severity: lsp::DiagnosticSeverity::ERROR,
3604                        is_primary: true,
3605                        ..Default::default()
3606                    }
3607                },
3608                DiagnosticEntry {
3609                    range: Point::new(0, 10)..Point::new(0, 13),
3610                    diagnostic: Diagnostic {
3611                        group_id: 3,
3612                        severity: lsp::DiagnosticSeverity::WARNING,
3613                        message: "message 2".to_string(),
3614                        is_primary: true,
3615                        ..Default::default()
3616                    }
3617                }
3618            ]
3619        );
3620    });
3621
3622    // Simulate a language server reporting no errors for a file.
3623    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3624        lsp::PublishDiagnosticsParams {
3625            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3626            version: None,
3627            diagnostics: vec![],
3628        },
3629    );
3630    deterministic.run_until_parked();
3631    project_a.read_with(cx_a, |project, cx| {
3632        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
3633    });
3634    project_b.read_with(cx_b, |project, cx| {
3635        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
3636    });
3637    project_c.read_with(cx_c, |project, cx| {
3638        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
3639    });
3640}
3641
3642#[gpui::test(iterations = 10)]
3643async fn test_collaborating_with_lsp_progress_updates_and_diagnostics_ordering(
3644    deterministic: Arc<Deterministic>,
3645    cx_a: &mut TestAppContext,
3646    cx_b: &mut TestAppContext,
3647) {
3648    deterministic.forbid_parking();
3649    let mut server = TestServer::start(&deterministic).await;
3650    let client_a = server.create_client(cx_a, "user_a").await;
3651    let client_b = server.create_client(cx_b, "user_b").await;
3652    server
3653        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3654        .await;
3655
3656    // Set up a fake language server.
3657    let mut language = Language::new(
3658        LanguageConfig {
3659            name: "Rust".into(),
3660            path_suffixes: vec!["rs".to_string()],
3661            ..Default::default()
3662        },
3663        Some(tree_sitter_rust::language()),
3664    );
3665    let mut fake_language_servers = language
3666        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3667            disk_based_diagnostics_progress_token: Some("the-disk-based-token".into()),
3668            disk_based_diagnostics_sources: vec!["the-disk-based-diagnostics-source".into()],
3669            ..Default::default()
3670        }))
3671        .await;
3672    client_a.language_registry.add(Arc::new(language));
3673
3674    let file_names = &["one.rs", "two.rs", "three.rs", "four.rs", "five.rs"];
3675    client_a
3676        .fs
3677        .insert_tree(
3678            "/test",
3679            json!({
3680                "one.rs": "const ONE: usize = 1;",
3681                "two.rs": "const TWO: usize = 2;",
3682                "three.rs": "const THREE: usize = 3;",
3683                "four.rs": "const FOUR: usize = 3;",
3684                "five.rs": "const FIVE: usize = 3;",
3685            }),
3686        )
3687        .await;
3688
3689    let (project_a, worktree_id) = client_a.build_local_project("/test", cx_a).await;
3690
3691    // Share a project as client A
3692    let active_call_a = cx_a.read(ActiveCall::global);
3693    let project_id = active_call_a
3694        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3695        .await
3696        .unwrap();
3697
3698    // Join the project as client B and open all three files.
3699    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3700    let guest_buffers = futures::future::try_join_all(file_names.iter().map(|file_name| {
3701        project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, file_name), cx))
3702    }))
3703    .await
3704    .unwrap();
3705
3706    // Simulate a language server reporting errors for a file.
3707    let fake_language_server = fake_language_servers.next().await.unwrap();
3708    fake_language_server
3709        .request::<lsp::request::WorkDoneProgressCreate>(lsp::WorkDoneProgressCreateParams {
3710            token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
3711        })
3712        .await
3713        .unwrap();
3714    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
3715        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
3716        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin(
3717            lsp::WorkDoneProgressBegin {
3718                title: "Progress Began".into(),
3719                ..Default::default()
3720            },
3721        )),
3722    });
3723    for file_name in file_names {
3724        fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3725            lsp::PublishDiagnosticsParams {
3726                uri: lsp::Url::from_file_path(Path::new("/test").join(file_name)).unwrap(),
3727                version: None,
3728                diagnostics: vec![lsp::Diagnostic {
3729                    severity: Some(lsp::DiagnosticSeverity::WARNING),
3730                    source: Some("the-disk-based-diagnostics-source".into()),
3731                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
3732                    message: "message one".to_string(),
3733                    ..Default::default()
3734                }],
3735            },
3736        );
3737    }
3738    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
3739        token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
3740        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End(
3741            lsp::WorkDoneProgressEnd { message: None },
3742        )),
3743    });
3744
3745    // When the "disk base diagnostics finished" message is received, the buffers'
3746    // diagnostics are expected to be present.
3747    let disk_based_diagnostics_finished = Arc::new(AtomicBool::new(false));
3748    project_b.update(cx_b, {
3749        let project_b = project_b.clone();
3750        let disk_based_diagnostics_finished = disk_based_diagnostics_finished.clone();
3751        move |_, cx| {
3752            cx.subscribe(&project_b, move |_, _, event, cx| {
3753                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
3754                    disk_based_diagnostics_finished.store(true, SeqCst);
3755                    for buffer in &guest_buffers {
3756                        assert_eq!(
3757                            buffer
3758                                .read(cx)
3759                                .snapshot()
3760                                .diagnostics_in_range::<_, usize>(0..5, false)
3761                                .count(),
3762                            1,
3763                            "expected a diagnostic for buffer {:?}",
3764                            buffer.read(cx).file().unwrap().path(),
3765                        );
3766                    }
3767                }
3768            })
3769            .detach();
3770        }
3771    });
3772
3773    deterministic.run_until_parked();
3774    assert!(disk_based_diagnostics_finished.load(SeqCst));
3775}
3776
3777#[gpui::test(iterations = 10)]
3778async fn test_collaborating_with_completion(
3779    deterministic: Arc<Deterministic>,
3780    cx_a: &mut TestAppContext,
3781    cx_b: &mut TestAppContext,
3782) {
3783    deterministic.forbid_parking();
3784    let mut server = TestServer::start(&deterministic).await;
3785    let client_a = server.create_client(cx_a, "user_a").await;
3786    let client_b = server.create_client(cx_b, "user_b").await;
3787    server
3788        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3789        .await;
3790    let active_call_a = cx_a.read(ActiveCall::global);
3791
3792    // Set up a fake language server.
3793    let mut language = Language::new(
3794        LanguageConfig {
3795            name: "Rust".into(),
3796            path_suffixes: vec!["rs".to_string()],
3797            ..Default::default()
3798        },
3799        Some(tree_sitter_rust::language()),
3800    );
3801    let mut fake_language_servers = language
3802        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3803            capabilities: lsp::ServerCapabilities {
3804                completion_provider: Some(lsp::CompletionOptions {
3805                    trigger_characters: Some(vec![".".to_string()]),
3806                    ..Default::default()
3807                }),
3808                ..Default::default()
3809            },
3810            ..Default::default()
3811        }))
3812        .await;
3813    client_a.language_registry.add(Arc::new(language));
3814
3815    client_a
3816        .fs
3817        .insert_tree(
3818            "/a",
3819            json!({
3820                "main.rs": "fn main() { a }",
3821                "other.rs": "",
3822            }),
3823        )
3824        .await;
3825    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3826    let project_id = active_call_a
3827        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3828        .await
3829        .unwrap();
3830    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3831
3832    // Open a file in an editor as the guest.
3833    let buffer_b = project_b
3834        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
3835        .await
3836        .unwrap();
3837    let (_, window_b) = cx_b.add_window(|_| EmptyView);
3838    let editor_b = cx_b.add_view(&window_b, |cx| {
3839        Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx)
3840    });
3841
3842    let fake_language_server = fake_language_servers.next().await.unwrap();
3843    cx_a.foreground().run_until_parked();
3844    buffer_b.read_with(cx_b, |buffer, _| {
3845        assert!(!buffer.completion_triggers().is_empty())
3846    });
3847
3848    // Type a completion trigger character as the guest.
3849    editor_b.update(cx_b, |editor, cx| {
3850        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
3851        editor.handle_input(".", cx);
3852        cx.focus(&editor_b);
3853    });
3854
3855    // Receive a completion request as the host's language server.
3856    // Return some completions from the host's language server.
3857    cx_a.foreground().start_waiting();
3858    fake_language_server
3859        .handle_request::<lsp::request::Completion, _, _>(|params, _| async move {
3860            assert_eq!(
3861                params.text_document_position.text_document.uri,
3862                lsp::Url::from_file_path("/a/main.rs").unwrap(),
3863            );
3864            assert_eq!(
3865                params.text_document_position.position,
3866                lsp::Position::new(0, 14),
3867            );
3868
3869            Ok(Some(lsp::CompletionResponse::Array(vec![
3870                lsp::CompletionItem {
3871                    label: "first_method(…)".into(),
3872                    detail: Some("fn(&mut self, B) -> C".into()),
3873                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
3874                        new_text: "first_method($1)".to_string(),
3875                        range: lsp::Range::new(
3876                            lsp::Position::new(0, 14),
3877                            lsp::Position::new(0, 14),
3878                        ),
3879                    })),
3880                    insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
3881                    ..Default::default()
3882                },
3883                lsp::CompletionItem {
3884                    label: "second_method(…)".into(),
3885                    detail: Some("fn(&mut self, C) -> D<E>".into()),
3886                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
3887                        new_text: "second_method()".to_string(),
3888                        range: lsp::Range::new(
3889                            lsp::Position::new(0, 14),
3890                            lsp::Position::new(0, 14),
3891                        ),
3892                    })),
3893                    insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
3894                    ..Default::default()
3895                },
3896            ])))
3897        })
3898        .next()
3899        .await
3900        .unwrap();
3901    cx_a.foreground().finish_waiting();
3902
3903    // Open the buffer on the host.
3904    let buffer_a = project_a
3905        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
3906        .await
3907        .unwrap();
3908    cx_a.foreground().run_until_parked();
3909    buffer_a.read_with(cx_a, |buffer, _| {
3910        assert_eq!(buffer.text(), "fn main() { a. }")
3911    });
3912
3913    // Confirm a completion on the guest.
3914    editor_b.read_with(cx_b, |editor, _| assert!(editor.context_menu_visible()));
3915    editor_b.update(cx_b, |editor, cx| {
3916        editor.confirm_completion(&ConfirmCompletion { item_ix: Some(0) }, cx);
3917        assert_eq!(editor.text(cx), "fn main() { a.first_method() }");
3918    });
3919
3920    // Return a resolved completion from the host's language server.
3921    // The resolved completion has an additional text edit.
3922    fake_language_server.handle_request::<lsp::request::ResolveCompletionItem, _, _>(
3923        |params, _| async move {
3924            assert_eq!(params.label, "first_method(…)");
3925            Ok(lsp::CompletionItem {
3926                label: "first_method(…)".into(),
3927                detail: Some("fn(&mut self, B) -> C".into()),
3928                text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
3929                    new_text: "first_method($1)".to_string(),
3930                    range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
3931                })),
3932                additional_text_edits: Some(vec![lsp::TextEdit {
3933                    new_text: "use d::SomeTrait;\n".to_string(),
3934                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
3935                }]),
3936                insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
3937                ..Default::default()
3938            })
3939        },
3940    );
3941
3942    // The additional edit is applied.
3943    cx_a.foreground().run_until_parked();
3944    buffer_a.read_with(cx_a, |buffer, _| {
3945        assert_eq!(
3946            buffer.text(),
3947            "use d::SomeTrait;\nfn main() { a.first_method() }"
3948        );
3949    });
3950    buffer_b.read_with(cx_b, |buffer, _| {
3951        assert_eq!(
3952            buffer.text(),
3953            "use d::SomeTrait;\nfn main() { a.first_method() }"
3954        );
3955    });
3956}
3957
3958#[gpui::test(iterations = 10)]
3959async fn test_reloading_buffer_manually(
3960    deterministic: Arc<Deterministic>,
3961    cx_a: &mut TestAppContext,
3962    cx_b: &mut TestAppContext,
3963) {
3964    deterministic.forbid_parking();
3965    let mut server = TestServer::start(&deterministic).await;
3966    let client_a = server.create_client(cx_a, "user_a").await;
3967    let client_b = server.create_client(cx_b, "user_b").await;
3968    server
3969        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3970        .await;
3971    let active_call_a = cx_a.read(ActiveCall::global);
3972
3973    client_a
3974        .fs
3975        .insert_tree("/a", json!({ "a.rs": "let one = 1;" }))
3976        .await;
3977    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3978    let buffer_a = project_a
3979        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
3980        .await
3981        .unwrap();
3982    let project_id = active_call_a
3983        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3984        .await
3985        .unwrap();
3986
3987    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3988
3989    let buffer_b = cx_b
3990        .background()
3991        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
3992        .await
3993        .unwrap();
3994    buffer_b.update(cx_b, |buffer, cx| {
3995        buffer.edit([(4..7, "six")], None, cx);
3996        buffer.edit([(10..11, "6")], None, cx);
3997        assert_eq!(buffer.text(), "let six = 6;");
3998        assert!(buffer.is_dirty());
3999        assert!(!buffer.has_conflict());
4000    });
4001    cx_a.foreground().run_until_parked();
4002    buffer_a.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "let six = 6;"));
4003
4004    client_a
4005        .fs
4006        .save(
4007            "/a/a.rs".as_ref(),
4008            &Rope::from("let seven = 7;"),
4009            LineEnding::Unix,
4010        )
4011        .await
4012        .unwrap();
4013    cx_a.foreground().run_until_parked();
4014    buffer_a.read_with(cx_a, |buffer, _| assert!(buffer.has_conflict()));
4015    buffer_b.read_with(cx_b, |buffer, _| assert!(buffer.has_conflict()));
4016
4017    project_b
4018        .update(cx_b, |project, cx| {
4019            project.reload_buffers(HashSet::from_iter([buffer_b.clone()]), true, cx)
4020        })
4021        .await
4022        .unwrap();
4023    buffer_a.read_with(cx_a, |buffer, _| {
4024        assert_eq!(buffer.text(), "let seven = 7;");
4025        assert!(!buffer.is_dirty());
4026        assert!(!buffer.has_conflict());
4027    });
4028    buffer_b.read_with(cx_b, |buffer, _| {
4029        assert_eq!(buffer.text(), "let seven = 7;");
4030        assert!(!buffer.is_dirty());
4031        assert!(!buffer.has_conflict());
4032    });
4033
4034    buffer_a.update(cx_a, |buffer, cx| {
4035        // Undoing on the host is a no-op when the reload was initiated by the guest.
4036        buffer.undo(cx);
4037        assert_eq!(buffer.text(), "let seven = 7;");
4038        assert!(!buffer.is_dirty());
4039        assert!(!buffer.has_conflict());
4040    });
4041    buffer_b.update(cx_b, |buffer, cx| {
4042        // Undoing on the guest rolls back the buffer to before it was reloaded but the conflict gets cleared.
4043        buffer.undo(cx);
4044        assert_eq!(buffer.text(), "let six = 6;");
4045        assert!(buffer.is_dirty());
4046        assert!(!buffer.has_conflict());
4047    });
4048}
4049
4050#[gpui::test(iterations = 10)]
4051async fn test_formatting_buffer(
4052    deterministic: Arc<Deterministic>,
4053    cx_a: &mut TestAppContext,
4054    cx_b: &mut TestAppContext,
4055) {
4056    use project::FormatTrigger;
4057
4058    let mut server = TestServer::start(&deterministic).await;
4059    let client_a = server.create_client(cx_a, "user_a").await;
4060    let client_b = server.create_client(cx_b, "user_b").await;
4061    server
4062        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4063        .await;
4064    let active_call_a = cx_a.read(ActiveCall::global);
4065
4066    // Set up a fake language server.
4067    let mut language = Language::new(
4068        LanguageConfig {
4069            name: "Rust".into(),
4070            path_suffixes: vec!["rs".to_string()],
4071            ..Default::default()
4072        },
4073        Some(tree_sitter_rust::language()),
4074    );
4075    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4076    client_a.language_registry.add(Arc::new(language));
4077
4078    // Here we insert a fake tree with a directory that exists on disk. This is needed
4079    // because later we'll invoke a command, which requires passing a working directory
4080    // that points to a valid location on disk.
4081    let directory = env::current_dir().unwrap();
4082    client_a
4083        .fs
4084        .insert_tree(&directory, json!({ "a.rs": "let one = \"two\"" }))
4085        .await;
4086    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
4087    let project_id = active_call_a
4088        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4089        .await
4090        .unwrap();
4091    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4092
4093    let buffer_b = cx_b
4094        .background()
4095        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4096        .await
4097        .unwrap();
4098
4099    let fake_language_server = fake_language_servers.next().await.unwrap();
4100    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
4101        Ok(Some(vec![
4102            lsp::TextEdit {
4103                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 4)),
4104                new_text: "h".to_string(),
4105            },
4106            lsp::TextEdit {
4107                range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 7)),
4108                new_text: "y".to_string(),
4109            },
4110        ]))
4111    });
4112
4113    project_b
4114        .update(cx_b, |project, cx| {
4115            project.format(
4116                HashSet::from_iter([buffer_b.clone()]),
4117                true,
4118                FormatTrigger::Save,
4119                cx,
4120            )
4121        })
4122        .await
4123        .unwrap();
4124
4125    // The edits from the LSP are applied, and a final newline is added.
4126    assert_eq!(
4127        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4128        "let honey = \"two\"\n"
4129    );
4130
4131    // Ensure buffer can be formatted using an external command. Notice how the
4132    // host's configuration is honored as opposed to using the guest's settings.
4133    cx_a.update(|cx| {
4134        cx.update_global(|settings: &mut Settings, _| {
4135            settings.editor_defaults.formatter = Some(Formatter::External {
4136                command: "awk".to_string(),
4137                arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()],
4138            });
4139        });
4140    });
4141    project_b
4142        .update(cx_b, |project, cx| {
4143            project.format(
4144                HashSet::from_iter([buffer_b.clone()]),
4145                true,
4146                FormatTrigger::Save,
4147                cx,
4148            )
4149        })
4150        .await
4151        .unwrap();
4152    assert_eq!(
4153        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
4154        format!("let honey = \"{}/a.rs\"\n", directory.to_str().unwrap())
4155    );
4156}
4157
4158#[gpui::test(iterations = 10)]
4159async fn test_definition(
4160    deterministic: Arc<Deterministic>,
4161    cx_a: &mut TestAppContext,
4162    cx_b: &mut TestAppContext,
4163) {
4164    deterministic.forbid_parking();
4165    let mut server = TestServer::start(&deterministic).await;
4166    let client_a = server.create_client(cx_a, "user_a").await;
4167    let client_b = server.create_client(cx_b, "user_b").await;
4168    server
4169        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4170        .await;
4171    let active_call_a = cx_a.read(ActiveCall::global);
4172
4173    // Set up a fake language server.
4174    let mut language = Language::new(
4175        LanguageConfig {
4176            name: "Rust".into(),
4177            path_suffixes: vec!["rs".to_string()],
4178            ..Default::default()
4179        },
4180        Some(tree_sitter_rust::language()),
4181    );
4182    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4183    client_a.language_registry.add(Arc::new(language));
4184
4185    client_a
4186        .fs
4187        .insert_tree(
4188            "/root",
4189            json!({
4190                "dir-1": {
4191                    "a.rs": "const ONE: usize = b::TWO + b::THREE;",
4192                },
4193                "dir-2": {
4194                    "b.rs": "const TWO: c::T2 = 2;\nconst THREE: usize = 3;",
4195                    "c.rs": "type T2 = usize;",
4196                }
4197            }),
4198        )
4199        .await;
4200    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4201    let project_id = active_call_a
4202        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4203        .await
4204        .unwrap();
4205    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4206
4207    // Open the file on client B.
4208    let buffer_b = cx_b
4209        .background()
4210        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4211        .await
4212        .unwrap();
4213
4214    // Request the definition of a symbol as the guest.
4215    let fake_language_server = fake_language_servers.next().await.unwrap();
4216    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4217        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4218            lsp::Location::new(
4219                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4220                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4221            ),
4222        )))
4223    });
4224
4225    let definitions_1 = project_b
4226        .update(cx_b, |p, cx| p.definition(&buffer_b, 23, cx))
4227        .await
4228        .unwrap();
4229    cx_b.read(|cx| {
4230        assert_eq!(definitions_1.len(), 1);
4231        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4232        let target_buffer = definitions_1[0].target.buffer.read(cx);
4233        assert_eq!(
4234            target_buffer.text(),
4235            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4236        );
4237        assert_eq!(
4238            definitions_1[0].target.range.to_point(target_buffer),
4239            Point::new(0, 6)..Point::new(0, 9)
4240        );
4241    });
4242
4243    // Try getting more definitions for the same buffer, ensuring the buffer gets reused from
4244    // the previous call to `definition`.
4245    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4246        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4247            lsp::Location::new(
4248                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4249                lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)),
4250            ),
4251        )))
4252    });
4253
4254    let definitions_2 = project_b
4255        .update(cx_b, |p, cx| p.definition(&buffer_b, 33, cx))
4256        .await
4257        .unwrap();
4258    cx_b.read(|cx| {
4259        assert_eq!(definitions_2.len(), 1);
4260        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4261        let target_buffer = definitions_2[0].target.buffer.read(cx);
4262        assert_eq!(
4263            target_buffer.text(),
4264            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4265        );
4266        assert_eq!(
4267            definitions_2[0].target.range.to_point(target_buffer),
4268            Point::new(1, 6)..Point::new(1, 11)
4269        );
4270    });
4271    assert_eq!(
4272        definitions_1[0].target.buffer,
4273        definitions_2[0].target.buffer
4274    );
4275
4276    fake_language_server.handle_request::<lsp::request::GotoTypeDefinition, _, _>(
4277        |req, _| async move {
4278            assert_eq!(
4279                req.text_document_position_params.position,
4280                lsp::Position::new(0, 7)
4281            );
4282            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4283                lsp::Location::new(
4284                    lsp::Url::from_file_path("/root/dir-2/c.rs").unwrap(),
4285                    lsp::Range::new(lsp::Position::new(0, 5), lsp::Position::new(0, 7)),
4286                ),
4287            )))
4288        },
4289    );
4290
4291    let type_definitions = project_b
4292        .update(cx_b, |p, cx| p.type_definition(&buffer_b, 7, cx))
4293        .await
4294        .unwrap();
4295    cx_b.read(|cx| {
4296        assert_eq!(type_definitions.len(), 1);
4297        let target_buffer = type_definitions[0].target.buffer.read(cx);
4298        assert_eq!(target_buffer.text(), "type T2 = usize;");
4299        assert_eq!(
4300            type_definitions[0].target.range.to_point(target_buffer),
4301            Point::new(0, 5)..Point::new(0, 7)
4302        );
4303    });
4304}
4305
4306#[gpui::test(iterations = 10)]
4307async fn test_references(
4308    deterministic: Arc<Deterministic>,
4309    cx_a: &mut TestAppContext,
4310    cx_b: &mut TestAppContext,
4311) {
4312    deterministic.forbid_parking();
4313    let mut server = TestServer::start(&deterministic).await;
4314    let client_a = server.create_client(cx_a, "user_a").await;
4315    let client_b = server.create_client(cx_b, "user_b").await;
4316    server
4317        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4318        .await;
4319    let active_call_a = cx_a.read(ActiveCall::global);
4320
4321    // Set up a fake language server.
4322    let mut language = Language::new(
4323        LanguageConfig {
4324            name: "Rust".into(),
4325            path_suffixes: vec!["rs".to_string()],
4326            ..Default::default()
4327        },
4328        Some(tree_sitter_rust::language()),
4329    );
4330    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4331    client_a.language_registry.add(Arc::new(language));
4332
4333    client_a
4334        .fs
4335        .insert_tree(
4336            "/root",
4337            json!({
4338                "dir-1": {
4339                    "one.rs": "const ONE: usize = 1;",
4340                    "two.rs": "const TWO: usize = one::ONE + one::ONE;",
4341                },
4342                "dir-2": {
4343                    "three.rs": "const THREE: usize = two::TWO + one::ONE;",
4344                }
4345            }),
4346        )
4347        .await;
4348    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4349    let project_id = active_call_a
4350        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4351        .await
4352        .unwrap();
4353    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4354
4355    // Open the file on client B.
4356    let buffer_b = cx_b
4357        .background()
4358        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
4359        .await
4360        .unwrap();
4361
4362    // Request references to a symbol as the guest.
4363    let fake_language_server = fake_language_servers.next().await.unwrap();
4364    fake_language_server.handle_request::<lsp::request::References, _, _>(|params, _| async move {
4365        assert_eq!(
4366            params.text_document_position.text_document.uri.as_str(),
4367            "file:///root/dir-1/one.rs"
4368        );
4369        Ok(Some(vec![
4370            lsp::Location {
4371                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4372                range: lsp::Range::new(lsp::Position::new(0, 24), lsp::Position::new(0, 27)),
4373            },
4374            lsp::Location {
4375                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4376                range: lsp::Range::new(lsp::Position::new(0, 35), lsp::Position::new(0, 38)),
4377            },
4378            lsp::Location {
4379                uri: lsp::Url::from_file_path("/root/dir-2/three.rs").unwrap(),
4380                range: lsp::Range::new(lsp::Position::new(0, 37), lsp::Position::new(0, 40)),
4381            },
4382        ]))
4383    });
4384
4385    let references = project_b
4386        .update(cx_b, |p, cx| p.references(&buffer_b, 7, cx))
4387        .await
4388        .unwrap();
4389    cx_b.read(|cx| {
4390        assert_eq!(references.len(), 3);
4391        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4392
4393        let two_buffer = references[0].buffer.read(cx);
4394        let three_buffer = references[2].buffer.read(cx);
4395        assert_eq!(
4396            two_buffer.file().unwrap().path().as_ref(),
4397            Path::new("two.rs")
4398        );
4399        assert_eq!(references[1].buffer, references[0].buffer);
4400        assert_eq!(
4401            three_buffer.file().unwrap().full_path(cx),
4402            Path::new("/root/dir-2/three.rs")
4403        );
4404
4405        assert_eq!(references[0].range.to_offset(two_buffer), 24..27);
4406        assert_eq!(references[1].range.to_offset(two_buffer), 35..38);
4407        assert_eq!(references[2].range.to_offset(three_buffer), 37..40);
4408    });
4409}
4410
4411#[gpui::test(iterations = 10)]
4412async fn test_project_search(
4413    deterministic: Arc<Deterministic>,
4414    cx_a: &mut TestAppContext,
4415    cx_b: &mut TestAppContext,
4416) {
4417    deterministic.forbid_parking();
4418    let mut server = TestServer::start(&deterministic).await;
4419    let client_a = server.create_client(cx_a, "user_a").await;
4420    let client_b = server.create_client(cx_b, "user_b").await;
4421    server
4422        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4423        .await;
4424    let active_call_a = cx_a.read(ActiveCall::global);
4425
4426    client_a
4427        .fs
4428        .insert_tree(
4429            "/root",
4430            json!({
4431                "dir-1": {
4432                    "a": "hello world",
4433                    "b": "goodnight moon",
4434                    "c": "a world of goo",
4435                    "d": "world champion of clown world",
4436                },
4437                "dir-2": {
4438                    "e": "disney world is fun",
4439                }
4440            }),
4441        )
4442        .await;
4443    let (project_a, _) = client_a.build_local_project("/root/dir-1", cx_a).await;
4444    let (worktree_2, _) = project_a
4445        .update(cx_a, |p, cx| {
4446            p.find_or_create_local_worktree("/root/dir-2", true, cx)
4447        })
4448        .await
4449        .unwrap();
4450    worktree_2
4451        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
4452        .await;
4453    let project_id = active_call_a
4454        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4455        .await
4456        .unwrap();
4457
4458    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4459
4460    // Perform a search as the guest.
4461    let results = project_b
4462        .update(cx_b, |project, cx| {
4463            project.search(SearchQuery::text("world", false, false), cx)
4464        })
4465        .await
4466        .unwrap();
4467
4468    let mut ranges_by_path = results
4469        .into_iter()
4470        .map(|(buffer, ranges)| {
4471            buffer.read_with(cx_b, |buffer, cx| {
4472                let path = buffer.file().unwrap().full_path(cx);
4473                let offset_ranges = ranges
4474                    .into_iter()
4475                    .map(|range| range.to_offset(buffer))
4476                    .collect::<Vec<_>>();
4477                (path, offset_ranges)
4478            })
4479        })
4480        .collect::<Vec<_>>();
4481    ranges_by_path.sort_by_key(|(path, _)| path.clone());
4482
4483    assert_eq!(
4484        ranges_by_path,
4485        &[
4486            (PathBuf::from("dir-1/a"), vec![6..11]),
4487            (PathBuf::from("dir-1/c"), vec![2..7]),
4488            (PathBuf::from("dir-1/d"), vec![0..5, 24..29]),
4489            (PathBuf::from("dir-2/e"), vec![7..12]),
4490        ]
4491    );
4492}
4493
4494#[gpui::test(iterations = 10)]
4495async fn test_document_highlights(
4496    deterministic: Arc<Deterministic>,
4497    cx_a: &mut TestAppContext,
4498    cx_b: &mut TestAppContext,
4499) {
4500    deterministic.forbid_parking();
4501    let mut server = TestServer::start(&deterministic).await;
4502    let client_a = server.create_client(cx_a, "user_a").await;
4503    let client_b = server.create_client(cx_b, "user_b").await;
4504    server
4505        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4506        .await;
4507    let active_call_a = cx_a.read(ActiveCall::global);
4508
4509    client_a
4510        .fs
4511        .insert_tree(
4512            "/root-1",
4513            json!({
4514                "main.rs": "fn double(number: i32) -> i32 { number + number }",
4515            }),
4516        )
4517        .await;
4518
4519    // Set up a fake language server.
4520    let mut language = Language::new(
4521        LanguageConfig {
4522            name: "Rust".into(),
4523            path_suffixes: vec!["rs".to_string()],
4524            ..Default::default()
4525        },
4526        Some(tree_sitter_rust::language()),
4527    );
4528    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4529    client_a.language_registry.add(Arc::new(language));
4530
4531    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4532    let project_id = active_call_a
4533        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4534        .await
4535        .unwrap();
4536    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4537
4538    // Open the file on client B.
4539    let buffer_b = cx_b
4540        .background()
4541        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
4542        .await
4543        .unwrap();
4544
4545    // Request document highlights as the guest.
4546    let fake_language_server = fake_language_servers.next().await.unwrap();
4547    fake_language_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
4548        |params, _| async move {
4549            assert_eq!(
4550                params
4551                    .text_document_position_params
4552                    .text_document
4553                    .uri
4554                    .as_str(),
4555                "file:///root-1/main.rs"
4556            );
4557            assert_eq!(
4558                params.text_document_position_params.position,
4559                lsp::Position::new(0, 34)
4560            );
4561            Ok(Some(vec![
4562                lsp::DocumentHighlight {
4563                    kind: Some(lsp::DocumentHighlightKind::WRITE),
4564                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 16)),
4565                },
4566                lsp::DocumentHighlight {
4567                    kind: Some(lsp::DocumentHighlightKind::READ),
4568                    range: lsp::Range::new(lsp::Position::new(0, 32), lsp::Position::new(0, 38)),
4569                },
4570                lsp::DocumentHighlight {
4571                    kind: Some(lsp::DocumentHighlightKind::READ),
4572                    range: lsp::Range::new(lsp::Position::new(0, 41), lsp::Position::new(0, 47)),
4573                },
4574            ]))
4575        },
4576    );
4577
4578    let highlights = project_b
4579        .update(cx_b, |p, cx| p.document_highlights(&buffer_b, 34, cx))
4580        .await
4581        .unwrap();
4582    buffer_b.read_with(cx_b, |buffer, _| {
4583        let snapshot = buffer.snapshot();
4584
4585        let highlights = highlights
4586            .into_iter()
4587            .map(|highlight| (highlight.kind, highlight.range.to_offset(&snapshot)))
4588            .collect::<Vec<_>>();
4589        assert_eq!(
4590            highlights,
4591            &[
4592                (lsp::DocumentHighlightKind::WRITE, 10..16),
4593                (lsp::DocumentHighlightKind::READ, 32..38),
4594                (lsp::DocumentHighlightKind::READ, 41..47)
4595            ]
4596        )
4597    });
4598}
4599
4600#[gpui::test(iterations = 10)]
4601async fn test_lsp_hover(
4602    deterministic: Arc<Deterministic>,
4603    cx_a: &mut TestAppContext,
4604    cx_b: &mut TestAppContext,
4605) {
4606    deterministic.forbid_parking();
4607    let mut server = TestServer::start(&deterministic).await;
4608    let client_a = server.create_client(cx_a, "user_a").await;
4609    let client_b = server.create_client(cx_b, "user_b").await;
4610    server
4611        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4612        .await;
4613    let active_call_a = cx_a.read(ActiveCall::global);
4614
4615    client_a
4616        .fs
4617        .insert_tree(
4618            "/root-1",
4619            json!({
4620                "main.rs": "use std::collections::HashMap;",
4621            }),
4622        )
4623        .await;
4624
4625    // Set up a fake language server.
4626    let mut language = Language::new(
4627        LanguageConfig {
4628            name: "Rust".into(),
4629            path_suffixes: vec!["rs".to_string()],
4630            ..Default::default()
4631        },
4632        Some(tree_sitter_rust::language()),
4633    );
4634    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4635    client_a.language_registry.add(Arc::new(language));
4636
4637    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4638    let project_id = active_call_a
4639        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4640        .await
4641        .unwrap();
4642    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4643
4644    // Open the file as the guest
4645    let buffer_b = cx_b
4646        .background()
4647        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
4648        .await
4649        .unwrap();
4650
4651    // Request hover information as the guest.
4652    let fake_language_server = fake_language_servers.next().await.unwrap();
4653    fake_language_server.handle_request::<lsp::request::HoverRequest, _, _>(
4654        |params, _| async move {
4655            assert_eq!(
4656                params
4657                    .text_document_position_params
4658                    .text_document
4659                    .uri
4660                    .as_str(),
4661                "file:///root-1/main.rs"
4662            );
4663            assert_eq!(
4664                params.text_document_position_params.position,
4665                lsp::Position::new(0, 22)
4666            );
4667            Ok(Some(lsp::Hover {
4668                contents: lsp::HoverContents::Array(vec![
4669                    lsp::MarkedString::String("Test hover content.".to_string()),
4670                    lsp::MarkedString::LanguageString(lsp::LanguageString {
4671                        language: "Rust".to_string(),
4672                        value: "let foo = 42;".to_string(),
4673                    }),
4674                ]),
4675                range: Some(lsp::Range::new(
4676                    lsp::Position::new(0, 22),
4677                    lsp::Position::new(0, 29),
4678                )),
4679            }))
4680        },
4681    );
4682
4683    let hover_info = project_b
4684        .update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
4685        .await
4686        .unwrap()
4687        .unwrap();
4688    buffer_b.read_with(cx_b, |buffer, _| {
4689        let snapshot = buffer.snapshot();
4690        assert_eq!(hover_info.range.unwrap().to_offset(&snapshot), 22..29);
4691        assert_eq!(
4692            hover_info.contents,
4693            vec![
4694                project::HoverBlock {
4695                    text: "Test hover content.".to_string(),
4696                    kind: HoverBlockKind::Markdown,
4697                },
4698                project::HoverBlock {
4699                    text: "let foo = 42;".to_string(),
4700                    kind: HoverBlockKind::Code {
4701                        language: "Rust".to_string()
4702                    },
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        .unwrap();
6371    deterministic.run_until_parked();
6372    workspace_b.read_with(cx_b, |workspace, cx| {
6373        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
6374    });
6375
6376    workspace_a
6377        .update(cx_a, |workspace, cx| {
6378            workspace::Pane::go_back(workspace, None, cx)
6379        })
6380        .await
6381        .unwrap();
6382    deterministic.run_until_parked();
6383    workspace_b.read_with(cx_b, |workspace, cx| {
6384        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b2.id());
6385    });
6386
6387    workspace_a
6388        .update(cx_a, |workspace, cx| {
6389            workspace::Pane::go_forward(workspace, None, cx)
6390        })
6391        .await
6392        .unwrap();
6393    deterministic.run_until_parked();
6394    workspace_b.read_with(cx_b, |workspace, cx| {
6395        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
6396    });
6397
6398    // Changes to client A's editor are reflected on client B.
6399    editor_a1.update(cx_a, |editor, cx| {
6400        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2]));
6401    });
6402    deterministic.run_until_parked();
6403    editor_b1.read_with(cx_b, |editor, cx| {
6404        assert_eq!(editor.selections.ranges(cx), &[1..1, 2..2]);
6405    });
6406
6407    editor_a1.update(cx_a, |editor, cx| editor.set_text("TWO", cx));
6408    deterministic.run_until_parked();
6409    editor_b1.read_with(cx_b, |editor, cx| assert_eq!(editor.text(cx), "TWO"));
6410
6411    editor_a1.update(cx_a, |editor, cx| {
6412        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
6413        editor.set_scroll_position(vec2f(0., 100.), cx);
6414    });
6415    deterministic.run_until_parked();
6416    editor_b1.read_with(cx_b, |editor, cx| {
6417        assert_eq!(editor.selections.ranges(cx), &[3..3]);
6418    });
6419
6420    // After unfollowing, client B stops receiving updates from client A.
6421    workspace_b.update(cx_b, |workspace, cx| {
6422        workspace.unfollow(&workspace.active_pane().clone(), cx)
6423    });
6424    workspace_a.update(cx_a, |workspace, cx| {
6425        workspace.activate_item(&editor_a2, cx)
6426    });
6427    deterministic.run_until_parked();
6428    assert_eq!(
6429        workspace_b.read_with(cx_b, |workspace, cx| workspace
6430            .active_item(cx)
6431            .unwrap()
6432            .id()),
6433        editor_b1.id()
6434    );
6435
6436    // Client A starts following client B.
6437    workspace_a
6438        .update(cx_a, |workspace, cx| {
6439            workspace
6440                .toggle_follow(&ToggleFollow(peer_id_b), cx)
6441                .unwrap()
6442        })
6443        .await
6444        .unwrap();
6445    assert_eq!(
6446        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
6447        Some(peer_id_b)
6448    );
6449    assert_eq!(
6450        workspace_a.read_with(cx_a, |workspace, cx| workspace
6451            .active_item(cx)
6452            .unwrap()
6453            .id()),
6454        editor_a1.id()
6455    );
6456
6457    // Client B activates an external window, which causes a new screen-sharing item to be added to the pane.
6458    let display = MacOSDisplay::new();
6459    active_call_b
6460        .update(cx_b, |call, cx| call.set_location(None, cx))
6461        .await
6462        .unwrap();
6463    active_call_b
6464        .update(cx_b, |call, cx| {
6465            call.room().unwrap().update(cx, |room, cx| {
6466                room.set_display_sources(vec![display.clone()]);
6467                room.share_screen(cx)
6468            })
6469        })
6470        .await
6471        .unwrap();
6472    deterministic.run_until_parked();
6473    let shared_screen = workspace_a.read_with(cx_a, |workspace, cx| {
6474        workspace
6475            .active_item(cx)
6476            .unwrap()
6477            .downcast::<SharedScreen>()
6478            .unwrap()
6479    });
6480
6481    // Client B activates Zed again, which causes the previous editor to become focused again.
6482    active_call_b
6483        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6484        .await
6485        .unwrap();
6486    deterministic.run_until_parked();
6487    workspace_a.read_with(cx_a, |workspace, cx| {
6488        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_a1.id())
6489    });
6490
6491    // Client B activates a multibuffer that was created by following client A. Client A returns to that multibuffer.
6492    workspace_b.update(cx_b, |workspace, cx| {
6493        workspace.activate_item(&multibuffer_editor_b, cx)
6494    });
6495    deterministic.run_until_parked();
6496    workspace_a.read_with(cx_a, |workspace, cx| {
6497        assert_eq!(
6498            workspace.active_item(cx).unwrap().id(),
6499            multibuffer_editor_a.id()
6500        )
6501    });
6502
6503    // Client B activates an external window again, and the previously-opened screen-sharing item
6504    // gets activated.
6505    active_call_b
6506        .update(cx_b, |call, cx| call.set_location(None, cx))
6507        .await
6508        .unwrap();
6509    deterministic.run_until_parked();
6510    assert_eq!(
6511        workspace_a.read_with(cx_a, |workspace, cx| workspace
6512            .active_item(cx)
6513            .unwrap()
6514            .id()),
6515        shared_screen.id()
6516    );
6517
6518    // Following interrupts when client B disconnects.
6519    client_b.disconnect(&cx_b.to_async());
6520    deterministic.advance_clock(RECONNECT_TIMEOUT);
6521    assert_eq!(
6522        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
6523        None
6524    );
6525}
6526
6527#[gpui::test(iterations = 10)]
6528async fn test_join_call_after_screen_was_shared(
6529    deterministic: Arc<Deterministic>,
6530    cx_a: &mut TestAppContext,
6531    cx_b: &mut TestAppContext,
6532) {
6533    deterministic.forbid_parking();
6534    let mut server = TestServer::start(&deterministic).await;
6535
6536    let client_a = server.create_client(cx_a, "user_a").await;
6537    let client_b = server.create_client(cx_b, "user_b").await;
6538    server
6539        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6540        .await;
6541
6542    let active_call_a = cx_a.read(ActiveCall::global);
6543    let active_call_b = cx_b.read(ActiveCall::global);
6544
6545    // Call users B and C from client A.
6546    active_call_a
6547        .update(cx_a, |call, cx| {
6548            call.invite(client_b.user_id().unwrap(), None, cx)
6549        })
6550        .await
6551        .unwrap();
6552    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
6553    deterministic.run_until_parked();
6554    assert_eq!(
6555        room_participants(&room_a, cx_a),
6556        RoomParticipants {
6557            remote: Default::default(),
6558            pending: vec!["user_b".to_string()]
6559        }
6560    );
6561
6562    // User B receives the call.
6563    let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
6564    let call_b = incoming_call_b.next().await.unwrap().unwrap();
6565    assert_eq!(call_b.calling_user.github_login, "user_a");
6566
6567    // User A shares their screen
6568    let display = MacOSDisplay::new();
6569    active_call_a
6570        .update(cx_a, |call, cx| {
6571            call.room().unwrap().update(cx, |room, cx| {
6572                room.set_display_sources(vec![display.clone()]);
6573                room.share_screen(cx)
6574            })
6575        })
6576        .await
6577        .unwrap();
6578
6579    client_b.user_store.update(cx_b, |user_store, _| {
6580        user_store.clear_cache();
6581    });
6582
6583    // User B joins the room
6584    active_call_b
6585        .update(cx_b, |call, cx| call.accept_incoming(cx))
6586        .await
6587        .unwrap();
6588    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
6589    assert!(incoming_call_b.next().await.unwrap().is_none());
6590
6591    deterministic.run_until_parked();
6592    assert_eq!(
6593        room_participants(&room_a, cx_a),
6594        RoomParticipants {
6595            remote: vec!["user_b".to_string()],
6596            pending: vec![],
6597        }
6598    );
6599    assert_eq!(
6600        room_participants(&room_b, cx_b),
6601        RoomParticipants {
6602            remote: vec!["user_a".to_string()],
6603            pending: vec![],
6604        }
6605    );
6606
6607    // Ensure User B sees User A's screenshare.
6608    room_b.read_with(cx_b, |room, _| {
6609        assert_eq!(
6610            room.remote_participants()
6611                .get(&client_a.user_id().unwrap())
6612                .unwrap()
6613                .tracks
6614                .len(),
6615            1
6616        );
6617    });
6618}
6619
6620#[gpui::test]
6621async fn test_following_tab_order(
6622    deterministic: Arc<Deterministic>,
6623    cx_a: &mut TestAppContext,
6624    cx_b: &mut TestAppContext,
6625) {
6626    cx_a.update(editor::init);
6627    cx_b.update(editor::init);
6628
6629    let mut server = TestServer::start(&deterministic).await;
6630    let client_a = server.create_client(cx_a, "user_a").await;
6631    let client_b = server.create_client(cx_b, "user_b").await;
6632    server
6633        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6634        .await;
6635    let active_call_a = cx_a.read(ActiveCall::global);
6636    let active_call_b = cx_b.read(ActiveCall::global);
6637
6638    client_a
6639        .fs
6640        .insert_tree(
6641            "/a",
6642            json!({
6643                "1.txt": "one",
6644                "2.txt": "two",
6645                "3.txt": "three",
6646            }),
6647        )
6648        .await;
6649    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
6650    active_call_a
6651        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
6652        .await
6653        .unwrap();
6654
6655    let project_id = active_call_a
6656        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6657        .await
6658        .unwrap();
6659    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6660    active_call_b
6661        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6662        .await
6663        .unwrap();
6664
6665    let workspace_a = client_a.build_workspace(&project_a, cx_a);
6666    let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
6667
6668    let workspace_b = client_b.build_workspace(&project_b, cx_b);
6669    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
6670
6671    let client_b_id = project_a.read_with(cx_a, |project, _| {
6672        project.collaborators().values().next().unwrap().peer_id
6673    });
6674
6675    //Open 1, 3 in that order on client A
6676    workspace_a
6677        .update(cx_a, |workspace, cx| {
6678            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6679        })
6680        .await
6681        .unwrap();
6682    workspace_a
6683        .update(cx_a, |workspace, cx| {
6684            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
6685        })
6686        .await
6687        .unwrap();
6688
6689    let pane_paths = |pane: &ViewHandle<workspace::Pane>, cx: &mut TestAppContext| {
6690        pane.update(cx, |pane, cx| {
6691            pane.items()
6692                .map(|item| {
6693                    item.project_path(cx)
6694                        .unwrap()
6695                        .path
6696                        .to_str()
6697                        .unwrap()
6698                        .to_owned()
6699                })
6700                .collect::<Vec<_>>()
6701        })
6702    };
6703
6704    //Verify that the tabs opened in the order we expect
6705    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt"]);
6706
6707    //Follow client B as client A
6708    workspace_a
6709        .update(cx_a, |workspace, cx| {
6710            workspace
6711                .toggle_follow(&ToggleFollow(client_b_id), cx)
6712                .unwrap()
6713        })
6714        .await
6715        .unwrap();
6716
6717    //Open just 2 on client B
6718    workspace_b
6719        .update(cx_b, |workspace, cx| {
6720            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
6721        })
6722        .await
6723        .unwrap();
6724    deterministic.run_until_parked();
6725
6726    // Verify that newly opened followed file is at the end
6727    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt", "2.txt"]);
6728
6729    //Open just 1 on client B
6730    workspace_b
6731        .update(cx_b, |workspace, cx| {
6732            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6733        })
6734        .await
6735        .unwrap();
6736    assert_eq!(&pane_paths(&pane_b, cx_b), &["2.txt", "1.txt"]);
6737    deterministic.run_until_parked();
6738
6739    // Verify that following into 1 did not reorder
6740    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt", "2.txt"]);
6741}
6742
6743#[gpui::test(iterations = 10)]
6744async fn test_peers_following_each_other(
6745    deterministic: Arc<Deterministic>,
6746    cx_a: &mut TestAppContext,
6747    cx_b: &mut TestAppContext,
6748) {
6749    deterministic.forbid_parking();
6750    cx_a.update(editor::init);
6751    cx_b.update(editor::init);
6752
6753    let mut server = TestServer::start(&deterministic).await;
6754    let client_a = server.create_client(cx_a, "user_a").await;
6755    let client_b = server.create_client(cx_b, "user_b").await;
6756    server
6757        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6758        .await;
6759    let active_call_a = cx_a.read(ActiveCall::global);
6760    let active_call_b = cx_b.read(ActiveCall::global);
6761
6762    // Client A shares a project.
6763    client_a
6764        .fs
6765        .insert_tree(
6766            "/a",
6767            json!({
6768                "1.txt": "one",
6769                "2.txt": "two",
6770                "3.txt": "three",
6771                "4.txt": "four",
6772            }),
6773        )
6774        .await;
6775    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
6776    active_call_a
6777        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
6778        .await
6779        .unwrap();
6780    let project_id = active_call_a
6781        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6782        .await
6783        .unwrap();
6784
6785    // Client B joins the project.
6786    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6787    active_call_b
6788        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6789        .await
6790        .unwrap();
6791
6792    // Client A opens some editors.
6793    let workspace_a = client_a.build_workspace(&project_a, cx_a);
6794    let pane_a1 = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
6795    let _editor_a1 = workspace_a
6796        .update(cx_a, |workspace, cx| {
6797            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6798        })
6799        .await
6800        .unwrap()
6801        .downcast::<Editor>()
6802        .unwrap();
6803
6804    // Client B opens an editor.
6805    let workspace_b = client_b.build_workspace(&project_b, cx_b);
6806    let pane_b1 = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
6807    let _editor_b1 = workspace_b
6808        .update(cx_b, |workspace, cx| {
6809            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
6810        })
6811        .await
6812        .unwrap()
6813        .downcast::<Editor>()
6814        .unwrap();
6815
6816    // Clients A and B follow each other in split panes
6817    workspace_a.update(cx_a, |workspace, cx| {
6818        workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
6819        let pane_a1 = pane_a1.clone();
6820        cx.defer(move |workspace, _| {
6821            assert_ne!(*workspace.active_pane(), pane_a1);
6822        });
6823    });
6824    workspace_a
6825        .update(cx_a, |workspace, cx| {
6826            let leader_id = *project_a.read(cx).collaborators().keys().next().unwrap();
6827            workspace
6828                .toggle_follow(&workspace::ToggleFollow(leader_id), cx)
6829                .unwrap()
6830        })
6831        .await
6832        .unwrap();
6833    workspace_b.update(cx_b, |workspace, cx| {
6834        workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
6835        let pane_b1 = pane_b1.clone();
6836        cx.defer(move |workspace, _| {
6837            assert_ne!(*workspace.active_pane(), pane_b1);
6838        });
6839    });
6840    workspace_b
6841        .update(cx_b, |workspace, cx| {
6842            let leader_id = *project_b.read(cx).collaborators().keys().next().unwrap();
6843            workspace
6844                .toggle_follow(&workspace::ToggleFollow(leader_id), cx)
6845                .unwrap()
6846        })
6847        .await
6848        .unwrap();
6849
6850    workspace_a.update(cx_a, |workspace, cx| {
6851        workspace.activate_next_pane(cx);
6852    });
6853    // Wait for focus effects to be fully flushed
6854    workspace_a.update(cx_a, |workspace, _| {
6855        assert_eq!(*workspace.active_pane(), pane_a1);
6856    });
6857
6858    workspace_a
6859        .update(cx_a, |workspace, cx| {
6860            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
6861        })
6862        .await
6863        .unwrap();
6864    workspace_b.update(cx_b, |workspace, cx| {
6865        workspace.activate_next_pane(cx);
6866    });
6867
6868    workspace_b
6869        .update(cx_b, |workspace, cx| {
6870            assert_eq!(*workspace.active_pane(), pane_b1);
6871            workspace.open_path((worktree_id, "4.txt"), None, true, cx)
6872        })
6873        .await
6874        .unwrap();
6875    cx_a.foreground().run_until_parked();
6876
6877    // Ensure leader updates don't change the active pane of followers
6878    workspace_a.read_with(cx_a, |workspace, _| {
6879        assert_eq!(*workspace.active_pane(), pane_a1);
6880    });
6881    workspace_b.read_with(cx_b, |workspace, _| {
6882        assert_eq!(*workspace.active_pane(), pane_b1);
6883    });
6884
6885    // Ensure peers following each other doesn't cause an infinite loop.
6886    assert_eq!(
6887        workspace_a.read_with(cx_a, |workspace, cx| workspace
6888            .active_item(cx)
6889            .unwrap()
6890            .project_path(cx)),
6891        Some((worktree_id, "3.txt").into())
6892    );
6893    workspace_a.update(cx_a, |workspace, cx| {
6894        assert_eq!(
6895            workspace.active_item(cx).unwrap().project_path(cx),
6896            Some((worktree_id, "3.txt").into())
6897        );
6898        workspace.activate_next_pane(cx);
6899    });
6900
6901    workspace_a.update(cx_a, |workspace, cx| {
6902        assert_eq!(
6903            workspace.active_item(cx).unwrap().project_path(cx),
6904            Some((worktree_id, "4.txt").into())
6905        );
6906    });
6907
6908    workspace_b.update(cx_b, |workspace, cx| {
6909        assert_eq!(
6910            workspace.active_item(cx).unwrap().project_path(cx),
6911            Some((worktree_id, "4.txt").into())
6912        );
6913        workspace.activate_next_pane(cx);
6914    });
6915
6916    workspace_b.update(cx_b, |workspace, cx| {
6917        assert_eq!(
6918            workspace.active_item(cx).unwrap().project_path(cx),
6919            Some((worktree_id, "3.txt").into())
6920        );
6921    });
6922}
6923
6924#[gpui::test(iterations = 10)]
6925async fn test_auto_unfollowing(
6926    deterministic: Arc<Deterministic>,
6927    cx_a: &mut TestAppContext,
6928    cx_b: &mut TestAppContext,
6929) {
6930    deterministic.forbid_parking();
6931    cx_a.update(editor::init);
6932    cx_b.update(editor::init);
6933
6934    // 2 clients connect to a server.
6935    let mut server = TestServer::start(&deterministic).await;
6936    let client_a = server.create_client(cx_a, "user_a").await;
6937    let client_b = server.create_client(cx_b, "user_b").await;
6938    server
6939        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6940        .await;
6941    let active_call_a = cx_a.read(ActiveCall::global);
6942    let active_call_b = cx_b.read(ActiveCall::global);
6943
6944    // Client A shares a project.
6945    client_a
6946        .fs
6947        .insert_tree(
6948            "/a",
6949            json!({
6950                "1.txt": "one",
6951                "2.txt": "two",
6952                "3.txt": "three",
6953            }),
6954        )
6955        .await;
6956    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
6957    active_call_a
6958        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
6959        .await
6960        .unwrap();
6961
6962    let project_id = active_call_a
6963        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6964        .await
6965        .unwrap();
6966    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6967    active_call_b
6968        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6969        .await
6970        .unwrap();
6971
6972    // Client A opens some editors.
6973    let workspace_a = client_a.build_workspace(&project_a, cx_a);
6974    let _editor_a1 = workspace_a
6975        .update(cx_a, |workspace, cx| {
6976            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6977        })
6978        .await
6979        .unwrap()
6980        .downcast::<Editor>()
6981        .unwrap();
6982
6983    // Client B starts following client A.
6984    let workspace_b = client_b.build_workspace(&project_b, cx_b);
6985    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
6986    let leader_id = project_b.read_with(cx_b, |project, _| {
6987        project.collaborators().values().next().unwrap().peer_id
6988    });
6989    workspace_b
6990        .update(cx_b, |workspace, cx| {
6991            workspace
6992                .toggle_follow(&ToggleFollow(leader_id), cx)
6993                .unwrap()
6994        })
6995        .await
6996        .unwrap();
6997    assert_eq!(
6998        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6999        Some(leader_id)
7000    );
7001    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
7002        workspace
7003            .active_item(cx)
7004            .unwrap()
7005            .downcast::<Editor>()
7006            .unwrap()
7007    });
7008
7009    // When client B moves, it automatically stops following client A.
7010    editor_b2.update(cx_b, |editor, cx| editor.move_right(&editor::MoveRight, cx));
7011    assert_eq!(
7012        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7013        None
7014    );
7015
7016    workspace_b
7017        .update(cx_b, |workspace, cx| {
7018            workspace
7019                .toggle_follow(&ToggleFollow(leader_id), cx)
7020                .unwrap()
7021        })
7022        .await
7023        .unwrap();
7024    assert_eq!(
7025        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7026        Some(leader_id)
7027    );
7028
7029    // When client B edits, it automatically stops following client A.
7030    editor_b2.update(cx_b, |editor, cx| editor.insert("X", cx));
7031    assert_eq!(
7032        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7033        None
7034    );
7035
7036    workspace_b
7037        .update(cx_b, |workspace, cx| {
7038            workspace
7039                .toggle_follow(&ToggleFollow(leader_id), cx)
7040                .unwrap()
7041        })
7042        .await
7043        .unwrap();
7044    assert_eq!(
7045        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7046        Some(leader_id)
7047    );
7048
7049    // When client B scrolls, it automatically stops following client A.
7050    editor_b2.update(cx_b, |editor, cx| {
7051        editor.set_scroll_position(vec2f(0., 3.), cx)
7052    });
7053    assert_eq!(
7054        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7055        None
7056    );
7057
7058    workspace_b
7059        .update(cx_b, |workspace, cx| {
7060            workspace
7061                .toggle_follow(&ToggleFollow(leader_id), cx)
7062                .unwrap()
7063        })
7064        .await
7065        .unwrap();
7066    assert_eq!(
7067        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7068        Some(leader_id)
7069    );
7070
7071    // When client B activates a different pane, it continues following client A in the original pane.
7072    workspace_b.update(cx_b, |workspace, cx| {
7073        workspace.split_pane(pane_b.clone(), SplitDirection::Right, cx)
7074    });
7075    assert_eq!(
7076        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7077        Some(leader_id)
7078    );
7079
7080    workspace_b.update(cx_b, |workspace, cx| workspace.activate_next_pane(cx));
7081    assert_eq!(
7082        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7083        Some(leader_id)
7084    );
7085
7086    // When client B activates a different item in the original pane, it automatically stops following client A.
7087    workspace_b
7088        .update(cx_b, |workspace, cx| {
7089            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
7090        })
7091        .await
7092        .unwrap();
7093    assert_eq!(
7094        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
7095        None
7096    );
7097}
7098
7099#[gpui::test(iterations = 10)]
7100async fn test_peers_simultaneously_following_each_other(
7101    deterministic: Arc<Deterministic>,
7102    cx_a: &mut TestAppContext,
7103    cx_b: &mut TestAppContext,
7104) {
7105    deterministic.forbid_parking();
7106    cx_a.update(editor::init);
7107    cx_b.update(editor::init);
7108
7109    let mut server = TestServer::start(&deterministic).await;
7110    let client_a = server.create_client(cx_a, "user_a").await;
7111    let client_b = server.create_client(cx_b, "user_b").await;
7112    server
7113        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
7114        .await;
7115    let active_call_a = cx_a.read(ActiveCall::global);
7116
7117    client_a.fs.insert_tree("/a", json!({})).await;
7118    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
7119    let workspace_a = client_a.build_workspace(&project_a, cx_a);
7120    let project_id = active_call_a
7121        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
7122        .await
7123        .unwrap();
7124
7125    let project_b = client_b.build_remote_project(project_id, cx_b).await;
7126    let workspace_b = client_b.build_workspace(&project_b, cx_b);
7127
7128    deterministic.run_until_parked();
7129    let client_a_id = project_b.read_with(cx_b, |project, _| {
7130        project.collaborators().values().next().unwrap().peer_id
7131    });
7132    let client_b_id = project_a.read_with(cx_a, |project, _| {
7133        project.collaborators().values().next().unwrap().peer_id
7134    });
7135
7136    let a_follow_b = workspace_a.update(cx_a, |workspace, cx| {
7137        workspace
7138            .toggle_follow(&ToggleFollow(client_b_id), cx)
7139            .unwrap()
7140    });
7141    let b_follow_a = workspace_b.update(cx_b, |workspace, cx| {
7142        workspace
7143            .toggle_follow(&ToggleFollow(client_a_id), cx)
7144            .unwrap()
7145    });
7146
7147    futures::try_join!(a_follow_b, b_follow_a).unwrap();
7148    workspace_a.read_with(cx_a, |workspace, _| {
7149        assert_eq!(
7150            workspace.leader_for_pane(workspace.active_pane()),
7151            Some(client_b_id)
7152        );
7153    });
7154    workspace_b.read_with(cx_b, |workspace, _| {
7155        assert_eq!(
7156            workspace.leader_for_pane(workspace.active_pane()),
7157            Some(client_a_id)
7158        );
7159    });
7160}
7161
7162#[derive(Debug, Eq, PartialEq)]
7163struct RoomParticipants {
7164    remote: Vec<String>,
7165    pending: Vec<String>,
7166}
7167
7168fn room_participants(room: &ModelHandle<Room>, cx: &mut TestAppContext) -> RoomParticipants {
7169    room.read_with(cx, |room, _| {
7170        let mut remote = room
7171            .remote_participants()
7172            .iter()
7173            .map(|(_, participant)| participant.user.github_login.clone())
7174            .collect::<Vec<_>>();
7175        let mut pending = room
7176            .pending_participants()
7177            .iter()
7178            .map(|user| user.github_login.clone())
7179            .collect::<Vec<_>>();
7180        remote.sort();
7181        pending.sort();
7182        RoomParticipants { remote, pending }
7183    })
7184}