integration_tests.rs

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