diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f2ce9e1..e86139a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,17 +71,10 @@ jobs: shell: bash run: | set -euo pipefail + bash skills/scripts/test-routing.sh + scratch="$(mktemp -d)" trap 'rm -rf "$scratch"' EXIT - while IFS='|' read -r hint expected; do - out="$scratch/$expected" - bash skills/scripts/master-route.sh --hint "$hint" --out-dir "$out" - grep -Eq "^- primary: ${expected}$" "$out/route-scope.md" - done <<'CASES' - apk reverse with jadx|R1 - js reverse signature|R3 - case review evidence graph|R40 - CASES if bash skills/scripts/case-init.sh \ --hint "offline apk" \ diff --git a/skills/scripts/test-routing.sh b/skills/scripts/test-routing.sh new file mode 100644 index 0000000..309e2aa --- /dev/null +++ b/skills/scripts/test-routing.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# Run the shared routing benchmark through the Bash structured router. +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +BENCHMARK="$SCRIPT_DIR/../tests/routing-benchmark.json" +ROUTER="$SCRIPT_DIR/master-route.sh" + +PYTHON="" +for candidate in python3 python; do + if command -v "$candidate" >/dev/null 2>&1; then + PYTHON="$candidate" + break + fi +done +if [[ -z "$PYTHON" ]]; then + echo 'ERROR: Python 3 is required by the Bash routing benchmark runner.' >&2 + exit 2 +fi + +"$PYTHON" - "$BENCHMARK" "$ROUTER" <<'PY' +import json +import pathlib +import re +import subprocess +import sys +import tempfile + +benchmark_path = pathlib.Path(sys.argv[1]) +router_path = pathlib.Path(sys.argv[2]) +benchmark = json.loads(benchmark_path.read_text(encoding="utf-8-sig")) +cases = benchmark["cases"] + +failures = [] +passed = 0 +print(f"=== Bash routing benchmark | {len(cases)} cases ===") + +with tempfile.TemporaryDirectory(prefix="rs-routing-bash-") as scratch: + root = pathlib.Path(scratch) + for index, case in enumerate(cases): + hint = case["hint"] + expected = case["expect"] + out_dir = root / str(index) + result = subprocess.run( + ["bash", str(router_path), "--hint", hint, "--out-dir", str(out_dir)], + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + ) + + got = "ERR" + scope_path = out_dir / "route-scope.md" + if result.returncode == 0 and scope_path.is_file(): + match = re.search( + r"(?m)^- primary:[ \t]*(\S+)[ \t]*$", + scope_path.read_text(encoding="utf-8"), + ) + if match: + got = match.group(1) + + if got == expected: + passed += 1 + else: + failures.append( + f"hint={hint!r} expect={expected} got={got} exit={result.returncode}" + ) + +print(f"TOTAL={len(cases)} PASS={passed} FAIL={len(failures)}") +if failures: + for failure in failures: + print(f"[FAIL] {failure}", file=sys.stderr) + raise SystemExit(1) + +print(f"OVERALL: ALL PASS ({passed})") +PY