use std::{
sync::{Arc, atomic::AtomicU64},
thread,
time::Duration,
};
use crate::{Cell, Gettable, Mutable, Watchable};
#[test]
fn test_metrics_disabled_by_default() {
let cell = Cell::new(0);
assert!(cell.metrics().is_none());
}
#[test]
fn test_metrics_enabled_with_constructor() {
let cell = Cell::with_metrics(0);
assert!(cell.metrics().is_some());
}
#[test]
fn test_notify_count() {
let cell = Cell::with_metrics(0);
let metrics = cell.metrics().unwrap();
assert_eq!(metrics.notify_count(), 0);
cell.set(1);
assert_eq!(metrics.notify_count(), 1);
cell.set(2);
cell.set(3);
assert_eq!(metrics.notify_count(), 3);
}
#[test]
fn test_subscriber_count() {
let cell = Cell::with_metrics(0);
let metrics = cell.metrics().unwrap();
assert_eq!(metrics.subscriber_count(), 0);
let guard1 = cell.subscribe(|_| {});
assert_eq!(metrics.subscriber_count(), 1);
let guard2 = cell.subscribe(|_| {});
assert_eq!(metrics.subscriber_count(), 2);
drop(guard1);
assert_eq!(metrics.subscriber_count(), 1);
drop(guard2);
assert_eq!(metrics.subscriber_count(), 0);
}
#[test]
fn test_notify_timing() {
let cell = Cell::with_metrics(0);
let metrics = cell.metrics().unwrap();
let _guard = cell.subscribe(|_| {
thread::sleep(Duration::from_millis(1));
});
cell.set(1);
assert!(metrics.total_notify_time_ns() > 0);
assert!(metrics.avg_notify_time_ns() > 0);
}
#[test]
fn test_slowest_subscriber_tracking() {
let cell = Cell::with_metrics(0);
let metrics = cell.metrics().unwrap();
let _guard = cell.subscribe(|_| {
thread::sleep(Duration::from_millis(5));
});
cell.set(1);
let slowest = metrics.slowest_subscriber_ns();
assert!(
slowest >= 4_000_000,
"Expected at least 4ms, got {}ns",
slowest
);
}
#[test]
fn test_reset_timing() {
let cell = Cell::with_metrics(0);
let metrics = cell.metrics().unwrap();
let _guard = cell.subscribe(|_| {});
cell.set(1);
cell.set(2);
assert!(metrics.notify_count() > 0);
assert!(metrics.total_notify_time_ns() > 0);
metrics.reset_timing();
assert_eq!(metrics.notify_count(), 0);
assert_eq!(metrics.total_notify_time_ns(), 0);
assert_eq!(metrics.slowest_subscriber_ns(), 0);
assert_eq!(metrics.subscriber_count(), 1);
}
#[test]
fn test_metrics_with_multiple_subscribers() {
let cell = Cell::with_metrics(0);
let metrics = cell.metrics().unwrap();
let _guard1 = cell.subscribe(|_| {
thread::sleep(Duration::from_micros(100));
});
let _guard2 = cell.subscribe(|_| {
thread::sleep(Duration::from_millis(2));
});
let _guard3 = cell.subscribe(|_| {
thread::sleep(Duration::from_micros(500));
});
cell.set(1);
let slowest = metrics.slowest_subscriber_ns();
assert!(
slowest >= 1_500_000,
"Expected at least 1.5ms from slowest subscriber, got {}ns",
slowest
);
}
#[test]
fn test_last_notify_time() {
let cell = Cell::with_metrics(0);
let metrics = cell.metrics().unwrap();
assert_eq!(metrics.last_notify_time_ns(), 0);
let _guard = cell.subscribe(|_| {
thread::sleep(Duration::from_millis(5));
});
cell.set(1);
let last = metrics.last_notify_time_ns();
assert!(last >= 4_000_000, "Expected at least 4ms, got {}ns", last);
}
#[test]
fn test_is_backed_up_false_without_metrics() {
let cell = Cell::new(0);
assert!(!cell.is_backed_up());
}
#[test]
fn test_is_backed_up_false_initially() {
let cell = Cell::with_metrics(0);
assert!(!cell.is_backed_up());
}
#[test]
fn test_is_backed_up_with_slow_subscriber() {
let cell = Cell::with_metrics(0);
let _guard = cell.subscribe(|_| {
thread::sleep(Duration::from_millis(5));
});
cell.set(1);
assert!(cell.is_backed_up());
}
#[test]
fn test_try_set_succeeds_when_not_backed_up() {
let cell = Cell::with_metrics(0);
assert!(cell.try_set(1).is_ok());
assert_eq!(cell.get(), 1);
}
#[test]
fn test_try_set_fails_when_backed_up() {
let cell = Cell::with_metrics(0);
let _guard = cell.subscribe(|_| {
thread::sleep(Duration::from_millis(5));
});
assert!(cell.try_set(1).is_ok());
let result = cell.try_set(2);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), 2);
assert_eq!(cell.get(), 1);
}
#[test]
fn test_slow_subscriber_callback() {
use std::sync::atomic::{AtomicUsize, Ordering};
let cell = Cell::with_metrics(0);
let alert_count = Arc::new(AtomicUsize::new(0));
let last_duration = Arc::new(AtomicU64::new(0));
let ac = alert_count.clone();
let ld = last_duration.clone();
cell.on_slow_subscriber(Duration::from_millis(2), move |alert| {
ac.fetch_add(1, Ordering::SeqCst);
ld.store(alert.duration_ns, Ordering::SeqCst);
});
let _guard = cell.subscribe(|_| {
thread::sleep(Duration::from_millis(5));
});
cell.set(1);
assert_eq!(alert_count.load(Ordering::SeqCst), 1);
assert!(last_duration.load(Ordering::SeqCst) >= 4_000_000); }
#[test]
fn test_slow_subscriber_callback_not_triggered_for_fast() {
use std::sync::atomic::{AtomicUsize, Ordering};
let cell = Cell::with_metrics(0);
let alert_count = Arc::new(AtomicUsize::new(0));
let ac = alert_count.clone();
cell.on_slow_subscriber(Duration::from_millis(100), move |_alert| {
ac.fetch_add(1, Ordering::SeqCst);
});
let _guard = cell.subscribe(|_| {
});
cell.set(1);
cell.set(2);
cell.set(3);
assert_eq!(alert_count.load(Ordering::SeqCst), 0);
}