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