Files
mtgodot-poc/script/live_smoke_test.sh
T
shenleiandClaude Opus 5 f39a55fdd5 feat(playable): 首个 Mac 联网内测版 STB-01 soak 工具与发布状态文档
- 新增 script/playable_soak.sh:先做 soak 配置/资源校验,无 --allow-gameplay
  只校验不启动客户端;N(>=10) 次独立正常退出运行 + 墙钟 soak,失败即停止后续
  运行并记 BLOCKED;写本批次 release-manifest.json 后聚合
- run_client_gate.sh:确认的故障代理对所有 suite 启动(共享场景的退出运行也经代理);
  soak 超时下限只约束 soak 客户端(validate_soak 增加 soak_client 参数)
- 新增本地 127.0.0.1 故障代理、RSS 采样/内存判定、窗口/指标/流程模块及其测试
- forest_mob_render_test 输出 PASS/FAIL 标记,供 rendering_batch_test.sh 识别
- 新增 docs/FIRST-MAC-PLAYABLE-STATUS.md:如实记录 PASS/BLOCKED、已知问题与环境需求
- .gitignore 排除本地场景配置、凭据文件与运行输出

离线回归:rendering_batch_test.sh failures=0,playable_gate_test.sh PASS,
node 夹具测试 PASS。未联网运行,未重建候选包。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ft3kXpNdLbKEfg5uDLfqVF
2026-09-11 22:06:48 +09:00

171 lines
5.1 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# 真实 40250 服务器端到端冒烟测试。
#
# 用法:
# ./script/live_smoke_test.sh
# ./script/live_smoke_test.sh --no-build
# ./script/live_smoke_test.sh --no-mutations
# ./script/live_smoke_test.sh --no-reconnect
#
# 账号和密码优先从环境变量读取;未提供时安全地交互输入,不写入脚本。
# 服务器地址与客户端一样经 project/net/serverinfo.gd 解析,不在脚本里写死。
# 不要在本脚本中启用 set -x。
set -euo pipefail
set +x
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO="$(cd "$SCRIPT_DIR/.." && pwd)"
APP="${MT_SMOKE_APP:-$REPO/build/export/mtgodot-poc.app}"
EXECUTABLE="$APP/Contents/MacOS/mtgodot-poc"
ARTIFACT_DIR="${MT_SMOKE_OUTPUT:-$REPO/build/live-smoke}"
LOG="$ARTIFACT_DIR/live-smoke.log"
REPORT="$ARTIFACT_DIR/live-smoke-report.json"
CONFIG_FILE="${MT_SMOKE_CONFIG:-$REPO/.env.live-smoke.local}"
BUILD=1
MUTATIONS=1
RECONNECT=1
usage() {
cat <<'EOF'
用法: ./script/live_smoke_test.sh [选项]
选项:
--no-build 使用现有 APP,不重新打包
--no-mutations 只做登录、进游戏和数据读取,不发送移动/聊天/重连包
--no-reconnect 做移动和聊天,但跳过重连
--reconnect-only 只测试登录/主动重连,不发送移动、聊天或施法
--help 显示帮助
环境变量:
MT_ACCOUNT / MT_PASSWORD 可预先提供账号密码,未提供时交互输入
MT_CHAR_SLOT=N 指定角色槽位;不设置时自动选择第一个角色
MT_GODOT 解析 serverinfo 用的 godot,默认 godot
EOF
}
while [ "$#" -gt 0 ]; do
case "$1" in
--no-build) BUILD=0 ;;
--no-mutations) MUTATIONS=0; RECONNECT=0 ;;
--no-reconnect) RECONNECT=0 ;;
--reconnect-only) MUTATIONS=0; RECONNECT=1 ;;
--help) usage; exit 0 ;;
*) echo "未知选项: $1" >&2; usage >&2; exit 2 ;;
esac
shift
done
cd "$REPO"
mkdir -p "$ARTIFACT_DIR"
if [ -f "$CONFIG_FILE" ]; then
# shellcheck disable=SC1090
. "$CONFIG_FILE"
fi
if [ -z "${MT_ACCOUNT:-}" ]; then
read -r -s -p "账号: " MT_ACCOUNT
echo
fi
if [ -z "${MT_PASSWORD:-}" ]; then
read -r -s -p "密码: " MT_PASSWORD
echo
fi
if [ -z "${MT_ACCOUNT:-}" ] || [ -z "${MT_PASSWORD:-}" ]; then
echo "账号和密码不能为空" >&2
exit 2
fi
if [ "$BUILD" -eq 1 ]; then
echo "== build release =="
./build-macos-client.sh release
fi
if [ ! -x "$EXECUTABLE" ]; then
echo "找不到可执行 APP$EXECUTABLE" >&2
echo "请先运行 ./build-macos-client.sh release" >&2
exit 2
fi
echo "== check server ports =="
# 登录页默认 server 0 / 第一个频道;与 AppFlow 同一 ServerInfo 解析。
ADDRESS_LINE="$("${MT_GODOT:-godot}" --headless --path project --script print_server_address.gd 2>/dev/null | grep '^SERVER_ADDRESS ' || true)"
if [ -z "$ADDRESS_LINE" ]; then
echo "无法从 serverinfo 解析服务器地址" >&2
exit 2
fi
read -r AUTH_HOST AUTH_PORT GAME_HOST GAME_PORT <<<"$(node -e '
const a = JSON.parse(process.argv[1].slice("SERVER_ADDRESS ".length));
process.stdout.write([a.auth_host, a.auth_port, a.game_host, a.game_port].join(" "));
' "$ADDRESS_LINE")"
for endpoint in "$AUTH_HOST:$AUTH_PORT" "$GAME_HOST:$GAME_PORT"; do
if ! nc -G 3 -z "${endpoint%:*}" "${endpoint##*:}" >/dev/null 2>&1; then
echo "服务器端口不可达:$endpoint" >&2
exit 3
fi
done
if pgrep -x mtgodot-poc >/dev/null; then
echo "已有客户端运行,请先关闭后再执行独立登录测试。" >&2
exit 2
fi
export MT_PROTOCOL=classic
export MT_AUTOLOGIN=1
export MT_TEST_MODE=smoke
export MT_ACCOUNT
export MT_PASSWORD
export MT_SMOKE_MUTATIONS="$MUTATIONS"
export MT_SMOKE_RECONNECT="$RECONNECT"
export MT_TEST_REPORT="$REPORT"
rm -f "$REPORT"
echo "== run live smoke =="
echo "log: $LOG"
echo "report: $REPORT"
set +e
# 日志落盘前脱敏;PIPESTATUS[0] 仍是 APP 的真实退出码。
env -u MT_ASSETS "$EXECUTABLE" 2>&1 | node script/redact_stream.mjs | tee "$LOG"
APP_STATUS=${PIPESTATUS[0]}
set -e
if [ ! -f "$REPORT" ]; then
echo "未生成本次测试报告,APP 可能在测试状态机启动前崩溃。" >&2
echo "LIVE_SMOKE RESULT: FAIL (missing report)" >&2
exit 4
fi
echo "== report =="
if command -v jq >/dev/null 2>&1; then
REPORT_STATUS=$(jq -r '.status // empty' "$REPORT")
jq -r '"status=" + .status, "duration=" + (.duration_seconds|tostring) + "s", ( .failures[]? | "failure=" + . )' "$REPORT"
else
REPORT_STATUS=""
if grep -q '"status"[[:space:]]*:[[:space:]]*"PASS"' "$REPORT"; then
REPORT_STATUS=PASS
fi
grep -E '"status"|"failures"' "$REPORT" || true
fi
if [ "$APP_STATUS" -ne 0 ]; then
echo "LIVE_SMOKE RESULT: FAIL (APP exit=$APP_STATUS)" >&2
echo "最近的崩溃报告:"
ls -1t "$HOME/Library/Logs/DiagnosticReports/mtgodot-poc-"*.ips 2>/dev/null | head -n 3 || true
exit "$APP_STATUS"
fi
# 与 package_render_test.sh / validate_playable_report.mjs 相同的完整日志门禁。
if grep -nE 'SCRIPT ERROR|Parse Error|^ERROR:|leaked at exit|shaders of type .* were never freed' "$LOG"; then
echo "LIVE_SMOKE RESULT: FAIL (log exit gate)" >&2
exit 1
fi
if [ "$REPORT_STATUS" != "PASS" ]; then
echo "LIVE_SMOKE RESULT: FAIL (report status=$REPORT_STATUS)" >&2
exit 1
fi
echo "LIVE_SMOKE RESULT: PASS"