koala_core/invariant/
perf.rs1use std::time::Duration;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct Baseline {
13 pub p99: Duration,
14 pub tolerance: Duration,
16}
17
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub enum PerfDecision {
20 Pass {
21 measured: Duration,
22 ceiling: Duration,
23 },
24 Fail {
25 measured: Duration,
26 ceiling: Duration,
27 excess: Duration,
28 },
29 Skip {
30 reason: &'static str,
31 },
32}
33
34pub fn evaluate_against_baseline(measured: Duration, baseline: Option<Baseline>) -> PerfDecision {
35 let Some(baseline) = baseline else {
36 return PerfDecision::Skip {
37 reason: "no baseline yet — record this run as the first sample",
38 };
39 };
40 let ceiling = baseline.p99 + baseline.tolerance;
41 if measured <= ceiling {
42 PerfDecision::Pass { measured, ceiling }
43 } else {
44 PerfDecision::Fail {
45 measured,
46 ceiling,
47 excess: measured - ceiling,
48 }
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55
56 #[test]
57 fn perf_baseline_regression_blocks() {
58 let baseline = Baseline {
60 p99: Duration::from_millis(5),
61 tolerance: Duration::from_millis(1),
62 };
63 let decision = evaluate_against_baseline(Duration::from_millis(7), Some(baseline));
65 match decision {
66 PerfDecision::Fail {
67 measured,
68 ceiling,
69 excess,
70 } => {
71 assert_eq!(measured, Duration::from_millis(7));
72 assert_eq!(ceiling, Duration::from_millis(6));
73 assert_eq!(excess, Duration::from_millis(1));
74 }
75 other => panic!("expected Fail, got {other:?}"),
76 }
77 }
78
79 #[test]
80 fn within_tolerance_passes() {
81 let baseline = Baseline {
82 p99: Duration::from_millis(5),
83 tolerance: Duration::from_millis(1),
84 };
85 assert!(matches!(
86 evaluate_against_baseline(Duration::from_millis(6), Some(baseline)),
87 PerfDecision::Pass { .. }
88 ));
89 assert!(matches!(
90 evaluate_against_baseline(Duration::from_millis(3), Some(baseline)),
91 PerfDecision::Pass { .. }
92 ));
93 }
94
95 #[test]
96 fn no_baseline_is_skip() {
97 let decision = evaluate_against_baseline(Duration::from_millis(42), None);
98 assert!(matches!(decision, PerfDecision::Skip { .. }));
99 }
100
101 #[test]
102 fn at_ceiling_passes() {
103 let baseline = Baseline {
104 p99: Duration::from_millis(10),
105 tolerance: Duration::from_millis(2),
106 };
107 assert!(matches!(
109 evaluate_against_baseline(Duration::from_millis(12), Some(baseline)),
110 PerfDecision::Pass { .. }
111 ));
112 }
113}