mod common;
use common::fixtures::{FIO, FIO_JSON, SCHBENCH, SCHBENCH_HINTED, STRESS_NG};
use ktstr::test_support::{
MetricCheck, MetricHint, MetricSource, MetricStream, OutputFormat, PayloadKind, Polarity,
extract_metrics,
};
#[test]
fn fio_identity_fields_are_stable() {
assert_eq!(FIO.name, "fio");
assert!(matches!(FIO.kind, PayloadKind::Binary("fio")));
assert!(matches!(FIO.output, OutputFormat::Json));
}
#[test]
fn fio_default_checks_include_exit_code_zero() {
assert_eq!(FIO.default_checks.len(), 1);
assert!(matches!(FIO.default_checks[0], MetricCheck::ExitCodeEq(0)));
}
#[test]
fn fio_metric_hints_cover_canonical_paths() {
let by_name: std::collections::BTreeMap<&str, &MetricHint> =
FIO.metrics.iter().map(|m| (m.name, m)).collect();
let rops = by_name.get("jobs.0.read.iops").expect("read iops hint");
assert_eq!(rops.polarity, Polarity::HigherBetter);
assert_eq!(rops.unit, "iops");
let wops = by_name.get("jobs.0.write.iops").expect("write iops hint");
assert_eq!(wops.polarity, Polarity::HigherBetter);
assert_eq!(wops.unit, "iops");
let rlat = by_name
.get("jobs.0.read.lat_ns.mean")
.expect("read lat hint");
assert_eq!(rlat.polarity, Polarity::LowerBetter);
assert_eq!(rlat.unit, "ns");
let wlat = by_name
.get("jobs.0.write.lat_ns.mean")
.expect("write lat hint");
assert_eq!(wlat.polarity, Polarity::LowerBetter);
assert_eq!(wlat.unit, "ns");
}
#[test]
fn fio_json_identity_and_default_args() {
assert_eq!(FIO_JSON.name, "fio_json");
assert!(matches!(FIO_JSON.kind, PayloadKind::Binary("fio")));
assert_eq!(FIO_JSON.default_args, &["--output-format=json"]);
assert_eq!(FIO_JSON.metrics.len(), FIO.metrics.len());
}
#[test]
fn stress_ng_identity_fields_are_stable() {
assert_eq!(STRESS_NG.name, "stress-ng");
assert!(matches!(STRESS_NG.kind, PayloadKind::Binary("stress-ng")));
assert!(matches!(STRESS_NG.output, OutputFormat::ExitCode));
assert!(STRESS_NG.metrics.is_empty());
assert_eq!(STRESS_NG.default_checks.len(), 1);
assert!(matches!(
STRESS_NG.default_checks[0],
MetricCheck::ExitCodeEq(0),
));
}
#[test]
fn fio_extract_metrics_smoke_from_realistic_json() {
let stdout = r#"{
"jobs": [{
"jobname": "example",
"read": {"iops": 12345.6, "lat_ns": {"mean": 500.0}},
"write": {"iops": 78.9, "lat_ns": {"mean": 2500.0}}
}]
}"#;
let metrics = extract_metrics(stdout, &FIO.output, MetricStream::Stdout).unwrap();
let by_name: std::collections::BTreeMap<&str, f64> =
metrics.iter().map(|m| (m.name.as_str(), m.value)).collect();
assert_eq!(by_name.get("jobs.0.read.iops"), Some(&12345.6));
assert_eq!(by_name.get("jobs.0.write.iops"), Some(&78.9));
assert_eq!(by_name.get("jobs.0.read.lat_ns.mean"), Some(&500.0));
assert_eq!(by_name.get("jobs.0.write.lat_ns.mean"), Some(&2500.0));
for m in &metrics {
assert_eq!(
m.source,
MetricSource::Json,
"fixture declares Json output; every metric must land with Json source tag"
);
}
}
#[test]
fn stress_ng_extract_metrics_smoke_returns_empty() {
let metrics =
extract_metrics("irrelevant stdout", &STRESS_NG.output, MetricStream::Stdout).unwrap();
assert!(
metrics.is_empty(),
"ExitCode output emits no metrics; got {metrics:?}"
);
}
#[test]
fn schbench_identity_fields_are_stable() {
assert_eq!(SCHBENCH.name, "schbench");
assert!(matches!(SCHBENCH.kind, PayloadKind::Binary("schbench")));
assert!(matches!(SCHBENCH.output, OutputFormat::LlmExtract(None)));
assert_eq!(
SCHBENCH.default_args,
&["--runtime", "5", "--message-threads", "2"]
);
assert!(SCHBENCH.metrics.is_empty());
assert_eq!(SCHBENCH.default_checks.len(), 1);
assert!(matches!(
SCHBENCH.default_checks[0],
MetricCheck::ExitCodeEq(0)
));
}
#[test]
fn schbench_hinted_output_carries_hint_through_derive() {
assert_eq!(SCHBENCH_HINTED.name, "schbench_hinted");
assert!(matches!(
SCHBENCH_HINTED.kind,
PayloadKind::Binary("schbench"),
));
match SCHBENCH_HINTED.output {
OutputFormat::LlmExtract(Some(hint)) => {
assert_eq!(hint, "wakeup latency percentiles");
}
other => panic!("expected OutputFormat::LlmExtract(Some(hint)), got {other:?}",),
}
assert_eq!(
SCHBENCH_HINTED.default_args, SCHBENCH.default_args,
"hinted fixture must differ from SCHBENCH only in name and output",
);
assert_eq!(
SCHBENCH_HINTED.metrics.len(),
SCHBENCH.metrics.len(),
"hinted fixture must differ from SCHBENCH only in name and output",
);
assert_eq!(
SCHBENCH_HINTED.default_checks.len(),
SCHBENCH.default_checks.len(),
"hinted fixture must differ from SCHBENCH only in name and output",
);
assert!(SCHBENCH_HINTED.metrics.is_empty());
assert_eq!(SCHBENCH_HINTED.default_checks.len(), 1);
assert!(matches!(
SCHBENCH_HINTED.default_checks[0],
MetricCheck::ExitCodeEq(0),
));
}
#[test]
fn schbench_and_schbench_hinted_have_distinct_names() {
assert_ne!(SCHBENCH.name, SCHBENCH_HINTED.name);
let PayloadKind::Binary(b1) = SCHBENCH.kind else {
panic!("SCHBENCH must be a Binary-kind payload, not Scheduler");
};
let PayloadKind::Binary(b2) = SCHBENCH_HINTED.kind else {
panic!("SCHBENCH_HINTED must be a Binary-kind payload, not Scheduler");
};
assert_eq!(b1, b2, "hinted fixture must point at same binary");
}
#[test]
fn fixtures_are_not_scheduler_kind() {
assert!(!FIO.is_scheduler());
assert!(!FIO_JSON.is_scheduler());
assert!(!STRESS_NG.is_scheduler());
assert!(!SCHBENCH.is_scheduler());
assert!(!SCHBENCH_HINTED.is_scheduler());
}
#[test]
fn extract_metrics_does_not_apply_polarity_hints() {
let stdout = r#"{"jobs":[{"read":{"iops": 1.0}}]}"#;
let metrics = extract_metrics(stdout, &FIO.output, MetricStream::Stdout).unwrap();
assert_eq!(metrics.len(), 1);
assert_eq!(metrics[0].name, "jobs.0.read.iops");
assert_eq!(metrics[0].polarity, Polarity::Unknown);
assert_eq!(metrics[0].unit, "");
}
#[test]
fn fixtures_default_checks_pin_exit_code_gate() {
const _: () = {
assert!(matches!(FIO.default_checks[0], MetricCheck::ExitCodeEq(0)));
assert!(matches!(
FIO_JSON.default_checks[0],
MetricCheck::ExitCodeEq(0)
));
assert!(matches!(
STRESS_NG.default_checks[0],
MetricCheck::ExitCodeEq(0)
));
assert!(matches!(
SCHBENCH.default_checks[0],
MetricCheck::ExitCodeEq(0)
));
assert!(matches!(
SCHBENCH_HINTED.default_checks[0],
MetricCheck::ExitCodeEq(0),
));
};
assert!(!FIO.default_checks.is_empty());
assert!(!FIO_JSON.default_checks.is_empty());
assert!(!STRESS_NG.default_checks.is_empty());
assert!(!SCHBENCH.default_checks.is_empty());
assert!(!SCHBENCH_HINTED.default_checks.is_empty());
}
#[test]
fn fixture_output_formats_span_json_exit_code_and_llm_extract() {
assert!(matches!(FIO.output, OutputFormat::Json));
assert!(matches!(FIO_JSON.output, OutputFormat::Json));
assert!(matches!(STRESS_NG.output, OutputFormat::ExitCode));
assert!(matches!(SCHBENCH.output, OutputFormat::LlmExtract(None)));
assert!(matches!(
SCHBENCH_HINTED.output,
OutputFormat::LlmExtract(Some(_)),
));
}