use std::path::Path;
const CONFORMANCE_ROOT_NAME: &str = "conformance";
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct TargetCensus {
pub harn_files: usize,
pub conformance_fixtures: usize,
}
impl TargetCensus {
fn add(&mut self, other: TargetCensus) {
self.harn_files += other.harn_files;
self.conformance_fixtures += other.conformance_fixtures;
}
}
fn is_conformance_fixture(path: &Path) -> bool {
path.with_extension("expected").is_file()
}
pub(crate) fn census_target(path: &Path) -> TargetCensus {
let mut census = TargetCensus::default();
if path.is_file() {
if path.extension().is_some_and(|ext| ext == "harn") {
census.harn_files = 1;
if is_conformance_fixture(path) {
census.conformance_fixtures = 1;
}
}
return census;
}
for entry in ignore::WalkBuilder::new(path)
.hidden(true)
.build()
.flatten()
{
let entry_path = entry.path();
if !entry_path.is_file() || entry_path.extension().is_none_or(|ext| ext != "harn") {
continue;
}
census.harn_files += 1;
if is_conformance_fixture(entry_path) {
census.conformance_fixtures += 1;
}
}
census
}
pub(crate) fn census_targets<P: AsRef<Path>>(paths: &[P]) -> TargetCensus {
let mut total = TargetCensus::default();
for path in paths {
total.add(census_target(path.as_ref()));
}
total
}
pub(crate) fn is_inside_conformance_suite(path: &Path) -> bool {
let absolute = path
.canonicalize()
.unwrap_or_else(|_| std::env::current_dir().unwrap_or_default().join(path));
absolute
.components()
.any(|component| component.as_os_str() == CONFORMANCE_ROOT_NAME)
}
pub(crate) fn conformance_fixture_refusal(
path_strs: &[String],
census: &TargetCensus,
) -> Option<String> {
if census.conformance_fixtures == 0 {
return None;
}
if !path_strs
.iter()
.any(|path| is_inside_conformance_suite(Path::new(path)))
{
return None;
}
Some(format!(
"{} of the {} .harn file(s) under {} are conformance fixtures, which are driven by their `.expected` file and are never executed by `harn test <path>`. This invocation would report a confident verdict over the remainder and say nothing about the rest. Run `harn test conformance` for the whole suite, or `harn test conformance --filter <name>` for one case.",
census.conformance_fixtures,
census.harn_files,
path_strs.join(", "),
))
}
pub(crate) fn coverage_line(path_strs: &[String], census: &TargetCensus, cases: usize) -> String {
format!(
"test targets: ran {cases} case(s) from {} discoverable .harn file(s) under {}",
census.harn_files,
path_strs.join(", "),
)
}
#[cfg(test)]
mod tests {
use super::*;
fn write(path: &Path, contents: &str) {
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent).expect("create fixture dir");
}
std::fs::write(path, contents).expect("write fixture");
}
#[test]
fn a_directory_inside_the_conformance_suite_is_refused_by_name() {
let dir = tempfile::tempdir().expect("tempdir");
let target = dir.path().join("conformance/tests");
write(&target.join("one.harn"), "// fixture\n");
write(&target.join("one.expected"), "ok\n");
write(&target.join("two.harn"), "// fixture\n");
write(&target.join("two.expected"), "ok\n");
write(&target.join("real_test.harn"), "// pipeline\n");
let census = census_target(&target);
assert_eq!(census.harn_files, 3);
assert_eq!(census.conformance_fixtures, 2);
let refusal =
conformance_fixture_refusal(&[target.to_string_lossy().into_owned()], &census)
.expect("a target inside the conformance suite must be refused");
assert!(refusal.starts_with("2 of the 3"));
assert!(refusal.contains("harn test conformance"));
}
#[test]
fn an_ordinary_test_directory_is_not_refused() {
let dir = tempfile::tempdir().expect("tempdir");
let target = dir.path().join("tests");
write(&target.join("a_test.harn"), "// pipeline\n");
write(&target.join("b_test.harn"), "// pipeline\n");
let census = census_target(&target);
assert_eq!(census.harn_files, 2);
assert_eq!(census.conformance_fixtures, 0);
assert_eq!(
conformance_fixture_refusal(&[target.to_string_lossy().into_owned()], &census),
None
);
}
#[test]
fn a_scaffolded_template_with_an_expected_sibling_still_runs() {
let dir = tempfile::tempdir().expect("tempdir");
let target = dir.path().join("assets/persona-templates/sweeper/tests");
write(&target.join("smoke.harn"), "// pipeline\n");
write(&target.join("smoke.expected"), "ok\n");
let census = census_target(&target);
assert_eq!(census.conformance_fixtures, 1);
assert_eq!(
conformance_fixture_refusal(&[target.to_string_lossy().into_owned()], &census),
None
);
}
#[test]
fn a_single_conformance_file_is_refused_too() {
let dir = tempfile::tempdir().expect("tempdir");
let target = dir.path().join("conformance/tests/one.harn");
write(&target, "// fixture\n");
write(&target.with_extension("expected"), "ok\n");
let census = census_target(&target);
assert_eq!(census.harn_files, 1);
assert_eq!(census.conformance_fixtures, 1);
assert!(
conformance_fixture_refusal(&[target.to_string_lossy().into_owned()], &census)
.is_some()
);
}
#[test]
fn the_coverage_line_names_both_numbers() {
let census = TargetCensus {
harn_files: 2165,
conformance_fixtures: 0,
};
let line = coverage_line(&["conformance/tests".to_string()], &census, 24);
assert!(line.contains("ran 24 case(s)"));
assert!(line.contains("2165 discoverable"));
}
}