diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 2327aa6aad..905aa328f2 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -1390,6 +1390,52 @@ async fn test_leaving_worktree_while_opening_buffer( .await; } +#[gpui::test(iterations = 10)] +async fn test_canceling_buffer_opening( + deterministic: Arc, + cx_a: &mut TestAppContext, + cx_b: &mut TestAppContext, +) { + deterministic.forbid_parking(); + + let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; + let client_a = server.create_client(cx_a, "user_a").await; + let client_b = server.create_client(cx_b, "user_b").await; + server + .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + .await; + + client_a + .fs + .insert_tree( + "/dir", + json!({ + "a.txt": "abc", + }), + ) + .await; + let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; + let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + + let buffer_a = project_a + .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)) + .await + .unwrap(); + + // Open a buffer as client B but cancel after a random amount of time. + let buffer_b = project_b.update(cx_b, |p, cx| p.open_buffer_by_id(buffer_a.id() as u64, cx)); + deterministic.simulate_random_delay().await; + drop(buffer_b); + + // Try opening the same buffer again as client B, and ensure we can + // still do it despite the cancellation above. + let buffer_b = project_b + .update(cx_b, |p, cx| p.open_buffer_by_id(buffer_a.id() as u64, cx)) + .await + .unwrap(); + buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "abc")); +} + #[gpui::test(iterations = 10)] async fn test_leaving_project(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { cx_a.foreground().forbid_parking(); diff --git a/crates/gpui/src/executor.rs b/crates/gpui/src/executor.rs index 539d582e5b..980da91167 100644 --- a/crates/gpui/src/executor.rs +++ b/crates/gpui/src/executor.rs @@ -381,6 +381,17 @@ impl Deterministic { state.forbid_parking = true; state.rng = StdRng::seed_from_u64(state.seed); } + + pub async fn simulate_random_delay(&self) { + use rand::prelude::*; + use smol::future::yield_now; + if self.state.lock().rng.gen_bool(0.2) { + let yields = self.state.lock().rng.gen_range(1..=10); + for _ in 0..yields { + yield_now().await; + } + } + } } impl Drop for Timer { @@ -662,17 +673,9 @@ impl Background { #[cfg(any(test, feature = "test-support"))] pub async fn simulate_random_delay(&self) { - use rand::prelude::*; - use smol::future::yield_now; - match self { Self::Deterministic { executor, .. } => { - if executor.state.lock().rng.gen_bool(0.2) { - let yields = executor.state.lock().rng.gen_range(1..=10); - for _ in 0..yields { - yield_now().await; - } - } + executor.simulate_random_delay().await; } _ => { panic!("this method can only be called on a deterministic executor")