//! 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::::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::::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::::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::::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::::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::::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); }