//! Targeted tests for Drop implementation mutations //! //! These tests specifically target the Drop impl for Announcer to catch //! mutations that would otherwise cause timeouts. They use short timeouts //! to fail fast instead of hanging. use announcement::{Announcement, ListenError}; use std::sync::Arc; use std::thread; use std::time::Duration; /// Test: Drop sets closed flag - verify with short timeout /// /// Verifies: Drop sets closed flag - with short timeout /// Category: drop #[test] fn test_drop_sets_closed_flag_quick() { let (announcer, announcement) = Announcement::::new(); // Verify not closed initially assert!(!announcement.is_closed()); // Drop the announcer drop(announcer); // CRITICAL: Must set closed flag // This catches the mutant that replaces Drop body with () assert!(announcement.is_closed(), "Drop must set closed flag"); } /// Test: Drop test: drop wakes blocking listeners quick /// /// Verifies: drop wakes blocking listeners quick /// Category: drop #[cfg(feature = "std")] #[test] fn test_drop_wakes_blocking_listeners_quick() { let (announcer, announcement) = Announcement::::new(); let listener = announcement.listener(); // Spawn thread that will wait let handle = thread::spawn(move || listener.listen_timeout_blocking(Duration::from_millis(100))); // Give thread time to start waiting thread::sleep(Duration::from_millis(10)); // Drop announcer - should wake listener drop(announcer); // Listener should receive Closed error, not Timeout // If Drop doesn't work, this will timeout let result = handle.join().expect("thread should not panic"); assert_eq!( result, Err(ListenError::Closed), "Drop must notify waiting listeners with Closed error" ); } /// Test: Drop with conditional check - verifies the condition logic /// /// Verifies: Drop with conditional check - verifies the condition logic /// Category: drop #[test] fn test_drop_conditional_already_announced() { let (announcer, announcement) = Announcement::::new(); let listener = announcement.listener(); // Announce a value announcer.announce(42).unwrap(); // Drop happens here, but should NOT set closed flag // because value was already announced // Verify: NOT closed (because value was announced) assert!( !announcement.is_closed(), "Drop should not set closed flag if value was announced" ); // Verify: Can still get the value assert_eq!(listener.try_listen(), Some(42)); } /// Test: Drop with conditional check - verifies the !closed check /// /// Verifies: Drop with conditional check - verifies the !closed check /// Category: drop #[test] fn test_drop_conditional_already_closed() { let (announcer, announcement) = Announcement::::new(); // Explicitly close first announcer.close(); // At this point, closed flag is already set assert!(announcement.is_closed()); // Drop happens here automatically // The condition should prevent double-notification // Still closed after drop assert!(announcement.is_closed()); } /// Test: Multiple drops with concurrent listeners /// /// Verifies: Multiple drops with concurrent listeners /// Category: drop #[tokio::test] async fn test_drop_wakes_multiple_async_listeners() { let (announcer, announcement) = Announcement::::new(); // Create multiple listeners let listeners: Vec<_> = (0..5).map(|_| announcement.listener()).collect(); // Spawn tasks for all listeners with short timeout let mut handles = Vec::new(); for listener in listeners { handles.push(tokio::spawn(async move { tokio::time::timeout(Duration::from_millis(100), listener.listen()).await })); } // Give listeners time to start waiting tokio::time::sleep(Duration::from_millis(10)).await; // Drop announcer - should wake all listeners drop(announcer); // All listeners should receive Closed error quickly, not timeout for handle in handles { let result = handle.await.expect("task should not panic"); assert!( result.is_ok(), "listener should receive notification, not timeout" ); assert_eq!( result.unwrap(), Err(ListenError::Closed), "Drop must notify all waiting listeners" ); } } /// Test: Drop notifies then listeners can check is_closed /// /// Verifies: Drop notifies then listeners can check is_closed /// Category: drop #[test] fn test_drop_notification_visible_to_new_listeners() { let (announcer, announcement) = Announcement::::new(); // Drop without announcing drop(announcer); // Create listener AFTER drop let listener = announcement.listener(); // Should immediately see closed status assert!(listener.is_closed()); assert_eq!(listener.try_listen(), None); } /// Test: Verify Drop impl checks value.is_none() correctly /// /// Verifies: Drop impl checks value.is_none() correctly /// Category: drop #[test] fn test_drop_checks_value_is_none_correctly() { // Test 1: value is None, should close { let (announcer, announcement) = Announcement::::new(); drop(announcer); assert!(announcement.is_closed(), "Should close when value is None"); } // Test 2: value is Some, should NOT close { let (_announcer, announcement) = Announcement::::new(); _announcer.announce(42).unwrap(); assert!( !announcement.is_closed(), "Should NOT close when value is Some" ); } } /// Test: Verify Drop impl checks !closed correctly /// /// Verifies: Drop impl checks !closed correctly /// Category: drop #[test] fn test_drop_checks_not_closed_correctly() { let (announcer, announcement) = Announcement::::new(); // First close explicitly announcer.close(); // closed flag should be set assert!(announcement.is_closed()); // Drop happens automatically here // The !closed check should prevent re-notification // Verify still closed (this is a sanity check) assert!(announcement.is_closed()); } /// Test: Stress test - many rapid drops /// /// Verifies: Stress test - many rapid drops /// Category: drop #[test] fn test_rapid_drops_stress() { for _ in 0..1000 { let (announcer, announcement) = Announcement::::new(); let listener = announcement.listener(); drop(announcer); assert!(announcement.is_closed()); assert!(listener.is_closed()); assert_eq!(listener.try_listen(), None); } } /// Test: Drop with Arc value type /// /// Verifies: Drop with Arc value type /// Category: drop #[test] fn test_drop_with_arc_value() { let (announcer, announcement) = Announcement::>::new(); let listener = announcement.listener(); drop(announcer); assert!(announcement.is_closed()); assert!(listener.is_closed()); } /// Test: Drop test: drop ordering set closed before notify /// /// Verifies: drop ordering set closed before notify /// Category: drop #[cfg(feature = "std")] #[test] fn test_drop_ordering_set_closed_before_notify() { // This test verifies that closed flag is set before notification // If the order is wrong, listeners might check closed flag before it's set let (announcer, announcement) = Announcement::::new(); let ann_clone = announcement.clone(); let handle = thread::spawn(move || { let listener = ann_clone.listener(); // Wait with very short timeout let result = listener.listen_timeout_blocking(Duration::from_millis(50)); // If we get Closed, verify the flag is set if result == Err(ListenError::Closed) { assert!( ann_clone.is_closed(), "closed flag must be visible when Closed error is returned" ); } result }); thread::sleep(Duration::from_millis(10)); drop(announcer); let result = handle.join().unwrap(); assert_eq!(result, Err(ListenError::Closed)); } /// Test: Drop in nested scope /// /// Verifies: Drop in nested scope /// Category: drop #[test] fn test_drop_in_nested_scope() { let announcement = { let (_announcer, announcement) = Announcement::::new(); // _announcer dropped here at end of scope announcement }; // Outside the scope, closed flag should be set assert!(announcement.is_closed()); } /// Test: Drop vs explicit close comparison /// /// Verifies: Drop vs explicit close comparison /// Category: drop #[test] fn test_drop_equivalent_to_explicit_close() { // Test 1: explicit close let (announcer1, announcement1) = Announcement::::new(); let listener1 = announcement1.listener(); announcer1.close(); // Test 2: drop let (announcer2, announcement2) = Announcement::::new(); let listener2 = announcement2.listener(); drop(announcer2); // Both should have same result assert_eq!(announcement1.is_closed(), announcement2.is_closed()); assert_eq!(listener1.try_listen(), listener2.try_listen()); assert_eq!(listener1.is_closed(), listener2.is_closed()); } /// Test: Drop with panic in another thread /// /// Verifies: Drop with panic in another thread /// Category: drop #[test] fn test_drop_after_panic() { let (announcer, announcement) = Announcement::::new(); let handle = thread::spawn(move || { let _announcer = announcer; panic!("intentional panic"); }); let _ = handle.join(); // Drop happened despite panic assert!(announcement.is_closed()); } /// Test: Verify closed flag atomic ordering /// /// Verifies: closed flag atomic ordering /// Category: drop #[test] fn test_drop_closed_flag_atomic_ordering() { // This is a documentation test - verifying our understanding // The actual ordering is in the implementation let (announcer, announcement) = Announcement::::new(); // Before drop, not closed assert!(!announcement.is_closed()); // Drop sets closed flag with Release ordering drop(announcer); // After drop, closed flag should be visible with Acquire ordering assert!(announcement.is_closed()); }