integration_tests.rs

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