diff --git a/lib/bats-core/test_functions.bash b/lib/bats-core/test_functions.bash index e9fdeb05..1301ff34 100644 --- a/lib/bats-core/test_functions.bash +++ b/lib/bats-core/test_functions.bash @@ -29,18 +29,121 @@ load() { source "${file}" } -run() { +bats_suppress_stderr() { + "$@" 2>/dev/null +} + +bats_suppress_stdout() { + # throw away stdout and redirect stderr into stdout + # shellcheck disable=SC2069 + "$@" 2>&1 >/dev/null +} + +bats_redirect_stderr_into_file() { + "$@" 2>>"$bats_run_separate_stderr_file" # use >> to see collisions' content +} + +bats_merge_stdout_and_stderr() { + "$@" 2>&1 +} + +# write separate lines from into +bats_separate_lines() { # + output_array_name="$1" + input_var_name="$2" + if [[ $keep_empty_lines ]]; then + local bats_separate_lines_lines=() + while IFS= read -r line; do + bats_separate_lines_lines+=("$line") + done <<<"${!input_var_name}" + eval "${output_array_name}=(\"\${bats_separate_lines_lines[@]}\")" + else + # shellcheck disable=SC2034,SC2206 + IFS=$'\n' read -d '' -r -a "$output_array_name" <<<"${!input_var_name}" + fi +} + +run() { # [--keep-empty-lines] [--output merged|separate|stderr|stdout] [--] trap bats_interrupt_trap_in_run INT + local keep_empty_lines= + local output_case=merged + # parse options starting with - + while [[ $# -gt 0 && $1 == -* ]]; do + case "$1" in + --keep-empty-lines) + keep_empty_lines=1 + ;; + --output) + output_case="$2" + shift 2 # consume the value too! + ;; + --) + shift # eat the -- before breaking away + break + ;; + esac + shift + done + + local pre_command= + + case "$output_case" in + merged) # redirects stderr into stdout and fills only $output/$lines + pre_command=bats_merge_stdout_and_stderr + ;; + separate) # splits stderr into own file and fills $stderr/$stderr_lines too + local bats_run_separate_stderr_file + bats_run_separate_stderr_file="$(mktemp "${BATS_TEST_TMPDIR}/separate-stderr-XXXXXX")" + pre_command=bats_redirect_stderr_into_file + ;; + stderr) # suppresses stdout and fills $stderr/$stderr_lines + pre_command=bats_suppress_stdout + ;; + stdout) # suppresses stderr and fills $output/$lines + pre_command=bats_suppress_stderr + ;; + *) + printf "ERROR: Unknown --output value %s" "$output_case" + return 1 + ;; + esac + local origFlags="$-" set -f +eET local origIFS="$IFS" - # 'output', 'status', 'lines' are global variables available to tests. - # shellcheck disable=SC2034 - output="$("$@" 2>&1)" - # shellcheck disable=SC2034 - status="$?" - # shellcheck disable=SC2034,SC2206 - IFS=$'\n' lines=($output) + if [[ $keep_empty_lines ]]; then + # 'output', 'status', 'lines' are global variables available to tests. + # preserve trailing newlines by appending . and removing it later + # shellcheck disable=SC2034 + output="$($pre_command "$@"; status=$?; printf .; exit $status)" + # shellcheck disable=SC2034 + status="$?" + output="${output%.}" + else + # 'output', 'status', 'lines' are global variables available to tests. + # shellcheck disable=SC2034 + output="$($pre_command "$@")" + # shellcheck disable=SC2034 + status="$?" + fi + + bats_separate_lines lines output + + case "$output_case" in + stderr) + stderr="$output" + # shellcheck disable=SC2034 + stderr_lines=("${lines[@]}") + unset output + unset lines + ;; + separate) + # shellcheck disable=SC2034 + read -d '' -r stderr < "$bats_run_separate_stderr_file" + bats_separate_lines stderr_lines stderr + ;; + esac + IFS="$origIFS" set "-$origFlags" } diff --git a/test/run.bats b/test/run.bats new file mode 100644 index 00000000..b87d88a1 --- /dev/null +++ b/test/run.bats @@ -0,0 +1,76 @@ +@test "run --keep-empty-lines preserves leading empty lines" { + run --keep-empty-lines -- echo -n $'\na' + printf "'%s'\n" "${lines[@]}" + [ "${lines[0]}" == '' ] + [ "${lines[1]}" == a ] + [ ${#lines[@]} -eq 2 ] +} + +@test "run --keep-empty-lines preserves inner empty lines" { + run --keep-empty-lines -- echo -n $'a\n\nb' + printf "'%s'\n" "${lines[@]}" + [ "${lines[0]}" == a ] + [ "${lines[1]}" == '' ] + [ "${lines[2]}" == b ] + [ ${#lines[@]} -eq 3 ] +} + +@test "run --keep-empty-lines preserves trailing empty lines" { + run --keep-empty-lines -- echo -n $'a\n' + printf "'%s'\n" "${lines[@]}" + [ "${lines[0]}" == a ] + [ "${lines[1]}" == '' ] + [ ${#lines[@]} -eq 2 ] +} + +@test "run --keep-empty-lines preserves multiple trailing empty lines" { + run --keep-empty-lines -- echo -n $'a\n\n' + printf "'%s'\n" "${lines[@]}" + [ "${lines[0]}" == a ] + [ "${lines[1]}" == '' ] + [ "${lines[2]}" == '' ] + [ ${#lines[@]} -eq 3 ] +} + +@test "run --keep-empty-lines preserves non-empty trailing line" { + run --keep-empty-lines -- echo -n $'a\nb' + printf "'%s'\n" "${lines[@]}" + [ "${lines[0]}" == a ] + [ "${lines[1]}" == b ] + [ ${#lines[@]} -eq 2 ] +} + +print-stderr-stdout() { + printf stdout + printf stderr >&2 +} + +@test "run --output stdout does not print stderr" { + run --output stdout -- print-stderr-stdout + echo "output='$output' stderr='$stderr'" + [ "$output" = "stdout" ] + [ ${#lines[@]} -eq 1 ] + + [ "${stderr-notset}" = notset ] + [ ${#stderr_lines[@]} -eq 0 ] +} + +@test "run --output stderr does not print stdout" { + run --output stderr -- print-stderr-stdout + echo "output='$output' stderr='$stderr'" + [ "${output-notset}" = notset ] + [ ${#lines[@]} -eq 0 ] + + [ "$stderr" = stderr ] + [ ${#stderr_lines[@]} -eq 1 ] +} + +@test "run --output separate splits output" { + run --output separate -- print-stderr-stdout + echo "output='$output' stderr='$stderr'" + [ "$output" = stdout ] + [ ${#lines[@]} -eq 1 ] + + [ "$stderr" = stderr ] + [ ${#stderr_lines[@]} -eq 1 ] +} \ No newline at end of file