#!/usr/bin/env bash # lib/blogimport/conformance.sh — run blog-import suites, emit scoreboard. # Mirrors lib/content/conformance.sh: epoch-loaded modules + a bi-test counter. set -uo pipefail cd "$(git rev-parse --show-toplevel)" SX_SERVER="hosts/ocaml/_build/default/bin/sx_server.exe" if [ ! -x "$SX_SERVER" ]; then MAIN_ROOT=$(git worktree list | head -1 | awk '{print $1}') if [ -x "$MAIN_ROOT/$SX_SERVER" ]; then SX_SERVER="$MAIN_ROOT/$SX_SERVER" else echo "ERROR: sx_server.exe not found." >&2 exit 1 fi fi SUITES=(lexical import verify source) OUT_JSON="lib/blogimport/scoreboard.json" OUT_MD="lib/blogimport/scoreboard.md" run_suite() { local suite=$1 local file="lib/blogimport/tests/${suite}.sx" [ -f "$file" ] || { echo "0 0"; return; } local TMP TMP=$(mktemp) cat > "$TMP" << EPOCHS (epoch 1) (load "lib/smalltalk/tokenizer.sx") (load "lib/smalltalk/parser.sx") (load "lib/guest/reflective/class-chain.sx") (load "lib/smalltalk/runtime.sx") (load "lib/guest/reflective/env.sx") (load "lib/smalltalk/eval.sx") (load "lib/persist/event.sx") (load "lib/persist/backend.sx") (load "lib/persist/log.sx") (load "lib/persist/kv.sx") (load "lib/persist/api.sx") (load "lib/content/block.sx") (load "lib/content/doc.sx") (load "lib/content/render.sx") (load "lib/content/api.sx") (load "lib/content/meta.sx") (load "lib/content/section.sx") (load "lib/content/callout.sx") (load "lib/content/media.sx") (load "lib/content/store.sx") (load "lib/dream/json.sx") (load "lib/blogimport/lexical.sx") (load "lib/blogimport/import.sx") (load "lib/blogimport/verify.sx") (load "lib/blogimport/source.sx") (epoch 2) (eval "(define bi-test-pass 0)") (eval "(define bi-test-fail 0)") (eval "(define bi-test-fails (list))") (eval "(define bi-test (fn (name got expected) (if (= got expected) (set! bi-test-pass (+ bi-test-pass 1)) (begin (set! bi-test-fail (+ bi-test-fail 1)) (set! bi-test-fails (cons name bi-test-fails))))))") (epoch 3) (load "${file}") (epoch 4) (eval "(list bi-test-pass bi-test-fail)") EPOCHS local OUTPUT OUTPUT=$(timeout 240 "$SX_SERVER" < "$TMP" 2>/dev/null) rm -f "$TMP" local LINE LINE=$(echo "$OUTPUT" | awk '/^\(ok-len 4 / {getline; print; exit}') if [ -z "$LINE" ]; then LINE=$(echo "$OUTPUT" | grep -E '^\(ok 4 \([0-9]+ [0-9]+\)\)' | tail -1 \ | sed -E 's/^\(ok 4 //; s/\)$//') fi local P F P=$(echo "$LINE" | sed -E 's/^\(([0-9]+) ([0-9]+)\).*/\1/') F=$(echo "$LINE" | sed -E 's/^\(([0-9]+) ([0-9]+)\).*/\2/') echo "${P:-0} ${F:-0}" } declare -A SUITE_PASS SUITE_FAIL TOTAL_PASS=0 TOTAL_FAIL=0 echo "Running blogimport conformance suite..." >&2 for s in "${SUITES[@]}"; do read -r p f < <(run_suite "$s") SUITE_PASS[$s]=$p SUITE_FAIL[$s]=$f TOTAL_PASS=$((TOTAL_PASS + p)) TOTAL_FAIL=$((TOTAL_FAIL + f)) printf " %-10s %d/%d\n" "$s" "$p" "$((p+f))" >&2 done { printf '{\n "suites": {\n' first=1 for s in "${SUITES[@]}"; do if [ $first -eq 0 ]; then printf ',\n'; fi printf ' "%s": {"pass": %d, "fail": %d}' "$s" "${SUITE_PASS[$s]}" "${SUITE_FAIL[$s]}" first=0 done printf '\n },\n "total_pass": %d,\n "total_fail": %d,\n "total": %d\n}\n' \ "$TOTAL_PASS" "$TOTAL_FAIL" "$((TOTAL_PASS + TOTAL_FAIL))" } > "$OUT_JSON" { printf '# blogimport Conformance Scoreboard\n\n_Generated by `lib/blogimport/conformance.sh`_\n\n' printf '| Suite | Pass | Fail | Total |\n|-------|-----:|-----:|------:|\n' for s in "${SUITES[@]}"; do printf '| %s | %d | %d | %d |\n' "$s" "${SUITE_PASS[$s]}" "${SUITE_FAIL[$s]}" "$(( ${SUITE_PASS[$s]} + ${SUITE_FAIL[$s]} ))" done printf '| **Total** | **%d** | **%d** | **%d** |\n' "$TOTAL_PASS" "$TOTAL_FAIL" "$((TOTAL_PASS + TOTAL_FAIL))" } > "$OUT_MD" echo "Total: $TOTAL_PASS pass, $TOTAL_FAIL fail" >&2 [ "$TOTAL_FAIL" -eq 0 ]