335 lines
9.4 KiB
Rust
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);
|
|
});
|
|
}
|