diff --git a/src/assert.sh b/src/assert.sh index 696d48a5..f33b0e40 100755 --- a/src/assert.sh +++ b/src/assert.sh @@ -45,7 +45,7 @@ function assert_true() { bashunit::run_command_or_eval "$actual" local exit_code=$? - if [[ $exit_code -ne 0 ]]; then + if [ "$exit_code" -ne 0 ]; then bashunit::handle_bool_assertion_failure "command or function with zero exit code" "exit code: $exit_code" else bashunit::state::add_assertions_passed @@ -73,7 +73,7 @@ function assert_false() { bashunit::run_command_or_eval "$actual" local exit_code=$? - if [[ $exit_code -eq 0 ]]; then + if [ "$exit_code" -eq 0 ]; then bashunit::handle_bool_assertion_failure "command or function with non-zero exit code" "exit code: $exit_code" else bashunit::state::add_assertions_passed @@ -83,17 +83,18 @@ function assert_false() { function bashunit::run_command_or_eval() { local cmd="$1" - local _re='^eval' - if [[ "$cmd" =~ $_re ]]; then + case "$cmd" in + eval\ * | eval) eval "${cmd#eval }" &>/dev/null - else - _re='^alias' - if [[ "$(command -v "$cmd")" =~ $_re ]]; then + ;; + *) + if [ "$(command -v "$cmd" | "$GREP" -cE '^alias' || true)" -gt 0 ]; then eval "$cmd" &>/dev/null else "$cmd" &>/dev/null fi - fi + ;; + esac return $? } @@ -115,7 +116,7 @@ function assert_same() { local expected="$1" local actual="$2" - if [[ "$expected" != "$actual" ]]; then + if [ "$expected" != "$actual" ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -139,7 +140,7 @@ function assert_equals() { local expected_cleaned expected_cleaned=$(bashunit::str::strip_ansi "$expected") - if [[ "$expected_cleaned" != "$actual_cleaned" ]]; then + if [ "$expected_cleaned" != "$actual_cleaned" ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -163,7 +164,7 @@ function assert_not_equals() { local expected_cleaned expected_cleaned=$(bashunit::str::strip_ansi "$expected") - if [[ "$expected_cleaned" == "$actual_cleaned" ]]; then + if [ "$expected_cleaned" = "$actual_cleaned" ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -181,7 +182,7 @@ function assert_empty() { local expected="$1" - if [[ "$expected" != "" ]]; then + if [ "$expected" != "" ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -199,7 +200,7 @@ function assert_not_empty() { local expected="$1" - if [[ "$expected" == "" ]]; then + if [ "$expected" = "" ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -218,7 +219,7 @@ function assert_not_same() { local expected="$1" local actual="$2" - if [[ "$expected" == "$actual" ]]; then + if [ "$expected" = "$actual" ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -241,7 +242,9 @@ function assert_contains() { local actual actual=$(printf '%s\n' "${actual_arr[@]}") - if ! [[ $actual == *"$expected"* ]]; then + case "$actual" in + *"$expected"*) ;; + *) local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -249,7 +252,8 @@ function assert_contains() { bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${actual}" "to contain" "${expected}" return - fi + ;; + esac bashunit::state::add_assertions_passed } @@ -267,7 +271,9 @@ function assert_contains_ignore_case() { expected_lower=$(printf '%s' "$expected" | tr '[:upper:]' '[:lower:]') actual_lower=$(printf '%s' "$actual" | tr '[:upper:]' '[:lower:]') - if [[ "$actual_lower" != *"$expected_lower"* ]]; then + case "$actual_lower" in + *"$expected_lower"*) ;; + *) local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -275,7 +281,8 @@ function assert_contains_ignore_case() { bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${actual}" "to contain" "${expected}" return - fi + ;; + esac bashunit::state::add_assertions_passed } @@ -290,7 +297,8 @@ function assert_not_contains() { local actual actual=$(printf '%s\n' "${actual_arr[@]}") - if [[ $actual == *"$expected"* ]]; then + case "$actual" in + *"$expected"*) local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -298,7 +306,8 @@ function assert_not_contains() { bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${actual}" "to not contain" "${expected}" return - fi + ;; + esac bashunit::state::add_assertions_passed } @@ -313,14 +322,17 @@ function assert_matches() { local actual actual=$(printf '%s\n' "${actual_arr[@]}") - if ! [[ "$actual" =~ $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::mark_failed - bashunit::console_results::print_failed_test "${label}" "${actual}" "to match" "${expected}" - return + if [ "$(printf '%s' "$actual" | "$GREP" -cE "$expected" || true)" -eq 0 ]; then + # Retry with newlines collapsed for cross-line patterns + if [ "$(printf '%s' "$actual" | tr '\n' ' ' | "$GREP" -cE "$expected" || true)" -eq 0 ]; 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::mark_failed + bashunit::console_results::print_failed_test "${label}" "${actual}" "to match" "${expected}" + return + fi fi bashunit::state::add_assertions_passed @@ -336,7 +348,9 @@ function assert_not_matches() { local actual actual=$(printf '%s\n' "${actual_arr[@]}") - if [[ "$actual" =~ $expected ]]; then + # Check both line-by-line and with newlines collapsed for cross-line patterns + if [ "$(printf '%s' "$actual" | "$GREP" -cE "$expected" || true)" -gt 0 ] || + [ "$(printf '%s' "$actual" | tr '\n' ' ' | "$GREP" -cE "$expected" || true)" -gt 0 ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -361,7 +375,7 @@ function assert_exec() { local check_stdout=false local check_stderr=false - while [[ $# -gt 0 ]]; do + while [ $# -gt 0 ]; do case "$1" in --exit) expected_exit="$2" @@ -401,14 +415,14 @@ function assert_exec() { local actual_desc="exit: $exit_code" local failed=0 - if [[ "$exit_code" -ne "$expected_exit" ]]; then + if [ "$exit_code" -ne "$expected_exit" ]; then failed=1 fi if $check_stdout; then expected_desc="$expected_desc"$'\n'"stdout: $expected_stdout" actual_desc="$actual_desc"$'\n'"stdout: $stdout" - if [[ "$stdout" != "$expected_stdout" ]]; then + if [ "$stdout" != "$expected_stdout" ]; then failed=1 fi fi @@ -416,12 +430,12 @@ function assert_exec() { if $check_stderr; then expected_desc="$expected_desc"$'\n'"stderr: $expected_stderr" actual_desc="$actual_desc"$'\n'"stderr: $stderr" - if [[ "$stderr" != "$expected_stderr" ]]; then + if [ "$stderr" != "$expected_stderr" ]; then failed=1 fi fi - if [[ $failed -eq 1 ]]; then + if [ "$failed" -eq 1 ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -440,7 +454,7 @@ function assert_exit_code() { local expected_exit_code="$1" - if [[ "$actual_exit_code" -ne "$expected_exit_code" ]]; then + if [ "$actual_exit_code" -ne "$expected_exit_code" ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -459,7 +473,7 @@ function assert_successful_code() { local expected_exit_code=0 - if [[ "$actual_exit_code" -ne "$expected_exit_code" ]]; then + if [ "$actual_exit_code" -ne "$expected_exit_code" ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -477,7 +491,7 @@ function assert_unsuccessful_code() { local actual_exit_code=${3-"$?"} # Capture $? before guard check bashunit::assert::should_skip && return 0 - if [[ "$actual_exit_code" -eq 0 ]]; then + if [ "$actual_exit_code" -eq 0 ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -496,7 +510,7 @@ function assert_general_error() { local expected_exit_code=1 - if [[ "$actual_exit_code" -ne "$expected_exit_code" ]]; then + if [ "$actual_exit_code" -ne "$expected_exit_code" ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -516,7 +530,7 @@ function assert_command_not_found() { local expected_exit_code=127 - if [[ $actual_exit_code -ne "$expected_exit_code" ]]; then + if [ "$actual_exit_code" -ne "$expected_exit_code" ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -540,7 +554,9 @@ function assert_string_starts_with() { local actual actual=$(printf '%s\n' "${actual_arr[@]}") - if [[ $actual != "$expected"* ]]; then + case "$actual" in + "$expected"*) ;; + *) local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -548,7 +564,8 @@ function assert_string_starts_with() { bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${actual}" "to start with" "${expected}" return - fi + ;; + esac bashunit::state::add_assertions_passed } @@ -559,7 +576,8 @@ function assert_string_not_starts_with() { local expected="$1" local actual="$2" - if [[ $actual == "$expected"* ]]; then + case "$actual" in + "$expected"*) local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -567,7 +585,8 @@ function assert_string_not_starts_with() { bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${actual}" "to not start with" "${expected}" return - fi + ;; + esac bashunit::state::add_assertions_passed } @@ -582,7 +601,9 @@ function assert_string_ends_with() { local actual actual=$(printf '%s\n' "${actual_arr[@]}") - if [[ $actual != *"$expected" ]]; then + case "$actual" in + *"$expected") ;; + *) local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -590,7 +611,8 @@ function assert_string_ends_with() { bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${actual}" "to end with" "${expected}" return - fi + ;; + esac bashunit::state::add_assertions_passed } @@ -605,7 +627,8 @@ function assert_string_not_ends_with() { local actual actual=$(printf '%s\n' "${actual_arr[@]}") - if [[ $actual == *"$expected" ]]; then + case "$actual" in + *"$expected") local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -613,7 +636,8 @@ function assert_string_not_ends_with() { bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${actual}" "to not end with" "${expected}" return - fi + ;; + esac bashunit::state::add_assertions_passed } @@ -624,7 +648,7 @@ function assert_less_than() { local expected="$1" local actual="$2" - if ! [[ "$actual" -lt "$expected" ]]; then + if ! [ "$actual" -lt "$expected" ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -643,7 +667,7 @@ function assert_less_or_equal_than() { local expected="$1" local actual="$2" - if ! [[ "$actual" -le "$expected" ]]; then + if ! [ "$actual" -le "$expected" ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -662,7 +686,7 @@ function assert_greater_than() { local expected="$1" local actual="$2" - if ! [[ "$actual" -gt "$expected" ]]; then + if ! [ "$actual" -gt "$expected" ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -681,7 +705,7 @@ function assert_greater_or_equal_than() { local expected="$1" local actual="$2" - if ! [[ "$actual" -ge "$expected" ]]; then + if ! [ "$actual" -ge "$expected" ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -714,7 +738,7 @@ function assert_line_count() { actual=$((actual + additional_new_lines)) fi - if [[ "$expected" != "$actual" ]]; then + if [ "$expected" != "$actual" ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -781,7 +805,7 @@ function assert_string_matches_format() { local regex regex="$(bashunit::format_to_regex "$format")" - if ! [[ "$actual" =~ $regex ]]; then + if [ "$(printf '%s' "$actual" | "$GREP" -cE "$regex" || true)" -eq 0 ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -803,7 +827,7 @@ function assert_string_not_matches_format() { local regex regex="$(bashunit::format_to_regex "$format")" - if [[ "$actual" =~ $regex ]]; then + if [ "$(printf '%s' "$actual" | "$GREP" -cE "$regex" || true)" -gt 0 ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label diff --git a/src/assert_arrays.sh b/src/assert_arrays.sh index 1d116997..f18f6278 100644 --- a/src/assert_arrays.sh +++ b/src/assert_arrays.sh @@ -13,11 +13,15 @@ function assert_array_contains() { local -a actual actual=("$@") - if ! [[ "${actual[*]:-}" == *"$expected"* ]]; then + case "${actual[*]:-}" in + *"$expected"*) + ;; + *) bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${actual[*]}" "to contain" "${expected}" return - fi + ;; + esac bashunit::state::add_assertions_passed } @@ -34,11 +38,13 @@ function assert_array_not_contains() { local -a actual actual=("$@") - if [[ "${actual[*]:-}" == *"$expected"* ]]; then + case "${actual[*]:-}" in + *"$expected"*) bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${actual[*]}" "to not contain" "${expected}" return - fi + ;; + esac bashunit::state::add_assertions_passed } diff --git a/src/assert_dates.sh b/src/assert_dates.sh index 3b709ab4..fa33486a 100644 --- a/src/assert_dates.sh +++ b/src/assert_dates.sh @@ -31,7 +31,7 @@ function bashunit::date::to_epoch() { return 0 } # Try GNU date with normalized (space-separated) input - if [[ "$normalized" != "$input" ]]; then + if [ "$normalized" != "$input" ]; then epoch=$(date -d "$normalized" +%s 2>/dev/null) && { echo "$epoch" return 0 @@ -71,7 +71,7 @@ function assert_date_equals() { local actual actual="$(bashunit::date::to_epoch "$2")" - if [[ "$actual" -ne "$expected" ]]; then + if [ "$actual" -ne "$expected" ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -92,7 +92,7 @@ function assert_date_before() { local actual actual="$(bashunit::date::to_epoch "$2")" - if ! [[ "$actual" -lt "$expected" ]]; then + if [ "$actual" -ge "$expected" ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -113,7 +113,7 @@ function assert_date_after() { local actual actual="$(bashunit::date::to_epoch "$2")" - if ! [[ "$actual" -gt "$expected" ]]; then + if [ "$actual" -le "$expected" ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -136,7 +136,7 @@ function assert_date_within_range() { local actual actual="$(bashunit::date::to_epoch "$3")" - if [[ "$actual" -lt "$from" ]] || [[ "$actual" -gt "$to" ]]; then + if [ "$actual" -lt "$from" ] || [ "$actual" -gt "$to" ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -159,11 +159,11 @@ function assert_date_within_delta() { local delta="$3" local diff=$((actual - expected)) - if [[ "$diff" -lt 0 ]]; then + if [ "$diff" -lt 0 ]; then diff=$((-diff)) fi - if [[ "$diff" -gt "$delta" ]]; then + if [ "$diff" -gt "$delta" ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label diff --git a/src/assert_files.sh b/src/assert_files.sh index db521315..27fab431 100644 --- a/src/assert_files.sh +++ b/src/assert_files.sh @@ -8,7 +8,7 @@ function assert_file_exists() { test_fn="$(bashunit::helper::find_test_function_name)" local label="${3:-$(bashunit::helper::normalize_test_function_name "$test_fn")}" - if [[ ! -f "$expected" ]]; then + if [ ! -f "$expected" ]; then bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to exist but" "do not exist" return @@ -25,7 +25,7 @@ function assert_file_not_exists() { test_fn="$(bashunit::helper::find_test_function_name)" local label="${3:-$(bashunit::helper::normalize_test_function_name "$test_fn")}" - if [[ -f "$expected" ]]; then + if [ -f "$expected" ]; then bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to not exist but" "the file exists" return @@ -42,7 +42,7 @@ function assert_is_file() { test_fn="$(bashunit::helper::find_test_function_name)" local label="${3:-$(bashunit::helper::normalize_test_function_name "$test_fn")}" - if [[ ! -f "$expected" ]]; then + if [ ! -f "$expected" ]; then bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to be a file" "but is not a file" return @@ -59,7 +59,7 @@ function assert_is_file_empty() { test_fn="$(bashunit::helper::find_test_function_name)" local label="${3:-$(bashunit::helper::normalize_test_function_name "$test_fn")}" - if [[ -s "$expected" ]]; then + if [ -s "$expected" ]; then bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to be empty" "but is not empty" return @@ -74,7 +74,7 @@ function assert_files_equals() { local expected="$1" local actual="$2" - if [[ "$(diff -u "$expected" "$actual")" != '' ]]; then + if [ "$(diff -u "$expected" "$actual")" != '' ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label @@ -95,7 +95,7 @@ function assert_files_not_equals() { local expected="$1" local actual="$2" - if [[ "$(diff -u "$expected" "$actual")" == '' ]]; then + if [ "$(diff -u "$expected" "$actual")" = '' ]; then local test_fn test_fn="$(bashunit::helper::find_test_function_name)" local label diff --git a/src/assert_folders.sh b/src/assert_folders.sh index 7ec45ccc..9faddacc 100644 --- a/src/assert_folders.sh +++ b/src/assert_folders.sh @@ -8,7 +8,7 @@ function assert_directory_exists() { test_fn="$(bashunit::helper::find_test_function_name)" local label="${2:-$(bashunit::helper::normalize_test_function_name "$test_fn")}" - if [[ ! -d "$expected" ]]; then + if [ ! -d "$expected" ]; then bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to exist but" "do not exist" return @@ -25,7 +25,7 @@ function assert_directory_not_exists() { test_fn="$(bashunit::helper::find_test_function_name)" local label="${2:-$(bashunit::helper::normalize_test_function_name "$test_fn")}" - if [[ -d "$expected" ]]; then + if [ -d "$expected" ]; then bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to not exist but" "the directory exists" return @@ -42,7 +42,7 @@ function assert_is_directory() { test_fn="$(bashunit::helper::find_test_function_name)" local label="${2:-$(bashunit::helper::normalize_test_function_name "$test_fn")}" - if [[ ! -d "$expected" ]]; then + if [ ! -d "$expected" ]; then bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to be a directory" "but is not a directory" return @@ -59,7 +59,7 @@ function assert_is_directory_empty() { 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 + if [ ! -d "$expected" ] || [ -n "$(ls -A "$expected")" ]; then bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to be empty" "but is not empty" return @@ -76,7 +76,7 @@ function assert_is_directory_not_empty() { 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 + if [ ! -d "$expected" ] || [ -z "$(ls -A "$expected")" ]; then bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to not be empty" "but is empty" return @@ -93,7 +93,7 @@ function assert_is_directory_readable() { 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 + if [ ! -d "$expected" ] || [ ! -r "$expected" ] || [ ! -x "$expected" ]; then bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to be readable" "but is not readable" return @@ -110,7 +110,7 @@ function assert_is_directory_not_readable() { 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 + if [ ! -d "$expected" ] || { [ -r "$expected" ] && [ -x "$expected" ]; }; then bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to be not readable" "but is readable" return @@ -127,7 +127,7 @@ function assert_is_directory_writable() { 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 + if [ ! -d "$expected" ] || [ ! -w "$expected" ]; then bashunit::assert::mark_failed bashunit::console_results::print_failed_test "${label}" "${expected}" "to be writable" "but is not writable" return @@ -144,7 +144,7 @@ function assert_is_directory_not_writable() { 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 + if [ ! -d "$expected" ] || [ -w "$expected" ]; then 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_snapshot.sh b/src/assert_snapshot.sh index a47d14be..e3b2bc22 100644 --- a/src/assert_snapshot.sh +++ b/src/assert_snapshot.sh @@ -7,7 +7,7 @@ function assert_match_snapshot() { test_fn="$(bashunit::helper::find_test_function_name)" local snapshot_file=$(bashunit::snapshot::resolve_file "${2:-}" "$test_fn") - if [[ ! -f "$snapshot_file" ]]; then + if [ ! -f "$snapshot_file" ]; then bashunit::snapshot::initialize "$snapshot_file" "$actual" return fi @@ -21,7 +21,7 @@ function assert_match_snapshot_ignore_colors() { test_fn="$(bashunit::helper::find_test_function_name)" local snapshot_file=$(bashunit::snapshot::resolve_file "${2:-}" "$test_fn") - if [[ ! -f "$snapshot_file" ]]; then + if [ ! -f "$snapshot_file" ]; then bashunit::snapshot::initialize "$snapshot_file" "$actual" return fi @@ -56,7 +56,7 @@ function bashunit::snapshot::resolve_file() { local file_hint="$1" local func_name="$2" - if [[ -n "$file_hint" ]]; then + if [ -n "$file_hint" ]; then echo "$file_hint" else local dir="./$(dirname "${BASH_SOURCE[2]}")/snapshots" diff --git a/src/benchmark.sh b/src/benchmark.sh index 92a509bd..fbc1a6fd 100644 --- a/src/benchmark.sh +++ b/src/benchmark.sh @@ -16,37 +16,33 @@ function bashunit::benchmark::parse_annotations() { local annotation annotation=$(awk "/function[[:space:]]+${fn_name}[[:space:]]*\(/ {print prev; exit} {prev=\$0}" "$script") - local _re='@revs=([0-9]+)' - if [[ "$annotation" =~ $_re ]]; then - revs="${BASH_REMATCH[1]}" + local _extracted + _extracted=$(echo "$annotation" | sed -n 's/.*@revs=\([0-9][0-9]*\).*/\1/p') + if [ -n "$_extracted" ]; then + revs="$_extracted" else - _re='@revolutions=([0-9]+)' - if [[ "$annotation" =~ $_re ]]; then - revs="${BASH_REMATCH[1]}" + _extracted=$(echo "$annotation" | sed -n 's/.*@revolutions=\([0-9][0-9]*\).*/\1/p') + if [ -n "$_extracted" ]; then + revs="$_extracted" fi fi - _re='@its=([0-9]+)' - if [[ "$annotation" =~ $_re ]]; then - its="${BASH_REMATCH[1]}" + _extracted=$(echo "$annotation" | sed -n 's/.*@its=\([0-9][0-9]*\).*/\1/p') + if [ -n "$_extracted" ]; then + its="$_extracted" else - _re='@iterations=([0-9]+)' - if [[ "$annotation" =~ $_re ]]; then - its="${BASH_REMATCH[1]}" + _extracted=$(echo "$annotation" | sed -n 's/.*@iterations=\([0-9][0-9]*\).*/\1/p') + if [ -n "$_extracted" ]; then + its="$_extracted" fi fi - _re='@max_ms=([0-9.]+)' - if [[ "$annotation" =~ $_re ]]; then - max_ms="${BASH_REMATCH[1]}" - else - _re='@max_ms=([0-9.]+)' - if [[ "$annotation" =~ $_re ]]; then - max_ms="${BASH_REMATCH[1]}" - fi + _extracted=$(echo "$annotation" | sed -n 's/.*@max_ms=\([0-9.][0-9.]*\).*/\1/p') + if [ -n "$_extracted" ]; then + max_ms="$_extracted" fi - if [[ -n "$max_ms" ]]; then + if [ -n "$max_ms" ]; then echo "$revs" "$its" "$max_ms" else echo "$revs" "$its" @@ -122,7 +118,7 @@ function bashunit::benchmark::print_results() { local has_threshold=false local val for val in "${_BASHUNIT_BENCH_MAX_MILLIS[@]+"${_BASHUNIT_BENCH_MAX_MILLIS[@]}"}"; do - if [[ -n "$val" ]]; then + if [ -n "$val" ]; then has_threshold=true break fi @@ -142,12 +138,12 @@ function bashunit::benchmark::print_results() { local avg="${_BASHUNIT_BENCH_AVERAGES[$i]:-}" local max_ms="${_BASHUNIT_BENCH_MAX_MILLIS[$i]:-}" - if [[ -z "$max_ms" ]]; then + if [ -z "$max_ms" ]; then printf '%-40s %6s %6s %10s\n' "$name" "$revs" "$its" "$avg" continue fi - if [[ "$avg" -le "$max_ms" ]]; then + if [ "$avg" -le "$max_ms" ]; then local raw="≤ ${max_ms}" local padded padded=$(printf "%14s" "$raw") diff --git a/src/check_os.sh b/src/check_os.sh index 27f81129..91298cfa 100644 --- a/src/check_os.sh +++ b/src/check_os.sh @@ -28,26 +28,26 @@ function bashunit::check_os::init() { } function bashunit::check_os::is_ubuntu() { - command -v apt >/dev/null + command -v apt >/dev/null 2>&1 } function bashunit::check_os::is_alpine() { - command -v apk >/dev/null + command -v apk >/dev/null 2>&1 } function bashunit::check_os::is_nixos() { - [[ -f /etc/NIXOS ]] && return 0 + [ -f /etc/NIXOS ] && return 0 grep -q '^ID=nixos' /etc/os-release 2>/dev/null } _BASHUNIT_UNAME="$(uname)" function bashunit::check_os::is_linux() { - [[ "$_BASHUNIT_UNAME" == "Linux" ]] + [ "$_BASHUNIT_UNAME" = "Linux" ] } function bashunit::check_os::is_macos() { - [[ "$_BASHUNIT_UNAME" == "Darwin" ]] + [ "$_BASHUNIT_UNAME" = "Darwin" ] } function bashunit::check_os::is_windows() { diff --git a/src/clock.sh b/src/clock.sh index 8033128a..3a3b9281 100644 --- a/src/clock.sh +++ b/src/clock.sh @@ -22,8 +22,8 @@ function bashunit::clock::_choose_impl() { if ! bashunit::check_os::is_macos && ! bashunit::check_os::is_alpine; then local result result=$(date +%s%N 2>/dev/null) - local _re='^[0-9]+$' - if [[ "$result" != *N ]] && [[ "$result" =~ $_re ]]; then + if [ "$(echo "$result" | "$GREP" -cv 'N' || true)" -gt 0 ] \ + && [ "$(echo "$result" | "$GREP" -cE '^[0-9]+$' || true)" -gt 0 ]; then _BASHUNIT_CLOCK_NOW_IMPL="date" return 0 fi @@ -76,7 +76,7 @@ function bashunit::clock::_choose_impl() { } function bashunit::clock::now() { - if [[ -z "$_BASHUNIT_CLOCK_NOW_IMPL" ]]; then + if [ -z "$_BASHUNIT_CLOCK_NOW_IMPL" ]; then bashunit::clock::_choose_impl || return 1 fi @@ -131,13 +131,13 @@ EOF function bashunit::clock::shell_time() { # Get time directly from the shell variable EPOCHREALTIME (Bash 5+) - [[ -n ${EPOCHREALTIME+x} && -n "$EPOCHREALTIME" ]] && LC_ALL=C echo "$EPOCHREALTIME" + [ -n "${EPOCHREALTIME+x}" ] && [ -n "$EPOCHREALTIME" ] && LC_ALL=C echo "$EPOCHREALTIME" } function bashunit::clock::total_runtime_in_milliseconds() { local end_time end_time=$(bashunit::clock::now) - if [[ -n $end_time ]]; then + if [ -n "$end_time" ]; then bashunit::math::calculate "($end_time - $_BASHUNIT_START_TIME) / 1000000" else echo "" @@ -147,7 +147,7 @@ function bashunit::clock::total_runtime_in_milliseconds() { function bashunit::clock::total_runtime_in_nanoseconds() { local end_time end_time=$(bashunit::clock::now) - if [[ -n $end_time ]]; then + if [ -n "$end_time" ]; then bashunit::math::calculate "$end_time - $_BASHUNIT_START_TIME" else echo "" diff --git a/src/console_header.sh b/src/console_header.sh index 1e008d5f..4efbb71c 100644 --- a/src/console_header.sh +++ b/src/console_header.sh @@ -23,7 +23,7 @@ function bashunit::console_header::print_version() { # Bash 3.0 compatible: check argument count after shift local files_count=$# local total_tests - if [[ "$files_count" -eq 0 ]]; then + if [ "$files_count" -eq 0 ]; then total_tests=0 elif bashunit::parallel::is_enabled && bashunit::env::is_simple_output_enabled; then # Skip counting in parallel+simple mode for faster startup diff --git a/src/console_results.sh b/src/console_results.sh index 665cbe22..20d1291c 100644 --- a/src/console_results.sh +++ b/src/console_results.sh @@ -4,7 +4,7 @@ _BASHUNIT_TOTAL_TESTS_COUNT=0 function bashunit::console_results::render_result() { - if [[ "$(bashunit::state::is_duplicated_test_functions_found)" == true ]]; then + if [ "$(bashunit::state::is_duplicated_test_functions_found)" = true ]; then bashunit::console_results::print_execution_time printf "%s%s%s\n" "${_BASHUNIT_COLOR_RETURN_ERROR}" "Duplicate test functions found" "${_BASHUNIT_COLOR_DEFAULT}" printf "File with duplicate functions: %s\n" "$(bashunit::state::get_file_with_duplicated_function_names)" @@ -14,7 +14,7 @@ function bashunit::console_results::render_result() { if bashunit::env::is_tap_output_enabled; then printf "1..%d\n" "$_BASHUNIT_TOTAL_TESTS_COUNT" - if [[ $_BASHUNIT_TESTS_FAILED -gt 0 ]]; then + if [ "$_BASHUNIT_TESTS_FAILED" -gt 0 ]; then return 1 fi return 0 @@ -53,75 +53,75 @@ function bashunit::console_results::render_result() { total_assertions=$((total_assertions + assertions_failed)) printf "%sTests: %s" "$_BASHUNIT_COLOR_FAINT" "$_BASHUNIT_COLOR_DEFAULT" - if [[ "$tests_passed" -gt 0 ]] || [[ "$assertions_passed" -gt 0 ]]; then + if [ "$tests_passed" -gt 0 ] || [ "$assertions_passed" -gt 0 ]; then printf " %s%s passed%s," "$_BASHUNIT_COLOR_PASSED" "$tests_passed" "$_BASHUNIT_COLOR_DEFAULT" fi - if [[ "$tests_skipped" -gt 0 ]] || [[ "$assertions_skipped" -gt 0 ]]; then + if [ "$tests_skipped" -gt 0 ] || [ "$assertions_skipped" -gt 0 ]; then printf " %s%s skipped%s," "$_BASHUNIT_COLOR_SKIPPED" "$tests_skipped" "$_BASHUNIT_COLOR_DEFAULT" fi - if [[ "$tests_incomplete" -gt 0 ]] || [[ "$assertions_incomplete" -gt 0 ]]; then + if [ "$tests_incomplete" -gt 0 ] || [ "$assertions_incomplete" -gt 0 ]; then printf " %s%s incomplete%s," "$_BASHUNIT_COLOR_INCOMPLETE" "$tests_incomplete" "$_BASHUNIT_COLOR_DEFAULT" fi - if [[ "$tests_snapshot" -gt 0 ]] || [[ "$assertions_snapshot" -gt 0 ]]; then + if [ "$tests_snapshot" -gt 0 ] || [ "$assertions_snapshot" -gt 0 ]; then printf " %s%s snapshot%s," "$_BASHUNIT_COLOR_SNAPSHOT" "$tests_snapshot" "$_BASHUNIT_COLOR_DEFAULT" fi - if [[ "$tests_failed" -gt 0 ]] || [[ "$assertions_failed" -gt 0 ]]; then + if [ "$tests_failed" -gt 0 ] || [ "$assertions_failed" -gt 0 ]; then printf " %s%s failed%s," "$_BASHUNIT_COLOR_FAILED" "$tests_failed" "$_BASHUNIT_COLOR_DEFAULT" fi - if [[ "$tests_risky" -gt 0 ]]; then + if [ "$tests_risky" -gt 0 ]; then printf " %s%s risky%s," "$_BASHUNIT_COLOR_RISKY" "$tests_risky" "$_BASHUNIT_COLOR_DEFAULT" fi printf " %s total\n" "$total_tests" printf "%sAssertions:%s" "$_BASHUNIT_COLOR_FAINT" "$_BASHUNIT_COLOR_DEFAULT" - if [[ "$tests_passed" -gt 0 ]] || [[ "$assertions_passed" -gt 0 ]]; then + if [ "$tests_passed" -gt 0 ] || [ "$assertions_passed" -gt 0 ]; then printf " %s%s passed%s," "$_BASHUNIT_COLOR_PASSED" "$assertions_passed" "$_BASHUNIT_COLOR_DEFAULT" fi - if [[ "$tests_skipped" -gt 0 ]] || [[ "$assertions_skipped" -gt 0 ]]; then + if [ "$tests_skipped" -gt 0 ] || [ "$assertions_skipped" -gt 0 ]; then printf " %s%s skipped%s," "$_BASHUNIT_COLOR_SKIPPED" "$assertions_skipped" "$_BASHUNIT_COLOR_DEFAULT" fi - if [[ "$tests_incomplete" -gt 0 ]] || [[ "$assertions_incomplete" -gt 0 ]]; then + if [ "$tests_incomplete" -gt 0 ] || [ "$assertions_incomplete" -gt 0 ]; then printf " %s%s incomplete%s," "$_BASHUNIT_COLOR_INCOMPLETE" "$assertions_incomplete" "$_BASHUNIT_COLOR_DEFAULT" fi - if [[ "$tests_snapshot" -gt 0 ]] || [[ "$assertions_snapshot" -gt 0 ]]; then + if [ "$tests_snapshot" -gt 0 ] || [ "$assertions_snapshot" -gt 0 ]; then printf " %s%s snapshot%s," "$_BASHUNIT_COLOR_SNAPSHOT" "$assertions_snapshot" "$_BASHUNIT_COLOR_DEFAULT" fi - if [[ "$tests_failed" -gt 0 ]] || [[ "$assertions_failed" -gt 0 ]]; then + if [ "$tests_failed" -gt 0 ] || [ "$assertions_failed" -gt 0 ]; then printf " %s%s failed%s," "$_BASHUNIT_COLOR_FAILED" "$assertions_failed" "$_BASHUNIT_COLOR_DEFAULT" fi printf " %s total\n" "$total_assertions" - if [[ "$tests_failed" -gt 0 ]]; then + if [ "$tests_failed" -gt 0 ]; then printf "\n%s%s%s\n" "$_BASHUNIT_COLOR_RETURN_ERROR" " Some tests failed " "$_BASHUNIT_COLOR_DEFAULT" bashunit::console_results::print_execution_time return 1 fi - if [[ "$tests_risky" -gt 0 ]]; then + if [ "$tests_risky" -gt 0 ]; then printf "\n%s%s%s\n" "$_BASHUNIT_COLOR_RETURN_RISKY" " Some tests risky (no assertions) " "$_BASHUNIT_COLOR_DEFAULT" bashunit::console_results::print_execution_time return 0 fi - if [[ "$tests_incomplete" -gt 0 ]]; then + if [ "$tests_incomplete" -gt 0 ]; then printf "\n%s%s%s\n" "$_BASHUNIT_COLOR_RETURN_INCOMPLETE" " Some tests incomplete " "$_BASHUNIT_COLOR_DEFAULT" bashunit::console_results::print_execution_time return 0 fi - if [[ "$tests_skipped" -gt 0 ]]; then + if [ "$tests_skipped" -gt 0 ]; then printf "\n%s%s%s\n" "$_BASHUNIT_COLOR_RETURN_SKIPPED" " Some tests skipped " "$_BASHUNIT_COLOR_DEFAULT" bashunit::console_results::print_execution_time return 0 fi - if [[ "$tests_snapshot" -gt 0 ]]; then + if [ "$tests_snapshot" -gt 0 ]; then printf "\n%s%s%s\n" "$_BASHUNIT_COLOR_RETURN_SNAPSHOT" " Some snapshots created " "$_BASHUNIT_COLOR_DEFAULT" bashunit::console_results::print_execution_time return 0 fi - if [[ $total_tests -eq 0 ]]; then + if [ "$total_tests" -eq 0 ]; then printf "\n%s%s%s\n" "$_BASHUNIT_COLOR_RETURN_ERROR" " No tests found " "$_BASHUNIT_COLOR_DEFAULT" bashunit::console_results::print_execution_time return 1 @@ -143,7 +143,7 @@ function bashunit::console_results::print_execution_time() { time="${time%%.*}" time="${time:-0}" - if [[ "$time" -lt 1000 ]]; then + if [ "$time" -lt 1000 ]; then printf "${_BASHUNIT_COLOR_BOLD}%s${_BASHUNIT_COLOR_DEFAULT}\n" \ "Time taken: ${time}ms" return @@ -151,7 +151,7 @@ function bashunit::console_results::print_execution_time() { local time_in_seconds=$((time / 1000)) - if [[ "$time_in_seconds" -ge 60 ]]; then + if [ "$time_in_seconds" -ge 60 ]; then local minutes=$((time_in_seconds / 60)) local seconds=$((time_in_seconds % 60)) printf "${_BASHUNIT_COLOR_BOLD}%s${_BASHUNIT_COLOR_DEFAULT}\n" \ @@ -171,12 +171,12 @@ function bashunit::console_results::print_execution_time() { function bashunit::console_results::format_duration() { local duration_ms="$1" - if [[ "$duration_ms" -ge 60000 ]]; then + if [ "$duration_ms" -ge 60000 ]; then local time_in_seconds=$((duration_ms / 1000)) local minutes=$((time_in_seconds / 60)) local seconds=$((time_in_seconds % 60)) echo "${minutes}m ${seconds}s" - elif [[ "$duration_ms" -ge 1000 ]]; then + elif [ "$duration_ms" -ge 1000 ]; then local integer_part=$((duration_ms / 1000)) local decimal_part=$(( (duration_ms % 1000) / 10 )) local formatted_seconds @@ -228,13 +228,13 @@ function bashunit::console_results::print_successful_test() { shift local line - if [[ -z "$*" ]]; then + if [ -z "$*" ]; then line=$(printf "%s✓ Passed%s: %s" "$_BASHUNIT_COLOR_PASSED" "$_BASHUNIT_COLOR_DEFAULT" "$test_name") else local quoted_args="" local arg for arg in "$@"; do - if [[ -z "$quoted_args" ]]; then + if [ -z "$quoted_args" ]; then quoted_args="'$arg'" else quoted_args="$quoted_args, '$arg'" @@ -247,12 +247,12 @@ function bashunit::console_results::print_successful_test() { local full_line=$line if bashunit::env::is_show_execution_time_enabled; then local time_display - if [[ "$duration" -ge 60000 ]]; then + if [ "$duration" -ge 60000 ]; then local time_in_seconds=$((duration / 1000)) local minutes=$((time_in_seconds / 60)) local seconds=$((time_in_seconds % 60)) time_display="${minutes}m ${seconds}s" - elif [[ "$duration" -ge 1000 ]]; then + elif [ "$duration" -ge 1000 ]; then local integer_part=$((duration / 1000)) local decimal_part=$(( (duration % 1000) / 10 )) local formatted_seconds @@ -338,7 +338,7 @@ function bashunit::console_results::print_skipped_test() { local line line="$(printf "${_BASHUNIT_COLOR_SKIPPED}↷ Skipped${_BASHUNIT_COLOR_DEFAULT}: %s\n" "${function_name}")" - if [[ -n "$reason" ]]; then + if [ -n "$reason" ]; then line="$line$(printf "${_BASHUNIT_COLOR_FAINT} %s${_BASHUNIT_COLOR_DEFAULT}\n" "${reason}")" fi @@ -352,7 +352,7 @@ function bashunit::console_results::print_incomplete_test() { local line line="$(printf "${_BASHUNIT_COLOR_INCOMPLETE}✒ Incomplete${_BASHUNIT_COLOR_DEFAULT}: %s\n" "${function_name}")" - if [[ -n "$pending" ]]; then + if [ -n "$pending" ]; then line="$line$(printf "${_BASHUNIT_COLOR_FAINT} %s${_BASHUNIT_COLOR_DEFAULT}\n" "${pending}")" fi @@ -399,7 +399,7 @@ function bashunit::console_results::print_error_test() { line="$(printf "${_BASHUNIT_COLOR_FAILED}✗ Error${_BASHUNIT_COLOR_DEFAULT}: %s ${_BASHUNIT_COLOR_FAINT}%s${_BASHUNIT_COLOR_DEFAULT}\n" "${test_name}" "${error}")" - if [[ -n "$raw_output" ]] && bashunit::env::is_show_output_on_failure_enabled; then + if [ -n "$raw_output" ] && bashunit::env::is_show_output_on_failure_enabled; then line="$line$(printf " %sOutput:%s\n" "${_BASHUNIT_COLOR_FAINT}" "${_BASHUNIT_COLOR_DEFAULT}")" local output_line while IFS= read -r output_line; do @@ -411,7 +411,7 @@ function bashunit::console_results::print_error_test() { } function bashunit::console_results::print_failing_tests_and_reset() { - if [[ -s "$FAILURES_OUTPUT_PATH" ]]; then + if [ -s "$FAILURES_OUTPUT_PATH" ]; then local total_failed total_failed=$(bashunit::state::get_tests_failed) @@ -419,7 +419,7 @@ function bashunit::console_results::print_failing_tests_and_reset() { printf "\n\n" fi - if [[ "$total_failed" -eq 1 ]]; then + if [ "$total_failed" -eq 1 ]; then echo -e "${_BASHUNIT_COLOR_BOLD}There was 1 failure:${_BASHUNIT_COLOR_DEFAULT}\n" else echo -e "${_BASHUNIT_COLOR_BOLD}There were $total_failed failures:${_BASHUNIT_COLOR_DEFAULT}\n" @@ -433,7 +433,7 @@ function bashunit::console_results::print_failing_tests_and_reset() { } function bashunit::console_results::print_skipped_tests_and_reset() { - if [[ -s "$SKIPPED_OUTPUT_PATH" ]] && bashunit::env::is_show_skipped_enabled; then + if [ -s "$SKIPPED_OUTPUT_PATH" ] && bashunit::env::is_show_skipped_enabled; then local total_skipped total_skipped=$(bashunit::state::get_tests_skipped) @@ -441,7 +441,7 @@ function bashunit::console_results::print_skipped_tests_and_reset() { printf "\n" fi - if [[ "$total_skipped" -eq 1 ]]; then + 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" @@ -455,7 +455,7 @@ function bashunit::console_results::print_skipped_tests_and_reset() { } function bashunit::console_results::print_incomplete_tests_and_reset() { - if [[ -s "$INCOMPLETE_OUTPUT_PATH" ]] && bashunit::env::is_show_incomplete_enabled; then + if [ -s "$INCOMPLETE_OUTPUT_PATH" ] && bashunit::env::is_show_incomplete_enabled; then local total_incomplete total_incomplete=$(bashunit::state::get_tests_incomplete) @@ -463,7 +463,7 @@ function bashunit::console_results::print_incomplete_tests_and_reset() { printf "\n" fi - if [[ "$total_incomplete" -eq 1 ]]; then + 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" @@ -477,7 +477,7 @@ function bashunit::console_results::print_incomplete_tests_and_reset() { } function bashunit::console_results::print_risky_tests_and_reset() { - if [[ -s "$RISKY_OUTPUT_PATH" ]]; then + if [ -s "$RISKY_OUTPUT_PATH" ]; then local total_risky total_risky=$(bashunit::state::get_tests_risky) @@ -485,7 +485,7 @@ function bashunit::console_results::print_risky_tests_and_reset() { printf "\n" fi - if [[ "$total_risky" -eq 1 ]]; then + 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" diff --git a/src/coverage.sh b/src/coverage.sh index 21c51213..cb8a7612 100644 --- a/src/coverage.sh +++ b/src/coverage.sh @@ -41,24 +41,23 @@ function bashunit::coverage::auto_discover_paths() { # Remove test suffixes to get source name: assert_test.sh -> assert local source_name="${file_basename%_test.sh}" - [[ "$source_name" == "$file_basename" ]] && source_name="${file_basename%Test.sh}" - [[ "$source_name" == "$file_basename" ]] && continue # Not a test file pattern + [ "$source_name" = "$file_basename" ] && source_name="${file_basename%Test.sh}" + [ "$source_name" = "$file_basename" ] && continue # Not a test file pattern # Find matching source files recursively local found_file while IFS= read -r -d '' found_file; do # Skip test files and vendor directories - [[ "$found_file" == *test* ]] && continue - [[ "$found_file" == *Test* ]] && continue - [[ "$found_file" == *vendor* ]] && continue - [[ "$found_file" == *node_modules* ]] && continue + case "$found_file" in + *test* | *Test* | *vendor* | *node_modules*) continue ;; + esac discovered_paths[discovered_paths_count]="$found_file" discovered_paths_count=$((discovered_paths_count + 1)) done < <(find "$project_root" -name "${source_name}*.sh" -type f -print0 2>/dev/null) done # Return unique paths, comma-separated - if [[ "$discovered_paths_count" -gt 0 ]]; then + if [ "$discovered_paths_count" -gt 0 ]; then printf '%s\n' "${discovered_paths[@]}" | sort -u | tr '\n' ',' | sed 's/,$//' fi } @@ -71,7 +70,7 @@ function bashunit::coverage::init() { # Skip coverage init if we're a subprocess of another coverage-enabled bashunit # This prevents nested bashunit calls (e.g., in acceptance tests) from # interfering with the parent's coverage tracking - if [[ -n "${_BASHUNIT_COVERAGE_DATA_FILE:-}" ]]; then + if [ -n "${_BASHUNIT_COVERAGE_DATA_FILE:-}" ]; then export BASHUNIT_COVERAGE=false return 0 fi @@ -133,7 +132,7 @@ function bashunit::coverage::normalize_path() { local file="$1" # Normalize path to absolute - if [[ -f "$file" ]]; then + if [ -f "$file" ]; then echo "$(cd "$(dirname "$file")" && pwd)/$(basename "$file")" else echo "$file" @@ -142,7 +141,7 @@ function bashunit::coverage::normalize_path() { # Get deduplicated list of tracked files function bashunit::coverage::get_tracked_files() { - if [[ ! -f "$_BASHUNIT_COVERAGE_TRACKED_FILES" ]]; then + if [ ! -f "$_BASHUNIT_COVERAGE_TRACKED_FILES" ]; then return fi sort -u "$_BASHUNIT_COVERAGE_TRACKED_FILES" @@ -151,9 +150,9 @@ function bashunit::coverage::get_tracked_files() { # Get coverage class (high/medium/low) based on percentage function bashunit::coverage::get_coverage_class() { local pct="$1" - if [[ $pct -ge ${BASHUNIT_COVERAGE_THRESHOLD_HIGH:-80} ]]; then + if [ "$pct" -ge "${BASHUNIT_COVERAGE_THRESHOLD_HIGH:-80}" ]; then echo "high" - elif [[ $pct -ge ${BASHUNIT_COVERAGE_THRESHOLD_LOW:-50} ]]; then + elif [ "$pct" -ge "${BASHUNIT_COVERAGE_THRESHOLD_LOW:-50}" ]; then echo "medium" else echo "low" @@ -164,7 +163,7 @@ function bashunit::coverage::get_coverage_class() { function bashunit::coverage::calculate_percentage() { local hit="$1" local executable="$2" - if [[ $executable -gt 0 ]]; then + if [ "$executable" -gt 0 ]; then echo $((hit * 100 / executable)) else echo "0" @@ -187,10 +186,10 @@ function bashunit::coverage::record_line() { local lineno="$2" # Skip if no file or line - [[ -z "$file" || -z "$lineno" ]] && return 0 + { [ -z "$file" ] || [ -z "$lineno" ]; } && return 0 # Skip if coverage data file doesn't exist (trap inherited by child process) - [[ -z "$_BASHUNIT_COVERAGE_DATA_FILE" ]] && return 0 + [ -z "$_BASHUNIT_COVERAGE_DATA_FILE" ] && return 0 # Fast in-memory should_track cache (avoids grep + file I/O per line) case "$_BASHUNIT_COVERAGE_TRACK_CACHE" in @@ -225,8 +224,8 @@ function bashunit::coverage::record_line() { _BASHUNIT_COVERAGE_BUFFER="${_BASHUNIT_COVERAGE_BUFFER}${normalized_file}:${lineno} " # Also buffer test hit data if in a test context - if [[ -n "${_BASHUNIT_COVERAGE_CURRENT_TEST_FILE:-}" && \ - -n "${_BASHUNIT_COVERAGE_CURRENT_TEST_FN:-}" ]]; then + if [ -n "${_BASHUNIT_COVERAGE_CURRENT_TEST_FILE:-}" ] && + [ -n "${_BASHUNIT_COVERAGE_CURRENT_TEST_FN:-}" ]; then _BASHUNIT_COVERAGE_HITS_BUFFER="${_BASHUNIT_COVERAGE_HITS_BUFFER}${normalized_file}:${lineno}|${_BASHUNIT_COVERAGE_CURRENT_TEST_FILE}:${_BASHUNIT_COVERAGE_CURRENT_TEST_FN} " fi @@ -234,21 +233,21 @@ function bashunit::coverage::record_line() { _BASHUNIT_COVERAGE_BUFFER_COUNT=$((_BASHUNIT_COVERAGE_BUFFER_COUNT + 1)) # Flush buffer to disk when threshold is reached - if [[ $_BASHUNIT_COVERAGE_BUFFER_COUNT -ge \ - $_BASHUNIT_COVERAGE_BUFFER_LIMIT ]]; then + if [ "$_BASHUNIT_COVERAGE_BUFFER_COUNT" -ge \ + "$_BASHUNIT_COVERAGE_BUFFER_LIMIT" ]; then bashunit::coverage::flush_buffer fi } function bashunit::coverage::flush_buffer() { - [[ -z "$_BASHUNIT_COVERAGE_BUFFER" ]] && return 0 + [ -z "$_BASHUNIT_COVERAGE_BUFFER" ] && return 0 # Determine output files (parallel-safe) local data_file="$_BASHUNIT_COVERAGE_DATA_FILE" local test_hits_file="$_BASHUNIT_COVERAGE_TEST_HITS_FILE" # Cache the parallel check to avoid function calls - if [[ -z "$_BASHUNIT_COVERAGE_IS_PARALLEL" ]]; then + if [ -z "$_BASHUNIT_COVERAGE_IS_PARALLEL" ]; then if bashunit::parallel::is_enabled; then _BASHUNIT_COVERAGE_IS_PARALLEL="yes" else @@ -256,7 +255,7 @@ function bashunit::coverage::flush_buffer() { fi fi - if [[ "$_BASHUNIT_COVERAGE_IS_PARALLEL" == "yes" ]]; then + if [ "$_BASHUNIT_COVERAGE_IS_PARALLEL" = "yes" ]; then data_file="${_BASHUNIT_COVERAGE_DATA_FILE}.$$" test_hits_file="${_BASHUNIT_COVERAGE_TEST_HITS_FILE}.$$" fi @@ -264,7 +263,7 @@ function bashunit::coverage::flush_buffer() { # Write buffered data in a single I/O operation printf '%s' "$_BASHUNIT_COVERAGE_BUFFER" >>"$data_file" - if [[ -n "$_BASHUNIT_COVERAGE_HITS_BUFFER" ]]; then + if [ -n "$_BASHUNIT_COVERAGE_HITS_BUFFER" ]; then printf '%s' "$_BASHUNIT_COVERAGE_HITS_BUFFER" >>"$test_hits_file" fi @@ -278,26 +277,26 @@ function bashunit::coverage::should_track() { local file="$1" # Skip empty paths - [[ -z "$file" ]] && return 1 + [ -z "$file" ] && return 1 # Skip if tracked files list doesn't exist (trap inherited by child process) - [[ -z "$_BASHUNIT_COVERAGE_TRACKED_FILES" ]] && return 1 + [ -z "$_BASHUNIT_COVERAGE_TRACKED_FILES" ] && return 1 # Check file-based cache for previous decision (Bash 3.0 compatible) # Cache format: "file:0" for excluded, "file:1" for tracked # In parallel mode, use per-process cache to avoid race conditions local cache_file="$_BASHUNIT_COVERAGE_TRACKED_CACHE_FILE" - if bashunit::parallel::is_enabled && [[ -n "$cache_file" ]]; then + if bashunit::parallel::is_enabled && [ -n "$cache_file" ]; then cache_file="${cache_file}.$$" # Initialize per-process cache if needed - [[ ! -f "$cache_file" ]] && [[ -d "$(dirname "$cache_file")" ]] && : >"$cache_file" + [ ! -f "$cache_file" ] && [ -d "$(dirname "$cache_file")" ] && : >"$cache_file" fi - if [[ -n "$cache_file" && -f "$cache_file" ]]; then + if [ -n "$cache_file" ] && [ -f "$cache_file" ]; then local cached_decision # Use || true to prevent exit in strict mode when grep finds no match cached_decision=$(grep "^${file}:" "$cache_file" 2>/dev/null | head -1) || true - if [[ -n "$cached_decision" ]]; then - [[ "${cached_decision##*:}" == "1" ]] && return 0 || return 1 + if [ -n "$cached_decision" ]; then + [ "${cached_decision##*:}" = "1" ] && return 0 || return 1 fi fi @@ -316,7 +315,7 @@ function bashunit::coverage::should_track() { *$pattern*) IFS="$old_ifs" # Cache exclusion decision (use per-process cache in parallel mode) - [[ -n "$cache_file" && -f "$cache_file" ]] && echo "${file}:0" >>"$cache_file" + { [ -n "$cache_file" ] && [ -f "$cache_file" ]; } && echo "${file}:0" >>"$cache_file" return 1 ;; esac @@ -328,27 +327,32 @@ function bashunit::coverage::should_track() { for path in $BASHUNIT_COVERAGE_PATHS; do # Resolve relative paths local resolved_path - if [[ "$path" == /* ]]; then + case "$path" in + /*) resolved_path="$path" - else + ;; + *) resolved_path="$(pwd)/$path" - fi + ;; + esac - if [[ "$normalized_file" == "$resolved_path"* ]]; then + case "$normalized_file" in + "$resolved_path"*) matched=true break - fi + ;; + esac done IFS="$old_ifs" - if [[ "$matched" == "false" ]]; then + if [ "$matched" = "false" ]; then # Cache exclusion decision (use per-process cache in parallel mode) - [[ -n "$cache_file" && -f "$cache_file" ]] && echo "${file}:0" >>"$cache_file" + { [ -n "$cache_file" ] && [ -f "$cache_file" ]; } && echo "${file}:0" >>"$cache_file" return 1 fi # Cache tracking decision (use per-process cache in parallel mode) - [[ -n "$cache_file" && -f "$cache_file" ]] && echo "${file}:1" >>"$cache_file" + { [ -n "$cache_file" ] && [ -f "$cache_file" ]; } && echo "${file}:1" >>"$cache_file" # Track this file for later reporting # In parallel mode, use a per-process file to avoid race conditions @@ -358,7 +362,7 @@ function bashunit::coverage::should_track() { fi # Only write if parent directory exists - if [[ -d "$(dirname "$tracked_file")" ]]; then + if [ -d "$(dirname "$tracked_file")" ]; then # Check if not already written to avoid duplicates if ! grep -q "^${normalized_file}$" "$tracked_file" 2>/dev/null; then echo "$normalized_file" >>"$tracked_file" @@ -378,9 +382,9 @@ function bashunit::coverage::aggregate_parallel() { # Use nullglob to handle case when no files match local pid_files pid_file pid_files=$(ls -1 "${base_file}."* 2>/dev/null) || true - if [[ -n "$pid_files" ]]; then + if [ -n "$pid_files" ]; then while IFS= read -r pid_file; do - [[ -f "$pid_file" ]] || continue + [ -f "$pid_file" ] || continue cat "$pid_file" >>"$base_file" rm -f "$pid_file" done <<<"$pid_files" @@ -388,20 +392,20 @@ function bashunit::coverage::aggregate_parallel() { # Find and merge all per-process tracked files lists pid_files=$(ls -1 "${tracked_base}."* 2>/dev/null) || true - if [[ -n "$pid_files" ]]; then + if [ -n "$pid_files" ]; then while IFS= read -r pid_file; do - [[ -f "$pid_file" ]] || continue + [ -f "$pid_file" ] || continue cat "$pid_file" >>"$tracked_base" rm -f "$pid_file" done <<<"$pid_files" fi # Find and merge all per-process test hits files - if [[ -n "$test_hits_base" ]]; then + if [ -n "$test_hits_base" ]; then pid_files=$(ls -1 "${test_hits_base}."* 2>/dev/null) || true - if [[ -n "$pid_files" ]]; then + if [ -n "$pid_files" ]; then while IFS= read -r pid_file; do - [[ -f "$pid_file" ]] || continue + [ -f "$pid_file" ] || continue cat "$pid_file" >>"$test_hits_base" rm -f "$pid_file" done <<<"$pid_files" @@ -409,7 +413,7 @@ function bashunit::coverage::aggregate_parallel() { fi # Deduplicate tracked files - if [[ -f "$tracked_base" ]]; then + if [ -f "$tracked_base" ]; then sort -u "$tracked_base" -o "$tracked_base" fi } @@ -431,30 +435,25 @@ function bashunit::coverage::is_executable_line() { : "$lineno" # Skip empty lines (line with only whitespace) - [[ -z "${line// /}" ]] && return 1 + [ -z "${line// /}" ] && return 1 # Skip comment-only lines (including shebang) - local _re='^[[:space:]]*#' - [[ "$line" =~ $_re ]] && return 1 + [ "$(echo "$line" | "$GREP" -cE '^[[:space:]]*#' || true)" -gt 0 ] && return 1 # Skip function declaration lines (but not single-line functions with body) - [[ "$line" =~ $_BASHUNIT_COVERAGE_FUNC_PATTERN ]] && return 1 + [ "$(echo "$line" | "$GREP" -cE "$_BASHUNIT_COVERAGE_FUNC_PATTERN" || true)" -gt 0 ] && return 1 # Skip lines with only braces - _re='^[[:space:]]*[\{\}][[:space:]]*$' - [[ "$line" =~ $_re ]] && return 1 + [ "$(echo "$line" | "$GREP" -cE '^[[:space:]]*[\{\}][[:space:]]*$' || true)" -gt 0 ] && return 1 # Skip control flow keywords (then, else, fi, do, done, esac, in, ;;, ;&, ;;&) - _re='^[[:space:]]*(then|else|fi|do|done|esac|in|;;|;;&|;&)[[:space:]]*(#.*)?$' - [[ "$line" =~ $_re ]] && return 1 + [ "$(echo "$line" | "$GREP" -cE '^[[:space:]]*(then|else|fi|do|done|esac|in|;;|;;&|;&)[[:space:]]*(#.*)?$' || true)" -gt 0 ] && return 1 # Skip case patterns like "--option)" or "*)" - _re='^[[:space:]]*[^\)]+\)[[:space:]]*$' - [[ "$line" =~ $_re ]] && return 1 + [ "$(echo "$line" | "$GREP" -cE '^[[:space:]]*[^\)]+\)[[:space:]]*$' || true)" -gt 0 ] && return 1 # Skip standalone ) for arrays/subshells - _re='^[[:space:]]*\)[[:space:]]*(#.*)?$' - [[ "$line" =~ $_re ]] && return 1 + [ "$(echo "$line" | "$GREP" -cE '^[[:space:]]*\)[[:space:]]*(#.*)?$' || true)" -gt 0 ] && return 1 return 0 } @@ -465,7 +464,7 @@ function bashunit::coverage::get_executable_lines() { local lineno=0 local line - while IFS= read -r line || [[ -n "$line" ]]; do + while IFS= read -r line || [ -n "$line" ]; do ((lineno++)) bashunit::coverage::is_executable_line "$line" "$lineno" && ((count++)) done <"$file" @@ -476,7 +475,7 @@ function bashunit::coverage::get_executable_lines() { function bashunit::coverage::get_hit_lines() { local file="$1" - if [[ ! -f "$_BASHUNIT_COVERAGE_DATA_FILE" ]]; then + if [ ! -f "$_BASHUNIT_COVERAGE_DATA_FILE" ]; then echo "0" return fi @@ -486,7 +485,7 @@ function bashunit::coverage::get_hit_lines() { hit_lines=$( (grep "^${file}:" "$_BASHUNIT_COVERAGE_DATA_FILE" 2>/dev/null || true) | cut -d: -f2 | sort -u) - if [[ -z "$hit_lines" ]]; then + if [ -z "$hit_lines" ]; then echo "0" return fi @@ -510,13 +509,13 @@ function bashunit::coverage::get_line_hits() { local file="$1" local lineno="$2" - if [[ ! -f "$_BASHUNIT_COVERAGE_DATA_FILE" ]]; then + if [ ! -f "$_BASHUNIT_COVERAGE_DATA_FILE" ]; then echo "0" return fi local count - count=$(grep -c "^${file}:${lineno}$" "$_BASHUNIT_COVERAGE_DATA_FILE" 2>/dev/null) || count=0 + count=$("$GREP" -c "^${file}:${lineno}$" "$_BASHUNIT_COVERAGE_DATA_FILE" 2>/dev/null) || count=0 echo "$count" } @@ -525,7 +524,7 @@ function bashunit::coverage::get_line_hits() { function bashunit::coverage::get_all_line_hits() { local file="$1" - if [[ ! -f "$_BASHUNIT_COVERAGE_DATA_FILE" ]]; then + if [ ! -f "$_BASHUNIT_COVERAGE_DATA_FILE" ]; then return fi @@ -543,7 +542,7 @@ function bashunit::coverage::get_all_line_hits() { function bashunit::coverage::get_all_line_tests() { local file="$1" - if [[ ! -f "${_BASHUNIT_COVERAGE_TEST_HITS_FILE:-}" ]]; then + if [ ! -f "${_BASHUNIT_COVERAGE_TEST_HITS_FILE:-}" ]; then return fi @@ -565,27 +564,24 @@ function bashunit::coverage::extract_functions() { local fn_start=0 local line - while IFS= read -r line || [[ -n "$line" ]]; do + while IFS= read -r line || [ -n "$line" ]; do ((lineno++)) # Check for function definition patterns # Pattern 1: function name() { or function name { # Pattern 2: name() { or name () { - if [[ $in_function -eq 0 ]]; then + if [ "$in_function" -eq 0 ]; then local fn_name="" # Match: function name() or function name { local _re='^[[:space:]]*(function[[:space:]]+)?([a-zA-Z_][a-zA-Z0-9_:]*)[[:space:]]*\(\)[[:space:]]*\{?[[:space:]]*(#.*)?$' - if [[ "$line" =~ $_re ]]; then - fn_name="${BASH_REMATCH[2]}" - else + fn_name=$(echo "$line" | sed -nE "s/$_re/\2/p") + if [ -z "$fn_name" ]; then _re='^[[:space:]]*(function[[:space:]]+)([a-zA-Z_][a-zA-Z0-9_:]*)[[:space:]]*\{[[:space:]]*(#.*)?$' - if [[ "$line" =~ $_re ]]; then - fn_name="${BASH_REMATCH[2]}" - fi + fn_name=$(echo "$line" | sed -nE "s/$_re/\2/p") fi - if [[ -n "$fn_name" ]]; then + if [ -n "$fn_name" ]; then in_function=1 current_fn="$fn_name" fn_start=$lineno @@ -597,8 +593,7 @@ function bashunit::coverage::extract_functions() { brace_count=$((brace_count + ${#open_braces} - ${#close_braces})) # Single-line function - local _re_ob='\{' _re_cb='\}' - if [[ $brace_count -eq 0 ]] && [[ "$line" =~ $_re_ob ]] && [[ "$line" =~ $_re_cb ]]; then + if [ "$brace_count" -eq 0 ] && [ "$(echo "$line" | "$GREP" -c '\{' || true)" -gt 0 ] && [ "$(echo "$line" | "$GREP" -c '\}' || true)" -gt 0 ]; then echo "${current_fn}:${fn_start}:${lineno}" in_function=0 current_fn="" @@ -608,13 +603,13 @@ function bashunit::coverage::extract_functions() { fi # Track braces inside function - if [[ $in_function -eq 1 ]]; then + if [ "$in_function" -eq 1 ]; then local open_braces="${line//[^\{]/}" local close_braces="${line//[^\}]/}" brace_count=$((brace_count + ${#open_braces} - ${#close_braces})) # Function ended - if [[ $brace_count -le 0 ]]; then + if [ "$brace_count" -le 0 ]; then echo "${current_fn}|${fn_start}|${lineno}" in_function=0 current_fn="" @@ -624,7 +619,7 @@ function bashunit::coverage::extract_functions() { done <"$file" # Handle unclosed function (shouldn't happen in valid code) - if [[ $in_function -eq 1 && -n "$current_fn" ]]; then + if [ "$in_function" -eq 1 ] && [ -n "$current_fn" ]; then echo "${current_fn}|${fn_start}|${lineno}" fi } @@ -651,14 +646,14 @@ function bashunit::coverage::get_function_coverage() { if bashunit::coverage::is_executable_line "$line_content" "$lineno"; then ((executable++)) local line_hits=${_hits_ref[$lineno]:-0} - if [[ $line_hits -gt 0 ]]; then + if [ "$line_hits" -gt 0 ]; then ((hit++)) fi fi done local pct=0 - if [[ $executable -gt 0 ]]; then + if [ "$executable" -gt 0 ]; then pct=$((hit * 100 / executable)) fi @@ -670,7 +665,7 @@ function bashunit::coverage::get_percentage() { local total_hit=0 while IFS= read -r file; do - [[ -z "$file" || ! -f "$file" ]] && continue + { [ -z "$file" ] || [ ! -f "$file" ]; } && continue local executable hit executable=$(bashunit::coverage::get_executable_lines "$file") @@ -698,7 +693,7 @@ function bashunit::coverage::report_text() { local file while IFS= read -r file; do - [[ -z "$file" || ! -f "$file" ]] && continue + { [ -z "$file" ] || [ ! -f "$file" ]; } && continue has_files=true local executable hit pct class @@ -712,7 +707,7 @@ function bashunit::coverage::report_text() { # Determine color based on class local color="" reset="" - if [[ "${BASHUNIT_NO_COLOR:-false}" != "true" ]]; then + if [ "${BASHUNIT_NO_COLOR:-false}" != "true" ]; then reset=$'\033[0m' case "$class" in high) color=$'\033[32m' ;; # Green @@ -727,7 +722,7 @@ function bashunit::coverage::report_text() { "$color" "$display_file" "$hit" "$executable" "$pct" "$reset" done < <(bashunit::coverage::get_tracked_files) - if [[ "$has_files" != "true" ]]; then + if [ "$has_files" != "true" ]; then echo "---------------" echo "Total: 0/0 (0%)" return 0 @@ -741,7 +736,7 @@ function bashunit::coverage::report_text() { total_class=$(bashunit::coverage::get_coverage_class "$total_pct") local color="" reset="" - if [[ "${BASHUNIT_NO_COLOR:-false}" != "true" ]]; then + if [ "${BASHUNIT_NO_COLOR:-false}" != "true" ]; then reset=$'\033[0m' case "$total_class" in high) color=$'\033[32m' ;; @@ -754,7 +749,7 @@ function bashunit::coverage::report_text() { "$color" "$total_hit" "$total_executable" "$total_pct" "$reset" # Show report location if generated - if [[ -n "$BASHUNIT_COVERAGE_REPORT" ]]; then + if [ -n "$BASHUNIT_COVERAGE_REPORT" ]; then echo "" echo "Coverage report written to: $BASHUNIT_COVERAGE_REPORT" fi @@ -763,7 +758,7 @@ function bashunit::coverage::report_text() { function bashunit::coverage::report_lcov() { local output_file="${1:-$BASHUNIT_COVERAGE_REPORT}" - if [[ -z "$output_file" ]]; then + if [ -z "$output_file" ]; then return 0 fi @@ -775,14 +770,14 @@ function bashunit::coverage::report_lcov() { echo "TN:" while IFS= read -r file; do - [[ -z "$file" || ! -f "$file" ]] && continue + { [ -z "$file" ] || [ ! -f "$file" ]; } && continue echo "SF:$file" local lineno=0 local line # shellcheck disable=SC2094 - while IFS= read -r line || [[ -n "$line" ]]; do + while IFS= read -r line || [ -n "$line" ]; do ((lineno++)) bashunit::coverage::is_executable_line "$line" "$lineno" || continue echo "DA:${lineno},$(bashunit::coverage::get_line_hits "$file" "$lineno")" @@ -800,17 +795,17 @@ function bashunit::coverage::report_lcov() { } function bashunit::coverage::check_threshold() { - if [[ -z "$BASHUNIT_COVERAGE_MIN" ]]; then + if [ -z "$BASHUNIT_COVERAGE_MIN" ]; then return 0 fi local pct pct=$(bashunit::coverage::get_percentage) - if [[ $pct -lt $BASHUNIT_COVERAGE_MIN ]]; then + if [ "$pct" -lt "$BASHUNIT_COVERAGE_MIN" ]; then local color="" local reset="" - if [[ "${BASHUNIT_NO_COLOR:-false}" != "true" ]]; then + if [ "${BASHUNIT_NO_COLOR:-false}" != "true" ]; then color=$'\033[31m' reset=$'\033[0m' fi @@ -841,7 +836,7 @@ function bashunit::coverage::path_to_filename() { function bashunit::coverage::report_html() { local output_dir="${1:-coverage/html}" - if [[ -z "$output_dir" ]]; then + if [ -z "$output_dir" ]; then return 0 fi @@ -857,7 +852,7 @@ function bashunit::coverage::report_html() { local file="" while IFS= read -r file; do - [[ -z "$file" || ! -f "$file" ]] && continue + { [ -z "$file" ] || [ ! -f "$file" ]; } && continue local executable hit pct executable=$(bashunit::coverage::get_executable_lines "$file") @@ -910,7 +905,7 @@ function bashunit::coverage::generate_index_html() { # Handle array passed as arguments - Bash 3.0 compatible local -a file_data=() local file_count=0 - if [[ $# -gt 0 ]]; then + if [ $# -gt 0 ]; then file_data=("$@") file_count=$# fi @@ -1284,15 +1279,20 @@ function bashunit::coverage::generate_file_html() { local -a tests_by_line=() local _line_and_test while IFS= read -r _line_and_test; do - [[ -z "$_line_and_test" ]] && continue + [ -z "$_line_and_test" ] && continue local _tln="${_line_and_test%%|*}" local _tinfo="${_line_and_test#*|}" - if [[ -n "${tests_by_line[_tln]:-}" ]]; then + if [ -n "${tests_by_line[_tln]:-}" ]; then # Append only if not already present (avoid duplicates) # Use newline boundaries to prevent false positives (e.g., test_foo matching test_foo_bar) - if [[ $'\n'"${tests_by_line[_tln]}"$'\n' != *$'\n'"$_tinfo"$'\n'* ]]; then + case $'\n'"${tests_by_line[_tln]}"$'\n' in + *$'\n'"$_tinfo"$'\n'*) + # already present, skip + ;; + *) tests_by_line[_tln]="${tests_by_line[_tln]}"$'\n'"${_tinfo}" - fi + ;; + esac else tests_by_line[_tln]="$_tinfo" fi @@ -1513,7 +1513,7 @@ EOF local functions_data functions_data=$(bashunit::coverage::extract_functions "$file") - if [[ -n "$functions_data" ]]; then + if [ -n "$functions_data" ]; then cat <<'EOF'
@@ -1528,7 +1528,7 @@ EOF EOF local fn_entry while IFS= read -r fn_entry; do - [[ -z "$fn_entry" ]] && continue + [ -z "$fn_entry" ] && continue local fn_name fn_start fn_end fn_name="${fn_entry%%|*}" local rest="${fn_entry#*|}" @@ -1545,7 +1545,7 @@ EOF if bashunit::coverage::is_executable_line "$ln_content" "$ln"; then ((fn_executable++)) local ln_hits=${hits_by_line[$ln]:-0} - if [[ $ln_hits -gt 0 ]]; then + if [ "$ln_hits" -gt 0 ]; then ((fn_hit++)) fi fi @@ -1596,7 +1596,7 @@ EOF local lineno=0 local line - while IFS= read -r line || [[ -n "$line" ]]; do + while IFS= read -r line || [ -n "$line" ]; do ((lineno++)) local escaped_line @@ -1609,17 +1609,17 @@ EOF # O(1) lookup from pre-loaded array local hits=${hits_by_line[$lineno]:-0} - if [[ $hits -gt 0 ]]; then + if [ "$hits" -gt 0 ]; then row_class="covered" # Check if we have test info for this line local test_info="${tests_by_line[$lineno]:-}" - if [[ -n "$test_info" ]]; then + if [ -n "$test_info" ]; then # Build tooltip with test information local tooltip_html="
Tests hitting this line