344 lines
15 KiB
Bash
Executable File
344 lines
15 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# Regression tests for scripts/vc6-probe-core.sh and, through it, the VC6
|
|
# _Py_ATOMIC_VC6_X86 branch added to
|
|
# cpython/Include/internal/pycore_atomic.h.
|
|
#
|
|
# Skipped (not failed) when wine + MSVC600 are unavailable, matching
|
|
# scripts/vc6-probe.test.sh's convention, since CI/dev boxes without the
|
|
# external MSVC600 bundle cannot exercise this.
|
|
#
|
|
# Usage: scripts/vc6-probe-core.test.sh
|
|
|
|
set -u
|
|
|
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
PROBE="$REPO_ROOT/scripts/vc6-probe-core.sh"
|
|
|
|
fail=0
|
|
pass() { echo "ok - $1"; }
|
|
bad() { echo "not ok - $1"; fail=1; }
|
|
|
|
MSVC600_ROOT="${MSVC600_ROOT:-$REPO_ROOT/MSVC600}"
|
|
if command -v wine >/dev/null 2>&1 && [ -e "$MSVC600_ROOT/VC98/Bin/CL.EXE" ]; then
|
|
|
|
# --- positive case: the isolated atomics smoke fixture compiles clean ---
|
|
out="$("$PROBE" scripts/fixtures/vc6_atomic_smoke.c 2>&1)"
|
|
status=$?
|
|
|
|
if [ "$status" -eq 0 ]; then
|
|
pass "pycore_atomic.h VC6 branch: atomics smoke fixture compiles (exit 0)"
|
|
else
|
|
bad "pycore_atomic.h VC6 branch: atomics smoke fixture compiles (exit $status)"
|
|
echo "$out"
|
|
fi
|
|
|
|
if printf '%s' "$out" | grep -qi 'error'; then
|
|
bad "atomics smoke fixture: no compiler errors in output"
|
|
else
|
|
pass "atomics smoke fixture: no compiler errors in output"
|
|
fi
|
|
|
|
if printf '%s' "$out" | grep -q 'intrin.h'; then
|
|
bad "atomics smoke fixture: does not hit the intrin.h blocker"
|
|
else
|
|
pass "atomics smoke fixture: does not hit the intrin.h blocker"
|
|
fi
|
|
|
|
# --- negative case: a deliberately invalid atomics usage still fails ---
|
|
BAD_SRC="$REPO_ROOT/scripts/fixtures/_vc6_atomic_smoke_bad.c"
|
|
cat > "$BAD_SRC" <<'EOF'
|
|
#include <inttypes.h>
|
|
#include "pycore_atomic.h"
|
|
int bad(void) { return this_identifier_does_not_exist; }
|
|
EOF
|
|
trap 'rm -f "$BAD_SRC"' EXIT
|
|
|
|
out_bad="$("$PROBE" scripts/fixtures/_vc6_atomic_smoke_bad.c 2>&1)"
|
|
status_bad=$?
|
|
|
|
if [ "$status_bad" -ne 0 ]; then
|
|
pass "probe-core reports nonzero exit for a broken translation unit"
|
|
else
|
|
bad "probe-core reports nonzero exit for a broken translation unit (got: $status_bad)"
|
|
fi
|
|
|
|
if printf '%s' "$out_bad" | grep -q 'error C'; then
|
|
pass "probe-core output surfaces the compiler diagnostic"
|
|
else
|
|
bad "probe-core output surfaces the compiler diagnostic"
|
|
fi
|
|
|
|
# --- reference case: Modules/main.c reaches past pycore_atomic.h ---
|
|
# main.c is expected to fail today (pycore_interp.h needs <stdbool.h>,
|
|
# a separate, unrelated VC6 gap) but must not fail *at*
|
|
# pycore_atomic.h/intrin.h; that would mean the atomics branch
|
|
# regressed even though the isolated fixture above passed.
|
|
out_main="$("$PROBE" cpython/Modules/main.c 2>&1)"
|
|
if printf '%s' "$out_main" | grep -q 'pycore_atomic.h.*intrin.h\|intrin.h.*No such file'; then
|
|
bad "Modules/main.c probe: does not regress to the pycore_atomic.h/intrin.h blocker"
|
|
else
|
|
pass "Modules/main.c probe: does not regress to the pycore_atomic.h/intrin.h blocker"
|
|
fi
|
|
|
|
# --- positive case: the isolated stdbool smoke fixture compiles clean ---
|
|
out_bool="$("$PROBE" scripts/fixtures/vc6_stdbool_smoke.c 2>&1)"
|
|
status_bool=$?
|
|
|
|
if [ "$status_bool" -eq 0 ]; then
|
|
pass "compat/msvc600/stdbool.h: stdbool smoke fixture compiles (exit 0)"
|
|
else
|
|
bad "compat/msvc600/stdbool.h: stdbool smoke fixture compiles (exit $status_bool)"
|
|
echo "$out_bool"
|
|
fi
|
|
|
|
if printf '%s' "$out_bool" | grep -qi 'error'; then
|
|
bad "stdbool smoke fixture: no compiler errors in output"
|
|
else
|
|
pass "stdbool smoke fixture: no compiler errors in output"
|
|
fi
|
|
|
|
# --- negative case: a deliberately invalid bool usage still fails ---
|
|
BAD_BOOL_SRC="$REPO_ROOT/scripts/fixtures/_vc6_stdbool_smoke_bad.c"
|
|
cat > "$BAD_BOOL_SRC" <<'EOF'
|
|
#include <stdbool.h>
|
|
int bad(void) { return this_identifier_does_not_exist; }
|
|
EOF
|
|
trap 'rm -f "$BAD_SRC" "$BAD_BOOL_SRC"' EXIT
|
|
|
|
out_bool_bad="$("$PROBE" scripts/fixtures/_vc6_stdbool_smoke_bad.c 2>&1)"
|
|
status_bool_bad=$?
|
|
|
|
if [ "$status_bool_bad" -ne 0 ]; then
|
|
pass "probe-core reports nonzero exit for a broken stdbool translation unit"
|
|
else
|
|
bad "probe-core reports nonzero exit for a broken stdbool translation unit (got: $status_bool_bad)"
|
|
fi
|
|
|
|
if printf '%s' "$out_bool_bad" | grep -q 'error C'; then
|
|
pass "probe-core output surfaces the compiler diagnostic (stdbool case)"
|
|
else
|
|
bad "probe-core output surfaces the compiler diagnostic (stdbool case)"
|
|
fi
|
|
|
|
# --- reference case: Modules/main.c reaches past pycore_interp.h's
|
|
# <stdbool.h> include. It is expected to still fail today -- the next,
|
|
# unrelated blocker is VC6's C89-only parser rejecting C99-style mixed
|
|
# declarations-and-code in several pycore_*.h inline functions (e.g.
|
|
# Include/internal/pycore_code.h's _PyLocals_GetKind(), which declares
|
|
# `char *ptr` after a preceding `assert(...)` statement) -- but it must
|
|
# not fail *at* <stdbool.h> anymore; that would mean this slice
|
|
# regressed even though the isolated fixture above passed.
|
|
if printf '%s' "$out_main" | grep -q "Cannot open include file: 'stdbool.h'"; then
|
|
bad "Modules/main.c probe: does not regress to the pycore_interp.h/stdbool.h blocker"
|
|
else
|
|
pass "Modules/main.c probe: does not regress to the pycore_interp.h/stdbool.h blocker"
|
|
fi
|
|
|
|
# --- positive case: the isolated mixed-declarations smoke fixture
|
|
# compiles clean (pycore_code.h, pycore_dict.h, pycore_list.h,
|
|
# pycore_call.h all hoisted their C99 mid-block declarations) ---
|
|
out_decls="$("$PROBE" scripts/fixtures/vc6_mixed_decls_smoke.c 2>&1)"
|
|
status_decls=$?
|
|
|
|
if [ "$status_decls" -eq 0 ]; then
|
|
pass "mixed-declarations header slice: smoke fixture compiles (exit 0)"
|
|
else
|
|
bad "mixed-declarations header slice: smoke fixture compiles (exit $status_decls)"
|
|
echo "$out_decls"
|
|
fi
|
|
|
|
if printf '%s' "$out_decls" | grep -qi 'error'; then
|
|
bad "mixed-declarations smoke fixture: no compiler errors in output"
|
|
else
|
|
pass "mixed-declarations smoke fixture: no compiler errors in output"
|
|
fi
|
|
|
|
# --- negative case: a deliberately invalid mixed-declarations usage
|
|
# still fails ---
|
|
BAD_DECLS_SRC="$REPO_ROOT/scripts/fixtures/_vc6_mixed_decls_smoke_bad.c"
|
|
cat > "$BAD_DECLS_SRC" <<'EOF'
|
|
#include "Python.h"
|
|
#include "pycore_code.h"
|
|
int bad(void) { return this_identifier_does_not_exist; }
|
|
EOF
|
|
trap 'rm -f "$BAD_SRC" "$BAD_BOOL_SRC" "$BAD_DECLS_SRC"' EXIT
|
|
|
|
out_decls_bad="$("$PROBE" scripts/fixtures/_vc6_mixed_decls_smoke_bad.c 2>&1)"
|
|
status_decls_bad=$?
|
|
|
|
if [ "$status_decls_bad" -ne 0 ]; then
|
|
pass "probe-core reports nonzero exit for a broken mixed-declarations translation unit"
|
|
else
|
|
bad "probe-core reports nonzero exit for a broken mixed-declarations translation unit (got: $status_decls_bad)"
|
|
fi
|
|
|
|
if printf '%s' "$out_decls_bad" | grep -q 'error C'; then
|
|
pass "probe-core output surfaces the compiler diagnostic (mixed-declarations case)"
|
|
else
|
|
bad "probe-core output surfaces the compiler diagnostic (mixed-declarations case)"
|
|
fi
|
|
|
|
# --- reference case: Modules/main.c no longer fails inside any of the
|
|
# four fixed pycore_*.h headers. It is expected to still fail today --
|
|
# Modules/main.c itself uses the same C99 mixed-declarations pattern
|
|
# pervasively in its own function bodies, a much larger, whole-file
|
|
# source rewrite that is out of scope for this header-only slice; see
|
|
# PORT_STATUS.md's "Mixed-declarations header slice" section for the
|
|
# documented boundary -- but it must not fail *inside*
|
|
# pycore_code.h/pycore_dict.h/pycore_list.h/pycore_call.h anymore;
|
|
# that would mean this slice regressed even though the isolated
|
|
# fixture above passed.
|
|
if printf '%s' "$out_main" | grep -qE 'pycore_(code|dict|list|call)\.h\([0-9]+\)'; then
|
|
bad "Modules/main.c probe: does not regress to the pycore_code/dict/list/call.h mixed-declarations blocker"
|
|
else
|
|
pass "Modules/main.c probe: does not regress to the pycore_code/dict/list/call.h mixed-declarations blocker"
|
|
fi
|
|
|
|
# --- positive case: the isolated declaration-order smoke fixture
|
|
# compiles clean. It exercises, self-contained, the same five hoist
|
|
# shapes applied to Modules/main.c itself (bare hoist above an early
|
|
# return, side-effecting-initializer hoist, a hoisted pair where fixing
|
|
# the first declaration would otherwise turn the second into a new
|
|
# violation, a nested-block hoist, and a platform-conditional same-name
|
|
# hoist) so this guards the general transformation, independent of
|
|
# main.c's own future edits. ---
|
|
out_decl_order="$("$PROBE" scripts/fixtures/vc6_decl_order_smoke.c 2>&1)"
|
|
status_decl_order=$?
|
|
|
|
if [ "$status_decl_order" -eq 0 ]; then
|
|
pass "Modules/main.c declaration-order slice: smoke fixture compiles (exit 0)"
|
|
else
|
|
bad "Modules/main.c declaration-order slice: smoke fixture compiles (exit $status_decl_order)"
|
|
echo "$out_decl_order"
|
|
fi
|
|
|
|
if printf '%s' "$out_decl_order" | grep -qi 'error'; then
|
|
bad "declaration-order smoke fixture: no compiler errors in output"
|
|
else
|
|
pass "declaration-order smoke fixture: no compiler errors in output"
|
|
fi
|
|
|
|
# --- negative case: a deliberately invalid declaration-order usage
|
|
# still fails ---
|
|
BAD_DECL_ORDER_SRC="$REPO_ROOT/scripts/fixtures/_vc6_decl_order_smoke_bad.c"
|
|
cat > "$BAD_DECL_ORDER_SRC" <<'EOF'
|
|
int bad(void) { return this_identifier_does_not_exist; }
|
|
EOF
|
|
trap 'rm -f "$BAD_SRC" "$BAD_BOOL_SRC" "$BAD_DECLS_SRC" "$BAD_DECL_ORDER_SRC"' EXIT
|
|
|
|
out_decl_order_bad="$("$PROBE" scripts/fixtures/_vc6_decl_order_smoke_bad.c 2>&1)"
|
|
status_decl_order_bad=$?
|
|
|
|
if [ "$status_decl_order_bad" -ne 0 ]; then
|
|
pass "probe-core reports nonzero exit for a broken declaration-order translation unit"
|
|
else
|
|
bad "probe-core reports nonzero exit for a broken declaration-order translation unit (got: $status_decl_order_bad)"
|
|
fi
|
|
|
|
if printf '%s' "$out_decl_order_bad" | grep -q 'error C'; then
|
|
pass "probe-core output surfaces the compiler diagnostic (declaration-order case)"
|
|
else
|
|
bad "probe-core output surfaces the compiler diagnostic (declaration-order case)"
|
|
fi
|
|
|
|
# --- reference case: Modules/main.c no longer fails on any C89
|
|
# mixed-declaration site anywhere in its own body (the C2146/C2065/
|
|
# C2275 "illegal use of this type as an expression" pattern tied to a
|
|
# declaration keyword). At the time this check was written, main.c
|
|
# still failed with a distinct, out-of-scope blocker instead (C99
|
|
# compound literals/designated initializers, see below); that blocker
|
|
# is now also fixed (see the compound-literal checks further down),
|
|
# but this check's job is narrower and unchanged: main.c must not show
|
|
# the declaration-order-specific error codes; that would mean this
|
|
# slice regressed even though the isolated fixture above passed.
|
|
if printf '%s' "$out_main" | grep -qE "main\.c\([0-9]+\) : error C2146|main\.c\([0-9]+\) : error C2065.*undeclared identifier|main\.c\([0-9]+\) : error C2275"; then
|
|
bad "Modules/main.c probe: does not regress to a C89 mixed-declaration error inside main.c's own body"
|
|
else
|
|
pass "Modules/main.c probe: does not regress to a C89 mixed-declaration error inside main.c's own body"
|
|
fi
|
|
|
|
# --- positive case: the isolated compound-literal/designated-
|
|
# initializer smoke fixture compiles clean. It exercises, self-
|
|
# contained (aside from the real Python.h/pycore_initconfig.h types
|
|
# it needs), the four replacement patterns now in use: the VC6-only
|
|
# _PyStatus_OK() helper (not PyStatus_Ok(), avoiding recursion), the
|
|
# VC6-only _PyStatus_ERR() helper, explicit field assignment instead of
|
|
# the _PyCompilerFlags_INIT compound-literal macro, and explicit field
|
|
# assignment instead of a _PyArgv designated-initializer aggregate. ---
|
|
if grep -q '#[[:space:]]*define[[:space:]]*_PyStatus_OK().*PyStatus_Ok' "$REPO_ROOT/cpython/Include/internal/pycore_initconfig.h"; then
|
|
bad "pycore_initconfig.h: VC6 _PyStatus_OK() helper does not recurse through PyStatus_Ok()"
|
|
else
|
|
pass "pycore_initconfig.h: VC6 _PyStatus_OK() helper does not recurse through PyStatus_Ok()"
|
|
fi
|
|
|
|
out_compound="$("$PROBE" scripts/fixtures/vc6_compound_literal_smoke.c 2>&1)"
|
|
status_compound=$?
|
|
|
|
if [ "$status_compound" -eq 0 ]; then
|
|
pass "compound-literal slice: smoke fixture compiles (exit 0)"
|
|
else
|
|
bad "compound-literal slice: smoke fixture compiles (exit $status_compound)"
|
|
echo "$out_compound"
|
|
fi
|
|
|
|
if printf '%s' "$out_compound" | grep -qi 'error'; then
|
|
bad "compound-literal smoke fixture: no compiler errors in output"
|
|
else
|
|
pass "compound-literal smoke fixture: no compiler errors in output"
|
|
fi
|
|
|
|
# --- negative case: a deliberately invalid compound-literal-fixture
|
|
# usage still fails ---
|
|
BAD_COMPOUND_SRC="$REPO_ROOT/scripts/fixtures/_vc6_compound_literal_smoke_bad.c"
|
|
cat > "$BAD_COMPOUND_SRC" <<'EOF'
|
|
#include "Python.h"
|
|
#include "pycore_initconfig.h"
|
|
int bad(void) { return this_identifier_does_not_exist; }
|
|
EOF
|
|
trap 'rm -f "$BAD_SRC" "$BAD_BOOL_SRC" "$BAD_DECLS_SRC" "$BAD_DECL_ORDER_SRC" "$BAD_COMPOUND_SRC"' EXIT
|
|
|
|
out_compound_bad="$("$PROBE" scripts/fixtures/_vc6_compound_literal_smoke_bad.c 2>&1)"
|
|
status_compound_bad=$?
|
|
|
|
if [ "$status_compound_bad" -ne 0 ]; then
|
|
pass "probe-core reports nonzero exit for a broken compound-literal translation unit"
|
|
else
|
|
bad "probe-core reports nonzero exit for a broken compound-literal translation unit (got: $status_compound_bad)"
|
|
fi
|
|
|
|
if printf '%s' "$out_compound_bad" | grep -q 'error C'; then
|
|
pass "probe-core output surfaces the compiler diagnostic (compound-literal case)"
|
|
else
|
|
bad "probe-core output surfaces the compiler diagnostic (compound-literal case)"
|
|
fi
|
|
|
|
# --- reference case: Modules/main.c itself no longer shows any C2059
|
|
# "syntax error : '{'"/"syntax error : '.'" (the compound-literal/
|
|
# designated-initializer error signature), and -- since this was the
|
|
# last recorded blocker for main.c as a compile-only (/c) translation
|
|
# unit -- now compiles with exit status 0. This is a compile-only
|
|
# result: no link or runtime success is claimed (vc6-probe-core.sh is
|
|
# /c only; see its header comment). ---
|
|
if printf '%s' "$out_main" | grep -q 'main\.c([0-9]*) : error C2059'; then
|
|
bad "Modules/main.c probe: does not regress to the compound-literal/designated-initializer C2059 blocker"
|
|
else
|
|
pass "Modules/main.c probe: does not regress to the compound-literal/designated-initializer C2059 blocker"
|
|
fi
|
|
|
|
out_main_status="$("$PROBE" cpython/Modules/main.c >/dev/null 2>&1; echo $?)"
|
|
if [ "$out_main_status" -eq 0 ]; then
|
|
pass "Modules/main.c probe: compiles clean end-to-end (compile-only, exit 0)"
|
|
else
|
|
bad "Modules/main.c probe: compiles clean end-to-end (compile-only, got exit $out_main_status)"
|
|
fi
|
|
else
|
|
echo "skip - wine/MSVC600 not available; skipping live probe-core integration checks"
|
|
fi
|
|
|
|
if [ "$fail" -ne 0 ]; then
|
|
echo "FAILED"
|
|
exit 1
|
|
fi
|
|
echo "ALL TESTS PASSED"
|