#!/usr/bin/env sh # test-abstraction-ssp.sh # # Regression test for spine's document abstraction. # Generates .ssp files for all sample documents and diffs # against committed reference files. # Also checks that a parallel run and a serial run of the same documents # produce identical .ssp (guards against per-document state leaking # between documents or between worker threads). # # Usage: # ./test/test-abstraction-ssp.sh # uses result/bin/spine # ./test/test-abstraction-ssp.sh ./bin/spine-ldc # specify binary # # Exit codes: # 0 all .ssp files match reference # 1 differences found (printed to stdout) # 2 missing reference directory (run with --generate first) # # To generate/update reference files: # ./test/test-abstraction-ssp.sh --generate # ./test/test-abstraction-ssp.sh --generate ./bin/spine-ldc set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" SPINE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" # $SpinePOD names the sample pod directory, relative to the spine directory. # Unset, "$SPINE_DIR/$SpinePOD" is the spine directory itself, which exists, # so the "sample documents not found" check below would not catch it and # spine would be handed every file in the spine tree. if [ -z "$SpinePOD" ]; then echo "ERROR: \$SpinePOD is not set." >&2 echo " Set it to the sample pod directory, relative to $SPINE_DIR" >&2 echo " e.g. SpinePOD=../../markup/sisudoc-spine-samples/markup/pod-samples/pod" >&2 exit 2 fi SAMPLES_RAW="$SPINE_DIR/$SpinePOD" if [ -d "$SAMPLES_RAW" ]; then SAMPLES_DIR="$(cd "$SAMPLES_RAW" && pwd)" else SAMPLES_DIR="$SAMPLES_RAW" fi REF_DIR="$SCRIPT_DIR/reference/abstraction" TMP_DIR="$SCRIPT_DIR/current/abstraction" # parse arguments GENERATE=false SPINE_BIN="" for arg in "$@"; do case "$arg" in --generate) GENERATE=true ;; *) SPINE_BIN="$arg" ;; esac done # find spine binary if [ -z "$SPINE_BIN" ]; then if [ -x "$SPINE_DIR/result/bin/spine" ]; then SPINE_BIN="$SPINE_DIR/result/bin/spine" elif [ -x "$SPINE_DIR/bin/spine-ldc" ]; then SPINE_BIN="$SPINE_DIR/bin/spine-ldc" elif [ -x "$SPINE_DIR/bin/spine" ]; then SPINE_BIN="$SPINE_DIR/bin/spine" else echo "ERROR: spine binary not found. Specify path as argument." >&2 exit 2 fi fi # check samples exist if [ ! -d "$SAMPLES_DIR" ]; then echo "ERROR: sample documents not found at $SAMPLES_DIR" >&2 exit 2 fi echo "spine binary: $SPINE_BIN" echo "samples: $SAMPLES_DIR" if [ "$GENERATE" = true ]; then echo "Generating reference .ssp files..." rm -rf "$REF_DIR" mkdir -p "$REF_DIR" $SPINE_BIN --show-abstraction --skip-output --output="$SCRIPT_DIR/reference" "$SAMPLES_DIR"/* 2>&1 | tail -1 # flatten language subdirs into reference dir find "$SCRIPT_DIR/reference" -name "*.ssp" ! -path "$REF_DIR/*" -exec mv {} "$REF_DIR/" \; # clean up empty language dirs find "$SCRIPT_DIR/reference" -mindepth 1 -type d -empty -delete 2>/dev/null || true COUNT=$(ls "$REF_DIR"/*.ssp 2>/dev/null | wc -l) echo "Generated $COUNT reference .ssp files in $REF_DIR" exit 0 fi # check reference exists if [ ! -d "$REF_DIR" ] || [ -z "$(ls "$REF_DIR"/*.ssp 2>/dev/null)" ]; then echo "ERROR: no reference files found at $REF_DIR" >&2 echo "Run with --generate first to create reference files." >&2 exit 2 fi # generate current .ssp files echo "Generating current .ssp files..." rm -rf "$TMP_DIR" mkdir -p "$TMP_DIR" # this run has to be the parallel one: the serial run below is what it is # compared against, and two serial runs compare nothing. Serial is spine's # behaviour now and --parallel is the option, so it has to be asked for # here by name. Drop it and this test stops guarding the .ssp write race # without any test failing to say so. $SPINE_BIN --parallel --show-abstraction --skip-output --output="$SCRIPT_DIR/current" "$SAMPLES_DIR"/* 2>&1 | tail -1 # flatten find "$SCRIPT_DIR/current" -name "*.ssp" ! -path "$TMP_DIR/*" -exec mv {} "$TMP_DIR/" \; find "$SCRIPT_DIR/current" -mindepth 1 -type d -empty -delete 2>/dev/null || true # generate the same .ssp serially, to check that parallel and serial # processing agree (per-document state leaking between documents, or # between worker threads, shows up here) SER_DIR="$SCRIPT_DIR/current-serial/abstraction" echo "Generating current .ssp files (serial)..." rm -rf "$SCRIPT_DIR/current-serial" mkdir -p "$SER_DIR" $SPINE_BIN --show-abstraction --serial --skip-output --output="$SCRIPT_DIR/current-serial" "$SAMPLES_DIR"/* 2>&1 | tail -1 find "$SCRIPT_DIR/current-serial" -name "*.ssp" ! -path "$SER_DIR/*" -exec mv {} "$SER_DIR/" \; find "$SCRIPT_DIR/current-serial" -mindepth 1 -type d -empty -delete 2>/dev/null || true # diff echo "Comparing against reference..." FAILURES=0 for ref_file in "$REF_DIR"/*.ssp; do basename=$(basename "$ref_file") cur_file="$TMP_DIR/$basename" if [ ! -f "$cur_file" ]; then echo "MISSING: $basename (in reference but not generated)" FAILURES=$((FAILURES + 1)) continue fi if ! diff -q "$ref_file" "$cur_file" > /dev/null 2>&1; then echo "CHANGED: $basename" diff --unified=3 "$ref_file" "$cur_file" | head -30 echo " ..." FAILURES=$((FAILURES + 1)) fi done # check for new files not in reference for cur_file in "$TMP_DIR"/*.ssp; do basename=$(basename "$cur_file") ref_file="$REF_DIR/$basename" if [ ! -f "$ref_file" ]; then echo "NEW: $basename (generated but not in reference)" FAILURES=$((FAILURES + 1)) fi done # parallel vs serial determinism for cur_file in "$TMP_DIR"/*.ssp; do basename=$(basename "$cur_file") ser_file="$SER_DIR/$basename" if [ ! -f "$ser_file" ]; then echo "MISSING (serial run): $basename" FAILURES=$((FAILURES + 1)) continue fi if ! diff -q "$cur_file" "$ser_file" > /dev/null 2>&1; then echo "NON-DETERMINISTIC: $basename (parallel and serial runs differ)" diff --unified=3 "$cur_file" "$ser_file" | head -30 echo " ..." FAILURES=$((FAILURES + 1)) fi done # clean up rm -rf "$SCRIPT_DIR/current" "$SCRIPT_DIR/current-serial" if [ "$FAILURES" -eq 0 ]; then REF_COUNT=$(ls "$REF_DIR"/*.ssp | wc -l) echo "PASS: all $REF_COUNT .ssp files match reference (parallel and serial agree)" exit 0 else echo "FAIL: $FAILURES difference(s) found" exit 1 fi