use super::*;
use crate::assert::ScenarioStats;
#[test]
fn sidecar_to_row_basic() {
use crate::monitor;
use crate::test_support;
let sc = test_support::SidecarResult {
test_name: "my_test".to_string(),
topology: "1n2l4c2t".to_string(),
scheduler: "scx_mitosis".to_string(),
stats: ScenarioStats {
cgroups: vec![],
total_workers: 4,
total_cpus: 8,
total_migrations: 12,
worst_spread: 15.0,
worst_gap_ms: 200,
worst_gap_cpu: 3,
..Default::default()
},
monitor: Some(monitor::MonitorSummary {
total_samples: 10,
max_imbalance_ratio: 2.5,
max_local_dsq_depth: 4,
stuck_count: 1,
event_deltas: Some(monitor::ScxEventDeltas {
total_fallback: 7,
fallback_rate: 0.5,
max_fallback_burst: 2,
total_dispatch_offline: 0,
total_dispatch_keep_last: 3,
keep_last_rate: 0.2,
total_enq_skip_exiting: 0,
total_enq_skip_migration_disabled: 0,
..Default::default()
}),
schedstat_deltas: None,
prog_stats_deltas: None,
..Default::default()
}),
..test_support::SidecarResult::test_fixture()
};
let row = sidecar_to_row(&sc);
assert_eq!(row.scenario, "my_test");
assert_eq!(row.topology, "1n2l4c2t");
assert!(row.is_pass());
assert_eq!(row.spread, 15.0);
assert_eq!(row.gap_ms, 200);
assert_eq!(row.migrations, 12);
assert_eq!(row.imbalance_ratio, 2.5);
assert_eq!(row.max_dsq_depth, 4);
assert_eq!(row.stuck_count, 1.0);
assert_eq!(row.fallback_count, 7);
assert_eq!(row.keep_last_count, 3);
}
#[test]
fn sidecar_to_row_carries_worst_iterations_per_cpu_sec_via_ext() {
use crate::test_support;
let mut stats = ScenarioStats::default();
stats
.ext_metrics
.insert("worst_iterations_per_cpu_sec".to_string(), 1234.5);
let present = test_support::SidecarResult {
stats,
..test_support::SidecarResult::test_fixture()
};
let def = metric_def("worst_iterations_per_cpu_sec")
.expect("metric must be registered so `stats compare --metric` resolves it");
assert_eq!(def.read(&sidecar_to_row(&present)), Some(1234.5));
let absent = test_support::SidecarResult {
stats: ScenarioStats::default(),
..test_support::SidecarResult::test_fixture()
};
assert_eq!(def.read(&sidecar_to_row(&absent)), None);
}
#[test]
fn sidecar_to_row_no_monitor() {
use crate::test_support;
let sc = test_support::SidecarResult {
test_name: "eevdf_test".to_string(),
topology: "1n1l2c1t".to_string(),
passed: false,
..test_support::SidecarResult::test_fixture()
};
let row = sidecar_to_row(&sc);
assert_eq!(row.scenario, "eevdf_test");
assert!(row.is_fail());
assert_eq!(row.imbalance_ratio, 0.0);
assert_eq!(row.max_dsq_depth, 0);
assert_eq!(row.stuck_count, 0.0);
assert_eq!(row.fallback_count, 0);
assert_eq!(row.keep_last_count, 0);
}
#[test]
fn sidecar_to_row_propagates_project_commit() {
use crate::test_support;
let sc_dirty = test_support::SidecarResult {
test_name: "commit_dirty_test".to_string(),
topology: "1n1l2c1t".to_string(),
project_commit: Some("abcdef1-dirty".to_string()),
..test_support::SidecarResult::test_fixture()
};
let row_dirty = sidecar_to_row(&sc_dirty);
assert_eq!(
row_dirty.commit.as_deref(),
Some("abcdef1-dirty"),
"populated dirty project_commit must propagate \
verbatim, including the `-dirty` suffix",
);
let sc_clean = test_support::SidecarResult {
test_name: "commit_clean_test".to_string(),
topology: "1n1l2c1t".to_string(),
project_commit: Some("abcdef1".to_string()),
..test_support::SidecarResult::test_fixture()
};
let row_clean = sidecar_to_row(&sc_clean);
assert_eq!(
row_clean.commit.as_deref(),
Some("abcdef1"),
"populated clean project_commit (no `-dirty` suffix) \
must propagate verbatim — a regression that always \
appended `-dirty` or always stripped a tail would \
surface here independently of the dirty case above",
);
let sc_none = test_support::SidecarResult {
test_name: "no_commit_test".to_string(),
topology: "1n1l2c1t".to_string(),
project_commit: None,
..test_support::SidecarResult::test_fixture()
};
let row_none = sidecar_to_row(&sc_none);
assert!(
row_none.commit.is_none(),
"absent project_commit must propagate as None — a \
regression substituting an empty string would dilute \
every `--project-commit` filter into matching all None rows",
);
}
#[test]
fn sidecar_to_row_propagates_kernel_commit() {
use crate::test_support;
let sc_dirty = test_support::SidecarResult {
test_name: "kc_dirty_test".to_string(),
topology: "1n1l2c1t".to_string(),
kernel_commit: Some("kabcde7-dirty".to_string()),
..test_support::SidecarResult::test_fixture()
};
let row_dirty = sidecar_to_row(&sc_dirty);
assert_eq!(
row_dirty.kernel_commit.as_deref(),
Some("kabcde7-dirty"),
"populated dirty kernel_commit must propagate \
verbatim, including the `-dirty` suffix",
);
let sc_clean = test_support::SidecarResult {
test_name: "kc_clean_test".to_string(),
topology: "1n1l2c1t".to_string(),
kernel_commit: Some("kabcde7".to_string()),
..test_support::SidecarResult::test_fixture()
};
let row_clean = sidecar_to_row(&sc_clean);
assert_eq!(
row_clean.kernel_commit.as_deref(),
Some("kabcde7"),
"populated clean kernel_commit (no `-dirty` suffix) \
must propagate verbatim — a regression that always \
appended `-dirty` or always stripped a tail would \
surface here independently of the dirty case above",
);
let sc_none = test_support::SidecarResult {
test_name: "no_kc_test".to_string(),
topology: "1n1l2c1t".to_string(),
kernel_commit: None,
..test_support::SidecarResult::test_fixture()
};
let row_none = sidecar_to_row(&sc_none);
assert!(
row_none.kernel_commit.is_none(),
"absent kernel_commit must propagate as None — a \
regression substituting an empty string would dilute \
every `--kernel-commit` filter into matching all \
None rows",
);
let sc_both = test_support::SidecarResult {
test_name: "both_test".to_string(),
topology: "1n1l2c1t".to_string(),
project_commit: Some("project1".to_string()),
kernel_commit: Some("kernel1".to_string()),
..test_support::SidecarResult::test_fixture()
};
let row_both = sidecar_to_row(&sc_both);
assert_eq!(
row_both.commit.as_deref(),
Some("project1"),
"row.commit must come from project_commit, not kernel_commit",
);
assert_eq!(
row_both.kernel_commit.as_deref(),
Some("kernel1"),
"row.kernel_commit must come from kernel_commit, not project_commit",
);
}
#[test]
fn sidecar_to_row_propagates_run_source() {
use crate::test_support;
for tag in ["local", "ci", "archive"] {
let sc = test_support::SidecarResult {
test_name: format!("run_source_{tag}_test"),
topology: "1n1l2c1t".to_string(),
run_source: Some(tag.to_string()),
..test_support::SidecarResult::test_fixture()
};
let row = sidecar_to_row(&sc);
assert_eq!(
row.run_source.as_deref(),
Some(tag),
"populated run_source `{tag}` must propagate verbatim",
);
}
let sc_none = test_support::SidecarResult {
test_name: "no_run_source_test".to_string(),
topology: "1n1l2c1t".to_string(),
run_source: None,
..test_support::SidecarResult::test_fixture()
};
let row_none = sidecar_to_row(&sc_none);
assert!(
row_none.run_source.is_none(),
"absent run_source must propagate as None — a regression \
substituting an empty string would dilute every \
`--run-source` filter into matching all None rows",
);
let sc_distinct = test_support::SidecarResult {
test_name: "run_source_distinct_test".to_string(),
topology: "1n1l2c1t".to_string(),
run_source: Some("local".to_string()),
kernel_commit: Some("kabcde7".to_string()),
project_commit: Some("pabcde7".to_string()),
..test_support::SidecarResult::test_fixture()
};
let row_distinct = sidecar_to_row(&sc_distinct);
assert_eq!(
row_distinct.run_source.as_deref(),
Some("local"),
"row.run_source must come from sc.run_source, not from \
kernel_commit or project_commit",
);
assert_eq!(
row_distinct.kernel_commit.as_deref(),
Some("kabcde7"),
"row.kernel_commit must remain sourced from sc.kernel_commit",
);
assert_eq!(
row_distinct.commit.as_deref(),
Some("pabcde7"),
"row.commit must remain sourced from sc.project_commit",
);
}
#[test]
fn sidecar_to_row_no_stall() {
use crate::monitor;
use crate::test_support;
let sc = test_support::SidecarResult {
monitor: Some(monitor::MonitorSummary {
prog_stats_deltas: None,
total_samples: 5,
max_imbalance_ratio: 1.0,
max_local_dsq_depth: 0,
stuck_count: 0,
event_deltas: None,
schedstat_deltas: None,
..Default::default()
}),
..test_support::SidecarResult::test_fixture()
};
let row = sidecar_to_row(&sc);
assert_eq!(row.stuck_count, 0.0);
assert_eq!(row.fallback_count, 0);
assert_eq!(row.keep_last_count, 0);
}
fn assert_all_direct_f64_fields_sanitized(non_finite: f64) {
use crate::assert::ScenarioStats;
use crate::monitor::MonitorSummary;
use crate::test_support;
let sc = test_support::SidecarResult {
stats: ScenarioStats {
worst_spread: non_finite,
worst_migration_ratio: non_finite,
worst_page_locality: non_finite,
worst_cross_node_migration_ratio: non_finite,
..Default::default()
},
monitor: Some(MonitorSummary {
max_imbalance_ratio: non_finite,
..Default::default()
}),
..test_support::SidecarResult::test_fixture()
};
let row = sidecar_to_row(&sc);
for (name, val) in [
("spread", row.spread),
("migration_ratio", row.migration_ratio),
("imbalance_ratio", row.imbalance_ratio),
("page_locality", row.page_locality),
("cross_node_migration_ratio", row.cross_node_migration_ratio),
] {
assert_eq!(
val, 0.0,
"{name} must collapse to 0.0 for non-finite input {non_finite:?}",
);
}
serde_json::to_string(&row).expect("sanitized row must serialize cleanly");
}
#[test]
fn sidecar_to_row_zeros_nan_in_every_direct_f64_field() {
assert_all_direct_f64_fields_sanitized(f64::NAN);
}
#[test]
fn sidecar_to_row_zeros_pos_infinity_in_every_direct_f64_field() {
assert_all_direct_f64_fields_sanitized(f64::INFINITY);
}
#[test]
fn sidecar_to_row_zeros_neg_infinity_in_every_direct_f64_field() {
assert_all_direct_f64_fields_sanitized(f64::NEG_INFINITY);
}
#[test]
fn sidecar_to_row_preserves_subnormal_f64_in_direct_fields() {
use crate::assert::ScenarioStats;
use crate::test_support;
let subnormal = f64::MIN_POSITIVE / 2.0;
assert!(subnormal.is_finite(), "subnormal must still be finite");
assert!(!subnormal.is_normal(), "subnormal must not be normal");
assert!(subnormal > 0.0, "subnormal is positive");
let sc = test_support::SidecarResult {
stats: ScenarioStats {
worst_spread: subnormal,
worst_page_locality: -subnormal,
worst_migration_ratio: subnormal,
..Default::default()
},
..test_support::SidecarResult::test_fixture()
};
let row = sidecar_to_row(&sc);
assert_eq!(
row.spread, subnormal,
"positive subnormal must pass through finite_or_zero unchanged",
);
assert_eq!(
row.page_locality, -subnormal,
"negative subnormal must pass through finite_or_zero unchanged",
);
assert_eq!(
row.migration_ratio, subnormal,
"subnormal on a second direct-f64 field must also pass through",
);
serde_json::to_string(&row).expect("subnormals serialize cleanly");
}
#[test]
fn sidecar_to_row_direct_field_nan_does_not_touch_ext_metrics() {
use crate::assert::ScenarioStats;
use crate::test_support;
let mut ext = BTreeMap::new();
ext.insert("finite_nonzero".to_string(), 2.5);
ext.insert("finite_zero".to_string(), 0.0);
ext.insert("finite_negative".to_string(), -7.25);
let sc = test_support::SidecarResult {
stats: ScenarioStats {
worst_spread: f64::NAN,
worst_migration_ratio: f64::INFINITY,
worst_page_locality: f64::INFINITY,
worst_cross_node_migration_ratio: f64::NEG_INFINITY,
ext_metrics: ext.clone(),
..Default::default()
},
..test_support::SidecarResult::test_fixture()
};
let row = sidecar_to_row(&sc);
assert_eq!(row.spread, 0.0);
assert_eq!(row.migration_ratio, 0.0);
assert_eq!(row.page_locality, 0.0);
assert_eq!(
row.ext_metrics.len(),
ext.len(),
"direct-field sanitization must not add or drop ext_metrics entries",
);
for (k, v) in &ext {
assert_eq!(
row.ext_metrics.get(k),
Some(v),
"ext_metrics entry {k:?} must pass through unchanged",
);
}
serde_json::to_string(&row).expect("sanitized row must serialize cleanly");
}
#[test]
fn sidecar_to_row_drops_non_finite_ext_metrics() {
use crate::assert::ScenarioStats;
use crate::test_support;
let mut ext = BTreeMap::new();
ext.insert("good".to_string(), 1.0);
ext.insert("nan".to_string(), f64::NAN);
ext.insert("inf".to_string(), f64::INFINITY);
ext.insert("neg_inf".to_string(), f64::NEG_INFINITY);
let sc = test_support::SidecarResult {
stats: ScenarioStats {
ext_metrics: ext,
..Default::default()
},
..test_support::SidecarResult::test_fixture()
};
let row = sidecar_to_row(&sc);
assert_eq!(
row.ext_metrics.len(),
1,
"only the finite entry should survive: {:?}",
row.ext_metrics
);
assert_eq!(row.ext_metrics.get("good"), Some(&1.0));
assert!(!row.ext_metrics.contains_key("nan"));
assert!(!row.ext_metrics.contains_key("inf"));
assert!(!row.ext_metrics.contains_key("neg_inf"));
serde_json::to_string(&row).expect("filtered row must serialize cleanly");
}
#[test]
fn sidecar_to_row_drops_walk_truncation_sentinel() {
use crate::assert::ScenarioStats;
use crate::test_support;
let mut ext = BTreeMap::new();
ext.insert("good".to_string(), 1.0);
ext.insert(
test_support::WALK_TRUNCATION_SENTINEL_NAME.to_string(),
72.0,
);
let sc = test_support::SidecarResult {
stats: ScenarioStats {
ext_metrics: ext,
..Default::default()
},
..test_support::SidecarResult::test_fixture()
};
let row = sidecar_to_row(&sc);
assert_eq!(
row.ext_metrics.len(),
1,
"only the real metric should survive: {:?}",
row.ext_metrics,
);
assert_eq!(row.ext_metrics.get("good"), Some(&1.0));
assert!(
!row.ext_metrics
.contains_key(test_support::WALK_TRUNCATION_SENTINEL_NAME),
"sentinel must not appear in the row's ext_metrics",
);
}
#[test]
fn metric_def_known() {
let d = metric_def("worst_spread").unwrap();
assert_eq!(d.name, "worst_spread");
assert!(d.higher_is_worse());
assert_eq!(d.display_unit, "%");
}
#[test]
fn metric_def_not_higher_is_worse() {
let d = metric_def("total_iterations").unwrap();
assert!(!d.higher_is_worse());
}
#[test]
fn metric_def_unknown() {
assert!(metric_def("nonexistent").is_none());
}
#[test]
fn infer_higher_is_worse_latency_shaped() {
for name in &[
"p99_wake_latency",
"wake_latency_us",
"scheduling_delay",
"task_run_delay_ns",
"io_completion_ms",
"stall_count",
"stuck_count",
"schedule_jitter_cv",
"max_gap_us",
"spread",
"page_drop_count",
"error_rate",
"fail_count",
"migration_ratio",
"imbalance_factor",
] {
assert!(
infer_higher_is_worse(name),
"metric `{name}` must infer higher_is_worse=true \
(latency/error-shaped); a folded max keeps the \
worst case across cgroups"
);
}
}
#[test]
fn infer_higher_is_worse_throughput_shaped() {
for name in &[
"read_iops",
"write_iops",
"throughput_mbps",
"bandwidth_kb",
"total_iterations",
"iterations_per_worker",
"ops_per_sec",
"page_locality",
"pass_score",
"goodput",
] {
assert!(
!infer_higher_is_worse(name),
"metric `{name}` must infer higher_is_worse=false \
(throughput-shaped); a folded min surfaces the \
cgroup that fell behind"
);
}
}
#[test]
fn infer_higher_is_worse_unknown_falls_back_to_higher_is_worse() {
for name in &["unrelated_field", "random_thing", "metric", "x", "a.b.c"] {
assert!(
infer_higher_is_worse(name),
"unknown metric `{name}` must fall back to \
higher_is_worse=true (conservative for regression \
detection)"
);
}
}
#[test]
fn infer_higher_is_worse_compound_names_resolve_to_latency() {
assert!(
infer_higher_is_worse("read_iops_latency_us"),
"compound name with `latency` and `iops` must resolve \
to higher-is-worse (latency token checked first)"
);
}
#[test]
fn metric_def_polarity_inverse_sense() {
use crate::test_support::Polarity;
let d = metric_def("worst_spread").unwrap();
assert!(d.higher_is_worse());
assert_eq!(d.polarity, Polarity::LowerBetter);
let d = metric_def("total_iterations").unwrap();
assert!(!d.higher_is_worse());
assert_eq!(d.polarity, Polarity::HigherBetter);
}
#[test]
fn metric_def_polarity_covers_all_entries() {
use crate::test_support::Polarity;
for m in METRICS.iter() {
assert!(
matches!(m.polarity, Polarity::HigherBetter | Polarity::LowerBetter),
"metric {} produced non-binary polarity {:?}",
m.name,
m.polarity
);
}
}
#[test]
fn metric_def_all_entries_unique() {
let mut names: Vec<&str> = METRICS.iter().map(|m| m.name).collect();
let len = names.len();
names.sort();
names.dedup();
assert_eq!(names.len(), len);
}
#[test]
fn every_rate_metric_has_registered_counter_components() {
for m in METRICS.iter() {
let MetricKind::Rate {
numerator,
denominator,
} = m.kind
else {
continue;
};
for (role, comp) in [("numerator", numerator), ("denominator", denominator)] {
let def = metric_def(comp).unwrap_or_else(|| {
panic!("Rate {} {role} {comp:?} is not a registered metric", m.name)
});
assert!(
matches!(def.kind, MetricKind::Counter),
"Rate {} {role} {comp:?} must be a Counter for the Σ-fold re-pool",
m.name,
);
}
}
}
#[test]
fn list_metrics_text_names_every_metric() {
let out = list_metrics(false).expect("text render must succeed");
assert!(!out.is_empty(), "text output must be non-empty");
for m in METRICS {
assert!(
out.contains(m.name),
"list_metrics(false) output missing metric name {}: {out}",
m.name,
);
}
}
#[test]
fn list_metrics_text_header_pins_column_names() {
let out = list_metrics(false).expect("text render must succeed");
for header in ["NAME", "POLARITY", "DEFAULT_ABS", "DEFAULT_REL", "UNIT"] {
assert!(
out.contains(header),
"list_metrics(false) output missing column header {header}: {out}",
);
}
}
#[test]
fn list_metrics_json_round_trips_via_minimal_schema() {
#[derive(serde::Deserialize)]
struct MetricEntry {
name: String,
default_abs: f64,
default_rel: f64,
display_unit: String,
polarity: serde_json::Value,
}
let out = list_metrics(true).expect("json render must succeed");
let parsed: Vec<MetricEntry> = serde_json::from_str(&out).expect("json output must parse");
assert_eq!(
parsed.len(),
METRICS.len(),
"json entry count must match METRICS.len()",
);
for (parsed_m, registry_m) in parsed.iter().zip(METRICS.iter()) {
assert_eq!(parsed_m.name, registry_m.name);
assert_eq!(parsed_m.default_abs, registry_m.default_abs);
assert_eq!(parsed_m.default_rel, registry_m.default_rel);
assert_eq!(parsed_m.display_unit, registry_m.display_unit);
assert!(
!parsed_m.polarity.is_null(),
"polarity for {} must serialize as a non-null value",
registry_m.name,
);
}
}
#[test]
fn list_metrics_json_omits_accessor_field() {
let out = list_metrics(true).expect("json render must succeed");
assert!(
!out.contains("\"accessor\""),
"list_metrics(true) must not emit the accessor field — \
fn-pointers are not serializable and the field carries \
#[serde(skip)]: {out}",
);
}
#[test]
fn distribution_worstlowest_kind_json_shape_pinned() {
let dist = serde_json::to_string(&MetricKind::Distribution {
source: SampleSource::WakeLatencyNs,
reduction: SampleReduction::P99,
})
.expect("MetricKind serializes");
for tok in [
"\"Distribution\"",
"\"source\"",
"\"WakeLatencyNs\"",
"\"reduction\"",
"\"P99\"",
] {
assert!(dist.contains(tok), "{tok} missing from {dist}");
}
let wl = serde_json::to_string(&MetricKind::WorstLowest {
numerator: WorstLowestNumerator::Iterations,
denominator: WorstLowestDenominator::CpuTimeNs,
})
.expect("MetricKind serializes");
for tok in [
"\"WorstLowest\"",
"\"numerator\"",
"\"Iterations\"",
"\"denominator\"",
"\"CpuTimeNs\"",
] {
assert!(wl.contains(tok), "{tok} missing from {wl}");
}
}
#[test]
fn list_metrics_text_preserves_registry_order() {
let out = list_metrics(false).expect("text render must succeed");
let mut last_pos = 0usize;
for m in METRICS {
let pos = out
.find(m.name)
.unwrap_or_else(|| panic!("metric {} must appear in text output", m.name));
assert!(
pos >= last_pos,
"metric {} appears before a prior metric — text output must \
preserve METRICS declaration order",
m.name,
);
last_pos = pos;
}
}
fn write_listvalues_fixture(
root: &std::path::Path,
sidecars: &[crate::test_support::SidecarResult],
) {
for (i, sc) in sidecars.iter().enumerate() {
let run_key = format!("__lv_fixture_{i}__");
let run_dir = root.join(&run_key);
std::fs::create_dir_all(&run_dir).expect("create run dir");
let json = serde_json::to_string(sc).expect("serialize fixture sidecar");
let path = run_dir.join(format!("{run_key}.ktstr.json"));
std::fs::write(&path, json).expect("write fixture sidecar");
}
}
#[test]
fn list_values_empty_pool_text_has_sentinel_per_dim() {
let alt = tempfile::TempDir::new().expect("tempdir");
let out = list_values(false, Some(alt.path())).expect("text render must succeed");
for dim in [
"kernel:",
"commit:",
"kernel_commit:",
"source:",
"cpu_budget:",
"scheduler:",
"topology:",
"work_type:",
] {
assert!(
out.contains(dim),
"text output must include heading for {dim}: {out}",
);
}
let sentinel_count = out.matches("(no sidecars in pool)").count();
assert_eq!(
sentinel_count, 8,
"empty pool must surface the no-sidecars sentinel under every \
one of the 8 dims (kernel/commit/kernel_commit/source/\
cpu_budget/scheduler/topology/work_type); got {sentinel_count} \
occurrences in:\n{out}",
);
}
#[test]
fn list_values_empty_pool_json_emits_empty_arrays() {
let alt = tempfile::TempDir::new().expect("tempdir");
let out = list_values(true, Some(alt.path())).expect("json render must succeed");
let parsed: serde_json::Value = serde_json::from_str(&out).expect("json output must parse");
for dim in [
"kernel",
"commit",
"kernel_commit",
"source",
"cpu_budget",
"scheduler",
"topology",
"work_type",
] {
let arr = parsed
.get(dim)
.unwrap_or_else(|| panic!("missing key {dim}"));
assert!(arr.is_array(), "key {dim} must serialize as an array");
assert_eq!(
arr.as_array().unwrap().len(),
0,
"key {dim} must be an empty array on empty pool",
);
}
}
#[test]
fn list_values_text_dedupes_and_sorts_per_dim() {
use crate::test_support::SidecarResult;
let alt = tempfile::TempDir::new().expect("tempdir");
let sidecars = vec![
SidecarResult {
test_name: "t_a".to_string(),
topology: "1n2l4c1t".to_string(),
scheduler: "scx_rusty".to_string(),
work_type: "SpinWait".to_string(),
kernel_version: Some("6.14.2".to_string()),
project_commit: Some("abcdef1".to_string()),
..SidecarResult::test_fixture()
},
SidecarResult {
test_name: "t_b".to_string(),
topology: "1n4l2c1t".to_string(),
scheduler: "eevdf".to_string(),
work_type: "PageFaultChurn".to_string(),
kernel_version: None,
project_commit: None,
..SidecarResult::test_fixture()
},
SidecarResult {
test_name: "t_c".to_string(),
topology: "1n2l4c1t".to_string(),
scheduler: "scx_rusty".to_string(),
work_type: "SpinWait".to_string(),
kernel_version: Some("6.14.2".to_string()),
project_commit: Some("abcdef1".to_string()),
..SidecarResult::test_fixture()
},
];
write_listvalues_fixture(alt.path(), &sidecars);
let out = list_values(false, Some(alt.path())).expect("text render must succeed");
for value in [
"6.14.2",
"abcdef1",
"scx_rusty",
"eevdf",
"1n2l4c1t",
"1n4l2c1t",
"SpinWait",
"PageFaultChurn",
] {
let count = out.matches(value).count();
assert_eq!(
count, 1,
"value {value} must appear exactly once in text output (BTreeSet dedup); \
got {count} in:\n{out}",
);
}
let unknown_count = out.matches("unknown").count();
assert_eq!(
unknown_count, 3,
"`unknown` must render once per optional dim with a None \
entry (kernel + commit + kernel_commit = 3); got \
{unknown_count} in:\n{out}",
);
let pos_eevdf = out.find("eevdf").expect("eevdf in output");
let pos_rusty = out.find("scx_rusty").expect("scx_rusty in output");
assert!(
pos_eevdf < pos_rusty,
"values within a dim must render sorted (BTreeSet iter order); \
expected 'eevdf' before 'scx_rusty' in:\n{out}",
);
}
#[test]
fn list_values_cpu_budget_renders_distinct_sorted() {
use crate::test_support::SidecarResult;
let alt = tempfile::TempDir::new().expect("tempdir");
let sidecars = vec![
SidecarResult {
test_name: "t_a".to_string(),
cpu_budget: 32,
vcpus: 32,
..SidecarResult::test_fixture()
},
SidecarResult {
test_name: "t_b".to_string(),
cpu_budget: 4,
vcpus: 16,
..SidecarResult::test_fixture()
},
SidecarResult {
test_name: "t_c".to_string(),
cpu_budget: 4,
vcpus: 16,
..SidecarResult::test_fixture()
},
SidecarResult {
test_name: "t_d".to_string(),
cpu_budget: 0,
vcpus: 0,
..SidecarResult::test_fixture()
},
];
write_listvalues_fixture(alt.path(), &sidecars);
let text = list_values(false, Some(alt.path())).expect("text render must succeed");
let block_start =
text.find("cpu_budget:\n").expect("cpu_budget heading") + "cpu_budget:\n".len();
let block = &text[block_start..];
let block_end = block.find("\n\n").map(|i| i + 1).unwrap_or(block.len());
let block = &block[..block_end];
assert_eq!(
block.matches(" 4\n").count(),
1,
"budget 4 once: {block:?}"
);
assert_eq!(
block.matches(" 32\n").count(),
1,
"budget 32 once: {block:?}"
);
let pos_4 = block.find(" 4\n").expect("4 present");
let pos_32 = block.find(" 32\n").expect("32 present");
assert!(pos_4 < pos_32, "budgets must sort ascending: {block:?}");
let json = list_values(true, Some(alt.path())).expect("json render must succeed");
let parsed: serde_json::Value = serde_json::from_str(&json).expect("json must parse");
let arr = parsed
.get("cpu_budget")
.and_then(|v| v.as_array())
.expect("cpu_budget must be a JSON array");
let nums: Vec<u64> = arr
.iter()
.map(|v| v.as_u64().expect("numeric budget"))
.collect();
assert_eq!(
nums,
vec![4, 32],
"JSON cpu_budget must be a sorted numeric array"
);
}
#[test]
fn list_values_json_carries_null_for_optional_dims() {
use crate::test_support::SidecarResult;
let alt = tempfile::TempDir::new().expect("tempdir");
let sidecars = vec![
SidecarResult {
test_name: "t_known".to_string(),
kernel_version: Some("6.14.2".to_string()),
project_commit: Some("abcdef1".to_string()),
..SidecarResult::test_fixture()
},
SidecarResult {
test_name: "t_unknown".to_string(),
kernel_version: None,
project_commit: None,
..SidecarResult::test_fixture()
},
];
write_listvalues_fixture(alt.path(), &sidecars);
let out = list_values(true, Some(alt.path())).expect("json render must succeed");
let parsed: serde_json::Value = serde_json::from_str(&out).expect("json output must parse");
let kernel = parsed
.get("kernel")
.expect("kernel key")
.as_array()
.unwrap();
assert!(
kernel.iter().any(|v| v.is_null()),
"kernel array must include a literal null for the None entry; got {kernel:?}",
);
assert!(
kernel.iter().any(|v| v.as_str() == Some("6.14.2")),
"kernel array must include the populated value 6.14.2; got {kernel:?}",
);
let commit = parsed
.get("commit")
.expect("commit key")
.as_array()
.unwrap();
assert!(
commit.iter().any(|v| v.is_null()),
"commit array must include a literal null for the None entry; got {commit:?}",
);
assert!(
commit.iter().any(|v| v.as_str() == Some("abcdef1")),
"commit array must include the populated value abcdef1; got {commit:?}",
);
}
#[test]
fn list_values_none_dir_does_not_bail_on_missing_root() {
let alt = tempfile::TempDir::new().expect("tempdir");
let nonexistent = alt.path().join("definitely_does_not_exist");
let out = list_values(false, Some(&nonexistent)).expect("must not bail on missing root");
assert!(
out.contains("(no sidecars in pool)"),
"missing root must render the no-sidecars sentinel: {out}",
);
}
fn read_metric(row: &GauntletRow, name: &str) -> Option<f64> {
metric_def(name).expect("metric name").read(row)
}
#[test]
fn metric_def_read_named_fields() {
let mut row = make_row("a", "t", true, 42.0);
row.gap_ms = 100;
row.migrations = 7;
row.migration_ratio = 0.3;
row.imbalance_ratio = 2.0;
row.max_dsq_depth = 5;
row.stuck_count = 3.0;
row.fallback_count = 11;
row.keep_last_count = 4;
row.total_iterations = 1000;
row.page_locality = 0.8;
row.cross_node_migration_ratio = 0.1;
for (name, v) in [
("worst_p99_wake_latency_us", 99.0),
("worst_median_wake_latency_us", 50.0),
("worst_wake_latency_cv", 0.5),
("worst_mean_run_delay_us", 25.0),
("worst_run_delay_us", 200.0),
] {
row.ext_metrics.insert(name.to_string(), v);
}
assert_eq!(read_metric(&row, "worst_spread"), Some(42.0));
assert_eq!(read_metric(&row, "worst_gap_ms"), Some(100.0));
assert_eq!(read_metric(&row, "total_migrations"), Some(7.0));
assert_eq!(read_metric(&row, "worst_migration_ratio"), Some(0.3));
assert_eq!(read_metric(&row, "max_imbalance_ratio"), Some(2.0));
assert_eq!(read_metric(&row, "max_dsq_depth"), Some(5.0));
assert_eq!(read_metric(&row, "stuck_count"), Some(3.0));
assert_eq!(read_metric(&row, "total_fallback"), Some(11.0));
assert_eq!(read_metric(&row, "total_keep_last"), Some(4.0));
assert_eq!(read_metric(&row, "worst_p99_wake_latency_us"), Some(99.0));
assert_eq!(
read_metric(&row, "worst_median_wake_latency_us"),
Some(50.0)
);
assert_eq!(read_metric(&row, "worst_wake_latency_cv"), Some(0.5));
assert_eq!(read_metric(&row, "total_iterations"), Some(1000.0));
assert_eq!(read_metric(&row, "worst_mean_run_delay_us"), Some(25.0));
assert_eq!(read_metric(&row, "worst_run_delay_us"), Some(200.0));
assert_eq!(read_metric(&row, "worst_page_locality"), Some(0.8));
assert_eq!(
read_metric(&row, "worst_cross_node_migration_ratio"),
Some(0.1)
);
}
#[test]
fn metric_def_read_prefers_accessor_over_ext_metrics() {
let mut row = make_row("a", "t", true, 5.0);
row.ext_metrics.insert("worst_spread".into(), 999.0);
assert_eq!(read_metric(&row, "worst_spread"), Some(5.0));
row.ext_metrics.insert("custom_metric".into(), 77.0);
assert!(metric_def("custom_metric").is_none());
assert_eq!(row.ext_metrics.get("custom_metric").copied(), Some(77.0));
}