"""Validation helpers for system tests."""
import os
from pathlib import Path
from typing import Iterable, Tuple
import difflib
def _resolve_kernel_paths(kernel_root: Path, kernel_name: str) -> Tuple[Path, Path]:
kernel_root = Path(kernel_root)
generated_dir = kernel_root / "kernel_meta"
if not generated_dir.is_dir():
raise AssertionError(f"Expected kernel_meta directory missing: {generated_dir}")
pid = os.getpid()
generated_file = generated_dir / f"{kernel_name}_{pid}_kernel.cpp"
log_file = generated_dir / f"{kernel_name}_{pid}.log"
return generated_file, log_file
def compare_files(golden_path, codegen_path, encoding='utf-8'):
"""
比较两个文件的内容并显示差异
参数:
golden_path: 预期结果文件(golden file)的路径
codegen_path: 代码生成文件(codegen file)的路径
encoding: 文件编码格式,默认为utf-8
返回:
bool: 两个文件是否相同
"""
try:
with open(golden_path, 'r', encoding=encoding) as f:
golden_lines = f.readlines()
with open(codegen_path, 'r', encoding=encoding) as f:
codegen_lines = f.readlines()
except FileNotFoundError as e:
print(f"错误: 文件未找到 - {e.filename}", file=sys.stderr)
return False
except UnicodeDecodeError as e:
print(f"错误: 文件编码错误 - {e}", file=sys.stderr)
return False
differ = difflib.Differ()
diff = list(differ.compare(golden_lines, codegen_lines))
has_diff = any(line.startswith(('+', '-', '?')) for line in diff)
if has_diff:
print(f"文件 {golden_path} 和 {codegen_path} 内容不同:")
print("=" * 80)
for line in diff:
if line.startswith('+'):
print(f"\033[92m{line}\033[0m", end='')
elif line.startswith('-'):
print(f"\033[91m{line}\033[0m", end='')
elif line.startswith('?'):
print(f"\033[93m{line}\033[0m", end='')
else:
print(line, end='')
print("=" * 80)
else:
print(f"文件 {golden_path} 和 {codegen_path} 内容相同")
return not has_diff
def validate_codegen_output(kernel_root: Path, kernel_name: str, expected_source: Path) -> None:
"""Validate generated code matches expected source code."""
generated_file, _ = _resolve_kernel_paths(kernel_root, kernel_name)
if not generated_file.is_file():
generated_dir = generated_file.parent
available = sorted(p.name for p in generated_dir.iterdir())
raise AssertionError(
"Generated kernel file missing: "
f"expected {generated_file.name}; available={available}"
)
expected_source = Path(expected_source)
if not expected_source.is_file():
raise AssertionError(f"Expected source template missing: {expected_source}")
assert compare_files(expected_source, generated_file) is True
def validate_compile_options(
kernel_root: Path,
kernel_name: str,
expected_options: Iterable[str],
) -> None:
"""Ensure the compile log contains all expected options for the given kernel."""
generated_file, log_file = _resolve_kernel_paths(kernel_root, kernel_name)
if not log_file.is_file():
generated_dir = log_file.parent
available = sorted(p.name for p in generated_dir.iterdir())
raise AssertionError(
"Compile log missing: "
f"expected {log_file.name}; available={available}"
)
log_lines = log_file.read_text(encoding="utf-8").splitlines()
target_line = None
generated_path = str(generated_file)
for line in log_lines:
if "bisheng" in line and generated_path in line:
target_line = line
break
if target_line is None:
raise AssertionError(
"Failed to locate bisheng compile command for generated kernel "
f"{generated_file.name}"
)
missing = [opt for opt in expected_options if opt not in target_line]
if missing:
raise AssertionError(
"Missing expected compile options in bisheng command: "
+ ", ".join(missing)
)