From a378b73c7eb4e3ba4f11abfd0e3c4889d90205ab Mon Sep 17 00:00:00 2001 From: bfs2021 <101915083+bfs2021@users.noreply.github.com> Date: Thu, 29 Oct 2026 11:27:36 +0800 Subject: [PATCH] fix(runner): detect duplicate test functions whose brace is on the next line The awk definition pattern required the opening brace on the same line, so a definition written as `function test_x()` with `{` on the next line was never counted. Accept either a brace or end of line after the parentheses; the existing field match and name extraction work on the name() token alone. Closes #1421 --- CHANGELOG.md | 1 + src/helper/discovery.sh | 12 ++++++------ tests/unit/helper/helper_test.sh | 15 +++++++++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac02f8faa..067c59326 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - `init` writes the bootstrap path and a custom tests directory to `.env` quoted, so a directory with a space runs afterwards instead of breaking every next run (#1411) - Failure source context stays inside a one-line test instead of listing the next function's assertions (#1420) - Parallel runs count tests under relative hidden paths and keep their results separate from matching visible paths (#1393) +- Duplicate test functions are detected when the opening brace is on the next line instead of silently replacing the first definition (#1421) ### Changed diff --git a/src/helper/discovery.sh b/src/helper/discovery.sh index e7ef08080..f12fca458 100644 --- a/src/helper/discovery.sh +++ b/src/helper/discovery.sh @@ -10,14 +10,14 @@ function bashunit::helper::check_duplicate_functions() { script="$BASHUNIT_WORKING_DIR/$script" fi - # One awk pass over the file finds each test-function definition and emits only - # the names seen more than once, folding the former grep + awk + sort + uniq - # chain into a single awk (#761). The END block insertion-sorts the (tiny, - # usually empty) duplicate list itself, so no `sort` fork is needed to keep - # the output deterministic. + # One awk pass over the file finds each test-function definition (the opening + # brace may sit on the next line) and emits only the names seen more than + # once, folding the former grep + awk + sort + uniq chain into a single awk + # (#761). The END block insertion-sorts the (tiny, usually empty) duplicate + # list itself, so no `sort` fork is needed to keep the output deterministic. local duplicates duplicates=$(awk ' - /^[[:space:]]*(function[[:space:]]+)?test[a-zA-Z_][a-zA-Z0-9_]*[[:space:]]*\(\)[[:space:]]*\{/ { + /^[[:space:]]*(function[[:space:]]+)?test[a-zA-Z_][a-zA-Z0-9_]*[[:space:]]*\(\)[[:space:]]*(\{|$)/ { for (i = 1; i <= NF; i++) { if ($i ~ /^test[a-zA-Z_][a-zA-Z0-9_]*\(\)$/) { name = $i diff --git a/tests/unit/helper/helper_test.sh b/tests/unit/helper/helper_test.sh index 32c64735c..7d45adf9b 100644 --- a/tests/unit/helper/helper_test.sh +++ b/tests/unit/helper/helper_test.sh @@ -130,6 +130,21 @@ function test_check_duplicate_functions_with_duplicates() { assert_general_error "$(bashunit::helper::check_duplicate_functions "$file")" } +function test_check_duplicate_functions_with_the_brace_on_the_next_line() { + local file detail + file="$(bashunit::temp_file duplicate_next_line).sh" + printf 'function %s()\n{\n :\n}\nfunction %s()\n{\n :\n}\n' \ + test_twice test_twice >"$file" + + assert_general_error "$(bashunit::helper::check_duplicate_functions "$file")" + + detail=$( + bashunit::helper::check_duplicate_functions "$file" >/dev/null 2>&1 || true + bashunit::state::get_duplicated_function_details + ) + assert_contains "test_twice (lines 1, 5)" "$detail" +} + function test_check_duplicate_functions_without_duplicates() { local file file="$(bashunit::current_dir)/../fixtures/no_duplicate_functions.sh"