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 = cx_b.update(|cx| Project::save_buffer(buffer_b.clone(), 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    new_buffer_a.update(cx_a, |buffer, cx| {
2330        buffer.edit([(0..0, "ok")], None, cx);
2331    });
2332    project_a
2333        .update(cx_a, |project, cx| {
2334            project.save_buffer_as(new_buffer_a.clone(), "/a/file3.rs".into(), cx)
2335        })
2336        .await
2337        .unwrap();
2338
2339    deterministic.run_until_parked();
2340    new_buffer_b.read_with(cx_b, |buffer_b, _| {
2341        assert_eq!(
2342            buffer_b.file().unwrap().path().as_ref(),
2343            Path::new("file3.rs")
2344        );
2345
2346        new_buffer_a.read_with(cx_a, |buffer_a, _| {
2347            assert_eq!(buffer_b.saved_mtime(), buffer_a.saved_mtime());
2348            assert_eq!(buffer_b.saved_version(), buffer_a.saved_version());
2349        });
2350    });
2351}
2352
2353#[gpui::test(iterations = 10)]
2354async fn test_git_diff_base_change(
2355    deterministic: Arc<Deterministic>,
2356    cx_a: &mut TestAppContext,
2357    cx_b: &mut TestAppContext,
2358) {
2359    deterministic.forbid_parking();
2360    let mut server = TestServer::start(&deterministic).await;
2361    let client_a = server.create_client(cx_a, "user_a").await;
2362    let client_b = server.create_client(cx_b, "user_b").await;
2363    server
2364        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2365        .await;
2366    let active_call_a = cx_a.read(ActiveCall::global);
2367
2368    client_a
2369        .fs
2370        .insert_tree(
2371            "/dir",
2372            json!({
2373            ".git": {},
2374            "sub": {
2375                ".git": {},
2376                "b.txt": "
2377                    one
2378                    two
2379                    three
2380                ".unindent(),
2381            },
2382            "a.txt": "
2383                    one
2384                    two
2385                    three
2386                ".unindent(),
2387            }),
2388        )
2389        .await;
2390
2391    let (project_local, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2392    let project_id = active_call_a
2393        .update(cx_a, |call, cx| {
2394            call.share_project(project_local.clone(), cx)
2395        })
2396        .await
2397        .unwrap();
2398
2399    let project_remote = client_b.build_remote_project(project_id, cx_b).await;
2400
2401    let diff_base = "
2402        one
2403        three
2404    "
2405    .unindent();
2406
2407    let new_diff_base = "
2408        one
2409        two
2410    "
2411    .unindent();
2412
2413    client_a
2414        .fs
2415        .as_fake()
2416        .set_index_for_repo(
2417            Path::new("/dir/.git"),
2418            &[(Path::new("a.txt"), diff_base.clone())],
2419        )
2420        .await;
2421
2422    // Create the buffer
2423    let buffer_local_a = project_local
2424        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2425        .await
2426        .unwrap();
2427
2428    // Wait for it to catch up to the new diff
2429    deterministic.run_until_parked();
2430
2431    // Smoke test diffing
2432    buffer_local_a.read_with(cx_a, |buffer, _| {
2433        assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
2434        git::diff::assert_hunks(
2435            buffer.snapshot().git_diff_hunks_in_row_range(0..4, false),
2436            &buffer,
2437            &diff_base,
2438            &[(1..2, "", "two\n")],
2439        );
2440    });
2441
2442    // Create remote buffer
2443    let buffer_remote_a = project_remote
2444        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2445        .await
2446        .unwrap();
2447
2448    // Wait remote buffer to catch up to the new diff
2449    deterministic.run_until_parked();
2450
2451    // Smoke test diffing
2452    buffer_remote_a.read_with(cx_b, |buffer, _| {
2453        assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
2454        git::diff::assert_hunks(
2455            buffer.snapshot().git_diff_hunks_in_row_range(0..4, false),
2456            &buffer,
2457            &diff_base,
2458            &[(1..2, "", "two\n")],
2459        );
2460    });
2461
2462    client_a
2463        .fs
2464        .as_fake()
2465        .set_index_for_repo(
2466            Path::new("/dir/.git"),
2467            &[(Path::new("a.txt"), new_diff_base.clone())],
2468        )
2469        .await;
2470
2471    // Wait for buffer_local_a to receive it
2472    deterministic.run_until_parked();
2473
2474    // Smoke test new diffing
2475    buffer_local_a.read_with(cx_a, |buffer, _| {
2476        assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
2477
2478        git::diff::assert_hunks(
2479            buffer.snapshot().git_diff_hunks_in_row_range(0..4, false),
2480            &buffer,
2481            &diff_base,
2482            &[(2..3, "", "three\n")],
2483        );
2484    });
2485
2486    // Smoke test B
2487    buffer_remote_a.read_with(cx_b, |buffer, _| {
2488        assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
2489        git::diff::assert_hunks(
2490            buffer.snapshot().git_diff_hunks_in_row_range(0..4, false),
2491            &buffer,
2492            &diff_base,
2493            &[(2..3, "", "three\n")],
2494        );
2495    });
2496
2497    //Nested git dir
2498
2499    let diff_base = "
2500        one
2501        three
2502    "
2503    .unindent();
2504
2505    let new_diff_base = "
2506        one
2507        two
2508    "
2509    .unindent();
2510
2511    client_a
2512        .fs
2513        .as_fake()
2514        .set_index_for_repo(
2515            Path::new("/dir/sub/.git"),
2516            &[(Path::new("b.txt"), diff_base.clone())],
2517        )
2518        .await;
2519
2520    // Create the buffer
2521    let buffer_local_b = project_local
2522        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "sub/b.txt"), cx))
2523        .await
2524        .unwrap();
2525
2526    // Wait for it to catch up to the new diff
2527    deterministic.run_until_parked();
2528
2529    // Smoke test diffing
2530    buffer_local_b.read_with(cx_a, |buffer, _| {
2531        assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
2532        git::diff::assert_hunks(
2533            buffer.snapshot().git_diff_hunks_in_row_range(0..4, false),
2534            &buffer,
2535            &diff_base,
2536            &[(1..2, "", "two\n")],
2537        );
2538    });
2539
2540    // Create remote buffer
2541    let buffer_remote_b = project_remote
2542        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "sub/b.txt"), cx))
2543        .await
2544        .unwrap();
2545
2546    // Wait remote buffer to catch up to the new diff
2547    deterministic.run_until_parked();
2548
2549    // Smoke test diffing
2550    buffer_remote_b.read_with(cx_b, |buffer, _| {
2551        assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
2552        git::diff::assert_hunks(
2553            buffer.snapshot().git_diff_hunks_in_row_range(0..4, false),
2554            &buffer,
2555            &diff_base,
2556            &[(1..2, "", "two\n")],
2557        );
2558    });
2559
2560    client_a
2561        .fs
2562        .as_fake()
2563        .set_index_for_repo(
2564            Path::new("/dir/sub/.git"),
2565            &[(Path::new("b.txt"), new_diff_base.clone())],
2566        )
2567        .await;
2568
2569    // Wait for buffer_local_b to receive it
2570    deterministic.run_until_parked();
2571
2572    // Smoke test new diffing
2573    buffer_local_b.read_with(cx_a, |buffer, _| {
2574        assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
2575        println!("{:?}", buffer.as_rope().to_string());
2576        println!("{:?}", buffer.diff_base());
2577        println!(
2578            "{:?}",
2579            buffer
2580                .snapshot()
2581                .git_diff_hunks_in_row_range(0..4, false)
2582                .collect::<Vec<_>>()
2583        );
2584
2585        git::diff::assert_hunks(
2586            buffer.snapshot().git_diff_hunks_in_row_range(0..4, false),
2587            &buffer,
2588            &diff_base,
2589            &[(2..3, "", "three\n")],
2590        );
2591    });
2592
2593    // Smoke test B
2594    buffer_remote_b.read_with(cx_b, |buffer, _| {
2595        assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
2596        git::diff::assert_hunks(
2597            buffer.snapshot().git_diff_hunks_in_row_range(0..4, false),
2598            &buffer,
2599            &diff_base,
2600            &[(2..3, "", "three\n")],
2601        );
2602    });
2603}
2604
2605#[gpui::test(iterations = 10)]
2606async fn test_fs_operations(
2607    deterministic: Arc<Deterministic>,
2608    cx_a: &mut TestAppContext,
2609    cx_b: &mut TestAppContext,
2610) {
2611    deterministic.forbid_parking();
2612    let mut server = TestServer::start(&deterministic).await;
2613    let client_a = server.create_client(cx_a, "user_a").await;
2614    let client_b = server.create_client(cx_b, "user_b").await;
2615    server
2616        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2617        .await;
2618    let active_call_a = cx_a.read(ActiveCall::global);
2619
2620    client_a
2621        .fs
2622        .insert_tree(
2623            "/dir",
2624            json!({
2625                "a.txt": "a-contents",
2626                "b.txt": "b-contents",
2627            }),
2628        )
2629        .await;
2630    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2631    let project_id = active_call_a
2632        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2633        .await
2634        .unwrap();
2635    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2636
2637    let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
2638    let worktree_b = project_b.read_with(cx_b, |project, cx| project.worktrees(cx).next().unwrap());
2639
2640    let entry = project_b
2641        .update(cx_b, |project, cx| {
2642            project
2643                .create_entry((worktree_id, "c.txt"), false, cx)
2644                .unwrap()
2645        })
2646        .await
2647        .unwrap();
2648    worktree_a.read_with(cx_a, |worktree, _| {
2649        assert_eq!(
2650            worktree
2651                .paths()
2652                .map(|p| p.to_string_lossy())
2653                .collect::<Vec<_>>(),
2654            ["a.txt", "b.txt", "c.txt"]
2655        );
2656    });
2657    worktree_b.read_with(cx_b, |worktree, _| {
2658        assert_eq!(
2659            worktree
2660                .paths()
2661                .map(|p| p.to_string_lossy())
2662                .collect::<Vec<_>>(),
2663            ["a.txt", "b.txt", "c.txt"]
2664        );
2665    });
2666
2667    project_b
2668        .update(cx_b, |project, cx| {
2669            project.rename_entry(entry.id, Path::new("d.txt"), cx)
2670        })
2671        .unwrap()
2672        .await
2673        .unwrap();
2674    worktree_a.read_with(cx_a, |worktree, _| {
2675        assert_eq!(
2676            worktree
2677                .paths()
2678                .map(|p| p.to_string_lossy())
2679                .collect::<Vec<_>>(),
2680            ["a.txt", "b.txt", "d.txt"]
2681        );
2682    });
2683    worktree_b.read_with(cx_b, |worktree, _| {
2684        assert_eq!(
2685            worktree
2686                .paths()
2687                .map(|p| p.to_string_lossy())
2688                .collect::<Vec<_>>(),
2689            ["a.txt", "b.txt", "d.txt"]
2690        );
2691    });
2692
2693    let dir_entry = project_b
2694        .update(cx_b, |project, cx| {
2695            project
2696                .create_entry((worktree_id, "DIR"), true, cx)
2697                .unwrap()
2698        })
2699        .await
2700        .unwrap();
2701    worktree_a.read_with(cx_a, |worktree, _| {
2702        assert_eq!(
2703            worktree
2704                .paths()
2705                .map(|p| p.to_string_lossy())
2706                .collect::<Vec<_>>(),
2707            ["DIR", "a.txt", "b.txt", "d.txt"]
2708        );
2709    });
2710    worktree_b.read_with(cx_b, |worktree, _| {
2711        assert_eq!(
2712            worktree
2713                .paths()
2714                .map(|p| p.to_string_lossy())
2715                .collect::<Vec<_>>(),
2716            ["DIR", "a.txt", "b.txt", "d.txt"]
2717        );
2718    });
2719
2720    project_b
2721        .update(cx_b, |project, cx| {
2722            project
2723                .create_entry((worktree_id, "DIR/e.txt"), false, 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"), true, cx)
2732                .unwrap()
2733        })
2734        .await
2735        .unwrap();
2736    project_b
2737        .update(cx_b, |project, cx| {
2738            project
2739                .create_entry((worktree_id, "DIR/SUBDIR/f.txt"), false, cx)
2740                .unwrap()
2741        })
2742        .await
2743        .unwrap();
2744    worktree_a.read_with(cx_a, |worktree, _| {
2745        assert_eq!(
2746            worktree
2747                .paths()
2748                .map(|p| p.to_string_lossy())
2749                .collect::<Vec<_>>(),
2750            [
2751                "DIR",
2752                "DIR/SUBDIR",
2753                "DIR/SUBDIR/f.txt",
2754                "DIR/e.txt",
2755                "a.txt",
2756                "b.txt",
2757                "d.txt"
2758            ]
2759        );
2760    });
2761    worktree_b.read_with(cx_b, |worktree, _| {
2762        assert_eq!(
2763            worktree
2764                .paths()
2765                .map(|p| p.to_string_lossy())
2766                .collect::<Vec<_>>(),
2767            [
2768                "DIR",
2769                "DIR/SUBDIR",
2770                "DIR/SUBDIR/f.txt",
2771                "DIR/e.txt",
2772                "a.txt",
2773                "b.txt",
2774                "d.txt"
2775            ]
2776        );
2777    });
2778
2779    project_b
2780        .update(cx_b, |project, cx| {
2781            project
2782                .copy_entry(entry.id, Path::new("f.txt"), cx)
2783                .unwrap()
2784        })
2785        .await
2786        .unwrap();
2787    worktree_a.read_with(cx_a, |worktree, _| {
2788        assert_eq!(
2789            worktree
2790                .paths()
2791                .map(|p| p.to_string_lossy())
2792                .collect::<Vec<_>>(),
2793            [
2794                "DIR",
2795                "DIR/SUBDIR",
2796                "DIR/SUBDIR/f.txt",
2797                "DIR/e.txt",
2798                "a.txt",
2799                "b.txt",
2800                "d.txt",
2801                "f.txt"
2802            ]
2803        );
2804    });
2805    worktree_b.read_with(cx_b, |worktree, _| {
2806        assert_eq!(
2807            worktree
2808                .paths()
2809                .map(|p| p.to_string_lossy())
2810                .collect::<Vec<_>>(),
2811            [
2812                "DIR",
2813                "DIR/SUBDIR",
2814                "DIR/SUBDIR/f.txt",
2815                "DIR/e.txt",
2816                "a.txt",
2817                "b.txt",
2818                "d.txt",
2819                "f.txt"
2820            ]
2821        );
2822    });
2823
2824    project_b
2825        .update(cx_b, |project, cx| {
2826            project.delete_entry(dir_entry.id, cx).unwrap()
2827        })
2828        .await
2829        .unwrap();
2830    deterministic.run_until_parked();
2831
2832    worktree_a.read_with(cx_a, |worktree, _| {
2833        assert_eq!(
2834            worktree
2835                .paths()
2836                .map(|p| p.to_string_lossy())
2837                .collect::<Vec<_>>(),
2838            ["a.txt", "b.txt", "d.txt", "f.txt"]
2839        );
2840    });
2841    worktree_b.read_with(cx_b, |worktree, _| {
2842        assert_eq!(
2843            worktree
2844                .paths()
2845                .map(|p| p.to_string_lossy())
2846                .collect::<Vec<_>>(),
2847            ["a.txt", "b.txt", "d.txt", "f.txt"]
2848        );
2849    });
2850
2851    project_b
2852        .update(cx_b, |project, cx| {
2853            project.delete_entry(entry.id, cx).unwrap()
2854        })
2855        .await
2856        .unwrap();
2857    worktree_a.read_with(cx_a, |worktree, _| {
2858        assert_eq!(
2859            worktree
2860                .paths()
2861                .map(|p| p.to_string_lossy())
2862                .collect::<Vec<_>>(),
2863            ["a.txt", "b.txt", "f.txt"]
2864        );
2865    });
2866    worktree_b.read_with(cx_b, |worktree, _| {
2867        assert_eq!(
2868            worktree
2869                .paths()
2870                .map(|p| p.to_string_lossy())
2871                .collect::<Vec<_>>(),
2872            ["a.txt", "b.txt", "f.txt"]
2873        );
2874    });
2875}
2876
2877#[gpui::test(iterations = 10)]
2878async fn test_buffer_conflict_after_save(
2879    deterministic: Arc<Deterministic>,
2880    cx_a: &mut TestAppContext,
2881    cx_b: &mut TestAppContext,
2882) {
2883    deterministic.forbid_parking();
2884    let mut server = TestServer::start(&deterministic).await;
2885    let client_a = server.create_client(cx_a, "user_a").await;
2886    let client_b = server.create_client(cx_b, "user_b").await;
2887    server
2888        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2889        .await;
2890    let active_call_a = cx_a.read(ActiveCall::global);
2891
2892    client_a
2893        .fs
2894        .insert_tree(
2895            "/dir",
2896            json!({
2897                "a.txt": "a-contents",
2898            }),
2899        )
2900        .await;
2901    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2902    let project_id = active_call_a
2903        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2904        .await
2905        .unwrap();
2906    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2907
2908    // Open a buffer as client B
2909    let buffer_b = project_b
2910        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2911        .await
2912        .unwrap();
2913
2914    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "world ")], None, cx));
2915    buffer_b.read_with(cx_b, |buf, _| {
2916        assert!(buf.is_dirty());
2917        assert!(!buf.has_conflict());
2918    });
2919
2920    cx_b.update(|cx| Project::save_buffer(buffer_b.clone(), cx))
2921        .await
2922        .unwrap();
2923    cx_a.foreground().forbid_parking();
2924    buffer_b.read_with(cx_b, |buffer_b, _| assert!(!buffer_b.is_dirty()));
2925    buffer_b.read_with(cx_b, |buf, _| {
2926        assert!(!buf.has_conflict());
2927    });
2928
2929    buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "hello ")], None, cx));
2930    buffer_b.read_with(cx_b, |buf, _| {
2931        assert!(buf.is_dirty());
2932        assert!(!buf.has_conflict());
2933    });
2934}
2935
2936#[gpui::test(iterations = 10)]
2937async fn test_buffer_reloading(
2938    deterministic: Arc<Deterministic>,
2939    cx_a: &mut TestAppContext,
2940    cx_b: &mut TestAppContext,
2941) {
2942    deterministic.forbid_parking();
2943    let mut server = TestServer::start(&deterministic).await;
2944    let client_a = server.create_client(cx_a, "user_a").await;
2945    let client_b = server.create_client(cx_b, "user_b").await;
2946    server
2947        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
2948        .await;
2949    let active_call_a = cx_a.read(ActiveCall::global);
2950
2951    client_a
2952        .fs
2953        .insert_tree(
2954            "/dir",
2955            json!({
2956                "a.txt": "a\nb\nc",
2957            }),
2958        )
2959        .await;
2960    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
2961    let project_id = active_call_a
2962        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
2963        .await
2964        .unwrap();
2965    let project_b = client_b.build_remote_project(project_id, cx_b).await;
2966
2967    // Open a buffer as client B
2968    let buffer_b = project_b
2969        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2970        .await
2971        .unwrap();
2972    buffer_b.read_with(cx_b, |buf, _| {
2973        assert!(!buf.is_dirty());
2974        assert!(!buf.has_conflict());
2975        assert_eq!(buf.line_ending(), LineEnding::Unix);
2976    });
2977
2978    let new_contents = Rope::from("d\ne\nf");
2979    client_a
2980        .fs
2981        .save("/dir/a.txt".as_ref(), &new_contents, LineEnding::Windows)
2982        .await
2983        .unwrap();
2984    cx_a.foreground().run_until_parked();
2985    buffer_b.read_with(cx_b, |buf, _| {
2986        assert_eq!(buf.text(), new_contents.to_string());
2987        assert!(!buf.is_dirty());
2988        assert!(!buf.has_conflict());
2989        assert_eq!(buf.line_ending(), LineEnding::Windows);
2990    });
2991}
2992
2993#[gpui::test(iterations = 10)]
2994async fn test_editing_while_guest_opens_buffer(
2995    deterministic: Arc<Deterministic>,
2996    cx_a: &mut TestAppContext,
2997    cx_b: &mut TestAppContext,
2998) {
2999    deterministic.forbid_parking();
3000    let mut server = TestServer::start(&deterministic).await;
3001    let client_a = server.create_client(cx_a, "user_a").await;
3002    let client_b = server.create_client(cx_b, "user_b").await;
3003    server
3004        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3005        .await;
3006    let active_call_a = cx_a.read(ActiveCall::global);
3007
3008    client_a
3009        .fs
3010        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3011        .await;
3012    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3013    let project_id = active_call_a
3014        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3015        .await
3016        .unwrap();
3017    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3018
3019    // Open a buffer as client A
3020    let buffer_a = project_a
3021        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3022        .await
3023        .unwrap();
3024
3025    // Start opening the same buffer as client B
3026    let buffer_b = cx_b
3027        .background()
3028        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)));
3029
3030    // Edit the buffer as client A while client B is still opening it.
3031    cx_b.background().simulate_random_delay().await;
3032    buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "X")], None, cx));
3033    cx_b.background().simulate_random_delay().await;
3034    buffer_a.update(cx_a, |buf, cx| buf.edit([(1..1, "Y")], None, cx));
3035
3036    let text = buffer_a.read_with(cx_a, |buf, _| buf.text());
3037    let buffer_b = buffer_b.await.unwrap();
3038    cx_a.foreground().run_until_parked();
3039    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), text));
3040}
3041
3042#[gpui::test(iterations = 10)]
3043async fn test_leaving_worktree_while_opening_buffer(
3044    deterministic: Arc<Deterministic>,
3045    cx_a: &mut TestAppContext,
3046    cx_b: &mut TestAppContext,
3047) {
3048    deterministic.forbid_parking();
3049    let mut server = TestServer::start(&deterministic).await;
3050    let client_a = server.create_client(cx_a, "user_a").await;
3051    let client_b = server.create_client(cx_b, "user_b").await;
3052    server
3053        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3054        .await;
3055    let active_call_a = cx_a.read(ActiveCall::global);
3056
3057    client_a
3058        .fs
3059        .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
3060        .await;
3061    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3062    let project_id = active_call_a
3063        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3064        .await
3065        .unwrap();
3066    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3067
3068    // See that a guest has joined as client A.
3069    cx_a.foreground().run_until_parked();
3070    project_a.read_with(cx_a, |p, _| assert_eq!(p.collaborators().len(), 1));
3071
3072    // Begin opening a buffer as client B, but leave the project before the open completes.
3073    let buffer_b = cx_b
3074        .background()
3075        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)));
3076    cx_b.update(|_| drop(project_b));
3077    drop(buffer_b);
3078
3079    // See that the guest has left.
3080    cx_a.foreground().run_until_parked();
3081    project_a.read_with(cx_a, |p, _| assert!(p.collaborators().is_empty()));
3082}
3083
3084#[gpui::test(iterations = 10)]
3085async fn test_canceling_buffer_opening(
3086    deterministic: Arc<Deterministic>,
3087    cx_a: &mut TestAppContext,
3088    cx_b: &mut TestAppContext,
3089) {
3090    deterministic.forbid_parking();
3091
3092    let mut server = TestServer::start(&deterministic).await;
3093    let client_a = server.create_client(cx_a, "user_a").await;
3094    let client_b = server.create_client(cx_b, "user_b").await;
3095    server
3096        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3097        .await;
3098    let active_call_a = cx_a.read(ActiveCall::global);
3099
3100    client_a
3101        .fs
3102        .insert_tree(
3103            "/dir",
3104            json!({
3105                "a.txt": "abc",
3106            }),
3107        )
3108        .await;
3109    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
3110    let project_id = active_call_a
3111        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3112        .await
3113        .unwrap();
3114    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3115
3116    let buffer_a = project_a
3117        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3118        .await
3119        .unwrap();
3120
3121    // Open a buffer as client B but cancel after a random amount of time.
3122    let buffer_b = project_b.update(cx_b, |p, cx| p.open_buffer_by_id(buffer_a.id() as u64, cx));
3123    deterministic.simulate_random_delay().await;
3124    drop(buffer_b);
3125
3126    // Try opening the same buffer again as client B, and ensure we can
3127    // still do it despite the cancellation above.
3128    let buffer_b = project_b
3129        .update(cx_b, |p, cx| p.open_buffer_by_id(buffer_a.id() as u64, cx))
3130        .await
3131        .unwrap();
3132    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "abc"));
3133}
3134
3135#[gpui::test(iterations = 10)]
3136async fn test_leaving_project(
3137    deterministic: Arc<Deterministic>,
3138    cx_a: &mut TestAppContext,
3139    cx_b: &mut TestAppContext,
3140    cx_c: &mut TestAppContext,
3141) {
3142    deterministic.forbid_parking();
3143    let mut server = TestServer::start(&deterministic).await;
3144    let client_a = server.create_client(cx_a, "user_a").await;
3145    let client_b = server.create_client(cx_b, "user_b").await;
3146    let client_c = server.create_client(cx_c, "user_c").await;
3147    server
3148        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3149        .await;
3150    let active_call_a = cx_a.read(ActiveCall::global);
3151
3152    client_a
3153        .fs
3154        .insert_tree(
3155            "/a",
3156            json!({
3157                "a.txt": "a-contents",
3158                "b.txt": "b-contents",
3159            }),
3160        )
3161        .await;
3162    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
3163    let project_id = active_call_a
3164        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3165        .await
3166        .unwrap();
3167    let project_b1 = client_b.build_remote_project(project_id, cx_b).await;
3168    let project_c = client_c.build_remote_project(project_id, cx_c).await;
3169
3170    // Client A sees that a guest has joined.
3171    deterministic.run_until_parked();
3172    project_a.read_with(cx_a, |project, _| {
3173        assert_eq!(project.collaborators().len(), 2);
3174    });
3175    project_b1.read_with(cx_b, |project, _| {
3176        assert_eq!(project.collaborators().len(), 2);
3177    });
3178    project_c.read_with(cx_c, |project, _| {
3179        assert_eq!(project.collaborators().len(), 2);
3180    });
3181
3182    // Client B opens a buffer.
3183    let buffer_b1 = project_b1
3184        .update(cx_b, |project, cx| {
3185            let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
3186            project.open_buffer((worktree_id, "a.txt"), cx)
3187        })
3188        .await
3189        .unwrap();
3190    buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3191
3192    // Drop client B's project and ensure client A and client C observe client B leaving.
3193    cx_b.update(|_| drop(project_b1));
3194    deterministic.run_until_parked();
3195    project_a.read_with(cx_a, |project, _| {
3196        assert_eq!(project.collaborators().len(), 1);
3197    });
3198    project_c.read_with(cx_c, |project, _| {
3199        assert_eq!(project.collaborators().len(), 1);
3200    });
3201
3202    // Client B re-joins the project and can open buffers as before.
3203    let project_b2 = client_b.build_remote_project(project_id, cx_b).await;
3204    deterministic.run_until_parked();
3205    project_a.read_with(cx_a, |project, _| {
3206        assert_eq!(project.collaborators().len(), 2);
3207    });
3208    project_b2.read_with(cx_b, |project, _| {
3209        assert_eq!(project.collaborators().len(), 2);
3210    });
3211    project_c.read_with(cx_c, |project, _| {
3212        assert_eq!(project.collaborators().len(), 2);
3213    });
3214
3215    let buffer_b2 = project_b2
3216        .update(cx_b, |project, cx| {
3217            let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
3218            project.open_buffer((worktree_id, "a.txt"), cx)
3219        })
3220        .await
3221        .unwrap();
3222    buffer_b2.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents"));
3223
3224    // Drop client B's connection and ensure client A and client C observe client B leaving.
3225    client_b.disconnect(&cx_b.to_async()).unwrap();
3226    deterministic.advance_clock(RECONNECT_TIMEOUT);
3227    project_a.read_with(cx_a, |project, _| {
3228        assert_eq!(project.collaborators().len(), 1);
3229    });
3230    project_b2.read_with(cx_b, |project, _| {
3231        assert!(project.is_read_only());
3232    });
3233    project_c.read_with(cx_c, |project, _| {
3234        assert_eq!(project.collaborators().len(), 1);
3235    });
3236
3237    // Client B can't join the project, unless they re-join the room.
3238    cx_b.spawn(|cx| {
3239        Project::remote(
3240            project_id,
3241            client_b.client.clone(),
3242            client_b.user_store.clone(),
3243            client_b.language_registry.clone(),
3244            FakeFs::new(cx.background()),
3245            cx,
3246        )
3247    })
3248    .await
3249    .unwrap_err();
3250
3251    // Simulate connection loss for client C and ensure client A observes client C leaving the project.
3252    client_c.wait_for_current_user(cx_c).await;
3253    server.forbid_connections();
3254    server.disconnect_client(client_c.peer_id().unwrap());
3255    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
3256    deterministic.run_until_parked();
3257    project_a.read_with(cx_a, |project, _| {
3258        assert_eq!(project.collaborators().len(), 0);
3259    });
3260    project_b2.read_with(cx_b, |project, _| {
3261        assert!(project.is_read_only());
3262    });
3263    project_c.read_with(cx_c, |project, _| {
3264        assert!(project.is_read_only());
3265    });
3266}
3267
3268#[gpui::test(iterations = 10)]
3269async fn test_collaborating_with_diagnostics(
3270    deterministic: Arc<Deterministic>,
3271    cx_a: &mut TestAppContext,
3272    cx_b: &mut TestAppContext,
3273    cx_c: &mut TestAppContext,
3274) {
3275    deterministic.forbid_parking();
3276    let mut server = TestServer::start(&deterministic).await;
3277    let client_a = server.create_client(cx_a, "user_a").await;
3278    let client_b = server.create_client(cx_b, "user_b").await;
3279    let client_c = server.create_client(cx_c, "user_c").await;
3280    server
3281        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
3282        .await;
3283    let active_call_a = cx_a.read(ActiveCall::global);
3284
3285    // Set up a fake language server.
3286    let mut language = Language::new(
3287        LanguageConfig {
3288            name: "Rust".into(),
3289            path_suffixes: vec!["rs".to_string()],
3290            ..Default::default()
3291        },
3292        Some(tree_sitter_rust::language()),
3293    );
3294    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
3295    client_a.language_registry.add(Arc::new(language));
3296
3297    // Share a project as client A
3298    client_a
3299        .fs
3300        .insert_tree(
3301            "/a",
3302            json!({
3303                "a.rs": "let one = two",
3304                "other.rs": "",
3305            }),
3306        )
3307        .await;
3308    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3309
3310    // Cause the language server to start.
3311    let _buffer = project_a
3312        .update(cx_a, |project, cx| {
3313            project.open_buffer(
3314                ProjectPath {
3315                    worktree_id,
3316                    path: Path::new("other.rs").into(),
3317                },
3318                cx,
3319            )
3320        })
3321        .await
3322        .unwrap();
3323
3324    // Simulate a language server reporting errors for a file.
3325    let mut fake_language_server = fake_language_servers.next().await.unwrap();
3326    fake_language_server
3327        .receive_notification::<lsp::notification::DidOpenTextDocument>()
3328        .await;
3329    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3330        lsp::PublishDiagnosticsParams {
3331            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3332            version: None,
3333            diagnostics: vec![lsp::Diagnostic {
3334                severity: Some(lsp::DiagnosticSeverity::WARNING),
3335                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3336                message: "message 0".to_string(),
3337                ..Default::default()
3338            }],
3339        },
3340    );
3341
3342    // Client A shares the project and, simultaneously, the language server
3343    // publishes a diagnostic. This is done to ensure that the server always
3344    // observes the latest diagnostics for a worktree.
3345    let project_id = active_call_a
3346        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3347        .await
3348        .unwrap();
3349    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3350        lsp::PublishDiagnosticsParams {
3351            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3352            version: None,
3353            diagnostics: vec![lsp::Diagnostic {
3354                severity: Some(lsp::DiagnosticSeverity::ERROR),
3355                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3356                message: "message 1".to_string(),
3357                ..Default::default()
3358            }],
3359        },
3360    );
3361
3362    // Join the worktree as client B.
3363    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3364
3365    // Wait for server to see the diagnostics update.
3366    deterministic.run_until_parked();
3367
3368    // Ensure client B observes the new diagnostics.
3369    project_b.read_with(cx_b, |project, cx| {
3370        assert_eq!(
3371            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
3372            &[(
3373                ProjectPath {
3374                    worktree_id,
3375                    path: Arc::from(Path::new("a.rs")),
3376                },
3377                DiagnosticSummary {
3378                    error_count: 1,
3379                    warning_count: 0,
3380                    ..Default::default()
3381                },
3382            )]
3383        )
3384    });
3385
3386    // Join project as client C and observe the diagnostics.
3387    let project_c = client_c.build_remote_project(project_id, cx_c).await;
3388    let project_c_diagnostic_summaries =
3389        Rc::new(RefCell::new(project_c.read_with(cx_c, |project, cx| {
3390            project.diagnostic_summaries(cx).collect::<Vec<_>>()
3391        })));
3392    project_c.update(cx_c, |_, cx| {
3393        let summaries = project_c_diagnostic_summaries.clone();
3394        cx.subscribe(&project_c, {
3395            move |p, _, event, cx| {
3396                if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
3397                    *summaries.borrow_mut() = p.diagnostic_summaries(cx).collect();
3398                }
3399            }
3400        })
3401        .detach();
3402    });
3403
3404    deterministic.run_until_parked();
3405    assert_eq!(
3406        project_c_diagnostic_summaries.borrow().as_slice(),
3407        &[(
3408            ProjectPath {
3409                worktree_id,
3410                path: Arc::from(Path::new("a.rs")),
3411            },
3412            DiagnosticSummary {
3413                error_count: 1,
3414                warning_count: 0,
3415                ..Default::default()
3416            },
3417        )]
3418    );
3419
3420    // Simulate a language server reporting more errors for a file.
3421    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3422        lsp::PublishDiagnosticsParams {
3423            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3424            version: None,
3425            diagnostics: vec![
3426                lsp::Diagnostic {
3427                    severity: Some(lsp::DiagnosticSeverity::ERROR),
3428                    range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
3429                    message: "message 1".to_string(),
3430                    ..Default::default()
3431                },
3432                lsp::Diagnostic {
3433                    severity: Some(lsp::DiagnosticSeverity::WARNING),
3434                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 13)),
3435                    message: "message 2".to_string(),
3436                    ..Default::default()
3437                },
3438            ],
3439        },
3440    );
3441
3442    // Clients B and C get the updated summaries
3443    deterministic.run_until_parked();
3444    project_b.read_with(cx_b, |project, cx| {
3445        assert_eq!(
3446            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
3447            [(
3448                ProjectPath {
3449                    worktree_id,
3450                    path: Arc::from(Path::new("a.rs")),
3451                },
3452                DiagnosticSummary {
3453                    error_count: 1,
3454                    warning_count: 1,
3455                    ..Default::default()
3456                },
3457            )]
3458        );
3459    });
3460    project_c.read_with(cx_c, |project, cx| {
3461        assert_eq!(
3462            project.diagnostic_summaries(cx).collect::<Vec<_>>(),
3463            [(
3464                ProjectPath {
3465                    worktree_id,
3466                    path: Arc::from(Path::new("a.rs")),
3467                },
3468                DiagnosticSummary {
3469                    error_count: 1,
3470                    warning_count: 1,
3471                    ..Default::default()
3472                },
3473            )]
3474        );
3475    });
3476
3477    // Open the file with the errors on client B. They should be present.
3478    let buffer_b = cx_b
3479        .background()
3480        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
3481        .await
3482        .unwrap();
3483
3484    buffer_b.read_with(cx_b, |buffer, _| {
3485        assert_eq!(
3486            buffer
3487                .snapshot()
3488                .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
3489                .collect::<Vec<_>>(),
3490            &[
3491                DiagnosticEntry {
3492                    range: Point::new(0, 4)..Point::new(0, 7),
3493                    diagnostic: Diagnostic {
3494                        group_id: 2,
3495                        message: "message 1".to_string(),
3496                        severity: lsp::DiagnosticSeverity::ERROR,
3497                        is_primary: true,
3498                        ..Default::default()
3499                    }
3500                },
3501                DiagnosticEntry {
3502                    range: Point::new(0, 10)..Point::new(0, 13),
3503                    diagnostic: Diagnostic {
3504                        group_id: 3,
3505                        severity: lsp::DiagnosticSeverity::WARNING,
3506                        message: "message 2".to_string(),
3507                        is_primary: true,
3508                        ..Default::default()
3509                    }
3510                }
3511            ]
3512        );
3513    });
3514
3515    // Simulate a language server reporting no errors for a file.
3516    fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
3517        lsp::PublishDiagnosticsParams {
3518            uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
3519            version: None,
3520            diagnostics: vec![],
3521        },
3522    );
3523    deterministic.run_until_parked();
3524    project_a.read_with(cx_a, |project, cx| {
3525        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
3526    });
3527    project_b.read_with(cx_b, |project, cx| {
3528        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
3529    });
3530    project_c.read_with(cx_c, |project, cx| {
3531        assert_eq!(project.diagnostic_summaries(cx).collect::<Vec<_>>(), [])
3532    });
3533}
3534
3535#[gpui::test(iterations = 10)]
3536async fn test_collaborating_with_completion(
3537    deterministic: Arc<Deterministic>,
3538    cx_a: &mut TestAppContext,
3539    cx_b: &mut TestAppContext,
3540) {
3541    deterministic.forbid_parking();
3542    let mut server = TestServer::start(&deterministic).await;
3543    let client_a = server.create_client(cx_a, "user_a").await;
3544    let client_b = server.create_client(cx_b, "user_b").await;
3545    server
3546        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3547        .await;
3548    let active_call_a = cx_a.read(ActiveCall::global);
3549
3550    // Set up a fake language server.
3551    let mut language = Language::new(
3552        LanguageConfig {
3553            name: "Rust".into(),
3554            path_suffixes: vec!["rs".to_string()],
3555            ..Default::default()
3556        },
3557        Some(tree_sitter_rust::language()),
3558    );
3559    let mut fake_language_servers = language
3560        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3561            capabilities: lsp::ServerCapabilities {
3562                completion_provider: Some(lsp::CompletionOptions {
3563                    trigger_characters: Some(vec![".".to_string()]),
3564                    ..Default::default()
3565                }),
3566                ..Default::default()
3567            },
3568            ..Default::default()
3569        }))
3570        .await;
3571    client_a.language_registry.add(Arc::new(language));
3572
3573    client_a
3574        .fs
3575        .insert_tree(
3576            "/a",
3577            json!({
3578                "main.rs": "fn main() { a }",
3579                "other.rs": "",
3580            }),
3581        )
3582        .await;
3583    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3584    let project_id = active_call_a
3585        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3586        .await
3587        .unwrap();
3588    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3589
3590    // Open a file in an editor as the guest.
3591    let buffer_b = project_b
3592        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
3593        .await
3594        .unwrap();
3595    let (_, window_b) = cx_b.add_window(|_| EmptyView);
3596    let editor_b = cx_b.add_view(&window_b, |cx| {
3597        Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx)
3598    });
3599
3600    let fake_language_server = fake_language_servers.next().await.unwrap();
3601    cx_a.foreground().run_until_parked();
3602    buffer_b.read_with(cx_b, |buffer, _| {
3603        assert!(!buffer.completion_triggers().is_empty())
3604    });
3605
3606    // Type a completion trigger character as the guest.
3607    editor_b.update(cx_b, |editor, cx| {
3608        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
3609        editor.handle_input(".", cx);
3610        cx.focus(&editor_b);
3611    });
3612
3613    // Receive a completion request as the host's language server.
3614    // Return some completions from the host's language server.
3615    cx_a.foreground().start_waiting();
3616    fake_language_server
3617        .handle_request::<lsp::request::Completion, _, _>(|params, _| async move {
3618            assert_eq!(
3619                params.text_document_position.text_document.uri,
3620                lsp::Url::from_file_path("/a/main.rs").unwrap(),
3621            );
3622            assert_eq!(
3623                params.text_document_position.position,
3624                lsp::Position::new(0, 14),
3625            );
3626
3627            Ok(Some(lsp::CompletionResponse::Array(vec![
3628                lsp::CompletionItem {
3629                    label: "first_method(…)".into(),
3630                    detail: Some("fn(&mut self, B) -> C".into()),
3631                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
3632                        new_text: "first_method($1)".to_string(),
3633                        range: lsp::Range::new(
3634                            lsp::Position::new(0, 14),
3635                            lsp::Position::new(0, 14),
3636                        ),
3637                    })),
3638                    insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
3639                    ..Default::default()
3640                },
3641                lsp::CompletionItem {
3642                    label: "second_method(…)".into(),
3643                    detail: Some("fn(&mut self, C) -> D<E>".into()),
3644                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
3645                        new_text: "second_method()".to_string(),
3646                        range: lsp::Range::new(
3647                            lsp::Position::new(0, 14),
3648                            lsp::Position::new(0, 14),
3649                        ),
3650                    })),
3651                    insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
3652                    ..Default::default()
3653                },
3654            ])))
3655        })
3656        .next()
3657        .await
3658        .unwrap();
3659    cx_a.foreground().finish_waiting();
3660
3661    // Open the buffer on the host.
3662    let buffer_a = project_a
3663        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
3664        .await
3665        .unwrap();
3666    cx_a.foreground().run_until_parked();
3667    buffer_a.read_with(cx_a, |buffer, _| {
3668        assert_eq!(buffer.text(), "fn main() { a. }")
3669    });
3670
3671    // Confirm a completion on the guest.
3672    editor_b.read_with(cx_b, |editor, _| assert!(editor.context_menu_visible()));
3673    editor_b.update(cx_b, |editor, cx| {
3674        editor.confirm_completion(&ConfirmCompletion { item_ix: Some(0) }, cx);
3675        assert_eq!(editor.text(cx), "fn main() { a.first_method() }");
3676    });
3677
3678    // Return a resolved completion from the host's language server.
3679    // The resolved completion has an additional text edit.
3680    fake_language_server.handle_request::<lsp::request::ResolveCompletionItem, _, _>(
3681        |params, _| async move {
3682            assert_eq!(params.label, "first_method(…)");
3683            Ok(lsp::CompletionItem {
3684                label: "first_method(…)".into(),
3685                detail: Some("fn(&mut self, B) -> C".into()),
3686                text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
3687                    new_text: "first_method($1)".to_string(),
3688                    range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
3689                })),
3690                additional_text_edits: Some(vec![lsp::TextEdit {
3691                    new_text: "use d::SomeTrait;\n".to_string(),
3692                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
3693                }]),
3694                insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
3695                ..Default::default()
3696            })
3697        },
3698    );
3699
3700    // The additional edit is applied.
3701    cx_a.foreground().run_until_parked();
3702    buffer_a.read_with(cx_a, |buffer, _| {
3703        assert_eq!(
3704            buffer.text(),
3705            "use d::SomeTrait;\nfn main() { a.first_method() }"
3706        );
3707    });
3708    buffer_b.read_with(cx_b, |buffer, _| {
3709        assert_eq!(
3710            buffer.text(),
3711            "use d::SomeTrait;\nfn main() { a.first_method() }"
3712        );
3713    });
3714}
3715
3716#[gpui::test(iterations = 10)]
3717async fn test_reloading_buffer_manually(
3718    deterministic: Arc<Deterministic>,
3719    cx_a: &mut TestAppContext,
3720    cx_b: &mut TestAppContext,
3721) {
3722    deterministic.forbid_parking();
3723    let mut server = TestServer::start(&deterministic).await;
3724    let client_a = server.create_client(cx_a, "user_a").await;
3725    let client_b = server.create_client(cx_b, "user_b").await;
3726    server
3727        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3728        .await;
3729    let active_call_a = cx_a.read(ActiveCall::global);
3730
3731    client_a
3732        .fs
3733        .insert_tree("/a", json!({ "a.rs": "let one = 1;" }))
3734        .await;
3735    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
3736    let buffer_a = project_a
3737        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
3738        .await
3739        .unwrap();
3740    let project_id = active_call_a
3741        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3742        .await
3743        .unwrap();
3744
3745    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3746
3747    let buffer_b = cx_b
3748        .background()
3749        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
3750        .await
3751        .unwrap();
3752    buffer_b.update(cx_b, |buffer, cx| {
3753        buffer.edit([(4..7, "six")], None, cx);
3754        buffer.edit([(10..11, "6")], None, cx);
3755        assert_eq!(buffer.text(), "let six = 6;");
3756        assert!(buffer.is_dirty());
3757        assert!(!buffer.has_conflict());
3758    });
3759    cx_a.foreground().run_until_parked();
3760    buffer_a.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "let six = 6;"));
3761
3762    client_a
3763        .fs
3764        .save(
3765            "/a/a.rs".as_ref(),
3766            &Rope::from("let seven = 7;"),
3767            LineEnding::Unix,
3768        )
3769        .await
3770        .unwrap();
3771    cx_a.foreground().run_until_parked();
3772    buffer_a.read_with(cx_a, |buffer, _| assert!(buffer.has_conflict()));
3773    buffer_b.read_with(cx_b, |buffer, _| assert!(buffer.has_conflict()));
3774
3775    project_b
3776        .update(cx_b, |project, cx| {
3777            project.reload_buffers(HashSet::from_iter([buffer_b.clone()]), true, cx)
3778        })
3779        .await
3780        .unwrap();
3781    buffer_a.read_with(cx_a, |buffer, _| {
3782        assert_eq!(buffer.text(), "let seven = 7;");
3783        assert!(!buffer.is_dirty());
3784        assert!(!buffer.has_conflict());
3785    });
3786    buffer_b.read_with(cx_b, |buffer, _| {
3787        assert_eq!(buffer.text(), "let seven = 7;");
3788        assert!(!buffer.is_dirty());
3789        assert!(!buffer.has_conflict());
3790    });
3791
3792    buffer_a.update(cx_a, |buffer, cx| {
3793        // Undoing on the host is a no-op when the reload was initiated by the guest.
3794        buffer.undo(cx);
3795        assert_eq!(buffer.text(), "let seven = 7;");
3796        assert!(!buffer.is_dirty());
3797        assert!(!buffer.has_conflict());
3798    });
3799    buffer_b.update(cx_b, |buffer, cx| {
3800        // Undoing on the guest rolls back the buffer to before it was reloaded but the conflict gets cleared.
3801        buffer.undo(cx);
3802        assert_eq!(buffer.text(), "let six = 6;");
3803        assert!(buffer.is_dirty());
3804        assert!(!buffer.has_conflict());
3805    });
3806}
3807
3808#[gpui::test(iterations = 10)]
3809async fn test_formatting_buffer(
3810    deterministic: Arc<Deterministic>,
3811    cx_a: &mut TestAppContext,
3812    cx_b: &mut TestAppContext,
3813) {
3814    use project::FormatTrigger;
3815
3816    let mut server = TestServer::start(&deterministic).await;
3817    let client_a = server.create_client(cx_a, "user_a").await;
3818    let client_b = server.create_client(cx_b, "user_b").await;
3819    server
3820        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3821        .await;
3822    let active_call_a = cx_a.read(ActiveCall::global);
3823
3824    // Set up a fake language server.
3825    let mut language = Language::new(
3826        LanguageConfig {
3827            name: "Rust".into(),
3828            path_suffixes: vec!["rs".to_string()],
3829            ..Default::default()
3830        },
3831        Some(tree_sitter_rust::language()),
3832    );
3833    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
3834    client_a.language_registry.add(Arc::new(language));
3835
3836    // Here we insert a fake tree with a directory that exists on disk. This is needed
3837    // because later we'll invoke a command, which requires passing a working directory
3838    // that points to a valid location on disk.
3839    let directory = env::current_dir().unwrap();
3840    client_a
3841        .fs
3842        .insert_tree(&directory, json!({ "a.rs": "let one = \"two\"" }))
3843        .await;
3844    let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
3845    let project_id = active_call_a
3846        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3847        .await
3848        .unwrap();
3849    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3850
3851    let buffer_b = cx_b
3852        .background()
3853        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
3854        .await
3855        .unwrap();
3856
3857    let fake_language_server = fake_language_servers.next().await.unwrap();
3858    fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
3859        Ok(Some(vec![
3860            lsp::TextEdit {
3861                range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 4)),
3862                new_text: "h".to_string(),
3863            },
3864            lsp::TextEdit {
3865                range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 7)),
3866                new_text: "y".to_string(),
3867            },
3868        ]))
3869    });
3870
3871    project_b
3872        .update(cx_b, |project, cx| {
3873            project.format(
3874                HashSet::from_iter([buffer_b.clone()]),
3875                true,
3876                FormatTrigger::Save,
3877                cx,
3878            )
3879        })
3880        .await
3881        .unwrap();
3882    assert_eq!(
3883        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
3884        "let honey = \"two\""
3885    );
3886
3887    // Ensure buffer can be formatted using an external command. Notice how the
3888    // host's configuration is honored as opposed to using the guest's settings.
3889    cx_a.update(|cx| {
3890        cx.update_global(|settings: &mut Settings, _| {
3891            settings.editor_defaults.formatter = Some(Formatter::External {
3892                command: "awk".to_string(),
3893                arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()],
3894            });
3895        });
3896    });
3897    project_b
3898        .update(cx_b, |project, cx| {
3899            project.format(
3900                HashSet::from_iter([buffer_b.clone()]),
3901                true,
3902                FormatTrigger::Save,
3903                cx,
3904            )
3905        })
3906        .await
3907        .unwrap();
3908    assert_eq!(
3909        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
3910        format!("let honey = \"{}/a.rs\"\n", directory.to_str().unwrap())
3911    );
3912}
3913
3914#[gpui::test(iterations = 10)]
3915async fn test_definition(
3916    deterministic: Arc<Deterministic>,
3917    cx_a: &mut TestAppContext,
3918    cx_b: &mut TestAppContext,
3919) {
3920    deterministic.forbid_parking();
3921    let mut server = TestServer::start(&deterministic).await;
3922    let client_a = server.create_client(cx_a, "user_a").await;
3923    let client_b = server.create_client(cx_b, "user_b").await;
3924    server
3925        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3926        .await;
3927    let active_call_a = cx_a.read(ActiveCall::global);
3928
3929    // Set up a fake language server.
3930    let mut language = Language::new(
3931        LanguageConfig {
3932            name: "Rust".into(),
3933            path_suffixes: vec!["rs".to_string()],
3934            ..Default::default()
3935        },
3936        Some(tree_sitter_rust::language()),
3937    );
3938    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
3939    client_a.language_registry.add(Arc::new(language));
3940
3941    client_a
3942        .fs
3943        .insert_tree(
3944            "/root",
3945            json!({
3946                "dir-1": {
3947                    "a.rs": "const ONE: usize = b::TWO + b::THREE;",
3948                },
3949                "dir-2": {
3950                    "b.rs": "const TWO: c::T2 = 2;\nconst THREE: usize = 3;",
3951                    "c.rs": "type T2 = usize;",
3952                }
3953            }),
3954        )
3955        .await;
3956    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
3957    let project_id = active_call_a
3958        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3959        .await
3960        .unwrap();
3961    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3962
3963    // Open the file on client B.
3964    let buffer_b = cx_b
3965        .background()
3966        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
3967        .await
3968        .unwrap();
3969
3970    // Request the definition of a symbol as the guest.
3971    let fake_language_server = fake_language_servers.next().await.unwrap();
3972    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
3973        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
3974            lsp::Location::new(
3975                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
3976                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
3977            ),
3978        )))
3979    });
3980
3981    let definitions_1 = project_b
3982        .update(cx_b, |p, cx| p.definition(&buffer_b, 23, cx))
3983        .await
3984        .unwrap();
3985    cx_b.read(|cx| {
3986        assert_eq!(definitions_1.len(), 1);
3987        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
3988        let target_buffer = definitions_1[0].target.buffer.read(cx);
3989        assert_eq!(
3990            target_buffer.text(),
3991            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
3992        );
3993        assert_eq!(
3994            definitions_1[0].target.range.to_point(target_buffer),
3995            Point::new(0, 6)..Point::new(0, 9)
3996        );
3997    });
3998
3999    // Try getting more definitions for the same buffer, ensuring the buffer gets reused from
4000    // the previous call to `definition`.
4001    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4002        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4003            lsp::Location::new(
4004                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4005                lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)),
4006            ),
4007        )))
4008    });
4009
4010    let definitions_2 = project_b
4011        .update(cx_b, |p, cx| p.definition(&buffer_b, 33, cx))
4012        .await
4013        .unwrap();
4014    cx_b.read(|cx| {
4015        assert_eq!(definitions_2.len(), 1);
4016        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4017        let target_buffer = definitions_2[0].target.buffer.read(cx);
4018        assert_eq!(
4019            target_buffer.text(),
4020            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4021        );
4022        assert_eq!(
4023            definitions_2[0].target.range.to_point(target_buffer),
4024            Point::new(1, 6)..Point::new(1, 11)
4025        );
4026    });
4027    assert_eq!(
4028        definitions_1[0].target.buffer,
4029        definitions_2[0].target.buffer
4030    );
4031
4032    fake_language_server.handle_request::<lsp::request::GotoTypeDefinition, _, _>(
4033        |req, _| async move {
4034            assert_eq!(
4035                req.text_document_position_params.position,
4036                lsp::Position::new(0, 7)
4037            );
4038            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4039                lsp::Location::new(
4040                    lsp::Url::from_file_path("/root/dir-2/c.rs").unwrap(),
4041                    lsp::Range::new(lsp::Position::new(0, 5), lsp::Position::new(0, 7)),
4042                ),
4043            )))
4044        },
4045    );
4046
4047    let type_definitions = project_b
4048        .update(cx_b, |p, cx| p.type_definition(&buffer_b, 7, cx))
4049        .await
4050        .unwrap();
4051    cx_b.read(|cx| {
4052        assert_eq!(type_definitions.len(), 1);
4053        let target_buffer = type_definitions[0].target.buffer.read(cx);
4054        assert_eq!(target_buffer.text(), "type T2 = usize;");
4055        assert_eq!(
4056            type_definitions[0].target.range.to_point(target_buffer),
4057            Point::new(0, 5)..Point::new(0, 7)
4058        );
4059    });
4060}
4061
4062#[gpui::test(iterations = 10)]
4063async fn test_references(
4064    deterministic: Arc<Deterministic>,
4065    cx_a: &mut TestAppContext,
4066    cx_b: &mut TestAppContext,
4067) {
4068    deterministic.forbid_parking();
4069    let mut server = TestServer::start(&deterministic).await;
4070    let client_a = server.create_client(cx_a, "user_a").await;
4071    let client_b = server.create_client(cx_b, "user_b").await;
4072    server
4073        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4074        .await;
4075    let active_call_a = cx_a.read(ActiveCall::global);
4076
4077    // Set up a fake language server.
4078    let mut language = Language::new(
4079        LanguageConfig {
4080            name: "Rust".into(),
4081            path_suffixes: vec!["rs".to_string()],
4082            ..Default::default()
4083        },
4084        Some(tree_sitter_rust::language()),
4085    );
4086    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4087    client_a.language_registry.add(Arc::new(language));
4088
4089    client_a
4090        .fs
4091        .insert_tree(
4092            "/root",
4093            json!({
4094                "dir-1": {
4095                    "one.rs": "const ONE: usize = 1;",
4096                    "two.rs": "const TWO: usize = one::ONE + one::ONE;",
4097                },
4098                "dir-2": {
4099                    "three.rs": "const THREE: usize = two::TWO + one::ONE;",
4100                }
4101            }),
4102        )
4103        .await;
4104    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4105    let project_id = active_call_a
4106        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4107        .await
4108        .unwrap();
4109    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4110
4111    // Open the file on client B.
4112    let buffer_b = cx_b
4113        .background()
4114        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
4115        .await
4116        .unwrap();
4117
4118    // Request references to a symbol as the guest.
4119    let fake_language_server = fake_language_servers.next().await.unwrap();
4120    fake_language_server.handle_request::<lsp::request::References, _, _>(|params, _| async move {
4121        assert_eq!(
4122            params.text_document_position.text_document.uri.as_str(),
4123            "file:///root/dir-1/one.rs"
4124        );
4125        Ok(Some(vec![
4126            lsp::Location {
4127                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4128                range: lsp::Range::new(lsp::Position::new(0, 24), lsp::Position::new(0, 27)),
4129            },
4130            lsp::Location {
4131                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4132                range: lsp::Range::new(lsp::Position::new(0, 35), lsp::Position::new(0, 38)),
4133            },
4134            lsp::Location {
4135                uri: lsp::Url::from_file_path("/root/dir-2/three.rs").unwrap(),
4136                range: lsp::Range::new(lsp::Position::new(0, 37), lsp::Position::new(0, 40)),
4137            },
4138        ]))
4139    });
4140
4141    let references = project_b
4142        .update(cx_b, |p, cx| p.references(&buffer_b, 7, cx))
4143        .await
4144        .unwrap();
4145    cx_b.read(|cx| {
4146        assert_eq!(references.len(), 3);
4147        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4148
4149        let two_buffer = references[0].buffer.read(cx);
4150        let three_buffer = references[2].buffer.read(cx);
4151        assert_eq!(
4152            two_buffer.file().unwrap().path().as_ref(),
4153            Path::new("two.rs")
4154        );
4155        assert_eq!(references[1].buffer, references[0].buffer);
4156        assert_eq!(
4157            three_buffer.file().unwrap().full_path(cx),
4158            Path::new("/root/dir-2/three.rs")
4159        );
4160
4161        assert_eq!(references[0].range.to_offset(two_buffer), 24..27);
4162        assert_eq!(references[1].range.to_offset(two_buffer), 35..38);
4163        assert_eq!(references[2].range.to_offset(three_buffer), 37..40);
4164    });
4165}
4166
4167#[gpui::test(iterations = 10)]
4168async fn test_project_search(
4169    deterministic: Arc<Deterministic>,
4170    cx_a: &mut TestAppContext,
4171    cx_b: &mut TestAppContext,
4172) {
4173    deterministic.forbid_parking();
4174    let mut server = TestServer::start(&deterministic).await;
4175    let client_a = server.create_client(cx_a, "user_a").await;
4176    let client_b = server.create_client(cx_b, "user_b").await;
4177    server
4178        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4179        .await;
4180    let active_call_a = cx_a.read(ActiveCall::global);
4181
4182    client_a
4183        .fs
4184        .insert_tree(
4185            "/root",
4186            json!({
4187                "dir-1": {
4188                    "a": "hello world",
4189                    "b": "goodnight moon",
4190                    "c": "a world of goo",
4191                    "d": "world champion of clown world",
4192                },
4193                "dir-2": {
4194                    "e": "disney world is fun",
4195                }
4196            }),
4197        )
4198        .await;
4199    let (project_a, _) = client_a.build_local_project("/root/dir-1", cx_a).await;
4200    let (worktree_2, _) = project_a
4201        .update(cx_a, |p, cx| {
4202            p.find_or_create_local_worktree("/root/dir-2", true, cx)
4203        })
4204        .await
4205        .unwrap();
4206    worktree_2
4207        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
4208        .await;
4209    let project_id = active_call_a
4210        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4211        .await
4212        .unwrap();
4213
4214    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4215
4216    // Perform a search as the guest.
4217    let results = project_b
4218        .update(cx_b, |project, cx| {
4219            project.search(SearchQuery::text("world", false, false), cx)
4220        })
4221        .await
4222        .unwrap();
4223
4224    let mut ranges_by_path = results
4225        .into_iter()
4226        .map(|(buffer, ranges)| {
4227            buffer.read_with(cx_b, |buffer, cx| {
4228                let path = buffer.file().unwrap().full_path(cx);
4229                let offset_ranges = ranges
4230                    .into_iter()
4231                    .map(|range| range.to_offset(buffer))
4232                    .collect::<Vec<_>>();
4233                (path, offset_ranges)
4234            })
4235        })
4236        .collect::<Vec<_>>();
4237    ranges_by_path.sort_by_key(|(path, _)| path.clone());
4238
4239    assert_eq!(
4240        ranges_by_path,
4241        &[
4242            (PathBuf::from("dir-1/a"), vec![6..11]),
4243            (PathBuf::from("dir-1/c"), vec![2..7]),
4244            (PathBuf::from("dir-1/d"), vec![0..5, 24..29]),
4245            (PathBuf::from("dir-2/e"), vec![7..12]),
4246        ]
4247    );
4248}
4249
4250#[gpui::test(iterations = 10)]
4251async fn test_document_highlights(
4252    deterministic: Arc<Deterministic>,
4253    cx_a: &mut TestAppContext,
4254    cx_b: &mut TestAppContext,
4255) {
4256    deterministic.forbid_parking();
4257    let mut server = TestServer::start(&deterministic).await;
4258    let client_a = server.create_client(cx_a, "user_a").await;
4259    let client_b = server.create_client(cx_b, "user_b").await;
4260    server
4261        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4262        .await;
4263    let active_call_a = cx_a.read(ActiveCall::global);
4264
4265    client_a
4266        .fs
4267        .insert_tree(
4268            "/root-1",
4269            json!({
4270                "main.rs": "fn double(number: i32) -> i32 { number + number }",
4271            }),
4272        )
4273        .await;
4274
4275    // Set up a fake language server.
4276    let mut language = Language::new(
4277        LanguageConfig {
4278            name: "Rust".into(),
4279            path_suffixes: vec!["rs".to_string()],
4280            ..Default::default()
4281        },
4282        Some(tree_sitter_rust::language()),
4283    );
4284    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4285    client_a.language_registry.add(Arc::new(language));
4286
4287    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4288    let project_id = active_call_a
4289        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4290        .await
4291        .unwrap();
4292    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4293
4294    // Open the file on client B.
4295    let buffer_b = cx_b
4296        .background()
4297        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
4298        .await
4299        .unwrap();
4300
4301    // Request document highlights as the guest.
4302    let fake_language_server = fake_language_servers.next().await.unwrap();
4303    fake_language_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
4304        |params, _| async move {
4305            assert_eq!(
4306                params
4307                    .text_document_position_params
4308                    .text_document
4309                    .uri
4310                    .as_str(),
4311                "file:///root-1/main.rs"
4312            );
4313            assert_eq!(
4314                params.text_document_position_params.position,
4315                lsp::Position::new(0, 34)
4316            );
4317            Ok(Some(vec![
4318                lsp::DocumentHighlight {
4319                    kind: Some(lsp::DocumentHighlightKind::WRITE),
4320                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 16)),
4321                },
4322                lsp::DocumentHighlight {
4323                    kind: Some(lsp::DocumentHighlightKind::READ),
4324                    range: lsp::Range::new(lsp::Position::new(0, 32), lsp::Position::new(0, 38)),
4325                },
4326                lsp::DocumentHighlight {
4327                    kind: Some(lsp::DocumentHighlightKind::READ),
4328                    range: lsp::Range::new(lsp::Position::new(0, 41), lsp::Position::new(0, 47)),
4329                },
4330            ]))
4331        },
4332    );
4333
4334    let highlights = project_b
4335        .update(cx_b, |p, cx| p.document_highlights(&buffer_b, 34, cx))
4336        .await
4337        .unwrap();
4338    buffer_b.read_with(cx_b, |buffer, _| {
4339        let snapshot = buffer.snapshot();
4340
4341        let highlights = highlights
4342            .into_iter()
4343            .map(|highlight| (highlight.kind, highlight.range.to_offset(&snapshot)))
4344            .collect::<Vec<_>>();
4345        assert_eq!(
4346            highlights,
4347            &[
4348                (lsp::DocumentHighlightKind::WRITE, 10..16),
4349                (lsp::DocumentHighlightKind::READ, 32..38),
4350                (lsp::DocumentHighlightKind::READ, 41..47)
4351            ]
4352        )
4353    });
4354}
4355
4356#[gpui::test(iterations = 10)]
4357async fn test_lsp_hover(
4358    deterministic: Arc<Deterministic>,
4359    cx_a: &mut TestAppContext,
4360    cx_b: &mut TestAppContext,
4361) {
4362    deterministic.forbid_parking();
4363    let mut server = TestServer::start(&deterministic).await;
4364    let client_a = server.create_client(cx_a, "user_a").await;
4365    let client_b = server.create_client(cx_b, "user_b").await;
4366    server
4367        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4368        .await;
4369    let active_call_a = cx_a.read(ActiveCall::global);
4370
4371    client_a
4372        .fs
4373        .insert_tree(
4374            "/root-1",
4375            json!({
4376                "main.rs": "use std::collections::HashMap;",
4377            }),
4378        )
4379        .await;
4380
4381    // Set up a fake language server.
4382    let mut language = Language::new(
4383        LanguageConfig {
4384            name: "Rust".into(),
4385            path_suffixes: vec!["rs".to_string()],
4386            ..Default::default()
4387        },
4388        Some(tree_sitter_rust::language()),
4389    );
4390    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4391    client_a.language_registry.add(Arc::new(language));
4392
4393    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4394    let project_id = active_call_a
4395        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4396        .await
4397        .unwrap();
4398    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4399
4400    // Open the file as the guest
4401    let buffer_b = cx_b
4402        .background()
4403        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
4404        .await
4405        .unwrap();
4406
4407    // Request hover information as the guest.
4408    let fake_language_server = fake_language_servers.next().await.unwrap();
4409    fake_language_server.handle_request::<lsp::request::HoverRequest, _, _>(
4410        |params, _| async move {
4411            assert_eq!(
4412                params
4413                    .text_document_position_params
4414                    .text_document
4415                    .uri
4416                    .as_str(),
4417                "file:///root-1/main.rs"
4418            );
4419            assert_eq!(
4420                params.text_document_position_params.position,
4421                lsp::Position::new(0, 22)
4422            );
4423            Ok(Some(lsp::Hover {
4424                contents: lsp::HoverContents::Array(vec![
4425                    lsp::MarkedString::String("Test hover content.".to_string()),
4426                    lsp::MarkedString::LanguageString(lsp::LanguageString {
4427                        language: "Rust".to_string(),
4428                        value: "let foo = 42;".to_string(),
4429                    }),
4430                ]),
4431                range: Some(lsp::Range::new(
4432                    lsp::Position::new(0, 22),
4433                    lsp::Position::new(0, 29),
4434                )),
4435            }))
4436        },
4437    );
4438
4439    let hover_info = project_b
4440        .update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
4441        .await
4442        .unwrap()
4443        .unwrap();
4444    buffer_b.read_with(cx_b, |buffer, _| {
4445        let snapshot = buffer.snapshot();
4446        assert_eq!(hover_info.range.unwrap().to_offset(&snapshot), 22..29);
4447        assert_eq!(
4448            hover_info.contents,
4449            vec![
4450                project::HoverBlock {
4451                    text: "Test hover content.".to_string(),
4452                    language: None,
4453                },
4454                project::HoverBlock {
4455                    text: "let foo = 42;".to_string(),
4456                    language: Some("Rust".to_string()),
4457                }
4458            ]
4459        );
4460    });
4461}
4462
4463#[gpui::test(iterations = 10)]
4464async fn test_project_symbols(
4465    deterministic: Arc<Deterministic>,
4466    cx_a: &mut TestAppContext,
4467    cx_b: &mut TestAppContext,
4468) {
4469    deterministic.forbid_parking();
4470    let mut server = TestServer::start(&deterministic).await;
4471    let client_a = server.create_client(cx_a, "user_a").await;
4472    let client_b = server.create_client(cx_b, "user_b").await;
4473    server
4474        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4475        .await;
4476    let active_call_a = cx_a.read(ActiveCall::global);
4477
4478    // Set up a fake language server.
4479    let mut language = Language::new(
4480        LanguageConfig {
4481            name: "Rust".into(),
4482            path_suffixes: vec!["rs".to_string()],
4483            ..Default::default()
4484        },
4485        Some(tree_sitter_rust::language()),
4486    );
4487    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4488    client_a.language_registry.add(Arc::new(language));
4489
4490    client_a
4491        .fs
4492        .insert_tree(
4493            "/code",
4494            json!({
4495                "crate-1": {
4496                    "one.rs": "const ONE: usize = 1;",
4497                },
4498                "crate-2": {
4499                    "two.rs": "const TWO: usize = 2; const THREE: usize = 3;",
4500                },
4501                "private": {
4502                    "passwords.txt": "the-password",
4503                }
4504            }),
4505        )
4506        .await;
4507    let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await;
4508    let project_id = active_call_a
4509        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4510        .await
4511        .unwrap();
4512    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4513
4514    // Cause the language server to start.
4515    let _buffer = cx_b
4516        .background()
4517        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
4518        .await
4519        .unwrap();
4520
4521    let fake_language_server = fake_language_servers.next().await.unwrap();
4522    fake_language_server.handle_request::<lsp::request::WorkspaceSymbol, _, _>(|_, _| async move {
4523        #[allow(deprecated)]
4524        Ok(Some(vec![lsp::SymbolInformation {
4525            name: "TWO".into(),
4526            location: lsp::Location {
4527                uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
4528                range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4529            },
4530            kind: lsp::SymbolKind::CONSTANT,
4531            tags: None,
4532            container_name: None,
4533            deprecated: None,
4534        }]))
4535    });
4536
4537    // Request the definition of a symbol as the guest.
4538    let symbols = project_b
4539        .update(cx_b, |p, cx| p.symbols("two", cx))
4540        .await
4541        .unwrap();
4542    assert_eq!(symbols.len(), 1);
4543    assert_eq!(symbols[0].name, "TWO");
4544
4545    // Open one of the returned symbols.
4546    let buffer_b_2 = project_b
4547        .update(cx_b, |project, cx| {
4548            project.open_buffer_for_symbol(&symbols[0], cx)
4549        })
4550        .await
4551        .unwrap();
4552    buffer_b_2.read_with(cx_b, |buffer, _| {
4553        assert_eq!(
4554            buffer.file().unwrap().path().as_ref(),
4555            Path::new("../crate-2/two.rs")
4556        );
4557    });
4558
4559    // Attempt to craft a symbol and violate host's privacy by opening an arbitrary file.
4560    let mut fake_symbol = symbols[0].clone();
4561    fake_symbol.path.path = Path::new("/code/secrets").into();
4562    let error = project_b
4563        .update(cx_b, |project, cx| {
4564            project.open_buffer_for_symbol(&fake_symbol, cx)
4565        })
4566        .await
4567        .unwrap_err();
4568    assert!(error.to_string().contains("invalid symbol signature"));
4569}
4570
4571#[gpui::test(iterations = 10)]
4572async fn test_open_buffer_while_getting_definition_pointing_to_it(
4573    deterministic: Arc<Deterministic>,
4574    cx_a: &mut TestAppContext,
4575    cx_b: &mut TestAppContext,
4576    mut rng: StdRng,
4577) {
4578    deterministic.forbid_parking();
4579    let mut server = TestServer::start(&deterministic).await;
4580    let client_a = server.create_client(cx_a, "user_a").await;
4581    let client_b = server.create_client(cx_b, "user_b").await;
4582    server
4583        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4584        .await;
4585    let active_call_a = cx_a.read(ActiveCall::global);
4586
4587    // Set up a fake language server.
4588    let mut language = Language::new(
4589        LanguageConfig {
4590            name: "Rust".into(),
4591            path_suffixes: vec!["rs".to_string()],
4592            ..Default::default()
4593        },
4594        Some(tree_sitter_rust::language()),
4595    );
4596    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4597    client_a.language_registry.add(Arc::new(language));
4598
4599    client_a
4600        .fs
4601        .insert_tree(
4602            "/root",
4603            json!({
4604                "a.rs": "const ONE: usize = b::TWO;",
4605                "b.rs": "const TWO: usize = 2",
4606            }),
4607        )
4608        .await;
4609    let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await;
4610    let project_id = active_call_a
4611        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4612        .await
4613        .unwrap();
4614    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4615
4616    let buffer_b1 = cx_b
4617        .background()
4618        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4619        .await
4620        .unwrap();
4621
4622    let fake_language_server = fake_language_servers.next().await.unwrap();
4623    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4624        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4625            lsp::Location::new(
4626                lsp::Url::from_file_path("/root/b.rs").unwrap(),
4627                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4628            ),
4629        )))
4630    });
4631
4632    let definitions;
4633    let buffer_b2;
4634    if rng.gen() {
4635        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
4636        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
4637    } else {
4638        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
4639        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
4640    }
4641
4642    let buffer_b2 = buffer_b2.await.unwrap();
4643    let definitions = definitions.await.unwrap();
4644    assert_eq!(definitions.len(), 1);
4645    assert_eq!(definitions[0].target.buffer, buffer_b2);
4646}
4647
4648#[gpui::test(iterations = 10)]
4649async fn test_collaborating_with_code_actions(
4650    deterministic: Arc<Deterministic>,
4651    cx_a: &mut TestAppContext,
4652    cx_b: &mut TestAppContext,
4653) {
4654    deterministic.forbid_parking();
4655    cx_b.update(editor::init);
4656    let mut server = TestServer::start(&deterministic).await;
4657    let client_a = server.create_client(cx_a, "user_a").await;
4658    let client_b = server.create_client(cx_b, "user_b").await;
4659    server
4660        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4661        .await;
4662    let active_call_a = cx_a.read(ActiveCall::global);
4663
4664    // Set up a fake language server.
4665    let mut language = Language::new(
4666        LanguageConfig {
4667            name: "Rust".into(),
4668            path_suffixes: vec!["rs".to_string()],
4669            ..Default::default()
4670        },
4671        Some(tree_sitter_rust::language()),
4672    );
4673    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4674    client_a.language_registry.add(Arc::new(language));
4675
4676    client_a
4677        .fs
4678        .insert_tree(
4679            "/a",
4680            json!({
4681                "main.rs": "mod other;\nfn main() { let foo = other::foo(); }",
4682                "other.rs": "pub fn foo() -> usize { 4 }",
4683            }),
4684        )
4685        .await;
4686    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
4687    let project_id = active_call_a
4688        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4689        .await
4690        .unwrap();
4691
4692    // Join the project as client B.
4693    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4694    let (_window_b, workspace_b) = cx_b.add_window(|cx| {
4695        Workspace::new(
4696            Default::default(),
4697            0,
4698            project_b.clone(),
4699            |_, _| unimplemented!(),
4700            cx,
4701        )
4702    });
4703    let editor_b = workspace_b
4704        .update(cx_b, |workspace, cx| {
4705            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
4706        })
4707        .await
4708        .unwrap()
4709        .downcast::<Editor>()
4710        .unwrap();
4711
4712    let mut fake_language_server = fake_language_servers.next().await.unwrap();
4713    fake_language_server
4714        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
4715            assert_eq!(
4716                params.text_document.uri,
4717                lsp::Url::from_file_path("/a/main.rs").unwrap(),
4718            );
4719            assert_eq!(params.range.start, lsp::Position::new(0, 0));
4720            assert_eq!(params.range.end, lsp::Position::new(0, 0));
4721            Ok(None)
4722        })
4723        .next()
4724        .await;
4725
4726    // Move cursor to a location that contains code actions.
4727    editor_b.update(cx_b, |editor, cx| {
4728        editor.change_selections(None, cx, |s| {
4729            s.select_ranges([Point::new(1, 31)..Point::new(1, 31)])
4730        });
4731        cx.focus(&editor_b);
4732    });
4733
4734    fake_language_server
4735        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
4736            assert_eq!(
4737                params.text_document.uri,
4738                lsp::Url::from_file_path("/a/main.rs").unwrap(),
4739            );
4740            assert_eq!(params.range.start, lsp::Position::new(1, 31));
4741            assert_eq!(params.range.end, lsp::Position::new(1, 31));
4742
4743            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
4744                lsp::CodeAction {
4745                    title: "Inline into all callers".to_string(),
4746                    edit: Some(lsp::WorkspaceEdit {
4747                        changes: Some(
4748                            [
4749                                (
4750                                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
4751                                    vec![lsp::TextEdit::new(
4752                                        lsp::Range::new(
4753                                            lsp::Position::new(1, 22),
4754                                            lsp::Position::new(1, 34),
4755                                        ),
4756                                        "4".to_string(),
4757                                    )],
4758                                ),
4759                                (
4760                                    lsp::Url::from_file_path("/a/other.rs").unwrap(),
4761                                    vec![lsp::TextEdit::new(
4762                                        lsp::Range::new(
4763                                            lsp::Position::new(0, 0),
4764                                            lsp::Position::new(0, 27),
4765                                        ),
4766                                        "".to_string(),
4767                                    )],
4768                                ),
4769                            ]
4770                            .into_iter()
4771                            .collect(),
4772                        ),
4773                        ..Default::default()
4774                    }),
4775                    data: Some(json!({
4776                        "codeActionParams": {
4777                            "range": {
4778                                "start": {"line": 1, "column": 31},
4779                                "end": {"line": 1, "column": 31},
4780                            }
4781                        }
4782                    })),
4783                    ..Default::default()
4784                },
4785            )]))
4786        })
4787        .next()
4788        .await;
4789
4790    // Toggle code actions and wait for them to display.
4791    editor_b.update(cx_b, |editor, cx| {
4792        editor.toggle_code_actions(
4793            &ToggleCodeActions {
4794                deployed_from_indicator: false,
4795            },
4796            cx,
4797        );
4798    });
4799    cx_a.foreground().run_until_parked();
4800    editor_b.read_with(cx_b, |editor, _| assert!(editor.context_menu_visible()));
4801
4802    fake_language_server.remove_request_handler::<lsp::request::CodeActionRequest>();
4803
4804    // Confirming the code action will trigger a resolve request.
4805    let confirm_action = workspace_b
4806        .update(cx_b, |workspace, cx| {
4807            Editor::confirm_code_action(workspace, &ConfirmCodeAction { item_ix: Some(0) }, cx)
4808        })
4809        .unwrap();
4810    fake_language_server.handle_request::<lsp::request::CodeActionResolveRequest, _, _>(
4811        |_, _| async move {
4812            Ok(lsp::CodeAction {
4813                title: "Inline into all callers".to_string(),
4814                edit: Some(lsp::WorkspaceEdit {
4815                    changes: Some(
4816                        [
4817                            (
4818                                lsp::Url::from_file_path("/a/main.rs").unwrap(),
4819                                vec![lsp::TextEdit::new(
4820                                    lsp::Range::new(
4821                                        lsp::Position::new(1, 22),
4822                                        lsp::Position::new(1, 34),
4823                                    ),
4824                                    "4".to_string(),
4825                                )],
4826                            ),
4827                            (
4828                                lsp::Url::from_file_path("/a/other.rs").unwrap(),
4829                                vec![lsp::TextEdit::new(
4830                                    lsp::Range::new(
4831                                        lsp::Position::new(0, 0),
4832                                        lsp::Position::new(0, 27),
4833                                    ),
4834                                    "".to_string(),
4835                                )],
4836                            ),
4837                        ]
4838                        .into_iter()
4839                        .collect(),
4840                    ),
4841                    ..Default::default()
4842                }),
4843                ..Default::default()
4844            })
4845        },
4846    );
4847
4848    // After the action is confirmed, an editor containing both modified files is opened.
4849    confirm_action.await.unwrap();
4850    let code_action_editor = workspace_b.read_with(cx_b, |workspace, cx| {
4851        workspace
4852            .active_item(cx)
4853            .unwrap()
4854            .downcast::<Editor>()
4855            .unwrap()
4856    });
4857    code_action_editor.update(cx_b, |editor, cx| {
4858        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
4859        editor.undo(&Undo, cx);
4860        assert_eq!(
4861            editor.text(cx),
4862            "mod other;\nfn main() { let foo = other::foo(); }\npub fn foo() -> usize { 4 }"
4863        );
4864        editor.redo(&Redo, cx);
4865        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
4866    });
4867}
4868
4869#[gpui::test(iterations = 10)]
4870async fn test_collaborating_with_renames(
4871    deterministic: Arc<Deterministic>,
4872    cx_a: &mut TestAppContext,
4873    cx_b: &mut TestAppContext,
4874) {
4875    deterministic.forbid_parking();
4876    cx_b.update(editor::init);
4877    let mut server = TestServer::start(&deterministic).await;
4878    let client_a = server.create_client(cx_a, "user_a").await;
4879    let client_b = server.create_client(cx_b, "user_b").await;
4880    server
4881        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4882        .await;
4883    let active_call_a = cx_a.read(ActiveCall::global);
4884
4885    // Set up a fake language server.
4886    let mut language = Language::new(
4887        LanguageConfig {
4888            name: "Rust".into(),
4889            path_suffixes: vec!["rs".to_string()],
4890            ..Default::default()
4891        },
4892        Some(tree_sitter_rust::language()),
4893    );
4894    let mut fake_language_servers = language
4895        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4896            capabilities: lsp::ServerCapabilities {
4897                rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
4898                    prepare_provider: Some(true),
4899                    work_done_progress_options: Default::default(),
4900                })),
4901                ..Default::default()
4902            },
4903            ..Default::default()
4904        }))
4905        .await;
4906    client_a.language_registry.add(Arc::new(language));
4907
4908    client_a
4909        .fs
4910        .insert_tree(
4911            "/dir",
4912            json!({
4913                "one.rs": "const ONE: usize = 1;",
4914                "two.rs": "const TWO: usize = one::ONE + one::ONE;"
4915            }),
4916        )
4917        .await;
4918    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
4919    let project_id = active_call_a
4920        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4921        .await
4922        .unwrap();
4923    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4924
4925    let (_window_b, workspace_b) = cx_b.add_window(|cx| {
4926        Workspace::new(
4927            Default::default(),
4928            0,
4929            project_b.clone(),
4930            |_, _| unimplemented!(),
4931            cx,
4932        )
4933    });
4934    let editor_b = workspace_b
4935        .update(cx_b, |workspace, cx| {
4936            workspace.open_path((worktree_id, "one.rs"), None, true, cx)
4937        })
4938        .await
4939        .unwrap()
4940        .downcast::<Editor>()
4941        .unwrap();
4942    let fake_language_server = fake_language_servers.next().await.unwrap();
4943
4944    // Move cursor to a location that can be renamed.
4945    let prepare_rename = editor_b.update(cx_b, |editor, cx| {
4946        editor.change_selections(None, cx, |s| s.select_ranges([7..7]));
4947        editor.rename(&Rename, cx).unwrap()
4948    });
4949
4950    fake_language_server
4951        .handle_request::<lsp::request::PrepareRenameRequest, _, _>(|params, _| async move {
4952            assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs");
4953            assert_eq!(params.position, lsp::Position::new(0, 7));
4954            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
4955                lsp::Position::new(0, 6),
4956                lsp::Position::new(0, 9),
4957            ))))
4958        })
4959        .next()
4960        .await
4961        .unwrap();
4962    prepare_rename.await.unwrap();
4963    editor_b.update(cx_b, |editor, cx| {
4964        let rename = editor.pending_rename().unwrap();
4965        let buffer = editor.buffer().read(cx).snapshot(cx);
4966        assert_eq!(
4967            rename.range.start.to_offset(&buffer)..rename.range.end.to_offset(&buffer),
4968            6..9
4969        );
4970        rename.editor.update(cx, |rename_editor, cx| {
4971            rename_editor.buffer().update(cx, |rename_buffer, cx| {
4972                rename_buffer.edit([(0..3, "THREE")], None, cx);
4973            });
4974        });
4975    });
4976
4977    let confirm_rename = workspace_b.update(cx_b, |workspace, cx| {
4978        Editor::confirm_rename(workspace, &ConfirmRename, cx).unwrap()
4979    });
4980    fake_language_server
4981        .handle_request::<lsp::request::Rename, _, _>(|params, _| async move {
4982            assert_eq!(
4983                params.text_document_position.text_document.uri.as_str(),
4984                "file:///dir/one.rs"
4985            );
4986            assert_eq!(
4987                params.text_document_position.position,
4988                lsp::Position::new(0, 6)
4989            );
4990            assert_eq!(params.new_name, "THREE");
4991            Ok(Some(lsp::WorkspaceEdit {
4992                changes: Some(
4993                    [
4994                        (
4995                            lsp::Url::from_file_path("/dir/one.rs").unwrap(),
4996                            vec![lsp::TextEdit::new(
4997                                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4998                                "THREE".to_string(),
4999                            )],
5000                        ),
5001                        (
5002                            lsp::Url::from_file_path("/dir/two.rs").unwrap(),
5003                            vec![
5004                                lsp::TextEdit::new(
5005                                    lsp::Range::new(
5006                                        lsp::Position::new(0, 24),
5007                                        lsp::Position::new(0, 27),
5008                                    ),
5009                                    "THREE".to_string(),
5010                                ),
5011                                lsp::TextEdit::new(
5012                                    lsp::Range::new(
5013                                        lsp::Position::new(0, 35),
5014                                        lsp::Position::new(0, 38),
5015                                    ),
5016                                    "THREE".to_string(),
5017                                ),
5018                            ],
5019                        ),
5020                    ]
5021                    .into_iter()
5022                    .collect(),
5023                ),
5024                ..Default::default()
5025            }))
5026        })
5027        .next()
5028        .await
5029        .unwrap();
5030    confirm_rename.await.unwrap();
5031
5032    let rename_editor = workspace_b.read_with(cx_b, |workspace, cx| {
5033        workspace
5034            .active_item(cx)
5035            .unwrap()
5036            .downcast::<Editor>()
5037            .unwrap()
5038    });
5039    rename_editor.update(cx_b, |editor, cx| {
5040        assert_eq!(
5041            editor.text(cx),
5042            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
5043        );
5044        editor.undo(&Undo, cx);
5045        assert_eq!(
5046            editor.text(cx),
5047            "const ONE: usize = 1;\nconst TWO: usize = one::ONE + one::ONE;"
5048        );
5049        editor.redo(&Redo, cx);
5050        assert_eq!(
5051            editor.text(cx),
5052            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
5053        );
5054    });
5055
5056    // Ensure temporary rename edits cannot be undone/redone.
5057    editor_b.update(cx_b, |editor, cx| {
5058        editor.undo(&Undo, cx);
5059        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
5060        editor.undo(&Undo, cx);
5061        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
5062        editor.redo(&Redo, cx);
5063        assert_eq!(editor.text(cx), "const THREE: usize = 1;");
5064    })
5065}
5066
5067#[gpui::test(iterations = 10)]
5068async fn test_language_server_statuses(
5069    deterministic: Arc<Deterministic>,
5070    cx_a: &mut TestAppContext,
5071    cx_b: &mut TestAppContext,
5072) {
5073    deterministic.forbid_parking();
5074
5075    cx_b.update(editor::init);
5076    let mut server = TestServer::start(&deterministic).await;
5077    let client_a = server.create_client(cx_a, "user_a").await;
5078    let client_b = server.create_client(cx_b, "user_b").await;
5079    server
5080        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5081        .await;
5082    let active_call_a = cx_a.read(ActiveCall::global);
5083
5084    // Set up a fake language server.
5085    let mut language = Language::new(
5086        LanguageConfig {
5087            name: "Rust".into(),
5088            path_suffixes: vec!["rs".to_string()],
5089            ..Default::default()
5090        },
5091        Some(tree_sitter_rust::language()),
5092    );
5093    let mut fake_language_servers = language
5094        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
5095            name: "the-language-server",
5096            ..Default::default()
5097        }))
5098        .await;
5099    client_a.language_registry.add(Arc::new(language));
5100
5101    client_a
5102        .fs
5103        .insert_tree(
5104            "/dir",
5105            json!({
5106                "main.rs": "const ONE: usize = 1;",
5107            }),
5108        )
5109        .await;
5110    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
5111
5112    let _buffer_a = project_a
5113        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
5114        .await
5115        .unwrap();
5116
5117    let fake_language_server = fake_language_servers.next().await.unwrap();
5118    fake_language_server.start_progress("the-token").await;
5119    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
5120        token: lsp::NumberOrString::String("the-token".to_string()),
5121        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
5122            lsp::WorkDoneProgressReport {
5123                message: Some("the-message".to_string()),
5124                ..Default::default()
5125            },
5126        )),
5127    });
5128    deterministic.run_until_parked();
5129    project_a.read_with(cx_a, |project, _| {
5130        let status = project.language_server_statuses().next().unwrap();
5131        assert_eq!(status.name, "the-language-server");
5132        assert_eq!(status.pending_work.len(), 1);
5133        assert_eq!(
5134            status.pending_work["the-token"].message.as_ref().unwrap(),
5135            "the-message"
5136        );
5137    });
5138
5139    let project_id = active_call_a
5140        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5141        .await
5142        .unwrap();
5143    deterministic.run_until_parked();
5144    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5145    project_b.read_with(cx_b, |project, _| {
5146        let status = project.language_server_statuses().next().unwrap();
5147        assert_eq!(status.name, "the-language-server");
5148    });
5149
5150    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
5151        token: lsp::NumberOrString::String("the-token".to_string()),
5152        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
5153            lsp::WorkDoneProgressReport {
5154                message: Some("the-message-2".to_string()),
5155                ..Default::default()
5156            },
5157        )),
5158    });
5159    deterministic.run_until_parked();
5160    project_a.read_with(cx_a, |project, _| {
5161        let status = project.language_server_statuses().next().unwrap();
5162        assert_eq!(status.name, "the-language-server");
5163        assert_eq!(status.pending_work.len(), 1);
5164        assert_eq!(
5165            status.pending_work["the-token"].message.as_ref().unwrap(),
5166            "the-message-2"
5167        );
5168    });
5169    project_b.read_with(cx_b, |project, _| {
5170        let status = project.language_server_statuses().next().unwrap();
5171        assert_eq!(status.name, "the-language-server");
5172        assert_eq!(status.pending_work.len(), 1);
5173        assert_eq!(
5174            status.pending_work["the-token"].message.as_ref().unwrap(),
5175            "the-message-2"
5176        );
5177    });
5178}
5179
5180#[gpui::test(iterations = 10)]
5181async fn test_contacts(
5182    deterministic: Arc<Deterministic>,
5183    cx_a: &mut TestAppContext,
5184    cx_b: &mut TestAppContext,
5185    cx_c: &mut TestAppContext,
5186    cx_d: &mut TestAppContext,
5187) {
5188    deterministic.forbid_parking();
5189    let mut server = TestServer::start(&deterministic).await;
5190    let client_a = server.create_client(cx_a, "user_a").await;
5191    let client_b = server.create_client(cx_b, "user_b").await;
5192    let client_c = server.create_client(cx_c, "user_c").await;
5193    let client_d = server.create_client(cx_d, "user_d").await;
5194    server
5195        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
5196        .await;
5197    let active_call_a = cx_a.read(ActiveCall::global);
5198    let active_call_b = cx_b.read(ActiveCall::global);
5199    let active_call_c = cx_c.read(ActiveCall::global);
5200    let _active_call_d = cx_d.read(ActiveCall::global);
5201
5202    deterministic.run_until_parked();
5203    assert_eq!(
5204        contacts(&client_a, cx_a),
5205        [
5206            ("user_b".to_string(), "online", "free"),
5207            ("user_c".to_string(), "online", "free")
5208        ]
5209    );
5210    assert_eq!(
5211        contacts(&client_b, cx_b),
5212        [
5213            ("user_a".to_string(), "online", "free"),
5214            ("user_c".to_string(), "online", "free")
5215        ]
5216    );
5217    assert_eq!(
5218        contacts(&client_c, cx_c),
5219        [
5220            ("user_a".to_string(), "online", "free"),
5221            ("user_b".to_string(), "online", "free")
5222        ]
5223    );
5224    assert_eq!(contacts(&client_d, cx_d), []);
5225
5226    server.disconnect_client(client_c.peer_id().unwrap());
5227    server.forbid_connections();
5228    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5229    assert_eq!(
5230        contacts(&client_a, cx_a),
5231        [
5232            ("user_b".to_string(), "online", "free"),
5233            ("user_c".to_string(), "offline", "free")
5234        ]
5235    );
5236    assert_eq!(
5237        contacts(&client_b, cx_b),
5238        [
5239            ("user_a".to_string(), "online", "free"),
5240            ("user_c".to_string(), "offline", "free")
5241        ]
5242    );
5243    assert_eq!(contacts(&client_c, cx_c), []);
5244    assert_eq!(contacts(&client_d, cx_d), []);
5245
5246    server.allow_connections();
5247    client_c
5248        .authenticate_and_connect(false, &cx_c.to_async())
5249        .await
5250        .unwrap();
5251
5252    deterministic.run_until_parked();
5253    assert_eq!(
5254        contacts(&client_a, cx_a),
5255        [
5256            ("user_b".to_string(), "online", "free"),
5257            ("user_c".to_string(), "online", "free")
5258        ]
5259    );
5260    assert_eq!(
5261        contacts(&client_b, cx_b),
5262        [
5263            ("user_a".to_string(), "online", "free"),
5264            ("user_c".to_string(), "online", "free")
5265        ]
5266    );
5267    assert_eq!(
5268        contacts(&client_c, cx_c),
5269        [
5270            ("user_a".to_string(), "online", "free"),
5271            ("user_b".to_string(), "online", "free")
5272        ]
5273    );
5274    assert_eq!(contacts(&client_d, cx_d), []);
5275
5276    active_call_a
5277        .update(cx_a, |call, cx| {
5278            call.invite(client_b.user_id().unwrap(), None, cx)
5279        })
5280        .await
5281        .unwrap();
5282    deterministic.run_until_parked();
5283    assert_eq!(
5284        contacts(&client_a, cx_a),
5285        [
5286            ("user_b".to_string(), "online", "busy"),
5287            ("user_c".to_string(), "online", "free")
5288        ]
5289    );
5290    assert_eq!(
5291        contacts(&client_b, cx_b),
5292        [
5293            ("user_a".to_string(), "online", "busy"),
5294            ("user_c".to_string(), "online", "free")
5295        ]
5296    );
5297    assert_eq!(
5298        contacts(&client_c, cx_c),
5299        [
5300            ("user_a".to_string(), "online", "busy"),
5301            ("user_b".to_string(), "online", "busy")
5302        ]
5303    );
5304    assert_eq!(contacts(&client_d, cx_d), []);
5305
5306    // Client B and client D become contacts while client B is being called.
5307    server
5308        .make_contacts(&mut [(&client_b, cx_b), (&client_d, cx_d)])
5309        .await;
5310    deterministic.run_until_parked();
5311    assert_eq!(
5312        contacts(&client_a, cx_a),
5313        [
5314            ("user_b".to_string(), "online", "busy"),
5315            ("user_c".to_string(), "online", "free")
5316        ]
5317    );
5318    assert_eq!(
5319        contacts(&client_b, cx_b),
5320        [
5321            ("user_a".to_string(), "online", "busy"),
5322            ("user_c".to_string(), "online", "free"),
5323            ("user_d".to_string(), "online", "free"),
5324        ]
5325    );
5326    assert_eq!(
5327        contacts(&client_c, cx_c),
5328        [
5329            ("user_a".to_string(), "online", "busy"),
5330            ("user_b".to_string(), "online", "busy")
5331        ]
5332    );
5333    assert_eq!(
5334        contacts(&client_d, cx_d),
5335        [("user_b".to_string(), "online", "busy")]
5336    );
5337
5338    active_call_b.update(cx_b, |call, _| call.decline_incoming().unwrap());
5339    deterministic.run_until_parked();
5340    assert_eq!(
5341        contacts(&client_a, cx_a),
5342        [
5343            ("user_b".to_string(), "online", "free"),
5344            ("user_c".to_string(), "online", "free")
5345        ]
5346    );
5347    assert_eq!(
5348        contacts(&client_b, cx_b),
5349        [
5350            ("user_a".to_string(), "online", "free"),
5351            ("user_c".to_string(), "online", "free"),
5352            ("user_d".to_string(), "online", "free")
5353        ]
5354    );
5355    assert_eq!(
5356        contacts(&client_c, cx_c),
5357        [
5358            ("user_a".to_string(), "online", "free"),
5359            ("user_b".to_string(), "online", "free")
5360        ]
5361    );
5362    assert_eq!(
5363        contacts(&client_d, cx_d),
5364        [("user_b".to_string(), "online", "free")]
5365    );
5366
5367    active_call_c
5368        .update(cx_c, |call, cx| {
5369            call.invite(client_a.user_id().unwrap(), None, cx)
5370        })
5371        .await
5372        .unwrap();
5373    deterministic.run_until_parked();
5374    assert_eq!(
5375        contacts(&client_a, cx_a),
5376        [
5377            ("user_b".to_string(), "online", "free"),
5378            ("user_c".to_string(), "online", "busy")
5379        ]
5380    );
5381    assert_eq!(
5382        contacts(&client_b, cx_b),
5383        [
5384            ("user_a".to_string(), "online", "busy"),
5385            ("user_c".to_string(), "online", "busy"),
5386            ("user_d".to_string(), "online", "free")
5387        ]
5388    );
5389    assert_eq!(
5390        contacts(&client_c, cx_c),
5391        [
5392            ("user_a".to_string(), "online", "busy"),
5393            ("user_b".to_string(), "online", "free")
5394        ]
5395    );
5396    assert_eq!(
5397        contacts(&client_d, cx_d),
5398        [("user_b".to_string(), "online", "free")]
5399    );
5400
5401    active_call_a
5402        .update(cx_a, |call, cx| call.accept_incoming(cx))
5403        .await
5404        .unwrap();
5405    deterministic.run_until_parked();
5406    assert_eq!(
5407        contacts(&client_a, cx_a),
5408        [
5409            ("user_b".to_string(), "online", "free"),
5410            ("user_c".to_string(), "online", "busy")
5411        ]
5412    );
5413    assert_eq!(
5414        contacts(&client_b, cx_b),
5415        [
5416            ("user_a".to_string(), "online", "busy"),
5417            ("user_c".to_string(), "online", "busy"),
5418            ("user_d".to_string(), "online", "free")
5419        ]
5420    );
5421    assert_eq!(
5422        contacts(&client_c, cx_c),
5423        [
5424            ("user_a".to_string(), "online", "busy"),
5425            ("user_b".to_string(), "online", "free")
5426        ]
5427    );
5428    assert_eq!(
5429        contacts(&client_d, cx_d),
5430        [("user_b".to_string(), "online", "free")]
5431    );
5432
5433    active_call_a
5434        .update(cx_a, |call, cx| {
5435            call.invite(client_b.user_id().unwrap(), None, cx)
5436        })
5437        .await
5438        .unwrap();
5439    deterministic.run_until_parked();
5440    assert_eq!(
5441        contacts(&client_a, cx_a),
5442        [
5443            ("user_b".to_string(), "online", "busy"),
5444            ("user_c".to_string(), "online", "busy")
5445        ]
5446    );
5447    assert_eq!(
5448        contacts(&client_b, cx_b),
5449        [
5450            ("user_a".to_string(), "online", "busy"),
5451            ("user_c".to_string(), "online", "busy"),
5452            ("user_d".to_string(), "online", "free")
5453        ]
5454    );
5455    assert_eq!(
5456        contacts(&client_c, cx_c),
5457        [
5458            ("user_a".to_string(), "online", "busy"),
5459            ("user_b".to_string(), "online", "busy")
5460        ]
5461    );
5462    assert_eq!(
5463        contacts(&client_d, cx_d),
5464        [("user_b".to_string(), "online", "busy")]
5465    );
5466
5467    active_call_a.update(cx_a, |call, cx| call.hang_up(cx).unwrap());
5468    deterministic.run_until_parked();
5469    assert_eq!(
5470        contacts(&client_a, cx_a),
5471        [
5472            ("user_b".to_string(), "online", "free"),
5473            ("user_c".to_string(), "online", "free")
5474        ]
5475    );
5476    assert_eq!(
5477        contacts(&client_b, cx_b),
5478        [
5479            ("user_a".to_string(), "online", "free"),
5480            ("user_c".to_string(), "online", "free"),
5481            ("user_d".to_string(), "online", "free")
5482        ]
5483    );
5484    assert_eq!(
5485        contacts(&client_c, cx_c),
5486        [
5487            ("user_a".to_string(), "online", "free"),
5488            ("user_b".to_string(), "online", "free")
5489        ]
5490    );
5491    assert_eq!(
5492        contacts(&client_d, cx_d),
5493        [("user_b".to_string(), "online", "free")]
5494    );
5495
5496    active_call_a
5497        .update(cx_a, |call, cx| {
5498            call.invite(client_b.user_id().unwrap(), None, cx)
5499        })
5500        .await
5501        .unwrap();
5502    deterministic.run_until_parked();
5503    assert_eq!(
5504        contacts(&client_a, cx_a),
5505        [
5506            ("user_b".to_string(), "online", "busy"),
5507            ("user_c".to_string(), "online", "free")
5508        ]
5509    );
5510    assert_eq!(
5511        contacts(&client_b, cx_b),
5512        [
5513            ("user_a".to_string(), "online", "busy"),
5514            ("user_c".to_string(), "online", "free"),
5515            ("user_d".to_string(), "online", "free")
5516        ]
5517    );
5518    assert_eq!(
5519        contacts(&client_c, cx_c),
5520        [
5521            ("user_a".to_string(), "online", "busy"),
5522            ("user_b".to_string(), "online", "busy")
5523        ]
5524    );
5525    assert_eq!(
5526        contacts(&client_d, cx_d),
5527        [("user_b".to_string(), "online", "busy")]
5528    );
5529
5530    server.forbid_connections();
5531    server.disconnect_client(client_a.peer_id().unwrap());
5532    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5533    assert_eq!(contacts(&client_a, cx_a), []);
5534    assert_eq!(
5535        contacts(&client_b, cx_b),
5536        [
5537            ("user_a".to_string(), "offline", "free"),
5538            ("user_c".to_string(), "online", "free"),
5539            ("user_d".to_string(), "online", "free")
5540        ]
5541    );
5542    assert_eq!(
5543        contacts(&client_c, cx_c),
5544        [
5545            ("user_a".to_string(), "offline", "free"),
5546            ("user_b".to_string(), "online", "free")
5547        ]
5548    );
5549    assert_eq!(
5550        contacts(&client_d, cx_d),
5551        [("user_b".to_string(), "online", "free")]
5552    );
5553
5554    // Test removing a contact
5555    client_b
5556        .user_store
5557        .update(cx_b, |store, cx| {
5558            store.remove_contact(client_c.user_id().unwrap(), cx)
5559        })
5560        .await
5561        .unwrap();
5562    deterministic.run_until_parked();
5563    assert_eq!(
5564        contacts(&client_b, cx_b),
5565        [
5566            ("user_a".to_string(), "offline", "free"),
5567            ("user_d".to_string(), "online", "free")
5568        ]
5569    );
5570    assert_eq!(
5571        contacts(&client_c, cx_c),
5572        [("user_a".to_string(), "offline", "free"),]
5573    );
5574
5575    fn contacts(
5576        client: &TestClient,
5577        cx: &TestAppContext,
5578    ) -> Vec<(String, &'static str, &'static str)> {
5579        client.user_store.read_with(cx, |store, _| {
5580            store
5581                .contacts()
5582                .iter()
5583                .map(|contact| {
5584                    (
5585                        contact.user.github_login.clone(),
5586                        if contact.online { "online" } else { "offline" },
5587                        if contact.busy { "busy" } else { "free" },
5588                    )
5589                })
5590                .collect()
5591        })
5592    }
5593}
5594
5595#[gpui::test(iterations = 10)]
5596async fn test_contact_requests(
5597    deterministic: Arc<Deterministic>,
5598    cx_a: &mut TestAppContext,
5599    cx_a2: &mut TestAppContext,
5600    cx_b: &mut TestAppContext,
5601    cx_b2: &mut TestAppContext,
5602    cx_c: &mut TestAppContext,
5603    cx_c2: &mut TestAppContext,
5604) {
5605    deterministic.forbid_parking();
5606
5607    // Connect to a server as 3 clients.
5608    let mut server = TestServer::start(&deterministic).await;
5609    let client_a = server.create_client(cx_a, "user_a").await;
5610    let client_a2 = server.create_client(cx_a2, "user_a").await;
5611    let client_b = server.create_client(cx_b, "user_b").await;
5612    let client_b2 = server.create_client(cx_b2, "user_b").await;
5613    let client_c = server.create_client(cx_c, "user_c").await;
5614    let client_c2 = server.create_client(cx_c2, "user_c").await;
5615
5616    assert_eq!(client_a.user_id().unwrap(), client_a2.user_id().unwrap());
5617    assert_eq!(client_b.user_id().unwrap(), client_b2.user_id().unwrap());
5618    assert_eq!(client_c.user_id().unwrap(), client_c2.user_id().unwrap());
5619
5620    // User A and User C request that user B become their contact.
5621    client_a
5622        .user_store
5623        .update(cx_a, |store, cx| {
5624            store.request_contact(client_b.user_id().unwrap(), cx)
5625        })
5626        .await
5627        .unwrap();
5628    client_c
5629        .user_store
5630        .update(cx_c, |store, cx| {
5631            store.request_contact(client_b.user_id().unwrap(), cx)
5632        })
5633        .await
5634        .unwrap();
5635    deterministic.run_until_parked();
5636
5637    // All users see the pending request appear in all their clients.
5638    assert_eq!(
5639        client_a.summarize_contacts(cx_a).outgoing_requests,
5640        &["user_b"]
5641    );
5642    assert_eq!(
5643        client_a2.summarize_contacts(cx_a2).outgoing_requests,
5644        &["user_b"]
5645    );
5646    assert_eq!(
5647        client_b.summarize_contacts(cx_b).incoming_requests,
5648        &["user_a", "user_c"]
5649    );
5650    assert_eq!(
5651        client_b2.summarize_contacts(cx_b2).incoming_requests,
5652        &["user_a", "user_c"]
5653    );
5654    assert_eq!(
5655        client_c.summarize_contacts(cx_c).outgoing_requests,
5656        &["user_b"]
5657    );
5658    assert_eq!(
5659        client_c2.summarize_contacts(cx_c2).outgoing_requests,
5660        &["user_b"]
5661    );
5662
5663    // Contact requests are present upon connecting (tested here via disconnect/reconnect)
5664    disconnect_and_reconnect(&client_a, cx_a).await;
5665    disconnect_and_reconnect(&client_b, cx_b).await;
5666    disconnect_and_reconnect(&client_c, cx_c).await;
5667    deterministic.run_until_parked();
5668    assert_eq!(
5669        client_a.summarize_contacts(cx_a).outgoing_requests,
5670        &["user_b"]
5671    );
5672    assert_eq!(
5673        client_b.summarize_contacts(cx_b).incoming_requests,
5674        &["user_a", "user_c"]
5675    );
5676    assert_eq!(
5677        client_c.summarize_contacts(cx_c).outgoing_requests,
5678        &["user_b"]
5679    );
5680
5681    // User B accepts the request from user A.
5682    client_b
5683        .user_store
5684        .update(cx_b, |store, cx| {
5685            store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
5686        })
5687        .await
5688        .unwrap();
5689
5690    deterministic.run_until_parked();
5691
5692    // User B sees user A as their contact now in all client, and the incoming request from them is removed.
5693    let contacts_b = client_b.summarize_contacts(cx_b);
5694    assert_eq!(contacts_b.current, &["user_a"]);
5695    assert_eq!(contacts_b.incoming_requests, &["user_c"]);
5696    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
5697    assert_eq!(contacts_b2.current, &["user_a"]);
5698    assert_eq!(contacts_b2.incoming_requests, &["user_c"]);
5699
5700    // User A sees user B as their contact now in all clients, and the outgoing request to them is removed.
5701    let contacts_a = client_a.summarize_contacts(cx_a);
5702    assert_eq!(contacts_a.current, &["user_b"]);
5703    assert!(contacts_a.outgoing_requests.is_empty());
5704    let contacts_a2 = client_a2.summarize_contacts(cx_a2);
5705    assert_eq!(contacts_a2.current, &["user_b"]);
5706    assert!(contacts_a2.outgoing_requests.is_empty());
5707
5708    // Contacts are present upon connecting (tested here via disconnect/reconnect)
5709    disconnect_and_reconnect(&client_a, cx_a).await;
5710    disconnect_and_reconnect(&client_b, cx_b).await;
5711    disconnect_and_reconnect(&client_c, cx_c).await;
5712    deterministic.run_until_parked();
5713    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
5714    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
5715    assert_eq!(
5716        client_b.summarize_contacts(cx_b).incoming_requests,
5717        &["user_c"]
5718    );
5719    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
5720    assert_eq!(
5721        client_c.summarize_contacts(cx_c).outgoing_requests,
5722        &["user_b"]
5723    );
5724
5725    // User B rejects the request from user C.
5726    client_b
5727        .user_store
5728        .update(cx_b, |store, cx| {
5729            store.respond_to_contact_request(client_c.user_id().unwrap(), false, cx)
5730        })
5731        .await
5732        .unwrap();
5733
5734    deterministic.run_until_parked();
5735
5736    // User B doesn't see user C as their contact, and the incoming request from them is removed.
5737    let contacts_b = client_b.summarize_contacts(cx_b);
5738    assert_eq!(contacts_b.current, &["user_a"]);
5739    assert!(contacts_b.incoming_requests.is_empty());
5740    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
5741    assert_eq!(contacts_b2.current, &["user_a"]);
5742    assert!(contacts_b2.incoming_requests.is_empty());
5743
5744    // User C doesn't see user B as their contact, and the outgoing request to them is removed.
5745    let contacts_c = client_c.summarize_contacts(cx_c);
5746    assert!(contacts_c.current.is_empty());
5747    assert!(contacts_c.outgoing_requests.is_empty());
5748    let contacts_c2 = client_c2.summarize_contacts(cx_c2);
5749    assert!(contacts_c2.current.is_empty());
5750    assert!(contacts_c2.outgoing_requests.is_empty());
5751
5752    // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect)
5753    disconnect_and_reconnect(&client_a, cx_a).await;
5754    disconnect_and_reconnect(&client_b, cx_b).await;
5755    disconnect_and_reconnect(&client_c, cx_c).await;
5756    deterministic.run_until_parked();
5757    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
5758    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
5759    assert!(client_b
5760        .summarize_contacts(cx_b)
5761        .incoming_requests
5762        .is_empty());
5763    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
5764    assert!(client_c
5765        .summarize_contacts(cx_c)
5766        .outgoing_requests
5767        .is_empty());
5768
5769    async fn disconnect_and_reconnect(client: &TestClient, cx: &mut TestAppContext) {
5770        client.disconnect(&cx.to_async()).unwrap();
5771        client.clear_contacts(cx).await;
5772        client
5773            .authenticate_and_connect(false, &cx.to_async())
5774            .await
5775            .unwrap();
5776    }
5777}
5778
5779#[gpui::test(iterations = 10)]
5780async fn test_following(
5781    deterministic: Arc<Deterministic>,
5782    cx_a: &mut TestAppContext,
5783    cx_b: &mut TestAppContext,
5784) {
5785    deterministic.forbid_parking();
5786    cx_a.update(editor::init);
5787    cx_b.update(editor::init);
5788
5789    let mut server = TestServer::start(&deterministic).await;
5790    let client_a = server.create_client(cx_a, "user_a").await;
5791    let client_b = server.create_client(cx_b, "user_b").await;
5792    server
5793        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5794        .await;
5795    let active_call_a = cx_a.read(ActiveCall::global);
5796    let active_call_b = cx_b.read(ActiveCall::global);
5797
5798    client_a
5799        .fs
5800        .insert_tree(
5801            "/a",
5802            json!({
5803                "1.txt": "one\none\none",
5804                "2.txt": "two\ntwo\ntwo",
5805                "3.txt": "three\nthree\nthree",
5806            }),
5807        )
5808        .await;
5809    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
5810    active_call_a
5811        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
5812        .await
5813        .unwrap();
5814
5815    let project_id = active_call_a
5816        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5817        .await
5818        .unwrap();
5819    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5820    active_call_b
5821        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
5822        .await
5823        .unwrap();
5824
5825    // Client A opens some editors.
5826    let workspace_a = client_a.build_workspace(&project_a, cx_a);
5827    let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
5828    let editor_a1 = workspace_a
5829        .update(cx_a, |workspace, cx| {
5830            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
5831        })
5832        .await
5833        .unwrap()
5834        .downcast::<Editor>()
5835        .unwrap();
5836    let editor_a2 = workspace_a
5837        .update(cx_a, |workspace, cx| {
5838            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
5839        })
5840        .await
5841        .unwrap()
5842        .downcast::<Editor>()
5843        .unwrap();
5844
5845    // Client B opens an editor.
5846    let workspace_b = client_b.build_workspace(&project_b, cx_b);
5847    let editor_b1 = workspace_b
5848        .update(cx_b, |workspace, cx| {
5849            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
5850        })
5851        .await
5852        .unwrap()
5853        .downcast::<Editor>()
5854        .unwrap();
5855
5856    let client_a_id = project_b.read_with(cx_b, |project, _| {
5857        project.collaborators().values().next().unwrap().peer_id
5858    });
5859    let client_b_id = project_a.read_with(cx_a, |project, _| {
5860        project.collaborators().values().next().unwrap().peer_id
5861    });
5862
5863    // When client B starts following client A, all visible view states are replicated to client B.
5864    editor_a1.update(cx_a, |editor, cx| {
5865        editor.change_selections(None, cx, |s| s.select_ranges([0..1]))
5866    });
5867    editor_a2.update(cx_a, |editor, cx| {
5868        editor.change_selections(None, cx, |s| s.select_ranges([2..3]))
5869    });
5870    workspace_b
5871        .update(cx_b, |workspace, cx| {
5872            workspace
5873                .toggle_follow(&ToggleFollow(client_a_id), cx)
5874                .unwrap()
5875        })
5876        .await
5877        .unwrap();
5878
5879    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
5880        workspace
5881            .active_item(cx)
5882            .unwrap()
5883            .downcast::<Editor>()
5884            .unwrap()
5885    });
5886    assert_eq!(
5887        cx_b.read(|cx| editor_b2.project_path(cx)),
5888        Some((worktree_id, "2.txt").into())
5889    );
5890    assert_eq!(
5891        editor_b2.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
5892        vec![2..3]
5893    );
5894    assert_eq!(
5895        editor_b1.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
5896        vec![0..1]
5897    );
5898
5899    // When client A activates a different editor, client B does so as well.
5900    workspace_a.update(cx_a, |workspace, cx| {
5901        workspace.activate_item(&editor_a1, cx)
5902    });
5903    deterministic.run_until_parked();
5904    workspace_b.read_with(cx_b, |workspace, cx| {
5905        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
5906    });
5907
5908    // When client A opens a multibuffer, client B does so as well.
5909    let multibuffer_a = cx_a.add_model(|cx| {
5910        let buffer_a1 = project_a.update(cx, |project, cx| {
5911            project
5912                .get_open_buffer(&(worktree_id, "1.txt").into(), cx)
5913                .unwrap()
5914        });
5915        let buffer_a2 = project_a.update(cx, |project, cx| {
5916            project
5917                .get_open_buffer(&(worktree_id, "2.txt").into(), cx)
5918                .unwrap()
5919        });
5920        let mut result = MultiBuffer::new(0);
5921        result.push_excerpts(
5922            buffer_a1,
5923            [ExcerptRange {
5924                context: 0..3,
5925                primary: None,
5926            }],
5927            cx,
5928        );
5929        result.push_excerpts(
5930            buffer_a2,
5931            [ExcerptRange {
5932                context: 4..7,
5933                primary: None,
5934            }],
5935            cx,
5936        );
5937        result
5938    });
5939    let multibuffer_editor_a = workspace_a.update(cx_a, |workspace, cx| {
5940        let editor =
5941            cx.add_view(|cx| Editor::for_multibuffer(multibuffer_a, Some(project_a.clone()), cx));
5942        workspace.add_item(Box::new(editor.clone()), cx);
5943        editor
5944    });
5945    deterministic.run_until_parked();
5946    let multibuffer_editor_b = workspace_b.read_with(cx_b, |workspace, cx| {
5947        workspace
5948            .active_item(cx)
5949            .unwrap()
5950            .downcast::<Editor>()
5951            .unwrap()
5952    });
5953    assert_eq!(
5954        multibuffer_editor_a.read_with(cx_a, |editor, cx| editor.text(cx)),
5955        multibuffer_editor_b.read_with(cx_b, |editor, cx| editor.text(cx)),
5956    );
5957
5958    // When client A navigates back and forth, client B does so as well.
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_b1.id());
5967    });
5968
5969    workspace_a
5970        .update(cx_a, |workspace, cx| {
5971            workspace::Pane::go_back(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_b2.id());
5977    });
5978
5979    workspace_a
5980        .update(cx_a, |workspace, cx| {
5981            workspace::Pane::go_forward(workspace, None, cx)
5982        })
5983        .await;
5984    deterministic.run_until_parked();
5985    workspace_b.read_with(cx_b, |workspace, cx| {
5986        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
5987    });
5988
5989    // Changes to client A's editor are reflected on client B.
5990    editor_a1.update(cx_a, |editor, cx| {
5991        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2]));
5992    });
5993    deterministic.run_until_parked();
5994    editor_b1.read_with(cx_b, |editor, cx| {
5995        assert_eq!(editor.selections.ranges(cx), &[1..1, 2..2]);
5996    });
5997
5998    editor_a1.update(cx_a, |editor, cx| editor.set_text("TWO", cx));
5999    deterministic.run_until_parked();
6000    editor_b1.read_with(cx_b, |editor, cx| assert_eq!(editor.text(cx), "TWO"));
6001
6002    editor_a1.update(cx_a, |editor, cx| {
6003        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
6004        editor.set_scroll_position(vec2f(0., 100.), cx);
6005    });
6006    deterministic.run_until_parked();
6007    editor_b1.read_with(cx_b, |editor, cx| {
6008        assert_eq!(editor.selections.ranges(cx), &[3..3]);
6009    });
6010
6011    // After unfollowing, client B stops receiving updates from client A.
6012    workspace_b.update(cx_b, |workspace, cx| {
6013        workspace.unfollow(&workspace.active_pane().clone(), cx)
6014    });
6015    workspace_a.update(cx_a, |workspace, cx| {
6016        workspace.activate_item(&editor_a2, cx)
6017    });
6018    deterministic.run_until_parked();
6019    assert_eq!(
6020        workspace_b.read_with(cx_b, |workspace, cx| workspace
6021            .active_item(cx)
6022            .unwrap()
6023            .id()),
6024        editor_b1.id()
6025    );
6026
6027    // Client A starts following client B.
6028    workspace_a
6029        .update(cx_a, |workspace, cx| {
6030            workspace
6031                .toggle_follow(&ToggleFollow(client_b_id), cx)
6032                .unwrap()
6033        })
6034        .await
6035        .unwrap();
6036    assert_eq!(
6037        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
6038        Some(client_b_id)
6039    );
6040    assert_eq!(
6041        workspace_a.read_with(cx_a, |workspace, cx| workspace
6042            .active_item(cx)
6043            .unwrap()
6044            .id()),
6045        editor_a1.id()
6046    );
6047
6048    // Client B activates an external window, which causes a new screen-sharing item to be added to the pane.
6049    let display = MacOSDisplay::new();
6050    active_call_b
6051        .update(cx_b, |call, cx| call.set_location(None, cx))
6052        .await
6053        .unwrap();
6054    active_call_b
6055        .update(cx_b, |call, cx| {
6056            call.room().unwrap().update(cx, |room, cx| {
6057                room.set_display_sources(vec![display.clone()]);
6058                room.share_screen(cx)
6059            })
6060        })
6061        .await
6062        .unwrap();
6063    deterministic.run_until_parked();
6064    let shared_screen = workspace_a.read_with(cx_a, |workspace, cx| {
6065        workspace
6066            .active_item(cx)
6067            .unwrap()
6068            .downcast::<SharedScreen>()
6069            .unwrap()
6070    });
6071
6072    // Client B activates Zed again, which causes the previous editor to become focused again.
6073    active_call_b
6074        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6075        .await
6076        .unwrap();
6077    deterministic.run_until_parked();
6078    workspace_a.read_with(cx_a, |workspace, cx| {
6079        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_a1.id())
6080    });
6081
6082    // Client B activates a multibuffer that was created by following client A. Client A returns to that multibuffer.
6083    workspace_b.update(cx_b, |workspace, cx| {
6084        workspace.activate_item(&multibuffer_editor_b, cx)
6085    });
6086    deterministic.run_until_parked();
6087    workspace_a.read_with(cx_a, |workspace, cx| {
6088        assert_eq!(
6089            workspace.active_item(cx).unwrap().id(),
6090            multibuffer_editor_a.id()
6091        )
6092    });
6093
6094    // Client B activates an external window again, and the previously-opened screen-sharing item
6095    // gets activated.
6096    active_call_b
6097        .update(cx_b, |call, cx| call.set_location(None, cx))
6098        .await
6099        .unwrap();
6100    deterministic.run_until_parked();
6101    assert_eq!(
6102        workspace_a.read_with(cx_a, |workspace, cx| workspace
6103            .active_item(cx)
6104            .unwrap()
6105            .id()),
6106        shared_screen.id()
6107    );
6108
6109    // Following interrupts when client B disconnects.
6110    client_b.disconnect(&cx_b.to_async()).unwrap();
6111    deterministic.advance_clock(RECONNECT_TIMEOUT);
6112    assert_eq!(
6113        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
6114        None
6115    );
6116}
6117
6118#[gpui::test]
6119async fn test_following_tab_order(
6120    deterministic: Arc<Deterministic>,
6121    cx_a: &mut TestAppContext,
6122    cx_b: &mut TestAppContext,
6123) {
6124    cx_a.update(editor::init);
6125    cx_b.update(editor::init);
6126
6127    let mut server = TestServer::start(&deterministic).await;
6128    let client_a = server.create_client(cx_a, "user_a").await;
6129    let client_b = server.create_client(cx_b, "user_b").await;
6130    server
6131        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6132        .await;
6133    let active_call_a = cx_a.read(ActiveCall::global);
6134    let active_call_b = cx_b.read(ActiveCall::global);
6135
6136    client_a
6137        .fs
6138        .insert_tree(
6139            "/a",
6140            json!({
6141                "1.txt": "one",
6142                "2.txt": "two",
6143                "3.txt": "three",
6144            }),
6145        )
6146        .await;
6147    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
6148    active_call_a
6149        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
6150        .await
6151        .unwrap();
6152
6153    let project_id = active_call_a
6154        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6155        .await
6156        .unwrap();
6157    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6158    active_call_b
6159        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6160        .await
6161        .unwrap();
6162
6163    let workspace_a = client_a.build_workspace(&project_a, cx_a);
6164    let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
6165
6166    let workspace_b = client_b.build_workspace(&project_b, cx_b);
6167    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
6168
6169    let client_b_id = project_a.read_with(cx_a, |project, _| {
6170        project.collaborators().values().next().unwrap().peer_id
6171    });
6172
6173    //Open 1, 3 in that order on client A
6174    workspace_a
6175        .update(cx_a, |workspace, cx| {
6176            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6177        })
6178        .await
6179        .unwrap();
6180    workspace_a
6181        .update(cx_a, |workspace, cx| {
6182            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
6183        })
6184        .await
6185        .unwrap();
6186
6187    let pane_paths = |pane: &ViewHandle<workspace::Pane>, cx: &mut TestAppContext| {
6188        pane.update(cx, |pane, cx| {
6189            pane.items()
6190                .map(|item| {
6191                    item.project_path(cx)
6192                        .unwrap()
6193                        .path
6194                        .to_str()
6195                        .unwrap()
6196                        .to_owned()
6197                })
6198                .collect::<Vec<_>>()
6199        })
6200    };
6201
6202    //Verify that the tabs opened in the order we expect
6203    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt"]);
6204
6205    //Follow client B as client A
6206    workspace_a
6207        .update(cx_a, |workspace, cx| {
6208            workspace
6209                .toggle_follow(&ToggleFollow(client_b_id), cx)
6210                .unwrap()
6211        })
6212        .await
6213        .unwrap();
6214
6215    //Open just 2 on client B
6216    workspace_b
6217        .update(cx_b, |workspace, cx| {
6218            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
6219        })
6220        .await
6221        .unwrap();
6222    deterministic.run_until_parked();
6223
6224    // Verify that newly opened followed file is at the end
6225    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt", "2.txt"]);
6226
6227    //Open just 1 on client B
6228    workspace_b
6229        .update(cx_b, |workspace, cx| {
6230            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6231        })
6232        .await
6233        .unwrap();
6234    assert_eq!(&pane_paths(&pane_b, cx_b), &["2.txt", "1.txt"]);
6235    deterministic.run_until_parked();
6236
6237    // Verify that following into 1 did not reorder
6238    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt", "2.txt"]);
6239}
6240
6241#[gpui::test(iterations = 10)]
6242async fn test_peers_following_each_other(
6243    deterministic: Arc<Deterministic>,
6244    cx_a: &mut TestAppContext,
6245    cx_b: &mut TestAppContext,
6246) {
6247    deterministic.forbid_parking();
6248    cx_a.update(editor::init);
6249    cx_b.update(editor::init);
6250
6251    let mut server = TestServer::start(&deterministic).await;
6252    let client_a = server.create_client(cx_a, "user_a").await;
6253    let client_b = server.create_client(cx_b, "user_b").await;
6254    server
6255        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6256        .await;
6257    let active_call_a = cx_a.read(ActiveCall::global);
6258    let active_call_b = cx_b.read(ActiveCall::global);
6259
6260    // Client A shares a project.
6261    client_a
6262        .fs
6263        .insert_tree(
6264            "/a",
6265            json!({
6266                "1.txt": "one",
6267                "2.txt": "two",
6268                "3.txt": "three",
6269                "4.txt": "four",
6270            }),
6271        )
6272        .await;
6273    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
6274    active_call_a
6275        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
6276        .await
6277        .unwrap();
6278    let project_id = active_call_a
6279        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6280        .await
6281        .unwrap();
6282
6283    // Client B joins the project.
6284    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6285    active_call_b
6286        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6287        .await
6288        .unwrap();
6289
6290    // Client A opens some editors.
6291    let workspace_a = client_a.build_workspace(&project_a, cx_a);
6292    let pane_a1 = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
6293    let _editor_a1 = workspace_a
6294        .update(cx_a, |workspace, cx| {
6295            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6296        })
6297        .await
6298        .unwrap()
6299        .downcast::<Editor>()
6300        .unwrap();
6301
6302    // Client B opens an editor.
6303    let workspace_b = client_b.build_workspace(&project_b, cx_b);
6304    let pane_b1 = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
6305    let _editor_b1 = workspace_b
6306        .update(cx_b, |workspace, cx| {
6307            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
6308        })
6309        .await
6310        .unwrap()
6311        .downcast::<Editor>()
6312        .unwrap();
6313
6314    // Clients A and B follow each other in split panes
6315    workspace_a.update(cx_a, |workspace, cx| {
6316        workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
6317        let pane_a1 = pane_a1.clone();
6318        cx.defer(move |workspace, _| {
6319            assert_ne!(*workspace.active_pane(), pane_a1);
6320        });
6321    });
6322    workspace_a
6323        .update(cx_a, |workspace, cx| {
6324            let leader_id = *project_a.read(cx).collaborators().keys().next().unwrap();
6325            workspace
6326                .toggle_follow(&workspace::ToggleFollow(leader_id), cx)
6327                .unwrap()
6328        })
6329        .await
6330        .unwrap();
6331    workspace_b.update(cx_b, |workspace, cx| {
6332        workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
6333        let pane_b1 = pane_b1.clone();
6334        cx.defer(move |workspace, _| {
6335            assert_ne!(*workspace.active_pane(), pane_b1);
6336        });
6337    });
6338    workspace_b
6339        .update(cx_b, |workspace, cx| {
6340            let leader_id = *project_b.read(cx).collaborators().keys().next().unwrap();
6341            workspace
6342                .toggle_follow(&workspace::ToggleFollow(leader_id), cx)
6343                .unwrap()
6344        })
6345        .await
6346        .unwrap();
6347
6348    workspace_a.update(cx_a, |workspace, cx| {
6349        workspace.activate_next_pane(cx);
6350    });
6351    // Wait for focus effects to be fully flushed
6352    workspace_a.update(cx_a, |workspace, _| {
6353        assert_eq!(*workspace.active_pane(), pane_a1);
6354    });
6355
6356    workspace_a
6357        .update(cx_a, |workspace, cx| {
6358            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
6359        })
6360        .await
6361        .unwrap();
6362    workspace_b.update(cx_b, |workspace, cx| {
6363        workspace.activate_next_pane(cx);
6364    });
6365
6366    workspace_b
6367        .update(cx_b, |workspace, cx| {
6368            assert_eq!(*workspace.active_pane(), pane_b1);
6369            workspace.open_path((worktree_id, "4.txt"), None, true, cx)
6370        })
6371        .await
6372        .unwrap();
6373    cx_a.foreground().run_until_parked();
6374
6375    // Ensure leader updates don't change the active pane of followers
6376    workspace_a.read_with(cx_a, |workspace, _| {
6377        assert_eq!(*workspace.active_pane(), pane_a1);
6378    });
6379    workspace_b.read_with(cx_b, |workspace, _| {
6380        assert_eq!(*workspace.active_pane(), pane_b1);
6381    });
6382
6383    // Ensure peers following each other doesn't cause an infinite loop.
6384    assert_eq!(
6385        workspace_a.read_with(cx_a, |workspace, cx| workspace
6386            .active_item(cx)
6387            .unwrap()
6388            .project_path(cx)),
6389        Some((worktree_id, "3.txt").into())
6390    );
6391    workspace_a.update(cx_a, |workspace, cx| {
6392        assert_eq!(
6393            workspace.active_item(cx).unwrap().project_path(cx),
6394            Some((worktree_id, "3.txt").into())
6395        );
6396        workspace.activate_next_pane(cx);
6397    });
6398
6399    workspace_a.update(cx_a, |workspace, cx| {
6400        assert_eq!(
6401            workspace.active_item(cx).unwrap().project_path(cx),
6402            Some((worktree_id, "4.txt").into())
6403        );
6404    });
6405
6406    workspace_b.update(cx_b, |workspace, cx| {
6407        assert_eq!(
6408            workspace.active_item(cx).unwrap().project_path(cx),
6409            Some((worktree_id, "4.txt").into())
6410        );
6411        workspace.activate_next_pane(cx);
6412    });
6413
6414    workspace_b.update(cx_b, |workspace, cx| {
6415        assert_eq!(
6416            workspace.active_item(cx).unwrap().project_path(cx),
6417            Some((worktree_id, "3.txt").into())
6418        );
6419    });
6420}
6421
6422#[gpui::test(iterations = 10)]
6423async fn test_auto_unfollowing(
6424    deterministic: Arc<Deterministic>,
6425    cx_a: &mut TestAppContext,
6426    cx_b: &mut TestAppContext,
6427) {
6428    deterministic.forbid_parking();
6429    cx_a.update(editor::init);
6430    cx_b.update(editor::init);
6431
6432    // 2 clients connect to a server.
6433    let mut server = TestServer::start(&deterministic).await;
6434    let client_a = server.create_client(cx_a, "user_a").await;
6435    let client_b = server.create_client(cx_b, "user_b").await;
6436    server
6437        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6438        .await;
6439    let active_call_a = cx_a.read(ActiveCall::global);
6440    let active_call_b = cx_b.read(ActiveCall::global);
6441
6442    // Client A shares a project.
6443    client_a
6444        .fs
6445        .insert_tree(
6446            "/a",
6447            json!({
6448                "1.txt": "one",
6449                "2.txt": "two",
6450                "3.txt": "three",
6451            }),
6452        )
6453        .await;
6454    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
6455    active_call_a
6456        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
6457        .await
6458        .unwrap();
6459
6460    let project_id = active_call_a
6461        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6462        .await
6463        .unwrap();
6464    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6465    active_call_b
6466        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6467        .await
6468        .unwrap();
6469
6470    // Client A opens some editors.
6471    let workspace_a = client_a.build_workspace(&project_a, cx_a);
6472    let _editor_a1 = workspace_a
6473        .update(cx_a, |workspace, cx| {
6474            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6475        })
6476        .await
6477        .unwrap()
6478        .downcast::<Editor>()
6479        .unwrap();
6480
6481    // Client B starts following client A.
6482    let workspace_b = client_b.build_workspace(&project_b, cx_b);
6483    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
6484    let leader_id = project_b.read_with(cx_b, |project, _| {
6485        project.collaborators().values().next().unwrap().peer_id
6486    });
6487    workspace_b
6488        .update(cx_b, |workspace, cx| {
6489            workspace
6490                .toggle_follow(&ToggleFollow(leader_id), cx)
6491                .unwrap()
6492        })
6493        .await
6494        .unwrap();
6495    assert_eq!(
6496        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6497        Some(leader_id)
6498    );
6499    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
6500        workspace
6501            .active_item(cx)
6502            .unwrap()
6503            .downcast::<Editor>()
6504            .unwrap()
6505    });
6506
6507    // When client B moves, it automatically stops following client A.
6508    editor_b2.update(cx_b, |editor, cx| editor.move_right(&editor::MoveRight, cx));
6509    assert_eq!(
6510        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6511        None
6512    );
6513
6514    workspace_b
6515        .update(cx_b, |workspace, cx| {
6516            workspace
6517                .toggle_follow(&ToggleFollow(leader_id), cx)
6518                .unwrap()
6519        })
6520        .await
6521        .unwrap();
6522    assert_eq!(
6523        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6524        Some(leader_id)
6525    );
6526
6527    // When client B edits, it automatically stops following client A.
6528    editor_b2.update(cx_b, |editor, cx| editor.insert("X", cx));
6529    assert_eq!(
6530        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6531        None
6532    );
6533
6534    workspace_b
6535        .update(cx_b, |workspace, cx| {
6536            workspace
6537                .toggle_follow(&ToggleFollow(leader_id), cx)
6538                .unwrap()
6539        })
6540        .await
6541        .unwrap();
6542    assert_eq!(
6543        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6544        Some(leader_id)
6545    );
6546
6547    // When client B scrolls, it automatically stops following client A.
6548    editor_b2.update(cx_b, |editor, cx| {
6549        editor.set_scroll_position(vec2f(0., 3.), cx)
6550    });
6551    assert_eq!(
6552        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6553        None
6554    );
6555
6556    workspace_b
6557        .update(cx_b, |workspace, cx| {
6558            workspace
6559                .toggle_follow(&ToggleFollow(leader_id), cx)
6560                .unwrap()
6561        })
6562        .await
6563        .unwrap();
6564    assert_eq!(
6565        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6566        Some(leader_id)
6567    );
6568
6569    // When client B activates a different pane, it continues following client A in the original pane.
6570    workspace_b.update(cx_b, |workspace, cx| {
6571        workspace.split_pane(pane_b.clone(), SplitDirection::Right, cx)
6572    });
6573    assert_eq!(
6574        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6575        Some(leader_id)
6576    );
6577
6578    workspace_b.update(cx_b, |workspace, cx| workspace.activate_next_pane(cx));
6579    assert_eq!(
6580        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6581        Some(leader_id)
6582    );
6583
6584    // When client B activates a different item in the original pane, it automatically stops following client A.
6585    workspace_b
6586        .update(cx_b, |workspace, cx| {
6587            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
6588        })
6589        .await
6590        .unwrap();
6591    assert_eq!(
6592        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6593        None
6594    );
6595}
6596
6597#[gpui::test(iterations = 10)]
6598async fn test_peers_simultaneously_following_each_other(
6599    deterministic: Arc<Deterministic>,
6600    cx_a: &mut TestAppContext,
6601    cx_b: &mut TestAppContext,
6602) {
6603    deterministic.forbid_parking();
6604    cx_a.update(editor::init);
6605    cx_b.update(editor::init);
6606
6607    let mut server = TestServer::start(&deterministic).await;
6608    let client_a = server.create_client(cx_a, "user_a").await;
6609    let client_b = server.create_client(cx_b, "user_b").await;
6610    server
6611        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6612        .await;
6613    let active_call_a = cx_a.read(ActiveCall::global);
6614
6615    client_a.fs.insert_tree("/a", json!({})).await;
6616    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
6617    let workspace_a = client_a.build_workspace(&project_a, cx_a);
6618    let project_id = active_call_a
6619        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6620        .await
6621        .unwrap();
6622
6623    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6624    let workspace_b = client_b.build_workspace(&project_b, cx_b);
6625
6626    deterministic.run_until_parked();
6627    let client_a_id = project_b.read_with(cx_b, |project, _| {
6628        project.collaborators().values().next().unwrap().peer_id
6629    });
6630    let client_b_id = project_a.read_with(cx_a, |project, _| {
6631        project.collaborators().values().next().unwrap().peer_id
6632    });
6633
6634    let a_follow_b = workspace_a.update(cx_a, |workspace, cx| {
6635        workspace
6636            .toggle_follow(&ToggleFollow(client_b_id), cx)
6637            .unwrap()
6638    });
6639    let b_follow_a = workspace_b.update(cx_b, |workspace, cx| {
6640        workspace
6641            .toggle_follow(&ToggleFollow(client_a_id), cx)
6642            .unwrap()
6643    });
6644
6645    futures::try_join!(a_follow_b, b_follow_a).unwrap();
6646    workspace_a.read_with(cx_a, |workspace, _| {
6647        assert_eq!(
6648            workspace.leader_for_pane(workspace.active_pane()),
6649            Some(client_b_id)
6650        );
6651    });
6652    workspace_b.read_with(cx_b, |workspace, _| {
6653        assert_eq!(
6654            workspace.leader_for_pane(workspace.active_pane()),
6655            Some(client_a_id)
6656        );
6657    });
6658}
6659
6660#[derive(Debug, Eq, PartialEq)]
6661struct RoomParticipants {
6662    remote: Vec<String>,
6663    pending: Vec<String>,
6664}
6665
6666fn room_participants(room: &ModelHandle<Room>, cx: &mut TestAppContext) -> RoomParticipants {
6667    room.read_with(cx, |room, _| {
6668        let mut remote = room
6669            .remote_participants()
6670            .iter()
6671            .map(|(_, participant)| participant.user.github_login.clone())
6672            .collect::<Vec<_>>();
6673        let mut pending = room
6674            .pending_participants()
6675            .iter()
6676            .map(|user| user.github_login.clone())
6677            .collect::<Vec<_>>();
6678        remote.sort();
6679        pending.sort();
6680        RoomParticipants { remote, pending }
6681    })
6682}