use {
super::super::{
timer::{TimerEntry, TimerWork},
*,
},
std::{
sync::{
Arc,
atomic::{AtomicBool, Ordering},
},
time::{Duration, Instant},
},
};
#[test]
fn test_timer_id_unique() {
let id1 = TimerId::new();
let id2 = TimerId::new();
let id3 = TimerId::new();
assert_ne!(id1, id2);
assert_ne!(id2, id3);
assert!(id2.as_u64() > id1.as_u64());
assert!(id3.as_u64() > id2.as_u64());
}
#[test]
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_timer_id_display() {
let id = TimerId::from_raw(42);
assert_eq!(format!("{id}"), "Timer(42)");
}
#[test]
fn test_timer_id_from_raw() {
let id = TimerId::from_raw(123);
assert_eq!(id.as_u64(), 123);
}
#[test]
fn test_timer_id_default() {
let id1 = TimerId::default();
let id2 = TimerId::default();
assert_ne!(id1, id2); }
#[test]
fn test_timer_id_ordering() {
let id1 = TimerId::from_raw(10);
let id2 = TimerId::from_raw(20);
let id3 = TimerId::from_raw(10);
assert!(id1 < id2);
assert!(id2 > id1);
assert_eq!(id1, id3);
}
#[test]
fn test_timer_config_default() {
let config = TimerConfig::default();
assert_eq!(config.delay, Duration::ZERO);
assert!(config.interval.is_none());
assert_eq!(config.priority, Priority::NORMAL);
}
#[test]
fn test_timer_config_periodic() {
let config = TimerConfig {
delay: Duration::from_millis(100),
interval: Some(Duration::from_millis(50)),
priority: Priority::HIGH,
};
assert_eq!(config.delay, Duration::from_millis(100));
assert_eq!(config.interval, Some(Duration::from_millis(50)));
assert_eq!(config.priority, Priority::HIGH);
}
#[test]
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_timer_handle_debug() {
let handle = TimerHandle::failed(TimerId::from_raw(99));
let debug = format!("{handle:?}");
assert!(debug.contains("TimerHandle"));
assert!(debug.contains("99"));
assert!(debug.contains("failed: true"));
}
#[test]
fn test_timer_handle_failed() {
let handle = TimerHandle::failed(TimerId::from_raw(1));
assert!(handle.is_failed());
assert_eq!(handle.id().as_u64(), 1);
}
#[test]
fn test_timer_handle_detach() {
let handle = TimerHandle::failed(TimerId::from_raw(42));
let id = handle.detach();
assert_eq!(id.as_u64(), 42);
}
#[test]
fn test_timer_entry_cancel() {
let entry = TimerEntry {
id: TimerId::from_raw(1),
deadline: Instant::now(),
interval: None,
priority: Priority::NORMAL,
work: TimerWork::OneShot(Some(Box::new(|| {}))),
cancelled: AtomicBool::new(false),
};
assert!(!entry.is_cancelled());
entry.cancel();
assert!(entry.is_cancelled());
}
#[test]
fn test_timer_entry_interval() {
let one_shot = TimerEntry {
id: TimerId::from_raw(1),
deadline: Instant::now(),
interval: None,
priority: Priority::NORMAL,
work: TimerWork::OneShot(Some(Box::new(|| {}))),
cancelled: AtomicBool::new(false),
};
let periodic = TimerEntry {
id: TimerId::from_raw(2),
deadline: Instant::now(),
interval: Some(Duration::from_millis(100)),
priority: Priority::NORMAL,
work: TimerWork::Periodic(Arc::new(|| {})),
cancelled: AtomicBool::new(false),
};
assert!(one_shot.interval.is_none());
assert!(periodic.interval.is_some());
}
#[test]
fn test_timer_work_debug() {
let one_shot = TimerWork::OneShot(Some(Box::new(|| {})));
let periodic = TimerWork::Periodic(Arc::new(|| {}));
assert_eq!(format!("{one_shot:?}"), "OneShot(...)");
assert_eq!(format!("{periodic:?}"), "Periodic(...)");
}
#[test]
fn test_timer_wheel_schedule_and_tick() {
use std::sync::atomic::AtomicUsize;
let wheel = Arc::new(TimerWheel::new());
let counter = Arc::new(AtomicUsize::new(0));
let counter_clone = counter.clone();
let handle = wheel.schedule_oneshot(Duration::ZERO, Priority::NORMAL, move || {
counter_clone.fetch_add(1, Ordering::SeqCst);
});
assert!(!handle.is_failed());
assert_eq!(wheel.pending_count(), 1);
let mut tasks = wheel.tick(Instant::now() + Duration::from_millis(1));
assert_eq!(tasks.len(), 1);
let _ = tasks[0].execute();
assert_eq!(counter.load(Ordering::SeqCst), 1);
assert_eq!(wheel.pending_count(), 0);
}
#[test]
fn test_timer_wheel_periodic_rescheduling() {
use std::sync::atomic::AtomicUsize;
let wheel = Arc::new(TimerWheel::new());
let counter = Arc::new(AtomicUsize::new(0));
let counter_clone = counter.clone();
let handle = wheel.schedule_periodic(Duration::ZERO, Priority::NORMAL, move || {
counter_clone.fetch_add(1, Ordering::SeqCst);
});
assert!(!handle.is_failed());
let _ = handle.detach();
let mut tasks = wheel.tick(Instant::now());
assert_eq!(tasks.len(), 1);
let _ = tasks[0].execute();
assert_eq!(counter.load(Ordering::SeqCst), 1);
assert_eq!(wheel.pending_count(), 1);
let mut tasks = wheel.tick(Instant::now() + Duration::from_millis(1));
assert_eq!(tasks.len(), 1);
let _ = tasks[0].execute();
assert_eq!(counter.load(Ordering::SeqCst), 2);
assert_eq!(wheel.pending_count(), 1);
}
#[test]
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_timer_handle_drop_cancels() {
use std::sync::atomic::AtomicUsize;
let wheel = Arc::new(TimerWheel::new());
let counter = Arc::new(AtomicUsize::new(0));
let counter_clone = counter.clone();
{
let _handle = wheel.schedule_oneshot(Duration::from_secs(1), Priority::NORMAL, move || {
counter_clone.fetch_add(1, Ordering::SeqCst);
});
assert_eq!(wheel.pending_count(), 1);
}
let tasks = wheel.tick(Instant::now() + Duration::from_secs(2));
assert!(tasks.is_empty());
assert_eq!(counter.load(Ordering::SeqCst), 0);
}
#[test]
fn test_timer_wheel_max_timers_enforced() {
let wheel = Arc::new(TimerWheel::with_max_timers(3));
let h1 = wheel.schedule_oneshot(Duration::from_secs(1), Priority::NORMAL, || {});
let h2 = wheel.schedule_oneshot(Duration::from_secs(1), Priority::NORMAL, || {});
let h3 = wheel.schedule_oneshot(Duration::from_secs(1), Priority::NORMAL, || {});
assert!(!h1.is_failed());
assert!(!h2.is_failed());
assert!(!h3.is_failed());
assert_eq!(wheel.pending_count(), 3);
let h4 = wheel.schedule_oneshot(Duration::from_secs(1), Priority::NORMAL, || {});
assert!(h4.is_failed());
assert_eq!(wheel.dropped_count(), 1);
assert_eq!(wheel.pending_count(), 3);
}
#[test]
fn test_timer_wheel_cancel() {
let wheel = Arc::new(TimerWheel::new());
let handle = wheel.schedule_oneshot(Duration::from_secs(1), Priority::NORMAL, || {});
assert_eq!(wheel.pending_count(), 1);
let cancelled = wheel.cancel(handle.id());
assert!(cancelled);
assert_eq!(wheel.pending_count(), 1);
let tasks = wheel.tick(Instant::now() + Duration::from_secs(2));
assert!(tasks.is_empty());
assert_eq!(wheel.pending_count(), 0);
}
#[test]
fn test_timer_wheel_cancel_idempotent() {
let wheel = Arc::new(TimerWheel::new());
let handle = wheel.schedule_oneshot(Duration::from_secs(1), Priority::NORMAL, || {});
assert!(wheel.cancel(handle.id()));
assert!(!wheel.cancel(handle.id()));
}
#[test]
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_timer_wheel_thread_safety() {
use std::{sync::atomic::AtomicUsize, thread};
let wheel = Arc::new(TimerWheel::new());
let counter = Arc::new(AtomicUsize::new(0));
let mut handles = Vec::new();
for _ in 0..4 {
let wheel = Arc::clone(&wheel);
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
for _ in 0..10 {
let counter = Arc::clone(&counter);
let _ = wheel.schedule_oneshot(Duration::ZERO, Priority::NORMAL, move || {
counter.fetch_add(1, Ordering::SeqCst);
});
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
let pending = wheel.pending_count();
assert!(pending <= 40, "pending count should be <= 40, got {pending}");
let mut tasks = wheel.tick(Instant::now() + Duration::from_secs(1));
for task in &mut tasks {
let _ = task.execute();
}
assert_eq!(counter.load(Ordering::SeqCst), tasks.len());
}
#[test]
fn test_timer_wheel_empty_tick() {
let wheel = Arc::new(TimerWheel::new());
let tasks = wheel.tick(Instant::now());
assert!(tasks.is_empty());
assert_eq!(wheel.pending_count(), 0);
}
#[test]
fn test_timer_wheel_is_pending() {
let wheel = Arc::new(TimerWheel::new());
let handle = wheel.schedule_oneshot(Duration::from_secs(10), Priority::NORMAL, || {});
assert!(wheel.is_pending(handle.id()));
wheel.cancel(handle.id());
assert!(!wheel.is_pending(handle.id()));
}
#[test]
fn test_timer_wheel_is_pending_nonexistent() {
let wheel = Arc::new(TimerWheel::new());
assert!(!wheel.is_pending(TimerId::from_raw(99999)));
}
#[test]
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_timer_wheel_debug() {
let wheel = Arc::new(TimerWheel::new());
let debug = format!("{wheel:?}");
assert!(debug.contains("TimerWheel"));
assert!(debug.contains("pending"));
assert!(debug.contains("max_timers"));
}
#[test]
fn test_timer_wheel_default() {
let wheel = TimerWheel::default();
assert_eq!(wheel.pending_count(), 0);
}
#[test]
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_timer_entry_debug() {
let entry = TimerEntry {
id: TimerId::from_raw(42),
deadline: Instant::now(),
interval: Some(Duration::from_millis(100)),
priority: Priority::HIGH,
work: TimerWork::Periodic(Arc::new(|| {})),
cancelled: AtomicBool::new(false),
};
let debug = format!("{entry:?}");
assert!(debug.contains("TimerEntry"));
assert!(debug.contains("42"));
}
#[test]
fn test_timer_handle_drop_with_dead_wheel() {
let id;
{
let wheel = Arc::new(TimerWheel::new());
let handle = wheel.schedule_oneshot(Duration::from_mins(1), Priority::NORMAL, || {});
id = handle.id();
drop(wheel);
}
let _ = id; }
#[test]
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_timer_not_expired_on_tick() {
let wheel = Arc::new(TimerWheel::new());
let counter = Arc::new(std::sync::atomic::AtomicUsize::new(0));
let counter_clone = counter.clone();
let handle = wheel.schedule_oneshot(Duration::from_mins(1), Priority::NORMAL, move || {
counter_clone.fetch_add(1, Ordering::SeqCst);
});
let _ = handle.detach();
let tasks = wheel.tick(Instant::now());
assert!(tasks.is_empty());
assert_eq!(counter.load(Ordering::SeqCst), 0);
assert_eq!(wheel.pending_count(), 1);
}
#[test]
fn test_timer_config_clone() {
let config = TimerConfig {
delay: Duration::from_millis(50),
interval: Some(Duration::from_millis(100)),
priority: Priority::HIGH,
};
#[allow(clippy::redundant_clone)]
let cloned = config.clone();
assert_eq!(cloned.delay, Duration::from_millis(50));
assert_eq!(cloned.interval, Some(Duration::from_millis(100)));
assert_eq!(cloned.priority, Priority::HIGH);
}
#[test]
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_timer_config_debug() {
let config = TimerConfig::default();
let debug = format!("{config:?}");
assert!(debug.contains("TimerConfig"));
assert!(debug.contains("delay"));
assert!(debug.contains("interval"));
}
#[test]
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_timer_handle_debug_with_alive_wheel() {
let wheel = Arc::new(TimerWheel::new());
let handle = wheel.schedule_oneshot(Duration::from_secs(10), Priority::NORMAL, || {});
let debug = format!("{handle:?}");
assert!(debug.contains("TimerHandle"));
assert!(debug.contains("wheel_alive: true"));
assert!(debug.contains("failed: false"));
}
#[test]
fn test_timer_tick_oneshot_already_taken() {
let wheel = Arc::new(TimerWheel::new());
let handle = wheel.schedule_oneshot(Duration::ZERO, Priority::NORMAL, || {});
let _ = handle.detach();
let tasks = wheel.tick(Instant::now() + Duration::from_millis(1));
assert_eq!(tasks.len(), 1);
assert_eq!(wheel.pending_count(), 0);
}
#[test]
fn test_timer_periodic_with_nonzero_interval() {
let wheel = Arc::new(TimerWheel::new());
let counter = Arc::new(std::sync::atomic::AtomicUsize::new(0));
let counter_clone = counter.clone();
let handle = wheel.schedule_periodic(Duration::from_millis(100), Priority::HIGH, move || {
counter_clone.fetch_add(1, Ordering::SeqCst);
});
let _ = handle.detach();
let mut tasks = wheel.tick(Instant::now() + Duration::from_millis(200));
assert_eq!(tasks.len(), 1);
let _ = tasks[0].execute();
assert_eq!(counter.load(Ordering::SeqCst), 1);
assert_eq!(wheel.pending_count(), 1);
}
#[test]
fn test_timer_max_timers_exceeded_periodic() {
let wheel = Arc::new(TimerWheel::with_max_timers(1));
let _h1 = wheel.schedule_oneshot(Duration::from_secs(1), Priority::NORMAL, || {});
let h2 = wheel.schedule_periodic(Duration::from_secs(1), Priority::NORMAL, || {});
assert!(h2.is_failed());
assert_eq!(wheel.dropped_count(), 1);
}
#[test]
fn test_timer_cancel_nonexistent() {
let wheel = Arc::new(TimerWheel::new());
assert!(!wheel.cancel(TimerId::from_raw(99999)));
}
#[test]
fn test_timer_wheel_capacity_overflow_at_one() {
let wheel = Arc::new(TimerWheel::with_max_timers(1));
let h1 = wheel.schedule_oneshot(Duration::from_secs(10), Priority::NORMAL, || {});
assert!(!h1.is_failed());
assert_eq!(wheel.pending_count(), 1);
assert_eq!(wheel.dropped_count(), 0);
let h2 = wheel.schedule_oneshot(Duration::from_secs(10), Priority::NORMAL, || {});
assert!(h2.is_failed());
assert_eq!(wheel.dropped_count(), 1);
assert_eq!(wheel.pending_count(), 1); }
#[test]
fn test_timer_cancel_already_cancelled_returns_false() {
let wheel = Arc::new(TimerWheel::new());
let handle = wheel.schedule_oneshot(Duration::from_secs(10), Priority::NORMAL, || {});
let id = handle.id();
let first = wheel.cancel(id);
assert!(first, "first cancel should succeed");
let second = wheel.cancel(id);
assert!(!second, "second cancel should return false (already cancelled)");
}