diff --git a/CHANGELOG.md b/CHANGELOG.md index b5460eef..1f942b43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Improve clock performance - Make install.sh args more flexible - Improve Windows detection allowing parallel tests on Git Bash, MSYS and Cygwin +- Enable chain asserts standalone assertions ## [0.20.0](https://github.com/TypedDevs/bashunit/compare/0.19.1...0.20.0) - 2025-06-01 diff --git a/bashunit b/bashunit index d06795f8..19aaf539 100755 --- a/bashunit +++ b/bashunit @@ -33,6 +33,11 @@ source "$BASHUNIT_ROOT_DIR/src/main.sh" _ASSERT_FN="" _FILTER="" _RAW_ARGS=() +_ASSERT_FNS=() +_ASSERT_ARGS_LIST=() +_CURRENT_ASSERT_ARGS=() +_ASSERT_COUNT=0 +_CURRENT_ASSERT_FN="" _ARGS=() _BENCH_MODE=false @@ -40,12 +45,19 @@ check_os::init clock::init # Argument parsing -while [[ $# -gt 0 ]]; do - case "$1" in - -a|--assert) - _ASSERT_FN="$2" - shift - ;; + while [[ $# -gt 0 ]]; do + case "$1" in + -a|--assert) + if [[ -n "$_CURRENT_ASSERT_FN" ]]; then + _ASSERT_FNS+=("$_CURRENT_ASSERT_FN") + joined="$(printf '%s\n' "${_CURRENT_ASSERT_ARGS[@]}")" + _ASSERT_ARGS_LIST+=("$joined") + _ASSERT_COUNT=$(( _ASSERT_COUNT + 1 )) + _CURRENT_ASSERT_ARGS=() + fi + _CURRENT_ASSERT_FN="$2" + shift + ;; -f|--filter) _FILTER="$2" shift @@ -106,11 +118,20 @@ while [[ $# -gt 0 ]]; do trap '' EXIT && exit 0 ;; *) - _RAW_ARGS+=("$1") + if [[ -n "$_CURRENT_ASSERT_FN" ]]; then + _CURRENT_ASSERT_ARGS+=("$1") + else + _RAW_ARGS+=("$1") + fi ;; esac shift done +if [[ -n "$_CURRENT_ASSERT_FN" ]]; then + _ASSERT_FNS+=("$_CURRENT_ASSERT_FN") + joined="$(printf '%s\n' "${_CURRENT_ASSERT_ARGS[@]}")" + _ASSERT_ARGS_LIST+=("$joined") +fi # Expand positional arguments after all options have been processed if [[ ${#_RAW_ARGS[@]} -gt 0 ]]; then @@ -132,8 +153,16 @@ set +eu ################# # Main execution ################# -if [[ -n "$_ASSERT_FN" ]]; then - main::exec_assert "$_ASSERT_FN" "${_ARGS[@]}" +if [[ ${#_ASSERT_FNS[@]} -gt 0 ]]; then + if [[ ${#_ASSERT_FNS[@]} -eq 1 ]]; then + args=() + while IFS= read -r line; do + args+=("$line") + done <<< "${_ASSERT_ARGS_LIST[0]}" + main::exec_assert "${_ASSERT_FNS[0]}" "${args[@]}" + else + main::exec_assert_chain + fi elif [[ "$_BENCH_MODE" == true ]]; then main::exec_benchmarks "$_FILTER" "${_ARGS[@]}" else diff --git a/docs/assertions.md b/docs/assertions.md index a9cdcdd9..74ffd376 100644 --- a/docs/assertions.md +++ b/docs/assertions.md @@ -366,6 +366,13 @@ function test_failure() { ``` ::: +You can chain another assertion to inspect the generated output when using +`assert_exit_code` standalone with the `-a` option: + +```bash +bashunit -a exit_code "1" "my_program" -a contains "error" +``` + ## assert_array_contains > `assert_array_contains "needle" "haystack"` diff --git a/docs/standalone.md b/docs/standalone.md index 16f80791..c2498225 100644 --- a/docs/standalone.md +++ b/docs/standalone.md @@ -73,6 +73,18 @@ OUTPUT=$(./bashunit -a exit_code "1" "$PHPSTAN_PATH analyze \ ``` ::: +You can also chain assertions directly using multiple `-a` flags. +The output from `exit_code` will be passed as the last argument of the next assertion: + +::: code-group +```bash [Example] +./bashunit -a exit_code "1" "my_program" -a contains "some error" +``` +```[Output] +# No output +``` +::: + ### Full control over the stdout and stderr The stdout will be used for the callable result, while bashunit output will be on stderr. diff --git a/src/main.sh b/src/main.sh index 2955b075..e0c146ab 100644 --- a/src/main.sh +++ b/src/main.sh @@ -164,6 +164,28 @@ function main::exec_assert() { return "$bashunit_exit_code" } +function main::exec_assert_chain() { + local output="" + local exit_code=0 + + for idx in "${!_ASSERT_FNS[@]}"; do + local fn="${_ASSERT_FNS[$idx]}" + args=() + while IFS= read -r line; do + args+=("$line") + done <<< "${_ASSERT_ARGS_LIST[$idx]}" + if [[ $idx -gt 0 ]]; then + args+=("$output") + fi + state::initialize_assertions_count + output=$(main::exec_assert "$fn" "${args[@]}") + exit_code=$? + done + + [[ -n "$output" ]] && echo "$output" + return "$exit_code" +} + function main::handle_assert_exit_code() { local cmd="$1" local output diff --git a/tests/acceptance/bashunit_direct_fn_call_test.sh b/tests/acceptance/bashunit_direct_fn_call_test.sh index 2d2549d2..8924bc9e 100644 --- a/tests/acceptance/bashunit_direct_fn_call_test.sh +++ b/tests/acceptance/bashunit_direct_fn_call_test.sh @@ -138,3 +138,8 @@ function test_bashunit_assert_exit_code_str_successful_and_exit_code_ok() { assert_same "something to stdout" "$output" assert_empty "$(cat "$temp")" } + +function test_bashunit_assert_exit_code_and_contains_chained() { + ./bashunit -a exit_code "1" "bash -c 'echo some error; exit 1'" -a contains "some error" + assert_successful_code +}