testmark-bash

examples: test todos.sh with the runner; keep the hand-rolled loop as test-todos-alone.sh.

commit 8dfec622109024bd0898da235bdb4cf7f25202ff · parent e85d9b9d37 · browse this tree
Ximianth, 2026-10-24 14:38:19 UTC

The runner's version is a source line, the exercise function, a rule, and the run.
Both feed todos.sh the input as /dev/stdin, so neither needs a temp dir or a sed to normalize it,
and both check the same fixtures.
diff --git a/Makefile b/Makefile
index 72d5216..cf4999f 100644
--- a/Makefile
+++ b/Makefile
@@ -30,3 +30,4 @@ check: ## Verify-only and fast; run by the pre-commit hook on every commit.
 test: ## Run all tests.
 	tests/run.sh
 	examples/test-todos.sh
+	examples/test-todos-alone.sh
diff --git a/examples/test-todos-alone.sh b/examples/test-todos-alone.sh
new file mode 100755
index 0000000..e7903c5
--- /dev/null
+++ b/examples/test-todos-alone.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+## Checks todos.sh against every case in todos.md, as test-todos.sh does, but with the primitives instead of the runner.
+## Run with TESTMARK_REGEN=1 to update todos.md to match what todos.sh does now; then review the change with `git diff`.
+set -euo pipefail
+cd "$(dirname "$0")"
+source ../lib/testmark.bash
+
+testmark_parse doc todos.md
+mapfile -t names < <(testmark_names doc)
+fails=0
+for name in "${names[@]}"; do
+	case $name in
+	*/input)
+		case=${name%/input}
+		if testmark_check doc "$case/output" < <(testmark_body doc "$name" | ./todos.sh /dev/stdin); then
+			echo "PASS  $case"
+		else
+			echo "FAIL  $case"
+			fails=$((fails + 1))
+		fi
+		;;
+	esac
+done
+testmark_write doc
+((fails == 0))
diff --git a/examples/test-todos.sh b/examples/test-todos.sh
index 61a3ceb..51f1d0c 100755
--- a/examples/test-todos.sh
+++ b/examples/test-todos.sh
@@ -1,29 +1,11 @@
 #!/bin/bash
 ## Checks todos.sh against every case in todos.md.
 ## Run with TESTMARK_REGEN=1 to update todos.md to match what todos.sh does now; then review the change with `git diff`.
-set -euo pipefail
-cd "$(dirname "$0")"
-source ../lib/testmark.bash
+source "$(dirname "$0")/../lib/testmark.bash"
 
-work=$(mktemp -d)
-trap 'rm -rf "$work"' EXIT
+exercise_todos() {
+	./todos.sh /dev/stdin
+}
 
-testmark_parse doc todos.md
-mapfile -t names < <(testmark_names doc)
-fails=0
-for name in "${names[@]}"; do
-	case $name in
-	*/input)
-		case=${name%/input}
-		testmark_body doc "$name" >"$work/notes.txt"
-		if testmark_check doc "$case/output" < <(./todos.sh "$work/notes.txt" | sed "s|$work|WORK|g"); then
-			echo "PASS  $case"
-		else
-			echo "FAIL  $case"
-			fails=$((fails + 1))
-		fi
-		;;
-	esac
-done
-testmark_write doc
-((fails == 0))
+testmarktest_rule_basic '*/input' exercise_todos
+testmarktest_run todos.md
diff --git a/examples/todos.md b/examples/todos.md
index 2d04c3c..4039dfa 100644
--- a/examples/todos.md
+++ b/examples/todos.md
@@ -5,7 +5,7 @@ todos.sh, by example
 
 Each case below is a pair of hunks:
 `CASE/input` is a file's content, and `CASE/output` is what `todos.sh` prints for that file.
-`test-todos.sh` runs every case, with the file in a temporary directory whose path it replaces with `WORK`.
+`test-todos.sh` runs every case, handing `todos.sh` the input as `/dev/stdin`.
 
 
 ### the basics
@@ -22,8 +22,8 @@ Lines are numbered from one.
 
 [testmark]:# (basic/output)
 ```text
-WORK/notes.txt:2:# TODO: handle spaces in names
-WORK/notes.txt:4:# TODO: and newlines
+/dev/stdin:2:# TODO: handle spaces in names
+/dev/stdin:4:# TODO: and newlines
 ```
 
 
@@ -40,8 +40,8 @@ notes:
 
 [testmark]:# (verbatim/output)
 ```text
-WORK/notes.txt:2:	TODO: indented with a tab
-WORK/notes.txt:3:  \TODO: with a backslash
+/dev/stdin:2:	TODO: indented with a tab
+/dev/stdin:3:  \TODO: with a backslash
 ```