From f1aba8cfb865448e5340867e7fd57684cbaa2088 Mon Sep 17 00:00:00 2001 From: Nicola Lunghi Date: Sun, 22 Dec 2024 15:30:06 +0000 Subject: [PATCH] assert_output and assert_lines: add --use-stderr option --- src/assert_line.bash | 42 ++++++++++++++++++++++----------- src/assert_output.bash | 53 +++++++++++++++++++++++++++++++++--------- 2 files changed, 70 insertions(+), 25 deletions(-) diff --git a/src/assert_line.bash b/src/assert_line.bash index ee05bb8..6f0e19d 100644 --- a/src/assert_line.bash +++ b/src/assert_line.bash @@ -3,12 +3,13 @@ # # Summary: Fail if the expected line is not found in the output (default) or at a specific line number. # -# Usage: assert_line [-n index] [-p | -e] [--] +# Usage: assert_line [-n index] [-p | -e] [-s | --use-stderr] [--] # # Options: # -n, --index Match the th line # -p, --partial Match if `expected` is a substring of `$output` or line # -e, --regexp Treat `expected` as an extended regular expression +# -s, --use-stderr Match against `$stderr_lines` instead of `$lines` # The expected line string, substring, or regular expression # # IO: @@ -17,6 +18,8 @@ # Globals: # output # lines +# stderr +# stderr_lines # Returns: # 0 - if matching line found # 1 - otherwise @@ -131,7 +134,7 @@ assert_line() { local -i is_match_line=0 local -i is_mode_partial=0 local -i is_mode_regexp=0 - : "${lines?}" + local -i use_stderr=0 # Handle options. while (( $# > 0 )); do @@ -149,6 +152,7 @@ assert_line() { ;; -p|--partial) is_mode_partial=1; shift ;; -e|--regexp) is_mode_regexp=1; shift ;; + -s|--use-stderr) use_stderr=1; shift ;; --) shift; break ;; *) break ;; esac @@ -171,33 +175,43 @@ assert_line() { return $? fi + # Select lines array based on use_stderr. + local -n selected_lines + if (( use_stderr )); then + : "${stderr_lines?}" + selected_lines="stderr_lines" + else + : "${lines?}" + selected_lines="lines" + fi + # Matching. if (( is_match_line )); then # Specific line. if (( is_mode_regexp )); then - if ! [[ ${lines[$idx]} =~ $expected ]]; then + if ! [[ ${selected_lines[$idx]} =~ $expected ]]; then batslib_print_kv_single 6 \ 'index' "$idx" \ 'regexp' "$expected" \ - 'line' "${lines[$idx]}" \ + 'line' "${selected_lines[$idx]}" \ | batslib_decorate 'regular expression does not match line' \ | fail fi elif (( is_mode_partial )); then - if [[ ${lines[$idx]} != *"$expected"* ]]; then + if [[ ${selected_lines[$idx]} != *"$expected"* ]]; then batslib_print_kv_single 9 \ 'index' "$idx" \ 'substring' "$expected" \ - 'line' "${lines[$idx]}" \ + 'line' "${selected_lines[$idx]}" \ | batslib_decorate 'line does not contain substring' \ | fail fi else - if [[ ${lines[$idx]} != "$expected" ]]; then + if [[ ${selected_lines[$idx]} != "$expected" ]]; then batslib_print_kv_single 8 \ 'index' "$idx" \ 'expected' "$expected" \ - 'actual' "${lines[$idx]}" \ + 'actual' "${selected_lines[$idx]}" \ | batslib_decorate 'line differs' \ | fail fi @@ -206,8 +220,8 @@ assert_line() { # Contained in output. if (( is_mode_regexp )); then local -i idx - for (( idx = 0; idx < ${#lines[@]}; ++idx )); do - [[ ${lines[$idx]} =~ $expected ]] && return 0 + for (( idx = 0; idx < ${#selected_lines[@]}; ++idx )); do + [[ ${selected_lines[$idx]} =~ $expected ]] && return 0 done { local -ar single=( 'regexp' "$expected" ) local -ar may_be_multi=( 'output' "$output" ) @@ -219,8 +233,8 @@ assert_line() { | fail elif (( is_mode_partial )); then local -i idx - for (( idx = 0; idx < ${#lines[@]}; ++idx )); do - [[ ${lines[$idx]} == *"$expected"* ]] && return 0 + for (( idx = 0; idx < ${#selected_lines[@]}; ++idx )); do + [[ ${selected_lines[$idx]} == *"$expected"* ]] && return 0 done { local -ar single=( 'substring' "$expected" ) local -ar may_be_multi=( 'output' "$output" ) @@ -232,8 +246,8 @@ assert_line() { | fail else local -i idx - for (( idx = 0; idx < ${#lines[@]}; ++idx )); do - [[ ${lines[$idx]} == "$expected" ]] && return 0 + for (( idx = 0; idx < ${#selected_lines[@]}; ++idx )); do + [[ ${selected_lines[$idx]} == "$expected" ]] && return 0 done { local -ar single=( 'line' "$expected" ) local -ar may_be_multi=( 'output' "$output" ) diff --git a/src/assert_output.bash b/src/assert_output.bash index 82d0a0c..eb4e163 100644 --- a/src/assert_output.bash +++ b/src/assert_output.bash @@ -1,14 +1,15 @@ # assert_output # ============= # -# Summary: Fail if `$output' does not match the expected output. +# Summary: Fail if `$output` or `$stderr` (with --use-stderr) does not match the expected output. # -# Usage: assert_output [-p | -e] [- | [--] ] +# Usage: assert_output [-p | -e] [--use-stderr] [- | [--] ] # # Options: -# -p, --partial Match if `expected` is a substring of `$output` +# -p, --partial Match if `expected` is a substring of `$output` or `$stderr` # -e, --regexp Treat `expected` as an extended regular expression # -, --stdin Read `expected` value from STDIN +# --use-stderr Compare against `$stderr` instead of `$output` # The expected value, substring or regular expression # # IO: @@ -17,6 +18,7 @@ # error message, on error # Globals: # output +# stderr # Returns: # 0 - if output matches the expected value/partial/regexp # 1 - otherwise @@ -121,12 +123,26 @@ # output : Foobar 0.1.0 # -- # ``` +# +# ## Using --use-stderr +# +# The `--use-stderr` option directs `assert_output` to match against `$stderr` instead of `$output`. +# This is useful for verifying error messages or other content written to standard error. +# +# ```bash +# @test 'assert_output() with --use-stderr' { +# run --separate-stderr bash -c 'echo "error message" >&2' +# assert_output --use-stderr 'error message' +# } +# ``` +# +# On failure, the expected and actual stderr values are displayed. assert_output() { local -i is_mode_partial=0 local -i is_mode_regexp=0 local -i is_mode_nonempty=0 local -i use_stdin=0 - : "${output?}" + local -i use_stderr=0 # Handle options. if (( $# == 0 )); then @@ -137,12 +153,19 @@ assert_output() { case "$1" in -p|--partial) is_mode_partial=1; shift ;; -e|--regexp) is_mode_regexp=1; shift ;; + --use-stderr) use_stderr=1; shift ;; -|--stdin) use_stdin=1; shift ;; --) shift; break ;; *) break ;; esac done + if (( use_stderr )); then + : "${stderr?}" + else + : "${output?}" + fi + if (( is_mode_partial )) && (( is_mode_regexp )); then echo "\`--partial' and \`--regexp' are mutually exclusive" \ | batslib_decorate 'ERROR: assert_output' \ @@ -158,9 +181,17 @@ assert_output() { expected="${1-}" fi + # Determine the source of output (stdout or stderr). + local actual_output + if (( use_stderr )); then + actual_output="$stderr" + else + actual_output="$output" + fi + # Matching. if (( is_mode_nonempty )); then - if [ -z "$output" ]; then + if [ -z "$actual_output" ]; then echo 'expected non-empty output, but output was empty' \ | batslib_decorate 'no output' \ | fail @@ -170,26 +201,26 @@ assert_output() { echo "Invalid extended regular expression: \`$expected'" \ | batslib_decorate 'ERROR: assert_output' \ | fail - elif ! [[ $output =~ $expected ]]; then + elif ! [[ $actual_output =~ $expected ]]; then batslib_print_kv_single_or_multi 6 \ 'regexp' "$expected" \ - 'output' "$output" \ + 'output' "$actual_output" \ | batslib_decorate 'regular expression does not match output' \ | fail fi elif (( is_mode_partial )); then - if [[ $output != *"$expected"* ]]; then + if [[ $actual_output != *"$expected"* ]]; then batslib_print_kv_single_or_multi 9 \ 'substring' "$expected" \ - 'output' "$output" \ + 'output' "$actual_output" \ | batslib_decorate 'output does not contain substring' \ | fail fi else - if [[ $output != "$expected" ]]; then + if [[ $actual_output != "$expected" ]]; then batslib_print_kv_single_or_multi 8 \ 'expected' "$expected" \ - 'actual' "$output" \ + 'actual' "$actual_output" \ | batslib_decorate 'output differs' \ | fail fi