添加了printf/sprintf格式化打印函数库的详细README文档,包含: - 库的功能介绍和设计目标 - 使用方法和CMake配置选项 - 支持的格式说明符和API文档 - 贡献指南和许可证信息 fix(test): 改进测试脚本的并发安全性和资源清理 修改了测试脚本以支持并发执行,避免多进程测试时的文件冲突: - 使用UUID生成唯一的可执行文件名 - 改进了编译和执行过程中的错误处理 - 添加了确保临时文件被正确清理的机制 - 移除了原有的固定文件名和清理逻辑
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]
|
||
# 编译时关注 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()
|