//! Tests specifically for close() behavior and mutation testing coverage //! //! These tests target gaps in mutation testing, particularly around the //! explicit close() method. use announcement::{Announcement, ListenError}; use std::sync::Arc; use std::thread; use std::time::Duration; /// Test: Verify close() actually sets the closed flag /// /// Verifies: close() actually sets the closed flag /// Category: close #[test] fn test_close_sets_closed_flag() { let (announcer, announcement) = Announcement::::new(); // Initially not closed assert!(!announcement.is_closed()); // Call close() announcer.close(); // Verify closed flag is set assert!(announcement.is_closed()); } /// Test: Verify close() notifies waiting async listeners /// /// Verifies: close() notifies waiting async listeners /// Category: close #[tokio::test] async fn test_close_notifies_async_listeners() { let (announcer, announcement) = Announcement::::new(); let listener = announcement.listener(); // Spawn task that waits let handle = tokio::spawn(async move { listener.listen().await }); // Give listener time to start waiting tokio::time::sleep(Duration::from_millis(10)).await; // Close the announcer announcer.close(); // Listener should receive Closed error quickly let result = tokio::time::timeout(Duration::from_millis(100), handle).await; assert!(result.is_ok(), "Listener should be notified immediately"); let listen_result = result.unwrap().unwrap(); assert_eq!(listen_result, Err(ListenError::Closed)); } /// Test: Close test: close notifies blocking listeners /// /// Verifies: close notifies blocking listeners /// Category: close #[cfg(feature = "std")] #[test] fn test_close_notifies_blocking_listeners() { let (announcer, announcement) = Announcement::::new(); let listener = announcement.listener(); // Spawn thread that waits let handle = thread::spawn(move || listener.listen_timeout_blocking(Duration::from_millis(100))); // Give listener time to start waiting thread::sleep(Duration::from_millis(10)); // Close the announcer announcer.close(); // Listener should receive Closed error quickly let result = handle.join().unwrap(); assert_eq!(result, Err(ListenError::Closed)); } /// Test: Verify close() notifies multiple waiting listeners /// /// Verifies: close() notifies multiple waiting listeners /// Category: close #[tokio::test] async fn test_close_notifies_all_listeners() { let (announcer, announcement) = Announcement::::new(); // Create multiple listeners let listeners: Vec<_> = (0..10).map(|_| announcement.listener()).collect(); // Spawn tasks for all listeners let mut handles = Vec::new(); for listener in listeners { handles.push(tokio::spawn(async move { listener.listen().await })); } // Give listeners time to start waiting tokio::time::sleep(Duration::from_millis(10)).await; // Close the announcer announcer.close(); // All listeners should receive Closed error for handle in handles { let result = tokio::time::timeout(Duration::from_millis(100), handle).await; assert!(result.is_ok(), "All listeners should be notified"); let listen_result = result.unwrap().unwrap(); assert_eq!(listen_result, Err(ListenError::Closed)); } } /// Test: Verify close() works even with no listeners /// /// Verifies: close() works even with no listeners /// Category: close #[test] fn test_close_with_no_listeners() { let (announcer, announcement) = Announcement::::new(); // Close without creating any listeners announcer.close(); // Verify closed flag is set assert!(announcement.is_closed()); // Create a listener after close let listener = announcement.listener(); assert_eq!(listener.try_listen(), None); assert!(listener.is_closed()); } /// Test: Verify close() prevents future listeners from waiting /// /// Verifies: close() prevents future listeners from waiting /// Category: close #[tokio::test] async fn test_close_prevents_future_waiting() { let (announcer, announcement) = Announcement::::new(); // Close immediately announcer.close(); // Create listener after close let listener = announcement.listener(); // listen() should return immediately with Closed let result = tokio::time::timeout(Duration::from_millis(50), listener.listen()).await; assert!(result.is_ok(), "listen() should not hang after close"); let listen_result = result.unwrap(); assert_eq!(listen_result, Err(ListenError::Closed)); } /// Test: Verify close() interacts correctly with try_listen() /// /// Verifies: close() interacts correctly with try_listen() /// Category: close #[test] fn test_close_try_listen_interaction() { let (announcer, announcement) = Announcement::::new(); let listener = announcement.listener(); // try_listen() returns None before close assert_eq!(listener.try_listen(), None); assert!(!listener.is_closed()); // Close the announcer announcer.close(); // try_listen() still returns None after close assert_eq!(listener.try_listen(), None); // But is_closed() returns true assert!(listener.is_closed()); } /// Test: Close followed by listener checks in rapid succession /// /// Verifies: Close followed by listener checks in rapid succession /// Category: close #[test] fn test_close_rapid_checks() { let (announcer, announcement) = Announcement::::new(); let listener = announcement.listener(); announcer.close(); // Rapidly check closed status for _ in 0..1000 { assert!(listener.is_closed()); assert_eq!(listener.try_listen(), None); } } /// Test: Verify close() with Arc-wrapped value type /// /// Verifies: close() with Arc-wrapped value type /// Category: close #[test] fn test_close_with_arc_type() { let (announcer, announcement) = Announcement::>::new(); let listener = announcement.listener(); announcer.close(); assert!(announcement.is_closed()); assert!(listener.is_closed()); assert_eq!(listener.try_listen(), None); } /// Test: Multiple threads checking closed status after close() /// /// Verifies: Multiple threads checking closed status after close() /// Category: close #[test] fn test_close_concurrent_status_checks() { use std::sync::Barrier; let (announcer, announcement) = Announcement::::new(); let barrier = Arc::new(Barrier::new(11)); // 10 threads + main thread // Create listeners in multiple threads let handles: Vec<_> = (0..10) .map(|_| { let ann = announcement.clone(); let barrier = Arc::clone(&barrier); thread::spawn(move || { let listener = ann.listener(); // Wait for close() to complete via barrier synchronization barrier.wait(); listener.is_closed() }) }) .collect(); // Close the announcer announcer.close(); // Synchronize: ensure close() completes before threads check barrier.wait(); // All threads should see closed = true for handle in handles { let is_closed = handle.join().unwrap(); assert!(is_closed, "All threads should see closed flag"); } }