integration_tests.rs

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