0.0.1-alpha.5.fix.1
This commit is contained in:
82
scripts/audit_rust_test_layout.py
Normal file
82
scripts/audit_rust_test_layout.py
Normal file
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python3
|
||||
# file: scripts/audit_rust_test_layout.py
|
||||
# version: 1
|
||||
|
||||
"""Audit the Knowledge Rust unit/integration test layout."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
|
||||
_TEST_ATTRIBUTE = re.compile(r"(?m)^\s*#\s*\[\s*test\s*\]")
|
||||
_PATH_ATTRIBUTE = re.compile(r'#\s*\[\s*path\s*=\s*"([^"]+)"\s*\]')
|
||||
_FORBIDDEN_MODULE = re.compile(r"(?m)^\s*mod\s+unit_tests\s*;")
|
||||
|
||||
|
||||
def audit_crate(crate_root: pathlib.Path) -> list[str]:
|
||||
"""Return layout violations for one crate."""
|
||||
errors: list[str] = []
|
||||
src_root = crate_root / "src"
|
||||
referenced_unit_tests: set[pathlib.Path] = set()
|
||||
|
||||
if src_root.is_dir():
|
||||
for source in sorted(src_root.rglob("*.rs")):
|
||||
text = source.read_text(encoding="utf-8")
|
||||
relative = source.relative_to(crate_root)
|
||||
|
||||
if _TEST_ATTRIBUTE.search(text):
|
||||
errors.append(f"{crate_root.name}/{relative}: #[test] must live under unit_tests/")
|
||||
|
||||
if source.name in {"tests.rs", "unit_tests.rs"}:
|
||||
errors.append(f"{crate_root.name}/{relative}: test implementation file must not live under src/")
|
||||
|
||||
if _FORBIDDEN_MODULE.search(text):
|
||||
errors.append(f"{crate_root.name}/{relative}: use #[path = \"../unit_tests/...\"] instead of mod unit_tests;")
|
||||
|
||||
for match in _PATH_ATTRIBUTE.finditer(text):
|
||||
candidate = (source.parent / match.group(1)).resolve()
|
||||
try:
|
||||
candidate.relative_to((crate_root / "unit_tests").resolve())
|
||||
except ValueError:
|
||||
continue
|
||||
referenced_unit_tests.add(candidate)
|
||||
|
||||
unit_root = crate_root / "unit_tests"
|
||||
if unit_root.is_dir():
|
||||
for test_file in sorted(unit_root.rglob("*.rs")):
|
||||
resolved = test_file.resolve()
|
||||
if resolved not in referenced_unit_tests:
|
||||
relative = test_file.relative_to(crate_root)
|
||||
errors.append(f"{crate_root.name}/{relative}: unit test file is not attached by any #[path] module")
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def main() -> int:
|
||||
"""Run the audit."""
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("root", nargs="?", default="crates")
|
||||
arguments = parser.parse_args()
|
||||
|
||||
root = pathlib.Path(arguments.root)
|
||||
errors: list[str] = []
|
||||
crate_count = 0
|
||||
|
||||
for manifest in sorted(root.glob("*/Cargo.toml")):
|
||||
crate_count += 1
|
||||
errors.extend(audit_crate(manifest.parent))
|
||||
|
||||
if errors:
|
||||
for error in errors:
|
||||
print(error, file=sys.stderr)
|
||||
return 1
|
||||
|
||||
print(f"Rust test-layout audit: clean ({crate_count} crate(s))")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user