#!/usr/bin/env python3 import os import subprocess import sys from pathlib import Path ROOT = Path(".").resolve() TEST_DIR = ROOT / "tests" CPP_EXTENSIONS = {".c", ".cc", ".cpp", ".cxx", ".h", ".hh", ".hpp", ".hxx"} TEST_PATTERNS = [ "test_{name}.cpp", "test_{name}.h", "{name}_test.cpp", "{name}_test.h", "{name}test.cpp", "{name}test.h", "{name}gtest.cpp", "{name}gtest.h", ] def run(cmd: str) -> str: return subprocess.check_output(cmd, shell=True, text=True).strip() def changed_files(): target = os.environ.get("CHANGE_TARGET", "origin/main") out = run(f"git diff --name-only {target}...HEAD") return [line.strip() for line in out.splitlines() if line.strip()] def is_cpp_file(path: Path) -> bool: return path.suffix.lower() in CPP_EXTENSIONS def require_clang_format(): if not (ROOT / ".clang-format").exists(): print("ERROR: .clang-format fehlt im Repository-Root.") sys.exit(1) def check_format(path: Path): subprocess.check_call(f"clang-format --dry-run --Werror '{path}'", shell=True) def candidate_names(path: Path): stem = path.stem.lower() names = {stem} for suffix in ["_p", "_impl", "_private", "_priv", "_ui"]: if stem.endswith(suffix): names.add(stem[:-len(suffix)]) return sorted(n for n in names if n) def has_related_test(path: Path) -> bool: if not TEST_DIR.exists(): return False test_files = [p for p in TEST_DIR.rglob("*") if p.is_file()] test_names = [p.name.lower() for p in test_files] for name in candidate_names(path): for pattern in TEST_PATTERNS: if pattern.format(name=name).lower() in test_names: return True for test_name in test_names: if name in test_name and ("test" in test_name or "gtest" in test_name): return True return False def main(): require_clang_format() files = changed_files() if not files: print("Keine geänderten Dateien erkannt.") return for rel in files: path = ROOT / rel if not path.exists(): continue if is_cpp_file(path): print(f"Prüfe: {rel}") try: check_format(path) except subprocess.CalledProcessError: print(f"ERROR: Formatfehler in {rel}") sys.exit(1) if not has_related_test(path): print(f"ERROR: Kein passender Test unter tests/ für {rel} gefunden.") sys.exit(1) print("Policy-Checks erfolgreich.") if __name__ == "__main__": main()