use super::*;
#[test]
fn dimension_all_canonical_order() {
assert_eq!(
Dimension::ALL,
&[
Dimension::Kernel,
Dimension::Scheduler,
Dimension::Topology,
Dimension::WorkType,
Dimension::ProjectCommit,
Dimension::KernelCommit,
Dimension::RunSource,
Dimension::CpuBudget,
],
);
}
#[test]
fn dimension_pairing_dims_complements_slicing() {
let pair = Dimension::pairing_dims(&[Dimension::Kernel, Dimension::ProjectCommit]);
assert_eq!(
pair,
vec![
Dimension::Scheduler,
Dimension::Topology,
Dimension::WorkType,
Dimension::KernelCommit,
Dimension::RunSource,
Dimension::CpuBudget,
],
);
let pair_reversed = Dimension::pairing_dims(&[Dimension::ProjectCommit, Dimension::Kernel]);
assert_eq!(pair, pair_reversed);
}
#[test]
fn dimension_pairing_dims_empty_slicing_yields_all() {
let pair = Dimension::pairing_dims(&[]);
assert_eq!(pair, Dimension::ALL.to_vec());
}
#[test]
fn derive_slicing_dims_identical_filters_yields_empty() {
let f = RowFilter {
schedulers: vec!["scx_alpha".to_string()],
..RowFilter::default()
};
assert!(derive_slicing_dims(&f, &f).is_empty());
}
#[test]
fn derive_slicing_dims_single_dim_diff() {
let f_a = RowFilter {
schedulers: vec!["scx_alpha".to_string()],
..RowFilter::default()
};
let f_b = RowFilter {
schedulers: vec!["scx_beta".to_string()],
..RowFilter::default()
};
assert_eq!(derive_slicing_dims(&f_a, &f_b), vec![Dimension::Scheduler]);
}
#[test]
fn derive_slicing_dims_vec_compares_as_set() {
let f_a = RowFilter {
kernels: vec!["6.14".to_string(), "6.15".to_string()],
..RowFilter::default()
};
let f_b = RowFilter {
kernels: vec!["6.15".to_string(), "6.14".to_string(), "6.14".to_string()],
..RowFilter::default()
};
assert!(
derive_slicing_dims(&f_a, &f_b).is_empty(),
"same set in different order/multiplicity must NOT slice",
);
}
#[test]
fn derive_slicing_dims_multi_dim_diff_in_canonical_order() {
let f_a = RowFilter {
kernels: vec!["6.14".to_string()],
schedulers: vec!["scx_alpha".to_string()],
..RowFilter::default()
};
let f_b = RowFilter {
kernels: vec!["6.15".to_string()],
schedulers: vec!["scx_beta".to_string()],
..RowFilter::default()
};
assert_eq!(
derive_slicing_dims(&f_a, &f_b),
vec![Dimension::Kernel, Dimension::Scheduler],
);
}
#[test]
fn derive_slicing_dims_source_only_diff() {
let f_a = RowFilter {
run_sources: vec!["local".to_string()],
..RowFilter::default()
};
let f_b = RowFilter {
run_sources: vec!["ci".to_string()],
..RowFilter::default()
};
assert_eq!(
derive_slicing_dims(&f_a, &f_b),
vec![Dimension::RunSource],
"differing `run_sources` must surface Source as a slicing dim",
);
let f_c = RowFilter {
run_sources: vec!["local".to_string(), "ci".to_string()],
..RowFilter::default()
};
let f_d = RowFilter {
run_sources: vec!["ci".to_string(), "local".to_string(), "local".to_string()],
..RowFilter::default()
};
assert!(
derive_slicing_dims(&f_c, &f_d).is_empty(),
"same run_source set in different order/multiplicity must NOT slice",
);
}
#[test]
fn derive_slicing_dims_topology_only_diff() {
let f_a = RowFilter {
topologies: vec!["1n2l4c1t".to_string()],
..RowFilter::default()
};
let f_b = RowFilter {
topologies: vec!["1n2l4c2t".to_string()],
..RowFilter::default()
};
assert_eq!(
derive_slicing_dims(&f_a, &f_b),
vec![Dimension::Topology],
"differing `topologies` must surface Topology as a slicing dim",
);
let f_c = RowFilter {
topologies: vec!["1n2l4c1t".to_string(), "1n2l4c2t".to_string()],
..RowFilter::default()
};
let f_d = RowFilter {
topologies: vec![
"1n2l4c2t".to_string(),
"1n2l4c1t".to_string(),
"1n2l4c1t".to_string(),
],
..RowFilter::default()
};
assert!(
derive_slicing_dims(&f_c, &f_d).is_empty(),
"same topology set in different order/multiplicity must NOT slice",
);
}
#[test]
fn derive_slicing_dims_work_type_only_diff() {
let f_a = RowFilter {
work_types: vec!["SpinWait".to_string()],
..RowFilter::default()
};
let f_b = RowFilter {
work_types: vec!["PageFaultChurn".to_string()],
..RowFilter::default()
};
assert_eq!(
derive_slicing_dims(&f_a, &f_b),
vec![Dimension::WorkType],
"differing `work_types` must surface WorkType as a slicing dim",
);
let f_c = RowFilter {
work_types: vec!["SpinWait".to_string(), "PageFaultChurn".to_string()],
..RowFilter::default()
};
let f_d = RowFilter {
work_types: vec![
"PageFaultChurn".to_string(),
"SpinWait".to_string(),
"SpinWait".to_string(),
],
..RowFilter::default()
};
assert!(
derive_slicing_dims(&f_c, &f_d).is_empty(),
"same work_type set in different order/multiplicity must NOT slice",
);
}
#[test]
fn kernel_filter_matches_major_minor_prefix() {
assert!(kernel_filter_matches("6.12", "6.12"));
assert!(kernel_filter_matches("6.12", "6.12.0"));
assert!(kernel_filter_matches("6.12", "6.12.5"));
assert!(!kernel_filter_matches("6.12", "6.13.0"));
assert!(!kernel_filter_matches("6.1", "6.10.0"));
}
#[test]
fn kernel_filter_matches_major_minor_admits_rc_pre_release() {
assert!(kernel_filter_matches("6.14", "6.14-rc3"));
assert!(kernel_filter_matches("6.14", "6.14-rc1"));
assert!(kernel_filter_matches("6.14", "6.14.0-rc3"));
assert!(kernel_filter_matches("6.14", "6.14.0-rc3+"));
assert!(!kernel_filter_matches("6.1", "6.14-rc3"));
assert!(!kernel_filter_matches("6.14", "6.15-rc3"));
}
#[test]
fn kernel_filter_matches_strict_for_three_plus_segments() {
assert!(kernel_filter_matches("6.14.2", "6.14.2"));
assert!(!kernel_filter_matches("6.14.2", "6.14.20"));
assert!(!kernel_filter_matches("6.14.2", "6.14.21"));
assert!(kernel_filter_matches("6.15-rc3", "6.15-rc3"));
assert!(!kernel_filter_matches("6.15-rc3", "6.15-rc30"));
}
#[test]
fn row_filter_kernel_major_minor_prefix_admits_patch_version() {
let row = make_filter_row("t", "scx_a", "1n2l4c1t", "SpinWait", Some("6.12.5"));
let filter = RowFilter {
kernels: vec!["6.12".to_string()],
..RowFilter::default()
};
assert!(
filter.matches(&row),
"major.minor filter `6.12` must admit row with kernel_version `6.12.5`",
);
}
#[test]
fn pairing_key_from_row_basic() {
let row_a = make_filter_row("scenA", "scx_a", "1n1l", "SpinWait", Some("6.14"));
let row_b = make_filter_row("scenA", "scx_a", "1n1l", "SpinWait", Some("6.14"));
let row_c = make_filter_row("scenA", "scx_a", "2n2l", "SpinWait", Some("6.14"));
let dims = &[Dimension::Topology, Dimension::WorkType];
assert_eq!(
PairingKey::from_row(&row_a, dims),
PairingKey::from_row(&row_b, dims),
);
assert_ne!(
PairingKey::from_row(&row_a, dims),
PairingKey::from_row(&row_c, dims),
"different topology must distinguish the keys when topology is a pairing dim",
);
}
#[test]
fn pairing_key_excludes_slicing_dim() {
let row_a = make_filter_row("scenA", "scx_a", "1n1l", "SpinWait", Some("6.14"));
let row_b = make_filter_row("scenA", "scx_a", "2n2l", "SpinWait", Some("6.14"));
let pair_dims = Dimension::pairing_dims(&[Dimension::Topology]);
assert_eq!(
PairingKey::from_row(&row_a, &pair_dims),
PairingKey::from_row(&row_b, &pair_dims),
"rows differing only on a slicing dim must produce equal pairing keys",
);
}
#[test]
fn pairing_key_join_renders_legacy_shape() {
let row = make_filter_row("test_a", "scx_a", "1n2l", "SpinWait", Some("6.14"));
let key = PairingKey::from_row(&row, LEGACY_PAIRING_DIMS);
assert_eq!(
key.0.join("/"),
"test_a/1n2l/SpinWait",
"legacy-shape join must render the three-segment label",
);
}
#[test]
fn pairing_key_from_row_includes_kernel_commit_when_pairing() {
let mut row_some = make_filter_row("scn", "scx_a", "1n1l", "SpinWait", Some("6.14"));
row_some.kernel_commit = Some("kabcde7".to_string());
let mut row_none = make_filter_row("scn", "scx_a", "1n1l", "SpinWait", Some("6.14"));
row_none.kernel_commit = None;
let pair_dims = &[Dimension::KernelCommit];
let key_some = PairingKey::from_row(&row_some, pair_dims);
let key_none = PairingKey::from_row(&row_none, pair_dims);
assert_eq!(
key_some.0,
vec!["scn".to_string(), "kabcde7".to_string()],
"Some(kernel_commit) must occupy the second slot verbatim",
);
assert_eq!(
key_none.0,
vec!["scn".to_string(), String::new()],
"None kernel_commit must collapse to an empty slot per \
unwrap_or_default policy",
);
assert_ne!(
key_some, key_none,
"two rows differing on kernel_commit must produce \
distinct pairing keys when KernelCommit is a pairing dim",
);
let slice_dims = Dimension::pairing_dims(&[Dimension::KernelCommit]);
assert_eq!(
PairingKey::from_row(&row_some, &slice_dims),
PairingKey::from_row(&row_none, &slice_dims),
"rows differing only on the slicing dim (KernelCommit) \
must produce equal pairing keys",
);
}
#[test]
fn pairing_key_from_row_includes_run_source_when_pairing() {
let mut row_local = make_filter_row("scn", "scx_a", "1n1l", "SpinWait", Some("6.14"));
row_local.run_source = Some("local".to_string());
let mut row_ci = make_filter_row("scn", "scx_a", "1n1l", "SpinWait", Some("6.14"));
row_ci.run_source = Some("ci".to_string());
let mut row_none = make_filter_row("scn", "scx_a", "1n1l", "SpinWait", Some("6.14"));
row_none.run_source = None;
let pair_dims = &[Dimension::RunSource];
let key_local = PairingKey::from_row(&row_local, pair_dims);
let key_ci = PairingKey::from_row(&row_ci, pair_dims);
let key_none = PairingKey::from_row(&row_none, pair_dims);
assert_eq!(
key_local.0,
vec!["scn".to_string(), "local".to_string()],
"Some(run_source) must occupy the second slot verbatim",
);
assert_eq!(key_ci.0, vec!["scn".to_string(), "ci".to_string()]);
assert_eq!(
key_none.0,
vec!["scn".to_string(), String::new()],
"None run_source must collapse to an empty slot per \
unwrap_or_default policy",
);
assert_ne!(
key_local, key_ci,
"two rows differing on run_source must produce \
distinct pairing keys when Source is a pairing dim",
);
let slice_dims = Dimension::pairing_dims(&[Dimension::RunSource]);
assert_eq!(
PairingKey::from_row(&row_local, &slice_dims),
PairingKey::from_row(&row_ci, &slice_dims),
"rows differing only on the slicing dim (Source) must \
produce equal pairing keys",
);
}
#[test]
fn pairing_key_from_row_includes_cpu_budget_when_pairing() {
let mut row_4 = make_filter_row("scn", "scx_a", "1n1l", "SpinWait", Some("6.14"));
row_4.cpu_budget = Some(4);
let mut row_32 = make_filter_row("scn", "scx_a", "1n1l", "SpinWait", Some("6.14"));
row_32.cpu_budget = Some(32);
let mut row_none = make_filter_row("scn", "scx_a", "1n1l", "SpinWait", Some("6.14"));
row_none.cpu_budget = None;
let pair_dims = &[Dimension::CpuBudget];
let key_4 = PairingKey::from_row(&row_4, pair_dims);
let key_32 = PairingKey::from_row(&row_32, pair_dims);
let key_none = PairingKey::from_row(&row_none, pair_dims);
assert_eq!(key_4.0, vec!["scn".to_string(), "4".to_string()]);
assert_eq!(key_32.0, vec!["scn".to_string(), "32".to_string()]);
assert_eq!(
key_none.0,
vec!["scn".to_string(), String::new()],
"None cpu_budget (a skip) collapses to an empty slot",
);
assert_ne!(
key_4, key_32,
"rows of different cpu_budget must NOT pair when CpuBudget pairs",
);
assert_ne!(
key_4, key_none,
"a budgeted row must not pair with a skip (None budget)",
);
let slice_dims = Dimension::pairing_dims(&[Dimension::CpuBudget]);
assert_eq!(
PairingKey::from_row(&row_4, &slice_dims),
PairingKey::from_row(&row_32, &slice_dims),
"rows differing only on the sliced dim (CpuBudget) must pair",
);
}
#[test]
fn pairing_key_from_row_strips_dirty_suffix_on_commit() {
let mut row_clean = make_filter_row("scn", "scx_a", "1n1l", "SpinWait", Some("6.14"));
row_clean.commit = Some("abc1234".to_string());
let mut row_dirty = make_filter_row("scn", "scx_a", "1n1l", "SpinWait", Some("6.14"));
row_dirty.commit = Some("abc1234-dirty".to_string());
let pair_dims = &[Dimension::ProjectCommit];
let key_clean = PairingKey::from_row(&row_clean, pair_dims);
let key_dirty = PairingKey::from_row(&row_dirty, pair_dims);
assert_eq!(
key_clean, key_dirty,
"clean `abc1234` and dirty `abc1234-dirty` must produce \
EQUAL pairing keys so the +mixed cohort machinery in \
group_and_average_by can surface their disagreement",
);
assert_eq!(
key_clean.0,
vec!["scn".to_string(), "abc1234".to_string()],
"key part must be the canonical un-suffixed hex",
);
}
#[test]
fn pairing_key_from_row_strips_dirty_suffix_on_kernel_commit() {
let mut row_clean = make_filter_row("scn", "scx_a", "1n1l", "SpinWait", Some("6.14"));
row_clean.kernel_commit = Some("def5678".to_string());
let mut row_dirty = make_filter_row("scn", "scx_a", "1n1l", "SpinWait", Some("6.14"));
row_dirty.kernel_commit = Some("def5678-dirty".to_string());
let pair_dims = &[Dimension::KernelCommit];
let key_clean = PairingKey::from_row(&row_clean, pair_dims);
let key_dirty = PairingKey::from_row(&row_dirty, pair_dims);
assert_eq!(
key_clean, key_dirty,
"clean and dirty kernel_commit at the same canonical \
hex must pair together",
);
assert_eq!(key_clean.0, vec!["scn".to_string(), "def5678".to_string()],);
}
#[test]
fn pairing_key_from_row_distinct_hexes_remain_distinct_under_strip() {
let mut row_a = make_filter_row("scn", "scx_a", "1n1l", "SpinWait", Some("6.14"));
row_a.commit = Some("aaa1111-dirty".to_string());
let mut row_b = make_filter_row("scn", "scx_a", "1n1l", "SpinWait", Some("6.14"));
row_b.commit = Some("bbb2222".to_string());
let pair_dims = &[Dimension::ProjectCommit];
let key_a = PairingKey::from_row(&row_a, pair_dims);
let key_b = PairingKey::from_row(&row_b, pair_dims);
assert_ne!(
key_a, key_b,
"distinct canonical hexes must remain distinct after the \
-dirty strip — only the suffix is stripped",
);
assert_eq!(key_a.0[1], "aaa1111");
assert_eq!(key_b.0[1], "bbb2222");
}
#[test]
fn pairing_key_from_row_none_commit_unchanged_under_strip() {
let mut row = make_filter_row("scn", "scx_a", "1n1l", "SpinWait", Some("6.14"));
row.commit = None;
row.kernel_commit = None;
let pair_dims = &[Dimension::ProjectCommit, Dimension::KernelCommit];
let key = PairingKey::from_row(&row, pair_dims);
assert_eq!(
key.0,
vec!["scn".to_string(), String::new(), String::new()],
"None commit and None kernel_commit must collapse to empty slots",
);
}
#[test]
fn render_side_label_empty_dims_yields_bare() {
let f = RowFilter::default();
assert_eq!(render_side_label(&f, &[], "A"), "A");
}
#[test]
fn render_side_label_single_value_dim() {
let f = RowFilter {
schedulers: vec!["scx_rusty".to_string()],
..RowFilter::default()
};
assert_eq!(
render_side_label(&f, &[Dimension::Scheduler], "A"),
"scx_rusty",
);
}
#[test]
fn render_side_label_vec_dim_short_joins_with_pipe() {
let f = RowFilter {
kernels: vec!["6.15".to_string(), "6.14".to_string()],
..RowFilter::default()
};
assert_eq!(
render_side_label(&f, &[Dimension::Kernel], "A"),
"6.14|6.15",
"≤3 values must join sorted with `|`",
);
}
#[test]
fn render_side_label_vec_dim_long_collapses_to_bare() {
let f = RowFilter {
kernels: vec![
"6.10".to_string(),
"6.11".to_string(),
"6.12".to_string(),
"6.13".to_string(),
"6.14".to_string(),
],
..RowFilter::default()
};
assert_eq!(
render_side_label(&f, &[Dimension::Kernel], "A"),
"A",
">3 values must collapse to the bare letter so the \
column header stays readable",
);
}
#[test]
fn render_side_label_multi_dim_joins_with_colon() {
let f = RowFilter {
kernels: vec!["6.14".to_string()],
schedulers: vec!["scx_rusty".to_string()],
..RowFilter::default()
};
assert_eq!(
render_side_label(&f, &[Dimension::Kernel, Dimension::Scheduler], "A"),
"6.14:scx_rusty",
);
}
#[test]
fn render_side_label_empty_dim_value_uses_bare() {
let f = RowFilter::default();
assert_eq!(
render_side_label(&f, &[Dimension::Kernel], "B"),
"B",
"empty Vec dim must fall back to the bare letter",
);
assert_eq!(
render_side_label(&f, &[Dimension::Scheduler], "B"),
"B",
"None Option dim must fall back to the bare letter",
);
}
#[test]
fn render_side_label_kernel_commit_arm_renders_filter_value() {
let f_one = RowFilter {
kernel_commits: vec!["kabcde7".to_string()],
..RowFilter::default()
};
assert_eq!(
render_side_label(&f_one, &[Dimension::KernelCommit], "A"),
"kabcde7",
"single kernel_commit value must render verbatim — \
a regression that read `filter.project_commits` instead of \
`filter.kernel_commits` would render `A` here because \
the project-commit field is empty",
);
let f_two = RowFilter {
kernel_commits: vec!["kbbb222".to_string(), "kaaa111".to_string()],
..RowFilter::default()
};
assert_eq!(
render_side_label(&f_two, &[Dimension::KernelCommit], "A"),
"kaaa111|kbbb222",
"≤3 kernel_commit values must join sorted with `|`",
);
let f_long = RowFilter {
kernel_commits: vec![
"k111".to_string(),
"k222".to_string(),
"k333".to_string(),
"k444".to_string(),
],
..RowFilter::default()
};
assert_eq!(
render_side_label(&f_long, &[Dimension::KernelCommit], "A"),
"A",
">3 kernel_commit values must collapse to the bare letter",
);
let f_empty = RowFilter::default();
assert_eq!(
render_side_label(&f_empty, &[Dimension::KernelCommit], "B"),
"B",
"empty kernel_commits Vec must fall back to the bare letter",
);
}
#[test]
fn render_side_label_source_arm_renders_filter_value() {
let f_one = RowFilter {
run_sources: vec!["local".to_string()],
..RowFilter::default()
};
assert_eq!(
render_side_label(&f_one, &[Dimension::RunSource], "A"),
"local",
"single run_source value must render verbatim — a \
regression that read another field would render `A` here",
);
let f_two = RowFilter {
run_sources: vec!["local".to_string(), "ci".to_string()],
..RowFilter::default()
};
assert_eq!(
render_side_label(&f_two, &[Dimension::RunSource], "A"),
"ci|local",
"≤3 run_source values must join sorted with `|`",
);
let f_long = RowFilter {
run_sources: vec![
"a".to_string(),
"b".to_string(),
"c".to_string(),
"d".to_string(),
],
..RowFilter::default()
};
assert_eq!(
render_side_label(&f_long, &[Dimension::RunSource], "A"),
"A",
">3 run_source values must collapse to the bare letter",
);
let f_empty = RowFilter::default();
assert_eq!(
render_side_label(&f_empty, &[Dimension::RunSource], "B"),
"B",
"empty run_sources Vec must fall back to the bare letter",
);
}
#[test]
fn zero_match_diagnostic_unknown_run_source_lists_present_values() {
let mut row_local = make_row("scn", "1n1l1c1t", true, 1.0);
row_local.run_source = Some("local".to_string());
let mut row_ci = make_row("scn", "1n1l1c1t", true, 1.0);
row_ci.run_source = Some("ci".to_string());
let rows = vec![row_local, row_ci];
let filter = RowFilter {
run_sources: vec!["loca".to_string()],
..Default::default()
};
let msg = zero_match_diagnostic("A", &filter, &rows, rows.len());
assert!(
msg.contains("--run-source `loca` not found"),
"must name the unknown value verbatim; got:\n{msg}",
);
assert!(
msg.contains("`ci`") && msg.contains("`local`"),
"must list distinct values present in the pool so the \
operator can correct the typo; got:\n{msg}",
);
assert!(
msg.contains("case-sensitive"),
"must mention case sensitivity (`ci` ≠ `CI`); got:\n{msg}",
);
}
#[test]
fn zero_match_diagnostic_unknown_cpu_budget_lists_present_values() {
let mut row4 = make_row("scn", "1n1l1c1t", true, 1.0);
row4.cpu_budget = Some(4);
let mut row32 = make_row("scn", "1n1l1c1t", true, 1.0);
row32.cpu_budget = Some(32);
let skip = make_row("scn", "1n1l1c1t", true, 1.0); let rows = vec![row4, row32, skip];
let filter = RowFilter {
cpu_budgets: vec!["64".to_string()],
..Default::default()
};
let msg = zero_match_diagnostic("A", &filter, &rows, rows.len());
assert!(
msg.contains("--cpu-budget `64` not found"),
"must name the unknown budget verbatim; got:\n{msg}",
);
assert!(
msg.contains("`4`") && msg.contains("`32`"),
"must list distinct budgets present in the pool; got:\n{msg}",
);
}
#[test]
fn zero_match_diagnostic_known_cpu_budget_does_not_fire_unknown_hint() {
let mut row = make_row("scn", "1n1l1c1t", true, 1.0);
row.cpu_budget = Some(4);
let rows = vec![row];
let filter = RowFilter {
cpu_budgets: vec!["4".to_string()],
..Default::default()
};
let msg = zero_match_diagnostic("A", &filter, &rows, rows.len());
assert!(
!msg.contains("--cpu-budget `4` not found"),
"a present budget must not fire the unknown-budget hint; got:\n{msg}",
);
}
#[test]
fn zero_match_diagnostic_unknown_run_source_with_empty_pool_explains_absence() {
let row = make_row("scn", "1n1l1c1t", true, 1.0);
let rows = vec![row];
let filter = RowFilter {
run_sources: vec!["ci".to_string()],
..Default::default()
};
let msg = zero_match_diagnostic("A", &filter, &rows, rows.len());
assert!(
msg.contains("--run-source `ci` not found"),
"must name the unknown value; got:\n{msg}",
);
assert!(
msg.contains("none — every row has `run_source: null`"),
"must explain the empty-distinct-values case rather than \
listing nothing; got:\n{msg}",
);
}
#[test]
fn zero_match_diagnostic_known_run_source_does_not_fire_unknown_hint() {
let mut row = make_row("scn", "1n1l1c1t", true, 1.0);
row.run_source = Some("local".to_string());
let rows = vec![row];
let filter = RowFilter {
run_sources: vec!["local".to_string()],
..Default::default()
};
let msg = zero_match_diagnostic("A", &filter, &rows, rows.len());
assert!(
!msg.contains("--run-source") || !msg.contains("not found"),
"must NOT fire the unknown-source hint when the value is \
present in the pool; got:\n{msg}",
);
}
#[test]
fn zero_match_diagnostic_project_commit_dirty_hint_fires() {
let mut row = make_row("scn", "1n1l1c1t", true, 1.0);
row.commit = Some("abcdef1-dirty".to_string());
let rows = vec![row];
let filter = RowFilter {
project_commits: vec!["abcdef1".to_string()],
..Default::default()
};
let msg = zero_match_diagnostic("A", &filter, &rows, rows.len());
assert!(
msg.contains("no rows match `--project-commit abcdef1`"),
"hint must name the unmatched filter value verbatim; \
got:\n{msg}",
);
assert!(
msg.contains("`abcdef1-dirty` exists in the pool"),
"hint must surface the dirty form found in the pool; \
got:\n{msg}",
);
assert!(
msg.contains("did you mean `--project-commit abcdef1-dirty`"),
"hint must propose the dirty form as the corrected flag; \
got:\n{msg}",
);
}
#[test]
fn zero_match_diagnostic_kernel_commit_dirty_hint_fires() {
let mut row = make_row("scn", "1n1l1c1t", true, 1.0);
row.kernel_commit = Some("kabcde7-dirty".to_string());
let rows = vec![row];
let filter = RowFilter {
kernel_commits: vec!["kabcde7".to_string()],
..Default::default()
};
let msg = zero_match_diagnostic("A", &filter, &rows, rows.len());
assert!(
msg.contains("no rows match `--kernel-commit kabcde7`"),
"hint must name the unmatched kernel_commit value verbatim; \
got:\n{msg}",
);
assert!(
msg.contains("`kabcde7-dirty` exists in the pool"),
"hint must surface the dirty form found in the pool; \
got:\n{msg}",
);
assert!(
msg.contains("did you mean `--kernel-commit kabcde7-dirty`"),
"hint must propose the dirty form as the corrected flag; \
got:\n{msg}",
);
}
#[test]
fn zero_match_diagnostic_list_values_redirect_when_commit_dim_populated() {
let row = make_row("scn", "1n1l1c1t", true, 1.0);
let rows = vec![row];
let filter = RowFilter {
project_commits: vec!["abcdef1".to_string()],
..Default::default()
};
let msg = zero_match_diagnostic("A", &filter, &rows, rows.len());
assert!(
msg.contains("cargo ktstr stats list-values"),
"must include the list-values redirect when commit \
dim filter is populated; got:\n{msg}",
);
let filter_kc = RowFilter {
kernel_commits: vec!["kabcde7".to_string()],
..Default::default()
};
let msg_kc = zero_match_diagnostic("A", &filter_kc, &rows, rows.len());
assert!(
msg_kc.contains("cargo ktstr stats list-values"),
"list-values redirect must also fire on the \
kernel_commits arm; got:\n{msg_kc}",
);
}
#[test]
fn zero_match_diagnostic_no_list_values_redirect_when_no_commit_dim() {
let row = make_row("scn", "1n1l1c1t", true, 1.0);
let rows = vec![row];
let filter = RowFilter {
schedulers: vec!["scx_alpha".to_string()],
..Default::default()
};
let msg = zero_match_diagnostic("A", &filter, &rows, rows.len());
assert!(
!msg.contains("cargo ktstr stats list-values"),
"list-values redirect must NOT fire when no commit-dim \
filter is populated; got:\n{msg}",
);
}
#[test]
fn sorted_run_entries_orders_by_mtime_descending() {
use std::thread::sleep;
use std::time::Duration;
let root = tempfile::TempDir::new().expect("tempdir");
let oldest = root.path().join("aaa_oldest");
let middle = root.path().join("mmm_middle");
let newest = root.path().join("zzz_newest");
std::fs::create_dir(&oldest).expect("mkdir oldest");
sleep(Duration::from_millis(100));
std::fs::create_dir(&middle).expect("mkdir middle");
sleep(Duration::from_millis(100));
std::fs::create_dir(&newest).expect("mkdir newest");
let rows = super::sorted_run_entries(root.path()).expect("sorted_run_entries must succeed");
let names: Vec<String> = rows
.iter()
.map(|(p, _, _, _)| {
p.file_name()
.expect("path must have a file_name")
.to_string_lossy()
.into_owned()
})
.collect();
assert_eq!(
names,
vec![
"zzz_newest".to_string(),
"mmm_middle".to_string(),
"aaa_oldest".to_string(),
],
"rows must be sorted by mtime descending: newest dir \
(`zzz_newest`) first, oldest dir (`aaa_oldest`) last. \
A regression that drops Reverse (mtime-ascending) or \
reverts to filename-only sort (lexical-ascending) \
would yield aaa, mmm, zzz — the OPPOSITE of the \
expected mtime-descending order — and would fail this \
assertion.",
);
}
#[test]
fn sorted_run_entries_empty_root_yields_empty_vec() {
let root = tempfile::TempDir::new().expect("tempdir");
let rows = super::sorted_run_entries(root.path()).expect("sorted_run_entries must succeed");
assert!(
rows.is_empty(),
"empty root must yield empty vec; got {rows:?}",
);
}
#[test]
fn sorted_run_entries_skips_non_directory_entries() {
let root = tempfile::TempDir::new().expect("tempdir");
std::fs::create_dir(root.path().join("a_dir")).expect("mkdir");
std::fs::write(root.path().join("a_file"), b"not a run dir").expect("write file");
let rows = super::sorted_run_entries(root.path()).expect("sorted_run_entries must succeed");
let names: Vec<String> = rows
.iter()
.map(|(p, _, _, _)| {
p.file_name()
.expect("path must have a file_name")
.to_string_lossy()
.into_owned()
})
.collect();
assert_eq!(
names,
vec!["a_dir".to_string()],
"only the subdirectory must be returned; file entries are skipped",
);
}
#[test]
fn sorted_run_entries_skips_dotfile_subdirectories() {
let root = tempfile::TempDir::new().expect("tempdir");
std::fs::create_dir(root.path().join("real-run")).expect("mkdir");
std::fs::create_dir(root.path().join(".locks")).expect("mkdir .locks");
std::fs::create_dir(root.path().join(".cache")).expect("mkdir .cache");
let rows = super::sorted_run_entries(root.path()).expect("sorted_run_entries must succeed");
let names: Vec<String> = rows
.iter()
.map(|(p, _, _, _)| {
p.file_name()
.expect("path must have a file_name")
.to_string_lossy()
.into_owned()
})
.collect();
assert_eq!(
names,
vec!["real-run".to_string()],
"dotfile-prefixed subdirs (.locks, .cache) must be filtered \
out of the run listing; only `real-run` may surface",
);
}
#[test]
fn sorted_run_entries_extracts_arch_from_first_sidecar() {
let root = tempfile::TempDir::new().expect("tempdir");
let run_dir = root.path().join("run-with-arch");
std::fs::create_dir(&run_dir).expect("mkdir run dir");
let mut sc = crate::test_support::SidecarResult::test_fixture();
sc.host = Some(crate::host_context::HostContext::test_fixture());
std::fs::write(
run_dir.join("t-0000000000000000.ktstr.json"),
serde_json::to_string(&sc).expect("serialize fixture"),
)
.expect("write sidecar");
let rows = super::sorted_run_entries(root.path()).expect("sorted_run_entries must succeed");
assert_eq!(rows.len(), 1, "one run dir must yield one row");
let (_, _, _, arch) = &rows[0];
assert_eq!(
arch.as_deref(),
Some("x86_64"),
"arch must come from host.arch on the first sidecar — \
test_fixture populates `Some(\"x86_64\")`",
);
}
#[test]
fn sorted_run_entries_arch_none_when_no_host() {
let root = tempfile::TempDir::new().expect("tempdir");
let run_dir = root.path().join("run-no-host");
std::fs::create_dir(&run_dir).expect("mkdir run dir");
let sc = crate::test_support::SidecarResult::test_fixture();
std::fs::write(
run_dir.join("t-0000000000000000.ktstr.json"),
serde_json::to_string(&sc).expect("serialize fixture"),
)
.expect("write sidecar");
let rows = super::sorted_run_entries(root.path()).expect("sorted_run_entries must succeed");
assert_eq!(rows.len(), 1, "one run dir must yield one row");
let (_, _, _, arch) = &rows[0];
assert!(
arch.is_none(),
"no host-populated sidecar must yield None arch; got {arch:?}",
);
}