From 3465f3ba7d869047b64f3e358cc07897aadfe5fe Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Wed, 19 Aug 2026 20:42:09 +0200 Subject: [PATCH] fix(runner): run bench file teardown before aborting on a bad annotation A malformed benchmark annotation aborted the run with `exit 1` from inside call_bench_functions. set_up_before_script had already run, and the file's tear_down_after_script never did. Return the failure to the bench loop instead, so the file's cleanup block runs, and abort from there. The annotation error still stops the whole run with the same message and exit code: a value the runner cannot honour would otherwise measure something other than what the annotation asked for (#884). The test runner has the same defect through its own @timeout/@retry validation. Filed separately as #1329 rather than widened into this fix. Closes #1322 --- CHANGELOG.md | 1 + src/runner/bench.sh | 10 ++++++++- tests/acceptance/bashunit_bench_test.sh | 28 +++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) 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)"