163 lines
6 KiB
Bash
163 lines
6 KiB
Bash
#!/usr/bin/env bash
|
|
# Test subcommands: data validation, full test pipeline, cross-language
|
|
# golden-vector parity, coverage reports.
|
|
#
|
|
# Split out of dev.sh. `./run test` runs GUT + cargo + vitest + stability;
|
|
# `./run test:golden` is the 3-consumer parity gate; `./run validate`
|
|
# runs the JSON schema validator standalone.
|
|
|
|
cmd_validate() {
|
|
echo -e "${BLUE}Validating game data JSON schemas...${NC}"
|
|
python3 "$REPO_ROOT/tools/validate-game-data.py" "$@"
|
|
echo -e "${BLUE}Validating audio manifests...${NC}"
|
|
python3 "$REPO_ROOT/tools/audio-validate.py"
|
|
echo -e "${BLUE}Checking audio license ledger...${NC}"
|
|
python3 "$REPO_ROOT/tools/audio-licenses-render.py" --check
|
|
}
|
|
|
|
# Run Rust workspace tests, preferring nextest when available.
|
|
_cargo_test_workspace() {
|
|
if _have_tool cargo-nextest "cargo install cargo-nextest --locked"; then
|
|
(cd "$SIMULATOR_DIR" && cargo nextest run --workspace)
|
|
else
|
|
(cd "$SIMULATOR_DIR" && cargo test --workspace)
|
|
fi
|
|
}
|
|
|
|
cmd_test() {
|
|
local exit_code=0
|
|
|
|
echo -e "${BLUE}Running GUT tests (GDScript)...${NC}"
|
|
WAYLAND_DISPLAY="${WAYLAND_DISPLAY:-wayland-0}" \
|
|
XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" \
|
|
$GODOT_BIN --path "$GAME_DIR" --headless --script res://addons/gut/gut_cmdln.gd \
|
|
-gexit "$@" || exit_code=$?
|
|
|
|
echo ""
|
|
echo -e "${BLUE}Running Rust tests (simulator)...${NC}"
|
|
_cargo_test_workspace || exit_code=$?
|
|
|
|
echo ""
|
|
echo -e "${BLUE}Running vitest (guide)...${NC}"
|
|
pnpm --prefix "$GUIDE_DIR" test || exit_code=$?
|
|
|
|
echo ""
|
|
echo -e "${BLUE}Running stability test (20s game boot)...${NC}"
|
|
_run_stability_test || exit_code=$?
|
|
|
|
return $exit_code
|
|
}
|
|
|
|
_run_stability_test() {
|
|
# Boots the game → world_map, waits 20s, captures screenshot.
|
|
# If the game crashes before capture, exit code is non-zero.
|
|
local LOG="/tmp/stability_test_$$.log"
|
|
cmd_screenshot "stability_test" "world_map" "20" > "$LOG" 2>&1
|
|
if [ $? -ne 0 ]; then
|
|
echo -e "${RED}FAIL: Game crashed during stability test${NC}"
|
|
grep -E "SCRIPT ERROR|ERROR:" "$LOG" | head -5
|
|
return 1
|
|
fi
|
|
if grep -q "Captured:" "$LOG"; then
|
|
echo -e "${GREEN}PASS: Game stable for 20s, screenshot captured${NC}"
|
|
return 0
|
|
else
|
|
echo -e "${RED}FAIL: Game ran but no screenshot captured${NC}"
|
|
cat "$LOG" | tail -5
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
cmd_coverage() {
|
|
# Generate coverage reports for Rust + TypeScript.
|
|
# Graceful degradation: each tool warn-skips if not installed.
|
|
local exit_code=0
|
|
|
|
echo -e "${BLUE}[1/2] Rust coverage (cargo llvm-cov)...${NC}"
|
|
if _have_tool cargo-llvm-cov "cargo install cargo-llvm-cov --locked"; then
|
|
(cd "$SIMULATOR_DIR" && cargo llvm-cov --workspace --html) || exit_code=$?
|
|
echo -e "${BLUE}HTML report: $SIMULATOR_DIR/target/llvm-cov/html/index.html${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "${BLUE}[2/2] TypeScript coverage (pnpm -r test:coverage)...${NC}"
|
|
# --if-present: pnpm exits 0 when no package defines the script, which is
|
|
# the graceful-degrade behavior we want. Without it, pnpm exits 1 with
|
|
# ERR_PNPM_RECURSIVE_RUN_NO_SCRIPT, which would falsely fail verify.
|
|
pnpm -r --if-present run test:coverage || exit_code=$?
|
|
|
|
return $exit_code
|
|
}
|
|
|
|
cmd_test_golden() {
|
|
# Cross-language golden-vector parity gate.
|
|
#
|
|
# Each fixture in src/simulator/tests/golden/vectors/*.json is consumed by
|
|
# three runners that MUST produce bitwise-identical output. Divergence =
|
|
# release blocker (FFI marshaling / non-determinism / SOT violation).
|
|
#
|
|
# See src/simulator/tests/golden/README.md for the fixture shape and
|
|
# ~/.claude/instructions/rust-code-standards.md §"Testing Strategy" for rationale.
|
|
|
|
local vectors_dir="$SIMULATOR_DIR/tests/golden/vectors"
|
|
local exit_code=0
|
|
|
|
if [ ! -d "$vectors_dir" ]; then
|
|
echo -e "${RED}Golden vectors directory missing: $vectors_dir${NC}"
|
|
return 1
|
|
fi
|
|
|
|
local vectors
|
|
vectors=$(find "$vectors_dir" -maxdepth 1 -name '*.json' -type f | sort)
|
|
|
|
if [ -z "$vectors" ]; then
|
|
echo -e "${YELLOW}No golden vectors yet — add JSON fixtures to:${NC}"
|
|
echo -e " $vectors_dir"
|
|
echo -e "${YELLOW}See $SIMULATOR_DIR/tests/golden/README.md for the fixture shape.${NC}"
|
|
return 0
|
|
fi
|
|
|
|
local count
|
|
count=$(echo "$vectors" | wc -l | tr -d ' ')
|
|
echo -e "${BLUE}Found $count golden vector(s) — running 3-consumer parity check${NC}"
|
|
echo ""
|
|
|
|
# Consumer 1: Rust native
|
|
echo -e "${BLUE}[1/3] Rust native consumer (cargo test --test golden)${NC}"
|
|
if ! (cd "$SIMULATOR_DIR" && cargo test --workspace --test golden 2>&1); then
|
|
echo -e "${RED}FAIL: Rust golden tests${NC}"
|
|
exit_code=1
|
|
fi
|
|
echo ""
|
|
|
|
# Consumer 2: WASM via Vitest (guide simulation worker)
|
|
echo -e "${BLUE}[2/3] WASM consumer (pnpm test — golden suite)${NC}"
|
|
if ! pnpm --prefix "$GUIDE_DIR" test -- --run golden 2>&1; then
|
|
echo -e "${RED}FAIL: WASM golden tests${NC}"
|
|
exit_code=1
|
|
fi
|
|
echo ""
|
|
|
|
# Consumer 3: GDExtension via headless Godot + GUT
|
|
echo -e "${BLUE}[3/3] GDExtension consumer (headless Godot + GUT ffi/)${NC}"
|
|
local ffi_dir="$GAME_DIR/engine/tests/ffi"
|
|
if [ -d "$ffi_dir" ] && [ -n "$(find "$ffi_dir" -maxdepth 1 -name 'test_golden_*.gd' -print -quit 2>/dev/null)" ]; then
|
|
WAYLAND_DISPLAY="${WAYLAND_DISPLAY:-wayland-0}" \
|
|
XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" \
|
|
$GODOT_BIN --path "$GAME_DIR" --headless \
|
|
--script res://addons/gut/gut_cmdln.gd \
|
|
-gdir=res://engine/tests/ffi -gprefix=test_golden_ -gexit 2>&1 \
|
|
|| exit_code=$?
|
|
else
|
|
echo -e "${YELLOW}SKIP: No GDExt golden tests yet at $ffi_dir/test_golden_*.gd${NC}"
|
|
fi
|
|
echo ""
|
|
|
|
if [ $exit_code -eq 0 ]; then
|
|
echo -e "${GREEN}All 3 consumers agree on $count vector(s)${NC}"
|
|
else
|
|
echo -e "${RED}Divergence detected — release blocker${NC}"
|
|
echo -e "${RED}See src/simulator/tests/golden/README.md for triage guidance${NC}"
|
|
fi
|
|
return $exit_code
|
|
}
|