## testcase.bash: the functions a case function of testmarktest_rule calls, to read hunks and give its outputs. ## Not sourced by testmark.bash: each case of testmarktest_rule sources it, in the case's own bash, so these functions exist only there. ## They run under the case's `set -euo pipefail`, but may be called where errexit is ignored, so they check every status themselves. ## Their contract is documented with testmarktest_rule, in testmark.bash. # shellcheck disable=SC2154 # _testcase_case, _testcase_index, and _testcase_blob are set by the case's script testcase_read_myhunk() { (($# == 1)) || { >&2 echo "usage: testcase_read_myhunk NAME" return 1 } _testcase_read testcase_read_myhunk "$_testcase_case/$1" } testcase_read_anyhunk() { (($# == 1)) || { >&2 echo "usage: testcase_read_anyhunk NAME" return 1 } _testcase_read testcase_read_anyhunk "$1" } ## Prints the body of hunk $2; $1 is the public function asking, for the message. _testcase_read() { local - LC_ALL=C IFS=$'\n' entry at set -f # Each index line is "NAME OFFSET LENGTH", and names hold no whitespace, so "NAME " starts exactly one line. for entry in $_testcase_index; do [[ $entry == "$2 "* ]] || continue at=${entry#"$2 "} printf '%s' "${_testcase_blob:${at% *}:${at#* }}" return done >&2 printf '%s: no hunk named "%s"\n' "$1" "$2" return 1 } testcase_output() { local LC_ALL=C body (($# == 1)) || { >&2 echo "usage: testcase_output NAME &2 printf 'testcase_output: "%s" is not an output name: it names a hunk in the case, so it has no leading "/", and no whitespace\n' "$1" return 1 } # Status 0 means read stopped at a NUL, which no hunk can hold. if IFS= read -r -d '' body; then >&2 printf 'testcase_output: %s: output contains a NUL byte\n' "$1" return 1 fi printf 'o %d %s\n%s' "${#body}" "$1" "$body" >&3 } testcase_withtmpdir() { local dir (($# == 0)) || { >&2 echo "usage: testcase_withtmpdir" return 1 } dir=$(mktemp -d) || return 1 # shellcheck disable=SC2064 # expanded now, on purpose: the trap outlives this function's locals trap "rm -rf -- $(printf %q "$dir")" EXIT cd "$dir" || return 1 } ## Reads the case function's stdout and stderr to the end, then sends them to the runner as one frame. ## The end comes only once everything that could write to them has exited, so this frame is the last. _testcase_stream() { local LC_ALL=C stream if IFS= read -r -d '' stream; then while IFS= read -r -d '' _; do :; done stream+=$'\ntestmark: the output contains a NUL byte\n' fi printf 's %d\n%s' "${#stream}" "$stream" >&3 }