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