//! Runtime comparison tests for the announcement crate. //! Verifies identical behavior across different async runtimes and blocking mode. use announcement::Announcement; use std::sync::Arc; use std::thread; use std::time::Duration; // === TOKIO TESTS === /// Test: Runtime test: basic announce and listen /// /// Verifies: Basic announcement and listening works correctly on the runtime /// Category: runtime #[tokio::test] async fn tokio_basic_announce_listen() { let (announcer, announcement) = Announcement::new(); let listener = announcement.listener(); announcer.announce(42).unwrap(); assert_eq!(listener.listen().await.unwrap(), 42); } /// Test: Runtime test: listen initiated before announce /// /// Verifies: Listener waiting before announce receives value correctly on the runtime /// Category: runtime #[tokio::test] async fn tokio_listen_before_announce() { let (announcer, announcement) = Announcement::new(); let listener = announcement.listener(); let handle = tokio::spawn(async move { listener.listen().await.unwrap() }); tokio::time::sleep(Duration::from_millis(10)).await; announcer.announce(42).unwrap(); assert_eq!(handle.await.unwrap(), 42); } /// Test: Runtime test: multiple concurrent listeners /// /// Verifies: Multiple listeners all receive the same announced value on the runtime /// Category: runtime #[tokio::test] async fn tokio_multiple_listeners() { let (announcer, announcement) = Announcement::new(); let mut handles = vec![]; for i in 0..10 { let listener = announcement.listener(); handles.push(tokio::spawn(async move { (i, listener.listen().await.unwrap()) })); } announcer.announce(42).unwrap(); for handle in handles { let (_, value) = handle.await.unwrap(); assert_eq!(value, 42); } } /// Test: Runtime test: multi-threaded execution /// /// Verifies: Multi-threaded runtime executes announcements correctly /// Category: runtime #[tokio::test(flavor = "multi_thread")] async fn tokio_multi_threaded() { let (announcer, announcement) = Announcement::new(); let mut handles = vec![]; for i in 0..20 { let listener = announcement.listener(); handles.push(tokio::spawn(async move { (i, listener.listen().await.unwrap()) })); } tokio::time::sleep(Duration::from_millis(10)).await; announcer.announce(42).unwrap(); for handle in handles { let (_, value) = handle.await.unwrap(); assert_eq!(value, 42); } } /// Test: Runtime test: current-thread execution /// /// Verifies: Current-thread runtime executes announcements correctly /// Category: runtime #[tokio::test(flavor = "current_thread")] async fn tokio_current_thread() { let (announcer, announcement) = Announcement::new(); let mut handles = vec![]; for i in 0..20 { let listener = announcement.listener(); handles.push(tokio::spawn(async move { (i, listener.listen().await.unwrap()) })); } tokio::time::sleep(Duration::from_millis(10)).await; announcer.announce(42).unwrap(); for handle in handles { let (_, value) = handle.await.unwrap(); assert_eq!(value, 42); } } /// Test: Runtime test: select/race pattern /// /// Verifies: Runtime select patterns work correctly with multiple announcements /// Category: runtime #[tokio::test] async fn tokio_select_pattern() { let (announcer1, announcement1) = Announcement::new(); let (announcer2, announcement2) = Announcement::new(); let listener1 = announcement1.listener(); let listener2 = announcement2.listener(); tokio::spawn(async move { tokio::time::sleep(Duration::from_millis(50)).await; announcer2.announce(2).unwrap(); }); tokio::spawn(async move { tokio::time::sleep(Duration::from_millis(20)).await; announcer1.announce(1).unwrap(); }); tokio::select! { val = listener1.listen() => assert_eq!(val, Ok(1)), val = listener2.listen() => assert_eq!(val, Ok(2)), } } /// Test: Runtime test: timeout behavior /// /// Verifies: Runtime timeout mechanisms work correctly with announcements /// Category: runtime #[tokio::test] async fn tokio_timeout_behavior() { let (_announcer, announcement) = Announcement::::new(); let listener = announcement.listener(); let result = tokio::time::timeout(Duration::from_millis(100), listener.listen()).await; assert!(result.is_err()); // Should timeout } /// Test: Runtime test: blocking task interoperability /// /// Verifies: Runtime blocking task spawning interoperates correctly with async /// Category: runtime #[tokio::test] async fn tokio_spawn_blocking_interop() { let (announcer, announcement) = Announcement::new(); let listener = announcement.listener(); let blocking_handle = tokio::task::spawn_blocking(move || { // Blocking thread operation thread::sleep(Duration::from_millis(10)); announcer.announce(42).unwrap(); }); let value = listener.listen().await.unwrap(); assert_eq!(value, 42); blocking_handle.await.unwrap(); } /// Test: Runtime test: mixed async and sync listeners /// /// Verifies: Mix of async and synchronous listeners work correctly on the runtime /// Category: runtime #[tokio::test] async fn tokio_mixed_async_sync() { let (announcer, announcement) = Announcement::new(); // Async listeners let mut async_handles = vec![]; for _ in 0..5 { let listener = announcement.listener(); async_handles.push(tokio::spawn( async move { listener.listen().await.unwrap() }, )); } // Sync listeners in blocking threads let mut sync_handles = vec![]; for _ in 0..5 { let listener = announcement.listener(); sync_handles.push(tokio::task::spawn_blocking(move || loop { if let Some(val) = listener.try_listen() { return val; } thread::sleep(Duration::from_millis(1)); })); } announcer.announce(42).unwrap(); for handle in async_handles { assert_eq!(handle.await.unwrap(), 42); } for handle in sync_handles { assert_eq!(handle.await.unwrap(), 42); } } // === SMOL TESTS (replacing async-std) === /// Test: Runtime test: basic announce and listen /// /// Verifies: Basic announcement and listening works correctly on the runtime /// Category: runtime #[test] fn smol_basic_announce_listen() { smol::block_on(async { let (announcer, announcement) = Announcement::new(); let listener = announcement.listener(); announcer.announce(42).unwrap(); assert_eq!(listener.listen().await.unwrap(), 42); }); } /// Test: Runtime test: listen initiated before announce /// /// Verifies: Listener waiting before announce receives value correctly on the runtime /// Category: runtime #[test] fn smol_listen_before_announce() { smol::block_on(async { let (announcer, announcement) = Announcement::new(); let listener = announcement.listener(); let handle = smol::spawn(async move { listener.listen().await.unwrap() }); smol::Timer::after(Duration::from_millis(10)).await; announcer.announce(42).unwrap(); assert_eq!(handle.await, 42); }); } /// Test: Runtime test: multiple concurrent listeners /// /// Verifies: Multiple listeners all receive the same announced value on the runtime /// Category: runtime #[test] fn smol_multiple_listeners() { smol::block_on(async { let (announcer, announcement) = Announcement::new(); let mut handles = vec![]; for i in 0..10 { let listener = announcement.listener(); handles.push(smol::spawn( async move { (i, listener.listen().await.unwrap()) }, )); } announcer.announce(42).unwrap(); for handle in handles { let (_, value) = handle.await; assert_eq!(value, 42); } }); } /// Test: Runtime test: timeout behavior /// /// Verifies: Runtime timeout mechanisms work correctly with announcements /// Category: runtime #[test] fn smol_timeout_behavior() { smol::block_on(async { let (_announcer, announcement) = Announcement::::new(); let listener = announcement.listener(); let result = smol::future::or( async { listener.listen().await?; Ok(()) }, async { smol::Timer::after(Duration::from_millis(100)).await; Err::<(), _>(announcement::ListenError::Timeout) }, ) .await; assert!(result.is_err()); // Should timeout }); } /// Test: Runtime test: blocking task interoperability /// /// Verifies: Runtime blocking task spawning interoperates correctly with async /// Category: runtime #[test] fn smol_spawn_blocking_interop() { smol::block_on(async { let (announcer, announcement) = Announcement::new(); let listener = announcement.listener(); let blocking_handle = smol::unblock(move || { // Blocking thread operation thread::sleep(Duration::from_millis(10)); announcer.announce(42).unwrap(); }); let value = listener.listen().await.unwrap(); assert_eq!(value, 42); blocking_handle.await; }); } // === SMOL TESTS (already in smol_runtime.rs, adding comparisons) === /// Test: Runtime test: Smol runtime basic operations /// /// Verifies: Basic announcement and listening works on Smol runtime /// Category: runtime #[test] fn smol_basic_operations() { smol::block_on(async { let (announcer, announcement) = Announcement::new(); let listener = announcement.listener(); announcer.announce(42).unwrap(); assert_eq!(listener.listen().await.unwrap(), 42); }); } /// Test: Runtime test: concurrent tasks /// /// Verifies: Concurrent tasks work correctly on the runtime /// Category: runtime #[test] fn smol_concurrent_tasks() { smol::block_on(async { let (announcer, announcement) = Announcement::new(); let mut handles = vec![]; for i in 0..10 { let listener = announcement.listener(); handles.push(smol::spawn( async move { (i, listener.listen().await.unwrap()) }, )); } smol::Timer::after(Duration::from_millis(10)).await; announcer.announce(42).unwrap(); for handle in handles { let (_, value) = handle.await; assert_eq!(value, 42); } }); } // === BLOCKING TESTS === /// Test: Runtime test: blocking mode basic operations /// /// Verifies: Basic announcement and listen operations work in blocking mode /// Category: runtime #[cfg(feature = "std")] #[test] fn blocking_basic_operations() { let (announcer, announcement) = Announcement::new(); let listener = announcement.listener(); announcer.announce(42).unwrap(); assert_eq!( listener.listen_timeout_blocking(Duration::from_millis(100)), Ok(42) ); } /// Test: Runtime test: blocking mode timeout /// /// Verifies: Timeout behavior works correctly in blocking mode /// Category: runtime #[cfg(feature = "std")] #[test] fn blocking_timeout() { let (_announcer, announcement) = Announcement::::new(); let listener = announcement.listener(); let result = listener.listen_timeout_blocking(Duration::from_millis(100)); assert_eq!(result, Err(announcement::ListenError::Timeout)); } /// Test: Runtime test: blocking mode multiple threads /// /// Verifies: Multiple blocking threads receive announced values correctly /// Category: runtime #[cfg(feature = "std")] #[test] fn blocking_multiple_threads() { let (announcer, announcement) = Announcement::new(); let mut handles = vec![]; for i in 0..10 { let listener = announcement.listener(); handles.push(thread::spawn(move || { ( i, listener.listen_timeout_blocking(Duration::from_millis(100)), ) })); } thread::sleep(Duration::from_millis(10)); announcer.announce(42).unwrap(); for handle in handles { let (_, value) = handle.join().unwrap(); assert_eq!(value, Ok(42)); } } // === COMPARISON TESTS === // Test that verifies identical behavior across all runtimes /// Test: Runtime test: all runtimes basic comparison /// /// Verifies: All runtimes produce identical results for basic operations /// Category: runtime #[test] fn compare_all_runtimes_basic() { // Tokio multi-thread let tokio_multi_result = tokio::runtime::Builder::new_multi_thread() .enable_all() .build() .unwrap() .block_on(async { let (announcer, announcement) = Announcement::new(); let listener = announcement.listener(); announcer.announce(42).unwrap(); listener.listen().await.unwrap() }); // Tokio current-thread let tokio_current_result = tokio::runtime::Builder::new_current_thread() .enable_all() .build() .unwrap() .block_on(async { let (announcer, announcement) = Announcement::new(); let listener = announcement.listener(); announcer.announce(42).unwrap(); listener.listen().await.unwrap() }); // Smol let smol_result2 = smol::block_on(async { let (announcer, announcement) = Announcement::new(); let listener = announcement.listener(); announcer.announce(42).unwrap(); listener.listen().await.unwrap() }); // Smol let smol_result = smol::block_on(async { let (announcer, announcement) = Announcement::new(); let listener = announcement.listener(); announcer.announce(42).unwrap(); listener.listen().await.unwrap() }); // Blocking #[cfg(feature = "std")] let blocking_result = { let (announcer, announcement) = Announcement::new(); let listener = announcement.listener(); announcer.announce(42).unwrap(); listener.listen_timeout_blocking(Duration::from_millis(100)) }; // All should be identical assert_eq!(tokio_multi_result, 42); assert_eq!(tokio_current_result, 42); assert_eq!(smol_result2, 42); assert_eq!(smol_result, 42); #[cfg(feature = "std")] assert_eq!(blocking_result, Ok(42)); } /// Test: Runtime test: all runtimes concurrent comparison /// /// Verifies: All runtimes produce identical results for concurrent operations /// Category: runtime #[test] fn compare_all_runtimes_concurrent() { let test_concurrent = |runtime: &str| -> Vec { match runtime { "tokio-multi" => tokio::runtime::Builder::new_multi_thread() .enable_all() .build() .unwrap() .block_on(async { let (announcer, announcement) = Announcement::new(); let mut handles = vec![]; for _ in 0..5 { let listener = announcement.listener(); handles.push(tokio::spawn( async move { listener.listen().await.unwrap() }, )); } announcer.announce(42).unwrap(); let mut results = vec![]; for handle in handles { results.push(handle.await.unwrap()); } results }), "tokio-current" => tokio::runtime::Builder::new_current_thread() .enable_all() .build() .unwrap() .block_on(async { let (announcer, announcement) = Announcement::new(); let mut handles = vec![]; for _ in 0..5 { let listener = announcement.listener(); handles.push(tokio::spawn( async move { listener.listen().await.unwrap() }, )); } announcer.announce(42).unwrap(); let mut results = vec![]; for handle in handles { results.push(handle.await.unwrap()); } results }), "smol2" => smol::block_on(async { let (announcer, announcement) = Announcement::new(); let mut handles = vec![]; for _ in 0..5 { let listener = announcement.listener(); handles.push(smol::spawn(async move { listener.listen().await.unwrap() })); } announcer.announce(42).unwrap(); let mut results = vec![]; for handle in handles { results.push(handle.await); } results }), "smol" => smol::block_on(async { let (announcer, announcement) = Announcement::new(); let mut handles = vec![]; for _ in 0..5 { let listener = announcement.listener(); handles.push(smol::spawn(async move { listener.listen().await.unwrap() })); } announcer.announce(42).unwrap(); let mut results = vec![]; for handle in handles { results.push(handle.await); } results }), _ => panic!("Unknown runtime"), } }; let tokio_multi = test_concurrent("tokio-multi"); let tokio_current = test_concurrent("tokio-current"); let smol2 = test_concurrent("smol2"); let smol = test_concurrent("smol"); // All should produce same results assert_eq!(tokio_multi, vec![42; 5]); assert_eq!(tokio_current, vec![42; 5]); assert_eq!(smol2, vec![42; 5]); assert_eq!(smol, vec![42; 5]); } // === CROSS-RUNTIME TESTS === /// Test: Runtime test: cross-runtime announcement /// /// Verifies: Announcement works correctly when components use different runtimes /// Category: runtime #[test] fn cross_runtime_announcement() { let (announcer, announcement) = Announcement::new(); let announcement = Arc::new(announcement); // Create listener in one runtime let ann1 = announcement.clone(); let tokio_listener = tokio::runtime::Builder::new_current_thread() .enable_all() .build() .unwrap() .block_on(async move { ann1.listener() }); // Create listener in another runtime let ann2 = announcement.clone(); let smol_listener = smol::block_on(async move { ann2.listener() }); // Announce in yet another runtime smol::block_on(async { announcer.announce(42).unwrap(); }); // Verify in original runtimes let tokio_result = tokio::runtime::Builder::new_current_thread() .enable_all() .build() .unwrap() .block_on(async { tokio_listener.listen().await.unwrap() }); let smol_result = smol::block_on(async { smol_listener.listen().await.unwrap() }); assert_eq!(tokio_result, 42); assert_eq!(smol_result, 42); } // === BEHAVIOR CONSISTENCY TESTS === /// Test: Runtime test: consistent is_announced behavior /// /// Verifies: is_announced() returns consistent results across invocations /// Category: runtime #[test] fn verify_consistent_is_announced() { let test_is_announced = || { let (announcer, announcement) = Announcement::new(); let before = announcement.is_announced(); announcer.announce(42).unwrap(); let after = announcement.is_announced(); (before, after) }; // Run multiple times to ensure consistency for _ in 0..10 { let (before, after) = test_is_announced(); assert!(!before); assert!(after); } } /// Test: Runtime test: consistent try_listen behavior /// /// Verifies: try_listen() returns consistent results across invocations /// Category: runtime #[test] fn verify_consistent_try_listen() { let test_try_listen = || { let (announcer, announcement) = Announcement::new(); let listener = announcement.listener(); let before = listener.try_listen(); announcer.announce(42).unwrap(); let after = listener.try_listen(); (before, after) }; // Run multiple times to ensure consistency for _ in 0..10 { let (before, after) = test_try_listen(); assert_eq!(before, None); assert_eq!(after, Some(42)); } } /// Test: Runtime test: multiple concurrent listeners /// /// Verifies: Multiple listeners all receive the same announced value on the runtime /// Category: runtime #[test] fn verify_consistent_multiple_listeners() { let test_multiple = || { let (announcer, announcement) = Announcement::new(); let listeners: Vec<_> = (0..5).map(|_| announcement.listener()).collect(); announcer.announce(42).unwrap(); listeners.iter().map(|l| l.try_listen()).collect::>() }; // Run multiple times for _ in 0..10 { let results = test_multiple(); assert_eq!(results, vec![Some(42); 5]); } } // === FINAL COMPARISON SUMMARY === /// Test: Runtime test: comparison summary /// /// Verifies: All runtime comparison tests pass and produce consistent results /// Category: runtime #[test] fn runtime_comparison_summary() { println!("Runtime Comparison Test Summary:"); println!("================================"); let mut all_passed = true; // Test each runtime { // Tokio let tokio_ok = std::panic::catch_unwind(|| { tokio::runtime::Runtime::new().unwrap().block_on(async { let (ann, announcement) = Announcement::new(); let l = announcement.listener(); ann.announce(1).unwrap(); assert_eq!(l.listen().await.unwrap(), 1); }); }) .is_ok(); println!("Tokio: {}", if tokio_ok { "✓ PASS" } else { "✗ FAIL" }); all_passed = all_passed && tokio_ok; // Smol let smol_ok = std::panic::catch_unwind(|| { smol::block_on(async { let (ann, announcement) = Announcement::new(); let l = announcement.listener(); ann.announce(3).unwrap(); assert_eq!(l.listen().await.unwrap(), 3); }); }) .is_ok(); println!("Smol: {}", if smol_ok { "✓ PASS" } else { "✗ FAIL" }); all_passed = all_passed && smol_ok; } // Blocking #[cfg(feature = "std")] { let blocking_ok = std::panic::catch_unwind(|| { let (ann, announcement) = Announcement::new(); let l = announcement.listener(); ann.announce(4).unwrap(); assert_eq!(l.listen_timeout_blocking(Duration::from_millis(100)), Ok(4)); }) .is_ok(); println!( "Blocking: {}", if blocking_ok { "✓ PASS" } else { "✗ FAIL" } ); all_passed = all_passed && blocking_ok; } println!("================================"); println!( "Overall: {}", if all_passed { "✓ ALL PASS" } else { "✗ SOME FAILED" } ); assert!(all_passed, "Not all runtime tests passed"); }