#!/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