119 lines
4.0 KiB
Python
Executable File
119 lines
4.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import os
|
|
import sys
|
|
import subprocess
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
def run_test(test_file: Path, compiler_bin: Path):
|
|
with open(test_file, "r", encoding="utf-8") as f:
|
|
lines = f.readlines()
|
|
|
|
expected_code = 0
|
|
code_lines = []
|
|
harness_lines = None
|
|
current_section = None
|
|
|
|
for line in lines:
|
|
trimmed = line.strip()
|
|
if trimmed.startswith("[") and trimmed.endswith("]"):
|
|
current_section = trimmed[1:-1]
|
|
if current_section == "harness":
|
|
harness_lines = []
|
|
elif current_section:
|
|
if current_section == "expected_return_code":
|
|
if trimmed:
|
|
expected_code = int(trimmed)
|
|
elif current_section == "code":
|
|
code_lines.append(line)
|
|
elif current_section == "harness":
|
|
harness_lines.append(line)
|
|
|
|
code = "".join(code_lines)
|
|
harness = "".join(harness_lines) if harness_lines is not None else None
|
|
|
|
with tempfile.TemporaryDirectory(prefix=f"compiler_test_{test_file.stem}_") as temp_dir_name:
|
|
temp_dir = Path(temp_dir_name)
|
|
|
|
code_path = temp_dir / "code.src"
|
|
obj_path = temp_dir / "code.o"
|
|
exec_path = temp_dir / "exec"
|
|
|
|
code_path.write_text(code, encoding="utf-8")
|
|
|
|
# 1. Invoke the custom compiler
|
|
comp_res = subprocess.run(
|
|
[str(compiler_bin), str(code_path), "-o", str(obj_path)],
|
|
capture_output=True,
|
|
text=True
|
|
)
|
|
|
|
if comp_res.returncode != 0:
|
|
out = comp_res.stdout.strip()
|
|
err = comp_res.stderr.strip()
|
|
error_msg = f"{out}\n{err}".strip() or "Compiler failed with no output"
|
|
raise Exception(error_msg)
|
|
|
|
# 2. Invoke GCC to link the object file (and compile harness if provided)
|
|
gcc_cmd = ["gcc", str(obj_path), "-o", str(exec_path)]
|
|
if harness is not None:
|
|
harness_path = temp_dir / "harness.c"
|
|
harness_path.write_text(harness, encoding="utf-8")
|
|
gcc_cmd.append(str(harness_path))
|
|
|
|
gcc_res = subprocess.run(gcc_cmd, capture_output=True, text=True)
|
|
|
|
if gcc_res.returncode != 0:
|
|
out = gcc_res.stdout.strip()
|
|
err = gcc_res.stderr.strip()
|
|
error_msg = f"{out}\n{err}".strip() or "GCC failed with no output"
|
|
raise Exception(error_msg)
|
|
|
|
# 3. Run the resulting executable
|
|
exec_res = subprocess.run([str(exec_path)], capture_output=True, text=True)
|
|
|
|
if exec_res.returncode != expected_code:
|
|
raise Exception(f"Expected return code {expected_code}, but got {exec_res.returncode}")
|
|
|
|
def main():
|
|
GREEN = '\033[92m'
|
|
RED = '\033[91m'
|
|
RESET = '\033[0m'
|
|
|
|
project_root = Path(__file__).resolve().parent
|
|
tests_dir = project_root / "tests"
|
|
compiler_bin = project_root / "target" / "debug" / "compiler"
|
|
|
|
# Automatically run cargo build to ensure the compiler is compiled and up to date
|
|
build_status = subprocess.run(["cargo", "build"], cwd=project_root)
|
|
if build_status.returncode != 0:
|
|
print("Failed to build the compiler binary.", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
test_files = sorted(tests_dir.glob("*.test"))
|
|
|
|
all_passed = True
|
|
failed_tests = []
|
|
|
|
for test_file in test_files:
|
|
test_name = test_file.name
|
|
|
|
# Format the output to align results, padding with dots
|
|
msg = f"Running {test_name} "
|
|
print(f"{msg:.<50} ", end="", file=sys.stderr, flush=True)
|
|
|
|
try:
|
|
run_test(test_file, compiler_bin)
|
|
print(f"{GREEN}ok{RESET}", file=sys.stderr)
|
|
except Exception as e:
|
|
print(f"{RED}FAIL{RESET}", file=sys.stderr)
|
|
print(f" Reason: {e}", file=sys.stderr)
|
|
all_passed = False
|
|
failed_tests.append(test_name)
|
|
|
|
if not all_passed:
|
|
print(f"\nOne or more E2E tests failed: {failed_tests}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
if __name__ == "__main__":
|
|
main() |