pub mod api;
pub mod assets;
pub mod history;
pub mod legacy;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BenchResult {
pub name: String,
#[serde(flatten)]
pub metrics: serde_json::Map<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TestOutcome {
pub name: String,
pub passed: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub duration_ms: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BenchRun {
pub date: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timestamp: Option<String>,
#[serde(default)]
pub version: String,
#[serde(default)]
pub machine: String,
#[serde(default)]
pub cores: u32,
pub results: Vec<BenchResult>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tests: Vec<TestOutcome>,
}
impl BenchRun {
pub fn find(&self, name: &str) -> Option<&BenchResult> {
self.results.iter().find(|r| r.name == name)
}
pub fn all_tests_passed(&self) -> bool {
self.tests.iter().all(|t| t.passed)
}
pub fn failed_tests(&self) -> Vec<&str> {
self.tests
.iter()
.filter(|t| !t.passed)
.map(|t| t.name.as_str())
.collect()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum MetricDirection {
High,
Low,
Neutral,
}
pub fn direction_of(
overrides: &std::collections::HashMap<String, MetricDirection>,
metric: &str,
) -> MetricDirection {
if let Some(d) = overrides.get(metric) {
return *d;
}
unit_of(metric).map(|u| u.direction).unwrap_or(MetricDirection::Neutral)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Unit {
pub name: &'static str,
pub direction: MetricDirection,
}
pub fn unit_of(metric: &str) -> Option<Unit> {
use MetricDirection::*;
let m = metric.to_ascii_lowercase();
const TABLE: &[(&str, &str, MetricDirection)] = &[
("mb_per_sec", "mbs", High),
("mbps", "mbs", High),
("_mbs", "mbs", High),
("gb_per_sec", "gbs", High),
("_gbs", "gbs", High),
("ops_per_sec", "ops_sec", High),
("ops_sec", "ops_sec", High),
("_ops_sec", "ops_sec", High),
("_per_sec", "per_sec", High),
("seconds", "secs", Low),
("_secs", "secs", Low),
("_s", "secs", Low),
("_ms", "ms", Low),
("_us", "us", Low),
("_ns", "ns", Low),
("_pct", "pct", Neutral),
("_x", "x", Neutral),
("speedup", "x", Neutral),
];
for (suffix, name, dir) in TABLE {
if m == *suffix || m.ends_with(suffix) {
return Some(Unit { name, direction: *dir });
}
}
None
}
#[cfg(test)]
mod direction_tests {
use super::*;
use std::collections::HashMap;
#[test]
fn unit_and_direction_from_name() {
assert_eq!(unit_of("ljar_mbs").unwrap().name, "mbs");
assert_eq!(unit_of("unzip_mbs").unwrap().direction, MetricDirection::High);
assert_eq!(unit_of("mb_per_sec").unwrap().name, "mbs");
assert_eq!(unit_of("holger_ops_sec").unwrap().name, "ops_sec");
assert_eq!(unit_of("decode_ms").unwrap().direction, MetricDirection::Low);
assert_eq!(unit_of("seconds").unwrap().direction, MetricDirection::Low);
assert_eq!(unit_of("speedup_x").unwrap().direction, MetricDirection::Neutral);
assert!(unit_of("bytes").is_none());
assert!(unit_of("files").is_none());
}
#[test]
fn explicit_override_wins() {
let mut o = HashMap::new();
o.insert("weird_metric".to_string(), MetricDirection::Low);
assert_eq!(direction_of(&o, "weird_metric"), MetricDirection::Low);
assert_eq!(direction_of(&o, "ljar_mbs"), MetricDirection::High);
assert_eq!(direction_of(&o, "count"), MetricDirection::Neutral);
}
}