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
3896    // The edits from the LSP are applied, and a final newline is added.
3897    assert_eq!(
3898        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
3899        "let honey = \"two\"\n"
3900    );
3901
3902    // Ensure buffer can be formatted using an external command. Notice how the
3903    // host's configuration is honored as opposed to using the guest's settings.
3904    cx_a.update(|cx| {
3905        cx.update_global(|settings: &mut Settings, _| {
3906            settings.editor_defaults.formatter = Some(Formatter::External {
3907                command: "awk".to_string(),
3908                arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()],
3909            });
3910        });
3911    });
3912    project_b
3913        .update(cx_b, |project, cx| {
3914            project.format(
3915                HashSet::from_iter([buffer_b.clone()]),
3916                true,
3917                FormatTrigger::Save,
3918                cx,
3919            )
3920        })
3921        .await
3922        .unwrap();
3923    assert_eq!(
3924        buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
3925        format!("let honey = \"{}/a.rs\"\n", directory.to_str().unwrap())
3926    );
3927}
3928
3929#[gpui::test(iterations = 10)]
3930async fn test_definition(
3931    deterministic: Arc<Deterministic>,
3932    cx_a: &mut TestAppContext,
3933    cx_b: &mut TestAppContext,
3934) {
3935    deterministic.forbid_parking();
3936    let mut server = TestServer::start(&deterministic).await;
3937    let client_a = server.create_client(cx_a, "user_a").await;
3938    let client_b = server.create_client(cx_b, "user_b").await;
3939    server
3940        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
3941        .await;
3942    let active_call_a = cx_a.read(ActiveCall::global);
3943
3944    // Set up a fake language server.
3945    let mut language = Language::new(
3946        LanguageConfig {
3947            name: "Rust".into(),
3948            path_suffixes: vec!["rs".to_string()],
3949            ..Default::default()
3950        },
3951        Some(tree_sitter_rust::language()),
3952    );
3953    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
3954    client_a.language_registry.add(Arc::new(language));
3955
3956    client_a
3957        .fs
3958        .insert_tree(
3959            "/root",
3960            json!({
3961                "dir-1": {
3962                    "a.rs": "const ONE: usize = b::TWO + b::THREE;",
3963                },
3964                "dir-2": {
3965                    "b.rs": "const TWO: c::T2 = 2;\nconst THREE: usize = 3;",
3966                    "c.rs": "type T2 = usize;",
3967                }
3968            }),
3969        )
3970        .await;
3971    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
3972    let project_id = active_call_a
3973        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
3974        .await
3975        .unwrap();
3976    let project_b = client_b.build_remote_project(project_id, cx_b).await;
3977
3978    // Open the file on client B.
3979    let buffer_b = cx_b
3980        .background()
3981        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
3982        .await
3983        .unwrap();
3984
3985    // Request the definition of a symbol as the guest.
3986    let fake_language_server = fake_language_servers.next().await.unwrap();
3987    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
3988        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
3989            lsp::Location::new(
3990                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
3991                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
3992            ),
3993        )))
3994    });
3995
3996    let definitions_1 = project_b
3997        .update(cx_b, |p, cx| p.definition(&buffer_b, 23, cx))
3998        .await
3999        .unwrap();
4000    cx_b.read(|cx| {
4001        assert_eq!(definitions_1.len(), 1);
4002        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4003        let target_buffer = definitions_1[0].target.buffer.read(cx);
4004        assert_eq!(
4005            target_buffer.text(),
4006            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4007        );
4008        assert_eq!(
4009            definitions_1[0].target.range.to_point(target_buffer),
4010            Point::new(0, 6)..Point::new(0, 9)
4011        );
4012    });
4013
4014    // Try getting more definitions for the same buffer, ensuring the buffer gets reused from
4015    // the previous call to `definition`.
4016    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4017        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4018            lsp::Location::new(
4019                lsp::Url::from_file_path("/root/dir-2/b.rs").unwrap(),
4020                lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)),
4021            ),
4022        )))
4023    });
4024
4025    let definitions_2 = project_b
4026        .update(cx_b, |p, cx| p.definition(&buffer_b, 33, cx))
4027        .await
4028        .unwrap();
4029    cx_b.read(|cx| {
4030        assert_eq!(definitions_2.len(), 1);
4031        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4032        let target_buffer = definitions_2[0].target.buffer.read(cx);
4033        assert_eq!(
4034            target_buffer.text(),
4035            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
4036        );
4037        assert_eq!(
4038            definitions_2[0].target.range.to_point(target_buffer),
4039            Point::new(1, 6)..Point::new(1, 11)
4040        );
4041    });
4042    assert_eq!(
4043        definitions_1[0].target.buffer,
4044        definitions_2[0].target.buffer
4045    );
4046
4047    fake_language_server.handle_request::<lsp::request::GotoTypeDefinition, _, _>(
4048        |req, _| async move {
4049            assert_eq!(
4050                req.text_document_position_params.position,
4051                lsp::Position::new(0, 7)
4052            );
4053            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4054                lsp::Location::new(
4055                    lsp::Url::from_file_path("/root/dir-2/c.rs").unwrap(),
4056                    lsp::Range::new(lsp::Position::new(0, 5), lsp::Position::new(0, 7)),
4057                ),
4058            )))
4059        },
4060    );
4061
4062    let type_definitions = project_b
4063        .update(cx_b, |p, cx| p.type_definition(&buffer_b, 7, cx))
4064        .await
4065        .unwrap();
4066    cx_b.read(|cx| {
4067        assert_eq!(type_definitions.len(), 1);
4068        let target_buffer = type_definitions[0].target.buffer.read(cx);
4069        assert_eq!(target_buffer.text(), "type T2 = usize;");
4070        assert_eq!(
4071            type_definitions[0].target.range.to_point(target_buffer),
4072            Point::new(0, 5)..Point::new(0, 7)
4073        );
4074    });
4075}
4076
4077#[gpui::test(iterations = 10)]
4078async fn test_references(
4079    deterministic: Arc<Deterministic>,
4080    cx_a: &mut TestAppContext,
4081    cx_b: &mut TestAppContext,
4082) {
4083    deterministic.forbid_parking();
4084    let mut server = TestServer::start(&deterministic).await;
4085    let client_a = server.create_client(cx_a, "user_a").await;
4086    let client_b = server.create_client(cx_b, "user_b").await;
4087    server
4088        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4089        .await;
4090    let active_call_a = cx_a.read(ActiveCall::global);
4091
4092    // Set up a fake language server.
4093    let mut language = Language::new(
4094        LanguageConfig {
4095            name: "Rust".into(),
4096            path_suffixes: vec!["rs".to_string()],
4097            ..Default::default()
4098        },
4099        Some(tree_sitter_rust::language()),
4100    );
4101    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4102    client_a.language_registry.add(Arc::new(language));
4103
4104    client_a
4105        .fs
4106        .insert_tree(
4107            "/root",
4108            json!({
4109                "dir-1": {
4110                    "one.rs": "const ONE: usize = 1;",
4111                    "two.rs": "const TWO: usize = one::ONE + one::ONE;",
4112                },
4113                "dir-2": {
4114                    "three.rs": "const THREE: usize = two::TWO + one::ONE;",
4115                }
4116            }),
4117        )
4118        .await;
4119    let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
4120    let project_id = active_call_a
4121        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4122        .await
4123        .unwrap();
4124    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4125
4126    // Open the file on client B.
4127    let buffer_b = cx_b
4128        .background()
4129        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
4130        .await
4131        .unwrap();
4132
4133    // Request references to a symbol as the guest.
4134    let fake_language_server = fake_language_servers.next().await.unwrap();
4135    fake_language_server.handle_request::<lsp::request::References, _, _>(|params, _| async move {
4136        assert_eq!(
4137            params.text_document_position.text_document.uri.as_str(),
4138            "file:///root/dir-1/one.rs"
4139        );
4140        Ok(Some(vec![
4141            lsp::Location {
4142                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4143                range: lsp::Range::new(lsp::Position::new(0, 24), lsp::Position::new(0, 27)),
4144            },
4145            lsp::Location {
4146                uri: lsp::Url::from_file_path("/root/dir-1/two.rs").unwrap(),
4147                range: lsp::Range::new(lsp::Position::new(0, 35), lsp::Position::new(0, 38)),
4148            },
4149            lsp::Location {
4150                uri: lsp::Url::from_file_path("/root/dir-2/three.rs").unwrap(),
4151                range: lsp::Range::new(lsp::Position::new(0, 37), lsp::Position::new(0, 40)),
4152            },
4153        ]))
4154    });
4155
4156    let references = project_b
4157        .update(cx_b, |p, cx| p.references(&buffer_b, 7, cx))
4158        .await
4159        .unwrap();
4160    cx_b.read(|cx| {
4161        assert_eq!(references.len(), 3);
4162        assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
4163
4164        let two_buffer = references[0].buffer.read(cx);
4165        let three_buffer = references[2].buffer.read(cx);
4166        assert_eq!(
4167            two_buffer.file().unwrap().path().as_ref(),
4168            Path::new("two.rs")
4169        );
4170        assert_eq!(references[1].buffer, references[0].buffer);
4171        assert_eq!(
4172            three_buffer.file().unwrap().full_path(cx),
4173            Path::new("/root/dir-2/three.rs")
4174        );
4175
4176        assert_eq!(references[0].range.to_offset(two_buffer), 24..27);
4177        assert_eq!(references[1].range.to_offset(two_buffer), 35..38);
4178        assert_eq!(references[2].range.to_offset(three_buffer), 37..40);
4179    });
4180}
4181
4182#[gpui::test(iterations = 10)]
4183async fn test_project_search(
4184    deterministic: Arc<Deterministic>,
4185    cx_a: &mut TestAppContext,
4186    cx_b: &mut TestAppContext,
4187) {
4188    deterministic.forbid_parking();
4189    let mut server = TestServer::start(&deterministic).await;
4190    let client_a = server.create_client(cx_a, "user_a").await;
4191    let client_b = server.create_client(cx_b, "user_b").await;
4192    server
4193        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4194        .await;
4195    let active_call_a = cx_a.read(ActiveCall::global);
4196
4197    client_a
4198        .fs
4199        .insert_tree(
4200            "/root",
4201            json!({
4202                "dir-1": {
4203                    "a": "hello world",
4204                    "b": "goodnight moon",
4205                    "c": "a world of goo",
4206                    "d": "world champion of clown world",
4207                },
4208                "dir-2": {
4209                    "e": "disney world is fun",
4210                }
4211            }),
4212        )
4213        .await;
4214    let (project_a, _) = client_a.build_local_project("/root/dir-1", cx_a).await;
4215    let (worktree_2, _) = project_a
4216        .update(cx_a, |p, cx| {
4217            p.find_or_create_local_worktree("/root/dir-2", true, cx)
4218        })
4219        .await
4220        .unwrap();
4221    worktree_2
4222        .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
4223        .await;
4224    let project_id = active_call_a
4225        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4226        .await
4227        .unwrap();
4228
4229    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4230
4231    // Perform a search as the guest.
4232    let results = project_b
4233        .update(cx_b, |project, cx| {
4234            project.search(SearchQuery::text("world", false, false), cx)
4235        })
4236        .await
4237        .unwrap();
4238
4239    let mut ranges_by_path = results
4240        .into_iter()
4241        .map(|(buffer, ranges)| {
4242            buffer.read_with(cx_b, |buffer, cx| {
4243                let path = buffer.file().unwrap().full_path(cx);
4244                let offset_ranges = ranges
4245                    .into_iter()
4246                    .map(|range| range.to_offset(buffer))
4247                    .collect::<Vec<_>>();
4248                (path, offset_ranges)
4249            })
4250        })
4251        .collect::<Vec<_>>();
4252    ranges_by_path.sort_by_key(|(path, _)| path.clone());
4253
4254    assert_eq!(
4255        ranges_by_path,
4256        &[
4257            (PathBuf::from("dir-1/a"), vec![6..11]),
4258            (PathBuf::from("dir-1/c"), vec![2..7]),
4259            (PathBuf::from("dir-1/d"), vec![0..5, 24..29]),
4260            (PathBuf::from("dir-2/e"), vec![7..12]),
4261        ]
4262    );
4263}
4264
4265#[gpui::test(iterations = 10)]
4266async fn test_document_highlights(
4267    deterministic: Arc<Deterministic>,
4268    cx_a: &mut TestAppContext,
4269    cx_b: &mut TestAppContext,
4270) {
4271    deterministic.forbid_parking();
4272    let mut server = TestServer::start(&deterministic).await;
4273    let client_a = server.create_client(cx_a, "user_a").await;
4274    let client_b = server.create_client(cx_b, "user_b").await;
4275    server
4276        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4277        .await;
4278    let active_call_a = cx_a.read(ActiveCall::global);
4279
4280    client_a
4281        .fs
4282        .insert_tree(
4283            "/root-1",
4284            json!({
4285                "main.rs": "fn double(number: i32) -> i32 { number + number }",
4286            }),
4287        )
4288        .await;
4289
4290    // Set up a fake language server.
4291    let mut language = Language::new(
4292        LanguageConfig {
4293            name: "Rust".into(),
4294            path_suffixes: vec!["rs".to_string()],
4295            ..Default::default()
4296        },
4297        Some(tree_sitter_rust::language()),
4298    );
4299    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4300    client_a.language_registry.add(Arc::new(language));
4301
4302    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4303    let project_id = active_call_a
4304        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4305        .await
4306        .unwrap();
4307    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4308
4309    // Open the file on client B.
4310    let buffer_b = cx_b
4311        .background()
4312        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
4313        .await
4314        .unwrap();
4315
4316    // Request document highlights as the guest.
4317    let fake_language_server = fake_language_servers.next().await.unwrap();
4318    fake_language_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
4319        |params, _| async move {
4320            assert_eq!(
4321                params
4322                    .text_document_position_params
4323                    .text_document
4324                    .uri
4325                    .as_str(),
4326                "file:///root-1/main.rs"
4327            );
4328            assert_eq!(
4329                params.text_document_position_params.position,
4330                lsp::Position::new(0, 34)
4331            );
4332            Ok(Some(vec![
4333                lsp::DocumentHighlight {
4334                    kind: Some(lsp::DocumentHighlightKind::WRITE),
4335                    range: lsp::Range::new(lsp::Position::new(0, 10), lsp::Position::new(0, 16)),
4336                },
4337                lsp::DocumentHighlight {
4338                    kind: Some(lsp::DocumentHighlightKind::READ),
4339                    range: lsp::Range::new(lsp::Position::new(0, 32), lsp::Position::new(0, 38)),
4340                },
4341                lsp::DocumentHighlight {
4342                    kind: Some(lsp::DocumentHighlightKind::READ),
4343                    range: lsp::Range::new(lsp::Position::new(0, 41), lsp::Position::new(0, 47)),
4344                },
4345            ]))
4346        },
4347    );
4348
4349    let highlights = project_b
4350        .update(cx_b, |p, cx| p.document_highlights(&buffer_b, 34, cx))
4351        .await
4352        .unwrap();
4353    buffer_b.read_with(cx_b, |buffer, _| {
4354        let snapshot = buffer.snapshot();
4355
4356        let highlights = highlights
4357            .into_iter()
4358            .map(|highlight| (highlight.kind, highlight.range.to_offset(&snapshot)))
4359            .collect::<Vec<_>>();
4360        assert_eq!(
4361            highlights,
4362            &[
4363                (lsp::DocumentHighlightKind::WRITE, 10..16),
4364                (lsp::DocumentHighlightKind::READ, 32..38),
4365                (lsp::DocumentHighlightKind::READ, 41..47)
4366            ]
4367        )
4368    });
4369}
4370
4371#[gpui::test(iterations = 10)]
4372async fn test_lsp_hover(
4373    deterministic: Arc<Deterministic>,
4374    cx_a: &mut TestAppContext,
4375    cx_b: &mut TestAppContext,
4376) {
4377    deterministic.forbid_parking();
4378    let mut server = TestServer::start(&deterministic).await;
4379    let client_a = server.create_client(cx_a, "user_a").await;
4380    let client_b = server.create_client(cx_b, "user_b").await;
4381    server
4382        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4383        .await;
4384    let active_call_a = cx_a.read(ActiveCall::global);
4385
4386    client_a
4387        .fs
4388        .insert_tree(
4389            "/root-1",
4390            json!({
4391                "main.rs": "use std::collections::HashMap;",
4392            }),
4393        )
4394        .await;
4395
4396    // Set up a fake language server.
4397    let mut language = Language::new(
4398        LanguageConfig {
4399            name: "Rust".into(),
4400            path_suffixes: vec!["rs".to_string()],
4401            ..Default::default()
4402        },
4403        Some(tree_sitter_rust::language()),
4404    );
4405    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4406    client_a.language_registry.add(Arc::new(language));
4407
4408    let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
4409    let project_id = active_call_a
4410        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4411        .await
4412        .unwrap();
4413    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4414
4415    // Open the file as the guest
4416    let buffer_b = cx_b
4417        .background()
4418        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
4419        .await
4420        .unwrap();
4421
4422    // Request hover information as the guest.
4423    let fake_language_server = fake_language_servers.next().await.unwrap();
4424    fake_language_server.handle_request::<lsp::request::HoverRequest, _, _>(
4425        |params, _| async move {
4426            assert_eq!(
4427                params
4428                    .text_document_position_params
4429                    .text_document
4430                    .uri
4431                    .as_str(),
4432                "file:///root-1/main.rs"
4433            );
4434            assert_eq!(
4435                params.text_document_position_params.position,
4436                lsp::Position::new(0, 22)
4437            );
4438            Ok(Some(lsp::Hover {
4439                contents: lsp::HoverContents::Array(vec![
4440                    lsp::MarkedString::String("Test hover content.".to_string()),
4441                    lsp::MarkedString::LanguageString(lsp::LanguageString {
4442                        language: "Rust".to_string(),
4443                        value: "let foo = 42;".to_string(),
4444                    }),
4445                ]),
4446                range: Some(lsp::Range::new(
4447                    lsp::Position::new(0, 22),
4448                    lsp::Position::new(0, 29),
4449                )),
4450            }))
4451        },
4452    );
4453
4454    let hover_info = project_b
4455        .update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
4456        .await
4457        .unwrap()
4458        .unwrap();
4459    buffer_b.read_with(cx_b, |buffer, _| {
4460        let snapshot = buffer.snapshot();
4461        assert_eq!(hover_info.range.unwrap().to_offset(&snapshot), 22..29);
4462        assert_eq!(
4463            hover_info.contents,
4464            vec![
4465                project::HoverBlock {
4466                    text: "Test hover content.".to_string(),
4467                    language: None,
4468                },
4469                project::HoverBlock {
4470                    text: "let foo = 42;".to_string(),
4471                    language: Some("Rust".to_string()),
4472                }
4473            ]
4474        );
4475    });
4476}
4477
4478#[gpui::test(iterations = 10)]
4479async fn test_project_symbols(
4480    deterministic: Arc<Deterministic>,
4481    cx_a: &mut TestAppContext,
4482    cx_b: &mut TestAppContext,
4483) {
4484    deterministic.forbid_parking();
4485    let mut server = TestServer::start(&deterministic).await;
4486    let client_a = server.create_client(cx_a, "user_a").await;
4487    let client_b = server.create_client(cx_b, "user_b").await;
4488    server
4489        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4490        .await;
4491    let active_call_a = cx_a.read(ActiveCall::global);
4492
4493    // Set up a fake language server.
4494    let mut language = Language::new(
4495        LanguageConfig {
4496            name: "Rust".into(),
4497            path_suffixes: vec!["rs".to_string()],
4498            ..Default::default()
4499        },
4500        Some(tree_sitter_rust::language()),
4501    );
4502    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4503    client_a.language_registry.add(Arc::new(language));
4504
4505    client_a
4506        .fs
4507        .insert_tree(
4508            "/code",
4509            json!({
4510                "crate-1": {
4511                    "one.rs": "const ONE: usize = 1;",
4512                },
4513                "crate-2": {
4514                    "two.rs": "const TWO: usize = 2; const THREE: usize = 3;",
4515                },
4516                "private": {
4517                    "passwords.txt": "the-password",
4518                }
4519            }),
4520        )
4521        .await;
4522    let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await;
4523    let project_id = active_call_a
4524        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4525        .await
4526        .unwrap();
4527    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4528
4529    // Cause the language server to start.
4530    let _buffer = cx_b
4531        .background()
4532        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
4533        .await
4534        .unwrap();
4535
4536    let fake_language_server = fake_language_servers.next().await.unwrap();
4537    fake_language_server.handle_request::<lsp::request::WorkspaceSymbol, _, _>(|_, _| async move {
4538        #[allow(deprecated)]
4539        Ok(Some(vec![lsp::SymbolInformation {
4540            name: "TWO".into(),
4541            location: lsp::Location {
4542                uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
4543                range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4544            },
4545            kind: lsp::SymbolKind::CONSTANT,
4546            tags: None,
4547            container_name: None,
4548            deprecated: None,
4549        }]))
4550    });
4551
4552    // Request the definition of a symbol as the guest.
4553    let symbols = project_b
4554        .update(cx_b, |p, cx| p.symbols("two", cx))
4555        .await
4556        .unwrap();
4557    assert_eq!(symbols.len(), 1);
4558    assert_eq!(symbols[0].name, "TWO");
4559
4560    // Open one of the returned symbols.
4561    let buffer_b_2 = project_b
4562        .update(cx_b, |project, cx| {
4563            project.open_buffer_for_symbol(&symbols[0], cx)
4564        })
4565        .await
4566        .unwrap();
4567    buffer_b_2.read_with(cx_b, |buffer, _| {
4568        assert_eq!(
4569            buffer.file().unwrap().path().as_ref(),
4570            Path::new("../crate-2/two.rs")
4571        );
4572    });
4573
4574    // Attempt to craft a symbol and violate host's privacy by opening an arbitrary file.
4575    let mut fake_symbol = symbols[0].clone();
4576    fake_symbol.path.path = Path::new("/code/secrets").into();
4577    let error = project_b
4578        .update(cx_b, |project, cx| {
4579            project.open_buffer_for_symbol(&fake_symbol, cx)
4580        })
4581        .await
4582        .unwrap_err();
4583    assert!(error.to_string().contains("invalid symbol signature"));
4584}
4585
4586#[gpui::test(iterations = 10)]
4587async fn test_open_buffer_while_getting_definition_pointing_to_it(
4588    deterministic: Arc<Deterministic>,
4589    cx_a: &mut TestAppContext,
4590    cx_b: &mut TestAppContext,
4591    mut rng: StdRng,
4592) {
4593    deterministic.forbid_parking();
4594    let mut server = TestServer::start(&deterministic).await;
4595    let client_a = server.create_client(cx_a, "user_a").await;
4596    let client_b = server.create_client(cx_b, "user_b").await;
4597    server
4598        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4599        .await;
4600    let active_call_a = cx_a.read(ActiveCall::global);
4601
4602    // Set up a fake language server.
4603    let mut language = Language::new(
4604        LanguageConfig {
4605            name: "Rust".into(),
4606            path_suffixes: vec!["rs".to_string()],
4607            ..Default::default()
4608        },
4609        Some(tree_sitter_rust::language()),
4610    );
4611    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4612    client_a.language_registry.add(Arc::new(language));
4613
4614    client_a
4615        .fs
4616        .insert_tree(
4617            "/root",
4618            json!({
4619                "a.rs": "const ONE: usize = b::TWO;",
4620                "b.rs": "const TWO: usize = 2",
4621            }),
4622        )
4623        .await;
4624    let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await;
4625    let project_id = active_call_a
4626        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4627        .await
4628        .unwrap();
4629    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4630
4631    let buffer_b1 = cx_b
4632        .background()
4633        .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
4634        .await
4635        .unwrap();
4636
4637    let fake_language_server = fake_language_servers.next().await.unwrap();
4638    fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(|_, _| async move {
4639        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
4640            lsp::Location::new(
4641                lsp::Url::from_file_path("/root/b.rs").unwrap(),
4642                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
4643            ),
4644        )))
4645    });
4646
4647    let definitions;
4648    let buffer_b2;
4649    if rng.gen() {
4650        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
4651        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
4652    } else {
4653        buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
4654        definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
4655    }
4656
4657    let buffer_b2 = buffer_b2.await.unwrap();
4658    let definitions = definitions.await.unwrap();
4659    assert_eq!(definitions.len(), 1);
4660    assert_eq!(definitions[0].target.buffer, buffer_b2);
4661}
4662
4663#[gpui::test(iterations = 10)]
4664async fn test_collaborating_with_code_actions(
4665    deterministic: Arc<Deterministic>,
4666    cx_a: &mut TestAppContext,
4667    cx_b: &mut TestAppContext,
4668) {
4669    deterministic.forbid_parking();
4670    cx_b.update(editor::init);
4671    let mut server = TestServer::start(&deterministic).await;
4672    let client_a = server.create_client(cx_a, "user_a").await;
4673    let client_b = server.create_client(cx_b, "user_b").await;
4674    server
4675        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4676        .await;
4677    let active_call_a = cx_a.read(ActiveCall::global);
4678
4679    // Set up a fake language server.
4680    let mut language = Language::new(
4681        LanguageConfig {
4682            name: "Rust".into(),
4683            path_suffixes: vec!["rs".to_string()],
4684            ..Default::default()
4685        },
4686        Some(tree_sitter_rust::language()),
4687    );
4688    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
4689    client_a.language_registry.add(Arc::new(language));
4690
4691    client_a
4692        .fs
4693        .insert_tree(
4694            "/a",
4695            json!({
4696                "main.rs": "mod other;\nfn main() { let foo = other::foo(); }",
4697                "other.rs": "pub fn foo() -> usize { 4 }",
4698            }),
4699        )
4700        .await;
4701    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
4702    let project_id = active_call_a
4703        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4704        .await
4705        .unwrap();
4706
4707    // Join the project as client B.
4708    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4709    let (_window_b, workspace_b) = cx_b.add_window(|cx| {
4710        Workspace::new(
4711            Default::default(),
4712            0,
4713            project_b.clone(),
4714            |_, _| unimplemented!(),
4715            cx,
4716        )
4717    });
4718    let editor_b = workspace_b
4719        .update(cx_b, |workspace, cx| {
4720            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
4721        })
4722        .await
4723        .unwrap()
4724        .downcast::<Editor>()
4725        .unwrap();
4726
4727    let mut fake_language_server = fake_language_servers.next().await.unwrap();
4728    fake_language_server
4729        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
4730            assert_eq!(
4731                params.text_document.uri,
4732                lsp::Url::from_file_path("/a/main.rs").unwrap(),
4733            );
4734            assert_eq!(params.range.start, lsp::Position::new(0, 0));
4735            assert_eq!(params.range.end, lsp::Position::new(0, 0));
4736            Ok(None)
4737        })
4738        .next()
4739        .await;
4740
4741    // Move cursor to a location that contains code actions.
4742    editor_b.update(cx_b, |editor, cx| {
4743        editor.change_selections(None, cx, |s| {
4744            s.select_ranges([Point::new(1, 31)..Point::new(1, 31)])
4745        });
4746        cx.focus(&editor_b);
4747    });
4748
4749    fake_language_server
4750        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
4751            assert_eq!(
4752                params.text_document.uri,
4753                lsp::Url::from_file_path("/a/main.rs").unwrap(),
4754            );
4755            assert_eq!(params.range.start, lsp::Position::new(1, 31));
4756            assert_eq!(params.range.end, lsp::Position::new(1, 31));
4757
4758            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
4759                lsp::CodeAction {
4760                    title: "Inline into all callers".to_string(),
4761                    edit: Some(lsp::WorkspaceEdit {
4762                        changes: Some(
4763                            [
4764                                (
4765                                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
4766                                    vec![lsp::TextEdit::new(
4767                                        lsp::Range::new(
4768                                            lsp::Position::new(1, 22),
4769                                            lsp::Position::new(1, 34),
4770                                        ),
4771                                        "4".to_string(),
4772                                    )],
4773                                ),
4774                                (
4775                                    lsp::Url::from_file_path("/a/other.rs").unwrap(),
4776                                    vec![lsp::TextEdit::new(
4777                                        lsp::Range::new(
4778                                            lsp::Position::new(0, 0),
4779                                            lsp::Position::new(0, 27),
4780                                        ),
4781                                        "".to_string(),
4782                                    )],
4783                                ),
4784                            ]
4785                            .into_iter()
4786                            .collect(),
4787                        ),
4788                        ..Default::default()
4789                    }),
4790                    data: Some(json!({
4791                        "codeActionParams": {
4792                            "range": {
4793                                "start": {"line": 1, "column": 31},
4794                                "end": {"line": 1, "column": 31},
4795                            }
4796                        }
4797                    })),
4798                    ..Default::default()
4799                },
4800            )]))
4801        })
4802        .next()
4803        .await;
4804
4805    // Toggle code actions and wait for them to display.
4806    editor_b.update(cx_b, |editor, cx| {
4807        editor.toggle_code_actions(
4808            &ToggleCodeActions {
4809                deployed_from_indicator: false,
4810            },
4811            cx,
4812        );
4813    });
4814    cx_a.foreground().run_until_parked();
4815    editor_b.read_with(cx_b, |editor, _| assert!(editor.context_menu_visible()));
4816
4817    fake_language_server.remove_request_handler::<lsp::request::CodeActionRequest>();
4818
4819    // Confirming the code action will trigger a resolve request.
4820    let confirm_action = workspace_b
4821        .update(cx_b, |workspace, cx| {
4822            Editor::confirm_code_action(workspace, &ConfirmCodeAction { item_ix: Some(0) }, cx)
4823        })
4824        .unwrap();
4825    fake_language_server.handle_request::<lsp::request::CodeActionResolveRequest, _, _>(
4826        |_, _| async move {
4827            Ok(lsp::CodeAction {
4828                title: "Inline into all callers".to_string(),
4829                edit: Some(lsp::WorkspaceEdit {
4830                    changes: Some(
4831                        [
4832                            (
4833                                lsp::Url::from_file_path("/a/main.rs").unwrap(),
4834                                vec![lsp::TextEdit::new(
4835                                    lsp::Range::new(
4836                                        lsp::Position::new(1, 22),
4837                                        lsp::Position::new(1, 34),
4838                                    ),
4839                                    "4".to_string(),
4840                                )],
4841                            ),
4842                            (
4843                                lsp::Url::from_file_path("/a/other.rs").unwrap(),
4844                                vec![lsp::TextEdit::new(
4845                                    lsp::Range::new(
4846                                        lsp::Position::new(0, 0),
4847                                        lsp::Position::new(0, 27),
4848                                    ),
4849                                    "".to_string(),
4850                                )],
4851                            ),
4852                        ]
4853                        .into_iter()
4854                        .collect(),
4855                    ),
4856                    ..Default::default()
4857                }),
4858                ..Default::default()
4859            })
4860        },
4861    );
4862
4863    // After the action is confirmed, an editor containing both modified files is opened.
4864    confirm_action.await.unwrap();
4865    let code_action_editor = workspace_b.read_with(cx_b, |workspace, cx| {
4866        workspace
4867            .active_item(cx)
4868            .unwrap()
4869            .downcast::<Editor>()
4870            .unwrap()
4871    });
4872    code_action_editor.update(cx_b, |editor, cx| {
4873        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
4874        editor.undo(&Undo, cx);
4875        assert_eq!(
4876            editor.text(cx),
4877            "mod other;\nfn main() { let foo = other::foo(); }\npub fn foo() -> usize { 4 }"
4878        );
4879        editor.redo(&Redo, cx);
4880        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
4881    });
4882}
4883
4884#[gpui::test(iterations = 10)]
4885async fn test_collaborating_with_renames(
4886    deterministic: Arc<Deterministic>,
4887    cx_a: &mut TestAppContext,
4888    cx_b: &mut TestAppContext,
4889) {
4890    deterministic.forbid_parking();
4891    cx_b.update(editor::init);
4892    let mut server = TestServer::start(&deterministic).await;
4893    let client_a = server.create_client(cx_a, "user_a").await;
4894    let client_b = server.create_client(cx_b, "user_b").await;
4895    server
4896        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
4897        .await;
4898    let active_call_a = cx_a.read(ActiveCall::global);
4899
4900    // Set up a fake language server.
4901    let mut language = Language::new(
4902        LanguageConfig {
4903            name: "Rust".into(),
4904            path_suffixes: vec!["rs".to_string()],
4905            ..Default::default()
4906        },
4907        Some(tree_sitter_rust::language()),
4908    );
4909    let mut fake_language_servers = language
4910        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4911            capabilities: lsp::ServerCapabilities {
4912                rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
4913                    prepare_provider: Some(true),
4914                    work_done_progress_options: Default::default(),
4915                })),
4916                ..Default::default()
4917            },
4918            ..Default::default()
4919        }))
4920        .await;
4921    client_a.language_registry.add(Arc::new(language));
4922
4923    client_a
4924        .fs
4925        .insert_tree(
4926            "/dir",
4927            json!({
4928                "one.rs": "const ONE: usize = 1;",
4929                "two.rs": "const TWO: usize = one::ONE + one::ONE;"
4930            }),
4931        )
4932        .await;
4933    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
4934    let project_id = active_call_a
4935        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
4936        .await
4937        .unwrap();
4938    let project_b = client_b.build_remote_project(project_id, cx_b).await;
4939
4940    let (_window_b, workspace_b) = cx_b.add_window(|cx| {
4941        Workspace::new(
4942            Default::default(),
4943            0,
4944            project_b.clone(),
4945            |_, _| unimplemented!(),
4946            cx,
4947        )
4948    });
4949    let editor_b = workspace_b
4950        .update(cx_b, |workspace, cx| {
4951            workspace.open_path((worktree_id, "one.rs"), None, true, cx)
4952        })
4953        .await
4954        .unwrap()
4955        .downcast::<Editor>()
4956        .unwrap();
4957    let fake_language_server = fake_language_servers.next().await.unwrap();
4958
4959    // Move cursor to a location that can be renamed.
4960    let prepare_rename = editor_b.update(cx_b, |editor, cx| {
4961        editor.change_selections(None, cx, |s| s.select_ranges([7..7]));
4962        editor.rename(&Rename, cx).unwrap()
4963    });
4964
4965    fake_language_server
4966        .handle_request::<lsp::request::PrepareRenameRequest, _, _>(|params, _| async move {
4967            assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs");
4968            assert_eq!(params.position, lsp::Position::new(0, 7));
4969            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
4970                lsp::Position::new(0, 6),
4971                lsp::Position::new(0, 9),
4972            ))))
4973        })
4974        .next()
4975        .await
4976        .unwrap();
4977    prepare_rename.await.unwrap();
4978    editor_b.update(cx_b, |editor, cx| {
4979        let rename = editor.pending_rename().unwrap();
4980        let buffer = editor.buffer().read(cx).snapshot(cx);
4981        assert_eq!(
4982            rename.range.start.to_offset(&buffer)..rename.range.end.to_offset(&buffer),
4983            6..9
4984        );
4985        rename.editor.update(cx, |rename_editor, cx| {
4986            rename_editor.buffer().update(cx, |rename_buffer, cx| {
4987                rename_buffer.edit([(0..3, "THREE")], None, cx);
4988            });
4989        });
4990    });
4991
4992    let confirm_rename = workspace_b.update(cx_b, |workspace, cx| {
4993        Editor::confirm_rename(workspace, &ConfirmRename, cx).unwrap()
4994    });
4995    fake_language_server
4996        .handle_request::<lsp::request::Rename, _, _>(|params, _| async move {
4997            assert_eq!(
4998                params.text_document_position.text_document.uri.as_str(),
4999                "file:///dir/one.rs"
5000            );
5001            assert_eq!(
5002                params.text_document_position.position,
5003                lsp::Position::new(0, 6)
5004            );
5005            assert_eq!(params.new_name, "THREE");
5006            Ok(Some(lsp::WorkspaceEdit {
5007                changes: Some(
5008                    [
5009                        (
5010                            lsp::Url::from_file_path("/dir/one.rs").unwrap(),
5011                            vec![lsp::TextEdit::new(
5012                                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
5013                                "THREE".to_string(),
5014                            )],
5015                        ),
5016                        (
5017                            lsp::Url::from_file_path("/dir/two.rs").unwrap(),
5018                            vec![
5019                                lsp::TextEdit::new(
5020                                    lsp::Range::new(
5021                                        lsp::Position::new(0, 24),
5022                                        lsp::Position::new(0, 27),
5023                                    ),
5024                                    "THREE".to_string(),
5025                                ),
5026                                lsp::TextEdit::new(
5027                                    lsp::Range::new(
5028                                        lsp::Position::new(0, 35),
5029                                        lsp::Position::new(0, 38),
5030                                    ),
5031                                    "THREE".to_string(),
5032                                ),
5033                            ],
5034                        ),
5035                    ]
5036                    .into_iter()
5037                    .collect(),
5038                ),
5039                ..Default::default()
5040            }))
5041        })
5042        .next()
5043        .await
5044        .unwrap();
5045    confirm_rename.await.unwrap();
5046
5047    let rename_editor = workspace_b.read_with(cx_b, |workspace, cx| {
5048        workspace
5049            .active_item(cx)
5050            .unwrap()
5051            .downcast::<Editor>()
5052            .unwrap()
5053    });
5054    rename_editor.update(cx_b, |editor, cx| {
5055        assert_eq!(
5056            editor.text(cx),
5057            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
5058        );
5059        editor.undo(&Undo, cx);
5060        assert_eq!(
5061            editor.text(cx),
5062            "const ONE: usize = 1;\nconst TWO: usize = one::ONE + one::ONE;"
5063        );
5064        editor.redo(&Redo, cx);
5065        assert_eq!(
5066            editor.text(cx),
5067            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
5068        );
5069    });
5070
5071    // Ensure temporary rename edits cannot be undone/redone.
5072    editor_b.update(cx_b, |editor, cx| {
5073        editor.undo(&Undo, cx);
5074        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
5075        editor.undo(&Undo, cx);
5076        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
5077        editor.redo(&Redo, cx);
5078        assert_eq!(editor.text(cx), "const THREE: usize = 1;");
5079    })
5080}
5081
5082#[gpui::test(iterations = 10)]
5083async fn test_language_server_statuses(
5084    deterministic: Arc<Deterministic>,
5085    cx_a: &mut TestAppContext,
5086    cx_b: &mut TestAppContext,
5087) {
5088    deterministic.forbid_parking();
5089
5090    cx_b.update(editor::init);
5091    let mut server = TestServer::start(&deterministic).await;
5092    let client_a = server.create_client(cx_a, "user_a").await;
5093    let client_b = server.create_client(cx_b, "user_b").await;
5094    server
5095        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5096        .await;
5097    let active_call_a = cx_a.read(ActiveCall::global);
5098
5099    // Set up a fake language server.
5100    let mut language = Language::new(
5101        LanguageConfig {
5102            name: "Rust".into(),
5103            path_suffixes: vec!["rs".to_string()],
5104            ..Default::default()
5105        },
5106        Some(tree_sitter_rust::language()),
5107    );
5108    let mut fake_language_servers = language
5109        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
5110            name: "the-language-server",
5111            ..Default::default()
5112        }))
5113        .await;
5114    client_a.language_registry.add(Arc::new(language));
5115
5116    client_a
5117        .fs
5118        .insert_tree(
5119            "/dir",
5120            json!({
5121                "main.rs": "const ONE: usize = 1;",
5122            }),
5123        )
5124        .await;
5125    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
5126
5127    let _buffer_a = project_a
5128        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
5129        .await
5130        .unwrap();
5131
5132    let fake_language_server = fake_language_servers.next().await.unwrap();
5133    fake_language_server.start_progress("the-token").await;
5134    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
5135        token: lsp::NumberOrString::String("the-token".to_string()),
5136        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
5137            lsp::WorkDoneProgressReport {
5138                message: Some("the-message".to_string()),
5139                ..Default::default()
5140            },
5141        )),
5142    });
5143    deterministic.run_until_parked();
5144    project_a.read_with(cx_a, |project, _| {
5145        let status = project.language_server_statuses().next().unwrap();
5146        assert_eq!(status.name, "the-language-server");
5147        assert_eq!(status.pending_work.len(), 1);
5148        assert_eq!(
5149            status.pending_work["the-token"].message.as_ref().unwrap(),
5150            "the-message"
5151        );
5152    });
5153
5154    let project_id = active_call_a
5155        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5156        .await
5157        .unwrap();
5158    deterministic.run_until_parked();
5159    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5160    project_b.read_with(cx_b, |project, _| {
5161        let status = project.language_server_statuses().next().unwrap();
5162        assert_eq!(status.name, "the-language-server");
5163    });
5164
5165    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
5166        token: lsp::NumberOrString::String("the-token".to_string()),
5167        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
5168            lsp::WorkDoneProgressReport {
5169                message: Some("the-message-2".to_string()),
5170                ..Default::default()
5171            },
5172        )),
5173    });
5174    deterministic.run_until_parked();
5175    project_a.read_with(cx_a, |project, _| {
5176        let status = project.language_server_statuses().next().unwrap();
5177        assert_eq!(status.name, "the-language-server");
5178        assert_eq!(status.pending_work.len(), 1);
5179        assert_eq!(
5180            status.pending_work["the-token"].message.as_ref().unwrap(),
5181            "the-message-2"
5182        );
5183    });
5184    project_b.read_with(cx_b, |project, _| {
5185        let status = project.language_server_statuses().next().unwrap();
5186        assert_eq!(status.name, "the-language-server");
5187        assert_eq!(status.pending_work.len(), 1);
5188        assert_eq!(
5189            status.pending_work["the-token"].message.as_ref().unwrap(),
5190            "the-message-2"
5191        );
5192    });
5193}
5194
5195#[gpui::test(iterations = 10)]
5196async fn test_contacts(
5197    deterministic: Arc<Deterministic>,
5198    cx_a: &mut TestAppContext,
5199    cx_b: &mut TestAppContext,
5200    cx_c: &mut TestAppContext,
5201    cx_d: &mut TestAppContext,
5202) {
5203    deterministic.forbid_parking();
5204    let mut server = TestServer::start(&deterministic).await;
5205    let client_a = server.create_client(cx_a, "user_a").await;
5206    let client_b = server.create_client(cx_b, "user_b").await;
5207    let client_c = server.create_client(cx_c, "user_c").await;
5208    let client_d = server.create_client(cx_d, "user_d").await;
5209    server
5210        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
5211        .await;
5212    let active_call_a = cx_a.read(ActiveCall::global);
5213    let active_call_b = cx_b.read(ActiveCall::global);
5214    let active_call_c = cx_c.read(ActiveCall::global);
5215    let _active_call_d = cx_d.read(ActiveCall::global);
5216
5217    deterministic.run_until_parked();
5218    assert_eq!(
5219        contacts(&client_a, cx_a),
5220        [
5221            ("user_b".to_string(), "online", "free"),
5222            ("user_c".to_string(), "online", "free")
5223        ]
5224    );
5225    assert_eq!(
5226        contacts(&client_b, cx_b),
5227        [
5228            ("user_a".to_string(), "online", "free"),
5229            ("user_c".to_string(), "online", "free")
5230        ]
5231    );
5232    assert_eq!(
5233        contacts(&client_c, cx_c),
5234        [
5235            ("user_a".to_string(), "online", "free"),
5236            ("user_b".to_string(), "online", "free")
5237        ]
5238    );
5239    assert_eq!(contacts(&client_d, cx_d), []);
5240
5241    server.disconnect_client(client_c.peer_id().unwrap());
5242    server.forbid_connections();
5243    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5244    assert_eq!(
5245        contacts(&client_a, cx_a),
5246        [
5247            ("user_b".to_string(), "online", "free"),
5248            ("user_c".to_string(), "offline", "free")
5249        ]
5250    );
5251    assert_eq!(
5252        contacts(&client_b, cx_b),
5253        [
5254            ("user_a".to_string(), "online", "free"),
5255            ("user_c".to_string(), "offline", "free")
5256        ]
5257    );
5258    assert_eq!(contacts(&client_c, cx_c), []);
5259    assert_eq!(contacts(&client_d, cx_d), []);
5260
5261    server.allow_connections();
5262    client_c
5263        .authenticate_and_connect(false, &cx_c.to_async())
5264        .await
5265        .unwrap();
5266
5267    deterministic.run_until_parked();
5268    assert_eq!(
5269        contacts(&client_a, cx_a),
5270        [
5271            ("user_b".to_string(), "online", "free"),
5272            ("user_c".to_string(), "online", "free")
5273        ]
5274    );
5275    assert_eq!(
5276        contacts(&client_b, cx_b),
5277        [
5278            ("user_a".to_string(), "online", "free"),
5279            ("user_c".to_string(), "online", "free")
5280        ]
5281    );
5282    assert_eq!(
5283        contacts(&client_c, cx_c),
5284        [
5285            ("user_a".to_string(), "online", "free"),
5286            ("user_b".to_string(), "online", "free")
5287        ]
5288    );
5289    assert_eq!(contacts(&client_d, cx_d), []);
5290
5291    active_call_a
5292        .update(cx_a, |call, cx| {
5293            call.invite(client_b.user_id().unwrap(), None, cx)
5294        })
5295        .await
5296        .unwrap();
5297    deterministic.run_until_parked();
5298    assert_eq!(
5299        contacts(&client_a, cx_a),
5300        [
5301            ("user_b".to_string(), "online", "busy"),
5302            ("user_c".to_string(), "online", "free")
5303        ]
5304    );
5305    assert_eq!(
5306        contacts(&client_b, cx_b),
5307        [
5308            ("user_a".to_string(), "online", "busy"),
5309            ("user_c".to_string(), "online", "free")
5310        ]
5311    );
5312    assert_eq!(
5313        contacts(&client_c, cx_c),
5314        [
5315            ("user_a".to_string(), "online", "busy"),
5316            ("user_b".to_string(), "online", "busy")
5317        ]
5318    );
5319    assert_eq!(contacts(&client_d, cx_d), []);
5320
5321    // Client B and client D become contacts while client B is being called.
5322    server
5323        .make_contacts(&mut [(&client_b, cx_b), (&client_d, cx_d)])
5324        .await;
5325    deterministic.run_until_parked();
5326    assert_eq!(
5327        contacts(&client_a, cx_a),
5328        [
5329            ("user_b".to_string(), "online", "busy"),
5330            ("user_c".to_string(), "online", "free")
5331        ]
5332    );
5333    assert_eq!(
5334        contacts(&client_b, cx_b),
5335        [
5336            ("user_a".to_string(), "online", "busy"),
5337            ("user_c".to_string(), "online", "free"),
5338            ("user_d".to_string(), "online", "free"),
5339        ]
5340    );
5341    assert_eq!(
5342        contacts(&client_c, cx_c),
5343        [
5344            ("user_a".to_string(), "online", "busy"),
5345            ("user_b".to_string(), "online", "busy")
5346        ]
5347    );
5348    assert_eq!(
5349        contacts(&client_d, cx_d),
5350        [("user_b".to_string(), "online", "busy")]
5351    );
5352
5353    active_call_b.update(cx_b, |call, _| call.decline_incoming().unwrap());
5354    deterministic.run_until_parked();
5355    assert_eq!(
5356        contacts(&client_a, cx_a),
5357        [
5358            ("user_b".to_string(), "online", "free"),
5359            ("user_c".to_string(), "online", "free")
5360        ]
5361    );
5362    assert_eq!(
5363        contacts(&client_b, cx_b),
5364        [
5365            ("user_a".to_string(), "online", "free"),
5366            ("user_c".to_string(), "online", "free"),
5367            ("user_d".to_string(), "online", "free")
5368        ]
5369    );
5370    assert_eq!(
5371        contacts(&client_c, cx_c),
5372        [
5373            ("user_a".to_string(), "online", "free"),
5374            ("user_b".to_string(), "online", "free")
5375        ]
5376    );
5377    assert_eq!(
5378        contacts(&client_d, cx_d),
5379        [("user_b".to_string(), "online", "free")]
5380    );
5381
5382    active_call_c
5383        .update(cx_c, |call, cx| {
5384            call.invite(client_a.user_id().unwrap(), None, cx)
5385        })
5386        .await
5387        .unwrap();
5388    deterministic.run_until_parked();
5389    assert_eq!(
5390        contacts(&client_a, cx_a),
5391        [
5392            ("user_b".to_string(), "online", "free"),
5393            ("user_c".to_string(), "online", "busy")
5394        ]
5395    );
5396    assert_eq!(
5397        contacts(&client_b, cx_b),
5398        [
5399            ("user_a".to_string(), "online", "busy"),
5400            ("user_c".to_string(), "online", "busy"),
5401            ("user_d".to_string(), "online", "free")
5402        ]
5403    );
5404    assert_eq!(
5405        contacts(&client_c, cx_c),
5406        [
5407            ("user_a".to_string(), "online", "busy"),
5408            ("user_b".to_string(), "online", "free")
5409        ]
5410    );
5411    assert_eq!(
5412        contacts(&client_d, cx_d),
5413        [("user_b".to_string(), "online", "free")]
5414    );
5415
5416    active_call_a
5417        .update(cx_a, |call, cx| call.accept_incoming(cx))
5418        .await
5419        .unwrap();
5420    deterministic.run_until_parked();
5421    assert_eq!(
5422        contacts(&client_a, cx_a),
5423        [
5424            ("user_b".to_string(), "online", "free"),
5425            ("user_c".to_string(), "online", "busy")
5426        ]
5427    );
5428    assert_eq!(
5429        contacts(&client_b, cx_b),
5430        [
5431            ("user_a".to_string(), "online", "busy"),
5432            ("user_c".to_string(), "online", "busy"),
5433            ("user_d".to_string(), "online", "free")
5434        ]
5435    );
5436    assert_eq!(
5437        contacts(&client_c, cx_c),
5438        [
5439            ("user_a".to_string(), "online", "busy"),
5440            ("user_b".to_string(), "online", "free")
5441        ]
5442    );
5443    assert_eq!(
5444        contacts(&client_d, cx_d),
5445        [("user_b".to_string(), "online", "free")]
5446    );
5447
5448    active_call_a
5449        .update(cx_a, |call, cx| {
5450            call.invite(client_b.user_id().unwrap(), None, cx)
5451        })
5452        .await
5453        .unwrap();
5454    deterministic.run_until_parked();
5455    assert_eq!(
5456        contacts(&client_a, cx_a),
5457        [
5458            ("user_b".to_string(), "online", "busy"),
5459            ("user_c".to_string(), "online", "busy")
5460        ]
5461    );
5462    assert_eq!(
5463        contacts(&client_b, cx_b),
5464        [
5465            ("user_a".to_string(), "online", "busy"),
5466            ("user_c".to_string(), "online", "busy"),
5467            ("user_d".to_string(), "online", "free")
5468        ]
5469    );
5470    assert_eq!(
5471        contacts(&client_c, cx_c),
5472        [
5473            ("user_a".to_string(), "online", "busy"),
5474            ("user_b".to_string(), "online", "busy")
5475        ]
5476    );
5477    assert_eq!(
5478        contacts(&client_d, cx_d),
5479        [("user_b".to_string(), "online", "busy")]
5480    );
5481
5482    active_call_a.update(cx_a, |call, cx| call.hang_up(cx).unwrap());
5483    deterministic.run_until_parked();
5484    assert_eq!(
5485        contacts(&client_a, cx_a),
5486        [
5487            ("user_b".to_string(), "online", "free"),
5488            ("user_c".to_string(), "online", "free")
5489        ]
5490    );
5491    assert_eq!(
5492        contacts(&client_b, cx_b),
5493        [
5494            ("user_a".to_string(), "online", "free"),
5495            ("user_c".to_string(), "online", "free"),
5496            ("user_d".to_string(), "online", "free")
5497        ]
5498    );
5499    assert_eq!(
5500        contacts(&client_c, cx_c),
5501        [
5502            ("user_a".to_string(), "online", "free"),
5503            ("user_b".to_string(), "online", "free")
5504        ]
5505    );
5506    assert_eq!(
5507        contacts(&client_d, cx_d),
5508        [("user_b".to_string(), "online", "free")]
5509    );
5510
5511    active_call_a
5512        .update(cx_a, |call, cx| {
5513            call.invite(client_b.user_id().unwrap(), None, cx)
5514        })
5515        .await
5516        .unwrap();
5517    deterministic.run_until_parked();
5518    assert_eq!(
5519        contacts(&client_a, cx_a),
5520        [
5521            ("user_b".to_string(), "online", "busy"),
5522            ("user_c".to_string(), "online", "free")
5523        ]
5524    );
5525    assert_eq!(
5526        contacts(&client_b, cx_b),
5527        [
5528            ("user_a".to_string(), "online", "busy"),
5529            ("user_c".to_string(), "online", "free"),
5530            ("user_d".to_string(), "online", "free")
5531        ]
5532    );
5533    assert_eq!(
5534        contacts(&client_c, cx_c),
5535        [
5536            ("user_a".to_string(), "online", "busy"),
5537            ("user_b".to_string(), "online", "busy")
5538        ]
5539    );
5540    assert_eq!(
5541        contacts(&client_d, cx_d),
5542        [("user_b".to_string(), "online", "busy")]
5543    );
5544
5545    server.forbid_connections();
5546    server.disconnect_client(client_a.peer_id().unwrap());
5547    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
5548    assert_eq!(contacts(&client_a, cx_a), []);
5549    assert_eq!(
5550        contacts(&client_b, cx_b),
5551        [
5552            ("user_a".to_string(), "offline", "free"),
5553            ("user_c".to_string(), "online", "free"),
5554            ("user_d".to_string(), "online", "free")
5555        ]
5556    );
5557    assert_eq!(
5558        contacts(&client_c, cx_c),
5559        [
5560            ("user_a".to_string(), "offline", "free"),
5561            ("user_b".to_string(), "online", "free")
5562        ]
5563    );
5564    assert_eq!(
5565        contacts(&client_d, cx_d),
5566        [("user_b".to_string(), "online", "free")]
5567    );
5568
5569    // Test removing a contact
5570    client_b
5571        .user_store
5572        .update(cx_b, |store, cx| {
5573            store.remove_contact(client_c.user_id().unwrap(), cx)
5574        })
5575        .await
5576        .unwrap();
5577    deterministic.run_until_parked();
5578    assert_eq!(
5579        contacts(&client_b, cx_b),
5580        [
5581            ("user_a".to_string(), "offline", "free"),
5582            ("user_d".to_string(), "online", "free")
5583        ]
5584    );
5585    assert_eq!(
5586        contacts(&client_c, cx_c),
5587        [("user_a".to_string(), "offline", "free"),]
5588    );
5589
5590    fn contacts(
5591        client: &TestClient,
5592        cx: &TestAppContext,
5593    ) -> Vec<(String, &'static str, &'static str)> {
5594        client.user_store.read_with(cx, |store, _| {
5595            store
5596                .contacts()
5597                .iter()
5598                .map(|contact| {
5599                    (
5600                        contact.user.github_login.clone(),
5601                        if contact.online { "online" } else { "offline" },
5602                        if contact.busy { "busy" } else { "free" },
5603                    )
5604                })
5605                .collect()
5606        })
5607    }
5608}
5609
5610#[gpui::test(iterations = 10)]
5611async fn test_contact_requests(
5612    deterministic: Arc<Deterministic>,
5613    cx_a: &mut TestAppContext,
5614    cx_a2: &mut TestAppContext,
5615    cx_b: &mut TestAppContext,
5616    cx_b2: &mut TestAppContext,
5617    cx_c: &mut TestAppContext,
5618    cx_c2: &mut TestAppContext,
5619) {
5620    deterministic.forbid_parking();
5621
5622    // Connect to a server as 3 clients.
5623    let mut server = TestServer::start(&deterministic).await;
5624    let client_a = server.create_client(cx_a, "user_a").await;
5625    let client_a2 = server.create_client(cx_a2, "user_a").await;
5626    let client_b = server.create_client(cx_b, "user_b").await;
5627    let client_b2 = server.create_client(cx_b2, "user_b").await;
5628    let client_c = server.create_client(cx_c, "user_c").await;
5629    let client_c2 = server.create_client(cx_c2, "user_c").await;
5630
5631    assert_eq!(client_a.user_id().unwrap(), client_a2.user_id().unwrap());
5632    assert_eq!(client_b.user_id().unwrap(), client_b2.user_id().unwrap());
5633    assert_eq!(client_c.user_id().unwrap(), client_c2.user_id().unwrap());
5634
5635    // User A and User C request that user B become their contact.
5636    client_a
5637        .user_store
5638        .update(cx_a, |store, cx| {
5639            store.request_contact(client_b.user_id().unwrap(), cx)
5640        })
5641        .await
5642        .unwrap();
5643    client_c
5644        .user_store
5645        .update(cx_c, |store, cx| {
5646            store.request_contact(client_b.user_id().unwrap(), cx)
5647        })
5648        .await
5649        .unwrap();
5650    deterministic.run_until_parked();
5651
5652    // All users see the pending request appear in all their clients.
5653    assert_eq!(
5654        client_a.summarize_contacts(cx_a).outgoing_requests,
5655        &["user_b"]
5656    );
5657    assert_eq!(
5658        client_a2.summarize_contacts(cx_a2).outgoing_requests,
5659        &["user_b"]
5660    );
5661    assert_eq!(
5662        client_b.summarize_contacts(cx_b).incoming_requests,
5663        &["user_a", "user_c"]
5664    );
5665    assert_eq!(
5666        client_b2.summarize_contacts(cx_b2).incoming_requests,
5667        &["user_a", "user_c"]
5668    );
5669    assert_eq!(
5670        client_c.summarize_contacts(cx_c).outgoing_requests,
5671        &["user_b"]
5672    );
5673    assert_eq!(
5674        client_c2.summarize_contacts(cx_c2).outgoing_requests,
5675        &["user_b"]
5676    );
5677
5678    // Contact requests are present upon connecting (tested here via disconnect/reconnect)
5679    disconnect_and_reconnect(&client_a, cx_a).await;
5680    disconnect_and_reconnect(&client_b, cx_b).await;
5681    disconnect_and_reconnect(&client_c, cx_c).await;
5682    deterministic.run_until_parked();
5683    assert_eq!(
5684        client_a.summarize_contacts(cx_a).outgoing_requests,
5685        &["user_b"]
5686    );
5687    assert_eq!(
5688        client_b.summarize_contacts(cx_b).incoming_requests,
5689        &["user_a", "user_c"]
5690    );
5691    assert_eq!(
5692        client_c.summarize_contacts(cx_c).outgoing_requests,
5693        &["user_b"]
5694    );
5695
5696    // User B accepts the request from user A.
5697    client_b
5698        .user_store
5699        .update(cx_b, |store, cx| {
5700            store.respond_to_contact_request(client_a.user_id().unwrap(), true, cx)
5701        })
5702        .await
5703        .unwrap();
5704
5705    deterministic.run_until_parked();
5706
5707    // User B sees user A as their contact now in all client, and the incoming request from them is removed.
5708    let contacts_b = client_b.summarize_contacts(cx_b);
5709    assert_eq!(contacts_b.current, &["user_a"]);
5710    assert_eq!(contacts_b.incoming_requests, &["user_c"]);
5711    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
5712    assert_eq!(contacts_b2.current, &["user_a"]);
5713    assert_eq!(contacts_b2.incoming_requests, &["user_c"]);
5714
5715    // User A sees user B as their contact now in all clients, and the outgoing request to them is removed.
5716    let contacts_a = client_a.summarize_contacts(cx_a);
5717    assert_eq!(contacts_a.current, &["user_b"]);
5718    assert!(contacts_a.outgoing_requests.is_empty());
5719    let contacts_a2 = client_a2.summarize_contacts(cx_a2);
5720    assert_eq!(contacts_a2.current, &["user_b"]);
5721    assert!(contacts_a2.outgoing_requests.is_empty());
5722
5723    // Contacts are present upon connecting (tested here via disconnect/reconnect)
5724    disconnect_and_reconnect(&client_a, cx_a).await;
5725    disconnect_and_reconnect(&client_b, cx_b).await;
5726    disconnect_and_reconnect(&client_c, cx_c).await;
5727    deterministic.run_until_parked();
5728    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
5729    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
5730    assert_eq!(
5731        client_b.summarize_contacts(cx_b).incoming_requests,
5732        &["user_c"]
5733    );
5734    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
5735    assert_eq!(
5736        client_c.summarize_contacts(cx_c).outgoing_requests,
5737        &["user_b"]
5738    );
5739
5740    // User B rejects the request from user C.
5741    client_b
5742        .user_store
5743        .update(cx_b, |store, cx| {
5744            store.respond_to_contact_request(client_c.user_id().unwrap(), false, cx)
5745        })
5746        .await
5747        .unwrap();
5748
5749    deterministic.run_until_parked();
5750
5751    // User B doesn't see user C as their contact, and the incoming request from them is removed.
5752    let contacts_b = client_b.summarize_contacts(cx_b);
5753    assert_eq!(contacts_b.current, &["user_a"]);
5754    assert!(contacts_b.incoming_requests.is_empty());
5755    let contacts_b2 = client_b2.summarize_contacts(cx_b2);
5756    assert_eq!(contacts_b2.current, &["user_a"]);
5757    assert!(contacts_b2.incoming_requests.is_empty());
5758
5759    // User C doesn't see user B as their contact, and the outgoing request to them is removed.
5760    let contacts_c = client_c.summarize_contacts(cx_c);
5761    assert!(contacts_c.current.is_empty());
5762    assert!(contacts_c.outgoing_requests.is_empty());
5763    let contacts_c2 = client_c2.summarize_contacts(cx_c2);
5764    assert!(contacts_c2.current.is_empty());
5765    assert!(contacts_c2.outgoing_requests.is_empty());
5766
5767    // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect)
5768    disconnect_and_reconnect(&client_a, cx_a).await;
5769    disconnect_and_reconnect(&client_b, cx_b).await;
5770    disconnect_and_reconnect(&client_c, cx_c).await;
5771    deterministic.run_until_parked();
5772    assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
5773    assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
5774    assert!(client_b
5775        .summarize_contacts(cx_b)
5776        .incoming_requests
5777        .is_empty());
5778    assert!(client_c.summarize_contacts(cx_c).current.is_empty());
5779    assert!(client_c
5780        .summarize_contacts(cx_c)
5781        .outgoing_requests
5782        .is_empty());
5783
5784    async fn disconnect_and_reconnect(client: &TestClient, cx: &mut TestAppContext) {
5785        client.disconnect(&cx.to_async());
5786        client.clear_contacts(cx).await;
5787        client
5788            .authenticate_and_connect(false, &cx.to_async())
5789            .await
5790            .unwrap();
5791    }
5792}
5793
5794#[gpui::test(iterations = 10)]
5795async fn test_following(
5796    deterministic: Arc<Deterministic>,
5797    cx_a: &mut TestAppContext,
5798    cx_b: &mut TestAppContext,
5799    cx_c: &mut TestAppContext,
5800) {
5801    deterministic.forbid_parking();
5802    cx_a.update(editor::init);
5803    cx_b.update(editor::init);
5804
5805    let mut server = TestServer::start(&deterministic).await;
5806    let client_a = server.create_client(cx_a, "user_a").await;
5807    let client_b = server.create_client(cx_b, "user_b").await;
5808    let client_c = server.create_client(cx_c, "user_c").await;
5809    server
5810        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
5811        .await;
5812    server
5813        .make_contacts(&mut [(&client_a, cx_a), (&client_c, cx_c)])
5814        .await;
5815    let active_call_a = cx_a.read(ActiveCall::global);
5816    let active_call_b = cx_b.read(ActiveCall::global);
5817
5818    client_a
5819        .fs
5820        .insert_tree(
5821            "/a",
5822            json!({
5823                "1.txt": "one\none\none",
5824                "2.txt": "two\ntwo\ntwo",
5825                "3.txt": "three\nthree\nthree",
5826            }),
5827        )
5828        .await;
5829    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
5830    active_call_a
5831        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
5832        .await
5833        .unwrap();
5834
5835    let project_id = active_call_a
5836        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
5837        .await
5838        .unwrap();
5839    let project_b = client_b.build_remote_project(project_id, cx_b).await;
5840    active_call_b
5841        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
5842        .await
5843        .unwrap();
5844
5845    let workspace_a = client_a.build_workspace(&project_a, cx_a);
5846    let workspace_b = client_b.build_workspace(&project_b, cx_b);
5847
5848    // Client A opens some editors.
5849    let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
5850    let editor_a1 = workspace_a
5851        .update(cx_a, |workspace, cx| {
5852            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
5853        })
5854        .await
5855        .unwrap()
5856        .downcast::<Editor>()
5857        .unwrap();
5858    let editor_a2 = workspace_a
5859        .update(cx_a, |workspace, cx| {
5860            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
5861        })
5862        .await
5863        .unwrap()
5864        .downcast::<Editor>()
5865        .unwrap();
5866
5867    // Client B opens an editor.
5868    let editor_b1 = workspace_b
5869        .update(cx_b, |workspace, cx| {
5870            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
5871        })
5872        .await
5873        .unwrap()
5874        .downcast::<Editor>()
5875        .unwrap();
5876
5877    let peer_id_a = client_a.peer_id().unwrap();
5878    let peer_id_b = client_b.peer_id().unwrap();
5879    let peer_id_c = client_c.peer_id().unwrap();
5880
5881    // Client A updates their selections in those editors
5882    editor_a1.update(cx_a, |editor, cx| {
5883        editor.change_selections(None, cx, |s| s.select_ranges([0..1]))
5884    });
5885    editor_a2.update(cx_a, |editor, cx| {
5886        editor.change_selections(None, cx, |s| s.select_ranges([2..3]))
5887    });
5888
5889    // When client B starts following client A, all visible view states are replicated to client B.
5890    workspace_b
5891        .update(cx_b, |workspace, cx| {
5892            workspace
5893                .toggle_follow(&ToggleFollow(peer_id_a), cx)
5894                .unwrap()
5895        })
5896        .await
5897        .unwrap();
5898
5899    // Client A invites client C to the call.
5900    active_call_a
5901        .update(cx_a, |call, cx| {
5902            call.invite(client_c.current_user_id(cx_c).to_proto(), None, cx)
5903        })
5904        .await
5905        .unwrap();
5906    cx_c.foreground().run_until_parked();
5907    let active_call_c = cx_c.read(ActiveCall::global);
5908    active_call_c
5909        .update(cx_c, |call, cx| call.accept_incoming(cx))
5910        .await
5911        .unwrap();
5912    let project_c = client_c.build_remote_project(project_id, cx_c).await;
5913    let workspace_c = client_c.build_workspace(&project_c, cx_c);
5914    active_call_c
5915        .update(cx_c, |call, cx| call.set_location(Some(&project_c), cx))
5916        .await
5917        .unwrap();
5918
5919    // Client C also follows client A.
5920    workspace_c
5921        .update(cx_c, |workspace, cx| {
5922            workspace
5923                .toggle_follow(&ToggleFollow(peer_id_a), cx)
5924                .unwrap()
5925        })
5926        .await
5927        .unwrap();
5928
5929    // All clients see that clients B and C are following client A.
5930    cx_c.foreground().run_until_parked();
5931    for (name, active_call, cx) in [
5932        ("A", &active_call_a, &cx_a),
5933        ("B", &active_call_b, &cx_b),
5934        ("C", &active_call_c, &cx_c),
5935    ] {
5936        active_call.read_with(*cx, |call, cx| {
5937            let room = call.room().unwrap().read(cx);
5938            assert_eq!(
5939                room.followers_for(peer_id_a, project_id),
5940                &[peer_id_b, peer_id_c],
5941                "checking followers for A as {name}"
5942            );
5943        });
5944    }
5945
5946    // Client C unfollows client A.
5947    workspace_c.update(cx_c, |workspace, cx| {
5948        workspace.toggle_follow(&ToggleFollow(peer_id_a), cx);
5949    });
5950
5951    // All clients see that clients B is following client A.
5952    cx_c.foreground().run_until_parked();
5953    for (name, active_call, cx) in [
5954        ("A", &active_call_a, &cx_a),
5955        ("B", &active_call_b, &cx_b),
5956        ("C", &active_call_c, &cx_c),
5957    ] {
5958        active_call.read_with(*cx, |call, cx| {
5959            let room = call.room().unwrap().read(cx);
5960            assert_eq!(
5961                room.followers_for(peer_id_a, project_id),
5962                &[peer_id_b],
5963                "checking followers for A as {name}"
5964            );
5965        });
5966    }
5967
5968    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
5969        workspace
5970            .active_item(cx)
5971            .unwrap()
5972            .downcast::<Editor>()
5973            .unwrap()
5974    });
5975    assert_eq!(
5976        cx_b.read(|cx| editor_b2.project_path(cx)),
5977        Some((worktree_id, "2.txt").into())
5978    );
5979    assert_eq!(
5980        editor_b2.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
5981        vec![2..3]
5982    );
5983    assert_eq!(
5984        editor_b1.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
5985        vec![0..1]
5986    );
5987
5988    // When client A activates a different editor, client B does so as well.
5989    workspace_a.update(cx_a, |workspace, cx| {
5990        workspace.activate_item(&editor_a1, cx)
5991    });
5992    deterministic.run_until_parked();
5993    workspace_b.read_with(cx_b, |workspace, cx| {
5994        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
5995    });
5996
5997    // When client A opens a multibuffer, client B does so as well.
5998    let multibuffer_a = cx_a.add_model(|cx| {
5999        let buffer_a1 = project_a.update(cx, |project, cx| {
6000            project
6001                .get_open_buffer(&(worktree_id, "1.txt").into(), cx)
6002                .unwrap()
6003        });
6004        let buffer_a2 = project_a.update(cx, |project, cx| {
6005            project
6006                .get_open_buffer(&(worktree_id, "2.txt").into(), cx)
6007                .unwrap()
6008        });
6009        let mut result = MultiBuffer::new(0);
6010        result.push_excerpts(
6011            buffer_a1,
6012            [ExcerptRange {
6013                context: 0..3,
6014                primary: None,
6015            }],
6016            cx,
6017        );
6018        result.push_excerpts(
6019            buffer_a2,
6020            [ExcerptRange {
6021                context: 4..7,
6022                primary: None,
6023            }],
6024            cx,
6025        );
6026        result
6027    });
6028    let multibuffer_editor_a = workspace_a.update(cx_a, |workspace, cx| {
6029        let editor =
6030            cx.add_view(|cx| Editor::for_multibuffer(multibuffer_a, Some(project_a.clone()), cx));
6031        workspace.add_item(Box::new(editor.clone()), cx);
6032        editor
6033    });
6034    deterministic.run_until_parked();
6035    let multibuffer_editor_b = workspace_b.read_with(cx_b, |workspace, cx| {
6036        workspace
6037            .active_item(cx)
6038            .unwrap()
6039            .downcast::<Editor>()
6040            .unwrap()
6041    });
6042    assert_eq!(
6043        multibuffer_editor_a.read_with(cx_a, |editor, cx| editor.text(cx)),
6044        multibuffer_editor_b.read_with(cx_b, |editor, cx| editor.text(cx)),
6045    );
6046
6047    // When client A navigates back and forth, client B does so as well.
6048    workspace_a
6049        .update(cx_a, |workspace, cx| {
6050            workspace::Pane::go_back(workspace, None, cx)
6051        })
6052        .await;
6053    deterministic.run_until_parked();
6054    workspace_b.read_with(cx_b, |workspace, cx| {
6055        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
6056    });
6057
6058    workspace_a
6059        .update(cx_a, |workspace, cx| {
6060            workspace::Pane::go_back(workspace, None, cx)
6061        })
6062        .await;
6063    deterministic.run_until_parked();
6064    workspace_b.read_with(cx_b, |workspace, cx| {
6065        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b2.id());
6066    });
6067
6068    workspace_a
6069        .update(cx_a, |workspace, cx| {
6070            workspace::Pane::go_forward(workspace, None, cx)
6071        })
6072        .await;
6073    deterministic.run_until_parked();
6074    workspace_b.read_with(cx_b, |workspace, cx| {
6075        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
6076    });
6077
6078    // Changes to client A's editor are reflected on client B.
6079    editor_a1.update(cx_a, |editor, cx| {
6080        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2]));
6081    });
6082    deterministic.run_until_parked();
6083    editor_b1.read_with(cx_b, |editor, cx| {
6084        assert_eq!(editor.selections.ranges(cx), &[1..1, 2..2]);
6085    });
6086
6087    editor_a1.update(cx_a, |editor, cx| editor.set_text("TWO", cx));
6088    deterministic.run_until_parked();
6089    editor_b1.read_with(cx_b, |editor, cx| assert_eq!(editor.text(cx), "TWO"));
6090
6091    editor_a1.update(cx_a, |editor, cx| {
6092        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
6093        editor.set_scroll_position(vec2f(0., 100.), cx);
6094    });
6095    deterministic.run_until_parked();
6096    editor_b1.read_with(cx_b, |editor, cx| {
6097        assert_eq!(editor.selections.ranges(cx), &[3..3]);
6098    });
6099
6100    // After unfollowing, client B stops receiving updates from client A.
6101    workspace_b.update(cx_b, |workspace, cx| {
6102        workspace.unfollow(&workspace.active_pane().clone(), cx)
6103    });
6104    workspace_a.update(cx_a, |workspace, cx| {
6105        workspace.activate_item(&editor_a2, cx)
6106    });
6107    deterministic.run_until_parked();
6108    assert_eq!(
6109        workspace_b.read_with(cx_b, |workspace, cx| workspace
6110            .active_item(cx)
6111            .unwrap()
6112            .id()),
6113        editor_b1.id()
6114    );
6115
6116    // Client A starts following client B.
6117    workspace_a
6118        .update(cx_a, |workspace, cx| {
6119            workspace
6120                .toggle_follow(&ToggleFollow(peer_id_b), cx)
6121                .unwrap()
6122        })
6123        .await
6124        .unwrap();
6125    assert_eq!(
6126        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
6127        Some(peer_id_b)
6128    );
6129    assert_eq!(
6130        workspace_a.read_with(cx_a, |workspace, cx| workspace
6131            .active_item(cx)
6132            .unwrap()
6133            .id()),
6134        editor_a1.id()
6135    );
6136
6137    // Client B activates an external window, which causes a new screen-sharing item to be added to the pane.
6138    let display = MacOSDisplay::new();
6139    active_call_b
6140        .update(cx_b, |call, cx| call.set_location(None, cx))
6141        .await
6142        .unwrap();
6143    active_call_b
6144        .update(cx_b, |call, cx| {
6145            call.room().unwrap().update(cx, |room, cx| {
6146                room.set_display_sources(vec![display.clone()]);
6147                room.share_screen(cx)
6148            })
6149        })
6150        .await
6151        .unwrap();
6152    deterministic.run_until_parked();
6153    let shared_screen = workspace_a.read_with(cx_a, |workspace, cx| {
6154        workspace
6155            .active_item(cx)
6156            .unwrap()
6157            .downcast::<SharedScreen>()
6158            .unwrap()
6159    });
6160
6161    // Client B activates Zed again, which causes the previous editor to become focused again.
6162    active_call_b
6163        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6164        .await
6165        .unwrap();
6166    deterministic.run_until_parked();
6167    workspace_a.read_with(cx_a, |workspace, cx| {
6168        assert_eq!(workspace.active_item(cx).unwrap().id(), editor_a1.id())
6169    });
6170
6171    // Client B activates a multibuffer that was created by following client A. Client A returns to that multibuffer.
6172    workspace_b.update(cx_b, |workspace, cx| {
6173        workspace.activate_item(&multibuffer_editor_b, cx)
6174    });
6175    deterministic.run_until_parked();
6176    workspace_a.read_with(cx_a, |workspace, cx| {
6177        assert_eq!(
6178            workspace.active_item(cx).unwrap().id(),
6179            multibuffer_editor_a.id()
6180        )
6181    });
6182
6183    // Client B activates an external window again, and the previously-opened screen-sharing item
6184    // gets activated.
6185    active_call_b
6186        .update(cx_b, |call, cx| call.set_location(None, cx))
6187        .await
6188        .unwrap();
6189    deterministic.run_until_parked();
6190    assert_eq!(
6191        workspace_a.read_with(cx_a, |workspace, cx| workspace
6192            .active_item(cx)
6193            .unwrap()
6194            .id()),
6195        shared_screen.id()
6196    );
6197
6198    // Following interrupts when client B disconnects.
6199    client_b.disconnect(&cx_b.to_async());
6200    deterministic.advance_clock(RECONNECT_TIMEOUT);
6201    assert_eq!(
6202        workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
6203        None
6204    );
6205}
6206
6207#[gpui::test]
6208async fn test_following_tab_order(
6209    deterministic: Arc<Deterministic>,
6210    cx_a: &mut TestAppContext,
6211    cx_b: &mut TestAppContext,
6212) {
6213    cx_a.update(editor::init);
6214    cx_b.update(editor::init);
6215
6216    let mut server = TestServer::start(&deterministic).await;
6217    let client_a = server.create_client(cx_a, "user_a").await;
6218    let client_b = server.create_client(cx_b, "user_b").await;
6219    server
6220        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6221        .await;
6222    let active_call_a = cx_a.read(ActiveCall::global);
6223    let active_call_b = cx_b.read(ActiveCall::global);
6224
6225    client_a
6226        .fs
6227        .insert_tree(
6228            "/a",
6229            json!({
6230                "1.txt": "one",
6231                "2.txt": "two",
6232                "3.txt": "three",
6233            }),
6234        )
6235        .await;
6236    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
6237    active_call_a
6238        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
6239        .await
6240        .unwrap();
6241
6242    let project_id = active_call_a
6243        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6244        .await
6245        .unwrap();
6246    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6247    active_call_b
6248        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6249        .await
6250        .unwrap();
6251
6252    let workspace_a = client_a.build_workspace(&project_a, cx_a);
6253    let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
6254
6255    let workspace_b = client_b.build_workspace(&project_b, cx_b);
6256    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
6257
6258    let client_b_id = project_a.read_with(cx_a, |project, _| {
6259        project.collaborators().values().next().unwrap().peer_id
6260    });
6261
6262    //Open 1, 3 in that order on client A
6263    workspace_a
6264        .update(cx_a, |workspace, cx| {
6265            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6266        })
6267        .await
6268        .unwrap();
6269    workspace_a
6270        .update(cx_a, |workspace, cx| {
6271            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
6272        })
6273        .await
6274        .unwrap();
6275
6276    let pane_paths = |pane: &ViewHandle<workspace::Pane>, cx: &mut TestAppContext| {
6277        pane.update(cx, |pane, cx| {
6278            pane.items()
6279                .map(|item| {
6280                    item.project_path(cx)
6281                        .unwrap()
6282                        .path
6283                        .to_str()
6284                        .unwrap()
6285                        .to_owned()
6286                })
6287                .collect::<Vec<_>>()
6288        })
6289    };
6290
6291    //Verify that the tabs opened in the order we expect
6292    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt"]);
6293
6294    //Follow client B as client A
6295    workspace_a
6296        .update(cx_a, |workspace, cx| {
6297            workspace
6298                .toggle_follow(&ToggleFollow(client_b_id), cx)
6299                .unwrap()
6300        })
6301        .await
6302        .unwrap();
6303
6304    //Open just 2 on client B
6305    workspace_b
6306        .update(cx_b, |workspace, cx| {
6307            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
6308        })
6309        .await
6310        .unwrap();
6311    deterministic.run_until_parked();
6312
6313    // Verify that newly opened followed file is at the end
6314    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt", "2.txt"]);
6315
6316    //Open just 1 on client B
6317    workspace_b
6318        .update(cx_b, |workspace, cx| {
6319            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6320        })
6321        .await
6322        .unwrap();
6323    assert_eq!(&pane_paths(&pane_b, cx_b), &["2.txt", "1.txt"]);
6324    deterministic.run_until_parked();
6325
6326    // Verify that following into 1 did not reorder
6327    assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt", "2.txt"]);
6328}
6329
6330#[gpui::test(iterations = 10)]
6331async fn test_peers_following_each_other(
6332    deterministic: Arc<Deterministic>,
6333    cx_a: &mut TestAppContext,
6334    cx_b: &mut TestAppContext,
6335) {
6336    deterministic.forbid_parking();
6337    cx_a.update(editor::init);
6338    cx_b.update(editor::init);
6339
6340    let mut server = TestServer::start(&deterministic).await;
6341    let client_a = server.create_client(cx_a, "user_a").await;
6342    let client_b = server.create_client(cx_b, "user_b").await;
6343    server
6344        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6345        .await;
6346    let active_call_a = cx_a.read(ActiveCall::global);
6347    let active_call_b = cx_b.read(ActiveCall::global);
6348
6349    // Client A shares a project.
6350    client_a
6351        .fs
6352        .insert_tree(
6353            "/a",
6354            json!({
6355                "1.txt": "one",
6356                "2.txt": "two",
6357                "3.txt": "three",
6358                "4.txt": "four",
6359            }),
6360        )
6361        .await;
6362    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
6363    active_call_a
6364        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
6365        .await
6366        .unwrap();
6367    let project_id = active_call_a
6368        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6369        .await
6370        .unwrap();
6371
6372    // Client B joins the project.
6373    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6374    active_call_b
6375        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6376        .await
6377        .unwrap();
6378
6379    // Client A opens some editors.
6380    let workspace_a = client_a.build_workspace(&project_a, cx_a);
6381    let pane_a1 = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
6382    let _editor_a1 = workspace_a
6383        .update(cx_a, |workspace, cx| {
6384            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6385        })
6386        .await
6387        .unwrap()
6388        .downcast::<Editor>()
6389        .unwrap();
6390
6391    // Client B opens an editor.
6392    let workspace_b = client_b.build_workspace(&project_b, cx_b);
6393    let pane_b1 = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
6394    let _editor_b1 = workspace_b
6395        .update(cx_b, |workspace, cx| {
6396            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
6397        })
6398        .await
6399        .unwrap()
6400        .downcast::<Editor>()
6401        .unwrap();
6402
6403    // Clients A and B follow each other in split panes
6404    workspace_a.update(cx_a, |workspace, cx| {
6405        workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
6406        let pane_a1 = pane_a1.clone();
6407        cx.defer(move |workspace, _| {
6408            assert_ne!(*workspace.active_pane(), pane_a1);
6409        });
6410    });
6411    workspace_a
6412        .update(cx_a, |workspace, cx| {
6413            let leader_id = *project_a.read(cx).collaborators().keys().next().unwrap();
6414            workspace
6415                .toggle_follow(&workspace::ToggleFollow(leader_id), cx)
6416                .unwrap()
6417        })
6418        .await
6419        .unwrap();
6420    workspace_b.update(cx_b, |workspace, cx| {
6421        workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
6422        let pane_b1 = pane_b1.clone();
6423        cx.defer(move |workspace, _| {
6424            assert_ne!(*workspace.active_pane(), pane_b1);
6425        });
6426    });
6427    workspace_b
6428        .update(cx_b, |workspace, cx| {
6429            let leader_id = *project_b.read(cx).collaborators().keys().next().unwrap();
6430            workspace
6431                .toggle_follow(&workspace::ToggleFollow(leader_id), cx)
6432                .unwrap()
6433        })
6434        .await
6435        .unwrap();
6436
6437    workspace_a.update(cx_a, |workspace, cx| {
6438        workspace.activate_next_pane(cx);
6439    });
6440    // Wait for focus effects to be fully flushed
6441    workspace_a.update(cx_a, |workspace, _| {
6442        assert_eq!(*workspace.active_pane(), pane_a1);
6443    });
6444
6445    workspace_a
6446        .update(cx_a, |workspace, cx| {
6447            workspace.open_path((worktree_id, "3.txt"), None, true, cx)
6448        })
6449        .await
6450        .unwrap();
6451    workspace_b.update(cx_b, |workspace, cx| {
6452        workspace.activate_next_pane(cx);
6453    });
6454
6455    workspace_b
6456        .update(cx_b, |workspace, cx| {
6457            assert_eq!(*workspace.active_pane(), pane_b1);
6458            workspace.open_path((worktree_id, "4.txt"), None, true, cx)
6459        })
6460        .await
6461        .unwrap();
6462    cx_a.foreground().run_until_parked();
6463
6464    // Ensure leader updates don't change the active pane of followers
6465    workspace_a.read_with(cx_a, |workspace, _| {
6466        assert_eq!(*workspace.active_pane(), pane_a1);
6467    });
6468    workspace_b.read_with(cx_b, |workspace, _| {
6469        assert_eq!(*workspace.active_pane(), pane_b1);
6470    });
6471
6472    // Ensure peers following each other doesn't cause an infinite loop.
6473    assert_eq!(
6474        workspace_a.read_with(cx_a, |workspace, cx| workspace
6475            .active_item(cx)
6476            .unwrap()
6477            .project_path(cx)),
6478        Some((worktree_id, "3.txt").into())
6479    );
6480    workspace_a.update(cx_a, |workspace, cx| {
6481        assert_eq!(
6482            workspace.active_item(cx).unwrap().project_path(cx),
6483            Some((worktree_id, "3.txt").into())
6484        );
6485        workspace.activate_next_pane(cx);
6486    });
6487
6488    workspace_a.update(cx_a, |workspace, cx| {
6489        assert_eq!(
6490            workspace.active_item(cx).unwrap().project_path(cx),
6491            Some((worktree_id, "4.txt").into())
6492        );
6493    });
6494
6495    workspace_b.update(cx_b, |workspace, cx| {
6496        assert_eq!(
6497            workspace.active_item(cx).unwrap().project_path(cx),
6498            Some((worktree_id, "4.txt").into())
6499        );
6500        workspace.activate_next_pane(cx);
6501    });
6502
6503    workspace_b.update(cx_b, |workspace, cx| {
6504        assert_eq!(
6505            workspace.active_item(cx).unwrap().project_path(cx),
6506            Some((worktree_id, "3.txt").into())
6507        );
6508    });
6509}
6510
6511#[gpui::test(iterations = 10)]
6512async fn test_auto_unfollowing(
6513    deterministic: Arc<Deterministic>,
6514    cx_a: &mut TestAppContext,
6515    cx_b: &mut TestAppContext,
6516) {
6517    deterministic.forbid_parking();
6518    cx_a.update(editor::init);
6519    cx_b.update(editor::init);
6520
6521    // 2 clients connect to a server.
6522    let mut server = TestServer::start(&deterministic).await;
6523    let client_a = server.create_client(cx_a, "user_a").await;
6524    let client_b = server.create_client(cx_b, "user_b").await;
6525    server
6526        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6527        .await;
6528    let active_call_a = cx_a.read(ActiveCall::global);
6529    let active_call_b = cx_b.read(ActiveCall::global);
6530
6531    // Client A shares a project.
6532    client_a
6533        .fs
6534        .insert_tree(
6535            "/a",
6536            json!({
6537                "1.txt": "one",
6538                "2.txt": "two",
6539                "3.txt": "three",
6540            }),
6541        )
6542        .await;
6543    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
6544    active_call_a
6545        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
6546        .await
6547        .unwrap();
6548
6549    let project_id = active_call_a
6550        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6551        .await
6552        .unwrap();
6553    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6554    active_call_b
6555        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
6556        .await
6557        .unwrap();
6558
6559    // Client A opens some editors.
6560    let workspace_a = client_a.build_workspace(&project_a, cx_a);
6561    let _editor_a1 = workspace_a
6562        .update(cx_a, |workspace, cx| {
6563            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
6564        })
6565        .await
6566        .unwrap()
6567        .downcast::<Editor>()
6568        .unwrap();
6569
6570    // Client B starts following client A.
6571    let workspace_b = client_b.build_workspace(&project_b, cx_b);
6572    let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
6573    let leader_id = project_b.read_with(cx_b, |project, _| {
6574        project.collaborators().values().next().unwrap().peer_id
6575    });
6576    workspace_b
6577        .update(cx_b, |workspace, cx| {
6578            workspace
6579                .toggle_follow(&ToggleFollow(leader_id), cx)
6580                .unwrap()
6581        })
6582        .await
6583        .unwrap();
6584    assert_eq!(
6585        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6586        Some(leader_id)
6587    );
6588    let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
6589        workspace
6590            .active_item(cx)
6591            .unwrap()
6592            .downcast::<Editor>()
6593            .unwrap()
6594    });
6595
6596    // When client B moves, it automatically stops following client A.
6597    editor_b2.update(cx_b, |editor, cx| editor.move_right(&editor::MoveRight, cx));
6598    assert_eq!(
6599        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6600        None
6601    );
6602
6603    workspace_b
6604        .update(cx_b, |workspace, cx| {
6605            workspace
6606                .toggle_follow(&ToggleFollow(leader_id), cx)
6607                .unwrap()
6608        })
6609        .await
6610        .unwrap();
6611    assert_eq!(
6612        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6613        Some(leader_id)
6614    );
6615
6616    // When client B edits, it automatically stops following client A.
6617    editor_b2.update(cx_b, |editor, cx| editor.insert("X", cx));
6618    assert_eq!(
6619        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6620        None
6621    );
6622
6623    workspace_b
6624        .update(cx_b, |workspace, cx| {
6625            workspace
6626                .toggle_follow(&ToggleFollow(leader_id), cx)
6627                .unwrap()
6628        })
6629        .await
6630        .unwrap();
6631    assert_eq!(
6632        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6633        Some(leader_id)
6634    );
6635
6636    // When client B scrolls, it automatically stops following client A.
6637    editor_b2.update(cx_b, |editor, cx| {
6638        editor.set_scroll_position(vec2f(0., 3.), cx)
6639    });
6640    assert_eq!(
6641        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6642        None
6643    );
6644
6645    workspace_b
6646        .update(cx_b, |workspace, cx| {
6647            workspace
6648                .toggle_follow(&ToggleFollow(leader_id), cx)
6649                .unwrap()
6650        })
6651        .await
6652        .unwrap();
6653    assert_eq!(
6654        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6655        Some(leader_id)
6656    );
6657
6658    // When client B activates a different pane, it continues following client A in the original pane.
6659    workspace_b.update(cx_b, |workspace, cx| {
6660        workspace.split_pane(pane_b.clone(), SplitDirection::Right, cx)
6661    });
6662    assert_eq!(
6663        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6664        Some(leader_id)
6665    );
6666
6667    workspace_b.update(cx_b, |workspace, cx| workspace.activate_next_pane(cx));
6668    assert_eq!(
6669        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6670        Some(leader_id)
6671    );
6672
6673    // When client B activates a different item in the original pane, it automatically stops following client A.
6674    workspace_b
6675        .update(cx_b, |workspace, cx| {
6676            workspace.open_path((worktree_id, "2.txt"), None, true, cx)
6677        })
6678        .await
6679        .unwrap();
6680    assert_eq!(
6681        workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
6682        None
6683    );
6684}
6685
6686#[gpui::test(iterations = 10)]
6687async fn test_peers_simultaneously_following_each_other(
6688    deterministic: Arc<Deterministic>,
6689    cx_a: &mut TestAppContext,
6690    cx_b: &mut TestAppContext,
6691) {
6692    deterministic.forbid_parking();
6693    cx_a.update(editor::init);
6694    cx_b.update(editor::init);
6695
6696    let mut server = TestServer::start(&deterministic).await;
6697    let client_a = server.create_client(cx_a, "user_a").await;
6698    let client_b = server.create_client(cx_b, "user_b").await;
6699    server
6700        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
6701        .await;
6702    let active_call_a = cx_a.read(ActiveCall::global);
6703
6704    client_a.fs.insert_tree("/a", json!({})).await;
6705    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
6706    let workspace_a = client_a.build_workspace(&project_a, cx_a);
6707    let project_id = active_call_a
6708        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
6709        .await
6710        .unwrap();
6711
6712    let project_b = client_b.build_remote_project(project_id, cx_b).await;
6713    let workspace_b = client_b.build_workspace(&project_b, cx_b);
6714
6715    deterministic.run_until_parked();
6716    let client_a_id = project_b.read_with(cx_b, |project, _| {
6717        project.collaborators().values().next().unwrap().peer_id
6718    });
6719    let client_b_id = project_a.read_with(cx_a, |project, _| {
6720        project.collaborators().values().next().unwrap().peer_id
6721    });
6722
6723    let a_follow_b = workspace_a.update(cx_a, |workspace, cx| {
6724        workspace
6725            .toggle_follow(&ToggleFollow(client_b_id), cx)
6726            .unwrap()
6727    });
6728    let b_follow_a = workspace_b.update(cx_b, |workspace, cx| {
6729        workspace
6730            .toggle_follow(&ToggleFollow(client_a_id), cx)
6731            .unwrap()
6732    });
6733
6734    futures::try_join!(a_follow_b, b_follow_a).unwrap();
6735    workspace_a.read_with(cx_a, |workspace, _| {
6736        assert_eq!(
6737            workspace.leader_for_pane(workspace.active_pane()),
6738            Some(client_b_id)
6739        );
6740    });
6741    workspace_b.read_with(cx_b, |workspace, _| {
6742        assert_eq!(
6743            workspace.leader_for_pane(workspace.active_pane()),
6744            Some(client_a_id)
6745        );
6746    });
6747}
6748
6749#[derive(Debug, Eq, PartialEq)]
6750struct RoomParticipants {
6751    remote: Vec<String>,
6752    pending: Vec<String>,
6753}
6754
6755fn room_participants(room: &ModelHandle<Room>, cx: &mut TestAppContext) -> RoomParticipants {
6756    room.read_with(cx, |room, _| {
6757        let mut remote = room
6758            .remote_participants()
6759            .iter()
6760            .map(|(_, participant)| participant.user.github_login.clone())
6761            .collect::<Vec<_>>();
6762        let mut pending = room
6763            .pending_participants()
6764            .iter()
6765            .map(|user| user.github_login.clone())
6766            .collect::<Vec<_>>();
6767        remote.sort();
6768        pending.sort();
6769        RoomParticipants { remote, pending }
6770    })
6771}