Modular test architecture: per-module test specs for SX

Split monolithic test.sx into composable test specs:
- test-framework.sx: deftest/defsuite macros + assertion helpers
- test-eval.sx: core evaluator + primitives (81 tests)
- test-parser.sx: parser + serializer + round-trips (39 tests)
- test-router.sx: route matching from router.sx (18 tests)
- test-render.sx: HTML adapter rendering (23 tests)

Runners auto-discover specs and test whatever bootstrapped code
is available. Usage: `run.js eval parser router` or just `run.js`.
Legacy mode (`--legacy`) still runs monolithic test.sx.

Router tests use bootstrapped functions (sx_ref.py / sx-browser.js)
because the hand-written evaluator's flat-dict env model doesn't
support set! mutation across lambda closure boundaries.

JS: 161/161. Python: 159/161 (2 parser escape bugs found).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-07 12:17:13 +00:00
parent 99a78a70b3
commit aab1f3e966
8 changed files with 1506 additions and 37 deletions

View File

@@ -1,16 +1,21 @@
;; ==========================================================================
;; test.sx — Self-hosting SX test framework
;; test.sx — Self-hosting SX test suite (backward-compatible entry point)
;;
;; Defines a minimal test framework in SX that tests SX — the language
;; proves its own correctness. The framework is self-executing: any host
;; that provides 5 platform functions can evaluate this file directly.
;; This file includes the test framework and core eval tests inline.
;; It exists for backward compatibility — runners that load "test.sx"
;; get the same 81 tests as before.
;;
;; For modular testing, runners should instead load:
;; 1. test-framework.sx (macros + assertions)
;; 2. One or more test specs: test-eval.sx, test-parser.sx,
;; test-router.sx, test-render.sx, etc.
;;
;; Platform functions required:
;; try-call (thunk) {:ok true} | {:ok false :error "msg"}
;; report-pass (name) platform-specific pass output
;; report-fail (name error) platform-specific fail output
;; push-suite (name) push suite name onto context stack
;; pop-suite () pop suite name from context stack
;; try-call (thunk) -> {:ok true} | {:ok false :error "msg"}
;; report-pass (name) -> platform-specific pass output
;; report-fail (name error) -> platform-specific fail output
;; push-suite (name) -> push suite name onto context stack
;; pop-suite () -> pop suite name from context stack
;;
;; Usage:
;; ;; Host injects platform functions into env, then: