AI_UIAutomation/scripts/overnight-soak-ios.sh

87 lines
4.4 KiB
Bash

#!/bin/bash
# iOS 夜间添加稳定性 soak:循环【清空设备 → 逐个添加】到次日 09:00。
# 关键:iOS 走 WDA(8100),WDA 易掉 → 每轮检查并自动重起;每个设备添加前 terminate/launch 把 app 复位到首页
# (iOS navigateToAddPage 无回首页恢复)。计数沿用 SKIP 护栏(SKIP 不计 PASS)。
# nohup caffeinate -dimsu bash scripts/overnight-soak-ios.sh > reports/soak-ios.out 2>&1 &
set -u
cd "$(dirname "$0")/.."
export PLATFORM=ios
export SOAK_MODE=1
UDID=00008110-001A34303AE9801E
BUNDLE=com.wohand.wohand
DEADLINE=$(date -v+1d -v9H -v0M -v0S +%s 2>/dev/null) || DEADLINE=$(($(date +%s) + 50400))
TS=$(date +%Y%m%d-%H%M%S)
mkdir -p reports
LOG="reports/soak-ios-$TS.log"
DETAIL="reports/soak-ios-$TS.detail.log"
DEVICES=(
"meter|tests/meter/meter_connect.test.ts|"
"remote|tests/remote/remote_connect.test.ts|"
"hub|tests/hub/hub_connect.test.ts|export HUB_DEVICE='HubMini 9I'"
)
PASS=(); FAIL=(); SKIP=()
for i in "${!DEVICES[@]}"; do PASS[$i]=0; FAIL[$i]=0; SKIP[$i]=0; done
log(){ echo "[$(date '+%m-%d %H:%M:%S')] $*" | tee -a "$LOG"; }
wda_sid(){ curl -s --max-time 5 http://localhost:8100/status 2>/dev/null | grep -oE '[0-9A-F]{8}-[0-9A-F-]{27,}' | head -1; }
wda_ready(){ curl -s --max-time 5 http://localhost:8100/status 2>/dev/null | grep -q '"ready" : true'; }
ensure_wda(){
if wda_ready; then return 0; fi
log " WDA 不在,重起 WDA..."
pkill -f iproxy 2>/dev/null; sleep 1
curl -s --max-time 280 -X POST http://127.0.0.1:4723/session -H 'Content-Type: application/json' \
-d "{\"capabilities\":{\"alwaysMatch\":{\"platformName\":\"iOS\",\"appium:automationName\":\"XCUITest\",\"appium:udid\":\"$UDID\",\"appium:platformVersion\":\"26.5\",\"appium:deviceName\":\"iPhone\",\"appium:bundleId\":\"$BUNDLE\",\"appium:wdaLocalPort\":8100,\"appium:noReset\":true,\"appium:newCommandTimeout\":3600}}}" >>"$DETAIL" 2>&1
sleep 3
wda_ready && log " WDA 已就绪" || log " WDA 重起失败(本轮可能全失败)"
}
app_home(){ local sid; sid=$(wda_sid); [ -z "$sid" ] && return 0
curl -s --max-time 15 -X POST "http://localhost:8100/session/$sid/wda/apps/terminate" -H 'Content-Type: application/json' -d "{\"bundleId\":\"$BUNDLE\"}" >/dev/null 2>&1
sleep 1
curl -s --max-time 15 -X POST "http://localhost:8100/session/$sid/wda/apps/launch" -H 'Content-Type: application/json' -d "{\"bundleId\":\"$BUNDLE\"}" >/dev/null 2>&1
sleep 2
}
names=""; for spec in "${DEVICES[@]}"; do names+="${spec%%|*} "; done
log "===== iOS SOAK 开始,截止 $(date -r $DEADLINE '+%m-%d %H:%M' 2>/dev/null) ; 设备: $names====="
rm -f reports/.results.json
ensure_wda
cycle=0
while [ "$(date +%s)" -lt "$DEADLINE" ]; do
cycle=$((cycle+1))
log "----- Cycle $cycle -----"
ensure_wda
app_home
if RESET_DEVICES=1 npx ts-node scripts/reset-devices.ts >>"$DETAIL" 2>&1; then log " reset 完成"; else log " reset 异常(继续)"; fi
for i in "${!DEVICES[@]}"; do
spec="${DEVICES[$i]}"; name="${spec%%|*}"; rest="${spec#*|}"; file="${rest%%|*}"; envs="${rest#*|}"
echo "[$(date '+%H:%M:%S')] >>> Cycle $cycle $name" >>"$DETAIL"
ensure_wda; app_home # 每个设备添加前复位到首页(iOS 导航无回首页恢复)
runlog=$(mktemp)
( eval "$envs"; npx vitest run "$file" ) >"$runlog" 2>&1; rc=$?
cat "$runlog" >>"$DETAIL"
if grep -q "\[SKIP\]" "$runlog"; then
SKIP[$i]=$(( SKIP[$i] + 1 )); log " $name SKIP(设备已存在 → 不计通过)"
elif [ $rc -eq 0 ] && grep -q "\[PASS\]" "$runlog"; then
PASS[$i]=$(( PASS[$i] + 1 )); log " $name PASS"
else
FAIL[$i]=$(( FAIL[$i] + 1 )); log " $name FAIL (见 $DETAIL)"
fi
rm -f "$runlog"
done
tally=""; for i in "${!DEVICES[@]}"; do n="${DEVICES[$i]%%|*}"; tot=$(( PASS[$i] + FAIL[$i] + SKIP[$i] )); tally+="$n ${PASS[$i]}/${tot}(skip${SKIP[$i]}) "; done
log " 累计通过率(pass/total,skip另计): $tally"
done
log "===== iOS SOAK 结束,共 $cycle 轮 ====="
final=""; for i in "${!DEVICES[@]}"; do n="${DEVICES[$i]%%|*}"; tot=$(( PASS[$i] + FAIL[$i] + SKIP[$i] )); final+="$n=${PASS[$i]}/${tot}(skip${SKIP[$i]}) "; done
log "最终: $final"
log "生成汇总报告..."
npx ts-node scripts/soak-report.ts "ios-$TS" >>"$DETAIL" 2>&1 && log "时间线汇总: reports/soak-report-ios-$TS.*" || log "时间线汇总生成失败"
npx ts-node scripts/gen-combined.ts "SoakIOS_$TS" >>"$DETAIL" 2>&1 && log "逐用例报告: reports/SoakIOS_${TS}_*.html" || log "逐用例报告生成失败"