#!/usr/bin/env bash # # Regression tests for reproducible getpath.py frozen-header generation. set -u REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" GENERATOR="$REPO_ROOT/scripts/generate-getpath-frozen.sh" TMP_ROOT="$REPO_ROOT/build/test-getpath-frozen" OUT_HEADER="$TMP_ROOT/cpython/Python/frozen_modules/getpath.h" DIRECT_HEADER="$TMP_ROOT/direct/getpath.h" fail=0 pass() { echo "ok - $1"; } bad() { echo "not ok - $1"; fail=1; } rm -rf "$TMP_ROOT" out="$($GENERATOR "$TMP_ROOT" 2>&1)" status=$? if [ "$status" -eq 0 ]; then pass "generator exits 0" else bad "generator exits 0 (got exit $status)" echo "$out" fi if [ "$out" = "build/test-getpath-frozen/cpython/Python/frozen_modules/getpath.h" ]; then pass "generator reports the project-local build-relative output path" else bad "generator reports the project-local build-relative output path (got: $out)" fi if [ -s "$OUT_HEADER" ]; then pass "generator writes a nonempty getpath.h under build/" else bad "generator writes a nonempty getpath.h under build/" fi first_line="$(sed -n '1p' "$OUT_HEADER" 2>/dev/null)" if [ "$first_line" = '/* Auto-generated by Programs/_freeze_module.py */' ]; then pass "generated header records the host-Python _freeze_module.py path" else bad "generated header records the host-Python _freeze_module.py path (got: $first_line)" fi second_line="$(sed -n '2p' "$OUT_HEADER" 2>/dev/null)" if [ "$second_line" = 'const unsigned char _Py_M__getpath[] = {' ]; then pass "generated header declares the expected getpath frozen symbol" else bad "generated header declares the expected getpath frozen symbol (got: $second_line)" fi python3 - "$OUT_HEADER" <<'PY' import marshal import re import sys from pathlib import Path path = Path(sys.argv[1]) text = path.read_text() if not text.endswith('};\n'): raise SystemExit('header does not end with closing array marker') match = re.search(r'const unsigned char _Py_M__getpath\[\] = \{\n(?P.*?)\};\n\Z', text, re.S) if not match: raise SystemExit('array body not found') lines = match.group('body').splitlines() values = [] for index, line in enumerate(lines, 1): if not line.startswith(' '): raise SystemExit(f'line {index} is not four-space indented') payload = line.strip() if not payload.endswith(','): raise SystemExit(f'line {index} is missing trailing comma') nums = [part for part in payload[:-1].split(',') if part] if len(nums) > 16: raise SystemExit(f'line {index} has more than 16 bytes') if index < len(lines) and len(nums) != 16: raise SystemExit(f'non-final line {index} has {len(nums)} bytes, expected 16') for num in nums: value = int(num) if not 0 <= value <= 255: raise SystemExit(f'byte out of range: {value}') values.append(value) code = marshal.loads(bytes(values)) if code.co_filename != '': raise SystemExit(f'unexpected code filename: {code.co_filename!r}') if code.co_name != '': raise SystemExit(f'unexpected code name: {code.co_name!r}') print('format-ok') PY format_status=$? if [ "$format_status" -eq 0 ]; then pass "generated byte array unmarshals as frozen getpath code with CPython format" else bad "generated byte array unmarshals as frozen getpath code with CPython format" fi mkdir -p "$(dirname "$DIRECT_HEADER")" python3 "$REPO_ROOT/cpython/Programs/_freeze_module.py" getpath \ "$REPO_ROOT/cpython/Modules/getpath.py" "$DIRECT_HEADER" if cmp -s "$OUT_HEADER" "$DIRECT_HEADER"; then pass "generator output matches direct Programs/_freeze_module.py invocation exactly" else bad "generator output matches direct Programs/_freeze_module.py invocation exactly" fi frontier_out="$(bash "$REPO_ROOT/scripts/vc6-probe-pythoncore-frontier.sh" --list-only 2>&1)" frontier_status=$? if [ "$frontier_status" -eq 0 ] && printf '%s\n' "$frontier_out" | grep -q $'^compile\tcpython/Modules/getpath.c\t'; then pass "frontier list still starts from Modules/getpath.c without requiring generated output" else bad "frontier list still starts from Modules/getpath.c without requiring generated output" echo "$frontier_out" fi if [ "$fail" -ne 0 ]; then echo "FAILED" exit 1 fi echo "ALL TESTS PASSED"