| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102 |
- #!/usr/bin/env python3
- """Compile the TP3 example corpus in Sources/Dezip/pascsrc.
- The corpus is deliberately outside the build tree, so it is easy for a compiler
- change to regress it without any of the fixture suites noticing. This script
- runs each source in its own compiletest process (one crashing file must not hide
- the rest), prints a compact result, and requires the whole corpus to compile.
- python3 tests/corpus_check.py
- python3 tests/corpus_check.py --expected 71
- python3 tests/corpus_check.py --verbose
- A missing Sources tree is a skip rather than a failure: the corpus is user
- material and is not tracked by git. Once the tree exists, both the file count
- and every verdict are checked, so adding or losing a source is visible too.
- """
- import argparse
- import pathlib
- import re
- import subprocess
- import sys
- HERE = pathlib.Path(__file__).resolve().parent
- SHELL = HERE.parent
- SOURCES = SHELL.parent / "Sources"
- CORPUS = SOURCES / "Dezip" / "pascsrc"
- COMPILETEST = SHELL / "compiletest"
- BUILD = HERE / "run_compile_tests.sh"
- def build_compiletest() -> bool:
- """Build the harness through its single, timestamp-checked recipe."""
- r = subprocess.run([str(BUILD), "--build-only"], cwd=SHELL,
- capture_output=True, text=True)
- if r.returncode != 0:
- print(r.stdout, end="")
- print(r.stderr, end="", file=sys.stderr)
- return False
- return True
- def compile_one(path: pathlib.Path) -> tuple[bool, str]:
- try:
- r = subprocess.run([str(COMPILETEST)],
- input=str(path) + "\n",
- capture_output=True, text=True, timeout=5)
- except subprocess.TimeoutExpired:
- return False, "timeout"
- out = r.stdout + r.stderr
- if re.search(r"\bOK\s+code=\d+\s+data=\d+", out):
- return True, "OK"
- m = re.search(r"\bERROR\s+(\d+)\s+at pos\s+(\d+)", out)
- if m:
- return False, "ERROR %s at pos %s" % (m.group(1), m.group(2))
- if "ERROR" in out:
- return False, out.strip().splitlines()[-1]
- return False, "no compile result (exit %d)" % r.returncode
- def main() -> int:
- ap = argparse.ArgumentParser(description=__doc__)
- ap.add_argument("--expected", type=int, default=71,
- help="required number of .pas files (default: 71)")
- ap.add_argument("--verbose", action="store_true",
- help="print every passing source as well as failures")
- args = ap.parse_args()
- if not CORPUS.is_dir():
- print("SKIP: %s does not exist" % CORPUS)
- return 0
- if not build_compiletest():
- print("FAIL: compiletest did not build")
- return 1
- files = sorted(CORPUS.rglob("*.[pP][aA][sS]"))
- if len(files) != args.expected:
- print("FAIL: expected %d .pas files, found %d in %s"
- % (args.expected, len(files), CORPUS))
- for p in files:
- print(" " + str(p.relative_to(SOURCES)))
- return 1
- failed = []
- for p in files:
- ok, detail = compile_one(p)
- rel = p.relative_to(SOURCES)
- if ok:
- if args.verbose:
- print(" PASS %s" % rel)
- else:
- failed.append((rel, detail))
- print(" FAIL %s %s" % (rel, detail))
- print("\ncorpus: %d passed, %d failed (of %d)"
- % (len(files) - len(failed), len(failed), len(files)))
- return 1 if failed else 0
- if __name__ == "__main__":
- sys.exit(main())
|