Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- The coverage HTML report handles filenames containing `|`, `<` or `&`. Rows were joined and split on `|`, so `src/a|b.sh` truncated to `a` in the index while the coverage numbers stayed right, and filenames went into the markup unescaped — `a<b>c.sh` was parsed as a tag and leaked into the document. The escaper now lives in `src/util/str.sh`, shared with the test report instead of duplicated (#1254)
- The HTML report summary counts risky and flaky tests. A run with a risky test showed `2 total` against categories summing to 1, with nothing on the page saying where the second test went — the row was there with its own CSS class, but the summary never counted it. The console and the Markdown report both report it (#1252)
- The HTML report says **why** a test failed. It listed name, status and duration only, while JUnit, JSON, TAP and Markdown all carry the message — and HTML is the format opened in a browser to find out what broke. A `Failures` section now gives each failure its name, `file:line` and message; a green run gains nothing (#1251)
- The bootstrap error names the actual cause. `cannot read the bootstrap file` covered a missing path, a directory, a device and a genuinely unreadable file alike, and was true of only the last — a directory *is* readable, since the check that rejects it is `-f`, not `-r`. Pointing `--env` at a directory or mistyping a filename now say so (#1262)
- `--env` with a space in the path explains itself. The flag takes `"file arg1 arg2"` and splits on the first space, so `--env "my boot.sh"` reported `cannot read the bootstrap file: 'my'` — a path the user never typed, for a file that is right there. It now says the value was split and that `BASHUNIT_BOOTSTRAP` takes the path whole; a genuinely missing file keeps the terse message (#1247)
- A `--filter` that selects nothing now explains why instead of ending on a bare `No tests found`: filters match the test **function name**, case-sensitively, while the report prints a humanized title, so feeding back the name you just read (`--filter "User login"` for `test_user_login`) silently matched nothing. The run names the test it most likely meant, resolving both the capitalisation and the spaces
- `bashunit doc <filter>` says `No assertion matches '<filter>'` instead of printing nothing, which was indistinguishable from a broken install. Mirrors the existing `--custom` wording; the exit code stays 0 because `doc` is informational (#1201)
Expand Down
22 changes: 20 additions & 2 deletions src/main/validate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,26 @@ function bashunit::main::report_unreadable_bootstrap() {
local boot_file=$1
local raw=${2-}

printf "%sError: cannot read the bootstrap file: '%s'.%s\n" \
"$_BASHUNIT_COLOR_FAILED" "$boot_file" "$_BASHUNIT_COLOR_DEFAULT" >&2
# Name the actual cause. All three used to report "cannot read", which is true
# of only one: a directory *is* readable -- the check that rejects it is -f,
# not -r -- and for a missing path "cannot read" understates "is not there".
# Pointing --env at a directory or mistyping a filename are ordinary mistakes,
# and one message sent the reader to permissions for both (#1262).
if [ ! -e "$boot_file" ]; then
printf "%sError: the bootstrap file does not exist: '%s'.%s\n" \
"$_BASHUNIT_COLOR_FAILED" "$boot_file" "$_BASHUNIT_COLOR_DEFAULT" >&2
elif [ -d "$boot_file" ]; then
printf "%sError: the bootstrap path is a directory, not a file: '%s'.%s\n" \
"$_BASHUNIT_COLOR_FAILED" "$boot_file" "$_BASHUNIT_COLOR_DEFAULT" >&2
elif [ ! -f "$boot_file" ]; then
# Readable, but not a regular file -- /dev/null and friends. The caller
# rejects on -f, so say that rather than blame permissions.
printf "%sError: the bootstrap path is not a regular file: '%s'.%s\n" \
"$_BASHUNIT_COLOR_FAILED" "$boot_file" "$_BASHUNIT_COLOR_DEFAULT" >&2
else
printf "%sError: cannot read the bootstrap file: '%s'.%s\n" \
"$_BASHUNIT_COLOR_FAILED" "$boot_file" "$_BASHUNIT_COLOR_DEFAULT" >&2
fi

if [ "$raw" != "$boot_file" ] && [ -r "$raw" ]; then
printf "%s--env splits its value on the first space to pass bootstrap arguments,%s\n" \
Expand Down
83 changes: 83 additions & 0 deletions tests/acceptance/bashunit_bootstrap_diagnosis_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#!/usr/bin/env bash
# shellcheck disable=SC2317

set -euo pipefail

# `-e/--env/--boot` reported "cannot read the bootstrap file" for four
# different causes and it was true of one: a directory *is* readable -- the
# check that rejects it is -f, not -r -- a missing path is not there at all,
# and /dev/null is readable but not a regular file (#1262).
#
# These live apart from bashunit_init_test.sh on purpose: the unreadable case
# can only be set up with chmod, which is a no-op for root, so it skips on the
# Bash 3.0 CI image -- and bashunit_summary_output_test.sh asserts that file
# has exactly one skipped test.

BASHUNIT_PATH="$PWD/bashunit"

function set_up() {
TMP_DIR=$(mktemp -d)
}

function tear_down() {
rm -rf "$TMP_DIR"
}

# One message covered three different causes, and was true of only one: a
# directory *is* readable -- the check that rejects it is -f, not -r -- and for
# a missing path "cannot read" understates "is not there" (#1262).
function test_a_bootstrap_that_does_not_exist_says_so() {
pushd "$TMP_DIR" >/dev/null
printf 'function test_ok() { assert_same 1 1; }\n' >t_test.sh

local output
output=$("$BASHUNIT_PATH" --no-parallel --env nope.sh t_test.sh 2>&1) || true
popd >/dev/null

assert_contains "does not exist" "$output"
}

function test_a_bootstrap_that_is_a_directory_says_so() {
pushd "$TMP_DIR" >/dev/null
mkdir -p boot_dir
printf 'function test_ok() { assert_same 1 1; }\n' >t_test.sh

local output
output=$("$BASHUNIT_PATH" --no-parallel --env boot_dir t_test.sh 2>&1) || true
popd >/dev/null

assert_contains "is a directory" "$output"
}

# Readable, but not a regular file. /dev/null exists everywhere the suite runs.
function test_a_bootstrap_that_is_not_a_regular_file_says_so() {
pushd "$TMP_DIR" >/dev/null
printf 'function test_ok() { assert_same 1 1; }\n' >t_test.sh

local output
output=$("$BASHUNIT_PATH" --no-parallel --env /dev/null t_test.sh 2>&1) || true
popd >/dev/null

assert_contains "not a regular file" "$output"
}

# The remaining case keeps the original wording, which is accurate for it. Root
# can read a mode-000 file, so ask the kernel rather than assume.
function test_an_unreadable_bootstrap_still_says_cannot_read() {
pushd "$TMP_DIR" >/dev/null
printf 'function test_ok() { assert_same 1 1; }\n' >t_test.sh
: >unread.sh
chmod 000 unread.sh
if [ -r unread.sh ]; then
chmod 644 unread.sh
popd >/dev/null
bashunit::skip "the current user can read a mode-000 file" && return
fi

local output
output=$("$BASHUNIT_PATH" --no-parallel --env unread.sh t_test.sh 2>&1) || true
chmod 644 unread.sh
popd >/dev/null

assert_contains "cannot read the bootstrap file" "$output"
}
14 changes: 13 additions & 1 deletion tests/acceptance/bashunit_init_test.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
#!/usr/bin/env bash
# shellcheck disable=SC2317

# This file must keep exactly ONE skipped test. bashunit_summary_output_test.sh
# runs it to demonstrate --show-skipped and asserts the wording "There was 1
# skipped test:", so a second skip turns that into "There were 2" and fails.
#
# A conditional skip is the dangerous kind: a permission test set up with
# `chmod 000` is a no-op for root, so it runs locally and skips only on the
# Bash 3.0 CI image -- green here, five jobs red there (#1264). Put those in
# their own file; bashunit_bootstrap_diagnosis_test.sh exists for that.

set -euo pipefail

BASHUNIT_PATH="$PWD/bashunit"
Expand Down Expand Up @@ -227,7 +236,10 @@ function test_a_missing_env_file_without_a_space_stays_terse() {
popd >/dev/null

assert_general_error "" "" "$ec"
assert_contains "cannot read the bootstrap file" "$output"
# The point of this test is the *absence* of the space explanation, not the
# wording of the error: since #1262 a missing file says "does not exist"
# rather than "cannot read", which was only ever true of an unreadable one.
assert_contains "bootstrap file does not exist" "$output"
assert_not_contains "BASHUNIT_BOOTSTRAP" "$output"
}

Expand Down
Loading