Two Bryan UX asks on the batch route-set compare:
1. Identification header now labels which file is left vs right, with the
REAL matched filenames:
left (baseline) = <windows-prefix>.*
right (candidate) = <linux-prefix>.*
Printed to stdout on --format text; to stderr on --format tsv (keeps
stdout pure tsv). No change to PASS/DIFF/MISSING/EXTRA classification or
exit codes (0=clean, 1=any non-clean, 2=usage).
2. --dir already defaulted to cwd (DIR="." in v0.9.10) — this only fixes the
docs (usage header, --help, MANUAL.md) to lead with the no---dir,
cd-into-the-dir example.
Vera CODE-PASS (agent ade4360f): 0 defects; ran the scripts live against a
4-route fixture (PASS/DIFF/MISSING/EXTRA), verified header/stream-separation,
bash -n clean under /bin/bash 3.2.57, make-manifest --check clean, VERSION +
larry.sh agree at 0.9.11. Live-acceptance owner: Bryan (Gundersen box).
237 lines
9.7 KiB
Bash
Executable File
237 lines
9.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# hl7-diff-set.sh — run hl7-diff over a WHOLE SET of paired Windows-vs-Linux
|
|
# (or any baseline-vs-candidate) regression output files in one command,
|
|
# instead of hand-writing a for-loop with hardcoded route names.
|
|
#
|
|
# Built for hciroutetest's real on-disk layout (Bryan-direct, 2026-07-23):
|
|
# one directory holding paired files named <prefix>.<route>, where TWO
|
|
# prefixes (a baseline/"windows" set and a candidate/"linux" set) share a set
|
|
# of route suffixes after the last dot, e.g.:
|
|
# adt_in_regression_out_windows_single.adt_process_out
|
|
# adt_in_regression_out_linux_single.adt_process_out
|
|
# adt_in_regression_out_windows_single.lab_adt_out
|
|
# adt_in_regression_out_linux_single.lab_adt_out
|
|
# ...
|
|
#
|
|
# Usage:
|
|
# hl7-diff-set.sh <baseline-prefix> <candidate-prefix>
|
|
# [--dir PATH] # directory holding the paired files.
|
|
# # DEFAULTS TO THE CURRENT DIRECTORY (.) if
|
|
# # omitted -- --dir is an OVERRIDE, not
|
|
# # required. Run from inside the regression
|
|
# # directory and just pass the two prefixes.
|
|
# [--ignore "FIELD,..."] # forwarded to hl7-diff. Default: MSH.7
|
|
# [--include-fields "F,.."] # forwarded to hl7-diff (overrides --ignore for that set)
|
|
# [--framing auto|nl|0x1c|len10] # forwarded to hl7-diff. Default: auto
|
|
# [--format text|tsv] # how THIS command reports. Default: text
|
|
#
|
|
# # from inside the regression directory -- no --dir needed:
|
|
# cd /opt/cloverleaf/cis2025.01/integrator/adt/data/regression_testing/single
|
|
# hl7-diff-set.sh adt_in_regression_out_windows_single adt_in_regression_out_linux_single
|
|
#
|
|
# # or from anywhere, with an explicit --dir override:
|
|
# hl7-diff-set.sh adt_in_regression_out_windows_single adt_in_regression_out_linux_single \
|
|
# --dir /opt/cloverleaf/cis2025.01/integrator/adt/data/regression_testing/single
|
|
#
|
|
# The FIRST prefix argument is always LEFT/baseline; the SECOND is always
|
|
# RIGHT/candidate -- consistently, everywhere (the per-run header states this
|
|
# explicitly so you never have to remember or guess which is which; v0.9.11).
|
|
#
|
|
# What it does:
|
|
# 1. Globs "<dir>/<baseline-prefix>.*", derives each route suffix (the part
|
|
# after "<baseline-prefix>."), and pairs it with "<dir>/<candidate-prefix>.<suffix>".
|
|
# Also globs "<dir>/<candidate-prefix>.*" so a candidate-only route (no
|
|
# baseline file) is found too — nothing is silently skipped.
|
|
# 2. Runs the existing lib/hl7-diff.sh on every matched pair (this does NOT
|
|
# reimplement diffing — same engine, same --ignore/--framing semantics).
|
|
# 3. Reports PASS/DIFF/MISSING/EXTRA per route, plus a one-line summary.
|
|
#
|
|
# Per-route outcomes:
|
|
# PASS — both files present, 0 field differences (post-ignore)
|
|
# DIFF — both files present, N>0 field differences (--format text also
|
|
# shows the full hl7-diff --format text detail, indented)
|
|
# MISSING — baseline route has no matching candidate file (a dropped output
|
|
# IS a regression — this counts as a failure)
|
|
# EXTRA — candidate has a route the baseline does not (reported, also
|
|
# counts as non-clean — never silently skipped)
|
|
#
|
|
# --format text (default) prints a human report to stdout: per-route lines,
|
|
# DIFF detail indented beneath, then the summary line.
|
|
# --format tsv prints ONE machine row per route to stdout:
|
|
# route<TAB>status<TAB>diff_count
|
|
# (diff_count is "-" for MISSING/EXTRA, since no diff was actually run) —
|
|
# the left/right identification header and the summary line still print,
|
|
# to STDERR, so stdout stays clean tsv.
|
|
# Recommended for scripting: hl7-diff.sh's OWN v0.9.9 text-mode message-
|
|
# header mislabel is fixed as of this release (F-8), but tsv remains the
|
|
# safer machine-parseable choice regardless.
|
|
#
|
|
# Exit codes: 0 = every route PASS (all clean); 1 = at least one route is
|
|
# DIFF/MISSING/EXTRA; 2 = usage/input error (bad flag, --dir missing, or
|
|
# neither prefix matched any file at all).
|
|
set -o pipefail
|
|
|
|
NC_SELF="$0"
|
|
LIB_DIR="$(cd "$(dirname "$NC_SELF")" && pwd)"
|
|
HL7DIFF="$LIB_DIR/hl7-diff.sh"
|
|
|
|
if [ -r "$LIB_DIR/cygwin-safe.sh" ]; then
|
|
# shellcheck disable=SC1090,SC1091
|
|
. "$LIB_DIR/cygwin-safe.sh"
|
|
else
|
|
coerce_int() { local r="${1:-}" d="${2:-0}" c; c=$(printf '%s' "$r" | tr -cd '0-9'); printf '%s' "${c:-$d}"; }
|
|
fi
|
|
|
|
die() { printf 'hl7-diff-set: %s\n' "$*" >&2; exit 2; }
|
|
|
|
DIR="."
|
|
IGNORE="MSH.7"
|
|
INCLUDE=""
|
|
FRAMING="auto"
|
|
FORMAT="text"
|
|
BASE_PREFIX=""
|
|
CAND_PREFIX=""
|
|
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--dir) shift; DIR="$1" ;;
|
|
--ignore) shift; IGNORE="$1" ;;
|
|
--include-fields) shift; INCLUDE="$1" ;;
|
|
--framing) shift; FRAMING="$1" ;;
|
|
--format) shift; FORMAT="$1" ;;
|
|
-h|--help) sed -n '2,70p' "$NC_SELF"; exit 0 ;;
|
|
-*) die "unknown flag: $1" ;;
|
|
*)
|
|
if [ -z "$BASE_PREFIX" ]; then BASE_PREFIX="$1"
|
|
elif [ -z "$CAND_PREFIX" ]; then CAND_PREFIX="$1"
|
|
else die "extra arg: $1"
|
|
fi
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
[ -x "$HL7DIFF" ] || [ -f "$HL7DIFF" ] || die "backing tool not found: $HL7DIFF"
|
|
[ -n "$BASE_PREFIX" ] && [ -n "$CAND_PREFIX" ] || die "usage: hl7-diff-set.sh <baseline-prefix> <candidate-prefix> [--dir PATH] [--ignore FIELDS] [--framing auto|nl|0x1c|len10] [--format text|tsv] (run --help for full usage)"
|
|
[ -d "$DIR" ] || die "no such --dir: $DIR"
|
|
case "$FORMAT" in text|tsv) ;; *) die "bad --format (want text|tsv)" ;; esac
|
|
case "$FRAMING" in auto|nl|0x1c|len10) ;; *) die "bad --framing (want auto|nl|0x1c|len10)" ;; esac
|
|
|
|
# --- discovery: glob both prefixes, derive route suffixes -------------------
|
|
# Portable (bash 3.2 / MobaXterm-safe) glob: a literal no-match pattern stays
|
|
# un-expanded, so every candidate is existence-checked with `[ -e ]` before
|
|
# use — no reliance on nullglob (bash-4-leaning / shopt-dependent).
|
|
_base_suffixes=()
|
|
for _f in "$DIR/$BASE_PREFIX".*; do
|
|
[ -e "$_f" ] || continue
|
|
_bn="$(basename "$_f")"
|
|
_base_suffixes+=("${_bn#"$BASE_PREFIX".}")
|
|
done
|
|
|
|
_cand_suffixes=()
|
|
for _f in "$DIR/$CAND_PREFIX".*; do
|
|
[ -e "$_f" ] || continue
|
|
_bn="$(basename "$_f")"
|
|
_cand_suffixes+=("${_bn#"$CAND_PREFIX".}")
|
|
done
|
|
|
|
if [ "${#_base_suffixes[@]}" -eq 0 ] && [ "${#_cand_suffixes[@]}" -eq 0 ]; then
|
|
die "no files matched '$BASE_PREFIX.*' or '$CAND_PREFIX.*' in $DIR"
|
|
fi
|
|
|
|
# Union of both suffix lists, sorted + de-duped, for a stable/deterministic
|
|
# route order regardless of filesystem glob order.
|
|
ROUTES="$(printf '%s\n' "${_base_suffixes[@]}" "${_cand_suffixes[@]}" | grep -v '^$' | sort -u)"
|
|
|
|
# --- run each pair ------------------------------------------------------------
|
|
diff_args=(--ignore "$IGNORE" --framing "$FRAMING")
|
|
[ -n "$INCLUDE" ] && diff_args+=(--include-fields "$INCLUDE")
|
|
|
|
n_routes=0 n_pass=0 n_diff=0 n_missing=0 n_extra=0
|
|
|
|
# v0.9.11 (Bryan UX ask): name which side is which, ONCE, up front — the
|
|
# per-diff-line `left=... right=...` labels (F-8, v0.9.10) tell you a value
|
|
# came from "left" or "right", but not WHICH FILE PREFIX left/right actually
|
|
# is. First positional arg is always left/baseline, second is always
|
|
# right/candidate -- consistently, everywhere -- so state it plainly here.
|
|
# text mode: part of the normal stdout report. tsv mode: routed to STDERR
|
|
# (same as the summary line) so stdout stays pure machine-parseable tsv rows.
|
|
if [ "$FORMAT" = "text" ]; then
|
|
printf 'hl7-diff-set: baseline=%s candidate=%s dir=%s\n' "$BASE_PREFIX" "$CAND_PREFIX" "$DIR"
|
|
printf ' left (baseline) = %s.*\n' "$BASE_PREFIX"
|
|
printf ' right (candidate) = %s.*\n' "$CAND_PREFIX"
|
|
printf ' ignore: %s framing: %s\n\n' "$IGNORE" "$FRAMING"
|
|
else
|
|
{
|
|
printf 'hl7-diff-set: baseline=%s candidate=%s dir=%s\n' "$BASE_PREFIX" "$CAND_PREFIX" "$DIR"
|
|
printf ' left (baseline) = %s.*\n' "$BASE_PREFIX"
|
|
printf ' right (candidate) = %s.*\n' "$CAND_PREFIX"
|
|
printf ' ignore: %s framing: %s\n' "$IGNORE" "$FRAMING"
|
|
} >&2
|
|
fi
|
|
|
|
while IFS= read -r route; do
|
|
[ -z "$route" ] && continue
|
|
n_routes=$((n_routes + 1))
|
|
bfile="$DIR/$BASE_PREFIX.$route"
|
|
cfile="$DIR/$CAND_PREFIX.$route"
|
|
|
|
if [ ! -f "$bfile" ]; then
|
|
n_extra=$((n_extra + 1))
|
|
if [ "$FORMAT" = "tsv" ]; then
|
|
printf '%s\tEXTRA\t-\n' "$route"
|
|
else
|
|
printf ' %-24s EXTRA (candidate-only route — no baseline file: %s)\n' "$route" "$bfile"
|
|
fi
|
|
continue
|
|
fi
|
|
if [ ! -f "$cfile" ]; then
|
|
n_missing=$((n_missing + 1))
|
|
if [ "$FORMAT" = "tsv" ]; then
|
|
printf '%s\tMISSING\t-\n' "$route"
|
|
else
|
|
printf ' %-24s MISSING (no candidate output — dropped route: %s)\n' "$route" "$cfile"
|
|
fi
|
|
continue
|
|
fi
|
|
|
|
count="$("$HL7DIFF" "${diff_args[@]}" --format count "$bfile" "$cfile" 2>&1)"
|
|
count="$(coerce_int "$count" 0)"
|
|
|
|
if [ "$count" -eq 0 ]; then
|
|
n_pass=$((n_pass + 1))
|
|
if [ "$FORMAT" = "tsv" ]; then
|
|
printf '%s\tPASS\t0\n' "$route"
|
|
else
|
|
printf ' %-24s PASS\n' "$route"
|
|
fi
|
|
else
|
|
n_diff=$((n_diff + 1))
|
|
if [ "$FORMAT" = "tsv" ]; then
|
|
printf '%s\tDIFF\t%s\n' "$route" "$count"
|
|
else
|
|
printf ' %-24s DIFF (%s diff(s))\n' "$route" "$count"
|
|
"$HL7DIFF" "${diff_args[@]}" --format text "$bfile" "$cfile" 2>&1 | sed 's/^/ /'
|
|
printf '\n'
|
|
fi
|
|
fi
|
|
done <<EOF_ROUTES
|
|
$ROUTES
|
|
EOF_ROUTES
|
|
|
|
# --- summary ------------------------------------------------------------------
|
|
_summary="$n_routes routes: $n_pass clean, $n_diff differs"
|
|
[ "$n_missing" -gt 0 ] && _summary="$_summary, $n_missing missing"
|
|
[ "$n_extra" -gt 0 ] && _summary="$_summary, $n_extra extra"
|
|
|
|
if [ "$FORMAT" = "tsv" ]; then
|
|
printf '%s\n' "$_summary" >&2
|
|
else
|
|
printf '%s\n' "$_summary"
|
|
fi
|
|
|
|
if [ "$n_diff" -eq 0 ] && [ "$n_missing" -eq 0 ] && [ "$n_extra" -eq 0 ] && [ "$n_routes" -gt 0 ]; then
|
|
exit 0
|
|
fi
|
|
exit 1
|