use core::fmt;
use crate::verdict::Verdict;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DivergenceClass {
NotYetImplemented,
IntentionalDeviation,
UpstreamPatchSkew,
ConformanceRelevanceUnclassified,
}
impl DivergenceClass {
#[must_use]
pub fn is_burn_down_debt(self) -> bool {
!matches!(self, Self::IntentionalDeviation)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Expect {
Match,
KnownDiverge {
class: DivergenceClass,
note: &'static str,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Outcome {
Held,
Tracked {
class: DivergenceClass,
},
Regressed {
divergences: usize,
},
Graduated {
stale_note: &'static str,
},
OracleLost,
}
impl Outcome {
#[must_use]
pub fn passes(&self) -> bool {
matches!(self, Self::Held | Self::Tracked { .. })
}
#[must_use]
pub fn is_failure(&self) -> bool {
!self.passes()
}
}
impl fmt::Display for Outcome {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Held => write!(f, "HELD"),
Self::Tracked { class } => write!(f, "TRACKED ({class})"),
Self::Regressed { divergences } => write!(
f,
"REGRESSED: declared Match but observed {divergences} divergence(s)"
),
Self::Graduated { stale_note } => write!(
f,
"GRADUATED: declared KnownDiverge ({stale_note}) but observed parity \
— delete the waiver in the same commit as the fix"
),
Self::OracleLost => write!(f, "ORACLE LOST: the reference could not be reached"),
}
}
}
impl fmt::Display for DivergenceClass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::NotYetImplemented => "not-yet-implemented",
Self::IntentionalDeviation => "intentional-deviation",
Self::UpstreamPatchSkew => "upstream-patch-skew",
Self::ConformanceRelevanceUnclassified => "conformance-relevance-unclassified",
}
)
}
}
#[must_use]
pub fn adjudicate(expect: &Expect, verdict: &Verdict) -> Outcome {
match (expect, verdict) {
(_, Verdict::ReferenceUnreachable) => Outcome::OracleLost,
(Expect::Match, Verdict::Parity(_)) => Outcome::Held,
(Expect::Match, Verdict::Divergent(d)) => Outcome::Regressed {
divergences: d.len(),
},
(Expect::KnownDiverge { class, .. }, Verdict::Divergent(_)) => {
Outcome::Tracked { class: *class }
}
(Expect::KnownDiverge { note, .. }, Verdict::Parity(_)) => {
Outcome::Graduated { stale_note: note }
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MatrixReport {
pub rows: usize,
pub held: usize,
pub tracked: usize,
pub failures: Vec<(String, Outcome)>,
}
impl MatrixReport {
#[must_use]
pub fn from_rows(named: impl IntoIterator<Item = (String, Outcome)>) -> Self {
let mut report = Self {
rows: 0,
held: 0,
tracked: 0,
failures: Vec::new(),
};
for (name, outcome) in named {
report.rows += 1;
match &outcome {
Outcome::Held => report.held += 1,
Outcome::Tracked { .. } => report.tracked += 1,
_ => {}
}
if outcome.is_failure() {
report.failures.push((name, outcome));
}
}
report
}
#[must_use]
pub fn is_green(&self) -> bool {
self.rows > 0 && self.failures.is_empty()
}
}
impl fmt::Display for MatrixReport {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"junkyo: {} rows — {} held, {} tracked, {} failed",
self.rows,
self.held,
self.tracked,
self.failures.len()
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::path::JsonPath;
use crate::verdict::{Divergence, ParityWitness, Side, Verdict};
fn parity() -> Verdict {
Verdict::Parity(ParityWitness::new())
}
fn divergent() -> Verdict {
Verdict::Divergent(vec![Divergence::MissingResource {
path: JsonPath::root(),
present_on: Side::K3s,
}])
}
fn known() -> Expect {
Expect::KnownDiverge {
class: DivergenceClass::NotYetImplemented,
note: "Table conversion absent",
}
}
#[test]
fn match_plus_parity_holds() {
let v = parity();
assert_eq!(adjudicate(&Expect::Match, &v), Outcome::Held);
assert!(adjudicate(&Expect::Match, &v).passes());
}
#[test]
fn match_plus_divergence_is_a_regression() {
let out = adjudicate(&Expect::Match, &divergent());
assert!(matches!(out, Outcome::Regressed { divergences: 1 }));
assert!(!out.passes(), "a regression MUST fail the gate");
}
#[test]
fn known_diverge_plus_divergence_is_tracked() {
let out = adjudicate(&known(), &divergent());
assert!(out.passes(), "known debt does not fail the gate");
assert!(matches!(
out,
Outcome::Tracked {
class: DivergenceClass::NotYetImplemented
}
));
}
#[test]
fn known_diverge_plus_parity_is_a_failure() {
let out = adjudicate(&known(), &parity());
assert!(
!out.passes(),
"a KnownDiverge that starts matching MUST fail — otherwise the \
ledger silently over-reports debt forever"
);
assert!(out.to_string().contains("GRADUATED"));
}
#[test]
fn unreachable_oracle_never_reads_as_agreement() {
for e in [Expect::Match, known()] {
let out = adjudicate(&e, &Verdict::ReferenceUnreachable);
assert_eq!(out, Outcome::OracleLost);
assert!(
!out.passes(),
"an unreachable oracle is never a silent skip"
);
}
}
#[test]
fn empty_corpus_is_not_green() {
let r = MatrixReport::from_rows(std::iter::empty());
assert_eq!(r.rows, 0);
assert!(
!r.is_green(),
"an empty matrix must FAIL — a corpus that discovered nothing is \
indistinguishable from one whose rows were all deleted"
);
}
#[test]
fn report_carries_its_denominator() {
let r = MatrixReport::from_rows([
("a".to_string(), Outcome::Held),
(
"b".to_string(),
Outcome::Tracked {
class: DivergenceClass::NotYetImplemented,
},
),
]);
assert!(r.is_green());
assert_eq!((r.rows, r.held, r.tracked), (2, 1, 1));
assert!(
r.to_string().contains("2 rows"),
"denominator must be visible"
);
}
#[test]
fn a_single_failure_reddens_the_whole_matrix() {
let r = MatrixReport::from_rows([
("ok".to_string(), Outcome::Held),
("bad".to_string(), Outcome::Regressed { divergences: 3 }),
]);
assert!(!r.is_green());
assert_eq!(r.failures.len(), 1);
assert!(r.failures[0].1.to_string().contains("REGRESSED"));
}
#[test]
fn intentional_deviation_is_the_only_non_debt_class() {
assert!(!DivergenceClass::IntentionalDeviation.is_burn_down_debt());
for c in [
DivergenceClass::NotYetImplemented,
DivergenceClass::UpstreamPatchSkew,
DivergenceClass::ConformanceRelevanceUnclassified,
] {
assert!(c.is_burn_down_debt());
}
}
}