From 82c46d47aad1bb1090d1c3b95a4228cd952e434b Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Tue, 24 Sep 2024 21:57:25 +0200 Subject: [PATCH 01/12] feat: add --forward-stdout option into exec_assert --- bashunit | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bashunit b/bashunit index 8ac501e8..6f4a396a 100755 --- a/bashunit +++ b/bashunit @@ -28,6 +28,7 @@ source "$BASHUNIT_ROOT_DIR/src/main.sh" _ASSERT_FN="" _FILTER="" +_FORWARD_STDOUT=false _ARGS=() while [[ $# -gt 0 ]]; do @@ -41,6 +42,9 @@ while [[ $# -gt 0 ]]; do _FILTER="$2" shift ;; + --forward-stdout) + _FORWARD_STDOUT=true + ;; -s|--simple) export BASHUNIT_SIMPLE_OUTPUT=true ;; @@ -97,7 +101,7 @@ done set +eu if [[ -n "$_ASSERT_FN" ]]; then - main::exec_assert "$_ASSERT_FN" "${_ARGS[@]}" + main::exec_assert "$_ASSERT_FN" "$_FORWARD_STDOUT" "${_ARGS[@]}" else main::exec_tests "$_FILTER" "${_ARGS[@]}" fi From 6134ce5f022a9bfd76fc0b59dac885ff62518b52 Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Tue, 24 Sep 2024 22:04:24 +0200 Subject: [PATCH 02/12] feat: allow defer callable on assert_exit_code --- src/main.sh | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/src/main.sh b/src/main.sh index 9f7733d7..eeb19084 100644 --- a/src/main.sh +++ b/src/main.sh @@ -34,21 +34,54 @@ function main::exec_tests() { function main::exec_assert() { local original_assert_fn=$1 + local forward_stdout=$2 + local args=("${@:3}") + local assert_fn=$original_assert_fn - local args=("${@:2}") + # Check if the function exists if ! type "$assert_fn" > /dev/null 2>&1; then - # try again using prefix `assert_` assert_fn="assert_$assert_fn" if ! type "$assert_fn" > /dev/null 2>&1; then - echo "Function $original_assert_fn does not exist." exit 127 fi fi + if [[ "$assert_fn" == "assert_exit_code" ]]; then + # Get the last argument safely by calculating the array length + local last_index=$((${#args[@]} - 1)) + local last_arg="${args[$last_index]}" + + if [[ "$last_arg" == callable:* ]]; then + local callable_command="${last_arg#callable:(}" + callable_command="${callable_command%)}" # Remove both 'callable:(' and ')' + + # Capture the output directly into a variable + local output + output=$(eval "$callable_command" 2>&1 || echo "exit_code:$?") + + local last_line + last_line=$(echo "$output" | tail -n 1) + local exit_code + exit_code=$(echo "$last_line" | grep -o 'exit_code:[0-9]*' | cut -d':' -f2) + output=$(echo "$output" | sed '$d') + + # Remove the last argument and append the output + args=("${args[@]:0:last_index}") + args+=("$exit_code") + fi + + if [[ "$forward_stdout" == "true" ]]; then + echo "$output" + fi + + assert_fn=assert_same + fi + + # Run the assertion function "$assert_fn" "${args[@]}" if [[ "$(state::get_tests_failed)" -gt 0 ]] || [[ "$(state::get_assertions_failed)" -gt 0 ]]; then - exit 1 + exit 1 fi } From aadc2e8d89e0830c5bc7b7cc77341c4cc94e1a18 Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Wed, 25 Sep 2024 19:12:01 +0200 Subject: [PATCH 03/12] feat: exec_assert bashunit $assert_fn write into stderr And if there is any output (by a lazy eval as last parameter) that will be rendered into the stdout --- src/assert.sh | 2 +- src/main.sh | 59 ++++++++++--------- .../bashunit_direct_fn_call_test.sh | 4 +- 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/src/assert.sh b/src/assert.sh index d9fdade3..07a54ba9 100755 --- a/src/assert.sh +++ b/src/assert.sh @@ -213,7 +213,7 @@ function assert_general_error() { local actual_exit_code=${3-"$?"} local expected_exit_code=1 - if [[ $actual_exit_code -ne "$expected_exit_code" ]]; then + if [[ "$actual_exit_code" -ne "$expected_exit_code" ]]; then local label label="$(helper::normalize_test_function_name "${FUNCNAME[1]}")" state::add_assertions_failed diff --git a/src/main.sh b/src/main.sh index eeb19084..43f74a1b 100644 --- a/src/main.sh +++ b/src/main.sh @@ -43,45 +43,48 @@ function main::exec_assert() { if ! type "$assert_fn" > /dev/null 2>&1; then assert_fn="assert_$assert_fn" if ! type "$assert_fn" > /dev/null 2>&1; then + # Write into stderr + echo "Function $original_assert_fn does not exist." 1>&2 exit 127 fi fi - if [[ "$assert_fn" == "assert_exit_code" ]]; then - # Get the last argument safely by calculating the array length - local last_index=$((${#args[@]} - 1)) - local last_arg="${args[$last_index]}" - - if [[ "$last_arg" == callable:* ]]; then - local callable_command="${last_arg#callable:(}" - callable_command="${callable_command%)}" # Remove both 'callable:(' and ')' - - # Capture the output directly into a variable - local output - output=$(eval "$callable_command" 2>&1 || echo "exit_code:$?") - - local last_line - last_line=$(echo "$output" | tail -n 1) - local exit_code - exit_code=$(echo "$last_line" | grep -o 'exit_code:[0-9]*' | cut -d':' -f2) - output=$(echo "$output" | sed '$d') - - # Remove the last argument and append the output - args=("${args[@]:0:last_index}") - args+=("$exit_code") + # Get the last argument safely by calculating the array length + local last_index=$((${#args[@]} - 1)) + local last_arg="${args[$last_index]}" + local inner_exit_code=0 + local bashunit_exit_code=0 + local output + + if [[ "$last_arg" == eval* ]]; then + local callable_command="${last_arg#eval }" + output=$(eval "$callable_command" 2>&1 || echo "inner_exit_code:$?") + + local last_line + last_line=$(echo "$output" | tail -n 1) + inner_exit_code=$(echo "$last_line" | grep -o 'inner_exit_code:[0-9]*' | cut -d':' -f2) + if ! [[ $inner_exit_code =~ ^[0-9]+$ ]]; then + inner_exit_code=1 fi + output=$(echo "$output" | sed '$d') - if [[ "$forward_stdout" == "true" ]]; then - echo "$output" - fi + # Remove the last argument and append the output + args=("${args[@]:0:last_index}") + args+=("$inner_exit_code") + fi - assert_fn=assert_same + if [[ -n "$output" ]]; then + [[ "$forward_stdout" == "true" ]] && echo "$output" 1>&1 + [[ "$assert_fn" == "assert_exit_code" ]] && assert_fn="assert_same" fi - # Run the assertion function - "$assert_fn" "${args[@]}" + # Run the assertion function and write into stderr + "$assert_fn" "${args[@]}" 1>&2 + bashunit_exit_code=$? if [[ "$(state::get_tests_failed)" -gt 0 ]] || [[ "$(state::get_assertions_failed)" -gt 0 ]]; then exit 1 fi + + return "$bashunit_exit_code" } diff --git a/tests/acceptance/bashunit_direct_fn_call_test.sh b/tests/acceptance/bashunit_direct_fn_call_test.sh index 8fe89796..7fef312c 100644 --- a/tests/acceptance/bashunit_direct_fn_call_test.sh +++ b/tests/acceptance/bashunit_direct_fn_call_test.sh @@ -74,11 +74,11 @@ function test_bashunit_direct_fn_call_failure() { local expected="foo" local actual="bar" - assert_match_snapshot "$(./bashunit -a assert_same --env "$TEST_ENV_FILE" "$expected" $actual)" + assert_match_snapshot "$(./bashunit -a assert_same --env "$TEST_ENV_FILE" "$expected" $actual 2>&1)" assert_general_error "$(./bashunit -a assert_same --env "$TEST_ENV_FILE" "$expected" $actual)" } function test_bashunit_direct_fn_call_non_existing_fn() { - assert_match_snapshot "$(./bashunit -a non_existing_fn --env "$TEST_ENV_FILE")" + assert_match_snapshot "$(./bashunit -a non_existing_fn --env "$TEST_ENV_FILE" 2>&1)" assert_command_not_found "$(./bashunit -a non_existing_fn --env "$TEST_ENV_FILE")" } From e661cb796c4a0fd89c7237565f795764339378cf Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Wed, 25 Sep 2024 19:19:47 +0200 Subject: [PATCH 04/12] feat: remove --forward-stdout option echo output to stdout if last arg start with eval on standalone assertions --- CHANGELOG.md | 1 + bashunit | 6 +----- src/main.sh | 9 ++++----- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 626b6d6f..d9592ca7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Renamed `BASHUNIT_TESTS_ENV` to `BASHUNIT_LOAD_FILE` - Better handler runtime errors - Display failing tests after running the entire suite +- Added defer expressions with `eval` when using standalone assertions ## [0.16.0](https://github.com/TypedDevs/bashunit/compare/0.15.0...0.16.0) - 2024-09-15 diff --git a/bashunit b/bashunit index 6f4a396a..8ac501e8 100755 --- a/bashunit +++ b/bashunit @@ -28,7 +28,6 @@ source "$BASHUNIT_ROOT_DIR/src/main.sh" _ASSERT_FN="" _FILTER="" -_FORWARD_STDOUT=false _ARGS=() while [[ $# -gt 0 ]]; do @@ -42,9 +41,6 @@ while [[ $# -gt 0 ]]; do _FILTER="$2" shift ;; - --forward-stdout) - _FORWARD_STDOUT=true - ;; -s|--simple) export BASHUNIT_SIMPLE_OUTPUT=true ;; @@ -101,7 +97,7 @@ done set +eu if [[ -n "$_ASSERT_FN" ]]; then - main::exec_assert "$_ASSERT_FN" "$_FORWARD_STDOUT" "${_ARGS[@]}" + main::exec_assert "$_ASSERT_FN" "${_ARGS[@]}" else main::exec_tests "$_FILTER" "${_ARGS[@]}" fi diff --git a/src/main.sh b/src/main.sh index 43f74a1b..ec8b72fd 100644 --- a/src/main.sh +++ b/src/main.sh @@ -34,8 +34,7 @@ function main::exec_tests() { function main::exec_assert() { local original_assert_fn=$1 - local forward_stdout=$2 - local args=("${@:3}") + local args=("${@:2}") local assert_fn=$original_assert_fn @@ -56,7 +55,7 @@ function main::exec_assert() { local bashunit_exit_code=0 local output - if [[ "$last_arg" == eval* ]]; then + if [[ "$assert_fn" == "assert_exit_code" && "$last_arg" == eval* ]]; then local callable_command="${last_arg#eval }" output=$(eval "$callable_command" 2>&1 || echo "inner_exit_code:$?") @@ -74,8 +73,8 @@ function main::exec_assert() { fi if [[ -n "$output" ]]; then - [[ "$forward_stdout" == "true" ]] && echo "$output" 1>&1 - [[ "$assert_fn" == "assert_exit_code" ]] && assert_fn="assert_same" + echo "$output" 1>&1 + assert_fn="assert_same" fi # Run the assertion function and write into stderr From 2b05959a784428ab29d893ad915105de6faa3f9b Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Wed, 25 Sep 2024 19:42:11 +0200 Subject: [PATCH 05/12] docs: add Lazy evaluations on standalone docs --- docs/standalone.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/standalone.md b/docs/standalone.md index 52b44741..97b4736f 100644 --- a/docs/standalone.md +++ b/docs/standalone.md @@ -43,3 +43,24 @@ The prefix `assert_` is optional. ``` ::: + +## Lazy evaluations + +You can evaluate the `exit_code` for your scripts using `eval...` instead of executing them with `$(...)` to avoid +interrupting the CI when encountering a potential error (anything different from `0`). For example: + +::: code-group +```bash [Example] +../bashunit -a exit_code "0" "eval $PHPSTAN_PATH analyze \ + --no-progress --level 8 \ + --error-format raw ./" 1>&1 2> /tmp/error.log +``` +```[Output] +Testing.php:3:Method Testing::bar() has no return type specified. +``` +```[/tmp/error.log] +✗ Failed: Main::exec assert + Expected '0' + but got '1' +``` +::: From 941f1ba21ece7aca49c3c7c3306932b7a70a242c Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Thu, 26 Sep 2024 12:06:02 +0200 Subject: [PATCH 06/12] docs: improve docs on lazy evaluations on standalone Adding full control over the stdout and stderr --- docs/standalone.md | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/docs/standalone.md b/docs/standalone.md index 97b4736f..533383fb 100644 --- a/docs/standalone.md +++ b/docs/standalone.md @@ -43,17 +43,46 @@ The prefix `assert_` is optional. ``` ::: - ## Lazy evaluations You can evaluate the `exit_code` for your scripts using `eval...` instead of executing them with `$(...)` to avoid -interrupting the CI when encountering a potential error (anything different from `0`). For example: +interrupting the CI when encountering a potential error (anything but `0`). + +::: code-group +```bash [Example] +./bashunit -a exit_code "1" "eval $PHPSTAN_PATH analyze \ + --no-progress --level 8 \ + --error-format raw ./" +``` +```[Output] +Testing.php:3:Method Testing::bar() has no return type specified. +``` +::: + +This is useful to get control over the output of your `eval...`: + +::: code-group +```bash [Example] +OUTPUT=$(./bashunit -a exit_code "1" "eval $PHPSTAN_PATH analyze \ + --no-progress --level 8 \ + --error-format raw ./") +./bashunit -a line_count 1 "$OUTPUT" +``` +```[Output] +# No output +``` +::: + +### Full control over the stdout and stderr + +The stdout will be used for the eval result, while bashunit output will be on stderr. +This way you can control the FD and redirect the output as you need. ::: code-group ```bash [Example] -../bashunit -a exit_code "0" "eval $PHPSTAN_PATH analyze \ +./bashunit -a exit_code "0" "eval $PHPSTAN_PATH analyze \ --no-progress --level 8 \ - --error-format raw ./" 1>&1 2> /tmp/error.log + --error-format raw ./" 2> /tmp/error.log ``` ```[Output] Testing.php:3:Method Testing::bar() has no return type specified. From 52b2255ccf4779090e3bc24f6fb2e099a06a471f Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Thu, 26 Sep 2024 18:12:25 +0200 Subject: [PATCH 07/12] docs: add more documentation inside main::exec_assert --- src/main.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.sh b/src/main.sh index ec8b72fd..3a4fcffa 100644 --- a/src/main.sh +++ b/src/main.sh @@ -56,7 +56,9 @@ function main::exec_assert() { local output if [[ "$assert_fn" == "assert_exit_code" && "$last_arg" == eval* ]]; then + # remove the "eval" word from the last argument local callable_command="${last_arg#eval }" + # use real bash "eval" to evaluate your command output=$(eval "$callable_command" 2>&1 || echo "inner_exit_code:$?") local last_line From 5a14e87054f93560a7b7b6e2435bf3b0c037be54 Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Fri, 27 Sep 2024 09:43:16 +0200 Subject: [PATCH 08/12] test: bashunit_assert_exit_code --- src/main.sh | 13 ++++--- .../bashunit_direct_fn_call_test.sh | 39 +++++++++++++++++++ 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/main.sh b/src/main.sh index 3a4fcffa..61034ec9 100644 --- a/src/main.sh +++ b/src/main.sh @@ -58,16 +58,19 @@ function main::exec_assert() { if [[ "$assert_fn" == "assert_exit_code" && "$last_arg" == eval* ]]; then # remove the "eval" word from the last argument local callable_command="${last_arg#eval }" - # use real bash "eval" to evaluate your command + # use real bash "eval" to evaluate the command output=$(eval "$callable_command" 2>&1 || echo "inner_exit_code:$?") local last_line last_line=$(echo "$output" | tail -n 1) - inner_exit_code=$(echo "$last_line" | grep -o 'inner_exit_code:[0-9]*' | cut -d':' -f2) - if ! [[ $inner_exit_code =~ ^[0-9]+$ ]]; then - inner_exit_code=1 + # extract inner_exit_code if the command failed + if echo "$last_line" | grep -q 'inner_exit_code:[0-9]*'; then + inner_exit_code=$(echo "$last_line" | grep -o 'inner_exit_code:[0-9]*' | cut -d':' -f2) + if ! [[ $inner_exit_code =~ ^[0-9]+$ ]]; then + inner_exit_code=1 + fi + output=$(echo "$output" | sed '$d') fi - output=$(echo "$output" | sed '$d') # Remove the last argument and append the output args=("${args[@]:0:last_index}") diff --git a/tests/acceptance/bashunit_direct_fn_call_test.sh b/tests/acceptance/bashunit_direct_fn_call_test.sh index 7fef312c..0c617193 100644 --- a/tests/acceptance/bashunit_direct_fn_call_test.sh +++ b/tests/acceptance/bashunit_direct_fn_call_test.sh @@ -82,3 +82,42 @@ function test_bashunit_direct_fn_call_non_existing_fn() { assert_match_snapshot "$(./bashunit -a non_existing_fn --env "$TEST_ENV_FILE" 2>&1)" assert_command_not_found "$(./bashunit -a non_existing_fn --env "$TEST_ENV_FILE")" } + +function test_bashunit_assert_exit_code_successful_code() { + ./bashunit -a exit_code "0" "eval ./bashunit -a same 1 1" + assert_successful_code +} + +function test_bashunit_assert_exit_code_general_error() { + ./bashunit -a exit_code "1" "eval ./bashunit -a same 0 1" + assert_successful_code +} + +function test_bashunit_assert_exit_code_eval_successful_but_exit_code_error() { + local temp + temp=$(mktemp) + + local output + output="$(./bashunit -a exit_code "1" "eval echo something to stdout" 2> "$temp")" + + assert_same "something to stdout" "$output" + + assert_contains\ + "$(console_results::print_failed_test "Main::exec assert" "1" "but got " "0")"\ + "$(cat "$temp")" + + rm "$temp" +} + +function test_bashunit_assert_exit_code_eval_successful_and_exit_code_ok() { + local temp + temp=$(mktemp) + + local output + output="$(./bashunit -a exit_code "0" "eval echo something to stdout" 2> "$temp")" + + assert_same "something to stdout" "$output" + assert_empty "$(cat "$temp")" + + rm "$temp" +} From a0f4a07e98d80be2af1b021787f0bc92a405bd38 Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Fri, 27 Sep 2024 10:10:07 +0200 Subject: [PATCH 09/12] test: add more bashunit_assert_exit_code tests --- .../bashunit_direct_fn_call_test.sh | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/tests/acceptance/bashunit_direct_fn_call_test.sh b/tests/acceptance/bashunit_direct_fn_call_test.sh index 0c617193..110d87b1 100644 --- a/tests/acceptance/bashunit_direct_fn_call_test.sh +++ b/tests/acceptance/bashunit_direct_fn_call_test.sh @@ -83,12 +83,43 @@ function test_bashunit_direct_fn_call_non_existing_fn() { assert_command_not_found "$(./bashunit -a non_existing_fn --env "$TEST_ENV_FILE")" } -function test_bashunit_assert_exit_code_successful_code() { +function test_bashunit_assert_exit_code_successful_with_inner_func() { + local temp + temp=$(mktemp) + + local output + # shellcheck disable=SC2116 + output="$(./bashunit -a exit_code "0" "$(echo "this wont go to stdout")" 2> "$temp")" + + assert_empty "$output" + assert_empty "$(cat "$temp")" + + rm "$temp" +} + +function test_bashunit_assert_exit_code_error_with_inner_func() { + local temp + temp=$(mktemp) + + local output + # shellcheck disable=SC2116 + output="$(./bashunit -a exit_code "1" "$(echo "this wont go to stdout")" 2> "$temp")" + + assert_empty "$output" + + assert_contains\ + "$(console_results::print_failed_test "Main::exec assert" "0" "to be" "1")"\ + "$(cat "$temp")" + + rm "$temp" +} + +function test_bashunit_assert_exit_code_eval_successful_code() { ./bashunit -a exit_code "0" "eval ./bashunit -a same 1 1" assert_successful_code } -function test_bashunit_assert_exit_code_general_error() { +function test_bashunit_assert_exit_code_eval_general_error() { ./bashunit -a exit_code "1" "eval ./bashunit -a same 0 1" assert_successful_code } From 8abe438798fe0e6d7bf7d49c14ce4c3674c8f9e2 Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Sat, 28 Sep 2024 10:08:16 +0200 Subject: [PATCH 10/12] feat: remove eval prefix from -a exit_code 2nd arg --- docs/standalone.md | 14 +++--- src/main.sh | 11 ++--- .../bashunit_direct_fn_call_test.sh | 44 ++++++++----------- 3 files changed, 29 insertions(+), 40 deletions(-) diff --git a/docs/standalone.md b/docs/standalone.md index 533383fb..16f80791 100644 --- a/docs/standalone.md +++ b/docs/standalone.md @@ -45,12 +45,12 @@ The prefix `assert_` is optional. ## Lazy evaluations -You can evaluate the `exit_code` for your scripts using `eval...` instead of executing them with `$(...)` to avoid -interrupting the CI when encountering a potential error (anything but `0`). +You can evaluate the `exit_code` for your scripts using the command to call as raw-string instead of +executing them with `$(...)` to avoid interrupting the CI when encountering a potential error (anything but `0`). ::: code-group ```bash [Example] -./bashunit -a exit_code "1" "eval $PHPSTAN_PATH analyze \ +./bashunit -a exit_code "1" "$PHPSTAN_PATH analyze \ --no-progress --level 8 \ --error-format raw ./" ``` @@ -59,11 +59,11 @@ Testing.php:3:Method Testing::bar() has no return type specified. ``` ::: -This is useful to get control over the output of your `eval...`: +This is useful to get control over the output of your "callable": ::: code-group ```bash [Example] -OUTPUT=$(./bashunit -a exit_code "1" "eval $PHPSTAN_PATH analyze \ +OUTPUT=$(./bashunit -a exit_code "1" "$PHPSTAN_PATH analyze \ --no-progress --level 8 \ --error-format raw ./") ./bashunit -a line_count 1 "$OUTPUT" @@ -75,12 +75,12 @@ OUTPUT=$(./bashunit -a exit_code "1" "eval $PHPSTAN_PATH analyze \ ### Full control over the stdout and stderr -The stdout will be used for the eval result, while bashunit output will be on stderr. +The stdout will be used for the callable result, while bashunit output will be on stderr. This way you can control the FD and redirect the output as you need. ::: code-group ```bash [Example] -./bashunit -a exit_code "0" "eval $PHPSTAN_PATH analyze \ +./bashunit -a exit_code "0" "$PHPSTAN_PATH analyze \ --no-progress --level 8 \ --error-format raw ./" 2> /tmp/error.log ``` diff --git a/src/main.sh b/src/main.sh index 61034ec9..44484e74 100644 --- a/src/main.sh +++ b/src/main.sh @@ -55,12 +55,9 @@ function main::exec_assert() { local bashunit_exit_code=0 local output - if [[ "$assert_fn" == "assert_exit_code" && "$last_arg" == eval* ]]; then - # remove the "eval" word from the last argument - local callable_command="${last_arg#eval }" - # use real bash "eval" to evaluate the command - output=$(eval "$callable_command" 2>&1 || echo "inner_exit_code:$?") - + # The first word in last_arg is a valid shell command or function + if [[ "$assert_fn" == "assert_exit_code" && $(command -v "${last_arg%% *}") ]]; then + output=$(eval "$last_arg" 2>&1 || echo "inner_exit_code:$?") local last_line last_line=$(echo "$output" | tail -n 1) # extract inner_exit_code if the command failed @@ -87,7 +84,7 @@ function main::exec_assert() { bashunit_exit_code=$? if [[ "$(state::get_tests_failed)" -gt 0 ]] || [[ "$(state::get_assertions_failed)" -gt 0 ]]; then - exit 1 + return 1 fi return "$bashunit_exit_code" diff --git a/tests/acceptance/bashunit_direct_fn_call_test.sh b/tests/acceptance/bashunit_direct_fn_call_test.sh index 110d87b1..eb5186e1 100644 --- a/tests/acceptance/bashunit_direct_fn_call_test.sh +++ b/tests/acceptance/bashunit_direct_fn_call_test.sh @@ -83,13 +83,11 @@ function test_bashunit_direct_fn_call_non_existing_fn() { assert_command_not_found "$(./bashunit -a non_existing_fn --env "$TEST_ENV_FILE")" } +# shellcheck disable=SC2155 function test_bashunit_assert_exit_code_successful_with_inner_func() { - local temp - temp=$(mktemp) - - local output + local temp=$(mktemp) # shellcheck disable=SC2116 - output="$(./bashunit -a exit_code "0" "$(echo "this wont go to stdout")" 2> "$temp")" + local output="$(./bashunit -a exit_code "0" "$(echo "this wont go to stdout")" 2> "$temp")" assert_empty "$output" assert_empty "$(cat "$temp")" @@ -97,13 +95,11 @@ function test_bashunit_assert_exit_code_successful_with_inner_func() { rm "$temp" } +# shellcheck disable=SC2155 function test_bashunit_assert_exit_code_error_with_inner_func() { - local temp - temp=$(mktemp) - - local output + local temp=$(mktemp) # shellcheck disable=SC2116 - output="$(./bashunit -a exit_code "1" "$(echo "this wont go to stdout")" 2> "$temp")" + local output="$(./bashunit -a exit_code "1" "$(echo "this wont go to stdout")" 2> "$temp")" assert_empty "$output" @@ -114,22 +110,20 @@ function test_bashunit_assert_exit_code_error_with_inner_func() { rm "$temp" } -function test_bashunit_assert_exit_code_eval_successful_code() { - ./bashunit -a exit_code "0" "eval ./bashunit -a same 1 1" +function test_bashunit_assert_exit_code_str_successful_code() { + ./bashunit -a exit_code "0" "./bashunit -a same 1 1" assert_successful_code } -function test_bashunit_assert_exit_code_eval_general_error() { - ./bashunit -a exit_code "1" "eval ./bashunit -a same 0 1" +function test_bashunit_assert_exit_code_str_general_error() { + ./bashunit -a exit_code "1" "./bashunit -a same 1 2" assert_successful_code } -function test_bashunit_assert_exit_code_eval_successful_but_exit_code_error() { - local temp - temp=$(mktemp) - - local output - output="$(./bashunit -a exit_code "1" "eval echo something to stdout" 2> "$temp")" +# shellcheck disable=SC2155 +function test_bashunit_assert_exit_code_str_successful_but_exit_code_error() { + local temp=$(mktemp) + local output="$(./bashunit -a exit_code "1" "echo something to stdout" 2> "$temp")" assert_same "something to stdout" "$output" @@ -140,12 +134,10 @@ function test_bashunit_assert_exit_code_eval_successful_but_exit_code_error() { rm "$temp" } -function test_bashunit_assert_exit_code_eval_successful_and_exit_code_ok() { - local temp - temp=$(mktemp) - - local output - output="$(./bashunit -a exit_code "0" "eval echo something to stdout" 2> "$temp")" +# shellcheck disable=SC2155 +function test_bashunit_assert_exit_code_str_successful_and_exit_code_ok() { + local temp=$(mktemp) + local output="$(./bashunit -a exit_code "0" "echo something to stdout" 2> "$temp")" assert_same "something to stdout" "$output" assert_empty "$(cat "$temp")" From b905306a36d98d2c46e2ff2953e18df92275fd5c Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Sat, 28 Sep 2024 11:11:20 +0200 Subject: [PATCH 11/12] refactor: extract handle_assert_exit_code --- src/main.sh | 58 ++++++++++++------- .../bashunit_direct_fn_call_test.sh | 11 +++- 2 files changed, 46 insertions(+), 23 deletions(-) diff --git a/src/main.sh b/src/main.sh index 44484e74..613d09b6 100644 --- a/src/main.sh +++ b/src/main.sh @@ -42,7 +42,6 @@ function main::exec_assert() { if ! type "$assert_fn" > /dev/null 2>&1; then assert_fn="assert_$assert_fn" if ! type "$assert_fn" > /dev/null 2>&1; then - # Write into stderr echo "Function $original_assert_fn does not exist." 1>&2 exit 127 fi @@ -51,28 +50,23 @@ function main::exec_assert() { # Get the last argument safely by calculating the array length local last_index=$((${#args[@]} - 1)) local last_arg="${args[$last_index]}" + local output="" local inner_exit_code=0 local bashunit_exit_code=0 - local output - - # The first word in last_arg is a valid shell command or function - if [[ "$assert_fn" == "assert_exit_code" && $(command -v "${last_arg%% *}") ]]; then - output=$(eval "$last_arg" 2>&1 || echo "inner_exit_code:$?") - local last_line - last_line=$(echo "$output" | tail -n 1) - # extract inner_exit_code if the command failed - if echo "$last_line" | grep -q 'inner_exit_code:[0-9]*'; then - inner_exit_code=$(echo "$last_line" | grep -o 'inner_exit_code:[0-9]*' | cut -d':' -f2) - if ! [[ $inner_exit_code =~ ^[0-9]+$ ]]; then - inner_exit_code=1 - fi - output=$(echo "$output" | sed '$d') - fi - # Remove the last argument and append the output - args=("${args[@]:0:last_index}") - args+=("$inner_exit_code") - fi + # Handle different assert_* functions + case "$assert_fn" in + assert_exit_code) + output=$(main::handle_assert_exit_code "$last_arg") + inner_exit_code=$? + # Remove the last argument and append the exit code + args=("${args[@]:0:last_index}") + args+=("$inner_exit_code") + ;; + *) + # Add more cases here for other assert_* handlers if needed + ;; + esac if [[ -n "$output" ]]; then echo "$output" 1>&1 @@ -89,3 +83,27 @@ function main::exec_assert() { return "$bashunit_exit_code" } + +function main::handle_assert_exit_code() { + local cmd="$1" + local output + local inner_exit_code=0 + + if [[ $(command -v "${cmd%% *}") ]]; then + output=$(eval "$cmd" 2>&1 || echo "inner_exit_code:$?") + local last_line + last_line=$(echo "$output" | tail -n 1) + if echo "$last_line" | grep -q 'inner_exit_code:[0-9]*'; then + inner_exit_code=$(echo "$last_line" | grep -o 'inner_exit_code:[0-9]*' | cut -d':' -f2) + if ! [[ $inner_exit_code =~ ^[0-9]+$ ]]; then + inner_exit_code=1 + fi + output=$(echo "$output" | sed '$d') + fi + echo "$output" + return "$inner_exit_code" + else + echo "Command not found: $cmd" 1>&2 + return 127 + fi +} diff --git a/tests/acceptance/bashunit_direct_fn_call_test.sh b/tests/acceptance/bashunit_direct_fn_call_test.sh index eb5186e1..4de6b7f4 100644 --- a/tests/acceptance/bashunit_direct_fn_call_test.sh +++ b/tests/acceptance/bashunit_direct_fn_call_test.sh @@ -87,10 +87,10 @@ function test_bashunit_direct_fn_call_non_existing_fn() { function test_bashunit_assert_exit_code_successful_with_inner_func() { local temp=$(mktemp) # shellcheck disable=SC2116 - local output="$(./bashunit -a exit_code "0" "$(echo "this wont go to stdout")" 2> "$temp")" + local output="$(./bashunit -a exit_code "0" "$(echo "unknown command")" 2> "$temp")" assert_empty "$output" - assert_empty "$(cat "$temp")" + assert_same "Command not found: unknown command" "$(cat "$temp")" rm "$temp" } @@ -99,7 +99,7 @@ function test_bashunit_assert_exit_code_successful_with_inner_func() { function test_bashunit_assert_exit_code_error_with_inner_func() { local temp=$(mktemp) # shellcheck disable=SC2116 - local output="$(./bashunit -a exit_code "1" "$(echo "this wont go to stdout")" 2> "$temp")" + local output="$(./bashunit -a exit_code "1" "$(echo "unknown command")" 2> "$temp")" assert_empty "$output" @@ -144,3 +144,8 @@ function test_bashunit_assert_exit_code_str_successful_and_exit_code_ok() { rm "$temp" } + +#function test_bashunit_assert_() { +# ./bashunit -a exit_code "1" -a contains "some error" -e "the_command" +# assert_successful_code +#} From a15788fec85d19e0f771ee6a99194b9402407012 Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Sat, 28 Sep 2024 16:46:59 +0200 Subject: [PATCH 12/12] refactor: remove commented code --- tests/acceptance/bashunit_direct_fn_call_test.sh | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/acceptance/bashunit_direct_fn_call_test.sh b/tests/acceptance/bashunit_direct_fn_call_test.sh index 4de6b7f4..4f99f7aa 100644 --- a/tests/acceptance/bashunit_direct_fn_call_test.sh +++ b/tests/acceptance/bashunit_direct_fn_call_test.sh @@ -144,8 +144,3 @@ function test_bashunit_assert_exit_code_str_successful_and_exit_code_ok() { rm "$temp" } - -#function test_bashunit_assert_() { -# ./bashunit -a exit_code "1" -a contains "some error" -e "the_command" -# assert_successful_code -#}