```shellscript
#!/bin/bash
# 性能回归测试脚本
# 从指定 commit 开始,逐个测试性能问题
set -e
# 配置
START_COMMIT="d3232f4135fcb71be0376e99a5436f2ede079d05"
TEST_FILE="test/index.test.js"
TEST_TIMEOUT=80
TARGET_OUTPUT="/post/test/2000"
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
echo -e "${YELLOW}开始性能回归测试...${NC}"
echo -e "${YELLOW}起始 commit: $START_COMMIT${NC}"
echo -e "${YELLOW}测试超时: ${TEST_TIMEOUT}秒${NC}"
echo -e "${YELLOW}目标输出: $TARGET_OUTPUT${NC}"
# 保存当前分支
ORIGINAL_BRANCH=$(git branch --show-current)
echo -e "${YELLOW}当前分支: $ORIGINAL_BRANCH${NC}"
# 备份原始测试文件
cp "$TEST_FILE" "${TEST_FILE}.backup"
# 获取从起始 commit 到当前的所有 commit
echo -e "${YELLOW}获取 commit 列表...${NC}"
COMMITS=($(git rev-list --reverse ${START_COMMIT}..HEAD))
# 添加起始 commit 到列表开头
COMMITS=("$START_COMMIT" "${COMMITS[@]}")
echo -e "${YELLOW}找到 ${#COMMITS[@]} 个 commit 需要测试${NC}"
# 清理函数
cleanup() {
echo -e "\n${YELLOW}清理环境...${NC}"
# 恢复原始测试文件
if [ -f "${TEST_FILE}.backup" ]; then
mv "${TEST_FILE}.backup" "$TEST_FILE"
echo -e "${GREEN}已恢复原始测试文件${NC}"
fi
# 回到原始分支
git checkout "$ORIGINAL_BRANCH" 2>/dev/null || true
echo -e "${GREEN}已切换回原始分支: $ORIGINAL_BRANCH${NC}"
# 停止可能还在运行的测试进程
pkill -f "npm run test:dev" 2>/dev/null || true
pkill -f "node.*test" 2>/dev/null || true
}
# 设置退出时清理
trap cleanup EXIT INT TERM
# 测试单个 commit 的函数
test_commit() {
local commit=$1
local commit_index=$2
local total_commits=$3
echo -e "\n${YELLOW}[${commit_index}/${total_commits}] 测试 commit: $commit${NC}"
# 切换到指定 commit
git checkout "$commit" --quiet
# 获取 commit 信息
local commit_msg=$(git log -1 --pretty=format:"%s" "$commit")
local commit_date=$(git log -1 --pretty=format:"%ci" "$commit")
echo -e "${YELLOW}提交信息: $commit_msg${NC}"
echo -e "${YELLOW}提交时间: $commit_date${NC}"
# 修改测试文件
sed -i 's/describe\.skip('\''性能'\''/describe.only('\''性能'\''/g' "$TEST_FILE"
if ! grep -q "describe\.only('性能'" "$TEST_FILE"; then
echo -e "${RED}❌ 未能修改测试文件${NC}"
return 1
fi
echo -e "${GREEN}✅ 已修改测试文件${NC}"
# 创建临时文件存储输出
local temp_output=$(mktemp)
local test_pid
echo -e "${YELLOW}开始运行测试...${NC}"
# 在后台运行测试,使用 tee 实时输出到终端和文件
timeout ${TEST_TIMEOUT}s npm run test:dev 2>&1 | tee "$temp_output" &
test_pid=$!
# 等待测试完成或超时
local elapsed=0
local found_target=false
while [ $elapsed -lt $TEST_TIMEOUT ]; do
sleep 1
elapsed=$((elapsed + 1))
# 检查是否找到目标输出
if grep -q "$TARGET_OUTPUT" "$temp_output" 2>/dev/null; then
found_target=true
echo -e "\n${GREEN}✅ 在第 ${elapsed} 秒找到预期输出: $TARGET_OUTPUT${NC}"
# 找到目标输出后立即跳出循环
break
fi
# 检查进程是否还在运行
if ! kill -0 $test_pid 2>/dev/null; then
echo -e "\n${YELLOW}测试进程已结束${NC}"
break
fi
# 显示进度(降低频率以免干扰实时输出)
if [ $((elapsed % 10)) -eq 0 ]; then
echo -e "\n${YELLOW}测试运行中... ${elapsed}/${TEST_TIMEOUT}秒${NC}"
fi
done
# 停止测试进程及其子进程
if kill -0 $test_pid 2>/dev/null; then
# 停止整个进程组
kill -TERM -$test_pid 2>/dev/null || true
sleep 2
kill -KILL -$test_pid 2>/dev/null || true
fi
wait $test_pid 2>/dev/null || true
# 分析结果
if [ "$found_target" = true ]; then
echo -e "${GREEN}✅ 测试正常!找到了预期的输出: $TARGET_OUTPUT${NC}"
echo -e "${GREEN}此 commit 没有性能问题,继续测试下一个...${NC}"
rm -f "$temp_output"
return 1 # 返回1继续测试下一个commit
else
echo -e "${RED}❌ 发现性能问题!${NC}"
echo -e "${RED}在 ${TEST_TIMEOUT} 秒内未找到预期输出: $TARGET_OUTPUT${NC}"
echo -e "${RED}🚨 这个 commit 导致了性能问题!${NC}"
echo -e "${RED}问题 commit: $commit${NC}"
echo -e "${RED}提交信息: $commit_msg${NC}"
echo -e "${RED}提交时间: $commit_date${NC}"
# 显示测试输出
echo -e "\n${YELLOW}测试输出摘要:${NC}"
tail -30 "$temp_output" || true
rm -f "$temp_output"
return 0 # 返回0表示找到了问题commit
fi
}
# 主循环
echo -e "\n${YELLOW}开始逐个测试 commit...${NC}"
for i in "${!COMMITS[@]}"; do
commit="${COMMITS[$i]}"
commit_index=$((i + 1))
total_commits=${#COMMITS[@]}
if test_commit "$commit" "$commit_index" "$total_commits"; then
echo -e "\n${RED}🚨 找到导致性能问题的 commit!${NC}"
exit 0
fi
# 恢复测试文件以便下次测试
git checkout HEAD -- "$TEST_FILE" 2>/dev/null || cp "${TEST_FILE}.backup" "$TEST_FILE"
done
echo -e "\n${GREEN}✅ 所有 commit 都测试完毕,未发现导致性能问题的版本${NC}"
echo -e "${GREEN}所有测试的 commit 都能正常找到预期输出${NC}"
exit 0