diff --git a/CHANGELOG.md b/CHANGELOG.md index 91d978a6..c78da546 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - `tear_down_after_script` runs when `set_up_before_script` fails, so it can release file-scoped resources acquired before the failure (#1318) - Under `--parallel`, `tear_down_after_script` runs after the file's own tests instead of alongside them, so a fixture it releases stays alive for the tests that read it. The same file no longer passed sequentially and failed in parallel (#1320) - `--stop-on-failure` runs `tear_down_after_script` for the file it halts in, so a sequential run releases what `set_up_before_script` acquired before the halt (#1321) +- `bashunit bench` runs `tear_down_after_script` before it aborts on a malformed annotation, so the file releases what `set_up_before_script` acquired (#1322) ## [0.50.0](https://github.com/TypedDevs/bashunit/compare/0.49.0...0.50.0) - 2026-08-18 diff --git a/src/runner/bench.sh b/src/runner/bench.sh index 8bc3ba07..cb008ea3 100644 --- a/src/runner/bench.sh +++ b/src/runner/bench.sh @@ -82,10 +82,18 @@ function bashunit::runner::load_bench_files() { continue fi bashunit::runner::call_bench_functions "$bench_file" "$filter" + local bench_status=$? bashunit::runner::run_tear_down_after_script "$bench_file" bashunit::runner::clean_set_up_and_tear_down_after_script bashunit::cleanup_script_temp_files bashunit::runner::restore_workdir + # A malformed annotation still aborts the whole run, as it has to: a value + # the runner cannot honour would otherwise measure something other than what + # the annotation asked for (#884). It aborts from here rather than from + # inside call_bench_functions so the file's teardown runs first (#1322). + if [ "$bench_status" -ne 0 ]; then + exit "$bench_status" + fi done } @@ -121,7 +129,7 @@ function bashunit::runner::call_bench_functions() { # Capture separately so a malformed annotation aborts the run: the exit # status of a $(...) inside `read <<<` is otherwise discarded (#884). local parsed_annotations - parsed_annotations=$(bashunit::benchmark::parse_annotations "$fn_name" "$script") || exit 1 + parsed_annotations=$(bashunit::benchmark::parse_annotations "$fn_name" "$script") || return 1 read -r revs its max_ms <<<"$parsed_annotations" bashunit::benchmark::run_function "$fn_name" "$revs" "$its" "$max_ms" "$script" unset -v fn_name diff --git a/tests/acceptance/bashunit_bench_test.sh b/tests/acceptance/bashunit_bench_test.sh index 452f1f25..7efa656e 100644 --- a/tests/acceptance/bashunit_bench_test.sh +++ b/tests/acceptance/bashunit_bench_test.sh @@ -108,6 +108,34 @@ function test_bench_cleans_up_when_set_up_before_script_fails() { assert_file_not_exists "$marker" } +# A malformed annotation aborts the run from inside call_bench_functions, which +# used to skip the file's teardown along with everything else (#1322). +function test_bench_cleans_up_when_an_annotation_is_malformed() { + local dir fixture marker + dir="$(bashunit::temp_dir bench_annotation_cleanup)" + fixture="$dir/bad_annotation_bench.sh" + marker="$dir/resource" + { + printf 'RESOURCE=""\n' + printf 'function set_up_before_script() {\n' + printf ' RESOURCE="$CLEANUP_MARKER"\n' + printf ' : >"$RESOURCE"\n' + printf '}\n' + printf 'function tear_down_after_script() {\n' + printf ' rm -f "$RESOURCE"\n' + printf '}\n' + printf '# @revs=abc\n' + printf 'function bench_bad_annotation() { :; }\n' + } >"$fixture" + + local exit_code=0 output + output=$(CLEANUP_MARKER="$marker" ./bashunit bench "$fixture" 2>&1) || exit_code=$? + + assert_general_error "" "" "$exit_code" + assert_contains "@revs in '# @revs=abc' is not a valid value" "$(printf "%s" "$output" | strip_ansi)" + assert_file_not_exists "$marker" +} + function test_bench_fails_when_a_file_cannot_be_sourced() { local dir dir="$(bashunit::temp_dir bench_source_fail)"