feat(ci): test runner scripts and Makefile integration (#270)
Six test runner scripts at tests/: run-rust, run-godot, run-ipc-fixtures, run-ipc-protocol, run-ipc-integration, run-all. Plus run-ipc-benchmark for Layer 3 timing. All produce structured JSON stdout, support --filter, and exit 0/non-zero. Makefile targets updated to delegate to scripts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,8 @@ GODOT := $(shell command -v godot4 2>/dev/null || command -v godot 2>/dev/null)
|
||||
pre-pr-server pre-pr-client pre-pr-content \
|
||||
fixtures-client golden-diff golden-update \
|
||||
checklist-validate checklist-generate \
|
||||
perf-baseline debug-schedule
|
||||
perf-baseline debug-schedule \
|
||||
test-ipc-fixtures test-ipc-protocol test-ipc-integration test-ipc-benchmark
|
||||
|
||||
# --- Configuration ---
|
||||
|
||||
@@ -23,11 +24,15 @@ help:
|
||||
@echo " make stop Stop any running server instance"
|
||||
@echo " make client Run the Godot client (test mode)"
|
||||
@echo " make server Run the Rust simulation server"
|
||||
@echo " make test Run all tests"
|
||||
@echo " make lint Run all linters"
|
||||
@echo " make ci Run full CI pipeline locally"
|
||||
@echo " make ci-client Run client CI checks"
|
||||
@echo " make ci-server Run server CI checks"
|
||||
@echo " make test Run all tests"
|
||||
@echo " make test-ipc-fixtures Layer 1: IPC serialization fixtures"
|
||||
@echo " make test-ipc-protocol Layer 2: mock IPC protocol tests"
|
||||
@echo " make test-ipc-integration Layer 3: real subprocess round-trip"
|
||||
@echo " make test-ipc-benchmark IPC latency benchmark (blocked: #555/#556)"
|
||||
@echo " make lint Run all linters"
|
||||
@echo " make ci Run full CI pipeline locally"
|
||||
@echo " make ci-client Run client CI checks"
|
||||
@echo " make ci-server Run server CI checks"
|
||||
@echo " make check-protocol Verify server/client protocol versions match"
|
||||
@echo " make clean Remove build artifacts and caches"
|
||||
@echo ""
|
||||
@@ -128,7 +133,7 @@ stop:
|
||||
test: test-server test-client
|
||||
|
||||
test-server:
|
||||
cd server && cargo nextest run
|
||||
tests/run-rust
|
||||
|
||||
fixtures:
|
||||
cd server && cargo test --test gen_fixtures -- --ignored
|
||||
@@ -169,8 +174,19 @@ golden-update:
|
||||
@echo "Review with: git diff --cached -- server/tests/golden/"
|
||||
|
||||
test-client:
|
||||
@test -n "$(GODOT)" || { echo "Godot not found. Run 'make setup' first."; exit 1; }
|
||||
$(GODOT) --headless --path client -s res://addons/gdUnit4/bin/GdUnitCmdTool.gd --ignoreHeadlessMode -a res://tests/
|
||||
tests/run-godot
|
||||
|
||||
test-ipc-fixtures:
|
||||
tests/run-ipc-fixtures
|
||||
|
||||
test-ipc-protocol:
|
||||
tests/run-ipc-protocol
|
||||
|
||||
test-ipc-integration:
|
||||
tests/run-ipc-integration
|
||||
|
||||
test-ipc-benchmark:
|
||||
tests/run-ipc-benchmark
|
||||
|
||||
# --- Lint ---
|
||||
|
||||
|
||||
+19
-3
@@ -62,11 +62,27 @@ The server must be running before the client connects (subprocess launch will be
|
||||
### Test
|
||||
|
||||
```bash
|
||||
make test # Run all tests
|
||||
make test-server # cargo test in server/
|
||||
make test-client # gdUnit4 tests (headless runner pending)
|
||||
make test # Run all tests (test-server + test-client)
|
||||
make test-server # Rust tests via tests/run-rust (cargo nextest, JSON summary)
|
||||
make test-client # Godot tests via tests/run-godot (gdUnit4 headless, JSON summary)
|
||||
```
|
||||
|
||||
The IPC test layers (D-030) have dedicated targets:
|
||||
|
||||
```bash
|
||||
make test-ipc-fixtures # Layer 1: serialization round-trip fixtures
|
||||
make test-ipc-protocol # Layer 2: mock LocalBridge protocol tests
|
||||
make test-ipc-integration # Layer 3: real subprocess round-trip (+ benchmark when ready)
|
||||
make test-ipc-benchmark # IPC latency benchmark (blocked: #555/#556 handshake)
|
||||
```
|
||||
|
||||
Each `tests/run-*` script outputs a JSON summary to stdout and streams progress to stderr:
|
||||
```json
|
||||
{"suite":"rust","total":42,"passed":42,"failed":0,"duration_ms":1230}
|
||||
```
|
||||
|
||||
All scripts accept `--filter <name>` to run a subset of tests. They are whitelistable for agent use (no TTY prompts, no interactive input).
|
||||
|
||||
Server tests use Rust's built-in test framework with `#[cfg(test)]` inline tests and `tests/` integration tests (D-030). Client tests use gdUnit4 (D-030).
|
||||
|
||||
### Cross-Encoder Fixtures
|
||||
|
||||
Executable
+77
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env bash
|
||||
# tests/run-all: Run all test suites in order (D-030)
|
||||
# Invokes run-rust, run-godot, run-ipc-fixtures, run-ipc-protocol, run-ipc-integration.
|
||||
# Exit: 0 = all suites pass, non-zero = any suite failed
|
||||
# Stdout: {"suite":"all","total":N,"passed":N,"failed":N,"duration_ms":N,"suites":[...]}
|
||||
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
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
PASS_ARGS=()
|
||||
[[ -n "$FILTER" ]] && PASS_ARGS+=(--filter "$FILTER")
|
||||
|
||||
SUITES=(
|
||||
run-rust
|
||||
run-godot
|
||||
run-ipc-fixtures
|
||||
run-ipc-protocol
|
||||
run-ipc-integration
|
||||
)
|
||||
|
||||
START_MS=$(date +%s%3N)
|
||||
|
||||
OVERALL_TOTAL=0
|
||||
OVERALL_PASSED=0
|
||||
OVERALL_FAILED=0
|
||||
OVERALL_EXIT=0
|
||||
SUITE_RESULTS=""
|
||||
|
||||
for suite in "${SUITES[@]}"; do
|
||||
script="$SCRIPT_DIR/$suite"
|
||||
if [[ ! -x "$script" ]]; then
|
||||
echo "Warning: $script not found or not executable — skipping" >&2
|
||||
continue
|
||||
fi
|
||||
|
||||
SUITE_OUT=$(mktemp)
|
||||
set +e
|
||||
"$script" "${PASS_ARGS[@]}" >"$SUITE_OUT"
|
||||
SUITE_EXIT=$?
|
||||
set -e
|
||||
|
||||
SUITE_JSON=$(cat "$SUITE_OUT")
|
||||
rm -f "$SUITE_OUT"
|
||||
|
||||
# Accumulate totals from the suite's JSON output
|
||||
S_TOTAL=$(echo "$SUITE_JSON" | grep -oE '"total":[0-9]+' | grep -oE '[0-9]+' || echo 0)
|
||||
S_PASSED=$(echo "$SUITE_JSON" | grep -oE '"passed":[0-9]+' | grep -oE '[0-9]+' || echo 0)
|
||||
S_FAILED=$(echo "$SUITE_JSON" | grep -oE '"failed":[0-9]+' | grep -oE '[0-9]+' || echo 0)
|
||||
|
||||
OVERALL_TOTAL=$(( OVERALL_TOTAL + ${S_TOTAL:-0} ))
|
||||
OVERALL_PASSED=$(( OVERALL_PASSED + ${S_PASSED:-0} ))
|
||||
OVERALL_FAILED=$(( OVERALL_FAILED + ${S_FAILED:-0} ))
|
||||
[[ $SUITE_EXIT -ne 0 ]] && OVERALL_EXIT=1
|
||||
|
||||
# Build suites array for JSON
|
||||
if [[ -n "$SUITE_RESULTS" ]]; then
|
||||
SUITE_RESULTS="$SUITE_RESULTS,$SUITE_JSON"
|
||||
else
|
||||
SUITE_RESULTS="$SUITE_JSON"
|
||||
fi
|
||||
done
|
||||
|
||||
END_MS=$(date +%s%3N)
|
||||
DURATION_MS=$((END_MS - START_MS))
|
||||
|
||||
printf '{"suite":"all","total":%d,"passed":%d,"failed":%d,"duration_ms":%d,"suites":[%s]}\n' \
|
||||
"$OVERALL_TOTAL" "$OVERALL_PASSED" "$OVERALL_FAILED" "$DURATION_MS" "$SUITE_RESULTS"
|
||||
exit $OVERALL_EXIT
|
||||
Executable
+88
@@ -0,0 +1,88 @@
|
||||
#!/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
|
||||
Executable
+59
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env bash
|
||||
# tests/run-ipc-benchmark: IPC round-trip latency benchmark (#342, D-020)
|
||||
#
|
||||
# Runs server/tests/ipc_bench.rs via `cargo test --release --test ipc_bench`.
|
||||
# Parses IPC_BENCH_RESULT:{json} from output and outputs the result JSON.
|
||||
#
|
||||
# Latency budget: p99 <= 5ms (D-020: "~1-5ms serialization latency per tick").
|
||||
#
|
||||
# NOTE (#342): Handshake step is stubbed in ipc_bench.rs pending #555 (server
|
||||
# protocol handshake) and #556 (client handshake). Full clean timing requires
|
||||
# a working handshake before the measurement loop starts.
|
||||
#
|
||||
# Exit: 0 = benchmark passed (p99 within threshold), non-zero = failure
|
||||
# Stdout: {"p50_ms":N,"p95_ms":N,"p99_ms":N,"threshold_ms":5,"passed":true,"rounds":100}
|
||||
set -euo pipefail
|
||||
|
||||
ITERATIONS=100
|
||||
THRESHOLD_MS=5
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--iterations) ITERATIONS="${2:-100}"; shift 2 ;;
|
||||
--iterations=*) ITERATIONS="${1#--iterations=}"; shift ;;
|
||||
--threshold-ms) THRESHOLD_MS="${2:-5}"; shift 2 ;;
|
||||
--filter) shift 2 ;; # ignored — benchmark has no test filter
|
||||
--filter=*) shift ;;
|
||||
*) echo "Unknown argument: $1" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
START_MS=$(date +%s%3N)
|
||||
TMPOUT=$(mktemp)
|
||||
|
||||
set +e
|
||||
cd "$REPO_ROOT/server" && \
|
||||
cargo test --release --test ipc_bench -- --ignored --nocapture 2>&1 | tee "$TMPOUT" >&2
|
||||
EXIT_CODE=${PIPESTATUS[0]}
|
||||
set -e
|
||||
|
||||
END_MS=$(date +%s%3N)
|
||||
DURATION_MS=$((END_MS - START_MS))
|
||||
|
||||
# Extract IPC_BENCH_RESULT:{json} line from output
|
||||
RESULT_LINE=$(grep "^IPC_BENCH_RESULT:" "$TMPOUT" | tail -1 || true)
|
||||
rm -f "$TMPOUT"
|
||||
|
||||
if [[ -n "$RESULT_LINE" ]]; then
|
||||
# Strip the prefix and output the JSON
|
||||
echo "${RESULT_LINE#IPC_BENCH_RESULT:}"
|
||||
else
|
||||
# No result line — test failed to produce output
|
||||
printf '{"p50_ms":0,"p95_ms":0,"p99_ms":0,"threshold_ms":%d,"passed":false,"error":"no benchmark output — server binary may not be built (run make build-server)"}\n' \
|
||||
"$THRESHOLD_MS"
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
|
||||
exit $EXIT_CODE
|
||||
Executable
+98
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env bash
|
||||
# tests/run-ipc-fixtures: Layer 1 IPC fixture tests (D-030)
|
||||
# Runs Rust serialization round-trip tests + GDScript fixture validation.
|
||||
# GDScript side is skipped if client/tests/test_ipc_fixtures.gd doesn't exist yet (#271).
|
||||
# Exit: 0 = all pass, non-zero = any failure
|
||||
# Stdout: {"suite":"ipc-fixtures","total":N,"passed":N,"failed":N,"duration_ms":N}
|
||||
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)"
|
||||
|
||||
_extract_num() {
|
||||
local haystack="$1" pattern="$2"
|
||||
echo "$haystack" | grep -oE "[0-9]+ $pattern" | grep -oE '^[0-9]+' || echo 0
|
||||
}
|
||||
|
||||
_parse_nextest_summary() {
|
||||
local tmpout="$1"
|
||||
local summary total passed failed
|
||||
summary=$(grep -E "^\s*(Summary|Finished)" "$tmpout" | tail -1 || true)
|
||||
if [[ -n "$summary" ]]; then
|
||||
total=$(_extract_num "$summary" "tests? run")
|
||||
passed=$(_extract_num "$summary" "passed")
|
||||
failed=$(_extract_num "$summary" "failed")
|
||||
else
|
||||
total=0; passed=0; failed=0
|
||||
fi
|
||||
echo "$total $passed $failed"
|
||||
}
|
||||
|
||||
# --- Layer 1a: Rust serialization tests ---
|
||||
START_MS=$(date +%s%3N)
|
||||
|
||||
cd "$REPO_ROOT/server"
|
||||
TMPOUT=$(mktemp)
|
||||
NEXTEST_ARGS=(nextest run --color never --test serialization)
|
||||
[[ -n "$FILTER" ]] && NEXTEST_ARGS+=(-E "test(~${FILTER})")
|
||||
|
||||
set +e
|
||||
cargo "${NEXTEST_ARGS[@]}" 2>&1 | tee "$TMPOUT" >&2
|
||||
RUST_EXIT=${PIPESTATUS[0]}
|
||||
set -e
|
||||
|
||||
read -r RUST_TOTAL RUST_PASSED RUST_FAILED < <(_parse_nextest_summary "$TMPOUT")
|
||||
rm -f "$TMPOUT"
|
||||
|
||||
# --- Layer 1b: GDScript fixture tests (optional until #271 lands) ---
|
||||
GDS_FIXTURE="$REPO_ROOT/client/tests/test_ipc_fixtures.gd"
|
||||
GDS_TOTAL=0; GDS_PASSED=0; GDS_FAILED=0; GDS_EXIT=0
|
||||
|
||||
if [[ -f "$GDS_FIXTURE" ]]; then
|
||||
GODOT=$(command -v godot4 2>/dev/null || command -v godot 2>/dev/null || echo "")
|
||||
if [[ -z "$GODOT" ]]; then
|
||||
echo "Warning: test_ipc_fixtures.gd found but godot not in PATH — skipping GDScript layer" >&2
|
||||
else
|
||||
GDTMP=$(mktemp)
|
||||
set +e
|
||||
"$GODOT" --headless --path "$REPO_ROOT/client" \
|
||||
-s res://addons/gdUnit4/bin/GdUnitCmdTool.gd \
|
||||
--ignoreHeadlessMode -c \
|
||||
-a res://tests/test_ipc_fixtures.gd \
|
||||
2>&1 | tee "$GDTMP" >&2
|
||||
GDS_EXIT=${PIPESTATUS[0]}
|
||||
set -e
|
||||
|
||||
STATS=$(grep -oE "[0-9]+ test cases \| [0-9]+ errors \| [0-9]+ failures" "$GDTMP" || true)
|
||||
if [[ -n "$STATS" ]]; then
|
||||
GDS_TOTAL=$(echo "$STATS" | grep -oE '^[0-9]+' | awk '{s+=$1} END {print s}')
|
||||
ERRS=$(echo "$STATS" | grep -oE '[0-9]+ errors' | grep -oE '^[0-9]+' | awk '{s+=$1} END {print s}')
|
||||
FAILS=$(echo "$STATS" | grep -oE '[0-9]+ failures' | grep -oE '^[0-9]+' | awk '{s+=$1} END {print s}')
|
||||
GDS_FAILED=$(( ${ERRS:-0} + ${FAILS:-0} ))
|
||||
GDS_PASSED=$(( GDS_TOTAL - GDS_FAILED ))
|
||||
fi
|
||||
rm -f "$GDTMP"
|
||||
fi
|
||||
fi
|
||||
|
||||
END_MS=$(date +%s%3N)
|
||||
DURATION_MS=$((END_MS - START_MS))
|
||||
|
||||
TOTAL=$(( RUST_TOTAL + GDS_TOTAL ))
|
||||
PASSED=$(( RUST_PASSED + GDS_PASSED ))
|
||||
FAILED=$(( RUST_FAILED + GDS_FAILED ))
|
||||
|
||||
# Overall exit: fail if either side failed
|
||||
EXIT_CODE=$(( RUST_EXIT != 0 || GDS_EXIT != 0 ? 1 : 0 ))
|
||||
|
||||
printf '{"suite":"ipc-fixtures","total":%d,"passed":%d,"failed":%d,"duration_ms":%d}\n' \
|
||||
"$TOTAL" "$PASSED" "$FAILED" "$DURATION_MS"
|
||||
exit $EXIT_CODE
|
||||
Executable
+72
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env bash
|
||||
# tests/run-ipc-integration: Layer 3 real-subprocess integration tests (D-030)
|
||||
# Spawns the server binary as a real child process, runs IPC round-trip.
|
||||
# Also invokes tests/run-ipc-benchmark when that script exists (#342).
|
||||
# Exit: 0 = all pass, non-zero = any failure
|
||||
# Stdout: {"suite":"ipc-integration","total":N,"passed":N,"failed":N,"duration_ms":N}
|
||||
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)"
|
||||
cd "$REPO_ROOT/server"
|
||||
|
||||
START_MS=$(date +%s%3N)
|
||||
TMPOUT=$(mktemp)
|
||||
|
||||
NEXTEST_ARGS=(nextest run --color never --test layer3)
|
||||
[[ -n "$FILTER" ]] && NEXTEST_ARGS+=(-E "test(~${FILTER})")
|
||||
|
||||
set +e
|
||||
cargo "${NEXTEST_ARGS[@]}" 2>&1 | tee "$TMPOUT" >&2
|
||||
LAYER3_EXIT=${PIPESTATUS[0]}
|
||||
set -e
|
||||
|
||||
_extract_num() {
|
||||
local haystack="$1" pattern="$2"
|
||||
echo "$haystack" | grep -oE "[0-9]+ $pattern" | grep -oE '^[0-9]+' || echo 0
|
||||
}
|
||||
|
||||
SUMMARY=$(grep -E "^\s*(Summary|Finished)" "$TMPOUT" | tail -1 || true)
|
||||
TOTAL=0; PASSED=0; FAILED=0
|
||||
if [[ -n "$SUMMARY" ]]; then
|
||||
TOTAL=$(_extract_num "$SUMMARY" "tests? run")
|
||||
PASSED=$(_extract_num "$SUMMARY" "passed")
|
||||
FAILED=$(_extract_num "$SUMMARY" "failed")
|
||||
fi
|
||||
rm -f "$TMPOUT"
|
||||
|
||||
# Run IPC benchmark if it exists (#342 — requires handshake from #555/#556)
|
||||
BENCH_SCRIPT="$REPO_ROOT/tests/run-ipc-benchmark"
|
||||
BENCH_EXIT=0
|
||||
if [[ -x "$BENCH_SCRIPT" ]]; then
|
||||
BENCH_ARGS=()
|
||||
[[ -n "$FILTER" ]] && BENCH_ARGS+=(--filter "$FILTER")
|
||||
set +e
|
||||
"$BENCH_SCRIPT" "${BENCH_ARGS[@]}" >&2
|
||||
BENCH_EXIT=$?
|
||||
set -e
|
||||
if [[ $BENCH_EXIT -ne 0 ]]; then
|
||||
FAILED=$(( FAILED + 1 ))
|
||||
TOTAL=$(( TOTAL + 1 ))
|
||||
else
|
||||
PASSED=$(( PASSED + 1 ))
|
||||
TOTAL=$(( TOTAL + 1 ))
|
||||
fi
|
||||
fi
|
||||
|
||||
END_MS=$(date +%s%3N)
|
||||
DURATION_MS=$((END_MS - START_MS))
|
||||
|
||||
EXIT_CODE=$(( LAYER3_EXIT != 0 || BENCH_EXIT != 0 ? 1 : 0 ))
|
||||
|
||||
printf '{"suite":"ipc-integration","total":%d,"passed":%d,"failed":%d,"duration_ms":%d}\n' \
|
||||
"$TOTAL" "$PASSED" "$FAILED" "$DURATION_MS"
|
||||
exit $EXIT_CODE
|
||||
Executable
+50
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env bash
|
||||
# tests/run-ipc-protocol: Layer 2 mock IPC protocol tests (D-030)
|
||||
# Runs LocalBridge Unix-socket round-trip tests (no real subprocess).
|
||||
# Exit: 0 = all pass, non-zero = failure
|
||||
# Stdout: {"suite":"ipc-protocol","total":N,"passed":N,"failed":N,"duration_ms":N}
|
||||
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)"
|
||||
cd "$REPO_ROOT/server"
|
||||
|
||||
START_MS=$(date +%s%3N)
|
||||
TMPOUT=$(mktemp)
|
||||
|
||||
NEXTEST_ARGS=(nextest run --color never --test bridge_ipc)
|
||||
[[ -n "$FILTER" ]] && NEXTEST_ARGS+=(-E "test(~${FILTER})")
|
||||
|
||||
set +e
|
||||
cargo "${NEXTEST_ARGS[@]}" 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 -oE "[0-9]+ $pattern" | grep -oE '^[0-9]+' || echo 0
|
||||
}
|
||||
|
||||
SUMMARY=$(grep -E "^\s*(Summary|Finished)" "$TMPOUT" | tail -1 || true)
|
||||
TOTAL=0; PASSED=0; FAILED=0
|
||||
if [[ -n "$SUMMARY" ]]; then
|
||||
TOTAL=$(_extract_num "$SUMMARY" "tests? run")
|
||||
PASSED=$(_extract_num "$SUMMARY" "passed")
|
||||
FAILED=$(_extract_num "$SUMMARY" "failed")
|
||||
fi
|
||||
|
||||
rm -f "$TMPOUT"
|
||||
printf '{"suite":"ipc-protocol","total":%d,"passed":%d,"failed":%d,"duration_ms":%d}\n' \
|
||||
"${TOTAL:-0}" "${PASSED:-0}" "${FAILED:-0}" "$DURATION_MS"
|
||||
exit $EXIT_CODE
|
||||
Executable
+54
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env bash
|
||||
# tests/run-rust: Run Rust test suite via cargo nextest (D-030)
|
||||
# Exit: 0 = all pass, non-zero = failure
|
||||
# Stdout: {"suite":"rust","total":N,"passed":N,"failed":N,"duration_ms":N}
|
||||
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)"
|
||||
cd "$REPO_ROOT/server"
|
||||
|
||||
START_MS=$(date +%s%3N)
|
||||
TMPOUT=$(mktemp)
|
||||
|
||||
NEXTEST_ARGS=(nextest run --color never)
|
||||
if [[ -n "$FILTER" ]]; then
|
||||
NEXTEST_ARGS+=(-E "test(~${FILTER})")
|
||||
fi
|
||||
|
||||
set +e
|
||||
cargo "${NEXTEST_ARGS[@]}" 2>&1 | tee "$TMPOUT" >&2
|
||||
EXIT_CODE=${PIPESTATUS[0]}
|
||||
set -e
|
||||
|
||||
END_MS=$(date +%s%3N)
|
||||
DURATION_MS=$((END_MS - START_MS))
|
||||
|
||||
# Parse nextest summary: " Summary [ 0.123s] N tests run: X passed[, Y failed], Z skipped"
|
||||
# (older nextest uses "Finished", newer uses "Summary" — match both)
|
||||
SUMMARY=$(grep -E "^\s*(Summary|Finished)" "$TMPOUT" | tail -1 || true)
|
||||
|
||||
_extract_num() {
|
||||
local haystack="$1" pattern="$2"
|
||||
echo "$haystack" | grep -oE "[0-9]+ $pattern" | grep -oE '^[0-9]+' || echo 0
|
||||
}
|
||||
|
||||
TOTAL=0; PASSED=0; FAILED=0
|
||||
if [[ -n "$SUMMARY" ]]; then
|
||||
TOTAL=$(_extract_num "$SUMMARY" "tests? run")
|
||||
PASSED=$(_extract_num "$SUMMARY" "passed")
|
||||
FAILED=$(_extract_num "$SUMMARY" "failed")
|
||||
fi
|
||||
|
||||
rm -f "$TMPOUT"
|
||||
printf '{"suite":"rust","total":%d,"passed":%d,"failed":%d,"duration_ms":%d}\n' \
|
||||
"${TOTAL:-0}" "${PASSED:-0}" "${FAILED:-0}" "$DURATION_MS"
|
||||
exit $EXIT_CODE
|
||||
Reference in New Issue
Block a user