Files
announcement/tests/toctou_coverage_tests.rs
swaits e8e55b7676 chore: initial release v0.1.0
Runtime-agnostic oneshot broadcast channel for Rust.
2025-10-13 19:36:37 -06:00

193 lines
6.4 KiB
Rust

//! Tests specifically designed to hit TOCTOU protection code paths for coverage.
//!
//! These tests target the race condition protection code in listen(), listen_blocking(),
//! and listen_timeout_blocking() that checks for values/closure after registering the listener.
use announcement::Announcement;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
#[cfg(feature = "std")]
use std::thread;
#[cfg(feature = "std")]
use std::time::Duration;
/// Test: Async listen() TOCTOU value path (line 674)
///
/// Verifies: Value announced during listener registration is caught
/// Category: race
#[tokio::test]
async fn test_async_listen_toctou_value_path() {
// Run multiple iterations to increase chance of hitting the race window
for _ in 0..1000 {
let (announcer, announcement) = Announcement::<i32>::new();
let listener = announcement.listener();
let ready = Arc::new(AtomicBool::new(false));
let ready_clone = ready.clone();
// Spawn task that announces as soon as listener starts waiting
tokio::spawn(async move {
// Spin until listener is likely setting up
while !ready_clone.load(Ordering::SeqCst) {
tokio::task::yield_now().await;
}
// Announce immediately to hit TOCTOU window
let _ = announcer.announce(42);
});
// Start listening with a flag to signal we're about to wait
ready.store(true, Ordering::SeqCst);
let result = listener.listen().await;
// Should get the value (either fast path or TOCTOU path)
assert_eq!(result, Ok(42));
}
}
/// Test: Async listen() TOCTOU closed path (line 681)
///
/// Verifies: Announcer closed during listener registration is caught
/// Category: race
#[tokio::test]
async fn test_async_listen_toctou_closed_path() {
use std::sync::Barrier;
// Run multiple iterations to increase chance of hitting the race window
for _ in 0..10000 {
let (announcer, announcement) = Announcement::<i32>::new();
let listener = announcement.listener();
let barrier = Arc::new(Barrier::new(2));
let barrier_clone = barrier.clone();
// Spawn task that drops announcer at precisely the right moment
tokio::task::spawn_blocking(move || {
// Wait for listener to start setup
barrier_clone.wait();
// Immediately drop to hit the narrow TOCTOU window
drop(announcer);
});
// Use a manual future that coordinates with the barrier
let result = tokio::task::spawn_blocking(move || {
barrier.wait(); // Signal we're about to call listen
// Immediately start listening to hit the race window
tokio::runtime::Handle::current().block_on(async { listener.listen().await })
})
.await
.unwrap();
// Should get Closed error (either first check or TOCTOU path)
assert!(result.is_err());
}
}
/// Test: Blocking listen_timeout TOCTOU value path (line 819)
///
/// Verifies: Value announced during listener registration is caught in blocking mode
/// Category: race
#[cfg(feature = "std")]
#[test]
fn test_blocking_timeout_toctou_value_path() {
use std::sync::Barrier;
// Run many iterations with precise synchronization
for _ in 0..10000 {
let (announcer, announcement) = Announcement::<i32>::new();
let listener = announcement.listener();
let barrier = Arc::new(Barrier::new(2));
let barrier_clone = barrier.clone();
// Spawn thread that announces at precisely the right moment
thread::spawn(move || {
// Wait for listener to start
barrier_clone.wait();
// Announce immediately to hit TOCTOU window
let _ = announcer.announce(42);
});
// Synchronize and immediately start listening
barrier.wait();
let result = listener.listen_timeout_blocking(Duration::from_millis(100));
// Should get the value (either fast path or TOCTOU path)
assert_eq!(result, Ok(42));
}
}
/// Test: Blocking listen_timeout TOCTOU closed path (line 823)
///
/// Verifies: Announcer closed during listener registration is caught in blocking mode
/// Category: race
#[cfg(feature = "std")]
#[test]
fn test_blocking_timeout_toctou_closed_path() {
use std::sync::Barrier;
// Run many iterations with precise synchronization
for _ in 0..10000 {
let (announcer, announcement) = Announcement::<i32>::new();
let listener = announcement.listener();
let barrier = Arc::new(Barrier::new(2));
let barrier_clone = barrier.clone();
// Spawn thread that drops announcer at precisely the right moment
thread::spawn(move || {
// Wait for listener to start
barrier_clone.wait();
// Drop immediately to hit TOCTOU window
drop(announcer);
});
// Synchronize and immediately start listening
barrier.wait();
let result = listener.listen_timeout_blocking(Duration::from_millis(100));
// Should get Closed error (either first check or TOCTOU path)
assert!(result.is_err());
}
}
/// Test: Blocking listen_timeout zero remaining time path (line 828)
///
/// Verifies: Timeout returns error when remaining time is zero
/// Category: unit
#[cfg(feature = "std")]
#[test]
fn test_blocking_timeout_zero_remaining() {
let (announcer, announcement) = Announcement::<i32>::new();
let listener = announcement.listener();
// Keep announcer alive but don't announce
thread::spawn(move || {
thread::sleep(Duration::from_secs(10));
drop(announcer);
});
// Use a very short timeout (1 nanosecond)
let result = listener.listen_timeout_blocking(Duration::from_nanos(1));
// Should timeout
assert!(result.is_err());
}
/// Test: Blocking listen_timeout actual timeout path (line 828 alternative)
///
/// Verifies: Timeout returns error when deadline passes
/// Category: unit
#[cfg(feature = "std")]
#[test]
fn test_blocking_timeout_deadline_expires() {
let (_announcer, announcement) = Announcement::<i32>::new();
let listener = announcement.listener();
// Use 1 millisecond timeout with no announcement
let result = listener.listen_timeout_blocking(Duration::from_millis(1));
// Should timeout (not closed)
assert_eq!(result.unwrap_err(), announcement::ListenError::Timeout);
}