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