The opam base image has dune in the switch but not on PATH. RUN eval $(opam env) doesn't persist across layers. Install dune explicitly and set PATH so dune is available in build steps. Also fix run-tests.sh to respect QUICK env var from caller (was being overwritten to false). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
167 lines
6.0 KiB
Bash
Executable File
167 lines
6.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ===========================================================================
|
|
# run-tests.sh — Run ALL test suites. Exit non-zero if any fail.
|
|
#
|
|
# Usage:
|
|
# ./run-tests.sh # run everything
|
|
# ./run-tests.sh --quick # skip Playwright (fast CI check)
|
|
# ./run-tests.sh --sx-only # SX language tests only (JS + Python + OCaml)
|
|
# ===========================================================================
|
|
set -euo pipefail
|
|
|
|
cd "$(dirname "$0")"
|
|
|
|
QUICK="${QUICK:-false}"
|
|
SX_ONLY="${SX_ONLY:-false}"
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--quick) QUICK=true ;;
|
|
--sx-only) SX_ONLY=true ;;
|
|
esac
|
|
done
|
|
|
|
FAILURES=()
|
|
PASSES=()
|
|
|
|
run_suite() {
|
|
local name="$1"
|
|
shift
|
|
echo ""
|
|
echo "============================================================"
|
|
echo " $name"
|
|
echo "============================================================"
|
|
if "$@"; then
|
|
PASSES+=("$name")
|
|
else
|
|
FAILURES+=("$name")
|
|
fi
|
|
}
|
|
|
|
# -------------------------------------------------------------------
|
|
# 1. Build SX bundles
|
|
# -------------------------------------------------------------------
|
|
echo "=== Building SX bundles ==="
|
|
python3 hosts/javascript/cli.py --output shared/static/scripts/sx-browser.js
|
|
python3 hosts/javascript/cli.py --extensions continuations --spec-modules types \
|
|
--output shared/static/scripts/sx-full-test.js
|
|
|
|
# -------------------------------------------------------------------
|
|
# 2. JavaScript SX tests (standard + full)
|
|
# -------------------------------------------------------------------
|
|
run_suite "JS standard (spec tests)" \
|
|
node hosts/javascript/run_tests.js
|
|
|
|
run_suite "JS full (spec + continuations + types + VM)" \
|
|
node hosts/javascript/run_tests.js --full
|
|
|
|
# -------------------------------------------------------------------
|
|
# 3. OCaml SX tests
|
|
# -------------------------------------------------------------------
|
|
if [ -x hosts/ocaml/_build/default/bin/run_tests.exe ]; then
|
|
run_suite "OCaml (spec tests)" \
|
|
hosts/ocaml/_build/default/bin/run_tests.exe
|
|
else
|
|
echo ""
|
|
echo "[SKIP] OCaml tests — binary not built (run: cd hosts/ocaml && dune build)"
|
|
fi
|
|
|
|
# -------------------------------------------------------------------
|
|
# 4. OCaml bridge integration (custom special forms, web-forms.sx)
|
|
# -------------------------------------------------------------------
|
|
run_suite "OCaml bridge — custom special forms + web-forms" \
|
|
python3 -c "
|
|
from shared.sx.ocaml_sync import OcamlSync
|
|
bridge = OcamlSync()
|
|
# Load exactly what the server does (no evaluator.sx!)
|
|
for f in ['spec/parser.sx', 'spec/render.sx', 'web/adapter-html.sx', 'web/adapter-sx.sx', 'web/web-forms.sx', 'lib/freeze.sx']:
|
|
bridge.load(f)
|
|
ok = 0; fail = 0
|
|
def check(name, expr, expected=None):
|
|
global ok, fail
|
|
try:
|
|
r = bridge.eval(expr)
|
|
if expected is not None and r != expected:
|
|
print(f' FAIL: {name}: expected {expected!r}, got {r!r}'); fail += 1
|
|
else:
|
|
print(f' PASS: {name}'); ok += 1
|
|
except Exception as e:
|
|
print(f' FAIL: {name}: {e}'); fail += 1
|
|
|
|
# Custom special forms registered by web-forms.sx
|
|
for form in ['defhandler', 'defquery', 'defaction', 'defpage', 'defrelation', 'defstyle', 'deftype', 'defeffect']:
|
|
check(f'{form} registered', f'(has-key? *custom-special-forms* \"{form}\")', 'true')
|
|
|
|
# Custom forms callable via eval (not just via load)
|
|
check('deftype via eval', '(deftype test-t number)', 'nil')
|
|
check('defeffect via eval', '(defeffect test-e)', 'nil')
|
|
check('defstyle via eval', '(defstyle my-s \"bold\")', 'bold')
|
|
check('defhandler via eval', '(has-key? (defhandler test-h (&key x) x) \"__type\")', 'true')
|
|
|
|
# Extension lists populated
|
|
check('definition-form-extensions populated', '(> (len *definition-form-extensions*) 0)', 'true')
|
|
check('RENDER_HTML_FORMS has defstyle', '(contains? RENDER_HTML_FORMS \"defstyle\")', 'true')
|
|
|
|
# Env-shadowing regression: custom forms survive evaluator.sx load
|
|
bridge2 = OcamlSync()
|
|
bridge2.eval('(register-special-form! \"shadow-test\" (fn (args env) 42))')
|
|
bridge2.load('spec/evaluator.sx')
|
|
check('custom form survives evaluator.sx load',
|
|
bridge2.eval('(has-key? *custom-special-forms* \"shadow-test\")'), 'true')
|
|
bridge2.eval('(register-special-form! \"post-load\" (fn (args env) 99))')
|
|
check('custom form callable after evaluator.sx load',
|
|
bridge2.eval('(post-load 1)'), '99')
|
|
|
|
print(f'\\nResults: {ok} passed, {fail} failed')
|
|
import sys; sys.exit(1 if fail > 0 else 0)
|
|
"
|
|
|
|
# -------------------------------------------------------------------
|
|
# 5. Python SX tests (post-removal regression, components, parser)
|
|
# -------------------------------------------------------------------
|
|
run_suite "Python — post-removal regression" \
|
|
python3 -m pytest shared/sx/tests/test_post_removal_bugs.py -v --tb=short
|
|
|
|
run_suite "Python — component rendering" \
|
|
python3 -m pytest shared/sx/tests/test_components.py -v --tb=short
|
|
|
|
run_suite "Python — parser" \
|
|
python3 -m pytest shared/sx/tests/test_parser.py -v --tb=short
|
|
|
|
# -------------------------------------------------------------------
|
|
# 5. Playwright tests (browser)
|
|
# -------------------------------------------------------------------
|
|
if [ "$QUICK" = false ] && [ "$SX_ONLY" = false ]; then
|
|
run_suite "Playwright — isomorphic SSR" \
|
|
npx playwright test --reporter=list
|
|
|
|
run_suite "Playwright — SX demos (98 tests)" \
|
|
python3 -m pytest sx/tests/test_demos.py -v --tb=short
|
|
fi
|
|
|
|
# -------------------------------------------------------------------
|
|
# Summary
|
|
# -------------------------------------------------------------------
|
|
echo ""
|
|
echo "============================================================"
|
|
echo " TEST SUMMARY"
|
|
echo "============================================================"
|
|
for p in "${PASSES[@]}"; do
|
|
echo " PASS: $p"
|
|
done
|
|
for f in "${FAILURES[@]}"; do
|
|
echo " FAIL: $f"
|
|
done
|
|
echo "============================================================"
|
|
|
|
if [ ${#FAILURES[@]} -gt 0 ]; then
|
|
echo ""
|
|
echo " ${#FAILURES[@]} suite(s) FAILED — deploy blocked."
|
|
echo ""
|
|
exit 1
|
|
else
|
|
echo ""
|
|
echo " All ${#PASSES[@]} suites passed."
|
|
echo ""
|
|
exit 0
|
|
fi
|