Skip to content

Commit 4246bdb

Browse files
committed
feat: add refute_stderr_line
Refactor the `refute_line` function to handle both output and stderr streams uniformly. Introduce a new helper function `__refute_stream_line` to streamline the logic and reduce code duplication. Introduce the `refute_stderr_line` function to verify that an unexpected line does not appear in the stderr output. Update documentation to reflect usage and necessary conditions for correct operation, including the requirement to use `--separate-stderr` when running commands.
1 parent 5bcb2b3 commit 4246bdb

File tree

4 files changed

+439
-28
lines changed

4 files changed

+439
-28
lines changed

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ This project provides the following functions:
4040
- [assert_line](#assert_line) / [refute_line](#refute_line) Assert a specific line of output does (or does not) contain given content.
4141
- [assert_regex](#assert_regex) / [refute_regex](#refute_regex) Assert a parameter does (or does not) match given pattern.
4242
- [assert_stderr](#assert_stderr) / [refute_stderr](#refute_stderr) Assert stderr does (or does not) contain given content.
43-
- [assert_stderr_line](#assert_stderr_line) Assert a specific line of stderr does contain given content.
43+
- [assert_stderr_line](#assert_stderr_line) / [refute_stderr_line](#refute_stderr_line) Assert a specific line of stderr does (or does not) contain given content.
4444

4545
These commands are described in more detail below.
4646

@@ -1007,6 +1007,17 @@ On failure, the same details are displayed as for literal matching, except that
10071007
--
10081008
```
10091009
1010+
### `refute_stderr_line`
1011+
1012+
> _**Note**:
1013+
> `run` has to be called with `--separate-stderr` to separate stdout and stderr into `$output` and `$stderr`.
1014+
> If not, `$stderr` will be empty, causing `refute_stderr_line` to always pass.
1015+
1016+
Similarly to `refute_stderr`, this function helps to verify that a command or function produces the correct stderr.
1017+
It checks that the unexpected line does not appear in the stderr (default) or in a specific line of it.
1018+
Matching can be literal (default), partial or regular expression.
1019+
This function is the logical complement of `assert_stderr_line`.
1020+
10101021
<!-- REFERENCES -->
10111022
10121023
[bats]: https://github.com/bats-core/bats-core

src/refute_line.bash

Lines changed: 70 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,6 @@
2626
# It checks that the unexpected line does not appear in the output (default) or at a specific line number.
2727
# Matching can be literal (default), partial or regular expression.
2828
#
29-
# *__Warning:__
30-
# Due to a [bug in Bats][bats-93], empty lines are discarded from `${lines[@]}`,
31-
# causing line indices to change and preventing testing for empty lines.*
32-
#
33-
# [bats-93]: https://github.com/sstephenson/bats/pull/93
34-
#
3529
# ## Looking for a line in the output
3630
#
3731
# By default, the entire output is searched for the unexpected line.
@@ -131,18 +125,67 @@
131125
# ```
132126
# FIXME(ztombol): Display `${lines[@]}' instead of `$output'!
133127
refute_line() {
128+
__refute_stream_line "$@"
129+
}
130+
131+
# refute_stderr_line
132+
# ==================
133+
#
134+
# Summary: Fail if the unexpected line is found in the stderr (default) or at a specific line number.
135+
#
136+
# Usage: refute_stderr_line [-n index] [-p | -e] [--] <unexpected>
137+
#
138+
# Options:
139+
# -n, --index <idx> Match the <idx>th line
140+
# -p, --partial Match if `unexpected` is a substring of `$stderr` or line <idx>
141+
# -e, --regexp Treat `unexpected` as an extended regular expression
142+
# <unexpected> The unexpected line string, substring, or regular expression.
143+
#
144+
# IO:
145+
# STDERR - details, on failure
146+
# error message, on error
147+
# Globals:
148+
# stderr
149+
# stderr_lines
150+
# Returns:
151+
# 0 - if match not found
152+
# 1 - otherwise
153+
#
154+
# Similarly to `refute_stderr`, this function verifies that a command or function does not produce the unexpected stderr.
155+
# (It is the logical complement of `assert_stderr_line`.)
156+
# It checks that the unexpected line does not appear in the stderr (default) or at a specific line number.
157+
# Matching can be literal (default), partial or regular expression.
158+
#
159+
refute_stderr_line() {
160+
__refute_stream_line "$@"
161+
}
162+
163+
__refute_stream_line() {
164+
local -r caller=${FUNCNAME[1]}
134165
local -i is_match_line=0
135166
local -i is_mode_partial=0
136167
local -i is_mode_regexp=0
137-
: "${lines?}"
168+
169+
if [[ "${caller}" == "refute_line" ]]; then
170+
: "${lines?}"
171+
local -ar stream_lines=("${lines[@]}")
172+
local -r stream_type=output
173+
elif [[ "${caller}" == "refute_stderr_line" ]]; then
174+
: "${stderr_lines?}"
175+
local -ar stream_lines=("${stderr_lines[@]}")
176+
local -r stream_type=stderr
177+
else
178+
# Coding error: unknown caller
179+
:
180+
fi
138181

139182
# Handle options.
140183
while (( $# > 0 )); do
141184
case "$1" in
142185
-n|--index)
143186
if (( $# < 2 )) || ! [[ $2 =~ ^-?([0-9]|[1-9][0-9]+)$ ]]; then
144187
echo "\`--index' requires an integer argument: \`$2'" \
145-
| batslib_decorate 'ERROR: refute_line' \
188+
| batslib_decorate "ERROR: ${caller}" \
146189
| fail
147190
return $?
148191
fi
@@ -159,7 +202,7 @@ refute_line() {
159202

160203
if (( is_mode_partial )) && (( is_mode_regexp )); then
161204
echo "\`--partial' and \`--regexp' are mutually exclusive" \
162-
| batslib_decorate 'ERROR: refute_line' \
205+
| batslib_decorate "ERROR: ${caller}" \
163206
| fail
164207
return $?
165208
fi
@@ -169,7 +212,7 @@ refute_line() {
169212

170213
if (( is_mode_regexp == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then
171214
echo "Invalid extended regular expression: \`$unexpected'" \
172-
| batslib_decorate 'ERROR: refute_line' \
215+
| batslib_decorate "ERROR: ${caller}" \
173216
| fail
174217
return $?
175218
fi
@@ -178,40 +221,40 @@ refute_line() {
178221
if (( is_match_line )); then
179222
# Specific line.
180223
if (( is_mode_regexp )); then
181-
if [[ ${lines[$idx]} =~ $unexpected ]]; then
224+
if [[ ${stream_lines[$idx]} =~ $unexpected ]]; then
182225
batslib_print_kv_single 6 \
183226
'index' "$idx" \
184227
'regexp' "$unexpected" \
185-
'line' "${lines[$idx]}" \
228+
'line' "${stream_lines[$idx]}" \
186229
| batslib_decorate 'regular expression should not match line' \
187230
| fail
188231
fi
189232
elif (( is_mode_partial )); then
190-
if [[ ${lines[$idx]} == *"$unexpected"* ]]; then
233+
if [[ ${stream_lines[$idx]} == *"$unexpected"* ]]; then
191234
batslib_print_kv_single 9 \
192235
'index' "$idx" \
193236
'substring' "$unexpected" \
194-
'line' "${lines[$idx]}" \
237+
'line' "${stream_lines[$idx]}" \
195238
| batslib_decorate 'line should not contain substring' \
196239
| fail
197240
fi
198241
else
199-
if [[ ${lines[$idx]} == "$unexpected" ]]; then
242+
if [[ ${stream_lines[$idx]} == "$unexpected" ]]; then
200243
batslib_print_kv_single 5 \
201244
'index' "$idx" \
202-
'line' "${lines[$idx]}" \
245+
'line' "${stream_lines[$idx]}" \
203246
| batslib_decorate 'line should differ' \
204247
| fail
205248
fi
206249
fi
207250
else
208-
# Line contained in output.
251+
# Line contained in output/error stream.
209252
if (( is_mode_regexp )); then
210253
local -i idx
211-
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
212-
if [[ ${lines[$idx]} =~ $unexpected ]]; then
254+
for (( idx = 0; idx < ${#stream_lines[@]}; ++idx )); do
255+
if [[ ${stream_lines[$idx]} =~ $unexpected ]]; then
213256
{ local -ar single=( 'regexp' "$unexpected" 'index' "$idx" )
214-
local -a may_be_multi=( 'output' "$output" )
257+
local -a may_be_multi=( "${stream_type}" "${!stream_type}" )
215258
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
216259
batslib_print_kv_single "$width" "${single[@]}"
217260
if batslib_is_single_line "${may_be_multi[1]}"; then
@@ -228,10 +271,10 @@ refute_line() {
228271
done
229272
elif (( is_mode_partial )); then
230273
local -i idx
231-
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
232-
if [[ ${lines[$idx]} == *"$unexpected"* ]]; then
274+
for (( idx = 0; idx < ${#stream_lines[@]}; ++idx )); do
275+
if [[ ${stream_lines[$idx]} == *"$unexpected"* ]]; then
233276
{ local -ar single=( 'substring' "$unexpected" 'index' "$idx" )
234-
local -a may_be_multi=( 'output' "$output" )
277+
local -a may_be_multi=( "${stream_type}" "${!stream_type}" )
235278
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
236279
batslib_print_kv_single "$width" "${single[@]}"
237280
if batslib_is_single_line "${may_be_multi[1]}"; then
@@ -248,10 +291,10 @@ refute_line() {
248291
done
249292
else
250293
local -i idx
251-
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
252-
if [[ ${lines[$idx]} == "$unexpected" ]]; then
294+
for (( idx = 0; idx < ${#stream_lines[@]}; ++idx )); do
295+
if [[ ${stream_lines[$idx]} == "$unexpected" ]]; then
253296
{ local -ar single=( 'line' "$unexpected" 'index' "$idx" )
254-
local -a may_be_multi=( 'output' "$output" )
297+
local -a may_be_multi=( "${stream_type}" "${!stream_type}" )
255298
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
256299
batslib_print_kv_single "$width" "${single[@]}"
257300
if batslib_is_single_line "${may_be_multi[1]}"; then
@@ -261,7 +304,7 @@ refute_line() {
261304
batslib_print_kv_multi "${may_be_multi[@]}"
262305
fi
263306
} \
264-
| batslib_decorate 'line should not be in output' \
307+
| batslib_decorate "line should not be in ${stream_type}" \
265308
| fail
266309
return $?
267310
fi

0 commit comments

Comments
 (0)