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