testmark-bash

run: set TESTCASE_DIR in every case, on both roads: the test script's directory.

commit 0bd1c475774ce19155ac745e32a9d527aa2e426a · parent 659f60b19c · browse this tree
Ximianth, 2026-10-25 14:54:23 UTC

Cases start there, but a case that moves (testcase_withtmpdir, or its own cd) still needs to reach the tool under test.
It's a plain variable in the case's bash, not exported, so the tools being tested don't see it.
diff --git a/examples/test-todos-files.sh b/examples/test-todos-files.sh
index ab1efb3..f4bff73 100755
--- a/examples/test-todos-files.sh
+++ b/examples/test-todos-files.sh
@@ -4,11 +4,10 @@
 source "$(dirname "${BASH_SOURCE[0]}")/../lib/testmark.bash"
 
 exercise_todos_files() {
-	local here=$PWD
 	testcase_withtmpdir
 	testcase_read_myhunk mine.txt >mine.txt
 	testcase_read_anyhunk shared/theirs.txt >theirs.txt
-	"$here/todos.sh" mine.txt theirs.txt | testcase_output output
+	"$TESTCASE_DIR/todos.sh" mine.txt theirs.txt | testcase_output output
 }
 
 testmarktest_rule '*/mine.txt' exercise_todos_files
diff --git a/lib/testmark.bash b/lib/testmark.bash
index 470ca26..9642d77 100644
--- a/lib/testmark.bash
+++ b/lib/testmark.bash
@@ -105,6 +105,7 @@ testmark_check() { _testmark_call _testmark_check "$@"; }
 ## If FN exits nonzero, a line `exit status: N` is added to its output before the check.
 ##
 ## FN runs under `set -euo pipefail`.  It sees exported variables (except BASH_ENV, so its bash starts clean), but no other variables, and no functions but itself and those given to testmarktest_provide.
+## One more variable is set for it, not exported: TESTCASE_DIR, the script's directory, for reaching things there after a `cd`.
 ##
 ## Bash's own runtime error messages from inside these functions (a command not found, an unbound variable) are relabeled in the output:
 ## `FN: message`, with no path and no line number, so the text is the same wherever the test runs.
@@ -124,7 +125,7 @@ testmarktest_rule_basic() { _testmark_call _testmarktest_rule_basic "$@"; }
 ## - Anything FN prints otherwise, and a nonzero exit, have no hunk to go in, so they fail the case.
 ##   They're shown as a diff from nothing, with an `exit status: N` line; regen can't fix that.
 ## - `testcase_withtmpdir` makes a temp dir, and makes it FN's working directory; it's removed when FN ends.
-##   Take what you need from the old working directory first: `local here=$PWD`.
+##   TESTCASE_DIR still names the script's directory.
 ##
 ## Each of these prints a message and returns nonzero on misuse, or a hunk that isn't there.
 ## They exist only inside FN; fd 3 inside FN is theirs too, so nothing else there may write to it.
diff --git a/lib/testmark/run.bash b/lib/testmark/run.bash
index b059ff2..533c1e4 100644
--- a/lib/testmark/run.bash
+++ b/lib/testmark/run.bash
@@ -174,6 +174,7 @@ _testmarktest_exec() {
 _testmarktest_script() {
 	local fn fd=$_testmarktest_fdbase
 	printf 'set -euo pipefail\n'
+	printf 'TESTCASE_DIR=%q\n' "$_testmarktest_dir"
 	if (($# == 2)); then
 		printf '%s\n' "$_testmarktest_testcase"
 		printf '_testcase_case=%q\n' "$2"
diff --git a/tests/test-run-full.sh b/tests/test-run-full.sh
index 2a379a6..acfbce4 100755
--- a/tests/test-run-full.sh
+++ b/tests/test-run-full.sh
@@ -219,13 +219,12 @@ testmark: WORK/t/doc.md:6: hunk \"a/in\": the case's report to the runner was ga
 
 # --- the working directory ---
 
-run_case "testcase_withtmpdir moves the case into a new dir, removed after" "$(one $'here: WORK/t\nin tmp: yes\nwrote: hi\n' '')" '
+run_case "testcase_withtmpdir moves the case into a new dir, removed after; TESTCASE_DIR still names the script's" "$(one $'in tmp: yes\nwrote: hi\n' '')" '
 exercise_x() {
-	local here=$PWD
 	testcase_withtmpdir
 	echo hi >f
-	{ echo "here: $here"; [[ $PWD != "$here" ]] && echo "in tmp: yes"; echo "wrote: $(<f)"; } | sed "s|^here: .*/t$|here: WORK/t|" | testcase_output out
-	echo "$PWD" >"$here/tmpdir"
+	{ [[ $PWD != "$TESTCASE_DIR" ]] && echo "in tmp: yes"; echo "wrote: $(<f)"; } | testcase_output out
+	echo "$PWD" >"$TESTCASE_DIR/tmpdir"
 }
 testmarktest_rule "*/in" exercise_x
 testmarktest_run doc.md
diff --git a/tests/test-run.sh b/tests/test-run.sh
index 8df6af3..a5df978 100755
--- a/tests/test-run.sh
+++ b/tests/test-run.sh
@@ -216,6 +216,15 @@ testmarktest_run doc.md' "PASS  doc.md: a/in (exercise_pwd)
 
 --- status 0"
 
+run_case "TESTCASE_DIR is the script's dir, set but not exported" "$(one "$work/t"$'\n'"unset"$'\n')" '
+exercise_dir() { echo "$TESTCASE_DIR"; bash -c "echo \${TESTCASE_DIR-unset}"; }
+testmarktest_rule_basic "*/in" exercise_dir
+cd /
+testmarktest_run doc.md' "PASS  doc.md: a/in (exercise_dir)
+--- stderr
+
+--- status 0"
+
 run_case "exported variables cross; others don't" "$(one $'yes\nunset\n')" '
 export EXPORTED=yes
 NOT_EXPORTED=no