mod counter;
mod cpu;
mod keyed;
pub use counter::{CounterDelta, CounterTracker, CounterWidth};
pub use cpu::{CpuTimeTotals, ProcessCpuTracker, SystemCpuTracker};
pub use keyed::{
DEFAULT_MAX_TRACKED, DeltaTracker, KeyedProcessCpuTrackers, KeyedRateTrackers, KeyedTrackers,
};
#[cfg(test)]
mod tests {
use core::time::Duration;
use std::time::Instant;
use super::*;
use crate::model::{CpuNormalization, MetricState, UnavailableReason};
use crate::units::Percent;
#[test]
fn a_full_sampling_cycle_warms_up_then_measures() {
let t0 = Instant::now();
let t1 = t0 + Duration::from_millis(1_400);
let mut cpu = SystemCpuTracker::new();
let mut rx: KeyedRateTrackers<&'static str> = KeyedRateTrackers::new(CounterWidth::Bits64);
let mut process = ProcessCpuTracker::new();
assert!(
cpu.observe(CpuTimeTotals::new(Duration::ZERO, Duration::ZERO), t0)
.is_warming_up()
);
assert!(rx.observe("eth0", 0, t0).is_warming_up());
assert!(process.observe(Duration::ZERO, t0).is_warming_up());
let cpu_state = cpu.observe(
CpuTimeTotals::new(Duration::from_millis(1_400), Duration::from_millis(9_800)),
t1,
);
let rx_state = rx.observe("eth0", 14_000, t1);
let process_state =
process.observe_normalized(Duration::from_millis(700), t1, CpuNormalization::Core, 8);
assert!(
(cpu_state
.fresh()
.copied()
.map(Percent::value)
.expect("measured")
- 12.5)
.abs()
< f32::EPSILON
);
let rx_per_second = rx_state
.fresh()
.map(|rate| rate.per_second())
.expect("measured");
assert!(
(rx_per_second - 10_000.0).abs() < 1e-6,
"the rate must use the real 1.4 s interval, not an assumed second, got {rx_per_second}"
);
assert!(
(process_state
.fresh()
.copied()
.map(Percent::value)
.expect("measured")
- 50.0)
.abs()
< f32::EPSILON
);
}
#[test]
fn no_unavailable_state_in_the_engine_exposes_a_value() {
let t0 = Instant::now();
let mut counter = CounterTracker::new(CounterWidth::Unknown);
counter.rate(1_000_000, t0);
let reset = counter.rate(1, t0 + Duration::from_secs(1));
let mut set: KeyedRateTrackers<&'static str> =
KeyedRateTrackers::new(CounterWidth::Unknown).with_max_tracked(0);
let skipped = set.observe("eth0", 1, t0);
let mut process = ProcessCpuTracker::new();
process.observe(Duration::from_secs(1), t0);
let denied = process.observe_normalized(
Duration::from_secs(2),
t0 + Duration::from_secs(1),
CpuNormalization::Machine,
0,
);
assert_eq!(reset.fresh(), None);
assert_eq!(reset.displayable(), None);
assert_eq!(skipped.fresh(), None);
assert_eq!(denied.fresh(), None);
for placeholder in [
reset.placeholder(),
skipped.placeholder(),
denied.placeholder(),
] {
assert!(
placeholder.is_some(),
"every unavailable state must explain itself"
);
}
}
#[test]
fn every_reason_the_engine_publishes_has_a_message() {
for reason in [
UnavailableReason::CounterReset,
UnavailableReason::DeviceDisappeared,
UnavailableReason::SkippedUnderLoad,
UnavailableReason::ReadFailed,
] {
let state: MetricState<u64> = MetricState::TemporarilyUnavailable(reason);
assert_eq!(state.placeholder(), Some(reason.message()));
}
}
}