test.sx now defines deftest/defsuite as macros. Any host that provides 5 platform functions (try-call, report-pass, report-fail, push-suite, pop-suite) can evaluate the file directly — no bootstrap compilation step needed for JS. - Added defmacro for deftest (wraps body in thunk, catches via try-call) - Added defmacro for defsuite (push/pop suite context stack) - Created run.js: sx-browser.js evaluates test.sx directly (81/81 pass) - Created run.py: Python evaluator evaluates test.sx directly (81/81 pass) - Deleted bootstrap_test_js.py and generated test_sx_spec.js - Updated testing docs page to reflect self-executing architecture Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
93 lines
2.0 KiB
Python
93 lines
2.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Run test.sx directly against the Python SX evaluator.
|
|
|
|
The Python evaluator parses and evaluates test.sx — SX tests itself.
|
|
This script provides only platform functions (error catching, reporting).
|
|
|
|
Usage: python shared/sx/tests/run.py
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
import traceback
|
|
|
|
_HERE = os.path.dirname(os.path.abspath(__file__))
|
|
_PROJECT = os.path.abspath(os.path.join(_HERE, "..", "..", ".."))
|
|
sys.path.insert(0, _PROJECT)
|
|
|
|
from shared.sx.parser import parse_all
|
|
from shared.sx.evaluator import _eval, _trampoline
|
|
|
|
# --- Test state ---
|
|
suite_stack: list[str] = []
|
|
passed = 0
|
|
failed = 0
|
|
test_num = 0
|
|
|
|
|
|
def try_call(thunk):
|
|
"""Call an SX thunk, catching errors."""
|
|
try:
|
|
_trampoline(_eval([thunk], {}))
|
|
return {"ok": True}
|
|
except Exception as e:
|
|
return {"ok": False, "error": str(e)}
|
|
|
|
|
|
def report_pass(name):
|
|
global passed, test_num
|
|
test_num += 1
|
|
passed += 1
|
|
full_name = " > ".join(suite_stack + [name])
|
|
print(f"ok {test_num} - {full_name}")
|
|
|
|
|
|
def report_fail(name, error):
|
|
global failed, test_num
|
|
test_num += 1
|
|
failed += 1
|
|
full_name = " > ".join(suite_stack + [name])
|
|
print(f"not ok {test_num} - {full_name}")
|
|
print(f" # {error}")
|
|
|
|
|
|
def push_suite(name):
|
|
suite_stack.append(name)
|
|
|
|
|
|
def pop_suite():
|
|
suite_stack.pop()
|
|
|
|
|
|
def main():
|
|
env = {
|
|
"try-call": try_call,
|
|
"report-pass": report_pass,
|
|
"report-fail": report_fail,
|
|
"push-suite": push_suite,
|
|
"pop-suite": pop_suite,
|
|
}
|
|
|
|
test_sx = os.path.join(_HERE, "..", "ref", "test.sx")
|
|
with open(test_sx) as f:
|
|
src = f.read()
|
|
|
|
exprs = parse_all(src)
|
|
|
|
print("TAP version 13")
|
|
for expr in exprs:
|
|
_trampoline(_eval(expr, env))
|
|
|
|
print()
|
|
print(f"1..{test_num}")
|
|
print(f"# tests {passed + failed}")
|
|
print(f"# pass {passed}")
|
|
if failed > 0:
|
|
print(f"# fail {failed}")
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|