integration_tests.rs

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