commit
This commit is contained in:
153
tests/run_tests.py
Normal file
153
tests/run_tests.py
Normal file
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Simple end-to-end test runner for L2.
|
||||
|
||||
Each test case provides an L2 program source and an expected stdout. The runner
|
||||
invokes the bootstrap compiler on the fly and executes the produced binary.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
COMPILER = ROOT / "main.py"
|
||||
PYTHON = Path(sys.executable)
|
||||
|
||||
|
||||
@dataclass
|
||||
class TestCase:
|
||||
name: str
|
||||
source: str
|
||||
expected_stdout: str
|
||||
|
||||
|
||||
CASES: List[TestCase] = [
|
||||
TestCase(
|
||||
name="call_syntax_parens",
|
||||
source=f"""
|
||||
import {ROOT / 'stdlib.sl'}
|
||||
import {ROOT / 'fn.sl'}
|
||||
|
||||
: main
|
||||
2 40 +
|
||||
puts
|
||||
extend-syntax
|
||||
foo(1, 2)
|
||||
puts
|
||||
0
|
||||
;
|
||||
|
||||
fn foo(int a, int b){{
|
||||
return a + b;
|
||||
}}
|
||||
""",
|
||||
expected_stdout="42\n3\n",
|
||||
),
|
||||
TestCase(
|
||||
name="loops_and_cmp",
|
||||
source=f"""
|
||||
import {ROOT / 'stdlib.sl'}
|
||||
|
||||
: main
|
||||
0
|
||||
5 for
|
||||
1 +
|
||||
next
|
||||
puts
|
||||
5 5 == puts
|
||||
5 4 == puts
|
||||
0
|
||||
;
|
||||
""",
|
||||
expected_stdout="5\n1\n0\n",
|
||||
),
|
||||
TestCase(
|
||||
name="override_dup_compile_time",
|
||||
source=f"""
|
||||
import {ROOT / 'stdlib.sl'}
|
||||
|
||||
: dup
|
||||
6
|
||||
;
|
||||
compile-only
|
||||
|
||||
: emit-overridden
|
||||
"dup" use-l2-ct
|
||||
42
|
||||
dup
|
||||
int>string
|
||||
nil
|
||||
token-from-lexeme
|
||||
list-new
|
||||
swap
|
||||
list-append
|
||||
inject-tokens
|
||||
;
|
||||
immediate
|
||||
compile-only
|
||||
|
||||
: main
|
||||
emit-overridden
|
||||
puts
|
||||
0
|
||||
;
|
||||
""",
|
||||
expected_stdout="6\n",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def run_case(case: TestCase) -> None:
|
||||
print(f"[run] {case.name}")
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
src_path = Path(tmp) / f"{case.name}.sl"
|
||||
exe_path = Path(tmp) / f"{case.name}.out"
|
||||
src_path.write_text(case.source.strip() + "\n", encoding="utf-8")
|
||||
|
||||
compile_cmd = [str(PYTHON), str(COMPILER), str(src_path), "-o", str(exe_path)]
|
||||
compile_result = subprocess.run(
|
||||
compile_cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
cwd=ROOT,
|
||||
)
|
||||
if compile_result.returncode != 0:
|
||||
sys.stderr.write("[fail] compile error\n")
|
||||
sys.stderr.write(compile_result.stdout)
|
||||
sys.stderr.write(compile_result.stderr)
|
||||
raise SystemExit(compile_result.returncode)
|
||||
|
||||
run_result = subprocess.run(
|
||||
[str(exe_path)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
cwd=ROOT,
|
||||
)
|
||||
if run_result.returncode != 0:
|
||||
sys.stderr.write("[fail] execution error\n")
|
||||
sys.stderr.write(run_result.stdout)
|
||||
sys.stderr.write(run_result.stderr)
|
||||
raise SystemExit(run_result.returncode)
|
||||
|
||||
if run_result.stdout != case.expected_stdout:
|
||||
sys.stderr.write(f"[fail] output mismatch for {case.name}\n")
|
||||
sys.stderr.write("expected:\n" + case.expected_stdout)
|
||||
sys.stderr.write("got:\n" + run_result.stdout)
|
||||
raise SystemExit(1)
|
||||
|
||||
print(f"[ok] {case.name}")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
for case in CASES:
|
||||
run_case(case)
|
||||
print("[all tests passed]")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user