添加了对未知长度数组的自动长度推导功能,支持字符串字面量和复合 初始化的数组长度计算。新增辅助函数resolve_array_length用于计算 数组实际长度,以及emit_array_initialization用于生成数组初始化 代码。 同时将AST转IR过程中的参数改为const引用,提高代码安全性。 新增IR构建器的借用检查机制,防止在借用期间进行重分配操作。 fix(ast): 为AST结构体添加详细注释说明字段用途
108 lines
3.2 KiB
Python
108 lines
3.2 KiB
Python
from pprint import PrettyPrinter
|
||
import subprocess
|
||
from pathlib import Path
|
||
import tomllib
|
||
import uuid
|
||
|
||
# 配置参数
|
||
WORKSPACE = Path(__file__).resolve().parent # 测试工作目录
|
||
TEST_DIR = Path(WORKSPACE)
|
||
CC_PATH = Path(WORKSPACE / "../../build/dev/scc")
|
||
|
||
def run_command(cmd, capture_output=True):
|
||
"""执行命令并捕获 stdout"""
|
||
try:
|
||
result = subprocess.run(
|
||
cmd,
|
||
cwd=WORKSPACE,
|
||
stdout=subprocess.PIPE if capture_output else None,
|
||
stderr=subprocess.PIPE,
|
||
text=True,
|
||
timeout=5, # 增加超时时间以防虚拟机启动慢
|
||
)
|
||
# 返回 stdout 用于获取返回值,同时检查是否有运行时错误
|
||
return result.stdout.strip(), result.stderr.strip(), result.returncode
|
||
except subprocess.TimeoutExpired:
|
||
return None, "Timeout expired", -1
|
||
except Exception as e:
|
||
return None, str(e), -1
|
||
|
||
def run_test(test_file, expected):
|
||
print(f"\nTesting {test_file}...")
|
||
|
||
# 使用唯一文件名避免并发冲突
|
||
unique_id = str(uuid.uuid4())[:8] # 简短的唯一标识符
|
||
exe_filename = f"test_{unique_id}.exe"
|
||
exe_path = WORKSPACE / exe_filename
|
||
|
||
# 1. 编译
|
||
compile_cmd = [str(CC_PATH), str(test_file), "-o", exe_filename, "--entry-point-symbol", "main"]
|
||
# 编译时关注 stderr 和返回码
|
||
_, compile_err, compile_ret = run_command(compile_cmd)
|
||
|
||
if not exe_path.exists() or compile_ret != 0:
|
||
print(f" Compilation failed: {compile_err}")
|
||
# 确保清理失败的输出文件
|
||
if exe_path.exists():
|
||
try:
|
||
exe_path.unlink()
|
||
except:
|
||
pass # 忽略清理失败
|
||
return False
|
||
|
||
# 2. 执行虚拟机并获取输出
|
||
vm_cmd = [str(exe_path)]
|
||
actual_output, vm_err, vm_ret = run_command(vm_cmd)
|
||
|
||
# 如果存在 stderr 且返回码异常(例如负数表示信号终止),则视为运行时错误
|
||
if vm_err and vm_ret < 0:
|
||
print(f" Runtime error: {vm_err}")
|
||
# 清理文件后返回
|
||
try:
|
||
exe_path.unlink()
|
||
except:
|
||
pass # 忽略清理失败
|
||
return False
|
||
|
||
# 3. 获取返回值 (修改进程返回值而非 stdout)
|
||
actual = vm_ret
|
||
|
||
# 4. 清理输出文件
|
||
try:
|
||
exe_path.unlink()
|
||
except:
|
||
pass # 忽略清理失败
|
||
|
||
# 5. 验证结果
|
||
# 注意:toml 中读取的 expected 可能是整数,actual 也是整数,直接比较
|
||
if actual == expected:
|
||
print(f" PASSED {test_file}")
|
||
return True
|
||
else:
|
||
print(f" FAILED: Expected '{expected}', got '{actual}'")
|
||
return False
|
||
|
||
def main():
|
||
passed = 0
|
||
total = 0
|
||
config = {}
|
||
config_path = WORKSPACE / "expect.toml"
|
||
|
||
if not config_path.exists():
|
||
print(f"Config file not found: {config_path}")
|
||
return
|
||
|
||
with open(config_path, "rb") as f:
|
||
config = tomllib.load(f)
|
||
PrettyPrinter().pprint(config)
|
||
|
||
for test_file, expected in config.get("return_val_cases", {}).items():
|
||
total += 1
|
||
if run_test(TEST_DIR / test_file, expected):
|
||
passed += 1
|
||
|
||
print(f"\nTest Summary: {passed}/{total} passed")
|
||
|
||
if __name__ == "__main__":
|
||
main()
|