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