#!/usr/bin/env bash # tests/run-godot: Run Godot client test suite via gdUnit4 (D-030) # Exit: 0 = all pass, non-zero = failure # Stdout: {"suite":"godot","total":N,"passed":N,"failed":N,"duration_ms":N} # # --filter: accepts a test filename stem (e.g. "test_protocol" → runs test_protocol.gd only) set -euo pipefail FILTER="" while [[ $# -gt 0 ]]; do case "$1" in --filter) FILTER="${2:-}"; shift 2 ;; --filter=*) FILTER="${1#--filter=}"; shift ;; *) echo "Unknown argument: $1" >&2; exit 2 ;; esac done REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" GODOT=$(command -v godot4 2>/dev/null || command -v godot 2>/dev/null || echo "") if [[ -z "$GODOT" ]]; then printf '{"suite":"godot","total":0,"passed":0,"failed":0,"duration_ms":0,"error":"godot not found in PATH"}\n' exit 1 fi # Resolve the test target: directory or specific file if [[ -n "$FILTER" ]]; then # Support bare name (test_protocol) or full path (test_protocol.gd) if [[ "$FILTER" == res://* ]]; then TEST_TARGET="$FILTER" elif [[ "$FILTER" == *.gd ]]; then TEST_TARGET="res://tests/$FILTER" else TEST_TARGET="res://tests/${FILTER}.gd" fi else TEST_TARGET="res://tests/" fi START_MS=$(date +%s%3N) TMPOUT=$(mktemp) set +e "$GODOT" --headless --path "$REPO_ROOT/client" \ -s res://addons/gdUnit4/bin/GdUnitCmdTool.gd \ --ignoreHeadlessMode \ -c \ -a "$TEST_TARGET" \ 2>&1 | tee "$TMPOUT" >&2 EXIT_CODE=${PIPESTATUS[0]} set -e END_MS=$(date +%s%3N) DURATION_MS=$((END_MS - START_MS)) _extract_num() { local haystack="$1" pattern="$2" echo "$haystack" | grep -oiE "[0-9]+ $pattern" | grep -oE '^[0-9]+' || echo 0 } # gdUnit4 outputs per-suite statistics: "N test cases | X errors | Y failures | ..." # and a summary: "Executed test cases : (X/N)" or "Executed test cases : (X/N), Z skipped" TOTAL=0; PASSED=0; FAILED=0 # Sum errors + failures across all suite statistics lines STATS_LINES=$(grep -oE "[0-9]+ test cases \| [0-9]+ errors \| [0-9]+ failures" "$TMPOUT" || true) if [[ -n "$STATS_LINES" ]]; then TOTAL=$(echo "$STATS_LINES" | grep -oE '^[0-9]+' | awk '{s+=$1} END {print s}') ERRORS=$(echo "$STATS_LINES" | grep -oE '[0-9]+ errors' | grep -oE '^[0-9]+' | awk '{s+=$1} END {print s}') FAILURES=$(echo "$STATS_LINES" | grep -oE '[0-9]+ failures' | grep -oE '^[0-9]+' | awk '{s+=$1} END {print s}') FAILED=$(( ${ERRORS:-0} + ${FAILURES:-0} )) PASSED=$(( TOTAL - FAILED )) fi # Fallback: parse "Executed test cases : (X/N)" for total if stats parse failed if [[ "$TOTAL" -eq 0 ]]; then EXEC_LINE=$(grep -oE "Executed test cases : \([0-9]+/[0-9]+\)" "$TMPOUT" | tail -1 || true) if [[ -n "$EXEC_LINE" ]]; then TOTAL=$(echo "$EXEC_LINE" | grep -oE '/[0-9]+\)' | grep -oE '[0-9]+') PASSED=$(echo "$EXEC_LINE" | grep -oE '\([0-9]+/' | grep -oE '[0-9]+') FAILED=$(( TOTAL - PASSED )) fi fi rm -f "$TMPOUT" printf '{"suite":"godot","total":%d,"passed":%d,"failed":%d,"duration_ms":%d}\n' \ "${TOTAL:-0}" "${PASSED:-0}" "${FAILED:-0}" "$DURATION_MS" exit $EXIT_CODE