use auditmysite::audit::AuditCatalog;
use serde::Serialize;
use std::fs;
use std::path::{Path, PathBuf};
#[derive(Debug, Serialize)]
struct CoverageEntry {
module_id: String,
module_label: String,
pdf_reference_count: usize,
docs_reference_count: usize,
schema_reference_count: usize,
fixture_reference_count: usize,
}
fn repo_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
}
fn concat_rs_files(dir: &Path) -> String {
let mut combined = String::new();
let Ok(entries) = fs::read_dir(dir) else {
return combined;
};
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
combined.push_str(&concat_rs_files(&path));
} else if path.extension().and_then(|e| e.to_str()) == Some("rs") {
if let Ok(text) = fs::read_to_string(&path) {
combined.push_str(&text);
}
}
}
combined
}
fn concat_files_in(dir: &Path) -> String {
let mut combined = String::new();
let Ok(entries) = fs::read_dir(dir) else {
return combined;
};
for entry in entries.flatten() {
let path = entry.path();
if path.is_file() {
if let Ok(text) = fs::read_to_string(&path) {
combined.push_str(&text);
}
}
}
combined
}
fn build_matrix() -> Vec<CoverageEntry> {
let root = repo_root();
let pdf_text = concat_rs_files(&root.join("src/output/pdf"));
let docs_text = fs::read_to_string(root.join("docs/OUTPUT_CONTRACT.md")).unwrap_or_default();
let schema_text = [
"docs/json-report.schema.json",
"docs/json-batch-report.schema.json",
]
.iter()
.map(|f| fs::read_to_string(root.join(f)).unwrap_or_default())
.collect::<Vec<_>>()
.join("\n");
let fixtures_text = concat_rs_files(&root.join("tests"))
+ &concat_files_in(&root.join("tests/fixtures"))
+ &concat_files_in(&root.join("tests/wcag_fixtures"));
AuditCatalog::standard()
.iter()
.map(|module| {
let id = module.id();
CoverageEntry {
module_id: id.to_string(),
module_label: module.label().to_string(),
pdf_reference_count: pdf_text.matches(id).count(),
docs_reference_count: docs_text.matches(id).count(),
schema_reference_count: schema_text.matches(id).count(),
fixture_reference_count: fixtures_text.matches(id).count(),
}
})
.collect()
}
#[test]
#[ignore = "run on demand to regenerate reports/coverage_matrix.json"]
fn export_coverage_matrix() {
let matrix = build_matrix();
let json = serde_json::to_string_pretty(&matrix).expect("serialize coverage matrix");
let out_dir = repo_root().join("reports");
fs::create_dir_all(&out_dir).expect("create reports/ dir");
let out_path = out_dir.join("coverage_matrix.json");
fs::write(&out_path, &json).expect("write coverage_matrix.json");
println!(
"Wrote {} module coverage entries to {}",
matrix.len(),
out_path.display()
);
}
#[test]
fn every_catalog_module_has_a_unique_nonempty_id() {
let catalog = AuditCatalog::standard();
let mut seen = std::collections::HashSet::new();
for module in catalog.iter() {
let id = module.id();
assert!(!id.is_empty(), "module id must not be empty");
assert!(seen.insert(id), "duplicate module id: {id}");
}
}