#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Tally {
pub passed: u64,
pub failed: u64,
pub ignored: u64,
}
#[must_use]
pub fn tally(output: &str) -> Tally {
let mut total = Tally::default();
for line in output.lines() {
let Some(rest) = line.trim_start().strip_prefix("test result:") else {
continue;
};
let fields: Vec<&str> = rest.split_whitespace().collect();
for pair in fields.windows(2) {
let (Ok(count), label) = (pair[0].parse::<u64>(), pair[1].trim_end_matches(';')) else {
continue;
};
match label {
"passed" => total.passed += count,
"failed" => total.failed += count,
"ignored" => total.ignored += count,
_ => {}
}
}
}
total
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IgnoredVerdict {
Accounted { ignored: u64 },
Uncovered { ignored: u64 },
Mismatch { ignored: u64, covered: u64 },
}
impl IgnoredVerdict {
#[must_use]
pub fn is_red(self) -> bool {
!matches!(self, IgnoredVerdict::Accounted { .. })
}
#[must_use]
pub fn ignored_count(self) -> u64 {
match self {
IgnoredVerdict::Accounted { ignored }
| IgnoredVerdict::Uncovered { ignored }
| IgnoredVerdict::Mismatch { ignored, .. } => ignored,
}
}
#[must_use]
pub fn message(self) -> String {
match self {
IgnoredVerdict::Accounted { ignored } => {
format!(
"{ignored} ignored, all of them run by the declared coverer. Accounted for."
)
}
IgnoredVerdict::Uncovered { ignored } => format!(
"!! {ignored} ignored tests, and no command declares \
covers_ignored_of for them. Something is #[ignore]d and NEVER \
RUN. This is the exact defect the accounting exists to \
prevent — find it before citing this floor."
),
IgnoredVerdict::Mismatch { ignored, covered } => format!(
"!! {ignored} ignored tests but the declared coverer ran \
{covered}. The declared link no longer describes what runs, so \
this floor is not evidence — reconcile it before citing it."
),
}
}
}
#[must_use]
pub fn reconcile(ignored: u64, covered: Option<u64>) -> IgnoredVerdict {
match covered {
_ if ignored == 0 => IgnoredVerdict::Accounted { ignored: 0 },
None => IgnoredVerdict::Uncovered { ignored },
Some(covered) if covered == ignored => IgnoredVerdict::Accounted { ignored },
Some(covered) => IgnoredVerdict::Mismatch { ignored, covered },
}
}