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