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