Skip to content

Annotations, tags and the header count ignore functions declared without parentheses #1422

Closed

Description

@Chemaclass

Problem

docs/test-files.md:28 says all three Bash forms are supported: function name(), name() and function name without parentheses. Tests written in the third form run, but every annotation above them is ignored and the header does not count them.

Reproduced on main 776997e9, macOS arm64, Bash 3.2.57:

cat > annot_test.sh <<'EOF'
function provide_rows() {
  bashunit::data_set 1
  bashunit::data_set 2
}
# @data_provider provide_rows
function test_with_provider {
  assert_same 1 "$#"
}
# @skip not ready
function test_skipped_by_annotation {
  assert_same 1 2
}
# @tag slow
function test_tagged {
  assert_same 1 1
}
EOF
./bashunit --skip-env-file annot_test.sh
./bashunit --skip-env-file --tag slow annot_test.sh

First run: the header shows no test count, test_with_provider runs once with zero arguments and fails, test_skipped_by_annotation runs and fails instead of being skipped. Second run: No tests found. With () added to the three definitions, the provider yields two cases, the skip is honoured and --tag slow selects one test.

A smaller case: function test_a { next to function test_b() { prints Tests: 1 in the header and 2 total in the summary, in both modes.

Where

Every textual scanner requires () after the name:

  • src/helper/provider.sh:153 and :165 (provider map, annotations, static header count).
  • src/helper/tags.sh:92 (tag map).
  • src/helper/discovery.sh:20 (duplicate check).

Execution does not: functions_for_script (src/runner/discovery.sh:324) uses declare -F, so the test runs, which is what makes the mismatch silent.

Why it matters

The docs promise the form, and a test that loses its @skip runs code the author marked as not ready, while a test that loses its @data_provider runs once with no arguments. --tag selections and the header count are wrong for every such file.

Suggested change

Accept [[:space:]]*(\(\))? after the name in the four patterns, so function name { and function name are scanned like name(). Add the form to the fixtures used by the provider, tag, duplicate and header-count tests. If the form is not meant to be supported, change the docs and make discovery refuse it loudly instead.

Acceptance criteria

  • @data_provider, @skip, @timeout, @retry and @tag apply to function name { definitions exactly as to name().
  • The header count equals the summary total for a file mixing the three forms, in sequential and parallel mode.
  • Duplicate detection reports two function test_x { definitions.
  • Tests cover each scanner with the parenthesis-free form and fail on the current code.

Proposed fix

Teach the four textual scanners the function name { form. A bare word must not match, so the parenthesis-free form requires the function keyword:

  • src/helper/provider.sh:153 (per-line definition match) and :165 (column-0 static count), src/helper/tags.sh:92, src/helper/discovery.sh:20: replace the single pattern with the alternation below, and extend the name extraction (provider.sh:156-157, tags.sh:95, discovery.sh:23-24) to strip [[:space:]]*(\(\)|\{).* after the name.
# definition: `function name`, `function name()`, or `name()`, with or without `{` on the line
is_fn = match($0, /^[[:space:]]*function[[:space:]]+[A-Za-z_][A-Za-z0-9_:]*[[:space:]]*(\(\))?[[:space:]]*(\{|$)/) ||
        match($0, /^[[:space:]]*[A-Za-z_][A-Za-z0-9_:]*[[:space:]]*\(\)/)
if (is_fn) {
  fn = $0
  sub(/^[[:space:]]*(function[[:space:]]+)?/, "", fn)
  sub(/[[:space:]]*(\(\)|\{).*/, "", fn)
}

The ? quantifier and alternation are POSIX awk ERE and work on macOS awk, gawk and busybox. Bash 3.0: nothing in the shell changes. Public behaviour that must not change: name() and function name() scanning, the "dynamic file" rule for indented definitions (provider.sh:160-171), the @data_provider two-line distance rule, and tests/acceptance/bashunit_test_function_names_test.sh. Docs: docs/test-files.md:28 stays true, no change. CHANGELOG: one line under Unreleased, Fixed.

Agent brief

  • Branch: fix/1422-scan-function-keyword-without-parens from fresh origin/main. Commit: fix(provider): honour annotations and tags on functions declared without parentheses with Closes #1422 in the body.
  • Files: src/helper/provider.sh, src/helper/tags.sh, src/helper/discovery.sh; extend tests/unit/helper/annotations_test.sh (copy its first build_provider_map test), tests/unit/helper/tags_map_test.sh, and tests/acceptance/bashunit_test_function_names_test.sh (line 29) for the header count.
  • RED first:
function test_annotations_apply_to_a_function_declared_without_parentheses() {
  local file
  file="$(bashunit::temp_file annot)"
  printf '# @skip not ready\nfunction test_no_parens {\n  :\n}\n' >"$file"
  bashunit::helper::build_provider_map "$file"
  bashunit::helper::annotations_for_function test_no_parens
  assert_same true "$_BASHUNIT_ANNOT_SKIP_OUT"
}

Fails on the current code: the slot is empty. Run only that file: ./bashunit tests/unit/helper/annotations_test.sh.

  • Non-goals: discovery itself (functions_for_script, declare -F) already runs these tests; do not touch it. Coordinate with Duplicate test functions are not detected when the opening brace is on the next line #1421, which edits the same discovery.sh:20 pattern; if it is merged first, extend its pattern instead of replacing it.
  • Gate, once, when the change is complete: ./bashunit tests/, ./bashunit --parallel --simple --strict tests/, make sa, make lint. make lint is the formatting authority; never shfmt -w; 2-space indent.
  • Done when: every acceptance box is checked, the gate is green, the CHANGELOG line is in, and a PR is open with the pr skill.

Activity

  1. added
    p2Wrong behaviour users hit in normal use
    agent-readyScoped, reproduced and testable; an agent can take it without more questions
    area:providersCode under src or scripts for providers
    area:runnerCode under src or scripts for runner
    on Oct 28, 2026
  2. self-assigned this
    on Oct 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

agent-readyScoped, reproduced and testable; an agent can take it without more questionsarea:providersCode under src or scripts for providersarea:runnerCode under src or scripts for runnerbugSomething isn't workingp2Wrong behaviour users hit in normal use

Type

No type

Projects

  • Status
    Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions