Skip to content

Commit 1bcd003

Browse files
authored
fix(runner): fail test on syntax error instead of silent halt (#628)
1 parent b2ddec7 commit 1bcd003

6 files changed

Lines changed: 85 additions & 1 deletion

File tree

.github/workflows/static_analysis.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,6 @@ jobs:
2525
uses: ludeeus/action-shellcheck@master
2626
env:
2727
SHELLCHECK_OPTS: -e SC1091 -e SC2155 -e SC2016
28+
with:
29+
ignore_names: test_bashunit_when_syntax_error.bash
2830

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
- Fix `clock::now` shell-time parsing when `EPOCHREALTIME` uses a comma decimal separator
1414
- Fix LCOV and HTML coverage reports generating incomplete/empty output due to post-increment operator causing silent exit under `set -e` (#618)
1515
- Enable parallel test execution on Alpine Linux; previously gated off due to race conditions, now resolved (#370)
16+
- Fix syntax error in test file silently passing; now reported as a failing test (#220)
1617

1718
## [0.34.1](https://github.com/TypedDevs/bashunit/compare/0.34.0...0.34.1) - 2026-03-20
1819

docs/test-files.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,23 @@ function tear_down_after_script() {
129129
}
130130
```
131131
:::
132+
133+
## Syntax errors in test files
134+
135+
If a test file contains a Bash syntax error, **bashunit** records a failing
136+
test for that file instead of silently skipping the remaining tests. The exact
137+
error message from Bash (including file path and line number) is shown in the
138+
summary, and the suite exits with a non-zero status.
139+
140+
```
141+
Running tests/example_test.sh
142+
✗ Error: Source
143+
tests/example_test.sh: line 10: syntax error near unexpected token `fi'
144+
tests/example_test.sh: line 10: ` fi'
145+
146+
Tests: 1 failed, 1 total
147+
Some tests failed
148+
```
149+
150+
This guarantees a broken test file always fails the suite, so it never
151+
passes by absence.

src/runner.sh

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,26 @@ function bashunit::runner::load_test_files() {
7272
scripts_ids[scripts_ids_count]="${BASHUNIT_CURRENT_SCRIPT_ID}"
7373
scripts_ids_count=$((scripts_ids_count + 1))
7474
bashunit::internal_log "Loading file" "$test_file"
75+
local source_err_file source_err source_status
76+
source_err_file="$(bashunit::temp_file "source_err")"
7577
# shellcheck source=/dev/null
76-
source "$test_file"
78+
source "$test_file" 2>"$source_err_file"
79+
source_status=$?
80+
source_err=""
81+
if [ -s "$source_err_file" ]; then
82+
source_err="$(cat "$source_err_file")"
83+
fi
84+
rm -f "$source_err_file"
85+
if [ "$source_status" -ne 0 ] || [ "$(printf '%s' "$source_err" \
86+
| "$GREP" -cE 'syntax error|unexpected EOF' || true)" -gt 0 ]; then
87+
local message="$source_err"
88+
[ -z "$message" ] && message="Failed to source '$test_file' (exit $source_status)"
89+
bashunit::runner::record_file_hook_failure \
90+
"source" "$test_file" "$message" 1 true
91+
bashunit::runner::clean_set_up_and_tear_down_after_script
92+
bashunit::runner::restore_workdir
93+
continue
94+
fi
7795
# Update function cache after sourcing new test file
7896
_BASHUNIT_CACHED_ALL_FUNCTIONS=$(declare -F | awk '{print $3}')
7997
# Check if any tests match the filter before rendering header or running hooks
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
function set_up_before_script() {
5+
TEST_ENV_FILE="tests/acceptance/fixtures/.env.default"
6+
}
7+
8+
function strip_ansi() {
9+
sed -E 's/\x1B\[[0-9;]*[A-Za-z]//g'
10+
}
11+
12+
function test_bashunit_when_test_file_has_syntax_error() {
13+
local test_file=./tests/acceptance/fixtures/test_bashunit_when_syntax_error.bash
14+
15+
local actual_raw
16+
set +e
17+
actual_raw="$(LC_ALL=C LANG=C ./bashunit \
18+
--no-parallel --detailed --env "$TEST_ENV_FILE" "$test_file" 2>&1)"
19+
set -e
20+
21+
local actual
22+
actual="$(printf "%s" "$actual_raw" | strip_ansi)"
23+
24+
assert_contains "failed" "$actual"
25+
assert_contains "Error" "$actual"
26+
assert_general_error "$(LC_ALL=C LANG=C ./bashunit \
27+
--no-parallel --env "$TEST_ENV_FILE" "$test_file" 2>&1)"
28+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/usr/bin/env bash
2+
3+
function test_good() {
4+
assert_equals 1 1
5+
}
6+
7+
function test_with_syntax_error() {
8+
if [ 1 -eq 1 ]
9+
echo "missing then keyword"
10+
fi
11+
}
12+
13+
function test_another() {
14+
assert_equals 2 2
15+
}

0 commit comments

Comments
 (0)