diff --git a/crates/scanner/src/scanner_io/tests/scoped_entry_fallback.rs b/crates/scanner/src/scanner_io/tests/scoped_entry_fallback.rs index e8f40467f..88a49b5dd 100644 --- a/crates/scanner/src/scanner_io/tests/scoped_entry_fallback.rs +++ b/crates/scanner/src/scanner_io/tests/scoped_entry_fallback.rs @@ -226,9 +226,40 @@ fn record_segment_dirty_usage(bucket: &str) { } } -#[tokio::test] +// The scoped fallback fixture keeps two EC pools and several scan futures live +// at once. Run the async cases on a dedicated stack so Linux libtest defaults +// exercise the assertions instead of aborting before the oracle finishes. +fn run_scoped_entry_fallback_test(thread_name: &'static str, test_fn: F) +where + F: FnOnce() -> Fut + Send + 'static, + Fut: std::future::Future + 'static, +{ + let handle = std::thread::Builder::new() + .name(thread_name.to_string()) + .stack_size(32 * 1024 * 1024) + .spawn(move || { + let runtime = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .expect("scoped entry fallback runtime should build"); + runtime.block_on(test_fn()); + }) + .expect("scoped entry fallback test thread should spawn"); + if let Err(payload) = handle.join() { + std::panic::resume_unwind(payload); + } +} + +#[test] #[serial] -async fn scoped_entry_fallback_distinguishes_planned_scope_from_real_cold_walks() { +fn scoped_entry_fallback_distinguishes_planned_scope_from_real_cold_walks() { + run_scoped_entry_fallback_test( + "scanner-scoped-entry-planned-scope", + scoped_entry_fallback_distinguishes_planned_scope_from_real_cold_walks_case, + ); +} + +async fn scoped_entry_fallback_distinguishes_planned_scope_from_real_cold_walks_case() { let (_dir, store) = setup_two_pool_scanner_store().await; clear_dirty_usage_buckets_for_tests(); let hot = format!("hot-{}", Uuid::new_v4().simple()); @@ -251,9 +282,16 @@ async fn scoped_entry_fallback_distinguishes_planned_scope_from_real_cold_walks( clear_dirty_usage_buckets_for_tests(); } -#[tokio::test] +#[test] #[serial] -async fn scoped_entry_fallback_rejects_invalid_persisted_baseline_at_the_walker() { +fn scoped_entry_fallback_rejects_invalid_persisted_baseline_at_the_walker() { + run_scoped_entry_fallback_test( + "scanner-scoped-entry-invalid-baseline", + scoped_entry_fallback_rejects_invalid_persisted_baseline_at_the_walker_case, + ); +} + +async fn scoped_entry_fallback_rejects_invalid_persisted_baseline_at_the_walker_case() { let (_dir, store) = setup_two_pool_scanner_store().await; clear_dirty_usage_buckets_for_tests(); let hot = format!("hot-{}", Uuid::new_v4().simple()); @@ -310,9 +348,16 @@ async fn scoped_entry_fallback_rejects_invalid_persisted_baseline_at_the_walker( clear_dirty_usage_buckets_for_tests(); } -#[tokio::test] +#[test] #[serial] -async fn scoped_entry_fallback_covers_overflow_and_new_bucket_inventory() { +fn scoped_entry_fallback_covers_overflow_and_new_bucket_inventory() { + run_scoped_entry_fallback_test( + "scanner-scoped-entry-overflow-inventory", + scoped_entry_fallback_covers_overflow_and_new_bucket_inventory_case, + ); +} + +async fn scoped_entry_fallback_covers_overflow_and_new_bucket_inventory_case() { let (_dir, store) = setup_two_pool_scanner_store().await; clear_dirty_usage_buckets_for_tests(); let hot = format!("hot-{}", Uuid::new_v4().simple());