//! Critical high-priority tests identified in quality assessment. //! These tests cover edge cases that could cause memory leaks, races, or UB. use announcement::Announcement; #[cfg(feature = "std")] use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::thread; #[cfg(feature = "std")] use std::time::Duration; /// Test: Arc refcount: stress test /// /// Verifies: Arc refcounting works under stress /// Category: refcount // Verifies no memory leaks under rapid announce/drop cycles #[test] fn test_arc_refcount_stress() { for iteration in 0..10_000 { let (announcer, announcement) = Announcement::new(); let data = Arc::new(vec![0u8; 1024]); let initial_count = Arc::strong_count(&data); let listener = announcement.listener(); // Announce with Arc announcer.announce(data.clone()).unwrap(); // Retrieve value let received = listener.try_listen().unwrap(); // Verify refcount is reasonable // Should be: original + announcement storage + received = 3 assert_eq!( Arc::strong_count(&received), initial_count + 2, "Refcount mismatch at iteration {}", iteration ); // Drop everything and verify no leak drop(received); drop(listener); drop(announcement); // After all drops, only original reference should remain assert_eq!( Arc::strong_count(&data), initial_count, "Memory leak detected at iteration {}", iteration ); } } /// Test: Arc refcount: multiple listener refs /// /// Verifies: Arc refcounts correct with multiple listeners /// Category: refcount #[test] fn test_arc_refcount_multiple_listeners() { let (announcer, announcement) = Announcement::new(); let data = Arc::new(vec![0u8; 1024]); let initial_count = Arc::strong_count(&data); // Create 100 listeners let listeners: Vec<_> = (0..100).map(|_| announcement.listener()).collect(); // Announce announcer.announce(data.clone()).unwrap(); // All listeners retrieve let values: Vec<_> = listeners.iter().map(|l| l.try_listen().unwrap()).collect(); // Refcount should be: original + storage + 100 listeners = 102 assert_eq!(Arc::strong_count(&values[0]), initial_count + 101); // Drop listeners one by one and verify refcount decreases drop(listeners); assert_eq!(Arc::strong_count(&values[0]), initial_count + 101); drop(values); drop(announcement); // Only original should remain assert_eq!(Arc::strong_count(&data), initial_count); } /// Test: Integration test: close race with active listen /// /// Verifies: Verifies close race with active listen /// Category: integration #[cfg(feature = "std")] #[tokio::test] async fn test_close_race_with_active_listen() { use announcement::ListenError; for iteration in 0..1_000 { let (announcer, announcement) = Announcement::::new(); let listener = announcement.listener(); // Spawn task that will listen let listen_task = tokio::spawn(async move { listener.listen().await }); // Give listener a moment to start waiting tokio::time::sleep(Duration::from_micros(1)).await; // Close while listener is actively waiting announcer.close(); // Listener should wake up with Closed error let result = listen_task.await.unwrap(); assert_eq!( result, Err(ListenError::Closed), "Expected Closed error at iteration {}", iteration ); } } /// Test: Integration test: close during listener setup /// /// Verifies: Verifies close during listener setup /// Category: integration #[cfg(feature = "std")] #[tokio::test] async fn test_close_during_listener_setup() { use announcement::ListenError; for _ in 0..1_000 { let (announcer, announcement) = Announcement::::new(); // Spawn multiple listeners that will race with close let mut tasks = vec![]; for _ in 0..10 { let listener = announcement.listener(); tasks.push(tokio::spawn(async move { listener.listen().await })); } // Close immediately announcer.close(); // All listeners should get Closed error for task in tasks { let result = task.await.unwrap(); assert_eq!(result, Err(ListenError::Closed)); } } } /// Test: Integration test: weak memory ordering visibility /// /// Verifies: Verifies weak memory ordering visibility /// Category: integration // Verifies that happens-before relationships work correctly #[cfg(feature = "std")] #[test] fn test_weak_memory_ordering_visibility() { let shared_data = Arc::new(AtomicUsize::new(0)); for iteration in 0..1_000 { let (announcer, announcement) = Announcement::new(); let data1 = shared_data.clone(); let data2 = shared_data.clone(); let listener = announcement.listener(); let writer = thread::spawn(move || { // Write to shared data data1.store(42, Ordering::Relaxed); // Announce (provides synchronization) announcer.announce(()).unwrap(); }); let reader = thread::spawn(move || { // Wait for announcement listener.listen_blocking().unwrap(); // Due to happens-before, must see the write data2.load(Ordering::Relaxed) }); writer.join().unwrap(); let seen_value = reader.join().unwrap(); assert_eq!( seen_value, 42, "Memory ordering violation at iteration {}", iteration ); // Reset for next iteration shared_data.store(0, Ordering::Relaxed); } } /// Test: Integration test: weak memory ordering multiple observers /// /// Verifies: Verifies weak memory ordering multiple observers /// Category: integration #[cfg(feature = "std")] #[test] fn test_weak_memory_ordering_multiple_observers() { for iteration in 0..1_000 { let shared_data = Arc::new(AtomicUsize::new(0)); let (announcer, announcement) = Announcement::new(); // Multiple reader threads let mut handles = vec![]; for i in 0..8 { let data = shared_data.clone(); let listener = announcement.listener(); handles.push(thread::spawn(move || { listener.listen_blocking().unwrap(); (i, data.load(Ordering::Relaxed)) })); } // Writer thread let data_writer = shared_data.clone(); let writer = thread::spawn(move || { data_writer.store(99, Ordering::Relaxed); announcer.announce(()).unwrap(); }); writer.join().unwrap(); // All readers must see the write for handle in handles { let (thread_id, value) = handle.join().unwrap(); assert_eq!( value, 99, "Thread {} didn't see write at iteration {}", thread_id, iteration ); } } } /// Test: Integration test: arc drop race with announcement /// /// Verifies: Verifies arc drop race with announcement /// Category: integration #[test] fn test_arc_drop_race_with_announcement() { for _ in 0..10_000 { let (announcer, announcement) = Announcement::new(); let data = Arc::new(vec![0u8; 1024]); let data_clone = data.clone(); // Thread that rapidly creates and drops listeners let ann1 = announcement.clone(); let dropper = thread::spawn(move || { for _ in 0..10 { let listener = ann1.listener(); let _ = listener.try_listen(); drop(listener); } }); // Thread that announces let announcer_thread = thread::spawn(move || { thread::yield_now(); announcer.announce(data_clone).unwrap(); }); dropper.join().unwrap(); announcer_thread.join().unwrap(); // Verify final refcount let listener = announcement.listener(); let received = listener.try_listen().unwrap(); // Should be: original + storage + received assert!(Arc::strong_count(&received) >= 2); } } /// Test: Integration test: closed flag visibility /// /// Verifies: Verifies closed flag visibility /// Category: integration #[test] fn test_closed_flag_visibility() { for iteration in 0..10_000 { let (announcer, announcement) = Announcement::::new(); // Multiple threads checking is_closed let mut handles = vec![]; for _ in 0..4 { let ann = announcement.clone(); handles.push(thread::spawn(move || { // Spin until closed while !ann.is_closed() { thread::yield_now(); } ann.is_closed() // Must be true })); } // Close after threads start thread::yield_now(); drop(announcer); // All threads must eventually see closed for handle in handles { let result = handle.join().unwrap(); assert!( result, "Failed to see closed flag at iteration {}", iteration ); } } }