#!/usr/bin/env bash # =========================================================================== # run-ci-tests.sh — CI test runner for SX language suite. # # Runs JS + OCaml tests. No Python evaluator (eliminated). # Exit non-zero if any suite fails. # =========================================================================== set -euo pipefail FAILURES=() PASSES=() run_suite() { local name="$1" shift echo "" echo "============================================================" echo " $name" echo "============================================================" if "$@"; then PASSES+=("$name") else FAILURES+=("$name") fi } # ------------------------------------------------------------------- # 1. JS standard tests # ------------------------------------------------------------------- run_suite "JS standard (spec tests)" \ node hosts/javascript/run_tests.js # ------------------------------------------------------------------- # 2. JS full tests (continuations + types + VM) # ------------------------------------------------------------------- run_suite "JS full (spec + continuations + types + VM)" \ node hosts/javascript/run_tests.js --full # ------------------------------------------------------------------- # 3. OCaml spec tests # ------------------------------------------------------------------- run_suite "OCaml (spec tests)" \ hosts/ocaml/_build/default/bin/run_tests.exe # ------------------------------------------------------------------- # 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() 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 for form in ['defhandler', 'defquery', 'defaction', 'defpage', 'defrelation', 'defstyle', 'deftype', 'defeffect']: check(f'{form} registered', f'(has-key? *custom-special-forms* \"{form}\")', 'true') 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') check('definition-form-extensions populated', '(> (len *definition-form-extensions*) 0)', 'true') check('RENDER_HTML_FORMS has defstyle', '(contains? RENDER_HTML_FORMS \"defstyle\")', 'true') 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) " # ------------------------------------------------------------------- # Summary # ------------------------------------------------------------------- echo "" echo "============================================================" echo " CI 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" echo "" exit 1 else echo "" echo " All ${#PASSES[@]} suites passed." echo "" exit 0 fi