use std::fs;
use std::path::{Path, PathBuf};
fn repo_root() -> PathBuf {
PathBuf::from(std::env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("conform is inside workspace")
.to_path_buf()
}
fn read_lines_filtered(path: &Path) -> Vec<String> {
let content = fs::read_to_string(path).unwrap_or_default();
content
.lines()
.map(|line| {
let trimmed = line.split('#').next().unwrap_or(line);
trimmed.trim().to_string()
})
.filter(|line| !line.is_empty())
.collect()
}
fn is_covered_by_pattern(file: &str, pattern: &str) -> bool {
if pattern.ends_with("/**") {
let prefix = &pattern[..pattern.len() - 3];
if file == prefix || file.starts_with(&format!("{}/", prefix)) {
return true;
}
} else if pattern.ends_with("*.rs") {
let prefix = &pattern[..pattern.len() - 4];
if file.starts_with(prefix) && file.ends_with(".rs") {
return true;
}
} else if file == pattern {
return true;
}
false
}
fn is_covered(file: &str, patterns: &[String]) -> bool {
patterns.iter().any(|p| is_covered_by_pattern(file, p))
}
fn walk_tests(dir: &Path, files: &mut Vec<PathBuf>) {
if !dir.is_dir() {
return;
}
for entry in fs::read_dir(dir).unwrap() {
let entry = entry.unwrap();
let path = entry.path();
if path.is_dir() {
walk_tests(&path, files);
} else if path.extension().and_then(|s| s.to_str()) == Some("rs") {
files.push(path);
}
}
}
#[test]
fn append_only_paths_cover_all_test_files() {
let root = repo_root();
let patterns_file = root.join("conform/.append-only-paths");
let allowlist_file = root.join("conform/tests/.append-only-allowlist.txt");
let tests_dir = root.join("conform/tests");
let patterns = read_lines_filtered(&patterns_file);
let allowlist: Vec<String> = read_lines_filtered(&allowlist_file);
let mut files = Vec::new();
walk_tests(&tests_dir, &mut files);
let mut failures = Vec::new();
for path in files {
let rel = path
.strip_prefix(&root)
.unwrap()
.to_string_lossy()
.replace("\\", "/");
let content = fs::read_to_string(&path).unwrap_or_default();
let has_assert = content.contains("assert_") || content.contains("#[test]");
if !has_assert {
continue;
}
if is_covered(&rel, &patterns) {
continue;
}
if allowlist.contains(&rel) {
continue;
}
failures.push(rel);
}
if !failures.is_empty() {
let mut msg = String::from(
"append_only_paths_completeness: FAILED — the following test files are not append-only:\n",
);
for f in &failures {
msg.push_str(&format!(
" - Fix: add {} to conform/.append-only-paths or move to conform/tests/regression/\n",
f
));
}
panic!("{}", msg);
}
}