117 lines
3.1 KiB
Bash
Executable File
117 lines
3.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# Run all herdr integration tests in parallel.
|
|
#
|
|
# Each spec gets its own tmux server (isolated socket).
|
|
# Workers are launched with PDEATHSIG so they get SIGTERM if this script dies.
|
|
# A stale sweep cleans up sockets from any previous crashed run.
|
|
#
|
|
set -uo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
SPECS_DIR="$SCRIPT_DIR/specs"
|
|
RESULTS_DIR="$SCRIPT_DIR/results"
|
|
TMUX_ROOT="${TMPDIR:-/tmp}/herdr-test-sockets"
|
|
|
|
mkdir -p "$RESULTS_DIR" "$TMUX_ROOT"
|
|
|
|
# Clean previous results
|
|
rm -f "$RESULTS_DIR"/*.json "$RESULTS_DIR"/*.txt 2>/dev/null
|
|
|
|
# Sweep stale tmux sockets from crashed previous runs
|
|
for dir in "$TMUX_ROOT"/*/; do
|
|
[ -d "$dir" ] || continue
|
|
owner_pid=""
|
|
[ -f "$dir/owner.pid" ] && owner_pid="$(<"$dir/owner.pid")"
|
|
# If owning worker is still alive, leave it
|
|
if [ -n "$owner_pid" ] && kill -0 "$owner_pid" 2>/dev/null; then
|
|
continue
|
|
fi
|
|
# Dead owner — kill the tmux server and remove the dir
|
|
[ -S "$dir/tmux.sock" ] && tmux -S "$dir/tmux.sock" kill-server 2>/dev/null || true
|
|
rm -rf "$dir"
|
|
done
|
|
|
|
# Collect specs
|
|
SPECS=("$SPECS_DIR"/*.md)
|
|
if [ ${#SPECS[@]} -eq 0 ]; then
|
|
echo "No test specs found in $SPECS_DIR"
|
|
exit 0
|
|
fi
|
|
|
|
echo "Running ${#SPECS[@]} integration tests..."
|
|
|
|
# Detect PDEATHSIG support
|
|
PDEATHSIG_CMD=()
|
|
if command -v setpriv >/dev/null 2>&1 && setpriv --help 2>&1 | grep -q -- '--pdeathsig'; then
|
|
PDEATHSIG_CMD=(setpriv --pdeathsig TERM --)
|
|
elif [ -f "$SCRIPT_DIR/pdeathsig.py" ]; then
|
|
PDEATHSIG_CMD=(python3 "$SCRIPT_DIR/pdeathsig.py" TERM)
|
|
fi
|
|
|
|
# Launch all specs in parallel — directly from this shell (no subshell wrapper)
|
|
PIDS=()
|
|
NAMES=()
|
|
|
|
for spec in "${SPECS[@]}"; do
|
|
name="$(basename "$spec" .md)"
|
|
NAMES+=("$name")
|
|
|
|
"${PDEATHSIG_CMD[@]}" "$SCRIPT_DIR/run_spec.sh" "$spec" "$RESULTS_DIR" "$TMUX_ROOT" &
|
|
PIDS+=($!)
|
|
done
|
|
|
|
# Wait for all, ignoring individual exit codes (we read results from files)
|
|
for pid in "${PIDS[@]}"; do
|
|
wait "$pid" 2>/dev/null || true
|
|
done
|
|
|
|
echo ""
|
|
|
|
# Print summary from result files
|
|
PASSED=0
|
|
FAILED=0
|
|
SKIPPED=0
|
|
|
|
for name in "${NAMES[@]}"; do
|
|
result_file="$RESULTS_DIR/$name.json"
|
|
|
|
if [[ ! -f "$result_file" ]]; then
|
|
echo " ? $name (no result file)"
|
|
FAILED=$((FAILED + 1))
|
|
continue
|
|
fi
|
|
|
|
result=$(python3 -c "import sys,json; print(json.load(sys.stdin).get('result','unknown'))" < "$result_file" 2>/dev/null || echo "unknown")
|
|
|
|
case "$result" in
|
|
pass)
|
|
echo " ✓ $name"
|
|
PASSED=$((PASSED + 1))
|
|
;;
|
|
skip)
|
|
echo " - $name (skipped)"
|
|
SKIPPED=$((SKIPPED + 1))
|
|
;;
|
|
*)
|
|
echo " ✗ $name"
|
|
python3 -c "
|
|
import sys, json
|
|
data = json.load(sys.stdin)
|
|
for c in data.get('checks', []):
|
|
if not c.get('pass', True):
|
|
print(f' ✗ {c[\"name\"]}: {c.get(\"detail\", \"\")}')
|
|
notes = data.get('notes', '')
|
|
if notes and data.get('result') == 'error':
|
|
print(f' {notes}')
|
|
" < "$result_file" 2>/dev/null
|
|
FAILED=$((FAILED + 1))
|
|
;;
|
|
esac
|
|
done
|
|
|
|
echo ""
|
|
echo "$PASSED passed, $FAILED failed, $SKIPPED skipped"
|
|
|
|
[ $FAILED -eq 0 ]
|