use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VersionComparison {
pub v1: u32,
pub v2: u32,
pub metric_diffs: HashMap<String, f64>,
pub v2_is_better: bool,
pub summary: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MetricRequirement {
pub name: String,
pub comparison: Comparison,
pub threshold: f64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Comparison {
Gt,
Gte,
Lt,
Lte,
Eq,
}
impl Comparison {
pub fn check(&self, value: f64, threshold: f64) -> bool {
match self {
Comparison::Gt => value > threshold,
Comparison::Gte => value >= threshold,
Comparison::Lt => value < threshold,
Comparison::Lte => value <= threshold,
Comparison::Eq => (value - threshold).abs() < f64::EPSILON,
}
}
pub fn as_str(&self) -> &'static str {
match self {
Comparison::Gt => ">",
Comparison::Gte => ">=",
Comparison::Lt => "<",
Comparison::Lte => "<=",
Comparison::Eq => "==",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_comparison_gt() {
assert!(Comparison::Gt.check(0.96, 0.95));
assert!(!Comparison::Gt.check(0.95, 0.95));
}
#[test]
fn test_comparison_gte() {
assert!(Comparison::Gte.check(0.95, 0.95));
assert!(Comparison::Gte.check(0.96, 0.95));
}
#[test]
fn test_comparison_lt() {
assert!(Comparison::Lt.check(0.5, 1.0));
assert!(!Comparison::Lt.check(1.0, 1.0));
}
#[test]
fn test_comparison_eq() {
assert!(Comparison::Eq.check(0.95, 0.95));
assert!(!Comparison::Eq.check(0.95, 0.96));
}
}
#[cfg(test)]
mod property_tests {
use super::*;
use proptest::prelude::*;
proptest! {
#![proptest_config(ProptestConfig::with_cases(200))]
#[test]
fn prop_comparison_consistent(value in -1000.0f64..1000.0, threshold in -1000.0f64..1000.0) {
let gt = Comparison::Gt.check(value, threshold);
let lte = Comparison::Lte.check(value, threshold);
prop_assert!(gt != lte || value == threshold);
}
}
}