#!/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=false 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', 'spec/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') 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