diff --git a/CHANGELOG.md b/CHANGELOG.md index ccf14cb9..39183b1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ - Performance: `--coverage` is roughly 5x faster and `--coverage-report-html` roughly 19x — a run over this repo went from 16.2s to 2.9s, and a 128-file HTML report from 58.7s to 3.1s. The report phase emits each format in one awk invocation per run instead of Bash loops and forks per file and per row, and the capture path writes records straight to disk, normalizes a path with one fork instead of four, and reads each cache once (#1092, #1096, #1098, #1099, #1102, #1104, #1110, #1117) ### Fixed -- A bootstrap file that fails to load now reports it and exits non-zero, instead of leaving the run with no tests, no summary and exit 0. `source` is a POSIX special builtin, so a syntax error or a bare `exit` in the file ends the shell before anything after the call can run — an absent bootstrap is still ignored, since the default path is one most projects do not have (#1179) +- A bootstrap file that fails to load now reports it and exits non-zero, through every path that loads one (`BASHUNIT_BOOTSTRAP`, `--env`, `--boot`, and the `bench` equivalents), instead of leaving the run with no tests, no summary and exit 0. `source` is a POSIX special builtin, so a syntax error or a bare `exit` in the file ends the shell before anything after the call can run — an absent bootstrap is still ignored, since the default path is one most projects do not have (#1179, #1181) - A report path that is a directory (`--report-junit build/` with the filename forgotten) fails fast with `is a directory, not a file` instead of passing validation, failing inside the writer with a raw bash message naming a bashunit source file, and exiting 0 with no report written. Affects every report flag and the `bench` equivalents (#1177) - `bashunit init` no longer adds a dead `BASHUNIT_BOOTSTRAP` line to `.env` on every run: it commented out the existing setting and appended a fresh copy even when the value was unchanged, so a third run left three lines, two of them inert. It also now reports what it did to `.env`, the one file it wrote without saying so (#1175) - A coverage run that tracked no executable line at all no longer reports it as `Coverage 0% is below minimum N%`: that 0% is arithmetic rather than a measurement, and the message sent the reader to their tests when the cause is almost always a `--coverage-paths` that matched nothing. The gate still fails, now naming the real problem (#1171) diff --git a/src/main/bench.sh b/src/main/bench.sh index 3aeeb624..8fe463a6 100644 --- a/src/main/bench.sh +++ b/src/main/bench.sh @@ -87,8 +87,13 @@ function bashunit::main::cmd_bench() { # Export all variables from the env file so they're available in subshells # (e.g., process substitution used in load_test_files) set -o allexport + # `source` is a special builtin: a syntax error in the file, or a bare + # `exit`, ends this shell here and nothing below runs. The marker is what + # the EXIT trap reports when it was never cleared (#1181). + _BASHUNIT_LOADING_BOOTSTRAP="$boot_file" # shellcheck disable=SC1090,SC2086 source "$boot_file" ${BASHUNIT_BOOTSTRAP_ARGS:-} + _BASHUNIT_LOADING_BOOTSTRAP="" set +o allexport shift ;; diff --git a/src/main/subcommands.sh b/src/main/subcommands.sh index 6ae837ae..24232a8c 100644 --- a/src/main/subcommands.sh +++ b/src/main/subcommands.sh @@ -43,8 +43,13 @@ function bashunit::main::cmd_doc() { exit 1 fi else + # `source` is a special builtin: a syntax error in the file, or a bare + # `exit`, ends this shell here and nothing below runs. The marker is what + # the EXIT trap reports when it was never cleared (#1181). + _BASHUNIT_LOADING_BOOTSTRAP="$boot_file" # shellcheck disable=SC1090,SC2086 source "$boot_file" ${BASHUNIT_BOOTSTRAP_ARGS:-} + _BASHUNIT_LOADING_BOOTSTRAP="" fi fi diff --git a/src/main/test.sh b/src/main/test.sh index 810c9fec..337bc73b 100644 --- a/src/main/test.sh +++ b/src/main/test.sh @@ -326,8 +326,13 @@ function bashunit::main::cmd_test() { # Export all variables from the env file so they're available in subshells # (e.g., process substitution used in load_test_files) set -o allexport + # `source` is a special builtin: a syntax error in the file, or a bare + # `exit`, ends this shell here and nothing below runs. The marker is what + # the EXIT trap reports when it was never cleared (#1181). + _BASHUNIT_LOADING_BOOTSTRAP="$boot_file" # shellcheck disable=SC1090,SC2086 source "$boot_file" ${BASHUNIT_BOOTSTRAP_ARGS:-} + _BASHUNIT_LOADING_BOOTSTRAP="" set +o allexport shift ;; diff --git a/tests/acceptance/bashunit_init_test.sh b/tests/acceptance/bashunit_init_test.sh index d6da4f91..5d255040 100644 --- a/tests/acceptance/bashunit_init_test.sh +++ b/tests/acceptance/bashunit_init_test.sh @@ -145,3 +145,49 @@ function test_an_absent_bootstrap_is_still_ignored() { assert_same 0 "$ec" assert_not_contains "bootstrap" "$output" } + +# #1180 covered the BASHUNIT_BOOTSTRAP env path. The --env/--boot *flag* sources +# its file at a different call site, and had the same hole: exit 0, no tests, +# no summary (#1181). +function test_an_env_flag_file_with_a_syntax_error_fails_the_run() { + pushd "$TMP_DIR" >/dev/null + printf 'function broken( {\n' >boot.sh + printf 'function test_ok() { assert_same 1 1; }\n' >t_test.sh + + local ec=0 + local output + output=$("$BASHUNIT_PATH" --no-parallel --env boot.sh t_test.sh 2>&1) || ec=$? + popd >/dev/null + + assert_general_error "" "" "$ec" + assert_contains "bootstrap" "$output" +} + +function test_an_env_flag_file_that_exits_says_so() { + pushd "$TMP_DIR" >/dev/null + printf 'exit 3\n' >boot.sh + printf 'function test_ok() { assert_same 1 1; }\n' >t_test.sh + + local ec=0 + local output + output=$("$BASHUNIT_PATH" --no-parallel --env boot.sh t_test.sh 2>&1) || ec=$? + popd >/dev/null + + assert_not_same 0 "$ec" + assert_contains "bootstrap" "$output" +} + +# A healthy --env file must still be sourced and its values reach the run. +function test_a_healthy_env_flag_file_still_loads() { + pushd "$TMP_DIR" >/dev/null + printf 'BASHUNIT_SHOW_HEADER=false\n' >boot.sh + printf 'function test_ok() { assert_same 1 1; }\n' >t_test.sh + + local ec=0 + local output + output=$("$BASHUNIT_PATH" --no-parallel --env boot.sh t_test.sh 2>&1) || ec=$? + popd >/dev/null + + assert_same 0 "$ec" + assert_contains "1 passed" "$output" +}