testmark-bash

test-api.sh

#!/bin/bash
## The read API: handles, errors, and exact output.
set -euo pipefail
cd "$(dirname "$0")"
# shellcheck source=tests/lib.sh
source ./lib.sh

## Runs a command that should fail, and checks it printed exactly the given error and nothing on stdout.
##
## - @param $1 description
## - @param $2 the expected stderr, without its final newline
## - @param $3... the command
expect_fail() {
	local rc=0
	"${@:3}" >"$work/out" 2>"$work/err" || rc=$?
	if [[ $rc == 0 ]]; then
		_fail "$1: succeeded; want a failure"
	else
		expect_eq "$1" "$(<"$work/err")$(<"$work/out")" "$2"
	fi
}

printf '[testmark]:# (x)\n```text\none\n```\n[testmark]:# (old)\n```\ngone\n```\n' >a.md
printf '[testmark]:# (x)\n```\ntwo\n```\n' >b.md
printf 'no hunks here\n' >none.md
: >empty.md

# --- output is exact, and travels through files ---
testmark_parse doc a.md
testmark_names doc >out
expect_eq "names: one per line, in document order" "$(od -c out)" "$(printf 'x\nold\n' | od -c)"
testmark_body doc x >out
expect_eq "body: exact bytes" "$(od -c out)" "$(printf 'one\n' | od -c)"
testmark_info doc x >out
expect_eq "info: then a newline" "$(od -c out)" "$(printf 'text\n' | od -c)"
testmark_parse doc none.md
testmark_names doc >out
expect_eq "names: nothing, for no hunks" "$(wc -c <out)" "0"

# --- several handles at once, and re-parsing ---
testmark_parse a a.md
testmark_parse b b.md
testmark_parse e empty.md
expect_eq "handle a" "$(testmark_body a x)" "one"
expect_eq "handle b" "$(testmark_body b x)" "two"
expect_fail "an empty doc doesn't see another doc's hunks" 'testmark: e: no hunk named "x"' testmark_body e x
testmark_parse a b.md
expect_eq "re-parse replaces the doc" "$(testmark_body a x)" "two"
expect_fail "re-parse drops the old doc's names" 'testmark: a: no hunk named "old"' testmark_body a old
expect_eq "re-parse leaves other handles alone" "$(testmark_names b)" "x"
expect_fail "a failed re-parse fails" 'testmark: missing.md: not a readable file' testmark_parse b missing.md
expect_fail "and leaves its handle unusable" 'testmark: unknown handle "b"' testmark_names b

# --- sourcing: twice keeps state; inside a function still makes globals ---
# shellcheck source=lib/testmark.bash
source "$TESTDATA/../../lib/testmark.bash"
expect_eq "sourcing again keeps open docs" "$(testmark_body a x)" "two"
testmark_parse c none.md
expect_fail "and later parses don't collide with them" 'testmark: c: no hunk named "x"' testmark_body c x
expect_eq "sourcing inside a function" "$(
	"$BASH" -c 'f() { source "$1"; }; f "$1"; testmark_parse d1 a.md; testmark_parse d2 b.md; testmark_body d1 x' _ "$TESTDATA/../../lib/testmark.bash"
)" "one"

# --- errors ---
expect_fail "unknown handle" 'testmark: unknown handle "nope"' testmark_body nope x
expect_fail "unknown hunk" 'testmark: a: no hunk named "nope"' testmark_info a nope
expect_fail "invalid handle" 'testmark: invalid handle "a-b": must match [A-Za-z0-9_]+' testmark_parse a-b a.md
expect_fail "empty handle" 'testmark: invalid handle "": must match [A-Za-z0-9_]+' testmark_parse '' a.md
expect_fail "directory" 'testmark: .: not a readable file' testmark_parse a .
expect_fail "parse arity" 'testmark: usage: testmark_parse HANDLE FILE' testmark_parse a
expect_fail "names arity" 'testmark: usage: testmark_names HANDLE' testmark_names
expect_fail "body arity" 'testmark: usage: testmark_body HANDLE NAME' testmark_body a x y
expect_fail "info arity" 'testmark: usage: testmark_info HANDLE NAME' testmark_info a

# --- hostile hunk names: plain data, never evaluated ---
names=('$(>pwned1)' '`>pwned2`' 'a]b' 'x[0]' '@' '*' '$HOME' "'q'" '"q"' 'a\b')
for name in "${names[@]}"; do
	printf '[testmark]:# (%s)\n```\nbody of %s\n```\n\n' "$name" "$name"
done >hostile.md
testmark_parse h hostile.md
mapfile -t got < <(testmark_names h)
expect_eq "hostile names list" "$(printf '%s\n' "${got[@]}")" "$(printf '%s\n' "${names[@]}")"
for name in "${names[@]}"; do
	expect_eq "hostile name $name" "$(testmark_body h "$name")" "body of $name"
done
expect_fail "a hostile unknown name" 'testmark: h: no hunk named "$(>pwned3)"' testmark_body h '$(>pwned3)'
expect_eq "nothing was executed" "$(echo pwned*)" "pwned*"

exit "$RESULT"

tests/test-api.sh · 4.1 KiB · 4469ad6c39