integration_tests.rs

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