Files
swaits e8e55b7676 chore: initial release v0.1.0
Runtime-agnostic oneshot broadcast channel for Rust.
2025-10-13 19:36:37 -06:00

335 lines
9.4 KiB
Rust

// Skip all smol tests under Miri - it doesn't support timerfd_create syscall
#![cfg_attr(miri, allow(dead_code))]
use announcement::Announcement;
use std::sync::Arc;
use std::time::Duration;
// =============================================================================
// Smol runtime tests
// =============================================================================
#[test]
#[cfg_attr(
miri,
ignore = "Miri doesn't support timerfd_create syscall used by smol"
)]
/// Test: Runtime test: Smol runtime basic operations
///
/// Verifies: Basic announcement and listening works on Smol runtime
/// Category: runtime
fn test_smol_basic() {
smol::block_on(async {
let (announcer, announcement) = Announcement::new();
let listener = announcement.listener();
smol::spawn(async move {
smol::Timer::after(Duration::from_millis(10)).await;
announcer.announce(42).unwrap();
})
.detach();
let result = listener.listen().await.unwrap();
assert_eq!(result, 42);
});
}
#[test]
#[cfg_attr(
miri,
ignore = "Miri doesn't support timerfd_create syscall used by smol"
)]
/// Test: Runtime test: multiple concurrent listeners
///
/// Verifies: Multiple listeners all receive the same announced value on the runtime
/// Category: runtime
fn test_smol_multiple_listeners() {
smol::block_on(async {
let (announcer, announcement) = Announcement::new();
let l1 = announcement.listener();
let l2 = announcement.listener();
let l3 = announcement.listener();
let h1 = smol::spawn(async move { l1.listen().await.unwrap() });
let h2 = smol::spawn(async move { l2.listen().await.unwrap() });
let h3 = smol::spawn(async move { l3.listen().await.unwrap() });
// Small delay before announcing
smol::Timer::after(Duration::from_millis(10)).await;
announcer.announce(42).unwrap();
assert_eq!(h1.await, 42);
assert_eq!(h2.await, 42);
assert_eq!(h3.await, 42);
});
}
#[test]
#[cfg_attr(
miri,
ignore = "Miri doesn't support timerfd_create syscall used by smol"
)]
/// Test: Runtime test: Smol concurrent announce and listen
///
/// Verifies: Concurrent announce and listen operations work on Smol runtime
/// Category: runtime
fn test_smol_concurrent_announce_and_listen() {
smol::block_on(async {
let (announcer, announcement) = Announcement::new();
// Create many listeners concurrently
let mut handles = Vec::new();
for i in 0..50 {
let listener = announcement.listener();
handles.push(smol::spawn(
async move { (i, listener.listen().await.unwrap()) },
));
}
// Announce value concurrently
smol::spawn(async move {
smol::Timer::after(Duration::from_millis(5)).await;
announcer.announce(999).unwrap();
})
.detach();
// Verify all listeners get the value
for handle in handles {
let (_, value) = handle.await;
assert_eq!(value, 999);
}
});
}
#[test]
#[cfg_attr(
miri,
ignore = "Miri doesn't support timerfd_create syscall used by smol"
)]
/// Test: Runtime test: Smol listen after announce
///
/// Verifies: Listening after announce returns immediately on Smol runtime
/// Category: runtime
fn test_smol_listen_after_announce() {
smol::block_on(async {
let (announcer, announcement) = Announcement::new();
// Announce first
announcer.announce(42).unwrap();
// Create listeners after
let l1 = announcement.listener();
let l2 = announcement.listener();
// Should return immediately
assert_eq!(l1.listen().await.unwrap(), 42);
assert_eq!(l2.listen().await.unwrap(), 42);
});
}
#[test]
#[cfg_attr(
miri,
ignore = "Miri doesn't support timerfd_create syscall used by smol"
)]
/// Test: Runtime test: Smol with Arc-wrapped data
///
/// Verifies: Arc-wrapped data is handled correctly on Smol runtime
/// Category: runtime
fn test_smol_with_arc() {
smol::block_on(async {
#[derive(Clone, Debug, PartialEq)]
struct Data {
value: Vec<u8>,
}
let (announcer, announcement) = Announcement::new();
let data = Arc::new(Data {
value: vec![1, 2, 3, 4, 5],
});
let l1 = announcement.listener();
let l2 = announcement.listener();
announcer.announce(data.clone()).unwrap();
let r1 = l1.listen().await.unwrap();
let r2 = l2.listen().await.unwrap();
assert_eq!(r1, data);
assert_eq!(r2, data);
// Verify Arc refcount
assert_eq!(Arc::strong_count(&r1), 4);
});
}
#[test]
#[cfg_attr(
miri,
ignore = "Miri doesn't support timerfd_create syscall used by smol"
)]
/// Test: Runtime test: Smol race condition protection
///
/// Verifies: Race conditions are properly handled on Smol runtime
/// Category: runtime
fn test_smol_race_condition_protection() {
smol::block_on(async {
// Run multiple times to catch any race conditions
for i in 0..100 {
let (announcer, announcement) = Announcement::new();
let listener = announcement.listener();
let handle = smol::spawn(async move { listener.listen().await.unwrap() });
// Announce immediately (potential race)
announcer.announce(i).unwrap();
assert_eq!(handle.await, i);
}
});
}
#[test]
#[cfg_attr(
miri,
ignore = "Miri doesn't support timerfd_create syscall used by smol"
)]
/// Test: Runtime test: Smol many concurrent tasks
///
/// Verifies: Many concurrent tasks all receive values correctly on Smol runtime
/// Category: runtime
fn test_smol_many_concurrent_tasks() {
smol::block_on(async {
let (announcer, announcement) = Announcement::new();
// Create 100 concurrent listeners
let mut handles = Vec::new();
for _ in 0..100 {
let listener = announcement.listener();
handles.push(smol::spawn(async move { listener.listen().await.unwrap() }));
}
// Wait a bit to ensure all are waiting
smol::Timer::after(Duration::from_millis(10)).await;
// Announce
announcer.announce(12345).unwrap();
// All should receive the same value
for handle in handles {
assert_eq!(handle.await, 12345);
}
});
}
#[test]
#[cfg_attr(
miri,
ignore = "Miri doesn't support timerfd_create syscall used by smol"
)]
/// Test: Runtime test: Smol dropped listener handling
///
/// Verifies: System works correctly when listeners are dropped on Smol runtime
/// Category: runtime
fn test_smol_listener_dropped() {
smol::block_on(async {
let (announcer, announcement) = Announcement::new();
// Create and immediately drop a listener
{
let _listener = announcement.listener();
// Dropped here
}
// Should still work with new listeners
let listener = announcement.listener();
announcer.announce(42).unwrap();
assert_eq!(listener.listen().await.unwrap(), 42);
});
}
#[test]
#[cfg_attr(
miri,
ignore = "Miri doesn't support timerfd_create syscall used by smol"
)]
/// Test: Runtime test: Smol announcement cloning
///
/// Verifies: Cloned announcements work correctly on Smol runtime
/// Category: runtime
fn test_smol_clone_announcement() {
smol::block_on(async {
let (announcer, announcement) = Announcement::new();
let announcement2 = announcement.clone();
let l1 = announcement.listener();
let l2 = announcement2.listener();
announcer.announce(42).unwrap();
assert_eq!(l1.listen().await.unwrap(), 42);
assert_eq!(l2.listen().await.unwrap(), 42);
});
}
#[test]
#[cfg_attr(
miri,
ignore = "Miri doesn't support timerfd_create syscall used by smol"
)]
/// Test: Runtime test: timeout behavior
///
/// Verifies: Runtime timeout mechanisms work correctly with announcements
/// Category: runtime
fn test_smol_timeout_behavior() {
use announcement::ListenError;
smol::block_on(async {
let (_, announcement) = Announcement::<i32>::new();
// Note: Announcer dropped immediately via _ pattern - sets closed flag
let listener = announcement.listener();
// With closed detection, listen() returns Closed immediately
let result = listener.listen().await;
assert_eq!(result, Err(ListenError::Closed));
assert!(announcement.is_closed());
});
}
#[test]
#[cfg_attr(
miri,
ignore = "Miri doesn't support timerfd_create syscall used by smol"
)]
/// Test: Runtime test: Smol complex data types
///
/// Verifies: Complex data types work correctly on Smol runtime
/// Category: runtime
fn test_smol_complex_types() {
smol::block_on(async {
#[derive(Clone, Debug, PartialEq)]
struct ComplexData {
name: String,
values: Vec<i32>,
nested: Option<Box<ComplexData>>,
}
let (announcer, announcement) = Announcement::new();
let listener = announcement.listener();
let data = ComplexData {
name: "test".to_string(),
values: vec![1, 2, 3, 4, 5],
nested: None,
};
announcer.announce(data.clone()).unwrap();
let result = listener.listen().await.unwrap();
assert_eq!(result, data);
});
}