#!/usr/bin/env bash # conformance/run.sh — the clawhdf5 conformance sweep, end to end. # # fetch the pinned corpora (cached) -> build the probe -> probe every file # with clawhdf5 and with h5py (and h5dump for the CVE corpus), each under a # timeout and a memory limit -> compare -> write CONFORMANCE.md -> check the # result against conformance/baseline.json. # # Usage: conformance/run.sh [--no-fetch] [--no-report] [--update-baseline] # # Environment: # CLAWHDF5_PYTHON python with h5py, numpy, hdf5plugin (default: repo .venv, then python3) # CONFORMANCE_CACHE corpus / build / results cache (default: conformance/.cache) # CONFORMANCE_OUT results directory (default: $CONFORMANCE_CACHE/results) # CONFORMANCE_REPORT report path (default: CONFORMANCE.md at the repo root) # JOBS parallel files (default: nproc) # CONFORMANCE_PROBE use this prebuilt probe binary instead of building one # TMO / MEM_KB per-process timeout in seconds (20) / address-space limit in KiB (4 GiB) # # Exit status: 0 = gate passed; 1 = a panic/hang/crash/oom in clawhdf5, or the # ok count fell below the baseline, or a baseline-ok file regressed; 2 = setup error. set -euo pipefail HERE="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$HERE/.." && pwd)" FETCH=1 REPORT=1 UPDATE=0 for a in "$@"; do case "$a" in --no-fetch) FETCH=0 ;; --no-report) REPORT=0 ;; --update-baseline) UPDATE=1 ;; -h|--help) sed -n '2,23p' "$0"; exit 0 ;; *) echo "unknown argument: $a" >&2; exit 2 ;; esac done export PATH="$HOME/.cargo/bin:$PATH" CACHE="${CONFORMANCE_CACHE:-$HERE/.cache}" mkdir -p "$CACHE"; CACHE="$(cd "$CACHE" && pwd)" OUT="${CONFORMANCE_OUT:-$CACHE/results}" REPORT_PATH="${CONFORMANCE_REPORT:-$ROOT/CONFORMANCE.md}" JOBS="${JOBS:-$(nproc 2>/dev/null || echo 4)}" if [ -n "${CLAWHDF5_PYTHON:-}" ]; then PY="$CLAWHDF5_PYTHON" elif [ -x "$ROOT/.venv/bin/python" ]; then PY="$ROOT/.venv/bin/python" else PY="$(command -v python3)"; fi export PY TMO="${TMO:-20}" MEM_KB="${MEM_KB:-4194304}" command -v h5dump >/dev/null || { echo "error: h5dump not found (install hdf5-tools)" >&2; exit 2; } "$PY" -c 'import h5py, numpy, hdf5plugin' || { echo "error: $PY lacks h5py/numpy/hdf5plugin" >&2; exit 2; } t0=$(date +%s) [ "$FETCH" = 1 ] && bash "$HERE/fetch-corpus.sh" "$CACHE" C="$CACHE/corpus" [ -d "$C" ] || { echo "error: no corpus in $C (run without --no-fetch)" >&2; exit 2; } if [ -n "${CONFORMANCE_PROBE:-}" ]; then export PROBE="$CONFORMANCE_PROBE" # a prebuilt probe, e.g. an older one for a before/after else echo "== building the probe" CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-$CACHE/target}" \ cargo build -q --release --manifest-path "$HERE/probe/Cargo.toml" export PROBE="${CARGO_TARGET_DIR:-$CACHE/target}/release/conformance-probe" fi t1=$(date +%s) rm -rf "$OUT"; mkdir -p "$OUT" "$PY" "$HERE/list_files.py" "$C" > "$OUT/files.txt" echo "== probing $(wc -l <"$OUT/files.txt") files, $JOBS at a time (timeout ${TMO}s, limit $((MEM_KB / 1024)) MiB)" export C OUT HERE # The shell's "Segmentation fault (core dumped)" notices go to probe.log; the # signals themselves are recorded in each side's .rc. xargs -a "$OUT/files.txt" -d '\n' -P "$JOBS" -I{} bash -c ' f="$1"; d="$OUT/runs/${f//\//__}" case "$f" in cve_hdf5/*) export WITH_H5DUMP=1 ;; esac "$HERE/run_one.sh" "$C/$f" "$d"' _ {} 2>"$OUT/probe.log" echo "== comparing" "$PY" "$HERE/compare.py" "$OUT" >/dev/null t2=$(date +%s) cat > "$OUT/meta.json" <