You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nudt-compiler-cpp/2026test.sh

692 lines
21 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/usr/bin/env bash
set -u
set -o pipefail
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
TEST_ROOT="./2026test"
OUTPUT_DIR="./2026test_results"
COMPILER="./build/bin/compiler"
VERIFY_SCRIPT="./scripts/verify_asm.sh"
TIME_ORI_FILE="$OUTPUT_DIR/time_ori.txt"
TIME_OPT_FILE="$OUTPUT_DIR/time_opt.txt"
MAX_CASES=0
STOP_ON_FIRST_FAILURE=false
START_FROM=1
KEEP_OLD=true
KEEP_ORI=false
OPTIMIZE=true
CATEGORY="all"
SKIP_LIST=""
VERBOSE=false
TIMEOUT_MS=300000
total_time_sum=0
time_cases_count=0
SUCCESS=0
FAILED=0
SKIPPED=0
RUNTIME_OBJ="./build/test_runtime/sylib.o"
show_help() {
cat << 'EOF'
用法: ./2026test.sh [选项]
说明:
自动化执行 2026test 文件夹中的所有测试用例。
支持功能测试(functional)、隐含功能测试(h_functional)、性能测试(performance)。
使用编译器生成 AArch64 汇编,交叉编译链接后通过 qemu-aarch64 运行验证。
自动记录每个测试集的纯运行时间(qemu执行时间)并生成基线文件。
注意: 计时仅包含程序在qemu中的执行时间不包含编译和汇编链接时间。
选项:
-h, --help 显示此帮助信息
-n, --max N 最多运行 N 个测试用例 (0=不限制,默认: 0)
-s, --start-from N 从第 N 个测试用例开始 (默认: 1)
-x, --stop-on-fail 遇到第一个失败即停止
-k, --keep 保留旧输出目录,不删除
--keep-ori 保留现有 time_ori.txt 基线文件,不从 time_opt.txt 复制替换
-O, --optimize 启用编译器优化 (默认启用)
-O0, --no-optimize 禁用编译器优化
-c, --category CAT 指定测试类别: functional|h_functional|performance|all (默认: all)
--skip N1,N2,... 跳过指定编号的测试用例 (逗号分隔)
-v, --verbose 显示详细输出
-o, --output-dir DIR 指定输出目录 (默认: ./2026test_results)
-t, --timeout MS 单个测试超时时间(毫秒) (默认: 300000)
示例:
./2026test.sh # 运行所有测试 (默认启用优化)
./2026test.sh -c functional # 仅运行功能测试
./2026test.sh -c performance # 仅运行性能测试
./2026test.sh -n 10 # 只运行前10个测试
./2026test.sh -s 5 # 从第5个测试开始
./2026test.sh --skip 3,7,15 # 跳过第3、7、15个测试
./2026test.sh -c functional -n 5 -v # 功能测试前5个详细模式
./2026test.sh -O0 # 不启用优化
./2026test.sh -x # 失败即停止
./2026test.sh -c functional -s 10 -n 5 # 功能测试从第10个开始运行5个
EOF
}
parse_args() {
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
show_help
exit 0
;;
-n|--max)
MAX_CASES="$2"
shift 2
;;
-s|--start-from)
START_FROM="$2"
shift 2
;;
-x|--stop-on-fail)
STOP_ON_FIRST_FAILURE=true
shift
;;
-k|--keep)
KEEP_OLD=true
shift
;;
--keep-ori)
KEEP_ORI=true
shift
;;
-O|--optimize)
OPTIMIZE=true
shift
;;
-O0|--no-optimize)
OPTIMIZE=false
shift
;;
-c|--category)
CATEGORY="$2"
if [[ "$CATEGORY" != "functional" && "$CATEGORY" != "h_functional" && "$CATEGORY" != "performance" && "$CATEGORY" != "all" ]]; then
echo -e "${RED}错误: 类别必须是 functional|h_functional|performance|all${NC}"
exit 1
fi
shift 2
;;
--skip)
SKIP_LIST="$2"
shift 2
;;
-v|--verbose)
VERBOSE=true
shift
;;
-o|--output-dir)
OUTPUT_DIR="$2"
shift 2
;;
-t|--timeout)
TIMEOUT_MS="$2"
shift 2
;;
*)
echo -e "${RED}错误: 未知选项 $1${NC}"
show_help
exit 1
;;
esac
done
}
parse_args "$@"
check_prerequisites() {
local missing=0
if [[ ! -x "$COMPILER" ]]; then
echo -e "${RED}错误: 编译器不可执行: $COMPILER${NC}"
echo -e "${YELLOW}提示: 请先构建项目:${NC}"
echo -e "${YELLOW} cmake -S . -B build -DCMAKE_BUILD_TYPE=Release${NC}"
echo -e "${YELLOW} cmake --build build -j \"\$(nproc)\"${NC}"
missing=1
fi
if [[ ! -d "$TEST_ROOT" ]]; then
echo -e "${RED}错误: 测试目录不存在: $TEST_ROOT${NC}"
missing=1
fi
if ! command -v aarch64-linux-gnu-gcc >/dev/null 2>&1; then
echo -e "${RED}错误: 未找到 aarch64-linux-gnu-gcc无法汇编/链接${NC}"
echo -e "${YELLOW}提示: sudo apt install gcc-aarch64-linux-gnu${NC}"
missing=1
fi
if ! command -v qemu-aarch64 >/dev/null 2>&1; then
echo -e "${RED}错误: 未找到 qemu-aarch64无法运行生成的可执行文件${NC}"
echo -e "${YELLOW}提示: sudo apt install qemu-user${NC}"
missing=1
fi
local runtime_found=false
if [[ -f "./sylib/sylib.c" ]]; then
runtime_found=true
elif [[ -n "${SYSY_RUNTIME:-}" ]] && [[ -f "$SYSY_RUNTIME" ]]; then
runtime_found=true
else
local found
found=$(find . -path './build' -prune -o -path './.git' -prune -o -type f -name 'sylib.c' -print 2>/dev/null | head -n 1)
if [[ -n "$found" ]]; then
runtime_found=true
fi
fi
if [[ "$runtime_found" != "true" ]]; then
echo -e "${RED}错误: 未找到运行时库 sylib.c${NC}"
echo -e "${YELLOW}提示: 可通过环境变量 SYSY_RUNTIME 指定路径${NC}"
missing=1
fi
if [[ $missing -eq 1 ]]; then
exit 1
fi
}
check_prerequisites
if ! [[ "$START_FROM" =~ ^[0-9]+$ ]] || [[ "$START_FROM" -lt 1 ]]; then
echo -e "${RED}错误: --start-from 需要正整数${NC}"
exit 1
fi
if ! [[ "$MAX_CASES" =~ ^[0-9]+$ ]]; then
echo -e "${RED}错误: --max 需要非负整数${NC}"
exit 1
fi
declare -A SKIP_SET
if [[ -n "$SKIP_LIST" ]]; then
IFS=',' read -ra SKIP_ITEMS <<< "$SKIP_LIST"
for item in "${SKIP_ITEMS[@]}"; do
item=$(echo "$item" | xargs)
if [[ "$item" =~ ^[0-9]+$ ]]; then
SKIP_SET[$item]=1
fi
done
fi
if [[ "$KEEP_OLD" != "true" ]]; then
if [[ -f "$TIME_OPT_FILE" ]]; then
cp "$TIME_OPT_FILE" /tmp/_time_opt_backup_2026test.txt
fi
if [[ "$KEEP_ORI" == "true" && -f "$TIME_ORI_FILE" ]]; then
cp "$TIME_ORI_FILE" /tmp/_time_ori_backup_2026test.txt
fi
rm -rf "$OUTPUT_DIR"
fi
mkdir -p "$OUTPUT_DIR"
if [[ "$KEEP_ORI" == "true" ]]; then
if [[ -f /tmp/_time_ori_backup_2026test.txt ]]; then
cp /tmp/_time_ori_backup_2026test.txt "$TIME_ORI_FILE"
rm -f /tmp/_time_ori_backup_2026test.txt
echo -e "${BLUE}保留现有基线文件: time_ori.txt (--keep-ori)${NC}"
elif [[ -f "$TIME_ORI_FILE" ]]; then
echo -e "${BLUE}保留现有基线文件: time_ori.txt (--keep-ori)${NC}"
else
echo -e "${YELLOW}--keep-ori 已指定但未找到现有 time_ori.txt本次测试将无基线对比${NC}"
fi
elif [[ -f /tmp/_time_opt_backup_2026test.txt ]]; then
cp /tmp/_time_opt_backup_2026test.txt "$TIME_ORI_FILE"
rm -f /tmp/_time_opt_backup_2026test.txt
echo -e "${BLUE}已将上次优化时间文件复制为基线: time_opt.txt -> time_ori.txt${NC}"
elif [[ -f "$TIME_OPT_FILE" ]]; then
cp "$TIME_OPT_FILE" "$TIME_ORI_FILE"
echo -e "${BLUE}已将上次优化时间文件复制为基线: time_opt.txt -> time_ori.txt${NC}"
else
echo -e "${YELLOW}未找到上次优化时间文件 time_opt.txt本次测试将无基线对比${NC}"
fi
LOG_FILE="$OUTPUT_DIR/2026test_batch.log"
FAIL_FILE="$OUTPUT_DIR/failed_cases.txt"
ERROR_LOG_FILE="$OUTPUT_DIR/error_log.txt"
TIME_SUMMARY_FILE="$OUTPUT_DIR/time_summary.txt"
DETAIL_TIME_FILE="$OUTPUT_DIR/detail_time.txt"
declare -A ORI_TIME_MAP
if [[ -f "$TIME_ORI_FILE" ]]; then
while IFS='|' read -r entry_name entry_start entry_end entry_time; do
entry_name=$(echo "$entry_name" | xargs)
entry_time=$(echo "$entry_time" | grep -oP '\d+(?=ms)')
if [[ -n "$entry_name" && -n "$entry_time" ]]; then
ORI_TIME_MAP["$entry_name"]="$entry_time"
fi
done < "$TIME_ORI_FILE"
fi
lookup_ori_time() {
local key="$1"
echo "${ORI_TIME_MAP[$key]:-}"
}
format_time_comparison() {
local current_ms="$1"
local ori_ms="$2"
if [[ -z "$ori_ms" ]]; then
echo "${current_ms}ms"
return
fi
local diff=$((current_ms - ori_ms))
local pct=0
if [[ "$ori_ms" -gt 0 ]]; then
pct=$((diff * 100 / ori_ms))
fi
if [[ "$diff" -lt 0 ]]; then
echo "${current_ms}ms (${GREEN}${diff}ms / ${pct}%${NC})"
elif [[ "$diff" -gt 0 ]]; then
echo "${current_ms}ms (${RED}+${diff}ms / +${pct}%${NC})"
else
echo "${current_ms}ms (${BLUE}0ms / 0%${NC})"
fi
}
if [[ "$KEEP_OLD" != "true" ]]; then
: > "$LOG_FILE"
: > "$FAIL_FILE"
: > "$ERROR_LOG_FILE"
: > "$TIME_SUMMARY_FILE"
: > "$DETAIL_TIME_FILE"
fi
{
echo "2026test 批量测试日志 - $(date '+%Y-%m-%d %H:%M:%S')"
echo "TEST_ROOT=$TEST_ROOT"
echo "OUTPUT_DIR=$OUTPUT_DIR"
echo "CATEGORY=$CATEGORY"
echo "OPTIMIZE=$OPTIMIZE"
echo "MAX_CASES=$MAX_CASES"
echo "START_FROM=$START_FROM"
echo "SKIP_LIST=$SKIP_LIST"
echo "================================================"
} >> "$LOG_FILE"
collect_sy_files() {
local dirs=()
if [[ "$CATEGORY" == "all" ]]; then
dirs=("functional" "h_functional" "performance")
else
dirs=("$CATEGORY")
fi
for dir in "${dirs[@]}"; do
local full_dir="$TEST_ROOT/$dir"
if [[ -d "$full_dir" ]]; then
find "$full_dir" -type f -name '*.sy' -print0 | sort -z
fi
done
}
mapfile -d '' -t ALL_CASES < <(collect_sy_files)
TOTAL_FOUND=${#ALL_CASES[@]}
if [[ $TOTAL_FOUND -eq 0 ]]; then
echo -e "${YELLOW}未找到任何 .sy 用例,请检查目录: $TEST_ROOT${NC}"
exit 0
fi
get_timestamp_ms() {
date +%s%3N 2>/dev/null || date +%s000
}
get_category_name() {
local rel_path="$1"
local dir_name
dir_name=$(dirname "$rel_path")
dir_name=$(basename "$dir_name")
echo "$dir_name"
}
find_runtime_src() {
if [[ -n "${SYSY_RUNTIME:-}" ]] && [[ -f "$SYSY_RUNTIME" ]]; then
printf '%s\n' "$SYSY_RUNTIME"
return 0
fi
local candidates=("./sylib/sylib.c" "./sylib.c" "./runtime/sylib.c" "./lib/sylib.c")
for candidate in "${candidates[@]}"; do
if [[ -f "$candidate" ]]; then
printf '%s\n' "$candidate"
return 0
fi
done
local found
found=$(find . -path './build' -prune -o -path './.git' -prune -o -type f -name 'sylib.c' -print 2>/dev/null | head -n 1)
if [[ -n "$found" ]]; then
printf '%s\n' "$found"
return 0
fi
return 1
}
RUNTIME_SRC="$(find_runtime_src || true)"
if [[ -z "$RUNTIME_SRC" ]]; then
echo -e "${RED}错误: 未找到运行时库源码 sylib.c${NC}"
exit 1
fi
runtime_cache_dir="./build/test_runtime"
RUNTIME_OBJ="$runtime_cache_dir/sylib.o"
mkdir -p "$runtime_cache_dir"
if [[ ! -f "$RUNTIME_OBJ" ]] || [[ "$RUNTIME_SRC" -nt "$RUNTIME_OBJ" ]]; then
aarch64-linux-gnu-gcc -O2 -c "$RUNTIME_SRC" -o "$RUNTIME_OBJ"
fi
echo -e "${BLUE}========================================================${NC}"
echo -e "${BLUE} 2026test 批量测试${NC}"
echo -e "${BLUE}========================================================${NC}"
echo -e "${BLUE}测试根目录: $TEST_ROOT${NC}"
echo -e "${BLUE}测试类别: $CATEGORY${NC}"
echo -e "${BLUE}找到用例数: $TOTAL_FOUND${NC}"
echo -e "${BLUE}输出目录: $OUTPUT_DIR${NC}"
echo -e "${BLUE}编译器优化: $OPTIMIZE${NC}"
echo -e "${BLUE}计时方式: 仅qemu运行时间(不含编译/汇编)${NC}"
if [[ "$START_FROM" -gt 1 ]]; then
echo -e "${BLUE}起始用例: $START_FROM${NC}"
fi
if [[ "$MAX_CASES" -gt 0 ]]; then
echo -e "${BLUE}最大用例数: $MAX_CASES${NC}"
fi
if [[ "${#SKIP_SET[@]}" -gt 0 ]] 2>/dev/null; then
echo -e "${BLUE}跳过编号: ${!SKIP_SET[*]}${NC}"
fi
if [[ -f "$TIME_ORI_FILE" ]]; then
echo -e "${BLUE}基线对比: $TIME_ORI_FILE (已加载)${NC}"
if [[ "$KEEP_ORI" == "true" ]]; then
echo -e "${BLUE}基线模式: 保留现有 (--keep-ori)${NC}"
else
echo -e "${BLUE}基线模式: 从time_opt.txt复制替换${NC}"
fi
else
echo -e "${YELLOW}基线对比: 无 (首次运行或无time_opt.txt)${NC}"
fi
echo -e "${BLUE}========================================================${NC}"
echo ""
declare -a BASELINE_ENTRIES=()
TOTAL=0
EXECUTED=0
for file in "${ALL_CASES[@]}"; do
TOTAL=$((TOTAL + 1))
if [[ $TOTAL -lt $START_FROM ]]; then
continue
fi
if [[ $MAX_CASES -gt 0 && $EXECUTED -ge $MAX_CASES ]]; then
break
fi
if [[ ${SKIP_SET[$TOTAL]+_} ]]; then
SKIPPED=$((SKIPPED + 1))
rel_path="${file#$TEST_ROOT/}"
echo -e "${CYAN}[$TOTAL] $(basename "$file") ... 跳过${NC}"
echo "[SKIPPED] $file (user skip)" >> "$LOG_FILE"
continue
fi
EXECUTED=$((EXECUTED + 1))
rel_path="${file#$TEST_ROOT/}"
filename="$(basename "$file")"
base_name="${filename%.sy}"
rel_dir="$(dirname "$rel_path")"
input_dir="$TEST_ROOT/$rel_dir"
category_name=$(get_category_name "$rel_path")
case_out_dir="$OUTPUT_DIR/$rel_dir"
mkdir -p "$case_out_dir"
rm -f "$case_out_dir/$base_name.s"
rm -f "$case_out_dir/$base_name.o"
rm -f "$case_out_dir/$base_name"
rm -f "$case_out_dir/$base_name.stdout"
rm -f "$case_out_dir/$base_name.actual.out"
if [[ "$VERBOSE" == "true" ]]; then
echo -e "${YELLOW}[$TOTAL] $category_name/$filename ... ${NC}"
else
echo -ne "${YELLOW}[$TOTAL] $category_name/$filename ... ${NC}"
fi
asm_file="$case_out_dir/$base_name.s"
exe="$case_out_dir/$base_name"
stdin_file="$input_dir/$base_name.in"
expected_file="$input_dir/$base_name.out"
stdout_file="$case_out_dir/$base_name.stdout"
actual_file="$case_out_dir/$base_name.actual.out"
compile_ok=true
set +e
if [[ "$OPTIMIZE" == "true" ]]; then
"$COMPILER" -O --emit-asm "$file" > "$asm_file" 2>/dev/null
else
"$COMPILER" --emit-asm "$file" > "$asm_file" 2>/dev/null
fi
compile_code=$?
set -e
if [[ $compile_code -ne 0 ]]; then
compile_ok=false
fi
if $compile_ok; then
set +e
aarch64-linux-gnu-gcc "$asm_file" "$RUNTIME_OBJ" -o "$exe" 2>/dev/null
link_code=$?
set -e
if [[ $link_code -ne 0 ]]; then
compile_ok=false
fi
fi
if ! $compile_ok; then
FAILED=$((FAILED + 1))
echo "$file" >> "$FAIL_FILE"
echo -e "${RED}编译/链接失败${NC}"
echo "[FAILED] $file (compile/link error)" >> "$LOG_FILE"
{
echo "========================================"
echo "测试失败: $file"
echo "原因: 编译或链接失败"
echo "时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo "========================================"
} >> "$ERROR_LOG_FILE"
if [[ "$STOP_ON_FIRST_FAILURE" == "true" ]]; then
echo -e "${RED}========================================================${NC}"
echo -e "${RED}在第一个失败处停止测试${NC}"
echo -e "${RED}失败文件: $file${NC}"
echo -e "${RED}日志: $LOG_FILE${NC}"
echo -e "${RED}错误日志: $ERROR_LOG_FILE${NC}"
echo -e "${RED}========================================================${NC}"
break
fi
continue
fi
exec_start_ms=$(get_timestamp_ms)
exec_start_human=$(date '+%Y-%m-%d %H:%M:%S.%3N')
set +e
if [[ -f "$stdin_file" ]]; then
qemu-aarch64 -L /usr/aarch64-linux-gnu -s 104857600 "$exe" < "$stdin_file" > "$stdout_file" 2>/dev/null
else
qemu-aarch64 -L /usr/aarch64-linux-gnu -s 104857600 "$exe" < /dev/null > "$stdout_file" 2>/dev/null
fi
exit_status=$?
set -e
exec_end_ms=$(get_timestamp_ms)
exec_end_human=$(date '+%Y-%m-%d %H:%M:%S.%3N')
exec_elapsed_ms=$((exec_end_ms - exec_start_ms))
{
cat "$stdout_file"
if [[ -s "$stdout_file" ]] && (( $(tail -c 1 "$stdout_file" | wc -l) == 0 )); then
printf '\n'
fi
printf '%s\n' "$exit_status"
} > "$actual_file"
output_ok=true
if [[ -f "$expected_file" ]]; then
if command -v python3 >/dev/null 2>&1; then
if ! python3 - "$expected_file" "$actual_file" <<'PY' >/dev/null 2>&1
import sys
from pathlib import Path
def canon(path: str) -> bytes:
data = Path(path).read_bytes()
data = data.replace(b'\r\n', b'\n')
while data.endswith(b'\n'):
data = data[:-1]
lines = data.split(b'\n')
lines = [line.rstrip() for line in lines]
return b'\n'.join(lines)
sys.exit(0 if canon(sys.argv[1]) == canon(sys.argv[2]) else 1)
PY
then
output_ok=false
fi
else
local_expected="/tmp/_test_expected_$$"
local_actual="/tmp/_test_actual_$$"
tr -d '\r' < "$expected_file" > "$local_expected"
tr -d '\r' < "$actual_file" > "$local_actual"
if ! diff -u "$local_expected" "$local_actual" > /dev/null 2>&1; then
output_ok=false
fi
rm -f "$local_expected" "$local_actual"
fi
fi
baseline_entry="${category_name}/${base_name}"
if $output_ok; then
SUCCESS=$((SUCCESS + 1))
if [[ "$exec_elapsed_ms" =~ ^[0-9]+$ ]]; then
total_time_sum=$((total_time_sum + exec_elapsed_ms))
time_cases_count=$((time_cases_count + 1))
fi
ori_time=$(lookup_ori_time "$baseline_entry")
time_display=$(format_time_comparison "$exec_elapsed_ms" "$ori_time")
if [[ "$VERBOSE" == "true" ]]; then
echo -e " ${GREEN}成功${NC} | 开始: $exec_start_human | 结束: $exec_end_human | 运行: $time_display"
else
echo -e "${GREEN}成功${NC} ($time_display)"
fi
echo "[SUCCESS] $file | start=$exec_start_human | end=$exec_end_human | exec=${exec_elapsed_ms}ms" >> "$LOG_FILE"
echo "$rel_path: ${exec_elapsed_ms}ms" >> "$TIME_SUMMARY_FILE"
echo "$baseline_entry | $exec_start_human | $exec_end_human | ${exec_elapsed_ms}ms" >> "$DETAIL_TIME_FILE"
BASELINE_ENTRIES+=("$baseline_entry ${exec_elapsed_ms}ms")
else
FAILED=$((FAILED + 1))
echo "$file" >> "$FAIL_FILE"
if [[ "$VERBOSE" == "true" ]]; then
echo -e " ${RED}失败${NC} | 开始: $exec_start_human | 结束: $exec_end_human | 运行: ${exec_elapsed_ms}ms | 输出不匹配"
else
echo -e "${RED}失败${NC} (运行${exec_elapsed_ms}ms, 输出不匹配)"
fi
echo "[FAILED] $file (output mismatch) | start=$exec_start_human | end=$exec_end_human | exec=${exec_elapsed_ms}ms" >> "$LOG_FILE"
{
echo "========================================"
echo "测试失败: $file"
echo "原因: 输出不匹配"
echo "运行时间: ${exec_elapsed_ms}ms"
echo "开始时间: $exec_start_human"
echo "结束时间: $exec_end_human"
echo "时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo "========================================"
} >> "$ERROR_LOG_FILE"
if [[ "$STOP_ON_FIRST_FAILURE" == "true" ]]; then
echo -e "${RED}========================================================${NC}"
echo -e "${RED}在第一个失败处停止测试${NC}"
echo -e "${RED}失败文件: $file${NC}"
echo -e "${RED}日志: $LOG_FILE${NC}"
echo -e "${RED}错误日志: $ERROR_LOG_FILE${NC}"
echo -e "${RED}========================================================${NC}"
break
fi
fi
done
: > "$TIME_OPT_FILE"
for entry in "${BASELINE_ENTRIES[@]}"; do
local_name=$(echo "$entry" | sed 's/ [0-9]*ms$//')
local_time=$(echo "$entry" | grep -oP '\d+(?=ms$)')
if [[ -n "$local_name" && -n "$local_time" ]]; then
echo "$local_name | | | ${local_time}ms" >> "$TIME_OPT_FILE"
fi
done
RATE="0.00"
if [[ $EXECUTED -gt 0 ]]; then
RATE=$(awk -v s="$SUCCESS" -v t="$EXECUTED" 'BEGIN { printf "%.2f", (s*100.0)/t }')
fi
echo ""
echo -e "${BLUE}========================================================${NC}"
echo -e "${BLUE} 2026test 批量测试完成${NC}"
echo -e "${BLUE}========================================================${NC}"
echo -e "${BLUE}总用例数: $TOTAL_FOUND${NC}"
echo -e "${BLUE}执行用例: $EXECUTED${NC}"
echo -e "${GREEN}成功: $SUCCESS${NC}"
echo -e "${RED}失败: $FAILED${NC}"
echo -e "${CYAN}跳过: $SKIPPED${NC}"
echo -e "${BLUE}成功率: ${RATE}%${NC}"
if [[ $time_cases_count -gt 0 ]]; then
avg_time=$((total_time_sum / time_cases_count))
echo -e "${BLUE}平均运行时间: ${avg_time}ms (基于 ${time_cases_count} 个成功用例)${NC}"
echo -e "${BLUE}总运行时间: ${total_time_sum}ms${NC}"
fi
echo ""
echo -e "${BLUE}日志文件: $LOG_FILE${NC}"
echo -e "${BLUE}时间汇总: $TIME_SUMMARY_FILE${NC}"
echo -e "${BLUE}详细时间: $DETAIL_TIME_FILE${NC}"
echo -e "${BLUE}基线时间: $TIME_ORI_FILE${NC}"
echo -e "${BLUE}本次时间: $TIME_OPT_FILE${NC}"
if [[ $FAILED -gt 0 ]]; then
echo -e "${RED}失败清单: $FAIL_FILE${NC}"
echo -e "${RED}错误日志: $ERROR_LOG_FILE${NC}"
fi
echo -e "${BLUE}========================================================${NC}"
if [[ $FAILED -gt 0 ]]; then
exit 1
fi
exit 0