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