use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Baseline {
pub p99: Duration,
pub tolerance: Duration,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PerfDecision {
Pass {
measured: Duration,
ceiling: Duration,
},
Fail {
measured: Duration,
ceiling: Duration,
excess: Duration,
},
Skip {
reason: &'static str,
},
}
pub fn evaluate_against_baseline(measured: Duration, baseline: Option<Baseline>) -> PerfDecision {
let Some(baseline) = baseline else {
return PerfDecision::Skip {
reason: "no baseline yet — record this run as the first sample",
};
};
let ceiling = baseline.p99 + baseline.tolerance;
if measured <= ceiling {
PerfDecision::Pass { measured, ceiling }
} else {
PerfDecision::Fail {
measured,
ceiling,
excess: measured - ceiling,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn perf_baseline_regression_blocks() {
let baseline = Baseline {
p99: Duration::from_millis(5),
tolerance: Duration::from_millis(1),
};
let decision = evaluate_against_baseline(Duration::from_millis(7), Some(baseline));
match decision {
PerfDecision::Fail {
measured,
ceiling,
excess,
} => {
assert_eq!(measured, Duration::from_millis(7));
assert_eq!(ceiling, Duration::from_millis(6));
assert_eq!(excess, Duration::from_millis(1));
}
other => panic!("expected Fail, got {other:?}"),
}
}
#[test]
fn within_tolerance_passes() {
let baseline = Baseline {
p99: Duration::from_millis(5),
tolerance: Duration::from_millis(1),
};
assert!(matches!(
evaluate_against_baseline(Duration::from_millis(6), Some(baseline)),
PerfDecision::Pass { .. }
));
assert!(matches!(
evaluate_against_baseline(Duration::from_millis(3), Some(baseline)),
PerfDecision::Pass { .. }
));
}
#[test]
fn no_baseline_is_skip() {
let decision = evaluate_against_baseline(Duration::from_millis(42), None);
assert!(matches!(decision, PerfDecision::Skip { .. }));
}
#[test]
fn at_ceiling_passes() {
let baseline = Baseline {
p99: Duration::from_millis(10),
tolerance: Duration::from_millis(2),
};
assert!(matches!(
evaluate_against_baseline(Duration::from_millis(12), Some(baseline)),
PerfDecision::Pass { .. }
));
}
}