diff --git a/CHANGELOG.md b/CHANGELOG.md index bdbe7a1d..f6aa7323 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Changed - Core string assertions (`assert_contains`/`assert_not_contains`, `assert_matches`/`assert_not_matches`, `assert_string_starts_with`/`assert_string_ends_with` and their negations) no longer fork a subshell per call to join their arguments; a fork-free join with identical behaviour replaces it (#844) +- The array, date, duration, json, files and folders assertions now resolve their failure label through the fork-free slot helper instead of a per-call command substitution — same labels, fewer forks ## [0.42.0](https://github.com/TypedDevs/bashunit/compare/0.41.0...0.42.0) - 2026-07-20 diff --git a/src/assert.sh b/src/assert.sh index 6436fb5b..a893a28d 100755 --- a/src/assert.sh +++ b/src/assert.sh @@ -31,9 +31,8 @@ function bashunit::assert::label_to_slot() { _BASHUNIT_ASSERT_JOINED_OUT="" # Join positional args into _BASHUNIT_ASSERT_JOINED_OUT with no fork. -# Mirrors $(printf '%s\n' "$@"): joins with newlines and strips trailing -# newlines, so callers that match on the result behave exactly as the previous -# command-substitution did. +# Output matches $(printf '%s\n' "$@") exactly: newline-joined, trailing +# newlines stripped (as command substitution strips them). function bashunit::assert::join_to_slot() { local IFS=$'\n' local joined="$*" diff --git a/src/assert_arrays.sh b/src/assert_arrays.sh index 2f917cc6..705cd7da 100644 --- a/src/assert_arrays.sh +++ b/src/assert_arrays.sh @@ -56,10 +56,8 @@ function assert_array_contains() { bashunit::assert::should_skip && return 0 local expected="$1" - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label - label="$(bashunit::helper::normalize_test_function_name "$test_fn")" + bashunit::assert::label_to_slot + local label=$_BASHUNIT_ASSERT_LABEL_OUT shift local -a actual @@ -82,10 +80,8 @@ function assert_array_length() { bashunit::assert::should_skip && return 0 local expected="$1" - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label - label="$(bashunit::helper::normalize_test_function_name "$test_fn")" + bashunit::assert::label_to_slot + local label=$_BASHUNIT_ASSERT_LABEL_OUT shift # Use $# / $* rather than building an array: on Bash 3.0 under `set -u`, @@ -106,10 +102,8 @@ function assert_array_not_contains() { bashunit::assert::should_skip && return 0 local expected="$1" - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label - label="$(bashunit::helper::normalize_test_function_name "$test_fn")" + bashunit::assert::label_to_slot + local label=$_BASHUNIT_ASSERT_LABEL_OUT shift local -a actual actual=("$@") diff --git a/src/assert_dates.sh b/src/assert_dates.sh index 849aff9d..48a609df 100644 --- a/src/assert_dates.sh +++ b/src/assert_dates.sh @@ -105,10 +105,8 @@ function assert_date_equals() { actual="$(bashunit::date::to_epoch "$2")" if [ "$actual" -ne "$expected" ]; then - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label - label="$(bashunit::helper::normalize_test_function_name "$test_fn")" + bashunit::assert::label_to_slot + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${actual}" "to be equal to" "${expected}" return @@ -126,10 +124,8 @@ function assert_date_before() { actual="$(bashunit::date::to_epoch "$2")" if [ "$actual" -ge "$expected" ]; then - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label - label="$(bashunit::helper::normalize_test_function_name "$test_fn")" + bashunit::assert::label_to_slot + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${actual}" "to be before" "${expected}" return @@ -147,10 +143,8 @@ function assert_date_after() { actual="$(bashunit::date::to_epoch "$2")" if [ "$actual" -le "$expected" ]; then - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label - label="$(bashunit::helper::normalize_test_function_name "$test_fn")" + bashunit::assert::label_to_slot + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${actual}" "to be after" "${expected}" return @@ -170,10 +164,8 @@ function assert_date_within_range() { actual="$(bashunit::date::to_epoch "$3")" if [ "$actual" -lt "$from" ] || [ "$actual" -gt "$to" ]; then - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label - label="$(bashunit::helper::normalize_test_function_name "$test_fn")" + bashunit::assert::label_to_slot + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${actual}" "to be between" "${from} and ${to}" return @@ -197,10 +189,8 @@ function assert_date_within_delta() { fi if [ "$diff" -gt "$delta" ]; then - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label - label="$(bashunit::helper::normalize_test_function_name "$test_fn")" + bashunit::assert::label_to_slot + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${actual}" "to be within" "${delta} seconds of ${expected}" return diff --git a/src/assert_duration.sh b/src/assert_duration.sh index 7e33f0b9..af290e9c 100644 --- a/src/assert_duration.sh +++ b/src/assert_duration.sh @@ -27,10 +27,8 @@ function assert_duration() { elapsed_ms=$(bashunit::duration::measure_ms "$command") if [ "$elapsed_ms" -gt "$threshold_ms" ]; then - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label - label="$(bashunit::helper::normalize_test_function_name "$test_fn")" + bashunit::assert::label_to_slot + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${threshold_ms}" "to complete within (ms)" "${command}" return @@ -49,10 +47,8 @@ function assert_duration_less_than() { elapsed_ms=$(bashunit::duration::measure_ms "$command") if [ "$elapsed_ms" -ge "$threshold_ms" ]; then - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label - label="$(bashunit::helper::normalize_test_function_name "$test_fn")" + bashunit::assert::label_to_slot + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${threshold_ms}" "to complete within (ms)" "${command}" return @@ -71,10 +67,8 @@ function assert_duration_greater_than() { elapsed_ms=$(bashunit::duration::measure_ms "$command") if [ "$elapsed_ms" -le "$threshold_ms" ]; then - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label - label="$(bashunit::helper::normalize_test_function_name "$test_fn")" + bashunit::assert::label_to_slot + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${threshold_ms}" "to take at least (ms)" "${command}" return diff --git a/src/assert_files.sh b/src/assert_files.sh index aeca0048..757ff09a 100644 --- a/src/assert_files.sh +++ b/src/assert_files.sh @@ -4,11 +4,10 @@ function assert_file_exists() { bashunit::assert::should_skip && return 0 local expected="$1" - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label="${3:-$(bashunit::helper::normalize_test_function_name "$test_fn")}" if [ ! -f "$expected" ]; then + bashunit::assert::label_to_slot "${3:-}" + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to exist but" "do not exist" return @@ -21,11 +20,10 @@ function assert_file_not_exists() { bashunit::assert::should_skip && return 0 local expected="$1" - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label="${3:-$(bashunit::helper::normalize_test_function_name "$test_fn")}" if [ -f "$expected" ]; then + bashunit::assert::label_to_slot "${3:-}" + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to not exist but" "the file exists" return @@ -38,11 +36,10 @@ function assert_is_file() { bashunit::assert::should_skip && return 0 local expected="$1" - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label="${3:-$(bashunit::helper::normalize_test_function_name "$test_fn")}" if [ ! -f "$expected" ]; then + bashunit::assert::label_to_slot "${3:-}" + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to be a file" "but is not a file" return @@ -55,11 +52,10 @@ function assert_is_file_empty() { bashunit::assert::should_skip && return 0 local expected="$1" - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label="${3:-$(bashunit::helper::normalize_test_function_name "$test_fn")}" if [ -s "$expected" ]; then + bashunit::assert::label_to_slot "${3:-}" + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to be empty" "but is not empty" return @@ -75,10 +71,8 @@ function assert_files_equals() { local actual="$2" if [ "$(diff -u "$expected" "$actual")" != '' ]; then - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label - label="$(bashunit::helper::normalize_test_function_name "$test_fn")" + bashunit::assert::label_to_slot + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "Compared" "${actual}" \ @@ -96,10 +90,8 @@ function assert_files_not_equals() { local actual="$2" if [ "$(diff -u "$expected" "$actual")" = '' ]; then - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label - label="$(bashunit::helper::normalize_test_function_name "$test_fn")" + bashunit::assert::label_to_slot + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "Compared" "${actual}" \ @@ -117,10 +109,8 @@ function assert_file_contains() { local string="$2" if ! grep -F -q "$string" "$file"; then - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label - label="$(bashunit::helper::normalize_test_function_name "$test_fn")" + bashunit::assert::label_to_slot + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${file}" "to contain" "${string}" @@ -137,10 +127,8 @@ function assert_file_not_contains() { local string="$2" if grep -q "$string" "$file"; then - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label - label="$(bashunit::helper::normalize_test_function_name "$test_fn")" + bashunit::assert::label_to_slot + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${file}" "to not contain" "${string}" @@ -172,10 +160,8 @@ function assert_file_permissions() { local expected="$1" local file="$2" - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label - label="$(bashunit::helper::normalize_test_function_name "$test_fn")" + bashunit::assert::label_to_slot + local label=$_BASHUNIT_ASSERT_LABEL_OUT if [ ! -e "$file" ]; then bashunit::assert::mark_failed diff --git a/src/assert_folders.sh b/src/assert_folders.sh index 9faddacc..369b21d2 100644 --- a/src/assert_folders.sh +++ b/src/assert_folders.sh @@ -4,11 +4,10 @@ function assert_directory_exists() { bashunit::assert::should_skip && return 0 local expected="$1" - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label="${2:-$(bashunit::helper::normalize_test_function_name "$test_fn")}" if [ ! -d "$expected" ]; then + bashunit::assert::label_to_slot "${2:-}" + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to exist but" "do not exist" return @@ -21,11 +20,10 @@ function assert_directory_not_exists() { bashunit::assert::should_skip && return 0 local expected="$1" - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label="${2:-$(bashunit::helper::normalize_test_function_name "$test_fn")}" if [ -d "$expected" ]; then + bashunit::assert::label_to_slot "${2:-}" + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to not exist but" "the directory exists" return @@ -38,11 +36,10 @@ function assert_is_directory() { bashunit::assert::should_skip && return 0 local expected="$1" - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label="${2:-$(bashunit::helper::normalize_test_function_name "$test_fn")}" if [ ! -d "$expected" ]; then + bashunit::assert::label_to_slot "${2:-}" + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to be a directory" "but is not a directory" return @@ -55,11 +52,10 @@ function assert_is_directory_empty() { bashunit::assert::should_skip && return 0 local expected="$1" - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label="${2:-$(bashunit::helper::normalize_test_function_name "$test_fn")}" if [ ! -d "$expected" ] || [ -n "$(ls -A "$expected")" ]; then + bashunit::assert::label_to_slot "${2:-}" + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to be empty" "but is not empty" return @@ -72,11 +68,10 @@ function assert_is_directory_not_empty() { bashunit::assert::should_skip && return 0 local expected="$1" - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label="${2:-$(bashunit::helper::normalize_test_function_name "$test_fn")}" if [ ! -d "$expected" ] || [ -z "$(ls -A "$expected")" ]; then + bashunit::assert::label_to_slot "${2:-}" + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to not be empty" "but is empty" return @@ -89,11 +84,10 @@ function assert_is_directory_readable() { bashunit::assert::should_skip && return 0 local expected="$1" - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label="${2:-$(bashunit::helper::normalize_test_function_name "$test_fn")}" if [ ! -d "$expected" ] || [ ! -r "$expected" ] || [ ! -x "$expected" ]; then + bashunit::assert::label_to_slot "${2:-}" + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to be readable" "but is not readable" return @@ -106,11 +100,10 @@ function assert_is_directory_not_readable() { bashunit::assert::should_skip && return 0 local expected="$1" - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label="${2:-$(bashunit::helper::normalize_test_function_name "$test_fn")}" if [ ! -d "$expected" ] || { [ -r "$expected" ] && [ -x "$expected" ]; }; then + bashunit::assert::label_to_slot "${2:-}" + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to be not readable" "but is readable" return @@ -123,11 +116,10 @@ function assert_is_directory_writable() { bashunit::assert::should_skip && return 0 local expected="$1" - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label="${2:-$(bashunit::helper::normalize_test_function_name "$test_fn")}" if [ ! -d "$expected" ] || [ ! -w "$expected" ]; then + bashunit::assert::label_to_slot "${2:-}" + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to be writable" "but is not writable" return @@ -140,11 +132,10 @@ function assert_is_directory_not_writable() { bashunit::assert::should_skip && return 0 local expected="$1" - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label="${2:-$(bashunit::helper::normalize_test_function_name "$test_fn")}" if [ ! -d "$expected" ] || [ -w "$expected" ]; then + bashunit::assert::label_to_slot "${2:-}" + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to be not writable" "but is writable" return diff --git a/src/assert_json.sh b/src/assert_json.sh index 8ec163a1..3ddf55ef 100644 --- a/src/assert_json.sh +++ b/src/assert_json.sh @@ -17,10 +17,8 @@ function assert_json_key_exists() { local result if ! result=$(printf '%s' "$json" | jq -e "$key" 2>/dev/null) || [ "$result" = "null" ]; then - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label - label="$(bashunit::helper::normalize_test_function_name "$test_fn")" + bashunit::assert::label_to_slot + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${json}" "to have key" "${key}" return @@ -39,20 +37,16 @@ function assert_json_contains() { local result if ! result=$(printf '%s' "$json" | jq -e -r "$key" 2>/dev/null) || [ "$result" = "null" ]; then - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label - label="$(bashunit::helper::normalize_test_function_name "$test_fn")" + bashunit::assert::label_to_slot + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${json}" "to have key" "${key}" return fi if [ "$result" != "$expected" ]; then - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label - label="$(bashunit::helper::normalize_test_function_name "$test_fn")" + bashunit::assert::label_to_slot + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "but got " "${result}" return @@ -74,10 +68,8 @@ function assert_json_equals() { actual_sorted=$(printf '%s' "$actual" | jq -S '.' 2>/dev/null) if [ "$expected_sorted" != "$actual_sorted" ]; then - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label - label="$(bashunit::helper::normalize_test_function_name "$test_fn")" + bashunit::assert::label_to_slot + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "but got " "${actual}" return diff --git a/src/bashunit.sh b/src/bashunit.sh index d7c56ef5..48f6a99b 100644 --- a/src/bashunit.sh +++ b/src/bashunit.sh @@ -11,10 +11,8 @@ function bashunit::assertion_failed() { local actual=$2 local failure_condition_message=${3:-"but got "} - local test_fn - test_fn="$(bashunit::helper::find_test_function_name)" - local label - label="$(bashunit::helper::normalize_test_function_name "$test_fn")" + bashunit::assert::label_to_slot + local label=$_BASHUNIT_ASSERT_LABEL_OUT bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" \ "$failure_condition_message" "${actual}" diff --git a/src/console_results.sh b/src/console_results.sh index 5ef55a30..e6d685a9 100644 --- a/src/console_results.sh +++ b/src/console_results.sh @@ -607,68 +607,53 @@ function bashunit::console_results::print_profile_and_reset() { rm -f "$PROFILE_OUTPUT_PATH" } -function bashunit::console_results::print_skipped_tests_and_reset() { - if [ -s "$SKIPPED_OUTPUT_PATH" ] && bashunit::env::is_show_skipped_enabled; then - local total_skipped - total_skipped=$(bashunit::state::get_tests_skipped) +## +# Flushes a deferred summary block (skipped/incomplete/risky): prints the +# "There was 1 " / "There were N " header, then the recorded lines +# from output_path (carriage returns stripped, blank lines dropped, each prefixed +# with "|"), removes the file and prints a trailing blank line. Callers own the +# `[ -s path ]` (and any `is_show_*`) guard so each block keeps its own gate. +# Arguments: $1 output path, $2 total count, $3 singular noun, $4 plural noun +## +function bashunit::console_results::flush_deferred_block() { + local output_path=$1 + local total=$2 + local singular=$3 + local plural=$4 - if bashunit::env::is_simple_output_enabled; then - printf "\n" - fi + if bashunit::env::is_simple_output_enabled; then + printf "\n" + fi - if [ "$total_skipped" -eq 1 ]; then - echo -e "${_BASHUNIT_COLOR_BOLD}There was 1 skipped test:${_BASHUNIT_COLOR_DEFAULT}\n" - else - echo -e "${_BASHUNIT_COLOR_BOLD}There were $total_skipped skipped tests:${_BASHUNIT_COLOR_DEFAULT}\n" - fi + if [ "$total" -eq 1 ]; then + echo -e "${_BASHUNIT_COLOR_BOLD}There was 1 ${singular}:${_BASHUNIT_COLOR_DEFAULT}\n" + else + echo -e "${_BASHUNIT_COLOR_BOLD}There were ${total} ${plural}:${_BASHUNIT_COLOR_DEFAULT}\n" + fi - tr -d '\r' <"$SKIPPED_OUTPUT_PATH" | sed '/^[[:space:]]*$/d' | sed 's/^/|/' - rm "$SKIPPED_OUTPUT_PATH" + tr -d '\r' <"$output_path" | sed '/^[[:space:]]*$/d' | sed 's/^/|/' + rm "$output_path" - echo "" + echo "" +} + +function bashunit::console_results::print_skipped_tests_and_reset() { + if [ -s "$SKIPPED_OUTPUT_PATH" ] && bashunit::env::is_show_skipped_enabled; then + bashunit::console_results::flush_deferred_block "$SKIPPED_OUTPUT_PATH" \ + "$(bashunit::state::get_tests_skipped)" "skipped test" "skipped tests" fi } function bashunit::console_results::print_incomplete_tests_and_reset() { if [ -s "$INCOMPLETE_OUTPUT_PATH" ] && bashunit::env::is_show_incomplete_enabled; then - local total_incomplete - total_incomplete=$(bashunit::state::get_tests_incomplete) - - if bashunit::env::is_simple_output_enabled; then - printf "\n" - fi - - if [ "$total_incomplete" -eq 1 ]; then - echo -e "${_BASHUNIT_COLOR_BOLD}There was 1 incomplete test:${_BASHUNIT_COLOR_DEFAULT}\n" - else - echo -e "${_BASHUNIT_COLOR_BOLD}There were $total_incomplete incomplete tests:${_BASHUNIT_COLOR_DEFAULT}\n" - fi - - tr -d '\r' <"$INCOMPLETE_OUTPUT_PATH" | sed '/^[[:space:]]*$/d' | sed 's/^/|/' - rm "$INCOMPLETE_OUTPUT_PATH" - - echo "" + bashunit::console_results::flush_deferred_block "$INCOMPLETE_OUTPUT_PATH" \ + "$(bashunit::state::get_tests_incomplete)" "incomplete test" "incomplete tests" fi } function bashunit::console_results::print_risky_tests_and_reset() { if [ -s "$RISKY_OUTPUT_PATH" ]; then - local total_risky - total_risky=$(bashunit::state::get_tests_risky) - - if bashunit::env::is_simple_output_enabled; then - printf "\n" - fi - - if [ "$total_risky" -eq 1 ]; then - echo -e "${_BASHUNIT_COLOR_BOLD}There was 1 risky test:${_BASHUNIT_COLOR_DEFAULT}\n" - else - echo -e "${_BASHUNIT_COLOR_BOLD}There were $total_risky risky tests:${_BASHUNIT_COLOR_DEFAULT}\n" - fi - - tr -d '\r' <"$RISKY_OUTPUT_PATH" | sed '/^[[:space:]]*$/d' | sed 's/^/|/' - rm "$RISKY_OUTPUT_PATH" - - echo "" + bashunit::console_results::flush_deferred_block "$RISKY_OUTPUT_PATH" \ + "$(bashunit::state::get_tests_risky)" "risky test" "risky tests" fi } diff --git a/src/helpers.sh b/src/helpers.sh index deb9c310..555a5463 100755 --- a/src/helpers.sh +++ b/src/helpers.sh @@ -176,7 +176,7 @@ function bashunit::helper::encode_base64() { # Handle empty string specially - base64 of "" is "", which gets lost in line parsing if [ -z "$value" ]; then - printf '%s' "_BASHUNIT_EMPTY_" + printf '%s' "$_BASHUNIT_BASE64_EMPTY_SENTINEL" return fi @@ -193,7 +193,7 @@ function bashunit::helper::decode_base64() { local value="$1" # Empty input decodes to empty; short-circuit to skip the base64 fork (#762). - if [ -z "$value" ] || [ "$value" = "_BASHUNIT_EMPTY_" ]; then + if [ -z "$value" ] || [ "$value" = "$_BASHUNIT_BASE64_EMPTY_SENTINEL" ]; then printf '' return fi @@ -569,8 +569,6 @@ function bashunit::helper::find_total_tests() { local -a functions_to_run=() # shellcheck disable=SC2206 functions_to_run=($filtered_functions) - # shellcheck disable=SC2034 - local -a provider_data=() local provider_data_count=0 local fn_name line # Scan once; functions without a provider count as 1 with no fork (#763). diff --git a/src/runner.sh b/src/runner.sh index b07ff49d..bc3864e8 100755 --- a/src/runner.sh +++ b/src/runner.sh @@ -1482,7 +1482,7 @@ function bashunit::runner::decode_subshell_output() { local test_output_base64="${test_execution_result##*##TEST_OUTPUT=}" test_output_base64="${test_output_base64%%##*}" - if [ -z "$test_output_base64" ] || [ "$test_output_base64" = "_BASHUNIT_EMPTY_" ]; then + if [ -z "$test_output_base64" ] || [ "$test_output_base64" = "$_BASHUNIT_BASE64_EMPTY_SENTINEL" ]; then _BASHUNIT_RUNNER_SUBSHELL_OUTPUT_OUT="" return fi diff --git a/src/state.sh b/src/state.sh index 67b8a19a..c36b74ff 100644 --- a/src/state.sh +++ b/src/state.sh @@ -10,6 +10,13 @@ case "$_bashunit_base64_help" in esac unset _bashunit_base64_help +# Wire sentinel for an empty base64 payload. base64 of "" is "", which gets lost +# in line parsing, so encode_base64 emits this token and both decode sites map it +# back to "". Single source of truth keeps the encode (helpers.sh) and decode +# (helpers.sh, runner.sh) sides byte-identical. +# shellcheck disable=SC2034 # read cross-file in helpers.sh and runner.sh +_BASHUNIT_BASE64_EMPTY_SENTINEL="_BASHUNIT_EMPTY_" + _BASHUNIT_TESTS_PASSED=0 _BASHUNIT_TESTS_FAILED=0 _BASHUNIT_TESTS_SKIPPED=0