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