#!/usr/bin/env bash
# 串行执行IR测试脚本,实时输出结果
set -euo pipefail
PROJECT_ROOT=$(cd "$(dirname "$0")/.." ; pwd)
# 默认参数
TEST_CASE_DIR="${PROJECT_ROOT}/test/test_case"
TEST_RESULT_DIR="${PROJECT_ROOT}/test/test_result/ir"
RUN_EXEC=false
VERBOSE=false
OPT_FLAG="-O1" # 默认开启优化
# 解析命令行参数
while [[ $# -gt 0 ]]; do
case "$1" in
--run)
RUN_EXEC=true
;;
--verbose|-v)
VERBOSE=true
;;
--O0)
OPT_FLAG=""
;;
--O1)
OPT_FLAG="-O1"
;;
--test-dir=*)
TEST_CASE_DIR="${1#*=}"
;;
--result-dir=*)
TEST_RESULT_DIR="${1#*=}"
;;
*)
echo "未知参数: $1" >&2
echo "用法: $0 [--run] [--O0|--O1] [--verbose] [--test-dir=
] [--result-dir=]" >&2
exit 1
;;
esac
shift
done
# 检查编译器是否存在
compiler="${PROJECT_ROOT}/build/bin/compiler"
if [[ ! -x "$compiler" ]]; then
echo "错误:未找到编译器 $compiler" >&2
echo "请先构建项目: mkdir -p build && cd build && cmake .. && make -j" >&2
exit 1
fi
# 创建输出目录
mkdir -p "$TEST_RESULT_DIR"
# 统计变量
total_tests=0
passed_tests=0
failed_tests=0
# 汇总日志文件
summary_log="${TEST_RESULT_DIR}/summary.log"
> "$summary_log"
# 失败测试列表
failed_list=""
echo "=== 开始IR测试 ==="
echo "测试目录: $TEST_CASE_DIR"
echo "结果目录: $TEST_RESULT_DIR"
echo "优化级别: ${OPT_FLAG:--O0}"
echo "运行可执行文件: $RUN_EXEC"
echo ""
# 串行遍历所有测试用例
while read -r test_file; do
total_tests=$((total_tests + 1))
# 计算相对路径
full_path=$(readlink -f "$test_file")
test_case_path=$(readlink -f "$TEST_CASE_DIR")
relative_path="${full_path#$test_case_path}"
# 确保路径以 / 开头
if [[ "${relative_path:0:1}" != "/" ]]; then
relative_path="/$relative_path"
fi
# 计算输出文件路径
base=$(basename "$test_file")
stem="${base%.sy}"
output_file="${TEST_RESULT_DIR}/${relative_path%.sy}.ll"
output_dir=$(dirname "$output_file")
# 创建输出目录
mkdir -p "$output_dir"
# 获取输入和预期输出文件路径
input_dir=$(dirname "$test_file")
stdin_file="${input_dir}/${stem}.in"
expected_file="${input_dir}/${stem}.out"
# 每个测试用例的详细日志文件
test_log="${output_dir}/${stem}.log"
> "$test_log"
echo "[$total_tests] 处理: $relative_path"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 开始处理: $relative_path" >> "$test_log"
echo "输入文件: $test_file" >> "$test_log"
echo "输出目录: $output_dir" >> "$test_log"
# 生成IR
if $VERBOSE; then
echo " 生成IR..."
fi
echo "步骤1: 生成IR" >> "$test_log"
# 计时编译
compile_start=$(date +%s.%N)
set +e
"$compiler" "$test_file" -IR -o "$output_file" $OPT_FLAG 2>&1
ir_status=$?
set -e
compile_end=$(date +%s.%N)
compile_time=$(python3 -c "print(f'{float($compile_end)-float($compile_start):.3f}s')" 2>/dev/null || echo "-")
if [[ $ir_status -ne 0 ]]; then
echo " ✗ IR生成失败"
echo "结果: FAILED (IR生成失败)" >> "$test_log"
echo "错误信息:" >> "$test_log"
cat "$output_file" >> "$test_log"
failed_tests=$((failed_tests + 1))
failed_list="$failed_list\n[$total_tests] $relative_path - IR生成失败"
if $VERBOSE; then
cat "$output_file"
fi
echo ""
continue
fi
echo "IR文件: $output_file" >> "$test_log"
if $VERBOSE; then
echo " ✓ IR已生成: $output_file"
fi
# 如果需要运行可执行文件
if [[ "$RUN_EXEC" == true ]]; then
echo "步骤2: 编译和运行" >> "$test_log"
if ! command -v llc >/dev/null 2>&1; then
echo " 警告: 未找到 llc,跳过执行测试" >&2
echo "警告: 未找到 llc,跳过执行测试" >> "$test_log"
echo "结果: SKIPPED (缺少llc)" >> "$test_log"
passed_tests=$((passed_tests + 1))
echo ""
continue
fi
if ! command -v clang >/dev/null 2>&1; then
echo " 警告: 未找到 clang,跳过执行测试" >&2
echo "警告: 未找到 clang,跳过执行测试" >> "$test_log"
echo "结果: SKIPPED (缺少clang)" >> "$test_log"
passed_tests=$((passed_tests + 1))
echo ""
continue
fi
obj="${output_dir}/${stem}.o"
exe="${output_dir}/${stem}"
stdout_file="${output_dir}/${stem}.stdout"
actual_file="${output_dir}/${stem}.actual.out"
# 编译IR为目标文件
if $VERBOSE; then
echo " 编译IR..."
fi
echo "编译IR: llc -filetype=obj $output_file -o $obj" >> "$test_log"
llc -filetype=obj "$output_file" -o "$obj" 2>/dev/null
if [[ $? -ne 0 ]]; then
echo " ✗ IR编译失败"
echo "结果: FAILED (IR编译失败)" >> "$test_log"
failed_tests=$((failed_tests + 1))
failed_list="$failed_list\n[$total_tests] $relative_path - IR编译失败"
echo ""
continue
fi
echo "目标文件: $obj" >> "$test_log"
# 链接为可执行文件
echo "链接: clang $obj ${PROJECT_ROOT}/sylib/sylib.c -o $exe -lm" >> "$test_log"
clang "$obj" "${PROJECT_ROOT}/sylib/sylib.c" -o "$exe" -lm 2>/dev/null
if [[ $? -ne 0 ]]; then
echo " ✗ 链接失败"
echo "结果: FAILED (链接失败)" >> "$test_log"
failed_tests=$((failed_tests + 1))
failed_list="$failed_list\n[$total_tests] $relative_path - 链接失败"
echo ""
continue
fi
echo "可执行文件: $exe" >> "$test_log"
# 运行可执行文件
if $VERBOSE; then
echo " 运行..."
fi
echo "运行命令: $exe" >> "$test_log"
if [[ -f "$stdin_file" ]]; then
echo "标准输入: $stdin_file" >> "$test_log"
fi
run_start=$(date +%s.%N)
set +e
if [[ -f "$stdin_file" ]]; then
(ulimit -s unlimited; "$exe" < "$stdin_file") > "$stdout_file"
else
(ulimit -s unlimited; "$exe") > "$stdout_file"
fi
status=$?
set -e
run_end=$(date +%s.%N)
run_time=$(python3 -c "print(f'{float($run_end)-float($run_start):.3f}s')" 2>/dev/null || echo "-")
echo "退出码: $status" >> "$test_log"
echo "标准输出:" >> "$test_log"
cat "$stdout_file" >> "$test_log"
# 保存实际输出(包含退出码)
{
cat "$stdout_file"
if [[ -s "$stdout_file" ]] && (( $(tail -c 1 "$stdout_file" | wc -l) == 0 )); then
printf '\n'
fi
printf '%s\n' "$status"
} > "$actual_file"
# 比对输出
echo "步骤3: 比对输出" >> "$test_log"
if [[ -f "$expected_file" ]]; then
echo "预期输出: $expected_file" >> "$test_log"
echo "实际输出: $actual_file" >> "$test_log"
if diff <(tr -d '\r' < "$expected_file" | sed -e '$a\') \
<(tr -d '\r' < "$actual_file" | sed -e '$a\') > /dev/null 2>&1; then
echo " ✓ 编译:${compile_time} 运行:${run_time}"
echo "结果: PASSED (输出匹配)" >> "$test_log"
passed_tests=$((passed_tests + 1))
else
echo " ✗ 输出不匹配"
echo "结果: FAILED (输出不匹配)" >> "$test_log"
echo "差异:" >> "$test_log"
diff <(tr -d '\r' < "$expected_file" | sed -e '$a\') \
<(tr -d '\r' < "$actual_file" | sed -e '$a\') >> "$test_log" 2>&1 || true
failed_tests=$((failed_tests + 1))
failed_list="$failed_list\n[$total_tests] $relative_path - 输出不匹配"
if $VERBOSE; then
echo " 预期:"
cat "$expected_file"
echo " 实际:"
cat "$actual_file"
fi
fi
else
echo " ? 无预期输出文件,跳过比对 (编译:${compile_time})"
echo "警告: 无预期输出文件,跳过比对" >> "$test_log"
echo "结果: SKIPPED (无预期输出)" >> "$test_log"
passed_tests=$((passed_tests + 1))
fi
else
echo "步骤2: 跳过执行测试 (--run未启用)" >> "$test_log"
echo "结果: PASSED (仅IR生成)" >> "$test_log"
passed_tests=$((passed_tests + 1))
echo " ✓ 编译:${compile_time}"
fi
echo ""
done < <(find "$TEST_CASE_DIR" -name "*.sy" | sort)
# 输出统计结果到终端
echo "=== 测试完成 ==="
echo "总测试数: $total_tests"
echo "通过: $passed_tests"
echo "失败: $failed_tests"
# 写入汇总日志
echo "=== IR测试汇总报告 ===" > "$summary_log"
echo "测试时间: $(date '+%Y-%m-%d %H:%M:%S')" >> "$summary_log"
echo "测试目录: $TEST_CASE_DIR" >> "$summary_log"
echo "结果目录: $TEST_RESULT_DIR" >> "$summary_log"
echo "运行可执行文件: $RUN_EXEC" >> "$summary_log"
echo "" >> "$summary_log"
echo "=== 统计结果 ===" >> "$summary_log"
echo "总测试数: $total_tests" >> "$summary_log"
echo "通过: $passed_tests" >> "$summary_log"
echo "失败: $failed_tests" >> "$summary_log"
echo "成功率: $((passed_tests * 100 / total_tests))%" >> "$summary_log"
echo "" >> "$summary_log"
if [[ $failed_tests -gt 0 ]]; then
echo "=== 失败测试列表 ===" >> "$summary_log"
echo -e "$failed_list" >> "$summary_log"
fi
echo "详细日志已保存到各测试用例目录"
echo "汇总日志: $summary_log"
if [[ $failed_tests -eq 0 ]]; then
echo "所有测试通过!"
exit 0
else
echo "有 $failed_tests 个测试失败"
exit 1
fi