use super::metrics::{LatencyStats, StorageMetrics};
use parking_lot::Mutex;
use std::thread;
use std::time::Duration;
#[test]
fn test_metrics_new_is_empty() {
let metrics = StorageMetrics::new();
assert_eq!(metrics.ensure_capacity_count(), 0);
assert_eq!(metrics.resize_count(), 0);
assert_eq!(metrics.total_bytes_resized(), 0);
}
#[test]
fn test_metrics_record_ensure_capacity_no_resize() {
let metrics = StorageMetrics::new();
let latency = Duration::from_micros(100);
metrics.record_ensure_capacity(latency, false, 0);
assert_eq!(metrics.ensure_capacity_count(), 1);
assert_eq!(metrics.resize_count(), 0);
assert_eq!(metrics.total_bytes_resized(), 0);
}
#[test]
fn test_metrics_record_ensure_capacity_with_resize() {
let metrics = StorageMetrics::new();
let latency = Duration::from_millis(50);
let bytes_resized = 64 * 1024 * 1024;
metrics.record_ensure_capacity(latency, true, bytes_resized);
assert_eq!(metrics.ensure_capacity_count(), 1);
assert_eq!(metrics.resize_count(), 1);
assert_eq!(metrics.total_bytes_resized(), bytes_resized);
}
#[test]
fn test_metrics_multiple_operations() {
let metrics = StorageMetrics::new();
for i in 0..10 {
let did_resize = i % 3 == 0; let bytes = if did_resize { 1024 } else { 0 };
metrics.record_ensure_capacity(Duration::from_micros(100), did_resize, bytes);
}
assert_eq!(metrics.ensure_capacity_count(), 10);
assert_eq!(metrics.resize_count(), 4); assert_eq!(metrics.total_bytes_resized(), 4 * 1024);
}
#[test]
fn test_metrics_reset() {
let metrics = StorageMetrics::new();
metrics.record_ensure_capacity(Duration::from_micros(100), true, 1024);
metrics.reset();
assert_eq!(metrics.ensure_capacity_count(), 0);
assert_eq!(metrics.resize_count(), 0);
assert_eq!(metrics.total_bytes_resized(), 0);
assert_eq!(metrics.ensure_capacity_latency_stats().count, 0);
}
#[test]
fn test_latency_stats_empty() {
let metrics = StorageMetrics::new();
let stats = metrics.ensure_capacity_latency_stats();
assert_eq!(stats.count, 0);
assert_eq!(stats.min_us, 0);
assert_eq!(stats.max_us, 0);
assert_eq!(stats.p99_us, 0);
}
#[test]
fn test_latency_stats_single_sample() {
let metrics = StorageMetrics::new();
metrics.record_ensure_capacity(Duration::from_micros(500), false, 0);
let stats = metrics.ensure_capacity_latency_stats();
assert_eq!(stats.count, 1);
assert_eq!(stats.min_us, 500);
assert_eq!(stats.max_us, 500);
assert!(stats.p50_us > 0, "P50 should be non-zero");
assert!(stats.p99_us > 0, "P99 should be non-zero");
}
#[test]
fn test_latency_stats_percentiles() {
let metrics = StorageMetrics::new();
for i in 1..=100 {
metrics.record_ensure_capacity(Duration::from_micros(i), false, 0);
}
let stats = metrics.ensure_capacity_latency_stats();
assert_eq!(stats.count, 100);
assert_eq!(stats.min_us, 1);
assert_eq!(stats.max_us, 100);
assert!(
stats.p50_us > 0 && stats.p50_us <= 100,
"p50 should be reasonable, got {}",
stats.p50_us
);
assert!(stats.p95_us >= stats.p50_us, "p95 should be >= p50");
assert!(stats.p99_us >= stats.p95_us, "p99 should be >= p95");
}
#[test]
fn test_latency_stats_p99_exceeds() {
let stats = LatencyStats {
count: 100,
min_us: 10,
max_us: 10000,
mean_us: 500,
p50_us: 100,
p95_us: 1000,
p99_us: 5000, };
assert!(stats.p99_exceeds(Duration::from_millis(1))); assert!(!stats.p99_exceeds(Duration::from_millis(10))); }
#[test]
fn test_metrics_thread_safety() {
let metrics = std::sync::Arc::new(StorageMetrics::new());
let num_threads: u64 = 4;
let ops_per_thread: u64 = 1000;
let handles: Vec<_> = (0..num_threads)
.map(|_| {
let m = metrics.clone();
thread::spawn(move || {
for i in 0..ops_per_thread {
m.record_ensure_capacity(Duration::from_micros(i), i % 10 == 0, 1024);
}
})
})
.collect();
for h in handles {
h.join().expect("Thread panicked");
}
let expected_total = num_threads * ops_per_thread;
assert_eq!(metrics.ensure_capacity_count(), expected_total);
let expected_resizes = num_threads * (ops_per_thread / 10);
assert_eq!(metrics.resize_count(), expected_resizes);
}
#[test]
fn test_timing_guard_records_elapsed() {
use super::metrics::TimingGuard;
let recorded = std::sync::Arc::new(Mutex::new(Duration::ZERO));
let recorded_clone = recorded.clone();
{
let _guard = TimingGuard::new(move |d| {
*recorded_clone.lock() = d;
});
thread::sleep(Duration::from_millis(10));
}
let elapsed = *recorded.lock();
assert!(elapsed >= Duration::from_millis(5)); assert!(elapsed < Duration::from_millis(100)); }
#[test]
fn test_detect_slow_ensure_capacity() {
let metrics = StorageMetrics::new();
for _ in 0..98 {
metrics.record_ensure_capacity(Duration::from_micros(10), false, 0);
}
metrics.record_ensure_capacity(Duration::from_millis(200), true, 1024 * 1024 * 1024);
metrics.record_ensure_capacity(Duration::from_millis(500), true, 2 * 1024 * 1024 * 1024);
let stats = metrics.ensure_capacity_latency_stats();
assert_eq!(stats.count, 100);
assert_eq!(stats.max_us, 500_000);
assert!(
stats.p99_us >= 100_000,
"P99 should capture slow ops: got {} us",
stats.p99_us
);
assert!(stats.p99_exceeds(Duration::from_millis(100)));
}
#[test]
fn test_bytes_resized_tracks_growth() {
let metrics = StorageMetrics::new();
let growth_sizes = [16 * 1024 * 1024u64, 32 * 1024 * 1024, 64 * 1024 * 1024];
for size in growth_sizes {
metrics.record_ensure_capacity(Duration::from_millis(50), true, size);
}
let expected_total: u64 = growth_sizes.iter().sum();
assert_eq!(metrics.total_bytes_resized(), expected_total);
assert_eq!(metrics.resize_count(), 3);
}