From 291d9c0062f8f2ce78869279dd9e4407bc378280 Mon Sep 17 00:00:00 2001 From: Zoltan Tombol Date: Fri, 3 Jul 2015 15:54:41 +0200 Subject: [PATCH 01/16] Rename test files to ensure testing order --- test/{bats.bats => 20-bats.bats} | 0 test/{suite.bats => 21-suite.bats} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename test/{bats.bats => 20-bats.bats} (100%) rename test/{suite.bats => 21-suite.bats} (100%) diff --git a/test/bats.bats b/test/20-bats.bats similarity index 100% rename from test/bats.bats rename to test/20-bats.bats diff --git a/test/suite.bats b/test/21-suite.bats similarity index 100% rename from test/suite.bats rename to test/21-suite.bats From e073c450e270fdd6e0dcfc5070014b040d12254d Mon Sep 17 00:00:00 2001 From: Zoltan Tombol Date: Sun, 12 Jul 2015 02:13:39 +0200 Subject: [PATCH 02/16] Add standard library --- README.md | 417 ++++++++++++++++++ install.sh | 2 + lib/bats/batslib.bash | 300 +++++++++++++ lib/bats/batslib/output.bash | 256 +++++++++++ libexec/bats | 1 + libexec/bats-exec-test | 3 + man/Makefile | 5 +- man/bats.1 | 4 +- man/bats.1.ronn | 2 +- man/bats.7 | 23 +- man/bats.7.ronn | 15 +- man/batslib.7 | 139 ++++++ man/batslib.7.ronn | 160 +++++++ test/20-bats.bats | 4 + ...50-lib-internal-output-10-batslib_err.bats | 15 + ...nternal-output-11-batslib_count_lines.bats | 21 + ...rnal-output-12-batslib_is_single_line.bats | 13 + ...batslib_get_max_single_line_key_width.bats | 17 + ...nal-output-14-batslib_print_kv_single.bats | 31 ++ ...rnal-output-15-batslib_print_kv_multi.bats | 21 + ...t-16-batslib_print_kv_single_or_multi.bats | 35 ++ ...lib-internal-output-17-batslib_prefix.bats | 39 ++ ...0-lib-internal-output-18-batslib_mark.bats | 64 +++ ...b-internal-output-19-batslib_decorate.bats | 12 + test/60-lib-assertion-10-flunk.bats | 20 + test/60-lib-assertion-11-assert.bats | 18 + test/60-lib-assertion-12-assert_equal.bats | 37 ++ test/60-lib-assertion-13-assert_output.bats | 49 ++ test/60-lib-assertion-14-assert_success.bats | 72 +++ test/60-lib-assertion-15-assert_failure.bats | 70 +++ test/60-lib-assertion-16-assert_line.bats | 66 +++ test/60-lib-assertion-17-refute_line.bats | 67 +++ 32 files changed, 1991 insertions(+), 7 deletions(-) create mode 100644 lib/bats/batslib.bash create mode 100644 lib/bats/batslib/output.bash create mode 100644 man/batslib.7 create mode 100644 man/batslib.7.ronn create mode 100755 test/50-lib-internal-output-10-batslib_err.bats create mode 100755 test/50-lib-internal-output-11-batslib_count_lines.bats create mode 100755 test/50-lib-internal-output-12-batslib_is_single_line.bats create mode 100755 test/50-lib-internal-output-13-batslib_get_max_single_line_key_width.bats create mode 100755 test/50-lib-internal-output-14-batslib_print_kv_single.bats create mode 100755 test/50-lib-internal-output-15-batslib_print_kv_multi.bats create mode 100755 test/50-lib-internal-output-16-batslib_print_kv_single_or_multi.bats create mode 100755 test/50-lib-internal-output-17-batslib_prefix.bats create mode 100755 test/50-lib-internal-output-18-batslib_mark.bats create mode 100755 test/50-lib-internal-output-19-batslib_decorate.bats create mode 100755 test/60-lib-assertion-10-flunk.bats create mode 100755 test/60-lib-assertion-11-assert.bats create mode 100755 test/60-lib-assertion-12-assert_equal.bats create mode 100755 test/60-lib-assertion-13-assert_output.bats create mode 100755 test/60-lib-assertion-14-assert_success.bats create mode 100755 test/60-lib-assertion-15-assert_failure.bats create mode 100755 test/60-lib-assertion-16-assert_line.bats create mode 100755 test/60-lib-assertion-17-refute_line.bats diff --git a/README.md b/README.md index 235bf1ee..84e8510d 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,10 @@ the first line: } ``` +Bats, as part of its [Standard Library](#standard-library), provides +[assertion](#assertions) type helper functions that perform a test and +output useful details when the test fails. + ### `load`: Share common code You may want to share common code across multiple test files. Bats @@ -201,6 +205,419 @@ case. in the test file. * `$BATS_TMPDIR` is the location to a directory that may be used to store temporary files. +* `$BATS_LIB` is the directory in which the standard libraries are +located. + + +## Standard library + +The Standard Library is a collection of test helpers intended to +simplify testing. It helps building test suits that provide relevant +information when a test fails to speed up debugging. + +It contains the following types of test helpers. + +* [**Assertions**](#assertions) are functions that perform a test and + output relevant information on failure to help debugging. + +Test helpers send all output to the standard error, making them usable +outside of `@test` functions too. Output is [formatted](#output) for +readability. + +The Standard Library is automatically loaded and available in all test +files. + +### Output + +On failure, in addition to the usual output generated by Bats, +[**Assertions**](#assertions) display information relevant to the failed +test to help debugging. The output is formatted for readability and +displayed as key-value pairs on the standard error. + +When the value is one line long, the pair is displayed in a columnar +fashion called ***two-column*** format. + +``` +-- output differs -- +expected : want +actual : have +-- +``` + +When the value is longer than one line, the number of lines in the value +is displayed after the key, and the value starts on the next line and is +indented by two spaces for added readability. This is called +***multi-line*** format. + +For convenience, sometimes related values are also displayed in this +format even if they are only one line long. + +``` +-- output differs -- +expected (1 lines): + want +actual (3 lines): + have 1 + have 2 + have 3 +-- +``` + +### Assertions + +Assertions are functions that perform a test and output relevant +information on failure to help debugging. They return `1` on failure and +`0` otherwise. + +Assertions about exit code and output operate on the results of the most +recent invocation of `run`. + +#### `flunk` + +Display an error message and fail. This function provides a convenient +way to report failure in arbitrary situations. You can use it to +implement your own helpers when the ones available do not meet your +needs. Other functions use it internally as well. + +```bash +@test 'flunk()' { + flunk 'this test always fails' +} +``` + +The message can also be specified on the standard input. + +```bash +@test 'flunk() with pipe' { + echo 'this test always fails' | flunk +} +``` + +This function always fails and simply outputs the given message. + +``` +this test always fails +``` + +#### `assert` + +Fail if the given expression evaluates to false. + +```bash +@test 'assert()' { + local value=1 + assert [ "$value" -eq 0 ] +} +``` + +On failure the failed expression is displayed. + +``` +-- assertion failed -- +condition : [ 1 -eq 0 ] +-- +``` + +***Note:*** *The expression can be a simple command only. [Compund +commands] (https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands), +such as `[[,` are not supported.* + +#### `assert_equal` + +Fail if the two parameters, expected and actual value respectively, do +not equal. + +```bash +@test 'assert_equal()' { + local expected='want' + local actual='have' + assert_equal "$expected" "$actual" +} +``` + +On failure the expected and actual values are displayed. + +``` +-- values do not equal -- +expected : want +actual : have +-- +``` + +If either value is longer than one line both are displayed in +*multi-line* format. + +#### `assert_output` + +Fail if `$output` does not equal the expected output. + +```bash +@test 'assert_output()' { + run echo 'have' + local expected='want' + assert_output "$expected" +} +``` + +The expected output can also be specified on the standard input. + +```bash +@test 'assert_output() with pipe' { + run echo 'have' + local expected='want' + echo "$expected" | assert_output +} +``` + +On failure the expected and actual outputs are displayed. + +``` +-- output differs -- +expected : want +actual : have +-- +``` + +If either value is longer than one line both are displayed in +*multi-line* format. + +#### `assert_success` + +This function tests `$status` and additionally `$output` depending on +the number of parameters. + +##### `$status` test + +When no parameters are specified, fail if `$status` is not zero. + +```bash +@test 'assert_success() status only' { + run bash -c "echo 'have'; exit 1" + assert_success +} +``` + +On failure `$status` and `$output` are displayed. + +``` +-- command failed -- +status : 1 +output : have +-- +``` + +If `$output` is longer than one line, it is displayed in *multi-line* +format. + +##### Optional `$output` test + +Additionally, if one parameter is specified and the status test passed, +`$output` is compared against the parameter as expected output. + +```bash +@test 'assert_success() status and output' { + run echo 'have' + local expected='want' + assert_success "$expected" +} +``` + +If the output test fails, the expected and actual outputs are displayed. + +``` +-- command succeeded, but output differs -- +expected : want +actual : have +-- +``` + +If either value is longer than one line both are displayed in +*multi-line* format. + +If the status test fails, the output test is not performed and the +output is identical to the status-only form's. + +#### `assert_failure` + +This function tests `$status` and additionally `$output` depending on +the number of parameters. + +##### `$status` test + +When no parameters are specified, fail if `$status` is zero. + +```bash +@test 'assert_failure() status only' { + run echo 'have' + assert_failure +} +``` + +On failure `$output` is displayed. + +``` +-- command succeeded, but it was expected to fail -- +output : have +-- +``` + +If `$output` is longer than one line, it is displayed in *multi-line* +format. + +##### Optional `$output` test + +Additionally, if one parameter is specified and the status test passed, +`$output` is compared against the parameter as expected output. + +```bash +@test 'assert_failure() status and output' { + run bash -c "echo 'have'; exit 1" + local expected='want' + assert_failure "$expected" +} +``` + +If the output test fails, the expected and actual outputs are displayed. + +``` +-- command failed as expected, but output differs -- +expected : want +actual : have +-- +``` + +If either value is longer than one line both are displayed in +*multi-line* format. + +If the status test fails, the output test is not performed and the +output is identical to the status-only form's. + +#### `assert_line` + +Depending on the number of parameters, this function tests either that +the output contains the expected line or that the expected line appears +in a specific line of the output identified by its index. + +This function is the opposite of `refute_line()`. + +***Note:*** *Due to a [bug in Bats][bats-93], empty lines are discarded, +causing line indices to change and preventing testing for empty lines.* + +##### Entire output + +When one parameter is specified, fail if `${lines[@]}` does not contain +the line specified by the parameter. + +```bash +@test 'assert_line() in entire output' { + run echo $'have 1\nhave 2\nhave 3' + assert_line 'want' +} +``` + +On failure `$output` and the expected line are displayed. + +``` +-- line is not in output -- +line : want +output (3 lines): + have 1 + have 2 + have 3 +-- +``` + +If `$output` is not longer than one line, it is displayed in +*two-column* format. + +##### Specific line + +When two parameters are specified, zero-based line index and expected +line respectively, fail if the output line identified by its index in +`${lines[@]}` does not equal the expected line. + +```bash +@test 'assert_line() in specific line' { + run echo $'have 1\nhave 2\nhave 3' + assert_line 1 'want' +} +``` + +On failure the index, and the expected and actual lines are displayed. + +``` +-- line differs -- +index : 1 +expected : want +actual : have 2 +-- +``` + +#### `refute_line` + +Depending on the number of parameters, this function tests either that +the output does not contain the unexpected line or that the unexpected +line does not appear in a specific line of the output identified by its +index. + +This function is the opposite of `assert_line()`. + +***Note:*** *Due to a [bug in Bats][bats-93], empty lines are discarded, +causing line indices to change and preventing testing for empty lines.* + + +##### Entire output + +When one parameter is specified, fail if `${lines[@]}` contains the line +specified by the parameter. + +```bash +@test 'refute_line() in entire output' { + run echo $'have 1\nwant\nhave 2' + refute_line 'want' +} +``` + +On failure the unexpected line, its zero-based index, and `$output` with +the unexpected line highlighted are displayed. + +``` +-- line should not be in output -- +line : want +index : 1 +output (3 lines): + have 1 +> want + have 2 +-- +``` + +If `$output` is not longer than one line, it is displayed in +*two-column* format. + +##### Specific line + +When two parameters are specified, zero-based line index and unexpected +line respectively, fail if the output line identified by its index in +`${lines[@]}` equals the unexpected line. + +```bash +@test 'refute_line() in specific line' { + run echo $'have 1\nwant\nhave 2' + refute_line 1 'want' +} +``` + +On failure the unexpected line and its index are displayed. + +``` +-- line should differ from expected -- +index : 1 +line : want +-- +``` + +[bats-93]: https://github.com/sstephenson/bats/pull/93 ## Installing Bats from source diff --git a/install.sh b/install.sh index 8bbdd16b..c10dc76d 100755 --- a/install.sh +++ b/install.sh @@ -31,7 +31,9 @@ BATS_ROOT="$(abs_dirname "$0")" mkdir -p "$PREFIX"/{bin,libexec,share/man/man{1,7}} cp -R "$BATS_ROOT"/bin/* "$PREFIX"/bin cp -R "$BATS_ROOT"/libexec/* "$PREFIX"/libexec +cp -R "$BATS_ROOT"/lib/* "$PREFIX"/lib cp "$BATS_ROOT"/man/bats.1 "$PREFIX"/share/man/man1 cp "$BATS_ROOT"/man/bats.7 "$PREFIX"/share/man/man7 +cp "$BATS_ROOT"/man/batslib.7 "$PREFIX"/share/man/man7 echo "Installed Bats to $PREFIX/bin/bats" diff --git a/lib/bats/batslib.bash b/lib/bats/batslib.bash new file mode 100644 index 00000000..fee8074d --- /dev/null +++ b/lib/bats/batslib.bash @@ -0,0 +1,300 @@ +# +# batslib.bash +# ------------ +# +# The Standard Library is a collection of test helpers intended to +# simplify testing. It contains the following types of test helpers. +# +# - Assertions are functions that perform a test and output relevant +# information on failure to help debugging. They return 1 on failure +# and 0 otherwise. +# +# All output is formatted for readability using the functions of +# `output.bash' and sent to the standard error. +# + +source "${BATS_LIB}/batslib/output.bash" + + +######################################################################## +# ASSERTIONS +######################################################################## + +# Fail and display an error message. The message is specified either by +# positional parameters or on the standard input (by piping or +# redirection). +# +# Globals: +# none +# Arguments: +# $@ - [opt = STDIN] message to display +# Returns: +# 1 - always +# Inputs: +# STDIN - [opt = $@] message to display +# Outputs: +# STDERR - error message +flunk() { + (( $# == 0 )) && batslib_err || batslib_err "$@" + return 1 +} + +# Fail and display the given condition if it evaluates to false. Only +# simple commands can be used in the expression given to `assert'. +# Compound commands, such as `[[', are not supported. +# +# Globals: +# none +# Arguments: +# $@ - condition to evaluate +# Returns: +# 0 - condition evaluated to TRUE +# 1 - condition evaluated to FALSE +# Outputs: +# STDERR - failed condition, on failure +assert() { + if ! "$@"; then + echo "condition : $@" | batslib_decorate 'assertion failed' | flunk + fi +} + +# Fail and display an error message if the two parameters, expected and +# actual value respectively, do not equal. The error message contains +# both parameters. +# +# Globals: +# none +# Arguments: +# $1 - expected value +# $2 - actual value +# Returns: +# 0 - expected equals actual value +# 1 - otherwise +# Outputs: +# STDERR - expected and actual value, on failure +assert_equal() { + if [[ $1 != "$2" ]]; then + batslib_print_kv_single_or_multi 8 \ + 'expected' "$1" \ + 'actual' "$2" \ + | batslib_decorate 'values do not equal' \ + | flunk + fi +} + +# Fail and display an error message if `$output' does not equal the +# expected output as specified either by the first positional parameter +# or on the standard input (by piping or redirection). The error message +# contains the expected and the actual output. +# +# Globals: +# output +# Arguments: +# $1 - [opt = STDIN] expected output +# Returns: +# 0 - expected equals actual output +# 1 - otherwise +# Inputs: +# STDIN - [opt = $1] expected output +# Outputs: +# STDERR - expected and actual output, on failure +assert_output() { + local expected + (( $# == 0 )) && expected="$(cat -)" || expected="$1" + if [[ $expected != "$output" ]]; then + batslib_print_kv_single_or_multi 8 \ + 'expected' "$expected" \ + 'actual' "$output" \ + | batslib_decorate 'output differs' \ + | flunk + fi +} + +# Fail and display an error message if `$status' is not zero. The error +# message contains `$status' and `$output'. +# +# Optionally, if the status check passed, `$output' can be tested +# against the first positional parameter as expected output. If the +# output check fails the error message contains the expected and the +# actual output. +# +# Globals: +# status +# output +# Arguments: +# $1 - [opt] expected output +# Returns: +# 0 - status and, optionally, output check passed +# 1 - otherwise +# Outputs: +# STDERR - `$status' and `$output', if status check fails +# expected and actual output, if output check fails +assert_success() { + (( $# > 0 )) && local -r expected="$1" + if (( status != 0 )); then + { local -ir width=6 + batslib_print_kv_single "$width" 'status' "$status" + batslib_print_kv_single_or_multi "$width" 'output' "$output" + } | batslib_decorate 'command failed' \ + | flunk + elif (( $# > 0 )) && [[ $output != "$1" ]]; then + batslib_print_kv_single_or_multi 8 \ + 'expected' "$expected" \ + 'actual' "$output" \ + | batslib_decorate 'command succeeded, but output differs' \ + | flunk + fi +} + +# Fail and display `$output' if `$status' is zero. +# +# Optionally, if the status check passed, `$output' can be tested +# against the first positional parameter as expected output. If the +# output check fails the error message contains the expected and the +# actual output. +# +# Globals: +# status +# output +# Arguments: +# $1 - [opt] expected output +# Returns: +# 0 - status and, optionally, output check passed +# 1 - otherwise +# Outputs: +# STDERR - `$output', if status check fails +# expected and actual output, if output check fails +assert_failure() { + if (( status == 0 )); then + batslib_print_kv_single_or_multi 6 'output' "$output" \ + | batslib_decorate 'command succeeded, but it was expected to fail' \ + | flunk + elif (( $# > 0 )) && [[ $output != "$1" ]]; then + batslib_print_kv_single_or_multi 8 \ + 'expected' "$1" \ + 'actual' "$output" \ + | batslib_decorate 'command failed as expected, but output differs' \ + | flunk + fi +} + +# Fail and display an error message if `${lines[@]}' does not contain +# the expected line. The error message contains the expected line and +# `$output'. +# +# Optionally, if two positional parameters are specified, the expected +# line is only sought in the line whose index is given in the first +# parameter. In this case, the error message contains the line index, +# and the expected and actual line at the given index. +# +# Globals: +# lines +# output +# Arguments: +# $1 - [opt] zero-based index of line to match against +# $2 - line to look for +# Returns: +# 0 - line found +# 1 - otherwise +# Outputs: +# STDERR - expected line and `$output', on failure +# index, expected and actual line at index, on failure +assert_line() { + if (( $# > 1 )); then + local -ir idx="$1" + local -r line="$2" + + if [[ ${lines[$idx]} != "$line" ]]; then + batslib_print_kv_single 8 \ + 'index' "$idx" \ + 'expected' "$line" \ + 'actual' "${lines[$idx]}" \ + | batslib_decorate 'line differs' \ + | flunk + fi + else + local -r line="$1" + local temp_line + + for temp_line in "${lines[@]}"; do + [[ $temp_line == "$line" ]] && return 0 + done + { local -ar single=( + 'line' "$line" + ) + local -ar may_be_multi=( + 'output' "$output" + ) + local -ir width="$( batslib_get_max_single_line_key_width \ + "${single[@]}" "${may_be_multi[@]}" )" + batslib_print_kv_single "$width" "${single[@]}" + batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}" + } | batslib_decorate 'line is not in output' \ + | flunk + fi +} + +# Fail and display an error message if `${lines[@]}' contains the given +# line. The error message contains the unexpected line, its index in +# `$output', and `$output'. +# +# Optionally, if two positional parameters are specified, the unexpected +# line is only sought in the line whose index is given in the first +# parameter. In this case, the error message contains the line index, +# and the unexpected line. +# +# Globals: +# lines +# output +# Arguments: +# $1 - [opt] zero-based index of line to match against +# $2 - line to look for +# Returns: +# 0 - line not found +# 1 - otherwise +# Outputs: +# STDERR - unexpected line, its index and `$output', on failure +# index and unexpected line, on failure +refute_line() { + if (( $# > 1 )); then + local -ir idx="$1" + local -r line="$2" + + if [[ ${lines[$idx]} == "$line" ]]; then + batslib_print_kv_single 5 \ + 'index' "$idx" \ + 'line' "$line" \ + | batslib_decorate 'line should differ from expected' \ + | flunk + fi + else + local -r line="$1" + + local idx + for (( idx = 0; idx < ${#lines[@]}; ++idx )); do + if [[ ${lines[$idx]} == "$line" ]]; then + { local -ar single=( + 'line' "$line" + 'index' "$idx" + ) + local -a may_be_multi=( + 'output' "$output" + ) + local -ir width="$( batslib_get_max_single_line_key_width \ + "${single[@]}" "${may_be_multi[@]}" )" + batslib_print_kv_single "$width" "${single[@]}" + if batslib_is_single_line "${may_be_multi[1]}"; then + batslib_print_kv_single "$width" "${may_be_multi[@]}" + else + may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" \ + | batslib_prefix \ + | batslib_mark '>' "$idx" )" + batslib_print_kv_multi "${may_be_multi[@]}" + fi + } | batslib_decorate 'line should not be in output' \ + | flunk + return 1 + fi + done + fi +} diff --git a/lib/bats/batslib/output.bash b/lib/bats/batslib/output.bash new file mode 100644 index 00000000..95cef47d --- /dev/null +++ b/lib/bats/batslib/output.bash @@ -0,0 +1,256 @@ +# +# output.bash +# ----------- +# +# Private functions implementing output formatting. Used by public +# helper functions. +# + +# Print an error message to the standard error. The message is specified +# either by positional parameters or on the standard input (by piping or +# redirection). +# +# Globals: +# none +# Arguments: +# $@ - [opt = STDIN] message to print +# Returns: +# none +# Inputs: +# STDIN - [opt = $@] message to print +# Outputs: +# STDERR - error message +batslib_err() { + { if (( $# > 0 )); then + echo "$@" + else + cat - + fi + } >&2 +} + +# Counts the number of lines in the given text. +# +# TODO(ztombol): Remove this notice and fix tests after Bats merged #93! +# NOTE: Due to a bug in Bats, `batslib_count_lines "$output"' does not +# give the same result as `${#lines[@]}' when the output contains +# empty lines. +# See PR #93 (https://github.com/sstephenson/bats/pull/93). +# +# Globals: +# none +# Arguments: +# $1 - text +# Returns: +# none +# Outputs: +# STDOUT - number of lines in given text +batslib_count_lines() { + local -i n_lines=0 + local line + while IFS='' read -r line || [[ -n $line ]]; do + (( ++n_lines )) + done < <(printf '%s' "$1") + echo "$n_lines" +} + +# Determine whether all parameters are single-line strings. +# +# Globals: +# none +# Arguments: +# $@ - strings to test +# Returns: +# 0 - all parameters are single-line strings +# 1 - otherwise +batslib_is_single_line() { + for string in "$@"; do + (( $(batslib_count_lines "$string") > 1 )) && return 1 + done + return 0 +} + +# Determine the length of the longest key that has a single-line value. +# Useful in determining the column width for printing key-value pairs in +# a two-column format when some keys may have multi-line values and thus +# should be excluded. +# +# Globals: +# none +# Arguments: +# $@ - strings to test +# Returns: +# none +# Outputs: +# STDOUT - length of longest key +batslib_get_max_single_line_key_width() { + local -i max_len=-1 + while (( $# != 0 )); do + local -i key_len="${#1}" + batslib_is_single_line "$2" && (( key_len > max_len )) && max_len="$key_len" + shift 2 + done + echo "$max_len" +} + +# Print key-value pairs in two-column format. The first column contains +# the keys. Its width is specified with the first positional parameter, +# usually acquired using `batslib_get_max_single_line_key_width()', to +# nicely line up the values in the second column. The rest of the +# parameters are used to supply the key-value pairs. Every even-numbered +# parameter is a key and the following parameter is its value. +# +# Globals: +# none +# Arguments: +# $1 - column width +# $even - key +# $odd - value of the previous key +# Returns: +# none +# Outputs: +# STDOUT - key-value pairs in two-column format +batslib_print_kv_single() { + local -ir col_width="$1"; shift + while (( $# != 0 )); do + printf '%-*s : %s\n' "$col_width" "$1" "$2" + shift 2 + done +} + +# Print key-value pairs in multi-line format. First, the key and the +# number of lines in the value is printed. Next, the value on a separate +# line. Every odd-numbered parameter is a key and the following +# parameters is its value. +# +# Globals: +# none +# Arguments: +# $odd - key +# $even - value of the previous key +# Returns: +# none +# Outputs: +# STDOUT - key-value pairs in multi-line format +batslib_print_kv_multi() { + while (( $# != 0 )); do + printf '%s (%d lines):\n' "$1" "$( batslib_count_lines "$2" )" + printf '%s\n' "$2" + shift 2 + done +} + +# Print all key-value pairs in either two-column or multi-line format. +# If all values are one line long, all pairs are printed in two-column +# format using `batslib_print_kv_single()'. Otherwise, they are printed +# in multi-line format using `batslib_print_kv_multi()' after each line +# of all values being prefixed with two spaces. +# +# Globals: +# none +# Arguments: +# $1 - column width for two-column format +# $even - key +# $odd - value of the previous key +# Returns: +# none +# Outputs: +# STDOUT - key-value pairs in two-column format, if all values are +# single-line +# key-value pairs in multi-line format, otherwise +batslib_print_kv_single_or_multi() { + local -ir width="$1"; shift + local -a pairs=( "$@" ) + + local -a values=() + local -i i + for (( i=1; i < ${#pairs[@]}; i+=2 )); do + values+=( "${pairs[$i]}" ) + done + + if batslib_is_single_line "${values[@]}"; then + batslib_print_kv_single "$width" "${pairs[@]}" + else + local -i i + for (( i=1; i < ${#pairs[@]}; i+=2 )); do + pairs[$i]="$( batslib_prefix < <(printf '%s' "${pairs[$i]}") )" + done + batslib_print_kv_multi "${pairs[@]}" + fi +} + +# Prefix each line of the input with an arbitrary string. +# +# Globals: +# none +# Arguments: +# $1 - [opt = ' '] prefix string +# Returns: +# none +# Inputs: +# STDIN - lines to prefix +# Outputs: +# STDOUT - prefixed lines +batslib_prefix() { + local -r prefix="${1:- }" + local line + while IFS='' read -r line || [[ -n $line ]]; do + printf '%s%s\n' "$prefix" "$line" + done +} + +# Mark select lines of the input by overwriting their first few +# characters with an arbitrary string. Usually, the input is indented by +# spaces first using `batslib_prefix()'. +# +# Globals: +# none +# Arguments: +# $1 - marking string +# $@ - zero-based indices of lines to mark +# Returns: +# none +# Inputs: +# STDIN - lines to work on +# Outputs: +# STDOUT - lines after marking +batslib_mark() { + local -r symbol="$1"; shift + # Sort line numbers. + set -- $( sort -nu <<< "$( printf '%d\n' "$@" )" ) + + local line + local -i idx=0 + while IFS='' read -r line || [[ -n $line ]]; do + if (( ${1:--1} == idx )); then + printf '%s\n' "${symbol}${line:${#symbol}}" + shift + else + printf '%s\n' "$line" + fi + (( ++idx )) + done +} + +# Enclose the input in header and footer lines. The header contains an +# arbitrary title specified with the first positional parameter. The +# output is preceded and followed by an additional newline to make it +# stand out more. +# +# Globals: +# none +# Arguments: +# $1 - title +# Returns: +# none +# Inputs: +# STDIN - text to enclose +# Outputs: +# STDOUT - enclosed text +batslib_decorate() { + echo + echo "-- $1 --" + cat - + echo '--' + echo +} diff --git a/libexec/bats b/libexec/bats index 71f392f7..f8a40cef 100755 --- a/libexec/bats +++ b/libexec/bats @@ -54,6 +54,7 @@ expand_path() { BATS_LIBEXEC="$(abs_dirname "$0")" export BATS_PREFIX="$(abs_dirname "$BATS_LIBEXEC")" +export BATS_LIB="${BATS_PREFIX}/lib/bats" export BATS_CWD="$(abs_dirname .)" export PATH="$BATS_LIBEXEC:$PATH" diff --git a/libexec/bats-exec-test b/libexec/bats-exec-test index 8f3bd510..452dfbe5 100755 --- a/libexec/bats-exec-test +++ b/libexec/bats-exec-test @@ -331,6 +331,9 @@ bats_evaluate_preprocessed_source() { exec 3<&1 +# Load Standard Library. +load "${BATS_LIB}/batslib.bash" + if [ "$#" -eq 0 ]; then bats_preprocess_source bats_evaluate_preprocessed_source diff --git a/man/Makefile b/man/Makefile index b3a44bdb..8d60eb69 100644 --- a/man/Makefile +++ b/man/Makefile @@ -1,5 +1,5 @@ RONN := ronn -PAGES := bats.1 bats.7 +PAGES := bats.1 bats.7 batslib.7 all: $(PAGES) @@ -8,3 +8,6 @@ bats.1: bats.1.ronn bats.7: bats.7.ronn $(RONN) -r $< + +batslib.7: batslib.7.ronn + $(RONN) -r $< diff --git a/man/bats.1 b/man/bats.1 index 82f73016..83c08009 100644 --- a/man/bats.1 +++ b/man/bats.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BATS" "1" "August 2014" "" "" +.TH "BATS" "1" "August 2015" "" "" . .SH "NAME" \fBbats\fR \- Bash Automated Testing System @@ -92,7 +92,7 @@ The \fBbats\fR interpreter exits with a value of \fB0\fR if all test cases pass, Bats wiki: \fIhttps://github\.com/sstephenson/bats/wiki/\fR . .P -\fBbash\fR(1), \fBbats\fR(7) +\fBbash\fR(1), \fBbats\fR(7), \fBbatslib\fR(7) . .SH "COPYRIGHT" (c) 2014 Sam Stephenson diff --git a/man/bats.1.ronn b/man/bats.1.ronn index bd8f45b5..beb773ae 100644 --- a/man/bats.1.ronn +++ b/man/bats.1.ronn @@ -95,7 +95,7 @@ SEE ALSO Bats wiki: _https://github.com/sstephenson/bats/wiki/_ -`bash`(1), `bats`(7) +`bash`(1), `bats`(7), `batslib`(7) COPYRIGHT diff --git a/man/bats.7 b/man/bats.7 index d0836e52..43970c06 100644 --- a/man/bats.7 +++ b/man/bats.7 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BATS" "7" "November 2013" "" "" +.TH "BATS" "7" "August 2015" "" "" . .SH "NAME" \fBbats\fR \- Bats test file format @@ -88,6 +88,25 @@ load test_helper .P will source the script \fBtest/test_helper\.bash\fR in your test file\. This can be useful for sharing functions to set up your environment or load fixtures\. . +.P +THE LOAD_STD COMMAND +. +.P +Common test helpers are compiled into libraries and included in the \fIStandard Library\fR for convenience\. To load a library use the \fBload_std\fR command\. For example, the command below loads the \fICore\fR library in your test file\. +. +.IP "" 4 +. +.nf + +load_std \'core\' +. +.fi +. +.IP "" 0 +. +.P +See the documentation of the Standard Library for the available libraries and the test helpers they provide\. +. .SH "THE SKIP COMMAND" Tests can be skipped by using the \fBskip\fR command at the point in a test you wish to skip\. . @@ -175,4 +194,4 @@ There are several global variables you can use to introspect on Bats tests: .IP "" 0 . .SH "SEE ALSO" -\fBbash\fR(1), \fBbats\fR(1) +\fBbash\fR(1), \fBbats\fR(1), \fBbatslib\fR(7) diff --git a/man/bats.7.ronn b/man/bats.7.ronn index 7f6dd184..aa2fcbda 100644 --- a/man/bats.7.ronn +++ b/man/bats.7.ronn @@ -78,6 +78,19 @@ can be useful for sharing functions to set up your environment or load fixtures. +THE LOAD_STD COMMAND + +Common test helpers are compiled into libraries and included in the +*Standard Library* for convenience. To load a library use the `load_std` +command. For example, the command below loads the *Core* library in your +test file. + + load_std 'core' + +See the documentation of the Standard Library for the available +libraries and the test helpers they provide. + + THE SKIP COMMAND ---------------- @@ -153,4 +166,4 @@ store temporary files. SEE ALSO -------- -`bash`(1), `bats`(1) +`bash`(1), `bats`(1), `batslib`(7) diff --git a/man/batslib.7 b/man/batslib.7 new file mode 100644 index 00000000..4572bc44 --- /dev/null +++ b/man/batslib.7 @@ -0,0 +1,139 @@ +.\" generated with Ronn/v0.7.3 +.\" http://github.com/rtomayko/ronn/tree/0.7.3 +. +.TH "BATSLIB" "7" "August 2015" "" "" +. +.SH "NAME" +\fBbatslib\fR \- Bats Standard Library of Test Helpers +. +.SH "DESCRIPTION" +The Standard Library is a collection of test helpers intended to simplify testing\. It helps building test suits that provide relevant information when a test fails to speed up debugging\. +. +.P +It contains the following types of test helpers\. +. +.IP "\(bu" 4 +\fIASSERTIONS\fR are functions that perform a test and output relevant information on failure to help debugging\. +. +.IP "" 0 +. +.P +Test helpers send all output to the standard error, making them usable outside of \fB@test\fR functions too\. Output is formatted for readability as described in \fIOUTPUT\fR\. +. +.P +The Standard Library is automatically loaded and available in all test files\. +. +.SH "OUTPUT" +On failure, in addition to the usual output generated by Bats, \fIASSERTIONS\fR display information relevant to the failed test to help debugging\. The output is formatted for readability and displayed as key\-value pairs on the standard error\. +. +.P +When the value is one line long, the pair is displayed in a columnar fashion called \fBtwo\-column\fR format\. +. +.IP "" 4 +. +.nf + +\-\- output differs \-\- +expected : want +actual : have +\-\- +. +.fi +. +.IP "" 0 +. +.P +When the value is longer than one line, the number of lines in the value is displayed after the key, and the value starts on the next line and is indented by two spaces for added readability\. This is called \fBmulti\-line\fR format\. +. +.P +For convenience, sometimes related values are also displayed in this format even if they are only one line long\. +. +.IP "" 4 +. +.nf + +\-\- output differs \-\- +expected (1 lines): + want +actual (3 lines): + have 1 + have 2 + have 3 +\-\- +. +.fi +. +.IP "" 0 +. +.SH "ASSERTIONS" +Assertions are functions that perform a test and output relevant information on failure to help debugging\. They return 1 on failure and 0 otherwise\. +. +.P +Assertions about exit code and output operate on the results of the most recent invocation of \fBrun\fR\. +. +.TP +\fBflunk\fR [\fIMESSAGE\fR] +Display \fIMESSAGE\fR and fail\. This function provides a convenient way to report various failures\. Other test helpers also use it to display output on failure\. When no parameters are specified, \fIMESSAGE\fR is read from the standard input\. +. +.TP +\fBassert\fR \fIEXPRESSION\fR +Fail if \fIEXPRESSION\fR evaluates to false\. On failure, \fIEXPRESSION\fR is displayed\. +. +.IP +\fBNote:\fR \fIEXPRESSION\fR can only be a simple command\. Compound commands, such as \fB[[\fR, are not supported\. +. +.TP +\fBassert_equal\fR \fIEXPECTED\fR \fIACTUAL\fR +Fail if \fIEXPECTED\fR and \fIACTUAL\fR do not equal\. On failure both values are displayed\. If either one is longer than one line, both are displayed in \fImulit\-line\fR format\. +. +.TP +\fBassert_output\fR [\fIEXPECTED\fR] +Fail if \fIEXPECTED\fR does not equal \fB$output\fR\. On failure both values are displayed\. If either one is longer than one line, both are displayed in \fImulit\-line\fR format\. When no parameters are specified, \fIEXPECTED\fR is read from the standard input\. +. +.TP +\fBassert_success\fR [\fIEXPECTED\fR] +When no parameters are specified, fail if \fB$status\fR is not \fB0\fR\. On failure \fB$status\fR and \fB$output\fR are displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulit\-line\fR format\. +. +.IP +When \fIEXPECTED\fR is specified and \fB$status\fR is \fB0\fR, fail if \fIEXPECTED\fR does not equal \fB$output\fR\. On failure both values are displayed\. If either one is longer than one line, both are displayed in \fImulti\-line\fR format\. +. +.TP +\fBassert_failure\fR [\fIEXPECTED\fR] +When no parameters are specified, fail if \fB$status\fR is \fB0\fR\. On failure \fB$output\fR is displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulit\-line\fR format\. +. +.IP +When \fIEXPECTED\fR is specified and \fB$status\fR is not \fB0\fR, fail if \fIEXPECTED\fR does not equal \fB$output\fR\. On failure both values are displayed\. If either one is longer than one line, both are displayed in \fImulti\-line\fR format\. +. +.TP +\fBassert_line\fR [\fIINDEX\fR] \fILINE\fR +When only \fILINE\fR is specified, fail if \fB${lines[@]}\fR does not contain \fILINE\fR\. On failure \fILINE\fR and \fB$output\fR are displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulit\-line\fR format\. +. +.IP +When \fIINDEX\fR is specified, fail if \fILINE\fR does not equal \fB${lines[INDEX]}\fR\. On failure \fIINDEX\fR, \fILINE\fR and \fB${lines[INDEX]}\fR are dispalyed\. +. +.IP +\fBNOTE:\fR Due to a bug in Bats, empty lines are discarded, causing line indices to change and preventing testing for empty lines\. See \fIBUGS\fR for more\. +. +.TP +\fBrefute_line\fR [\fIINDEX\fR] \fILINE\fR +When only \fILINE\fR is specified, fail if \fB${lines[@]}\fR contains \fILINE\fR\. On failure \fILINE\fR, its zero\-based index in \fB${lines[@]}\fR, and \fB$output\fR are dispalyed\. If \fB$output\fR is longer than one line, it is displayed in \fImulit\-line\fR format with \fILINE\fR highlighted\. +. +.IP +When \fIINDEX\fR is specified, fail if \fILINE\fR equals \fB${lines[INDEX]}\fR\. On failure \fILINE\fR and \fIINDEX\fR are dispalyed\. +. +.IP +\fBNOTE:\fR Due to a bug in Bats, empty lines are discarded, causing line indices to change and preventing testing for empty lines\. See \fIBUGS\fR for more\. +. +.SH "BUGS" +. +.SS "Report bugs" +Report bugs on Bats\' GitHub issue tracker at \fIhttps://github\.com/sstephenson/bats/issues\fR\. +. +.SS "Known bugs" +Due to a bug in Bats, empty lines are missing from \fB${lines[@]}\fR, causing line indices to change and preventing testing for empty lines when using \fBassert_line\fR and \fBrefute_line\fR\. See PR #93 on Github at \fIhttps://github\.com/sstephenson/bats/pull/93\fR\. +. +.SH "COPYRIGHT" +TODO(ztombol): Find a suitable license\. +. +.SH "SEE ALSO" +\fBbash\fR(1), \fBbats\fR(1), \fBbats\fR(7) diff --git a/man/batslib.7.ronn b/man/batslib.7.ronn new file mode 100644 index 00000000..7f532d45 --- /dev/null +++ b/man/batslib.7.ronn @@ -0,0 +1,160 @@ +batslib(7) -- Bats Standard Library of Test Helpers +=================================================== + + +## DESCRIPTION + +The Standard Library is a collection of test helpers intended to +simplify testing. It helps building test suits that provide relevant +information when a test fails to speed up debugging. + +It contains the following types of test helpers. + + * [ASSERTIONS][] are functions that perform a test and output relevant + information on failure to help debugging. + +Test helpers send all output to the standard error, making them usable +outside of `@test` functions too. Output is formatted for readability as +described in [OUTPUT][]. + +The Standard Library is automatically loaded and available in all test +files. + + +## OUTPUT + +On failure, in addition to the usual output generated by Bats, +[ASSERTIONS][] display information relevant to the failed test to help +debugging. The output is formatted for readability and displayed as +key-value pairs on the standard error. + +When the value is one line long, the pair is displayed in a columnar +fashion called **two-column** format. + + -- output differs -- + expected : want + actual : have + -- + +When the value is longer than one line, the number of lines in the value +is displayed after the key, and the value starts on the next line and is +indented by two spaces for added readability. This is called +**multi-line** format. + +For convenience, sometimes related values are also displayed in this +format even if they are only one line long. + + -- output differs -- + expected (1 lines): + want + actual (3 lines): + have 1 + have 2 + have 3 + -- + + +## ASSERTIONS + +Assertions are functions that perform a test and output relevant +information on failure to help debugging. They return 1 on failure and 0 +otherwise. + +Assertions about exit code and output operate on the results of the most +recent invocation of `run`. + +* `flunk` []: + Display and fail. This function provides a convenient way to + report various failures. Other test helpers also use it to display + output on failure. When no parameters are specified, is read + from the standard input. + +* `assert` : + Fail if evaluates to false. On failure, is + displayed. + + **Note:** can only be a simple command. Compound + commands, such as `[[`, are not supported. + +* `assert_equal` : + Fail if and do not equal. On failure both values + are displayed. If either one is longer than one line, both are + displayed in _mulit-line_ format. + +* `assert_output` []: + Fail if does not equal `$output`. On failure both values + are displayed. If either one is longer than one line, both are + displayed in _mulit-line_ format. When no parameters are specified, + is read from the standard input. + +* `assert_success` []: + When no parameters are specified, fail if `$status` is not `0`. On + failure `$status` and `$output` are displayed. If `$output` is longer + than one line, it is displayed in _mulit-line_ format. + + When is specified and `$status` is `0`, fail if + does not equal `$output`. On failure both values are displayed. If + either one is longer than one line, both are displayed in _multi-line_ + format. + +* `assert_failure` []: + When no parameters are specified, fail if `$status` is `0`. On failure + `$output` is displayed. If `$output` is longer than one line, it is + displayed in _mulit-line_ format. + + When is specified and `$status` is not `0`, fail if + does not equal `$output`. On failure both values are + displayed. If either one is longer than one line, both are displayed + in _multi-line_ format. + +* `assert_line` [] : + When only is specified, fail if `${lines[@]}` does not contain + . On failure and `$output` are displayed. If `$output` is + longer than one line, it is displayed in _mulit-line_ format. + + When is specified, fail if does not equal + `${lines[INDEX]}`. On failure , and `${lines[INDEX]}` + are dispalyed. + + **NOTE:** Due to a bug in Bats, empty lines are discarded, causing + line indices to change and preventing testing for empty lines. See + [BUGS][] for more. + +* `refute_line` [] : + When only is specified, fail if `${lines[@]}` contains . + On failure , its zero-based index in `${lines[@]}`, and + `$output` are dispalyed. If `$output` is longer than one line, it is + displayed in _mulit-line_ format with highlighted. + + When is specified, fail if equals `${lines[INDEX]}`. On + failure and are dispalyed. + + **NOTE:** Due to a bug in Bats, empty lines are discarded, causing + line indices to change and preventing testing for empty lines. See + [BUGS][] for more. + + +## BUGS + +### Report bugs + +Report bugs on Bats' GitHub issue tracker at +. + + +### Known bugs + +Due to a bug in Bats, empty lines are missing from `${lines[@]}`, +causing line indices to change and preventing testing for empty lines +when using `assert_line` and `refute_line`. See PR \#93 on Github at +. + + +## COPYRIGHT + +TODO(ztombol): Find a suitable license. + + +## SEE ALSO + +`bash`(1), `bats`(1), `bats`(7) diff --git a/test/20-bats.bats b/test/20-bats.bats index f1aff293..db8bbef9 100755 --- a/test/20-bats.bats +++ b/test/20-bats.bats @@ -262,3 +262,7 @@ fixtures bats [ $status -eq 0 ] [ "${lines[1]}" = "ok 1 loop_func" ] } + +@test "standard library is loaded automatically" { + assert true +} diff --git a/test/50-lib-internal-output-10-batslib_err.bats b/test/50-lib-internal-output-10-batslib_err.bats new file mode 100755 index 00000000..21a1a876 --- /dev/null +++ b/test/50-lib-internal-output-10-batslib_err.bats @@ -0,0 +1,15 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'batslib_err() prints positional parameters' { + run batslib_err 'message' + [ "$status" -eq 0 ] + [ "$output" == 'message' ] +} + +@test 'batslib_err() prints STDIN when no positional parameters are specified' { + run bash -c "source '${BATS_LIB}/batslib.bash'; echo 'message' | batslib_err" + [ "$status" -eq 0 ] + [ "$output" == 'message' ] +} diff --git a/test/50-lib-internal-output-11-batslib_count_lines.bats b/test/50-lib-internal-output-11-batslib_count_lines.bats new file mode 100755 index 00000000..dff37bbc --- /dev/null +++ b/test/50-lib-internal-output-11-batslib_count_lines.bats @@ -0,0 +1,21 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'batslib_count_lines() prints the number of lines in the input' { + run batslib_count_lines $'a\nb\nc\n' + [ "$status" -eq 0 ] + [ "$output" == '3' ] +} + +@test 'batslib_count_lines() counts last line when it is not terminated by a newline' { + run batslib_count_lines $'a\nb\nc' + [ "$status" -eq 0 ] + [ "$output" == '3' ] +} + +@test 'batslib_count_lines() counts empty lines' { + run batslib_count_lines $'\n\n\n' + [ "$status" -eq 0 ] + [ "$output" == '3' ] +} diff --git a/test/50-lib-internal-output-12-batslib_is_single_line.bats b/test/50-lib-internal-output-12-batslib_is_single_line.bats new file mode 100755 index 00000000..9af68bab --- /dev/null +++ b/test/50-lib-internal-output-12-batslib_is_single_line.bats @@ -0,0 +1,13 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'batslib_is_single_line() returns 0 if all parameters are one-line strings' { + run batslib_is_single_line 'a' $'b\n' 'c' + [ "$status" -eq 0 ] +} + +@test 'batslib_is_single_line() returns 1 if at least one parameter is longer than one line' { + run batslib_is_single_line 'a' $'b\nb' 'c' + [ "$status" -eq 1 ] +} diff --git a/test/50-lib-internal-output-13-batslib_get_max_single_line_key_width.bats b/test/50-lib-internal-output-13-batslib_get_max_single_line_key_width.bats new file mode 100755 index 00000000..9b405a5e --- /dev/null +++ b/test/50-lib-internal-output-13-batslib_get_max_single_line_key_width.bats @@ -0,0 +1,17 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'batslib_get_max_single_line_key_width() prints the length of the longest key' { + local -ar pairs=( 'k _1' 'v 1' 'k 2' 'v 2' 'k __3' 'v 3' ) + run batslib_get_max_single_line_key_width "${pairs[@]}" + [ "$status" -eq 0 ] + [ "$output" == '5' ] +} + +@test 'batslib_get_max_single_line_key_width() only considers keys with one-line values' { + local -ar pairs=( 'k _1' 'v 1' 'k 2' 'v 2' 'k __3' $'v\n3' ) + run batslib_get_max_single_line_key_width "${pairs[@]}" + [ "$status" -eq 0 ] + [ "$output" == '4' ] +} diff --git a/test/50-lib-internal-output-14-batslib_print_kv_single.bats b/test/50-lib-internal-output-14-batslib_print_kv_single.bats new file mode 100755 index 00000000..70942de0 --- /dev/null +++ b/test/50-lib-internal-output-14-batslib_print_kv_single.bats @@ -0,0 +1,31 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'batslib_print_kv_single() prints key-value pairs with keys in fixed width columns' { + local -ar pairs=( + 'k _1' 'v 1' + 'k 2 ' 'v 2' + 'k __3' 'v 3' + ) + run batslib_print_kv_single 5 "${pairs[@]}" + [ "$status" -eq 0 ] + [ "${#lines[@]}" == '3' ] + [ "${lines[0]}" == 'k _1 : v 1' ] + [ "${lines[1]}" == 'k 2 : v 2' ] + [ "${lines[2]}" == 'k __3 : v 3' ] +} + +@test 'batslib_print_kv_single() does not truncate keys when the column is too narrow' { + local -ar pairs=( + 'k _1' 'v 1' + 'k 2' 'v 2' + 'k __3' 'v 3' + ) + run batslib_print_kv_single 0 "${pairs[@]}" + [ "$status" -eq 0 ] + [ "${#lines[@]}" == '3' ] + [ "${lines[0]}" == 'k _1 : v 1' ] + [ "${lines[1]}" == 'k 2 : v 2' ] + [ "${lines[2]}" == 'k __3 : v 3' ] +} diff --git a/test/50-lib-internal-output-15-batslib_print_kv_multi.bats b/test/50-lib-internal-output-15-batslib_print_kv_multi.bats new file mode 100755 index 00000000..885a6b1a --- /dev/null +++ b/test/50-lib-internal-output-15-batslib_print_kv_multi.bats @@ -0,0 +1,21 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'batslib_print_kv_multi() prints keys and values on separate lines' { + local -ar pairs=( + 'k _1' 'v 1' + 'k 2' $'v 2-1\nv 2-2' + 'k __3' 'v 3' + ) + run batslib_print_kv_multi "${pairs[@]}" + [ "$status" -eq 0 ] + [ "${#lines[@]}" == '7' ] + [ "${lines[0]}" == 'k _1 (1 lines):' ] + [ "${lines[1]}" == 'v 1' ] + [ "${lines[2]}" == 'k 2 (2 lines):' ] + [ "${lines[3]}" == 'v 2-1' ] + [ "${lines[4]}" == 'v 2-2' ] + [ "${lines[5]}" == 'k __3 (1 lines):' ] + [ "${lines[6]}" == 'v 3' ] +} diff --git a/test/50-lib-internal-output-16-batslib_print_kv_single_or_multi.bats b/test/50-lib-internal-output-16-batslib_print_kv_single_or_multi.bats new file mode 100755 index 00000000..5e7cb1b2 --- /dev/null +++ b/test/50-lib-internal-output-16-batslib_print_kv_single_or_multi.bats @@ -0,0 +1,35 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'batslib_print_kv_single_or_multi() prints in two-column format if all values are one line long' { + local -ar pairs=( + 'k _1' 'v 1' + 'k 2 ' 'v 2' + 'k __3' 'v 3' + ) + run batslib_print_kv_single_or_multi 5 "${pairs[@]}" + [ "$status" -eq 0 ] + [ "${#lines[@]}" == '3' ] + [ "${lines[0]}" == 'k _1 : v 1' ] + [ "${lines[1]}" == 'k 2 : v 2' ] + [ "${lines[2]}" == 'k __3 : v 3' ] +} + +@test 'batslib_print_kv_single_or_multi() prints in multi-line format if a at least one value is longer than one line' { + local -ar pairs=( + 'k _1' 'v 1' + 'k 2' $'v 2-1\nv 2-2' + 'k __3' 'v 3' + ) + run batslib_print_kv_single_or_multi 5 "${pairs[@]}" + [ "$status" -eq 0 ] + [ "${#lines[@]}" == '7' ] + [ "${lines[0]}" == 'k _1 (1 lines):' ] + [ "${lines[1]}" == ' v 1' ] + [ "${lines[2]}" == 'k 2 (2 lines):' ] + [ "${lines[3]}" == ' v 2-1' ] + [ "${lines[4]}" == ' v 2-2' ] + [ "${lines[5]}" == 'k __3 (1 lines):' ] + [ "${lines[6]}" == ' v 3' ] +} diff --git a/test/50-lib-internal-output-17-batslib_prefix.bats b/test/50-lib-internal-output-17-batslib_prefix.bats new file mode 100755 index 00000000..23a0e3d3 --- /dev/null +++ b/test/50-lib-internal-output-17-batslib_prefix.bats @@ -0,0 +1,39 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'batslib_prefix() prefixes each line with the given string' { + run bash -c "source '${BATS_LIB}/batslib.bash'; printf 'a\nb\nc\n' | batslib_prefix '_'" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '_a' ] + [ "${lines[1]}" == '_b' ] + [ "${lines[2]}" == '_c' ] +} + +@test 'batslib_prefix() uses two spaces as default prefix' { + run bash -c "source '${BATS_LIB}/batslib.bash'; printf 'a\nb\nc\n' | batslib_prefix" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == ' a' ] + [ "${lines[1]}" == ' b' ] + [ "${lines[2]}" == ' c' ] +} + +@test 'batslib_prefix() prefixes the last line when it is not terminated by a newline' { + run bash -c "source '${BATS_LIB}/batslib.bash'; printf 'a\nb\nc' | batslib_prefix" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == ' a' ] + [ "${lines[1]}" == ' b' ] + [ "${lines[2]}" == ' c' ] +} + +@test 'batslib_prefix() prefixes empty lines' { + run bash -c "source '${BATS_LIB}/batslib.bash'; printf '\n\n\n' | batslib_prefix" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == ' ' ] + [ "${lines[1]}" == ' ' ] + [ "${lines[2]}" == ' ' ] +} diff --git a/test/50-lib-internal-output-18-batslib_mark.bats b/test/50-lib-internal-output-18-batslib_mark.bats new file mode 100755 index 00000000..4502e975 --- /dev/null +++ b/test/50-lib-internal-output-18-batslib_mark.bats @@ -0,0 +1,64 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'batslib_mark() highlights a single line' { + run bash -c "source '${BATS_LIB}/batslib.bash'; printf ' a\n b\n c\n' | batslib_mark '>' 0" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '>a' ] + [ "${lines[1]}" == ' b' ] + [ "${lines[2]}" == ' c' ] +} + +@test 'batslib_mark() highlights lines when indices are in ascending order' { + run bash -c "source '${BATS_LIB}/batslib.bash'; printf ' a\n b\n c\n' | batslib_mark '>' 1 2" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == ' a' ] + [ "${lines[1]}" == '>b' ] + [ "${lines[2]}" == '>c' ] +} + +@test 'batslib_mark() highlights lines when indices are in random order' { + run bash -c "source '${BATS_LIB}/batslib.bash'; printf ' a\n b\n c\n' | batslib_mark '>' 2 1" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == ' a' ] + [ "${lines[1]}" == '>b' ] + [ "${lines[2]}" == '>c' ] +} + +@test 'batslib_mark() ignores duplicate line indices' { + run bash -c "source '${BATS_LIB}/batslib.bash'; printf ' a\n b\n c\n' | batslib_mark '>' 1 2 1" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == ' a' ] + [ "${lines[1]}" == '>b' ] + [ "${lines[2]}" == '>c' ] +} + +@test 'batslib_mark() outputs the input untouched if the marking string is the empty string' { + run bash -c "source '${BATS_LIB}/batslib.bash'; printf ' a\n b\n c\n' | batslib_mark '' 1" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == ' a' ] + [ "${lines[1]}" == ' b' ] + [ "${lines[2]}" == ' c' ] +} + +@test 'batslib_mark() highlights the last line when it is not terminated by a newline' { + run bash -c "source '${BATS_LIB}/batslib.bash'; printf ' a\n b\n c' | batslib_mark '>' 2" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == ' a' ] + [ "${lines[1]}" == ' b' ] + [ "${lines[2]}" == '>c' ] +} + +@test 'batslib_mark() replaces the line with the marking string if the line is shorter or equally long' { + run bash -c "source '${BATS_LIB}/batslib.bash'; printf '\n' | batslib_mark '>' 0" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 1 ] + [ "${lines[0]}" == '>' ] +} diff --git a/test/50-lib-internal-output-19-batslib_decorate.bats b/test/50-lib-internal-output-19-batslib_decorate.bats new file mode 100755 index 00000000..d7bcbe8a --- /dev/null +++ b/test/50-lib-internal-output-19-batslib_decorate.bats @@ -0,0 +1,12 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'batslib_decorate() encloses body in header and footer lines' { + run bash -c "source '${BATS_LIB}/batslib.bash'; echo 'body' | batslib_decorate 'title'" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '-- title --' ] + [ "${lines[1]}" == 'body' ] + [ "${lines[2]}" == '--' ] +} diff --git a/test/60-lib-assertion-10-flunk.bats b/test/60-lib-assertion-10-flunk.bats new file mode 100755 index 00000000..d424181b --- /dev/null +++ b/test/60-lib-assertion-10-flunk.bats @@ -0,0 +1,20 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'flunk() returns 1' { + run flunk '' + [ "$status" -eq 1 ] +} + +@test 'flunk() prints positional parameters' { + run flunk 'message' + [ "$status" -eq 1 ] + [ "$output" == 'message' ] +} + +@test 'flunk() prints STDIN if no positional parameters are specified' { + run bash -c "source '${BATS_LIB}/batslib.bash'; echo 'message' | flunk" + [ "$status" -eq 1 ] + [ "$output" == 'message' ] +} diff --git a/test/60-lib-assertion-11-assert.bats b/test/60-lib-assertion-11-assert.bats new file mode 100755 index 00000000..003ab0e2 --- /dev/null +++ b/test/60-lib-assertion-11-assert.bats @@ -0,0 +1,18 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'assert() returns 0 if the condition evaluates to TRUE' { + run assert true + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'assert() returns 1 and displays the condition if it evaluates to FALSE' { + run assert false + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '-- assertion failed --' ] + [ "${lines[1]}" == 'condition : false' ] + [ "${lines[2]}" == '--' ] +} diff --git a/test/60-lib-assertion-12-assert_equal.bats b/test/60-lib-assertion-12-assert_equal.bats new file mode 100755 index 00000000..0404dfc7 --- /dev/null +++ b/test/60-lib-assertion-12-assert_equal.bats @@ -0,0 +1,37 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'assert_equal() returns 0 if the actual value equals the expected' { + run assert_equal 'a' 'a' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'assert_equal() returns 1 and displays the actual and expected value if they do not equal' { + run assert_equal 'a' 'b' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 4 ] + [ "${lines[0]}" == '-- values do not equal --' ] + [ "${lines[1]}" == 'expected : a' ] + [ "${lines[2]}" == 'actual : b' ] + [ "${lines[3]}" == '--' ] +} + +@test 'assert_equal() displays the expected and actual value in multi-line format if necessary' { + run assert_equal 'a' $'b 1\nb 2' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 7 ] + [ "${lines[0]}" == '-- values do not equal --' ] + [ "${lines[1]}" == 'expected (1 lines):' ] + [ "${lines[2]}" == ' a' ] + [ "${lines[3]}" == 'actual (2 lines):' ] + [ "${lines[4]}" == ' b 1' ] + [ "${lines[5]}" == ' b 2' ] + [ "${lines[6]}" == '--' ] +} + +@test 'assert_equal() performs literal matching' { + run assert_equal 'a' '*' + [ "$status" -eq 1 ] +} diff --git a/test/60-lib-assertion-13-assert_output.bats b/test/60-lib-assertion-13-assert_output.bats new file mode 100755 index 00000000..7514323c --- /dev/null +++ b/test/60-lib-assertion-13-assert_output.bats @@ -0,0 +1,49 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'assert_output() returns 0 if $output equals the expected output' { + run echo 'a' + run assert_output 'a' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'assert_output() returns 1 and displays the expected and actual output if they do not equal' { + run echo 'b' + run assert_output 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 4 ] + [ "${lines[0]}" == '-- output differs --' ] + [ "${lines[1]}" == 'expected : a' ] + [ "${lines[2]}" == 'actual : b' ] + [ "${lines[3]}" == '--' ] +} + +@test 'assert_output() displays the expected and actual output in multi-line format if necessary' { + run echo $'b 1\nb 2' + run assert_output 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 7 ] + [ "${lines[0]}" == '-- output differs --' ] + [ "${lines[1]}" == 'expected (1 lines):' ] + [ "${lines[2]}" == ' a' ] + [ "${lines[3]}" == 'actual (2 lines):' ] + [ "${lines[4]}" == ' b 1' ] + [ "${lines[5]}" == ' b 2' ] + [ "${lines[6]}" == '--' ] +} + +@test 'assert_output() reads the expected output from STDIN when no positional parameters are specified' { + run echo 'a' + export output + run bash -c "source '${BATS_LIB}/batslib.bash'; echo 'a' | assert_output" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'assert_output() performs literal matching' { + run echo '*' + run assert_output 'a' + [ "$status" -eq 1 ] +} diff --git a/test/60-lib-assertion-14-assert_success.bats b/test/60-lib-assertion-14-assert_success.bats new file mode 100755 index 00000000..8caa5af2 --- /dev/null +++ b/test/60-lib-assertion-14-assert_success.bats @@ -0,0 +1,72 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'assert_success() returns 0 if $status is 0' { + run true + run assert_success + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'assert_success() returns 1 and displays $status and $output if $status is not 0' { + run bash -c 'echo error; exit 1' + run assert_success + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 4 ] + [ "${lines[0]}" == '-- command failed --' ] + [ "${lines[1]}" == 'status : 1' ] + [ "${lines[2]}" == 'output : error' ] + [ "${lines[3]}" == '--' ] +} + +@test 'assert_success() displays $output in multi-line format if necessary' { + run bash -c "echo $'error 1\nerror 2'; exit 1" + run assert_success + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 6 ] + [ "${lines[0]}" == '-- command failed --' ] + [ "${lines[1]}" == 'status : 1' ] + [ "${lines[2]}" == 'output (2 lines):' ] + [ "${lines[3]}" == ' error 1' ] + [ "${lines[4]}" == ' error 2' ] + [ "${lines[5]}" == '--' ] +} + +@test 'assert_success() tests $output against the first positional parameter if specified' { + run echo 'a' + run assert_success 'a' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'assert_success() displays the expected and actual output if they differ' { + run echo 'b' + run assert_success 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 4 ] + [ "${lines[0]}" == '-- command succeeded, but output differs --' ] + [ "${lines[1]}" == 'expected : a' ] + [ "${lines[2]}" == 'actual : b' ] + [ "${lines[3]}" == '--' ] +} + +@test 'assert_success() displays the expected and actual output in multi-line format if necessary' { + run echo $'b 1\nb 2' + run assert_success 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 7 ] + [ "${lines[0]}" == '-- command succeeded, but output differs --' ] + [ "${lines[1]}" == 'expected (1 lines):' ] + [ "${lines[2]}" == ' a' ] + [ "${lines[3]}" == 'actual (2 lines):' ] + [ "${lines[4]}" == ' b 1' ] + [ "${lines[5]}" == ' b 2' ] + [ "${lines[6]}" == '--' ] +} + +@test 'assert_success() performs literal matching on $output' { + run echo 'a' + run assert_success '*' + [ "$status" -eq 1 ] +} diff --git a/test/60-lib-assertion-15-assert_failure.bats b/test/60-lib-assertion-15-assert_failure.bats new file mode 100755 index 00000000..c01df855 --- /dev/null +++ b/test/60-lib-assertion-15-assert_failure.bats @@ -0,0 +1,70 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'assert_failure() returns 0 if $status is not 0' { + run false + run assert_failure + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'assert_failure() returns 1 and displays $output if $status is 0' { + run bash -c 'echo ok; exit 0' + run assert_failure + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '-- command succeeded, but it was expected to fail --' ] + [ "${lines[1]}" == 'output : ok' ] + [ "${lines[2]}" == '--' ] +} + +@test 'assert_failure() displays $output in multi-line format if necessary' { + run bash -c "echo $'ok 1\nok 2'; exit 0" + run assert_failure + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 5 ] + [ "${lines[0]}" == '-- command succeeded, but it was expected to fail --' ] + [ "${lines[1]}" == 'output (2 lines):' ] + [ "${lines[2]}" == ' ok 1' ] + [ "${lines[3]}" == ' ok 2' ] + [ "${lines[4]}" == '--' ] +} + +@test 'assert_failure() tests $output against the first positional parameter if specified' { + run bash -c 'echo a; exit 1' + run assert_failure 'a' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'assert_failure() displays the expected and actual output if they differ' { + run bash -c 'echo b; exit 1' + run assert_failure 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 4 ] + [ "${lines[0]}" == '-- command failed as expected, but output differs --' ] + [ "${lines[1]}" == 'expected : a' ] + [ "${lines[2]}" == 'actual : b' ] + [ "${lines[3]}" == '--' ] +} + +@test 'assert_failure() displays the expected and actual output in multi-line format if necessary' { + run bash -c "echo $'b 1\nb 2'; exit 1" + run assert_failure 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 7 ] + [ "${lines[0]}" == '-- command failed as expected, but output differs --' ] + [ "${lines[1]}" == 'expected (1 lines):' ] + [ "${lines[2]}" == ' a' ] + [ "${lines[3]}" == 'actual (2 lines):' ] + [ "${lines[4]}" == ' b 1' ] + [ "${lines[5]}" == ' b 2' ] + [ "${lines[6]}" == '--' ] +} + +@test 'assert_failure() performs literal matching on $output' { + run bash -c 'echo a; exit 1' + run assert_failure '*' + [ "$status" -eq 1 ] +} diff --git a/test/60-lib-assertion-16-assert_line.bats b/test/60-lib-assertion-16-assert_line.bats new file mode 100755 index 00000000..76d8d494 --- /dev/null +++ b/test/60-lib-assertion-16-assert_line.bats @@ -0,0 +1,66 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'assert_line() returns 0 if the expected line is found' { + run echo $'a\nb\nc' + run assert_line 'b' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'assert_line() returns 1 and displays $output and the expected line if it was not found' { + run echo 'a' + run assert_line 'd' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 4 ] + [ "${lines[0]}" == '-- line is not in output --' ] + [ "${lines[1]}" == 'line : d' ] + [ "${lines[2]}" == 'output : a' ] + [ "${lines[3]}" == '--' ] +} + +@test 'assert_line() displays $output in multi-line format if necessary' { + run echo $'a\nb\nc' + run assert_line 'd' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 7 ] + [ "${lines[0]}" == '-- line is not in output --' ] + [ "${lines[1]}" == 'line : d' ] + [ "${lines[2]}" == 'output (3 lines):' ] + [ "${lines[3]}" == ' a' ] + [ "${lines[4]}" == ' b' ] + [ "${lines[5]}" == ' c' ] + [ "${lines[6]}" == '--' ] +} + +@test 'assert_line() returns 0 if the expected line is found at the given index' { + run echo $'a\nb\nc' + run assert_line 1 'b' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'assert_line() returns 1 and displays the expected and the actual line at the given index if they do not equal' { + run echo $'a\nb\nc' + run assert_line 1 'd' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 5 ] + [ "${lines[0]}" == '-- line differs --' ] + [ "${lines[1]}" == 'index : 1' ] + [ "${lines[2]}" == 'expected : d' ] + [ "${lines[3]}" == 'actual : b' ] + [ "${lines[4]}" == '--' ] +} + +@test 'assert_line() performs literal matching when the expected line is sought in the entire output' { + run echo $'a\nb\nc' + run assert_line '*' + [ "$status" -eq 1 ] +} + +@test 'assert_line() performs literal matching when the expected line is sought at a given index' { + run echo $'a\nb\nc' + run assert_line 1 '*' + [ "$status" -eq 1 ] +} diff --git a/test/60-lib-assertion-17-refute_line.bats b/test/60-lib-assertion-17-refute_line.bats new file mode 100755 index 00000000..4303dab6 --- /dev/null +++ b/test/60-lib-assertion-17-refute_line.bats @@ -0,0 +1,67 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'refute_line() returns 0 if the unexpected line is not found' { + run echo $'a\nb\nc' + run refute_line 'd' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'refute_line() returns 1 and displays $output, the unexpected line and its index if it was found' { + run echo $'b' + run refute_line 'b' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 5 ] + [ "${lines[0]}" == '-- line should not be in output --' ] + [ "${lines[1]}" == 'line : b' ] + [ "${lines[2]}" == 'index : 0' ] + [ "${lines[3]}" == 'output : b' ] + [ "${lines[4]}" == '--' ] +} + +@test 'refute_line() displays $output in multi-line format with the unexpected line highlighted if necessary' { + run echo $'a\nb\nc' + run refute_line 'b' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 8 ] + [ "${lines[0]}" == '-- line should not be in output --' ] + [ "${lines[1]}" == 'line : b' ] + [ "${lines[2]}" == 'index : 1' ] + [ "${lines[3]}" == 'output (3 lines):' ] + [ "${lines[4]}" == ' a' ] + [ "${lines[5]}" == '> b' ] + [ "${lines[6]}" == ' c' ] + [ "${lines[7]}" == '--' ] +} + +@test 'refute_line() returns 0 if the unexpected line is not found at the given index' { + run echo $'a\nb\nc' + run refute_line 1 'd' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'refute_line() returns 1 and displays the unexpected line and the index if it was found at the given index' { + run echo $'a\nb\nc' + run refute_line 1 'b' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 4 ] + [ "${lines[0]}" == '-- line should differ from expected --' ] + [ "${lines[1]}" == 'index : 1' ] + [ "${lines[2]}" == 'line : b' ] + [ "${lines[3]}" == '--' ] +} + +@test 'refute_line() performs literal matching when the unexpected line is sought in the entire output' { + run echo $'a\nb\nc' + run refute_line '*' + [ "$status" -eq 0 ] +} + +@test 'refute_line() performs literal matching when the unexpected line is sought at a given index' { + run echo $'a\nb\nc' + run refute_line 1 '*' + [ "$status" -eq 0 ] +} From e2348a3e0b8c1c57ce3164dc427f41d5bd94c9b7 Mon Sep 17 00:00:00 2001 From: Zoltan Tombol Date: Thu, 13 Aug 2015 00:02:13 +0200 Subject: [PATCH 03/16] Remove output matching from assert_success --- README.md | 35 +----------------- lib/bats/batslib.bash | 21 +++-------- man/batslib.7 | 7 ++-- man/batslib.7.ronn | 13 +++---- test/60-lib-assertion-14-assert_success.bats | 38 -------------------- 5 files changed, 11 insertions(+), 103 deletions(-) diff --git a/README.md b/README.md index 84e8510d..51d6e58d 100644 --- a/README.md +++ b/README.md @@ -383,12 +383,7 @@ If either value is longer than one line both are displayed in #### `assert_success` -This function tests `$status` and additionally `$output` depending on -the number of parameters. - -##### `$status` test - -When no parameters are specified, fail if `$status` is not zero. +Fail if `$status` is not 0. ```bash @test 'assert_success() status only' { @@ -409,34 +404,6 @@ output : have If `$output` is longer than one line, it is displayed in *multi-line* format. -##### Optional `$output` test - -Additionally, if one parameter is specified and the status test passed, -`$output` is compared against the parameter as expected output. - -```bash -@test 'assert_success() status and output' { - run echo 'have' - local expected='want' - assert_success "$expected" -} -``` - -If the output test fails, the expected and actual outputs are displayed. - -``` --- command succeeded, but output differs -- -expected : want -actual : have --- -``` - -If either value is longer than one line both are displayed in -*multi-line* format. - -If the status test fails, the output test is not performed and the -output is identical to the status-only form's. - #### `assert_failure` This function tests `$status` and additionally `$output` depending on diff --git a/lib/bats/batslib.bash b/lib/bats/batslib.bash index fee8074d..36a6016e 100644 --- a/lib/bats/batslib.bash +++ b/lib/bats/batslib.bash @@ -110,39 +110,26 @@ assert_output() { fi } -# Fail and display an error message if `$status' is not zero. The error +# Fail and display an error message if `$status' is not 0. The error # message contains `$status' and `$output'. # -# Optionally, if the status check passed, `$output' can be tested -# against the first positional parameter as expected output. If the -# output check fails the error message contains the expected and the -# actual output. -# # Globals: # status # output # Arguments: -# $1 - [opt] expected output +# none # Returns: -# 0 - status and, optionally, output check passed +# 0 - `$status' is 0 # 1 - otherwise # Outputs: -# STDERR - `$status' and `$output', if status check fails -# expected and actual output, if output check fails +# STDERR - `$status' and `$output', if `$status' is not 0 assert_success() { - (( $# > 0 )) && local -r expected="$1" if (( status != 0 )); then { local -ir width=6 batslib_print_kv_single "$width" 'status' "$status" batslib_print_kv_single_or_multi "$width" 'output' "$output" } | batslib_decorate 'command failed' \ | flunk - elif (( $# > 0 )) && [[ $output != "$1" ]]; then - batslib_print_kv_single_or_multi 8 \ - 'expected' "$expected" \ - 'actual' "$output" \ - | batslib_decorate 'command succeeded, but output differs' \ - | flunk fi } diff --git a/man/batslib.7 b/man/batslib.7 index 4572bc44..852316ac 100644 --- a/man/batslib.7 +++ b/man/batslib.7 @@ -91,11 +91,8 @@ Fail if \fIEXPECTED\fR and \fIACTUAL\fR do not equal\. On failure both values ar Fail if \fIEXPECTED\fR does not equal \fB$output\fR\. On failure both values are displayed\. If either one is longer than one line, both are displayed in \fImulit\-line\fR format\. When no parameters are specified, \fIEXPECTED\fR is read from the standard input\. . .TP -\fBassert_success\fR [\fIEXPECTED\fR] -When no parameters are specified, fail if \fB$status\fR is not \fB0\fR\. On failure \fB$status\fR and \fB$output\fR are displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulit\-line\fR format\. -. -.IP -When \fIEXPECTED\fR is specified and \fB$status\fR is \fB0\fR, fail if \fIEXPECTED\fR does not equal \fB$output\fR\. On failure both values are displayed\. If either one is longer than one line, both are displayed in \fImulti\-line\fR format\. +\fBassert_success\fR +Fail if \fB$status\fR is not \fB0\fR\. On failure \fB$status\fR and \fB$output\fR are displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulit\-line\fR format\. . .TP \fBassert_failure\fR [\fIEXPECTED\fR] diff --git a/man/batslib.7.ronn b/man/batslib.7.ronn index 7f532d45..a480ab77 100644 --- a/man/batslib.7.ronn +++ b/man/batslib.7.ronn @@ -87,15 +87,10 @@ recent invocation of `run`. displayed in _mulit-line_ format. When no parameters are specified, is read from the standard input. -* `assert_success` []: - When no parameters are specified, fail if `$status` is not `0`. On - failure `$status` and `$output` are displayed. If `$output` is longer - than one line, it is displayed in _mulit-line_ format. - - When is specified and `$status` is `0`, fail if - does not equal `$output`. On failure both values are displayed. If - either one is longer than one line, both are displayed in _multi-line_ - format. +* `assert_success`: + Fail if `$status` is not `0`. On failure `$status` and `$output` are + displayed. If `$output` is longer than one line, it is displayed in + _mulit-line_ format. * `assert_failure` []: When no parameters are specified, fail if `$status` is `0`. On failure diff --git a/test/60-lib-assertion-14-assert_success.bats b/test/60-lib-assertion-14-assert_success.bats index 8caa5af2..01dc2af5 100755 --- a/test/60-lib-assertion-14-assert_success.bats +++ b/test/60-lib-assertion-14-assert_success.bats @@ -32,41 +32,3 @@ load test_helper [ "${lines[4]}" == ' error 2' ] [ "${lines[5]}" == '--' ] } - -@test 'assert_success() tests $output against the first positional parameter if specified' { - run echo 'a' - run assert_success 'a' - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 0 ] -} - -@test 'assert_success() displays the expected and actual output if they differ' { - run echo 'b' - run assert_success 'a' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 4 ] - [ "${lines[0]}" == '-- command succeeded, but output differs --' ] - [ "${lines[1]}" == 'expected : a' ] - [ "${lines[2]}" == 'actual : b' ] - [ "${lines[3]}" == '--' ] -} - -@test 'assert_success() displays the expected and actual output in multi-line format if necessary' { - run echo $'b 1\nb 2' - run assert_success 'a' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 7 ] - [ "${lines[0]}" == '-- command succeeded, but output differs --' ] - [ "${lines[1]}" == 'expected (1 lines):' ] - [ "${lines[2]}" == ' a' ] - [ "${lines[3]}" == 'actual (2 lines):' ] - [ "${lines[4]}" == ' b 1' ] - [ "${lines[5]}" == ' b 2' ] - [ "${lines[6]}" == '--' ] -} - -@test 'assert_success() performs literal matching on $output' { - run echo 'a' - run assert_success '*' - [ "$status" -eq 1 ] -} From aa5b5e7d1d1d27737d1a9a16213d5ad2a4510c37 Mon Sep 17 00:00:00 2001 From: Zoltan Tombol Date: Thu, 13 Aug 2015 00:23:27 +0200 Subject: [PATCH 04/16] Remove output matching from assert_failure --- README.md | 35 +----------------- lib/bats/batslib.bash | 18 ++-------- man/batslib.7 | 5 +-- man/batslib.7.ronn | 11 ++---- test/60-lib-assertion-15-assert_failure.bats | 38 -------------------- 5 files changed, 8 insertions(+), 99 deletions(-) diff --git a/README.md b/README.md index 51d6e58d..65144d3e 100644 --- a/README.md +++ b/README.md @@ -406,12 +406,7 @@ format. #### `assert_failure` -This function tests `$status` and additionally `$output` depending on -the number of parameters. - -##### `$status` test - -When no parameters are specified, fail if `$status` is zero. +Fail if `$status` is 0. ```bash @test 'assert_failure() status only' { @@ -431,34 +426,6 @@ output : have If `$output` is longer than one line, it is displayed in *multi-line* format. -##### Optional `$output` test - -Additionally, if one parameter is specified and the status test passed, -`$output` is compared against the parameter as expected output. - -```bash -@test 'assert_failure() status and output' { - run bash -c "echo 'have'; exit 1" - local expected='want' - assert_failure "$expected" -} -``` - -If the output test fails, the expected and actual outputs are displayed. - -``` --- command failed as expected, but output differs -- -expected : want -actual : have --- -``` - -If either value is longer than one line both are displayed in -*multi-line* format. - -If the status test fails, the output test is not performed and the -output is identical to the status-only form's. - #### `assert_line` Depending on the number of parameters, this function tests either that diff --git a/lib/bats/batslib.bash b/lib/bats/batslib.bash index 36a6016e..00218b2d 100644 --- a/lib/bats/batslib.bash +++ b/lib/bats/batslib.bash @@ -133,35 +133,23 @@ assert_success() { fi } -# Fail and display `$output' if `$status' is zero. -# -# Optionally, if the status check passed, `$output' can be tested -# against the first positional parameter as expected output. If the -# output check fails the error message contains the expected and the -# actual output. +# Fail and display `$output' if `$status' is 0. # # Globals: # status # output # Arguments: -# $1 - [opt] expected output +# none # Returns: -# 0 - status and, optionally, output check passed +# 0 - $status is not 0 # 1 - otherwise # Outputs: # STDERR - `$output', if status check fails -# expected and actual output, if output check fails assert_failure() { if (( status == 0 )); then batslib_print_kv_single_or_multi 6 'output' "$output" \ | batslib_decorate 'command succeeded, but it was expected to fail' \ | flunk - elif (( $# > 0 )) && [[ $output != "$1" ]]; then - batslib_print_kv_single_or_multi 8 \ - 'expected' "$1" \ - 'actual' "$output" \ - | batslib_decorate 'command failed as expected, but output differs' \ - | flunk fi } diff --git a/man/batslib.7 b/man/batslib.7 index 852316ac..4bfb91fb 100644 --- a/man/batslib.7 +++ b/man/batslib.7 @@ -96,10 +96,7 @@ Fail if \fB$status\fR is not \fB0\fR\. On failure \fB$status\fR and \fB$output\f . .TP \fBassert_failure\fR [\fIEXPECTED\fR] -When no parameters are specified, fail if \fB$status\fR is \fB0\fR\. On failure \fB$output\fR is displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulit\-line\fR format\. -. -.IP -When \fIEXPECTED\fR is specified and \fB$status\fR is not \fB0\fR, fail if \fIEXPECTED\fR does not equal \fB$output\fR\. On failure both values are displayed\. If either one is longer than one line, both are displayed in \fImulti\-line\fR format\. +Fail if \fB$status\fR is \fB0\fR\. On failure \fB$output\fR is displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulit\-line\fR format\. . .TP \fBassert_line\fR [\fIINDEX\fR] \fILINE\fR diff --git a/man/batslib.7.ronn b/man/batslib.7.ronn index a480ab77..59b974f8 100644 --- a/man/batslib.7.ronn +++ b/man/batslib.7.ronn @@ -93,14 +93,9 @@ recent invocation of `run`. _mulit-line_ format. * `assert_failure` []: - When no parameters are specified, fail if `$status` is `0`. On failure - `$output` is displayed. If `$output` is longer than one line, it is - displayed in _mulit-line_ format. - - When is specified and `$status` is not `0`, fail if - does not equal `$output`. On failure both values are - displayed. If either one is longer than one line, both are displayed - in _multi-line_ format. + Fail if `$status` is `0`. On failure `$output` is displayed. If + `$output` is longer than one line, it is displayed in _mulit-line_ + format. * `assert_line` [] : When only is specified, fail if `${lines[@]}` does not contain diff --git a/test/60-lib-assertion-15-assert_failure.bats b/test/60-lib-assertion-15-assert_failure.bats index c01df855..8b95d0a3 100755 --- a/test/60-lib-assertion-15-assert_failure.bats +++ b/test/60-lib-assertion-15-assert_failure.bats @@ -30,41 +30,3 @@ load test_helper [ "${lines[3]}" == ' ok 2' ] [ "${lines[4]}" == '--' ] } - -@test 'assert_failure() tests $output against the first positional parameter if specified' { - run bash -c 'echo a; exit 1' - run assert_failure 'a' - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 0 ] -} - -@test 'assert_failure() displays the expected and actual output if they differ' { - run bash -c 'echo b; exit 1' - run assert_failure 'a' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 4 ] - [ "${lines[0]}" == '-- command failed as expected, but output differs --' ] - [ "${lines[1]}" == 'expected : a' ] - [ "${lines[2]}" == 'actual : b' ] - [ "${lines[3]}" == '--' ] -} - -@test 'assert_failure() displays the expected and actual output in multi-line format if necessary' { - run bash -c "echo $'b 1\nb 2'; exit 1" - run assert_failure 'a' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 7 ] - [ "${lines[0]}" == '-- command failed as expected, but output differs --' ] - [ "${lines[1]}" == 'expected (1 lines):' ] - [ "${lines[2]}" == ' a' ] - [ "${lines[3]}" == 'actual (2 lines):' ] - [ "${lines[4]}" == ' b 1' ] - [ "${lines[5]}" == ' b 2' ] - [ "${lines[6]}" == '--' ] -} - -@test 'assert_failure() performs literal matching on $output' { - run bash -c 'echo a; exit 1' - run assert_failure '*' - [ "$status" -eq 1 ] -} From 7a66afc56fca9a53e18f1e74084523ab7ed6a0e3 Mon Sep 17 00:00:00 2001 From: Zoltan Tombol Date: Fri, 14 Aug 2015 01:06:56 +0200 Subject: [PATCH 05/16] Add expected status parameter to assert_failure --- README.md | 26 +++++++++++++++ lib/bats/batslib.bash | 23 +++++++++++--- man/batslib.7 | 3 ++ man/batslib.7.ronn | 3 ++ test/60-lib-assertion-15-assert_failure.bats | 33 ++++++++++++++++++++ 5 files changed, 84 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 65144d3e..cca88011 100644 --- a/README.md +++ b/README.md @@ -426,6 +426,32 @@ output : have If `$output` is longer than one line, it is displayed in *multi-line* format. +##### Expected status + +When one parameter is specified, fail if `$status` does not equal the +expected status specified by the parameter. + +```bash +@test 'assert_failure() with expected status' { + run bash -c "echo 'error'; exit 1" + assert_failure 2 +} +``` + +On failure the expected and actual status, and `$output` are displayed. + +``` +-- command failed as expected, but status differs -- +expected : 2 +actual : 1 +output : error +-- +``` + +If `$output` is longer than one line, it is displayed in *multi-line* +format. + + #### `assert_line` Depending on the number of parameters, this function tests either that diff --git a/lib/bats/batslib.bash b/lib/bats/batslib.bash index 00218b2d..30a23554 100644 --- a/lib/bats/batslib.bash +++ b/lib/bats/batslib.bash @@ -133,23 +133,38 @@ assert_success() { fi } -# Fail and display `$output' if `$status' is 0. +# Fail and display `$output' if `$status' is 0. Additionally, if the +# expected status is specified in the first parameter, fail if it does +# not equal `$status'. In this case, display both values and `$output'. # # Globals: # status # output # Arguments: -# none +# $1 - [opt] expected exit status # Returns: -# 0 - $status is not 0 +# 0 - $status is not 0, and optionally expected and actual `$status' +# equals # 1 - otherwise # Outputs: -# STDERR - `$output', if status check fails +# STDERR - `$output', if `$status' is 0 +# `$output', `$status' and expected status, if the latter two +# do not equal assert_failure() { + (( $# > 0 )) && local -r expected="$1" if (( status == 0 )); then batslib_print_kv_single_or_multi 6 'output' "$output" \ | batslib_decorate 'command succeeded, but it was expected to fail' \ | flunk + elif (( $# > 0 )) && (( status != expected )); then + { local -ir width=8 + batslib_print_kv_single "$width" \ + 'expected' "$expected" \ + 'actual' "$status" + batslib_print_kv_single_or_multi "$width" \ + 'output' "$output" + } | batslib_decorate 'command failed as expected, but status differs' \ + | flunk fi } diff --git a/man/batslib.7 b/man/batslib.7 index 4bfb91fb..1a7ee6fc 100644 --- a/man/batslib.7 +++ b/man/batslib.7 @@ -98,6 +98,9 @@ Fail if \fB$status\fR is not \fB0\fR\. On failure \fB$status\fR and \fB$output\f \fBassert_failure\fR [\fIEXPECTED\fR] Fail if \fB$status\fR is \fB0\fR\. On failure \fB$output\fR is displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulit\-line\fR format\. . +.IP +When \fIEXPECTED\fR is specified, fail if it does not equal \fB$status\fR\. On failure \fIEXPECTED\fR, \fB$status\fR and \fB$output\fR are displayed\. +. .TP \fBassert_line\fR [\fIINDEX\fR] \fILINE\fR When only \fILINE\fR is specified, fail if \fB${lines[@]}\fR does not contain \fILINE\fR\. On failure \fILINE\fR and \fB$output\fR are displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulit\-line\fR format\. diff --git a/man/batslib.7.ronn b/man/batslib.7.ronn index 59b974f8..e5d68d04 100644 --- a/man/batslib.7.ronn +++ b/man/batslib.7.ronn @@ -97,6 +97,9 @@ recent invocation of `run`. `$output` is longer than one line, it is displayed in _mulit-line_ format. + When is specified, fail if it does not equal `$status`. On + failure , `$status` and `$output` are displayed. + * `assert_line` [] : When only is specified, fail if `${lines[@]}` does not contain . On failure and `$output` are displayed. If `$output` is diff --git a/test/60-lib-assertion-15-assert_failure.bats b/test/60-lib-assertion-15-assert_failure.bats index 8b95d0a3..56dfdb9f 100755 --- a/test/60-lib-assertion-15-assert_failure.bats +++ b/test/60-lib-assertion-15-assert_failure.bats @@ -30,3 +30,36 @@ load test_helper [ "${lines[3]}" == ' ok 2' ] [ "${lines[4]}" == '--' ] } + +@test 'assert_failure() test $status against the first positional parameter if specified' { + run bash -c 'echo ok; exit 1' + run assert_failure 1 + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'assert_failure() displays $output, and the expected and actual status if they differ' { + run bash -c 'echo error; exit 1' + run assert_failure 2 + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 5 ] + [ "${lines[0]}" == '-- command failed as expected, but status differs --' ] + [ "${lines[1]}" == 'expected : 2' ] + [ "${lines[2]}" == 'actual : 1' ] + [ "${lines[3]}" == 'output : error' ] + [ "${lines[4]}" == '--' ] +} + +@test 'assert_failure() when status differs, displays $output in multi-line format if necessary' { + run bash -c "echo $'error 1\nerror 2'; exit 1" + run assert_failure 2 + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 7 ] + [ "${lines[0]}" == '-- command failed as expected, but status differs --' ] + [ "${lines[1]}" == 'expected : 2' ] + [ "${lines[2]}" == 'actual : 1' ] + [ "${lines[3]}" == 'output (2 lines):' ] + [ "${lines[4]}" == ' error 1' ] + [ "${lines[5]}" == ' error 2' ] + [ "${lines[6]}" == '--' ] +} From d6c0afb82c5c8cc685808ea6cb269d83e4c8fbb2 Mon Sep 17 00:00:00 2001 From: Zoltan Tombol Date: Wed, 19 Aug 2015 01:01:05 +0200 Subject: [PATCH 06/16] Move assert_line into assert_output --- README.md | 121 +++++++-------- lib/bats/batslib.bash | 162 +++++++++++--------- man/batslib.7 | 21 ++- man/batslib.7.ronn | 27 ++-- test/60-lib-assertion-13-assert_output.bats | 120 ++++++++++++++- test/60-lib-assertion-16-assert_line.bats | 66 -------- 6 files changed, 293 insertions(+), 224 deletions(-) delete mode 100755 test/60-lib-assertion-16-assert_line.bats diff --git a/README.md b/README.md index cca88011..bfae8c7c 100644 --- a/README.md +++ b/README.md @@ -349,7 +349,16 @@ If either value is longer than one line both are displayed in #### `assert_output` -Fail if `$output` does not equal the expected output. +This function helps to verify that a command or function produces the +correct output. It can compare the entire output, a single line or make +sure that a given line is part of the output. + +***Note:*** *Due to a [bug in Bats][bats-93], empty lines are discarded, +causing line indices to change and preventing testing for empty lines.* + +##### Matching the entire output + +By default, fail if `$output` does not equal the expected output. ```bash @test 'assert_output()' { @@ -381,6 +390,55 @@ actual : have If either value is longer than one line both are displayed in *multi-line* format. +##### Matching a single line + +When `-l ` is used, fail if the expected line does not equal the +output line identified by its index in `${lines[@]}`. + +```bash +@test 'assert_line() in specific line' { + run echo $'have 0\nhave 1\nhave 2' + assert_output -l 1 'want' +} +``` + +On failure the index, and the expected and actual lines are displayed. + +``` +-- line differs -- +index : 1 +expected : want +actual : have 1 +-- +``` + +##### Line contained in output + +When `-L` is used, fail if `${lines[@]}` does not contain the expected +line. + +```bash +@test 'assert_line() in entire output' { + run echo $'have 0\nhave 1\nhave 2' + assert_output -L 'want' +} +``` + +On failure `$output` and the expected line are displayed. + +``` +-- line is not in output -- +line : want +output (3 lines): + have 1 + have 2 + have 3 +-- +``` + +If `$output` is not longer than one line, it is displayed in +*two-column* format. + #### `assert_success` Fail if `$status` is not 0. @@ -452,67 +510,6 @@ If `$output` is longer than one line, it is displayed in *multi-line* format. -#### `assert_line` - -Depending on the number of parameters, this function tests either that -the output contains the expected line or that the expected line appears -in a specific line of the output identified by its index. - -This function is the opposite of `refute_line()`. - -***Note:*** *Due to a [bug in Bats][bats-93], empty lines are discarded, -causing line indices to change and preventing testing for empty lines.* - -##### Entire output - -When one parameter is specified, fail if `${lines[@]}` does not contain -the line specified by the parameter. - -```bash -@test 'assert_line() in entire output' { - run echo $'have 1\nhave 2\nhave 3' - assert_line 'want' -} -``` - -On failure `$output` and the expected line are displayed. - -``` --- line is not in output -- -line : want -output (3 lines): - have 1 - have 2 - have 3 --- -``` - -If `$output` is not longer than one line, it is displayed in -*two-column* format. - -##### Specific line - -When two parameters are specified, zero-based line index and expected -line respectively, fail if the output line identified by its index in -`${lines[@]}` does not equal the expected line. - -```bash -@test 'assert_line() in specific line' { - run echo $'have 1\nhave 2\nhave 3' - assert_line 1 'want' -} -``` - -On failure the index, and the expected and actual lines are displayed. - -``` --- line differs -- -index : 1 -expected : want -actual : have 2 --- -``` - #### `refute_line` Depending on the number of parameters, this function tests either that diff --git a/lib/bats/batslib.bash b/lib/bats/batslib.bash index 30a23554..a024aab3 100644 --- a/lib/bats/batslib.bash +++ b/lib/bats/batslib.bash @@ -82,31 +82,111 @@ assert_equal() { fi } -# Fail and display an error message if `$output' does not equal the -# expected output as specified either by the first positional parameter -# or on the standard input (by piping or redirection). The error message -# contains the expected and the actual output. +# Fail and display an error message if the expected output does not +# match the actual output. The expected output can be specified either +# by the first parameter or on the standard input. +# +# By default, `$output' is compared against the expected output, and the +# error message contains both values. +# +# When `-l' is used, the -th element of `${lines[@]}' is +# compared. On failure, the error message contains the line index, and +# the expected and actual line at the given index. +# +# When `-L' is used, the test passes if `${lines[@]}' contains the +# expected line. On failure, the expected line and `$output' are +# displayed. # # Globals: # output +# lines +# Options: +# -l - match against the -th element of `$lines' +# -L - pass if the expected line is found in `$output' # Arguments: -# $1 - [opt = STDIN] expected output +# $1 - [opt = STDIN] expected output/line # Returns: -# 0 - expected equals actual output +# 0 - expected matches actual output/line # 1 - otherwise # Inputs: -# STDIN - [opt = $1] expected output +# STDIN - [opt = $1] expected output/line # Outputs: -# STDERR - expected and actual output, on failure +# STDERR - failure details, on failure +# error message, on `-l' missing its argument assert_output() { + # Local variables. + local is_line_match=0 + local is_contained=0 + + # Handle options. + while (( $# > 0 )); do + case "$1" in + -l) + if (( $# < 2 )) || ! [[ $2 =~ ^([0-9]|[1-9][0-9]+)$ ]]; then + echo "\`-l' requires an integer argument" \ + | batslib_decorate 'ERROR: assert_output' \ + | flunk + return "$?" + fi + is_line_match=1 + local -ri idx="$2" + shift 2 + ;; + -L) is_contained=1; shift ;; + --) break ;; + *) break ;; + esac + done + + if (( is_line_match )) && (( is_contained )); then + echo "\`-l' and \`-L' are mutually exclusive" \ + | batslib_decorate 'ERROR: assert_output' \ + | flunk + return "$?" + fi + + # Arguments. local expected (( $# == 0 )) && expected="$(cat -)" || expected="$1" - if [[ $expected != "$output" ]]; then - batslib_print_kv_single_or_multi 8 \ - 'expected' "$expected" \ - 'actual' "$output" \ - | batslib_decorate 'output differs' \ + + # Matching. + if (( is_contained )); then + # Line contained in output. + local temp_line + for temp_line in "${lines[@]}"; do + [[ $temp_line == "$expected" ]] && return 0 + done + { local -ar single=( + 'line' "$expected" + ) + local -ar may_be_multi=( + 'output' "$output" + ) + local -ir width="$( batslib_get_max_single_line_key_width \ + "${single[@]}" "${may_be_multi[@]}" )" + batslib_print_kv_single "$width" "${single[@]}" + batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}" + } | batslib_decorate 'line is not in output' \ | flunk + elif (( is_line_match )); then + # Specific line. + if [[ ${lines[$idx]} != "$expected" ]]; then + batslib_print_kv_single 8 \ + 'index' "$idx" \ + 'expected' "$expected" \ + 'actual' "${lines[$idx]}" \ + | batslib_decorate 'line differs' \ + | flunk + fi + else + # Entire output. + if [[ $output != "$expected" ]]; then + batslib_print_kv_single_or_multi 8 \ + 'expected' "$expected" \ + 'actual' "$output" \ + | batslib_decorate 'output differs' \ + | flunk + fi fi } @@ -168,62 +248,6 @@ assert_failure() { fi } -# Fail and display an error message if `${lines[@]}' does not contain -# the expected line. The error message contains the expected line and -# `$output'. -# -# Optionally, if two positional parameters are specified, the expected -# line is only sought in the line whose index is given in the first -# parameter. In this case, the error message contains the line index, -# and the expected and actual line at the given index. -# -# Globals: -# lines -# output -# Arguments: -# $1 - [opt] zero-based index of line to match against -# $2 - line to look for -# Returns: -# 0 - line found -# 1 - otherwise -# Outputs: -# STDERR - expected line and `$output', on failure -# index, expected and actual line at index, on failure -assert_line() { - if (( $# > 1 )); then - local -ir idx="$1" - local -r line="$2" - - if [[ ${lines[$idx]} != "$line" ]]; then - batslib_print_kv_single 8 \ - 'index' "$idx" \ - 'expected' "$line" \ - 'actual' "${lines[$idx]}" \ - | batslib_decorate 'line differs' \ - | flunk - fi - else - local -r line="$1" - local temp_line - - for temp_line in "${lines[@]}"; do - [[ $temp_line == "$line" ]] && return 0 - done - { local -ar single=( - 'line' "$line" - ) - local -ar may_be_multi=( - 'output' "$output" - ) - local -ir width="$( batslib_get_max_single_line_key_width \ - "${single[@]}" "${may_be_multi[@]}" )" - batslib_print_kv_single "$width" "${single[@]}" - batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}" - } | batslib_decorate 'line is not in output' \ - | flunk - fi -} - # Fail and display an error message if `${lines[@]}' contains the given # line. The error message contains the unexpected line, its index in # `$output', and `$output'. diff --git a/man/batslib.7 b/man/batslib.7 index 1a7ee6fc..0dbd9e27 100644 --- a/man/batslib.7 +++ b/man/batslib.7 @@ -87,9 +87,18 @@ Fail if \fIEXPRESSION\fR evaluates to false\. On failure, \fIEXPRESSION\fR is di Fail if \fIEXPECTED\fR and \fIACTUAL\fR do not equal\. On failure both values are displayed\. If either one is longer than one line, both are displayed in \fImulit\-line\fR format\. . .TP -\fBassert_output\fR [\fIEXPECTED\fR] +\fBassert_output\fR [\-l \fIINDEX\fR|\-L] [\fIEXPECTED\fR] Fail if \fIEXPECTED\fR does not equal \fB$output\fR\. On failure both values are displayed\. If either one is longer than one line, both are displayed in \fImulit\-line\fR format\. When no parameters are specified, \fIEXPECTED\fR is read from the standard input\. . +.IP +When \fB\-l\fR is used, fail if \fIEXPECTED\fR does not equal the \fIINDEX\fR\-th element of \fB${lines[@]}\fR\. On failure, \fIINDEX\fR, \fIEXPECTED\fR and the actual line are displayed\. +. +.IP +When \fB\-L\fR is used, fail if \fIEXPECTED\fR is not found in \fB${lines[@]}\fR\. On failure, \fIEXPECTED\fR and \fB$output\fR are displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulit\-line\fR format\. +. +.IP +\fBNOTE:\fR Due to a bug in Bats, empty lines are discarded, causing line indices to change and preventing testing for empty lines\. See \fIBUGS\fR for more\. +. .TP \fBassert_success\fR Fail if \fB$status\fR is not \fB0\fR\. On failure \fB$status\fR and \fB$output\fR are displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulit\-line\fR format\. @@ -102,16 +111,6 @@ Fail if \fB$status\fR is \fB0\fR\. On failure \fB$output\fR is displayed\. If \f When \fIEXPECTED\fR is specified, fail if it does not equal \fB$status\fR\. On failure \fIEXPECTED\fR, \fB$status\fR and \fB$output\fR are displayed\. . .TP -\fBassert_line\fR [\fIINDEX\fR] \fILINE\fR -When only \fILINE\fR is specified, fail if \fB${lines[@]}\fR does not contain \fILINE\fR\. On failure \fILINE\fR and \fB$output\fR are displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulit\-line\fR format\. -. -.IP -When \fIINDEX\fR is specified, fail if \fILINE\fR does not equal \fB${lines[INDEX]}\fR\. On failure \fIINDEX\fR, \fILINE\fR and \fB${lines[INDEX]}\fR are dispalyed\. -. -.IP -\fBNOTE:\fR Due to a bug in Bats, empty lines are discarded, causing line indices to change and preventing testing for empty lines\. See \fIBUGS\fR for more\. -. -.TP \fBrefute_line\fR [\fIINDEX\fR] \fILINE\fR When only \fILINE\fR is specified, fail if \fB${lines[@]}\fR contains \fILINE\fR\. On failure \fILINE\fR, its zero\-based index in \fB${lines[@]}\fR, and \fB$output\fR are dispalyed\. If \fB$output\fR is longer than one line, it is displayed in \fImulit\-line\fR format with \fILINE\fR highlighted\. . diff --git a/man/batslib.7.ronn b/man/batslib.7.ronn index e5d68d04..267fb602 100644 --- a/man/batslib.7.ronn +++ b/man/batslib.7.ronn @@ -81,12 +81,24 @@ recent invocation of `run`. are displayed. If either one is longer than one line, both are displayed in _mulit-line_ format. -* `assert_output` []: +* `assert_output` [-l |-L] []: Fail if does not equal `$output`. On failure both values are displayed. If either one is longer than one line, both are displayed in _mulit-line_ format. When no parameters are specified, is read from the standard input. + When `-l` is used, fail if does not equal the -th + element of `${lines[@]}`. On failure, , and the + actual line are displayed. + + When `-L` is used, fail if is not found in `${lines[@]}`. + On failure, and `$output` are displayed. If `$output` is + longer than one line, it is displayed in _mulit-line_ format. + + **NOTE:** Due to a bug in Bats, empty lines are discarded, causing + line indices to change and preventing testing for empty lines. See + [BUGS][] for more. + * `assert_success`: Fail if `$status` is not `0`. On failure `$status` and `$output` are displayed. If `$output` is longer than one line, it is displayed in @@ -100,19 +112,6 @@ recent invocation of `run`. When is specified, fail if it does not equal `$status`. On failure , `$status` and `$output` are displayed. -* `assert_line` [] : - When only is specified, fail if `${lines[@]}` does not contain - . On failure and `$output` are displayed. If `$output` is - longer than one line, it is displayed in _mulit-line_ format. - - When is specified, fail if does not equal - `${lines[INDEX]}`. On failure , and `${lines[INDEX]}` - are dispalyed. - - **NOTE:** Due to a bug in Bats, empty lines are discarded, causing - line indices to change and preventing testing for empty lines. See - [BUGS][] for more. - * `refute_line` [] : When only is specified, fail if `${lines[@]}` contains . On failure , its zero-based index in `${lines[@]}`, and diff --git a/test/60-lib-assertion-13-assert_output.bats b/test/60-lib-assertion-13-assert_output.bats index 7514323c..29739455 100755 --- a/test/60-lib-assertion-13-assert_output.bats +++ b/test/60-lib-assertion-13-assert_output.bats @@ -43,7 +43,123 @@ load test_helper } @test 'assert_output() performs literal matching' { - run echo '*' - run assert_output 'a' + run echo 'a' + run assert_output '*' + [ "$status" -eq 1 ] +} + + +# +# -l +# + +@test 'assert_output() -l returns 0 if the expected line is found at the given index' { + run echo $'a\nb\nc' + run assert_output -l 1 'b' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'assert_output() -l returns 1 and displays the index, and the expected and actual line at the given index if they do not equal' { + run echo $'a\nb\nc' + run assert_output -l 1 'd' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 5 ] + [ "${lines[0]}" == '-- line differs --' ] + [ "${lines[1]}" == 'index : 1' ] + [ "${lines[2]}" == 'expected : d' ] + [ "${lines[3]}" == 'actual : b' ] + [ "${lines[4]}" == '--' ] +} + +@test 'assert_output() -l reads the expected output from STDIN when no positional parameters are specified' { + run echo $'a\nb\nc' + export output + run bash -c "lines=${lines[@]}; source '${BATS_LIB}/batslib.bash'; echo 'b' | assert_output -l 1" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'assert_output() -l performs literal matching' { + run echo $'a\nb\nc' + run assert_output -l 1 '*' + [ "$status" -eq 1 ] +} + +@test 'assert_output() -l without returns 1 and displays an error message' { + run echo $'a\nb\nc' + run assert_output -l 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '-- ERROR: assert_output --' ] + [ "${lines[1]}" == "\`-l' requires an integer argument" ] + [ "${lines[2]}" == '--' ] +} + +@test 'assert_output() -l without returns 1 and displays an error message when reading the expected output from STDIN' { + run echo $'a\nb\nc' + run bash -c "source '${BATS_LIB}/batslib.bash'; echo 'a' | assert_output -l" + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '-- ERROR: assert_output --' ] + [ "${lines[1]}" == "\`-l' requires an integer argument" ] + [ "${lines[2]}" == '--' ] +} + + +# +# -L +# + +@test 'assert_output() -L returns 0 if the expected line is found' { + run echo $'a\nb\nc' + run assert_output -L 'b' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'assert_output() -L returns 1 and displays $output and the expected line if it was not found' { + run echo 'a' + run assert_output -L 'b' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 4 ] + [ "${lines[0]}" == '-- line is not in output --' ] + [ "${lines[1]}" == 'line : b' ] + [ "${lines[2]}" == 'output : a' ] + [ "${lines[3]}" == '--' ] +} + +@test 'assert_output() -L displays $output in multi-line format if necessary' { + run echo $'a\nb\nc' + run assert_output -L 'd' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 7 ] + [ "${lines[0]}" == '-- line is not in output --' ] + [ "${lines[1]}" == 'line : d' ] + [ "${lines[2]}" == 'output (3 lines):' ] + [ "${lines[3]}" == ' a' ] + [ "${lines[4]}" == ' b' ] + [ "${lines[5]}" == ' c' ] + [ "${lines[6]}" == '--' ] +} + +@test 'assert_output() -L performs literal matching' { + run echo $'a\nb\nc' + run assert_output -l 1 '*' + [ "$status" -eq 1 ] +} + + +# +# Options. +# + +@test 'assert_output() -l and -L are mutually exclusive' { + run echo $'a\nb\nc' + run assert_output -l 1 -L 'b' [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '-- ERROR: assert_output --' ] + [ "${lines[1]}" == "\`-l' and \`-L' are mutually exclusive" ] + [ "${lines[2]}" == '--' ] } diff --git a/test/60-lib-assertion-16-assert_line.bats b/test/60-lib-assertion-16-assert_line.bats deleted file mode 100755 index 76d8d494..00000000 --- a/test/60-lib-assertion-16-assert_line.bats +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -@test 'assert_line() returns 0 if the expected line is found' { - run echo $'a\nb\nc' - run assert_line 'b' - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 0 ] -} - -@test 'assert_line() returns 1 and displays $output and the expected line if it was not found' { - run echo 'a' - run assert_line 'd' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 4 ] - [ "${lines[0]}" == '-- line is not in output --' ] - [ "${lines[1]}" == 'line : d' ] - [ "${lines[2]}" == 'output : a' ] - [ "${lines[3]}" == '--' ] -} - -@test 'assert_line() displays $output in multi-line format if necessary' { - run echo $'a\nb\nc' - run assert_line 'd' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 7 ] - [ "${lines[0]}" == '-- line is not in output --' ] - [ "${lines[1]}" == 'line : d' ] - [ "${lines[2]}" == 'output (3 lines):' ] - [ "${lines[3]}" == ' a' ] - [ "${lines[4]}" == ' b' ] - [ "${lines[5]}" == ' c' ] - [ "${lines[6]}" == '--' ] -} - -@test 'assert_line() returns 0 if the expected line is found at the given index' { - run echo $'a\nb\nc' - run assert_line 1 'b' - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 0 ] -} - -@test 'assert_line() returns 1 and displays the expected and the actual line at the given index if they do not equal' { - run echo $'a\nb\nc' - run assert_line 1 'd' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 5 ] - [ "${lines[0]}" == '-- line differs --' ] - [ "${lines[1]}" == 'index : 1' ] - [ "${lines[2]}" == 'expected : d' ] - [ "${lines[3]}" == 'actual : b' ] - [ "${lines[4]}" == '--' ] -} - -@test 'assert_line() performs literal matching when the expected line is sought in the entire output' { - run echo $'a\nb\nc' - run assert_line '*' - [ "$status" -eq 1 ] -} - -@test 'assert_line() performs literal matching when the expected line is sought at a given index' { - run echo $'a\nb\nc' - run assert_line 1 '*' - [ "$status" -eq 1 ] -} From 0a6476dec22acccdc3d93da8023428c19ae7412a Mon Sep 17 00:00:00 2001 From: Zoltan Tombol Date: Fri, 28 Aug 2015 22:54:13 +0200 Subject: [PATCH 07/16] Add partial and regex matching to assert_output --- README.md | 213 ++++++++----- lib/bats/batslib.bash | 208 ++++++++---- man/batslib.7 | 17 +- man/batslib.7.ronn | 22 +- test/60-lib-assertion-13-assert_output.bats | 335 ++++++++++++++++---- 5 files changed, 587 insertions(+), 208 deletions(-) diff --git a/README.md b/README.md index bfae8c7c..1f98501d 100644 --- a/README.md +++ b/README.md @@ -347,168 +347,205 @@ actual : have If either value is longer than one line both are displayed in *multi-line* format. -#### `assert_output` - -This function helps to verify that a command or function produces the -correct output. It can compare the entire output, a single line or make -sure that a given line is part of the output. - -***Note:*** *Due to a [bug in Bats][bats-93], empty lines are discarded, -causing line indices to change and preventing testing for empty lines.* - -##### Matching the entire output +#### `assert_success` -By default, fail if `$output` does not equal the expected output. +Fail if `$status` is not 0. ```bash -@test 'assert_output()' { - run echo 'have' - local expected='want' - assert_output "$expected" +@test 'assert_success() status only' { + run bash -c "echo 'have'; exit 1" + assert_success } ``` -The expected output can also be specified on the standard input. +On failure `$status` and `$output` are displayed. + +``` +-- command failed -- +status : 1 +output : have +-- +``` + +If `$output` is longer than one line, it is displayed in *multi-line* +format. + +#### `assert_failure` + +Fail if `$status` is 0. ```bash -@test 'assert_output() with pipe' { +@test 'assert_failure() status only' { run echo 'have' - local expected='want' - echo "$expected" | assert_output + assert_failure } ``` -On failure the expected and actual outputs are displayed. +On failure `$output` is displayed. ``` --- output differs -- -expected : want -actual : have +-- command succeeded, but it was expected to fail -- +output : have -- ``` -If either value is longer than one line both are displayed in -*multi-line* format. +If `$output` is longer than one line, it is displayed in *multi-line* +format. -##### Matching a single line +##### Expected status -When `-l ` is used, fail if the expected line does not equal the -output line identified by its index in `${lines[@]}`. +When one parameter is specified, fail if `$status` does not equal the +expected status specified by the parameter. ```bash -@test 'assert_line() in specific line' { - run echo $'have 0\nhave 1\nhave 2' - assert_output -l 1 'want' +@test 'assert_failure() with expected status' { + run bash -c "echo 'error'; exit 1" + assert_failure 2 } ``` -On failure the index, and the expected and actual lines are displayed. +On failure the expected and actual status, and `$output` are displayed. ``` --- line differs -- -index : 1 -expected : want -actual : have 1 +-- command failed as expected, but status differs -- +expected : 2 +actual : 1 +output : error -- ``` -##### Line contained in output +If `$output` is longer than one line, it is displayed in *multi-line* +format. -When `-L` is used, fail if `${lines[@]}` does not contain the expected -line. +#### `assert_output` + +This functions helps to verify that a command or function produces the +correct output. It can match the entire output, a specific line, and +even look for a line in the output. Matching can be literal, partial or +regular expression. + +##### Matching the entire output + +By default, the entire `$output` is matched, and the assertion fails if +it does not equal the expected output. ```bash -@test 'assert_line() in entire output' { - run echo $'have 0\nhave 1\nhave 2' - assert_output -L 'want' +@test 'assert_output()' { + run echo 'have' + assert_output 'want' } ``` -On failure `$output` and the expected line are displayed. +The expected output can be specified on the standard input as well. +```bash +@test 'assert_output() with pipe' { + run echo 'have' + echo 'want' | assert_output +} ``` --- line is not in output -- -line : want -output (3 lines): - have 1 - have 2 - have 3 + +On failure, the expected and actual outputs are displayed. + +``` +-- output differs -- +expected : want +actual : have -- ``` -If `$output` is not longer than one line, it is displayed in -*two-column* format. +If either value is longer than one line both are displayed in +*multi-line* format. -#### `assert_success` +##### Matching a specific line -Fail if `$status` is not 0. +***Note:*** *Due to a [bug in Bats][bats-93], empty lines are discarded +from `${lines[@]}`, causing line indices to change and preventing +testing for empty lines.* + +When `-l ` is used, only the line specified by its `index` in +`${lines[@]}` is matched, and the assertion fails if it does not equal +the expected line. ```bash -@test 'assert_success() status only' { - run bash -c "echo 'have'; exit 1" - assert_success +@test 'assert_output() specific line' { + run echo $'have-0\nhave-1\nhave-2' + assert_output -l 1 'want-1' } ``` -On failure `$status` and `$output` are displayed. +On failure, the index, and the expected and actual line are displayed. ``` --- command failed -- -status : 1 -output : have +-- line differs -- +index : 1 +expected : want-1 +actual : have-1 -- ``` -If `$output` is longer than one line, it is displayed in *multi-line* -format. +##### Line found in output -#### `assert_failure` +***Note:*** *Due to a [bug in Bats][bats-93], empty lines are discarded +from `${lines[@]}`, causing line indices to change and preventing +testing for empty lines.* -Fail if `$status` is 0. +When `-L` is used, fail if the expected line is not found in +`${lines[@]}`. ```bash -@test 'assert_failure() status only' { - run echo 'have' - assert_failure +@test 'assert_output() line found in output' { + run echo $'have-0\nhave-1\nhave-2' + assert_output -L 'want' } ``` -On failure `$output` is displayed. +On failure, the expected line and `$output` are displayed. ``` --- command succeeded, but it was expected to fail -- -output : have +-- output does not contain line -- +line : want +output (3 lines): + have-0 + have-1 + have-2 -- ``` -If `$output` is longer than one line, it is displayed in *multi-line* -format. +If `$output` is not longer than one line, it is displayed in +*two-column* format. -##### Expected status +##### Partial matching -When one parameter is specified, fail if `$status` does not equal the -expected status specified by the parameter. +Partial matching, enabled using `-p`, provides more flexibility than the +default literal matching. The assertion fails if the expected output as +a substring can not be found in the output. ```bash -@test 'assert_failure() with expected status' { - run bash -c "echo 'error'; exit 1" - assert_failure 2 +@test 'assert_output() partial matching' { + run echo 'ERROR: no such file or directory' + assert_output -p 'ERROR' } ``` -On failure the expected and actual status, and `$output` are displayed. +This option can be used with `-l` or `-L` as well, and does not change +the set of information displayed on failure. -``` --- command failed as expected, but status differs -- -expected : 2 -actual : 1 -output : error --- -``` +##### Regular expression matching -If `$output` is longer than one line, it is displayed in *multi-line* -format. +Regular expression matching, enabled using `-r`, provides the most +flexibility. The assertion fails if the expected output specified as an +extended regular expression does not match the output. + +```bash +@test 'assert_output() regular expression matching' { + run echo 'Foobar v0.1.0' + assert_output -r '^Foobar v[0-9]+\.[0-9]+\.[0-9]$' +} +``` +This option can be used with `-l` or `-L` as well, and does not change +the set of information displayed on failure. #### `refute_line` diff --git a/lib/bats/batslib.bash b/lib/bats/batslib.bash index a024aab3..f2036d7e 100644 --- a/lib/bats/batslib.bash +++ b/lib/bats/batslib.bash @@ -82,41 +82,47 @@ assert_equal() { fi } -# Fail and display an error message if the expected output does not -# match the actual output. The expected output can be specified either -# by the first parameter or on the standard input. +# Fail and display an error message if the expected does not match the +# actual output. The expected output can be specified either by the +# first parameter or on the standard input. # -# By default, `$output' is compared against the expected output, and the +# By default, the expected output is compared against `$output', and the # error message contains both values. # -# When `-l' is used, the -th element of `${lines[@]}' is -# compared. On failure, the error message contains the line index, and -# the expected and actual line at the given index. +# Option `-l ' compares against `${lines[]}'. The error +# message contains the compared lines and . # -# When `-L' is used, the test passes if `${lines[@]}' contains the -# expected line. On failure, the expected line and `$output' are -# displayed. +# Option `-L' compares against all lines in `${lines[@]}' until a match +# is found. If no match is found, the function fails and the error +# message contains the expected line and `$output'. +# +# By default, literal matching is performed. Option `-p' and `-r' change +# this to partial (substring) and regular expression (extended) +# matching, respectively. # # Globals: # output # lines # Options: -# -l - match against the -th element of `$lines' -# -L - pass if the expected line is found in `$output' +# -l - match against the -th element of `${lines[@]}' +# -L - match against all elements of `${lines[@]}' until one matches +# -p - substring match +# -r - extended regular expression match # Arguments: -# $1 - [opt = STDIN] expected output/line +# $1 - [=STDIN] expected output # Returns: -# 0 - expected matches actual output/line +# 0 - expected matches the actual output # 1 - otherwise # Inputs: -# STDIN - [opt = $1] expected output/line +# STDIN - [=$1] expected output # Outputs: -# STDERR - failure details, on failure -# error message, on `-l' missing its argument +# STDERR - assertion details, on failure +# error message, on error assert_output() { - # Local variables. - local is_line_match=0 - local is_contained=0 + local -i is_match_line=0 + local -i is_match_contained=0 + local -i is_mode_partial=0 + local -i is_mode_regex=0 # Handle options. while (( $# > 0 )); do @@ -126,66 +132,156 @@ assert_output() { echo "\`-l' requires an integer argument" \ | batslib_decorate 'ERROR: assert_output' \ | flunk - return "$?" + return $? fi - is_line_match=1 + is_match_line=1 local -ri idx="$2" shift 2 ;; - -L) is_contained=1; shift ;; + -L) is_match_contained=1; shift ;; + -p) is_mode_partial=1; shift ;; + -r) is_mode_regex=1; shift ;; --) break ;; *) break ;; esac done - if (( is_line_match )) && (( is_contained )); then + if (( is_match_line )) && (( is_match_contained )); then echo "\`-l' and \`-L' are mutually exclusive" \ | batslib_decorate 'ERROR: assert_output' \ | flunk - return "$?" + return $? + fi + + if (( is_mode_partial )) && (( is_mode_regex )); then + echo "\`-p' and \`-r' are mutually exclusive" \ + | batslib_decorate 'ERROR: assert_output' \ + | flunk + return $? fi # Arguments. local expected (( $# == 0 )) && expected="$(cat -)" || expected="$1" + if (( is_mode_regex == 1 )) && [[ '' =~ $expected ]] || (( $? == 2 )); then + echo "Invalid extended regular expression: \`$expected'" \ + | batslib_decorate 'ERROR: assert_output' \ + | flunk + return $? + fi + # Matching. - if (( is_contained )); then + if (( is_match_contained )); then # Line contained in output. - local temp_line - for temp_line in "${lines[@]}"; do - [[ $temp_line == "$expected" ]] && return 0 - done - { local -ar single=( - 'line' "$expected" - ) - local -ar may_be_multi=( - 'output' "$output" - ) - local -ir width="$( batslib_get_max_single_line_key_width \ - "${single[@]}" "${may_be_multi[@]}" )" - batslib_print_kv_single "$width" "${single[@]}" - batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}" - } | batslib_decorate 'line is not in output' \ - | flunk - elif (( is_line_match )); then - # Specific line. - if [[ ${lines[$idx]} != "$expected" ]]; then - batslib_print_kv_single 8 \ - 'index' "$idx" \ - 'expected' "$expected" \ - 'actual' "${lines[$idx]}" \ - | batslib_decorate 'line differs' \ + if (( is_mode_regex )); then + local -i idx + for (( idx = 0; idx < ${#lines[@]}; ++idx )); do + [[ ${lines[$idx]} =~ $expected ]] && return 0 + done + { local -ar single=( + 'regex' "$expected" + ) + local -ar may_be_multi=( + 'output' "$output" + ) + local -ir width="$( batslib_get_max_single_line_key_width \ + "${single[@]}" "${may_be_multi[@]}" )" + batslib_print_kv_single "$width" "${single[@]}" + batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}" + } | batslib_decorate 'no output line matches regular expression' \ + | flunk + elif (( is_mode_partial )); then + local -i idx + for (( idx = 0; idx < ${#lines[@]}; ++idx )); do + [[ ${lines[$idx]} == *"$expected"* ]] && return 0 + done + { local -ar single=( + 'substring' "$expected" + ) + local -ar may_be_multi=( + 'output' "$output" + ) + local -ir width="$( batslib_get_max_single_line_key_width \ + "${single[@]}" "${may_be_multi[@]}" )" + batslib_print_kv_single "$width" "${single[@]}" + batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}" + } | batslib_decorate 'no output line contains substring' \ | flunk + else + local -i idx + for (( idx = 0; idx < ${#lines[@]}; ++idx )); do + [[ ${lines[$idx]} == "$expected" ]] && return 0 + done + { local -ar single=( + 'line' "$expected" + ) + local -ar may_be_multi=( + 'output' "$output" + ) + local -ir width="$( batslib_get_max_single_line_key_width \ + "${single[@]}" "${may_be_multi[@]}" )" + batslib_print_kv_single "$width" "${single[@]}" + batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}" + } | batslib_decorate 'output does not contain line' \ + | flunk + fi + elif (( is_match_line )); then + # Specific line. + if (( is_mode_regex )); then + if ! [[ ${lines[$idx]} =~ $expected ]]; then + batslib_print_kv_single 5 \ + 'index' "$idx" \ + 'regex' "$expected" \ + 'line' "${lines[$idx]}" \ + | batslib_decorate 'regular expression does not match line' \ + | flunk + fi + elif (( is_mode_partial )); then + if [[ ${lines[$idx]} != *"$expected"* ]]; then + batslib_print_kv_single 9 \ + 'index' "$idx" \ + 'substring' "$expected" \ + 'line' "${lines[$idx]}" \ + | batslib_decorate 'line does not contain substring' \ + | flunk + fi + else + if [[ ${lines[$idx]} != "$expected" ]]; then + batslib_print_kv_single 8 \ + 'index' "$idx" \ + 'expected' "$expected" \ + 'actual' "${lines[$idx]}" \ + | batslib_decorate 'line differs' \ + | flunk + fi fi else # Entire output. - if [[ $output != "$expected" ]]; then - batslib_print_kv_single_or_multi 8 \ - 'expected' "$expected" \ - 'actual' "$output" \ - | batslib_decorate 'output differs' \ - | flunk + if (( is_mode_regex )); then + if ! [[ $output =~ $expected ]]; then + batslib_print_kv_single_or_multi 6 \ + 'regex' "$expected" \ + 'output' "$output" \ + | batslib_decorate 'regular expression does not match output' \ + | flunk + fi + elif (( is_mode_partial )); then + if [[ $output != *"$expected"* ]]; then + batslib_print_kv_single_or_multi 9 \ + 'substring' "$expected" \ + 'output' "$output" \ + | batslib_decorate 'output does not contain substring' \ + | flunk + fi + else + if [[ $output != "$expected" ]]; then + batslib_print_kv_single_or_multi 8 \ + 'expected' "$expected" \ + 'actual' "$output" \ + | batslib_decorate 'output differs' \ + | flunk + fi fi fi } diff --git a/man/batslib.7 b/man/batslib.7 index 0dbd9e27..0d07b000 100644 --- a/man/batslib.7 +++ b/man/batslib.7 @@ -87,7 +87,7 @@ Fail if \fIEXPRESSION\fR evaluates to false\. On failure, \fIEXPRESSION\fR is di Fail if \fIEXPECTED\fR and \fIACTUAL\fR do not equal\. On failure both values are displayed\. If either one is longer than one line, both are displayed in \fImulit\-line\fR format\. . .TP -\fBassert_output\fR [\-l \fIINDEX\fR|\-L] [\fIEXPECTED\fR] +\fBassert_output\fR [\-l \fIINDEX\fR|\-L] [\-p|\-r] [\fIEXPECTED\fR] Fail if \fIEXPECTED\fR does not equal \fB$output\fR\. On failure both values are displayed\. If either one is longer than one line, both are displayed in \fImulit\-line\fR format\. When no parameters are specified, \fIEXPECTED\fR is read from the standard input\. . .IP @@ -97,7 +97,16 @@ When \fB\-l\fR is used, fail if \fIEXPECTED\fR does not equal the \fIINDEX\fR\-t When \fB\-L\fR is used, fail if \fIEXPECTED\fR is not found in \fB${lines[@]}\fR\. On failure, \fIEXPECTED\fR and \fB$output\fR are displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulit\-line\fR format\. . .IP -\fBNOTE:\fR Due to a bug in Bats, empty lines are discarded, causing line indices to change and preventing testing for empty lines\. See \fIBUGS\fR for more\. +The default, literal matching can be changed to substring and regular expression matching\. +. +.IP +Using \fB\-p\fR enables partial matching, and the assertion fails if \fIEXPECTED\fR as a substring is not found in the output\. +. +.IP +Using \fB\-r\fR enables regular expression matching, and the assertion fails if \fIEXPECTED\fR as an extended regular expression does not match the output\. +. +.IP +\fBNOTE:\fR Due to a bug in Bats, empty lines are discarded from \fB${lines[@]}\fR, causing line indices to change and preventing testing for empty lines\. See \fIBUGS\fR for more\. . .TP \fBassert_success\fR @@ -126,10 +135,10 @@ When \fIINDEX\fR is specified, fail if \fILINE\fR equals \fB${lines[INDEX]}\fR\. Report bugs on Bats\' GitHub issue tracker at \fIhttps://github\.com/sstephenson/bats/issues\fR\. . .SS "Known bugs" -Due to a bug in Bats, empty lines are missing from \fB${lines[@]}\fR, causing line indices to change and preventing testing for empty lines when using \fBassert_line\fR and \fBrefute_line\fR\. See PR #93 on Github at \fIhttps://github\.com/sstephenson/bats/pull/93\fR\. +Due to a bug in Bats, empty lines are missing from \fB${lines[@]}\fR, causing line indices to change and preventing testing for empty lines when using \fBassert_output\fR and \fBrefute_line\fR\. See PR #93 on Github at \fIhttps://github\.com/sstephenson/bats/pull/93\fR\. . .SH "COPYRIGHT" TODO(ztombol): Find a suitable license\. . .SH "SEE ALSO" -\fBbash\fR(1), \fBbats\fR(1), \fBbats\fR(7) +\fBbash\fR(1), \fBbats\fR(1), \fBbats\fR(7), \fBregex(7)\fR diff --git a/man/batslib.7.ronn b/man/batslib.7.ronn index 267fb602..489d67af 100644 --- a/man/batslib.7.ronn +++ b/man/batslib.7.ronn @@ -81,7 +81,7 @@ recent invocation of `run`. are displayed. If either one is longer than one line, both are displayed in _mulit-line_ format. -* `assert_output` [-l |-L] []: +* `assert_output` [-l |-L] [-p|-r] []: Fail if does not equal `$output`. On failure both values are displayed. If either one is longer than one line, both are displayed in _mulit-line_ format. When no parameters are specified, @@ -95,9 +95,19 @@ recent invocation of `run`. On failure, and `$output` are displayed. If `$output` is longer than one line, it is displayed in _mulit-line_ format. - **NOTE:** Due to a bug in Bats, empty lines are discarded, causing - line indices to change and preventing testing for empty lines. See - [BUGS][] for more. + The default, literal matching can be changed to substring and regular + expression matching. + + Using `-p` enables partial matching, and the assertion fails if + as a substring is not found in the output. + + Using `-r` enables regular expression matching, and the assertion + fails if as an extended regular expression does not match + the output. + + **NOTE:** Due to a bug in Bats, empty lines are discarded from + `${lines[@]}`, causing line indices to change and preventing testing + for empty lines. See [BUGS][] for more. * `assert_success`: Fail if `$status` is not `0`. On failure `$status` and `$output` are @@ -138,7 +148,7 @@ Report bugs on Bats' GitHub issue tracker at Due to a bug in Bats, empty lines are missing from `${lines[@]}`, causing line indices to change and preventing testing for empty lines -when using `assert_line` and `refute_line`. See PR \#93 on Github at +when using `assert_output` and `refute_line`. See PR \#93 on Github at . @@ -149,4 +159,4 @@ TODO(ztombol): Find a suitable license. ## SEE ALSO -`bash`(1), `bats`(1), `bats`(7) +`bash`(1), `bats`(1), `bats`(7), `regex(7)` diff --git a/test/60-lib-assertion-13-assert_output.bats b/test/60-lib-assertion-13-assert_output.bats index 29739455..9de2b2da 100755 --- a/test/60-lib-assertion-13-assert_output.bats +++ b/test/60-lib-assertion-13-assert_output.bats @@ -2,14 +2,21 @@ load test_helper -@test 'assert_output() returns 0 if $output equals the expected output' { + +# +# Matching entire output. +# + +# Literal matching. + +@test "assert_output() : returns 0 if equals \`\$output'" { run echo 'a' run assert_output 'a' [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } -@test 'assert_output() returns 1 and displays the expected and actual output if they do not equal' { +@test "assert_output() : returns 1 and displays details if does not equal \`\$output'" { run echo 'b' run assert_output 'a' [ "$status" -eq 1 ] @@ -20,8 +27,8 @@ load test_helper [ "${lines[3]}" == '--' ] } -@test 'assert_output() displays the expected and actual output in multi-line format if necessary' { - run echo $'b 1\nb 2' +@test "assert_output() : displays details in multi-line format if \`\$output' is longer than one line" { + run echo $'b 0\nb 1' run assert_output 'a' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 7 ] @@ -29,64 +36,168 @@ load test_helper [ "${lines[1]}" == 'expected (1 lines):' ] [ "${lines[2]}" == ' a' ] [ "${lines[3]}" == 'actual (2 lines):' ] - [ "${lines[4]}" == ' b 1' ] - [ "${lines[5]}" == ' b 2' ] + [ "${lines[4]}" == ' b 0' ] + [ "${lines[5]}" == ' b 1' ] + [ "${lines[6]}" == '--' ] +} + +@test "assert_output() : displays details in multi-line format if is longer than one line" { + run echo 'b' + run assert_output $'a 0\na 1' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 7 ] + [ "${lines[0]}" == '-- output differs --' ] + [ "${lines[1]}" == 'expected (2 lines):' ] + [ "${lines[2]}" == ' a 0' ] + [ "${lines[3]}" == ' a 1' ] + [ "${lines[4]}" == 'actual (1 lines):' ] + [ "${lines[5]}" == ' b' ] [ "${lines[6]}" == '--' ] } -@test 'assert_output() reads the expected output from STDIN when no positional parameters are specified' { +@test 'assert_output() : performs literal matching by default' { + run echo 'a' + run assert_output '*' + [ "$status" -eq 1 ] +} + +@test 'assert_output(): reads the expected output from STDIN' { run echo 'a' export output - run bash -c "source '${BATS_LIB}/batslib.bash'; echo 'a' | assert_output" + run bash -c ". '${BATS_LIB}/batslib.bash'; echo 'a' | assert_output" [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } -@test 'assert_output() performs literal matching' { - run echo 'a' - run assert_output '*' +# Partial matching: `-p '. + +@test "assert_output() -p : returns 0 if is a substring in \`\$output'" { + run echo $'a\nb\nc' + run assert_output -p 'b' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "assert_output() -p : returns 1 and displays details if is not a substring in \`\$output'" { + run echo 'b' + run assert_output -p 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 4 ] + [ "${lines[0]}" == '-- output does not contain substring --' ] + [ "${lines[1]}" == 'substring : a' ] + [ "${lines[2]}" == 'output : b' ] + [ "${lines[3]}" == '--' ] +} + +@test "assert_output() -p : displays details in multi-line format if \`\$output' is longer than one line" { + run echo $'b 0\nb 1' + run assert_output -p 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 7 ] + [ "${lines[0]}" == '-- output does not contain substring --' ] + [ "${lines[1]}" == 'substring (1 lines):' ] + [ "${lines[2]}" == ' a' ] + [ "${lines[3]}" == 'output (2 lines):' ] + [ "${lines[4]}" == ' b 0' ] + [ "${lines[5]}" == ' b 1' ] + [ "${lines[6]}" == '--' ] +} + +@test 'assert_output() -p : displays details in multi-line format if is longer than one line' { + run echo 'b' + run assert_output -p $'a 0\na 1' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 7 ] + [ "${lines[0]}" == '-- output does not contain substring --' ] + [ "${lines[1]}" == 'substring (2 lines):' ] + [ "${lines[2]}" == ' a 0' ] + [ "${lines[3]}" == ' a 1' ] + [ "${lines[4]}" == 'output (1 lines):' ] + [ "${lines[5]}" == ' b' ] + [ "${lines[6]}" == '--' ] +} + +# Regular expression matching: `-r '. + +@test "assert_output() -r : returns 0 if matches \`\$output'" { + run echo $'a\nb\nc' + run assert_output -r '.*b.*' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "assert_output() -r : returns 1 and displays details if does not match \`\$output'" { + run echo 'b' + run assert_output -r '.*a.*' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 4 ] + [ "${lines[0]}" == '-- regular expression does not match output --' ] + [ "${lines[1]}" == 'regex : .*a.*' ] + [ "${lines[2]}" == 'output : b' ] + [ "${lines[3]}" == '--' ] +} + +@test "assert_output() -r : displays details in multi-line format if \`\$output' is longer than one line" { + run echo $'b 0\nb 1' + run assert_output -r '.*a.*' [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 7 ] + [ "${lines[0]}" == '-- regular expression does not match output --' ] + [ "${lines[1]}" == 'regex (1 lines):' ] + [ "${lines[2]}" == ' .*a.*' ] + [ "${lines[3]}" == 'output (2 lines):' ] + [ "${lines[4]}" == ' b 0' ] + [ "${lines[5]}" == ' b 1' ] + [ "${lines[6]}" == '--' ] +} + +@test "assert_output() -r : displays details in multi-line format if is longer than one line" { + run echo 'b' + run assert_output -r $'.*a\nb.*' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 7 ] + [ "${lines[0]}" == '-- regular expression does not match output --' ] + [ "${lines[1]}" == 'regex (2 lines):' ] + [ "${lines[2]}" == ' .*a' ] + [ "${lines[3]}" == ' b.*' ] + [ "${lines[4]}" == 'output (1 lines):' ] + [ "${lines[5]}" == ' b' ] + [ "${lines[6]}" == '--' ] } # -# -l +# Matching a single line: `-l '. # -@test 'assert_output() -l returns 0 if the expected line is found at the given index' { +# Literal matching. + +@test "assert_output() -l : returns 0 if equals \`\${lines[]}'" { run echo $'a\nb\nc' run assert_output -l 1 'b' [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } -@test 'assert_output() -l returns 1 and displays the index, and the expected and actual line at the given index if they do not equal' { +@test "assert_output() -l : returns 1 and displays details if does not equal \`\${lines[]}'" { run echo $'a\nb\nc' - run assert_output -l 1 'd' + run assert_output -l 1 'a' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 5 ] [ "${lines[0]}" == '-- line differs --' ] [ "${lines[1]}" == 'index : 1' ] - [ "${lines[2]}" == 'expected : d' ] + [ "${lines[2]}" == 'expected : a' ] [ "${lines[3]}" == 'actual : b' ] [ "${lines[4]}" == '--' ] } -@test 'assert_output() -l reads the expected output from STDIN when no positional parameters are specified' { - run echo $'a\nb\nc' - export output - run bash -c "lines=${lines[@]}; source '${BATS_LIB}/batslib.bash'; echo 'b' | assert_output -l 1" - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 0 ] -} - -@test 'assert_output() -l performs literal matching' { +@test 'assert_output() -l : performs literal matching by default' { run echo $'a\nb\nc' run assert_output -l 1 '*' [ "$status" -eq 1 ] } -@test 'assert_output() -l without returns 1 and displays an error message' { +@test 'assert_output() -l: without returns 1 and displays an error message' { run echo $'a\nb\nc' run assert_output -l 'a' [ "$status" -eq 1 ] @@ -96,66 +207,164 @@ load test_helper [ "${lines[2]}" == '--' ] } -@test 'assert_output() -l without returns 1 and displays an error message when reading the expected output from STDIN' { +# Partial matching: `-p '. + +@test "assert_output() -l -p : returns 0 if is a substring in \`\${lines[]}'" { + run echo $'a\nabc\nc' + run assert_output -l 1 -p 'b' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "assert_output() -l -p : returns 1 and displays details if is not a substring in \`\${lines[]}'" { + run echo $'b 0\nb 1' + run assert_output -l 1 -p 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 5 ] + [ "${lines[0]}" == '-- line does not contain substring --' ] + [ "${lines[1]}" == 'index : 1' ] + [ "${lines[2]}" == 'substring : a' ] + [ "${lines[3]}" == 'line : b 1' ] + [ "${lines[4]}" == '--' ] +} + +# Regular expression matching: `-r '. + +@test "assert_output() -l -r : returns 0 if matches \`\${lines[]}'" { + run echo $'a\nabc\nc' + run assert_output -l 1 -r '.*b.*' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "assert_output() -l -r : returns 1 and displays details if does not match \`\${lines[]}'" { run echo $'a\nb\nc' - run bash -c "source '${BATS_LIB}/batslib.bash'; echo 'a' | assert_output -l" + run assert_output -l 1 -r '.*a.*' [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 3 ] - [ "${lines[0]}" == '-- ERROR: assert_output --' ] - [ "${lines[1]}" == "\`-l' requires an integer argument" ] - [ "${lines[2]}" == '--' ] + [ "${#lines[@]}" -eq 5 ] + [ "${lines[0]}" == '-- regular expression does not match line --' ] + [ "${lines[1]}" == 'index : 1' ] + [ "${lines[2]}" == 'regex : .*a.*' ] + [ "${lines[3]}" == 'line : b' ] + [ "${lines[4]}" == '--' ] } # -# -L +# Containing a line: `-L'. # -@test 'assert_output() -L returns 0 if the expected line is found' { +# Literal matching. + +@test "assert_output() -L : returns 0 if is a line in \`\${lines[@]}'" { run echo $'a\nb\nc' run assert_output -L 'b' [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } -@test 'assert_output() -L returns 1 and displays $output and the expected line if it was not found' { +@test "assert_output() -L : returns 1 and displays details if is not a line in \`\${lines[@]}'" { + run echo 'b' + run assert_output -L 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 4 ] + [ "${lines[0]}" == '-- output does not contain line --' ] + [ "${lines[1]}" == 'line : a' ] + [ "${lines[2]}" == 'output : b' ] + [ "${lines[3]}" == '--' ] +} + +@test "assert_output() -L : displays \`\$output' in multi-line format if necessary" { + run echo $'b 0\nb 1' + run assert_output -L 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 6 ] + [ "${lines[0]}" == '-- output does not contain line --' ] + [ "${lines[1]}" == 'line : a' ] + [ "${lines[2]}" == 'output (2 lines):' ] + [ "${lines[3]}" == ' b 0' ] + [ "${lines[4]}" == ' b 1' ] + [ "${lines[5]}" == '--' ] +} + +@test 'assert_output() -L : performs literal matching by default' { run echo 'a' - run assert_output -L 'b' + run assert_output -L '*' + [ "$status" -eq 1 ] +} + +# Partial matching: `-p '. + +@test "assert_output() -L -p : returns 0 if is a substring in at least one line in \`\${lines[@]}'" { + run echo $'a\nabc\nc' + run assert_output -L -p 'b' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "assert_output() -L -p : returns 1 and displays details if is not a substring in any line in \`\${lines[@]}'" { + run echo 'b' + run assert_output -L -p 'a' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 4 ] - [ "${lines[0]}" == '-- line is not in output --' ] - [ "${lines[1]}" == 'line : b' ] - [ "${lines[2]}" == 'output : a' ] + [ "${lines[0]}" == '-- no output line contains substring --' ] + [ "${lines[1]}" == 'substring : a' ] + [ "${lines[2]}" == 'output : b' ] [ "${lines[3]}" == '--' ] } -@test 'assert_output() -L displays $output in multi-line format if necessary' { - run echo $'a\nb\nc' - run assert_output -L 'd' +@test "assert_output() -L -p : displays details in multi-line format if necessary" { + run echo $'b 0\nb 1' + run assert_output -L -p 'a' [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 7 ] - [ "${lines[0]}" == '-- line is not in output --' ] - [ "${lines[1]}" == 'line : d' ] - [ "${lines[2]}" == 'output (3 lines):' ] - [ "${lines[3]}" == ' a' ] - [ "${lines[4]}" == ' b' ] - [ "${lines[5]}" == ' c' ] - [ "${lines[6]}" == '--' ] + [ "${#lines[@]}" -eq 6 ] + [ "${lines[0]}" == '-- no output line contains substring --' ] + [ "${lines[1]}" == 'substring : a' ] + [ "${lines[2]}" == 'output (2 lines):' ] + [ "${lines[3]}" == ' b 0' ] + [ "${lines[4]}" == ' b 1' ] + [ "${lines[5]}" == '--' ] } -@test 'assert_output() -L performs literal matching' { +# Regular expression matching: `-r '. + +@test "assert_output() -L -r : returns 0 if matches any line in \`\${lines[@]}'" { run echo $'a\nb\nc' - run assert_output -l 1 '*' + run assert_output -L -r '.*b.*' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "assert_output() -L -r : returns 1 and displays details if does not match any lines in \`\${lines[@]}'" { + run echo 'b' + run assert_output -L -r '.*a.*' [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 4 ] + [ "${lines[0]}" == '-- no output line matches regular expression --' ] + [ "${lines[1]}" == 'regex : .*a.*' ] + [ "${lines[2]}" == 'output : b' ] + [ "${lines[3]}" == '--' ] +} + +@test 'assert_output() -L -r : displays details in multi-line format if necessary' { + run echo $'b 0\nb 1' + run assert_output -L -r '.*a.*' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 6 ] + [ "${lines[0]}" == '-- no output line matches regular expression --' ] + [ "${lines[1]}" == 'regex : .*a.*' ] + [ "${lines[2]}" == 'output (2 lines):' ] + [ "${lines[3]}" == ' b 0' ] + [ "${lines[4]}" == ' b 1' ] + [ "${lines[5]}" == '--' ] } # -# Options. +# Common. # @test 'assert_output() -l and -L are mutually exclusive' { - run echo $'a\nb\nc' run assert_output -l 1 -L 'b' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 3 ] @@ -163,3 +372,21 @@ load test_helper [ "${lines[1]}" == "\`-l' and \`-L' are mutually exclusive" ] [ "${lines[2]}" == '--' ] } + +@test 'assert_output() -p and -r are mutually exclusive' { + run assert_output -p -r 'b' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '-- ERROR: assert_output --' ] + [ "${lines[1]}" == "\`-p' and \`-r' are mutually exclusive" ] + [ "${lines[2]}" == '--' ] +} + +@test "assert_output() -r : returns 1 and displays an error message if is not a valid extended regular expression" { + run assert_output -r '[.*' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '-- ERROR: assert_output --' ] + [ "${lines[1]}" == "Invalid extended regular expression: \`[.*'" ] + [ "${lines[2]}" == '--' ] +} From eaeaf52f57af9b6d1907f8ea491cf411ec547b17 Mon Sep 17 00:00:00 2001 From: Zoltan Tombol Date: Sun, 6 Sep 2015 22:42:42 +0200 Subject: [PATCH 08/16] Add refute_line and move refute_line into it --- README.md | 145 ++++++-- lib/bats/batslib.bash | 298 ++++++++++++---- man/batslib.7 | 34 +- man/batslib.7.ronn | 42 ++- test/60-lib-assertion-16-refute_output.bats | 355 ++++++++++++++++++++ test/60-lib-assertion-17-refute_line.bats | 67 ---- 6 files changed, 755 insertions(+), 186 deletions(-) create mode 100755 test/60-lib-assertion-16-refute_output.bats delete mode 100755 test/60-lib-assertion-17-refute_line.bats diff --git a/README.md b/README.md index 1f98501d..bce905fd 100644 --- a/README.md +++ b/README.md @@ -547,70 +547,155 @@ extended regular expression does not match the output. This option can be used with `-l` or `-L` as well, and does not change the set of information displayed on failure. -#### `refute_line` +#### `refute_output` -Depending on the number of parameters, this function tests either that -the output does not contain the unexpected line or that the unexpected -line does not appear in a specific line of the output identified by its -index. +Similarly to `assert_output`, this function also helps to verify that a +command or function produces the correct output. `refute_output` is the +logical complement of `assert_output`. Instead of testing that the +output matches the expected output, `refute_output` tests that the +output does not match the unexpected output. It can match the entire +output, a specific line, and even look for a line in the output. +Matching can be literal, partial or regular expression. -This function is the opposite of `assert_line()`. +##### Matching the entire output + +By default, the entire `$output` is matched, and the assertion fails if +it equals the unexpected output. + +```bash +@test 'refute_output()' { + run echo 'want' + refute_output 'want' +} +``` + +The unexpected output can be specified on the standard input as well. + +```bash +@test 'refute_output() with pipe' { + run echo 'want' + echo 'want' | refute_output +} +``` -***Note:*** *Due to a [bug in Bats][bats-93], empty lines are discarded, -causing line indices to change and preventing testing for empty lines.* +On failure, the unexpected output is displayed. +``` +-- output equals, but it was expected to differ -- +output : want +-- +``` -##### Entire output +If `$output` is longer than one line, it is displayed in *multi-line* +format. -When one parameter is specified, fail if `${lines[@]}` contains the line -specified by the parameter. +##### Matching a specific line + +***Note:*** *Due to a [bug in Bats][bats-93], empty lines are discarded +from `${lines[@]}`, causing line indices to change and preventing +testing for empty lines.* + +When `-l ` is used, only the line specified by its `index` in +`${lines[@]}` is matched, and the assertion fails if it equals the +expected line. ```bash -@test 'refute_line() in entire output' { - run echo $'have 1\nwant\nhave 2' - refute_line 'want' +@test 'refute_output() specific line' { + run echo $'have-0\nwant-1\nhave-2' + refute_output -l 1 'want-1' } ``` -On failure the unexpected line, its zero-based index, and `$output` with -the unexpected line highlighted are displayed. +On failure, the index, and the unexpected line are displayed. + +``` +-- line should differ -- +index : 1 +unexpected : want-1 +-- +``` + +##### Line found in output + +***Note:*** *Due to a [bug in Bats][bats-93], empty lines are discarded +from `${lines[@]}`, causing line indices to change and preventing +testing for empty lines.* + +When `-L` is used, fail if the unexpected line is found in +`${lines[@]}`. + +```bash +@test 'refute_output() line found in output' { + run echo $'have-0\nwant\nhave-2' + refute_output -L 'want' +} +``` + +On failure, the unexpected line, its index in `${lines[@]}` and +`$output` with the unexpected line highlighted are displayed. ``` -- line should not be in output -- line : want index : 1 output (3 lines): - have 1 + have-0 > want - have 2 + have-2 -- ``` If `$output` is not longer than one line, it is displayed in -*two-column* format. +*two-column* format without highlighting. -##### Specific line +##### Partial matching -When two parameters are specified, zero-based line index and unexpected -line respectively, fail if the output line identified by its index in -`${lines[@]}` equals the unexpected line. +Partial matching, enabled using `-p`, provides more flexibility than the +default literal matching. The assertion fails if the unexpected output as +a substring can be found in the output. ```bash -@test 'refute_line() in specific line' { - run echo $'have 1\nwant\nhave 2' - refute_line 1 'want' +@test 'refute_output() partial matching' { + run echo 'ERROR: no such file or directory' + refute_output -p 'ERROR' } ``` -On failure the unexpected line and its index are displayed. +On failure, this option displays the substring in addition. ``` --- line should differ from expected -- -index : 1 -line : want +-- output should not contain substring -- +substring : ERROR +output : ERROR: no such file or directory +-- +``` + +This option can be used with `-l` or `-L` as well. + +##### Regular expression matching + +Regular expression matching, enabled using `-r`, provides the most +flexibility. The assertion fails if the expected output specified as an +extended regular expression does not match the output. + +```bash +@test 'refute_output() regular expression matching' { + run echo 'Foobar v0.1.0' + refute_output -r '^Foobar v[0-9]+\.[0-9]+\.[0-9]$' +} +``` + +On failure, this option displays the regular expression in addition. + +``` +-- regular expression should not match output -- +regex : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$ +output : Foobar v0.1.0 -- ``` +This option can be used with `-l` or `-L` as well. + [bats-93]: https://github.com/sstephenson/bats/pull/93 diff --git a/lib/bats/batslib.bash b/lib/bats/batslib.bash index f2036d7e..c5b51213 100644 --- a/lib/bats/batslib.bash +++ b/lib/bats/batslib.bash @@ -286,6 +286,239 @@ assert_output() { fi } +# Fail and display an error message if the unexpected matches the actual +# output. The unexpected output can be specified either by the first +# parameter or on the standard input. +# +# By default, the unexpected output is compared against `$output', and +# the error message contains this value. +# +# Option `-l ' compares against `${lines[]}'. The error +# message contains the compared lines and . +# +# Option `-L' compares against all lines in `${lines[@]}' until a match +# is found. If a match is found, the function fails and the error +# message contains the unexpected line, its index and `${lines[@]}'. +# +# By default, literal matching is performed. Option `-p' and `-r' change +# this to partial (substring) and regular expression (extended) +# matching, respectively. On failure, the substring and regular +# expression is added to the error message. +# +# Globals: +# output +# lines +# Options: +# -l - match against the -th element of `${lines[@]}' +# -L - match against all elements of `${lines[@]}' until one matches +# -p - substring match +# -r - extended regular expression match +# Arguments: +# $1 - [=STDIN] unexpected output +# Returns: +# 0 - unexpected does not match the actual output +# 1 - otherwise +# Inputs: +# STDIN - [=$1] unexpected output +# Outputs: +# STDERR - assertion details, on failure +# error message, on error +refute_output() { + local -i is_match_line=0 + local -i is_match_contained=0 + local -i is_mode_partial=0 + local -i is_mode_regex=0 + + # Handle options. + while (( $# > 0 )); do + case "$1" in + -l) + if (( $# < 2 )) || ! [[ $2 =~ ^([0-9]|[1-9][0-9]+)$ ]]; then + echo "\`-l' requires an integer argument" \ + | batslib_decorate 'ERROR: refute_output' \ + | flunk + return $? + fi + is_match_line=1 + local -ri idx="$2" + shift 2 + ;; + -L) is_match_contained=1; shift ;; + -p) is_mode_partial=1; shift ;; + -r) is_mode_regex=1; shift ;; + --) break ;; + *) break ;; + esac + done + + if (( is_match_line )) && (( is_match_contained )); then + echo "\`-l' and \`-L' are mutually exclusive" \ + | batslib_decorate 'ERROR: refute_output' \ + | flunk + return $? + fi + + if (( is_mode_partial )) && (( is_mode_regex )); then + echo "\`-p' and \`-r' are mutually exclusive" \ + | batslib_decorate 'ERROR: refute_output' \ + | flunk + return $? + fi + + # Arguments. + local unexpected + (( $# == 0 )) && unexpected="$(cat -)" || unexpected="$1" + + if (( is_mode_regex == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then + echo "Invalid extended regular expression: \`$unexpected'" \ + | batslib_decorate 'ERROR: refute_output' \ + | flunk + return $? + fi + + # Matching. + if (( is_match_contained )); then + # Line contained in output. + if (( is_mode_regex )); then + local -i idx + for (( idx = 0; idx < ${#lines[@]}; ++idx )); do + if [[ ${lines[$idx]} =~ $unexpected ]]; then + { local -ar single=( + 'regex' "$unexpected" + 'index' "$idx" + ) + local -a may_be_multi=( + 'output' "$output" + ) + local -ir width="$( batslib_get_max_single_line_key_width \ + "${single[@]}" "${may_be_multi[@]}" )" + batslib_print_kv_single "$width" "${single[@]}" + if batslib_is_single_line "${may_be_multi[1]}"; then + batslib_print_kv_single "$width" "${may_be_multi[@]}" + else + may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" \ + | batslib_prefix \ + | batslib_mark '>' "$idx" )" + batslib_print_kv_multi "${may_be_multi[@]}" + fi + } | batslib_decorate 'no line should match the regular expression' \ + | flunk + return $? + fi + done + elif (( is_mode_partial )); then + local -i idx + for (( idx = 0; idx < ${#lines[@]}; ++idx )); do + if [[ ${lines[$idx]} == *"$unexpected"* ]]; then + { local -ar single=( + 'substring' "$unexpected" + 'index' "$idx" + ) + local -a may_be_multi=( + 'output' "$output" + ) + local -ir width="$( batslib_get_max_single_line_key_width \ + "${single[@]}" "${may_be_multi[@]}" )" + batslib_print_kv_single "$width" "${single[@]}" + if batslib_is_single_line "${may_be_multi[1]}"; then + batslib_print_kv_single "$width" "${may_be_multi[@]}" + else + may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" \ + | batslib_prefix \ + | batslib_mark '>' "$idx" )" + batslib_print_kv_multi "${may_be_multi[@]}" + fi + } | batslib_decorate 'no line should contain substring' \ + | flunk + return $? + fi + done + else + local -i idx + for (( idx = 0; idx < ${#lines[@]}; ++idx )); do + if [[ ${lines[$idx]} == "$unexpected" ]]; then + { local -ar single=( + 'line' "$unexpected" + 'index' "$idx" + ) + local -a may_be_multi=( + 'output' "$output" + ) + local -ir width="$( batslib_get_max_single_line_key_width \ + "${single[@]}" "${may_be_multi[@]}" )" + batslib_print_kv_single "$width" "${single[@]}" + if batslib_is_single_line "${may_be_multi[1]}"; then + batslib_print_kv_single "$width" "${may_be_multi[@]}" + else + may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" \ + | batslib_prefix \ + | batslib_mark '>' "$idx" )" + batslib_print_kv_multi "${may_be_multi[@]}" + fi + } | batslib_decorate 'line should not be in output' \ + | flunk + return $? + fi + done + fi + elif (( is_match_line )); then + # Specific line. + if (( is_mode_regex )); then + if [[ ${lines[$idx]} =~ $unexpected ]] || (( $? == 0 )); then + batslib_print_kv_single 5 \ + 'index' "$idx" \ + 'regex' "$unexpected" \ + 'line' "${lines[$idx]}" \ + | batslib_decorate 'regular expression should not match line' \ + | flunk + fi + elif (( is_mode_partial )); then + if [[ ${lines[$idx]} == *"$unexpected"* ]]; then + batslib_print_kv_single 9 \ + 'index' "$idx" \ + 'substring' "$unexpected" \ + 'line' "${lines[$idx]}" \ + | batslib_decorate 'line should not contain substring' \ + | flunk + fi + else + if [[ ${lines[$idx]} == "$unexpected" ]]; then + batslib_print_kv_single 10 \ + 'index' "$idx" \ + 'unexpected' "$unexpected" \ + | batslib_decorate 'line should differ' \ + | flunk + fi + fi + else + # Entire output. + if (( is_mode_regex )); then + if [[ $output =~ $unexpected ]] || (( $? == 0 )); then + batslib_print_kv_single_or_multi 6 \ + 'regex' "$unexpected" \ + 'output' "$output" \ + | batslib_decorate 'regular expression should not match output' \ + | flunk + fi + elif (( is_mode_partial )); then + if [[ $output == *"$unexpected"* ]]; then + batslib_print_kv_single_or_multi 9 \ + 'substring' "$unexpected" \ + 'output' "$output" \ + | batslib_decorate 'output should not contain substring' \ + | flunk + fi + else + if [[ $output == "$unexpected" ]]; then + batslib_print_kv_single_or_multi 6 \ + 'output' "$output" \ + | batslib_decorate 'output equals, but it was expected to differ' \ + | flunk + fi + fi + fi +} + # Fail and display an error message if `$status' is not 0. The error # message contains `$status' and `$output'. # @@ -343,68 +576,3 @@ assert_failure() { | flunk fi } - -# Fail and display an error message if `${lines[@]}' contains the given -# line. The error message contains the unexpected line, its index in -# `$output', and `$output'. -# -# Optionally, if two positional parameters are specified, the unexpected -# line is only sought in the line whose index is given in the first -# parameter. In this case, the error message contains the line index, -# and the unexpected line. -# -# Globals: -# lines -# output -# Arguments: -# $1 - [opt] zero-based index of line to match against -# $2 - line to look for -# Returns: -# 0 - line not found -# 1 - otherwise -# Outputs: -# STDERR - unexpected line, its index and `$output', on failure -# index and unexpected line, on failure -refute_line() { - if (( $# > 1 )); then - local -ir idx="$1" - local -r line="$2" - - if [[ ${lines[$idx]} == "$line" ]]; then - batslib_print_kv_single 5 \ - 'index' "$idx" \ - 'line' "$line" \ - | batslib_decorate 'line should differ from expected' \ - | flunk - fi - else - local -r line="$1" - - local idx - for (( idx = 0; idx < ${#lines[@]}; ++idx )); do - if [[ ${lines[$idx]} == "$line" ]]; then - { local -ar single=( - 'line' "$line" - 'index' "$idx" - ) - local -a may_be_multi=( - 'output' "$output" - ) - local -ir width="$( batslib_get_max_single_line_key_width \ - "${single[@]}" "${may_be_multi[@]}" )" - batslib_print_kv_single "$width" "${single[@]}" - if batslib_is_single_line "${may_be_multi[1]}"; then - batslib_print_kv_single "$width" "${may_be_multi[@]}" - else - may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" \ - | batslib_prefix \ - | batslib_mark '>' "$idx" )" - batslib_print_kv_multi "${may_be_multi[@]}" - fi - } | batslib_decorate 'line should not be in output' \ - | flunk - return 1 - fi - done - fi -} diff --git a/man/batslib.7 b/man/batslib.7 index 0d07b000..56bb48c7 100644 --- a/man/batslib.7 +++ b/man/batslib.7 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BATSLIB" "7" "August 2015" "" "" +.TH "BATSLIB" "7" "September 2015" "" "" . .SH "NAME" \fBbatslib\fR \- Bats Standard Library of Test Helpers @@ -109,6 +109,28 @@ Using \fB\-r\fR enables regular expression matching, and the assertion fails if \fBNOTE:\fR Due to a bug in Bats, empty lines are discarded from \fB${lines[@]}\fR, causing line indices to change and preventing testing for empty lines\. See \fIBUGS\fR for more\. . .TP +\fBrefute_output\fR [\-l \fIINDEX\fR|\-L] [\-p|\-r] [\fIUNEXPECTED\fR] +Fail if \fIUNEXPECTED\fR equals \fB$output\fR\. On failure, \fB$output\fR is displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulti\-line\fR format\. When no parameters are specified, \fIUNEXPECTED\fR is read from the standard input\. +. +.IP +When \fB\-l\fR is used, fail if \fIUNEXPECTED\fR equals the \fIINDEX\fR\-th element of \fB${lines[@]}\fR\. On failure, \fIINDEX\fR and \fIUNEXPECTED\fR are displayed\. +. +.IP +When \fB\-L\fR is used, fail if \fIUNEXPECTED\fR is found in \fB${lines[@]}\fR\. On failure, \fIUNEXPECTED\fR, its index in \fB${lines[@]}\fR and \fB$output\fR are displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulti\-line\fR format with the unexpected line highlighted\. +. +.IP +The default, literal matching can be changed to substring and regular expression matching\. +. +.IP +Using \fB\-p\fR enables partial matching, and the assertion fails if \fIUNEXPECTED\fR as a substring is found in the output\. On failure, \fIUNEXPECTED\fR is displayed in addition\. +. +.IP +Using \fB\-r\fR enables regular expression matching, and the assertion fails if \fIUNEXPECTED\fR as an extended regular expression matches the output\. On failure, \fIUNEXPECTED\fR is displayed in addition\. +. +.IP +\fBNOTE:\fR Due to a bug in Bats, empty lines are discarded from \fB${lines[@]}\fR, causing line indices to change and preventing testing for empty lines\. See \fIBUGS\fR for more\. +. +.TP \fBassert_success\fR Fail if \fB$status\fR is not \fB0\fR\. On failure \fB$status\fR and \fB$output\fR are displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulit\-line\fR format\. . @@ -119,16 +141,6 @@ Fail if \fB$status\fR is \fB0\fR\. On failure \fB$output\fR is displayed\. If \f .IP When \fIEXPECTED\fR is specified, fail if it does not equal \fB$status\fR\. On failure \fIEXPECTED\fR, \fB$status\fR and \fB$output\fR are displayed\. . -.TP -\fBrefute_line\fR [\fIINDEX\fR] \fILINE\fR -When only \fILINE\fR is specified, fail if \fB${lines[@]}\fR contains \fILINE\fR\. On failure \fILINE\fR, its zero\-based index in \fB${lines[@]}\fR, and \fB$output\fR are dispalyed\. If \fB$output\fR is longer than one line, it is displayed in \fImulit\-line\fR format with \fILINE\fR highlighted\. -. -.IP -When \fIINDEX\fR is specified, fail if \fILINE\fR equals \fB${lines[INDEX]}\fR\. On failure \fILINE\fR and \fIINDEX\fR are dispalyed\. -. -.IP -\fBNOTE:\fR Due to a bug in Bats, empty lines are discarded, causing line indices to change and preventing testing for empty lines\. See \fIBUGS\fR for more\. -. .SH "BUGS" . .SS "Report bugs" diff --git a/man/batslib.7.ronn b/man/batslib.7.ronn index 489d67af..42dba5c9 100644 --- a/man/batslib.7.ronn +++ b/man/batslib.7.ronn @@ -109,6 +109,35 @@ recent invocation of `run`. `${lines[@]}`, causing line indices to change and preventing testing for empty lines. See [BUGS][] for more. +* `refute_output` [-l |-L] [-p|-r] []: + Fail if equals `$output`. On failure, `$output` is + displayed. If `$output` is longer than one line, it is displayed in + _multi-line_ format. When no parameters are specified, is + read from the standard input. + + When `-l` is used, fail if equals the -th element + of `${lines[@]}`. On failure, and are displayed. + + When `-L` is used, fail if is found in `${lines[@]}`. On + failure, , its index in `${lines[@]}` and `$output` are + displayed. If `$output` is longer than one line, it is displayed in + _multi-line_ format with the unexpected line highlighted. + + The default, literal matching can be changed to substring and regular + expression matching. + + Using `-p` enables partial matching, and the assertion fails if + as a substring is found in the output. On failure, + is displayed in addition. + + Using `-r` enables regular expression matching, and the assertion + fails if as an extended regular expression matches the + output. On failure, is displayed in addition. + + **NOTE:** Due to a bug in Bats, empty lines are discarded from + `${lines[@]}`, causing line indices to change and preventing testing + for empty lines. See [BUGS][] for more. + * `assert_success`: Fail if `$status` is not `0`. On failure `$status` and `$output` are displayed. If `$output` is longer than one line, it is displayed in @@ -122,19 +151,6 @@ recent invocation of `run`. When is specified, fail if it does not equal `$status`. On failure , `$status` and `$output` are displayed. -* `refute_line` [] : - When only is specified, fail if `${lines[@]}` contains . - On failure , its zero-based index in `${lines[@]}`, and - `$output` are dispalyed. If `$output` is longer than one line, it is - displayed in _mulit-line_ format with highlighted. - - When is specified, fail if equals `${lines[INDEX]}`. On - failure and are dispalyed. - - **NOTE:** Due to a bug in Bats, empty lines are discarded, causing - line indices to change and preventing testing for empty lines. See - [BUGS][] for more. - ## BUGS diff --git a/test/60-lib-assertion-16-refute_output.bats b/test/60-lib-assertion-16-refute_output.bats new file mode 100755 index 00000000..850f48b2 --- /dev/null +++ b/test/60-lib-assertion-16-refute_output.bats @@ -0,0 +1,355 @@ +#!/usr/bin/env bats + +load test_helper + + +# +# Matching entire output. +# + +# Literal matching. + +@test "refute_output() : returns 0 if does not equal \`\$output'" { + run echo 'b' + run refute_output 'a' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "refute_output() : returns 1 and displays details if equals \`\$output'" { + run echo 'a' + run refute_output 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '-- output equals, but it was expected to differ --' ] + [ "${lines[1]}" == 'output : a' ] + [ "${lines[2]}" == '--' ] +} + +@test "refute_output() : displays details in multi-line format if necessary" { + run echo $'a 0\na 1' + run refute_output $'a 0\na 1' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 5 ] + [ "${lines[0]}" == '-- output equals, but it was expected to differ --' ] + [ "${lines[1]}" == 'output (2 lines):' ] + [ "${lines[2]}" == ' a 0' ] + [ "${lines[3]}" == ' a 1' ] + [ "${lines[4]}" == '--' ] +} + +@test 'refute_output() : performs literal matching by default' { + run echo 'a' + run refute_output '*' + [ "$status" -eq 0 ] +} + +@test 'refute_output(): reads the unexpected output from STDIN' { + run echo 'b' + export output + run bash -c ". '${BATS_LIB}/batslib.bash'; echo 'a' | refute_output" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +# Partial matching: `-p '. + +@test "refute_output() -p : returns 0 if is not a substring in \`\$output'" { + run echo $'a\nb\nc' + run refute_output -p 'd' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "refute_output() -p : returns 1 and displays details if is a substring in \`\$output'" { + run echo 'a' + run refute_output -p 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 4 ] + [ "${lines[0]}" == '-- output should not contain substring --' ] + [ "${lines[1]}" == 'substring : a' ] + [ "${lines[2]}" == 'output : a' ] + [ "${lines[3]}" == '--' ] +} + +@test "refute_output() -p : displays details in multi-line format if necessary" { + run echo $'a 0\na 1' + run refute_output -p 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 7 ] + [ "${lines[0]}" == '-- output should not contain substring --' ] + [ "${lines[1]}" == 'substring (1 lines):' ] + [ "${lines[2]}" == ' a' ] + [ "${lines[3]}" == 'output (2 lines):' ] + [ "${lines[4]}" == ' a 0' ] + [ "${lines[5]}" == ' a 1' ] + [ "${lines[6]}" == '--' ] +} + +# Regular expression matching: `-r '. + +@test "refute_output() -r : returns 0 if does not match \`\$output'" { + run echo $'a\nb\nc' + run refute_output -r '.*d.*' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "refute_output() -r : returns 1 and displays details if matches \`\$output'" { + run echo 'a' + run refute_output -r '.*a.*' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 4 ] + [ "${lines[0]}" == '-- regular expression should not match output --' ] + [ "${lines[1]}" == 'regex : .*a.*' ] + [ "${lines[2]}" == 'output : a' ] + [ "${lines[3]}" == '--' ] +} + +@test "refute_output() -r : displays details in multi-line format if necessary" { + run echo $'a 0\na 1' + run refute_output -r '.*a.*' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 7 ] + [ "${lines[0]}" == '-- regular expression should not match output --' ] + [ "${lines[1]}" == 'regex (1 lines):' ] + [ "${lines[2]}" == ' .*a.*' ] + [ "${lines[3]}" == 'output (2 lines):' ] + [ "${lines[4]}" == ' a 0' ] + [ "${lines[5]}" == ' a 1' ] + [ "${lines[6]}" == '--' ] +} + + +# +# Matching a single line: `-l '. +# + +# Literal matching. + +@test "refute_output() -l : returns 0 if does not equal \`\${lines[]}'" { + run echo $'a\nb\nc' + run refute_output -l 1 'd' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "refute_output() -l : returns 1 and displays details if equals \`\${lines[]}'" { + run echo $'a\nb\nc' + run refute_output -l 1 'b' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 4 ] + [ "${lines[0]}" == '-- line should differ --' ] + [ "${lines[1]}" == 'index : 1' ] + [ "${lines[2]}" == 'unexpected : b' ] + [ "${lines[3]}" == '--' ] +} + +@test 'refute_output() -l : performs literal matching by default' { + run echo $'a\nb\nc' + run refute_output -l 1 '*' + [ "$status" -eq 0 ] +} + +@test 'refute_output() -l: without returns 1 and displays an error message' { + run echo $'a\nb\nc' + run refute_output -l 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '-- ERROR: refute_output --' ] + [ "${lines[1]}" == "\`-l' requires an integer argument" ] + [ "${lines[2]}" == '--' ] +} + +# Partial matching: `-p '. + +@test "refute_output() -l -p : returns 0 if is not a substring in \`\${lines[]}'" { + run echo $'a\nabc\nc' + run refute_output -l 1 -p 'd' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "refute_output() -l -p : returns 1 and displays details if is a substring in \`\${lines[]}'" { + run echo $'a\nabc\nc' + run refute_output -l 1 -p 'b' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 5 ] + [ "${lines[0]}" == '-- line should not contain substring --' ] + [ "${lines[1]}" == 'index : 1' ] + [ "${lines[2]}" == 'substring : b' ] + [ "${lines[3]}" == 'line : abc' ] + [ "${lines[4]}" == '--' ] +} + +# Regular expression matching: `-r '. + +@test "refute_output() -l -r : returns 0 if does not match \`\${lines[]}'" { + run echo $'a\nabc\nc' + run refute_output -l 1 -r '.*d.*' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "refute_output() -l -r : returns 1 and displays details if matches \`\${lines[]}'" { + run echo $'a\nabc\nc' + run refute_output -l 1 -r '.*b.*' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 5 ] + [ "${lines[0]}" == '-- regular expression should not match line --' ] + [ "${lines[1]}" == 'index : 1' ] + [ "${lines[2]}" == 'regex : .*b.*' ] + [ "${lines[3]}" == 'line : abc' ] + [ "${lines[4]}" == '--' ] +} + + +# +# Containing a line: `-L'. +# + +# Literal matching. + +@test "refute_output() -L : returns 0 if is not a line in \`\${lines[@]}'" { + run echo $'a\nb\nc' + run refute_output -L 'd' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "refute_output() -L : returns 1 and displays details if is not a line in \`\${lines[@]}'" { + run echo 'a' + run refute_output -L 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 5 ] + [ "${lines[0]}" == '-- line should not be in output --' ] + [ "${lines[1]}" == 'line : a' ] + [ "${lines[2]}" == 'index : 0' ] + [ "${lines[3]}" == 'output : a' ] + [ "${lines[4]}" == '--' ] +} + +@test "refute_output() -L : displays details in multi-line format if necessary" { + run echo $'a\nb\nc' + run refute_output -L 'b' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 8 ] + [ "${lines[0]}" == '-- line should not be in output --' ] + [ "${lines[1]}" == 'line : b' ] + [ "${lines[2]}" == 'index : 1' ] + [ "${lines[3]}" == 'output (3 lines):' ] + [ "${lines[4]}" == ' a' ] + [ "${lines[5]}" == '> b' ] + [ "${lines[6]}" == ' c' ] + [ "${lines[7]}" == '--' ] +} + +@test 'refute_output() -L : performs literal matching by default' { + run echo 'a' + run refute_output -L '*' + [ "$status" -eq 0 ] +} + +# Partial matching: `-p '. + +@test "refute_output() -L -p : returns 0 if is not a substring in any line in \`\${lines[@]}'" { + run echo $'a\nb\nc' + run refute_output -L -p 'd' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "refute_output() -L -p : returns 1 and displays details if is a substring in at least one line in \`\${lines[@]}'" { + run echo 'a' + run refute_output -L -p 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 5 ] + [ "${lines[0]}" == '-- no line should contain substring --' ] + [ "${lines[1]}" == 'substring : a' ] + [ "${lines[2]}" == 'index : 0' ] + [ "${lines[3]}" == 'output : a' ] + [ "${lines[4]}" == '--' ] +} + +@test "refute_output() -L -p : displays details in multi-line format if necessary" { + run echo $'a\nabc\nc' + run refute_output -L -p 'b' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 8 ] + [ "${lines[0]}" == '-- no line should contain substring --' ] + [ "${lines[1]}" == 'substring : b' ] + [ "${lines[2]}" == 'index : 1' ] + [ "${lines[3]}" == 'output (3 lines):' ] + [ "${lines[4]}" == ' a' ] + [ "${lines[5]}" == '> abc' ] + [ "${lines[6]}" == ' c' ] + [ "${lines[7]}" == '--' ] +} + +# Regular expression matching: `-r '. + +@test "refute_output() -L -r : returns 0 if does not match any line in \`\${lines[@]}'" { + run echo $'a\nb\nc' + run refute_output -L -r '.*d.*' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "refute_output() -L -r : returns 1 and displays details if matches any lines in \`\${lines[@]}'" { + run echo 'a' + run refute_output -L -r '.*a.*' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 5 ] + [ "${lines[0]}" == '-- no line should match the regular expression --' ] + [ "${lines[1]}" == 'regex : .*a.*' ] + [ "${lines[2]}" == 'index : 0' ] + [ "${lines[3]}" == 'output : a' ] + [ "${lines[4]}" == '--' ] +} + +@test 'refute_output() -L -r : displays details in multi-line format if necessary' { + run echo $'a\nabc\nc' + run refute_output -L -r '.*b.*' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 8 ] + [ "${lines[0]}" == '-- no line should match the regular expression --' ] + [ "${lines[1]}" == 'regex : .*b.*' ] + [ "${lines[2]}" == 'index : 1' ] + [ "${lines[3]}" == 'output (3 lines):' ] + [ "${lines[4]}" == ' a' ] + [ "${lines[5]}" == '> abc' ] + [ "${lines[6]}" == ' c' ] + [ "${lines[7]}" == '--' ] +} + + +# +# Common. +# + +@test 'refute_output() -l and -L are mutually exclusive' { + run refute_output -l 1 -L 'b' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '-- ERROR: refute_output --' ] + [ "${lines[1]}" == "\`-l' and \`-L' are mutually exclusive" ] + [ "${lines[2]}" == '--' ] +} + +@test 'refute_output() -p and -r are mutually exclusive' { + run refute_output -p -r 'b' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '-- ERROR: refute_output --' ] + [ "${lines[1]}" == "\`-p' and \`-r' are mutually exclusive" ] + [ "${lines[2]}" == '--' ] +} + +@test "refute_output() -r : returns 1 and displays an error message if is not a valid extended regular expression" { + run refute_output -r '[.*' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '-- ERROR: refute_output --' ] + [ "${lines[1]}" == "Invalid extended regular expression: \`[.*'" ] + [ "${lines[2]}" == '--' ] +} diff --git a/test/60-lib-assertion-17-refute_line.bats b/test/60-lib-assertion-17-refute_line.bats deleted file mode 100755 index 4303dab6..00000000 --- a/test/60-lib-assertion-17-refute_line.bats +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -@test 'refute_line() returns 0 if the unexpected line is not found' { - run echo $'a\nb\nc' - run refute_line 'd' - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 0 ] -} - -@test 'refute_line() returns 1 and displays $output, the unexpected line and its index if it was found' { - run echo $'b' - run refute_line 'b' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 5 ] - [ "${lines[0]}" == '-- line should not be in output --' ] - [ "${lines[1]}" == 'line : b' ] - [ "${lines[2]}" == 'index : 0' ] - [ "${lines[3]}" == 'output : b' ] - [ "${lines[4]}" == '--' ] -} - -@test 'refute_line() displays $output in multi-line format with the unexpected line highlighted if necessary' { - run echo $'a\nb\nc' - run refute_line 'b' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 8 ] - [ "${lines[0]}" == '-- line should not be in output --' ] - [ "${lines[1]}" == 'line : b' ] - [ "${lines[2]}" == 'index : 1' ] - [ "${lines[3]}" == 'output (3 lines):' ] - [ "${lines[4]}" == ' a' ] - [ "${lines[5]}" == '> b' ] - [ "${lines[6]}" == ' c' ] - [ "${lines[7]}" == '--' ] -} - -@test 'refute_line() returns 0 if the unexpected line is not found at the given index' { - run echo $'a\nb\nc' - run refute_line 1 'd' - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 0 ] -} - -@test 'refute_line() returns 1 and displays the unexpected line and the index if it was found at the given index' { - run echo $'a\nb\nc' - run refute_line 1 'b' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 4 ] - [ "${lines[0]}" == '-- line should differ from expected --' ] - [ "${lines[1]}" == 'index : 1' ] - [ "${lines[2]}" == 'line : b' ] - [ "${lines[3]}" == '--' ] -} - -@test 'refute_line() performs literal matching when the unexpected line is sought in the entire output' { - run echo $'a\nb\nc' - run refute_line '*' - [ "$status" -eq 0 ] -} - -@test 'refute_line() performs literal matching when the unexpected line is sought at a given index' { - run echo $'a\nb\nc' - run refute_line 1 '*' - [ "$status" -eq 0 ] -} From 6cc3f4af58fd46da1bc5c72df9a6a6d2ef72f6db Mon Sep 17 00:00:00 2001 From: Zoltan Tombol Date: Tue, 8 Sep 2015 21:19:40 +0200 Subject: [PATCH 09/16] Add $status and $output to the output of assert --- README.md | 19 +++++++++++------ lib/bats/batslib.bash | 32 ++++++++++++++++++++-------- man/batslib.7 | 4 ++-- man/batslib.7.ronn | 8 +++---- test/60-lib-assertion-11-assert.bats | 27 ++++++++++++++++++----- 5 files changed, 63 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index bce905fd..f84fece7 100644 --- a/README.md +++ b/README.md @@ -305,22 +305,27 @@ Fail if the given expression evaluates to false. ```bash @test 'assert()' { - local value=1 - assert [ "$value" -eq 0 ] + run touch '/var/log/test.log' + assert [ -e '/var/log/test.log' ] } ``` -On failure the failed expression is displayed. +***Note:*** *The expression must be a simple command. [Compound +commands](https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands), +such as `[[', can be used only when executed with `bash -c'.* + +On failure the failed expression, `$status' and `$output' are displayed. ``` -- assertion failed -- -condition : [ 1 -eq 0 ] +expression : [ -e /var/log/test.log ] +status : 1 +output : touch: cannot touch ‘/var/log/test.log’: Permission denied -- ``` -***Note:*** *The expression can be a simple command only. [Compund -commands] (https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands), -such as `[[,` are not supported.* +If `$output` is longer than one line, it is displayed in *multi-line* +format. #### `assert_equal` diff --git a/lib/bats/batslib.bash b/lib/bats/batslib.bash index c5b51213..207b49ec 100644 --- a/lib/bats/batslib.bash +++ b/lib/bats/batslib.bash @@ -39,22 +39,36 @@ flunk() { return 1 } -# Fail and display the given condition if it evaluates to false. Only -# simple commands can be used in the expression given to `assert'. -# Compound commands, such as `[[', are not supported. +# Fail and display an error message if the expression evaluates to +# false. The expression must be a simple command. Compound commands, +# such as `[[', can be used only when executed with `bash -c'. The error +# message contains the expression, `$status' and `$output'. # # Globals: -# none +# status +# output # Arguments: -# $@ - condition to evaluate +# $0 - expression to evaluate # Returns: -# 0 - condition evaluated to TRUE -# 1 - condition evaluated to FALSE +# 0 - expression evaluates to TRUE +# 1 - expression evaluates to FALSE # Outputs: -# STDERR - failed condition, on failure +# STDERR - assertion details, on failure assert() { if ! "$@"; then - echo "condition : $@" | batslib_decorate 'assertion failed' | flunk + { local -ar single=( + 'expression' "$*" + 'status' "$status" + ) + local -ar may_be_multi=( + 'output' "$output" + ) + local -ir width="$( batslib_get_max_single_line_key_width \ + "${single[@]}" "${may_be_multi[@]}" )" + batslib_print_kv_single "$width" "${single[@]}" + batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}" + } | batslib_decorate 'assertion failed' \ + | flunk fi } diff --git a/man/batslib.7 b/man/batslib.7 index 56bb48c7..674f533c 100644 --- a/man/batslib.7 +++ b/man/batslib.7 @@ -77,10 +77,10 @@ Display \fIMESSAGE\fR and fail\. This function provides a convenient way to repo . .TP \fBassert\fR \fIEXPRESSION\fR -Fail if \fIEXPRESSION\fR evaluates to false\. On failure, \fIEXPRESSION\fR is displayed\. +Fail if \fIEXPRESSION\fR evaluates to false\. On failure, \fIEXPRESSION\fR, \fB$status\fR and \fB$output\fR is displayed\. . .IP -\fBNote:\fR \fIEXPRESSION\fR can only be a simple command\. Compound commands, such as \fB[[\fR, are not supported\. +\fBNote:\fR \fIEXPRESSION\fR must be a simple command\. Compound commands, such as \fB[[\fR, can be used only when executed with \fBbash \-c\fR\. . .TP \fBassert_equal\fR \fIEXPECTED\fR \fIACTUAL\fR diff --git a/man/batslib.7.ronn b/man/batslib.7.ronn index 42dba5c9..d90a8deb 100644 --- a/man/batslib.7.ronn +++ b/man/batslib.7.ronn @@ -70,11 +70,11 @@ recent invocation of `run`. from the standard input. * `assert` : - Fail if evaluates to false. On failure, is - displayed. + Fail if evaluates to false. On failure, , + `$status` and `$output` is displayed. - **Note:** can only be a simple command. Compound - commands, such as `[[`, are not supported. + **Note:** must be a simple command. Compound commands, + such as `[[`, can be used only when executed with `bash -c`. * `assert_equal` : Fail if and do not equal. On failure both values diff --git a/test/60-lib-assertion-11-assert.bats b/test/60-lib-assertion-11-assert.bats index 003ab0e2..0652819b 100755 --- a/test/60-lib-assertion-11-assert.bats +++ b/test/60-lib-assertion-11-assert.bats @@ -2,17 +2,34 @@ load test_helper -@test 'assert() returns 0 if the condition evaluates to TRUE' { +@test 'assert() : returns 0 if evaluates to TRUE' { run assert true [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } -@test 'assert() returns 1 and displays the condition if it evaluates to FALSE' { +@test 'assert() : returns 1 and displays details if evaluates to FALSE' { + run bash -c 'echo "error"; false' run assert false [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 3 ] + [ "${#lines[@]}" -eq 5 ] [ "${lines[0]}" == '-- assertion failed --' ] - [ "${lines[1]}" == 'condition : false' ] - [ "${lines[2]}" == '--' ] + [ "${lines[1]}" == 'expression : false' ] + [ "${lines[2]}" == 'status : 1' ] + [ "${lines[3]}" == 'output : error' ] + [ "${lines[4]}" == '--' ] +} + +@test "assert() : displays details in multi-line format if \`\$output' is longer than one line" { + run bash -c "echo $'0. error\n1. error'; false" + run assert false + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 7 ] + [ "${lines[0]}" == '-- assertion failed --' ] + [ "${lines[1]}" == 'expression : false' ] + [ "${lines[2]}" == 'status : 1' ] + [ "${lines[3]}" == 'output (2 lines):' ] + [ "${lines[4]}" == ' 0. error' ] + [ "${lines[5]}" == ' 1. error' ] + [ "${lines[6]}" == '--' ] } From 149ddc56b3c9fdacd6b4ddd622722383c7a67c47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20L=C3=89VEIL?= Date: Tue, 8 Sep 2015 23:52:52 +0200 Subject: [PATCH 10/16] fix code formatting in README file --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f84fece7..dc92583f 100644 --- a/README.md +++ b/README.md @@ -312,9 +312,9 @@ Fail if the given expression evaluates to false. ***Note:*** *The expression must be a simple command. [Compound commands](https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands), -such as `[[', can be used only when executed with `bash -c'.* +such as `[[`, can be used only when executed with `bash -c`. -On failure the failed expression, `$status' and `$output' are displayed. +On failure the failed expression, `$status` and `$output` are displayed. ``` -- assertion failed -- From 35923445e1222d2236d1c5025c077674ee3b5b63 Mon Sep 17 00:00:00 2001 From: Zoltan Tombol Date: Wed, 9 Sep 2015 21:49:16 +0200 Subject: [PATCH 11/16] Remove STDIN support and change -L to -l in assert_output and refute_output --- README.md | 42 ++++-------- lib/bats/batslib.bash | 73 +++++++++------------ man/batslib.7 | 18 ++--- man/batslib.7.ronn | 39 +++++------ test/60-lib-assertion-13-assert_output.bats | 68 +++++++------------ test/60-lib-assertion-16-refute_output.bats | 68 +++++++------------ 6 files changed, 123 insertions(+), 185 deletions(-) diff --git a/README.md b/README.md index dc92583f..43300bb7 100644 --- a/README.md +++ b/README.md @@ -441,15 +441,6 @@ it does not equal the expected output. } ``` -The expected output can be specified on the standard input as well. - -```bash -@test 'assert_output() with pipe' { - run echo 'have' - echo 'want' | assert_output -} -``` - On failure, the expected and actual outputs are displayed. ``` @@ -495,13 +486,13 @@ actual : have-1 from `${lines[@]}`, causing line indices to change and preventing testing for empty lines.* -When `-L` is used, fail if the expected line is not found in -`${lines[@]}`. +When `-l` is used without ``, fail if the expected line is not +found in `${lines[@]}`. ```bash @test 'assert_output() line found in output' { run echo $'have-0\nhave-1\nhave-2' - assert_output -L 'want' + assert_output -l 'want' } ``` @@ -533,8 +524,8 @@ a substring can not be found in the output. } ``` -This option can be used with `-l` or `-L` as well, and does not change -the set of information displayed on failure. +This option can be used with `-l` as well, and does not change the set +of information displayed on failure. ##### Regular expression matching @@ -549,8 +540,8 @@ extended regular expression does not match the output. } ``` -This option can be used with `-l` or `-L` as well, and does not change -the set of information displayed on failure. +This option can be used with `-l` as well, and does not change the set +of information displayed on failure. #### `refute_output` @@ -574,15 +565,6 @@ it equals the unexpected output. } ``` -The unexpected output can be specified on the standard input as well. - -```bash -@test 'refute_output() with pipe' { - run echo 'want' - echo 'want' | refute_output -} -``` - On failure, the unexpected output is displayed. ``` @@ -626,13 +608,13 @@ unexpected : want-1 from `${lines[@]}`, causing line indices to change and preventing testing for empty lines.* -When `-L` is used, fail if the unexpected line is found in -`${lines[@]}`. +When `-l` is used without ``, fail if the unexpected line is +found in `${lines[@]}`. ```bash @test 'refute_output() line found in output' { run echo $'have-0\nwant\nhave-2' - refute_output -L 'want' + refute_output -l 'want' } ``` @@ -675,7 +657,7 @@ output : ERROR: no such file or directory -- ``` -This option can be used with `-l` or `-L` as well. +This option can be used with `-l` as well. ##### Regular expression matching @@ -699,7 +681,7 @@ output : Foobar v0.1.0 -- ``` -This option can be used with `-l` or `-L` as well. +This option can be used with `-l` as well. [bats-93]: https://github.com/sstephenson/bats/pull/93 diff --git a/lib/bats/batslib.bash b/lib/bats/batslib.bash index 207b49ec..85f123c3 100644 --- a/lib/bats/batslib.bash +++ b/lib/bats/batslib.bash @@ -97,8 +97,7 @@ assert_equal() { } # Fail and display an error message if the expected does not match the -# actual output. The expected output can be specified either by the -# first parameter or on the standard input. +# actual output. # # By default, the expected output is compared against `$output', and the # error message contains both values. @@ -106,9 +105,10 @@ assert_equal() { # Option `-l ' compares against `${lines[]}'. The error # message contains the compared lines and . # -# Option `-L' compares against all lines in `${lines[@]}' until a match -# is found. If no match is found, the function fails and the error -# message contains the expected line and `$output'. +# Option `-l' without `' compares against all lines in +# `${lines[@]}' until a match is found. If no match is found, the +# function fails and the error message contains the expected line and +# `$output'. # # By default, literal matching is performed. Option `-p' and `-r' change # this to partial (substring) and regular expression (extended) @@ -119,16 +119,14 @@ assert_equal() { # lines # Options: # -l - match against the -th element of `${lines[@]}' -# -L - match against all elements of `${lines[@]}' until one matches +# -l - match against all elements of `${lines[@]}' until one matches # -p - substring match # -r - extended regular expression match # Arguments: -# $1 - [=STDIN] expected output +# $1 - expected output # Returns: # 0 - expected matches the actual output # 1 - otherwise -# Inputs: -# STDIN - [=$1] expected output # Outputs: # STDERR - assertion details, on failure # error message, on error @@ -142,17 +140,15 @@ assert_output() { while (( $# > 0 )); do case "$1" in -l) - if (( $# < 2 )) || ! [[ $2 =~ ^([0-9]|[1-9][0-9]+)$ ]]; then - echo "\`-l' requires an integer argument" \ - | batslib_decorate 'ERROR: assert_output' \ - | flunk - return $? + if (( $# > 2 )) && [[ $2 =~ ^([0-9]|[1-9][0-9]+)$ ]]; then + is_match_line=1 + local -ri idx="$2" + shift + else + is_match_contained=1; fi - is_match_line=1 - local -ri idx="$2" - shift 2 + shift ;; - -L) is_match_contained=1; shift ;; -p) is_mode_partial=1; shift ;; -r) is_mode_regex=1; shift ;; --) break ;; @@ -161,7 +157,7 @@ assert_output() { done if (( is_match_line )) && (( is_match_contained )); then - echo "\`-l' and \`-L' are mutually exclusive" \ + echo "\`-l' and \`-l ' are mutually exclusive" \ | batslib_decorate 'ERROR: assert_output' \ | flunk return $? @@ -175,8 +171,7 @@ assert_output() { fi # Arguments. - local expected - (( $# == 0 )) && expected="$(cat -)" || expected="$1" + local -r expected="$1" if (( is_mode_regex == 1 )) && [[ '' =~ $expected ]] || (( $? == 2 )); then echo "Invalid extended regular expression: \`$expected'" \ @@ -301,8 +296,7 @@ assert_output() { } # Fail and display an error message if the unexpected matches the actual -# output. The unexpected output can be specified either by the first -# parameter or on the standard input. +# output. # # By default, the unexpected output is compared against `$output', and # the error message contains this value. @@ -310,9 +304,10 @@ assert_output() { # Option `-l ' compares against `${lines[]}'. The error # message contains the compared lines and . # -# Option `-L' compares against all lines in `${lines[@]}' until a match -# is found. If a match is found, the function fails and the error -# message contains the unexpected line, its index and `${lines[@]}'. +# Option `-l' without `' compares against all lines in +# `${lines[@]}' until a match is found. If a match is found, the +# function fails and the error message contains the unexpected line, its +# index and `${lines[@]}'. # # By default, literal matching is performed. Option `-p' and `-r' change # this to partial (substring) and regular expression (extended) @@ -324,16 +319,14 @@ assert_output() { # lines # Options: # -l - match against the -th element of `${lines[@]}' -# -L - match against all elements of `${lines[@]}' until one matches +# -l - match against all elements of `${lines[@]}' until one matches # -p - substring match # -r - extended regular expression match # Arguments: -# $1 - [=STDIN] unexpected output +# $1 - unexpected output # Returns: # 0 - unexpected does not match the actual output # 1 - otherwise -# Inputs: -# STDIN - [=$1] unexpected output # Outputs: # STDERR - assertion details, on failure # error message, on error @@ -347,15 +340,14 @@ refute_output() { while (( $# > 0 )); do case "$1" in -l) - if (( $# < 2 )) || ! [[ $2 =~ ^([0-9]|[1-9][0-9]+)$ ]]; then - echo "\`-l' requires an integer argument" \ - | batslib_decorate 'ERROR: refute_output' \ - | flunk - return $? + if (( $# > 2 )) && [[ $2 =~ ^([0-9]|[1-9][0-9]+)$ ]]; then + is_match_line=1 + local -ri idx="$2" + shift + else + is_match_contained=1; fi - is_match_line=1 - local -ri idx="$2" - shift 2 + shift ;; -L) is_match_contained=1; shift ;; -p) is_mode_partial=1; shift ;; @@ -366,7 +358,7 @@ refute_output() { done if (( is_match_line )) && (( is_match_contained )); then - echo "\`-l' and \`-L' are mutually exclusive" \ + echo "\`-l' and \`-l ' are mutually exclusive" \ | batslib_decorate 'ERROR: refute_output' \ | flunk return $? @@ -380,8 +372,7 @@ refute_output() { fi # Arguments. - local unexpected - (( $# == 0 )) && unexpected="$(cat -)" || unexpected="$1" + local -r unexpected="$1" if (( is_mode_regex == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then echo "Invalid extended regular expression: \`$unexpected'" \ diff --git a/man/batslib.7 b/man/batslib.7 index 674f533c..9d59838e 100644 --- a/man/batslib.7 +++ b/man/batslib.7 @@ -87,14 +87,14 @@ Fail if \fIEXPRESSION\fR evaluates to false\. On failure, \fIEXPRESSION\fR, \fB$ Fail if \fIEXPECTED\fR and \fIACTUAL\fR do not equal\. On failure both values are displayed\. If either one is longer than one line, both are displayed in \fImulit\-line\fR format\. . .TP -\fBassert_output\fR [\-l \fIINDEX\fR|\-L] [\-p|\-r] [\fIEXPECTED\fR] -Fail if \fIEXPECTED\fR does not equal \fB$output\fR\. On failure both values are displayed\. If either one is longer than one line, both are displayed in \fImulit\-line\fR format\. When no parameters are specified, \fIEXPECTED\fR is read from the standard input\. +\fBassert_output\fR [\-l [\fIINDEX\fR]] [\-p|\-r] \fIEXPECTED\fR +Fail if \fIEXPECTED\fR does not equal \fB$output\fR\. On failure both values are displayed\. If either one is longer than one line, both are displayed in \fImulit\-line\fR format\. . .IP -When \fB\-l\fR is used, fail if \fIEXPECTED\fR does not equal the \fIINDEX\fR\-th element of \fB${lines[@]}\fR\. On failure, \fIINDEX\fR, \fIEXPECTED\fR and the actual line are displayed\. +When \fB\-l \fR is used, fail if \fIEXPECTED\fR does not equal the \fIINDEX\fR\-th element of \fB${lines[@]}\fR\. On failure, \fIINDEX\fR, \fIEXPECTED\fR and the actual line are displayed\. . .IP -When \fB\-L\fR is used, fail if \fIEXPECTED\fR is not found in \fB${lines[@]}\fR\. On failure, \fIEXPECTED\fR and \fB$output\fR are displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulit\-line\fR format\. +When \fB\-l\fR is used without \fB\fR, fail if \fIEXPECTED\fR is not found in \fB${lines[@]}\fR\. On failure, \fIEXPECTED\fR and \fB$output\fR are displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulit\-line\fR format\. . .IP The default, literal matching can be changed to substring and regular expression matching\. @@ -109,14 +109,14 @@ Using \fB\-r\fR enables regular expression matching, and the assertion fails if \fBNOTE:\fR Due to a bug in Bats, empty lines are discarded from \fB${lines[@]}\fR, causing line indices to change and preventing testing for empty lines\. See \fIBUGS\fR for more\. . .TP -\fBrefute_output\fR [\-l \fIINDEX\fR|\-L] [\-p|\-r] [\fIUNEXPECTED\fR] -Fail if \fIUNEXPECTED\fR equals \fB$output\fR\. On failure, \fB$output\fR is displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulti\-line\fR format\. When no parameters are specified, \fIUNEXPECTED\fR is read from the standard input\. +\fBrefute_output\fR [\-l [\fIINDEX\fR]] [\-p|\-r] \fIUNEXPECTED\fR +Fail if \fIUNEXPECTED\fR equals \fB$output\fR\. On failure, \fB$output\fR is displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulti\-line\fR format\. . .IP -When \fB\-l\fR is used, fail if \fIUNEXPECTED\fR equals the \fIINDEX\fR\-th element of \fB${lines[@]}\fR\. On failure, \fIINDEX\fR and \fIUNEXPECTED\fR are displayed\. +When \fB\-l \fR is used, fail if \fIUNEXPECTED\fR equals the \fIINDEX\fR\-th element of \fB${lines[@]}\fR\. On failure, \fIINDEX\fR and \fIUNEXPECTED\fR are displayed\. . .IP -When \fB\-L\fR is used, fail if \fIUNEXPECTED\fR is found in \fB${lines[@]}\fR\. On failure, \fIUNEXPECTED\fR, its index in \fB${lines[@]}\fR and \fB$output\fR are displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulti\-line\fR format with the unexpected line highlighted\. +When \fB\-l\fR is used without \fB\fR, fail if \fIUNEXPECTED\fR is found in \fB${lines[@]}\fR\. On failure, \fIUNEXPECTED\fR, its index in \fB${lines[@]}\fR and \fB$output\fR are displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulti\-line\fR format with the unexpected line highlighted\. . .IP The default, literal matching can be changed to substring and regular expression matching\. @@ -150,7 +150,7 @@ Report bugs on Bats\' GitHub issue tracker at \fIhttps://github\.com/sstephenson Due to a bug in Bats, empty lines are missing from \fB${lines[@]}\fR, causing line indices to change and preventing testing for empty lines when using \fBassert_output\fR and \fBrefute_line\fR\. See PR #93 on Github at \fIhttps://github\.com/sstephenson/bats/pull/93\fR\. . .SH "COPYRIGHT" -TODO(ztombol): Find a suitable license\. +TODO(ztombol): Find a suitable licence\. . .SH "SEE ALSO" \fBbash\fR(1), \fBbats\fR(1), \fBbats\fR(7), \fBregex(7)\fR diff --git a/man/batslib.7.ronn b/man/batslib.7.ronn index d90a8deb..bcbf4807 100644 --- a/man/batslib.7.ronn +++ b/man/batslib.7.ronn @@ -81,19 +81,19 @@ recent invocation of `run`. are displayed. If either one is longer than one line, both are displayed in _mulit-line_ format. -* `assert_output` [-l |-L] [-p|-r] []: +* `assert_output` [-l []] [-p|-r] : Fail if does not equal `$output`. On failure both values are displayed. If either one is longer than one line, both are - displayed in _mulit-line_ format. When no parameters are specified, - is read from the standard input. + displayed in _mulit-line_ format. - When `-l` is used, fail if does not equal the -th - element of `${lines[@]}`. On failure, , and the - actual line are displayed. + When `-l ` is used, fail if does not equal the + -th element of `${lines[@]}`. On failure, , + and the actual line are displayed. - When `-L` is used, fail if is not found in `${lines[@]}`. - On failure, and `$output` are displayed. If `$output` is - longer than one line, it is displayed in _mulit-line_ format. + When `-l` is used without ``, fail if is not found + in `${lines[@]}`. On failure, and `$output` are displayed. + If `$output` is longer than one line, it is displayed in _mulit-line_ + format. The default, literal matching can be changed to substring and regular expression matching. @@ -109,19 +109,20 @@ recent invocation of `run`. `${lines[@]}`, causing line indices to change and preventing testing for empty lines. See [BUGS][] for more. -* `refute_output` [-l |-L] [-p|-r] []: +* `refute_output` [-l []] [-p|-r] : Fail if equals `$output`. On failure, `$output` is displayed. If `$output` is longer than one line, it is displayed in - _multi-line_ format. When no parameters are specified, is - read from the standard input. + _multi-line_ format. - When `-l` is used, fail if equals the -th element - of `${lines[@]}`. On failure, and are displayed. + When `-l ` is used, fail if equals the -th + element of `${lines[@]}`. On failure, and are + displayed. - When `-L` is used, fail if is found in `${lines[@]}`. On - failure, , its index in `${lines[@]}` and `$output` are - displayed. If `$output` is longer than one line, it is displayed in - _multi-line_ format with the unexpected line highlighted. + When `-l` is used without ``, fail if is found in + `${lines[@]}`. On failure, , its index in `${lines[@]}` + and `$output` are displayed. If `$output` is longer than one line, it + is displayed in _multi-line_ format with the unexpected line + highlighted. The default, literal matching can be changed to substring and regular expression matching. @@ -170,7 +171,7 @@ when using `assert_output` and `refute_line`. See PR \#93 on Github at ## COPYRIGHT -TODO(ztombol): Find a suitable license. +TODO(ztombol): Find a suitable licence. ## SEE ALSO diff --git a/test/60-lib-assertion-13-assert_output.bats b/test/60-lib-assertion-13-assert_output.bats index 9de2b2da..6cfff8d2 100755 --- a/test/60-lib-assertion-13-assert_output.bats +++ b/test/60-lib-assertion-13-assert_output.bats @@ -61,14 +61,6 @@ load test_helper [ "$status" -eq 1 ] } -@test 'assert_output(): reads the expected output from STDIN' { - run echo 'a' - export output - run bash -c ". '${BATS_LIB}/batslib.bash'; echo 'a' | assert_output" - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 0 ] -} - # Partial matching: `-p '. @test "assert_output() -p : returns 0 if is a substring in \`\$output'" { @@ -167,7 +159,7 @@ load test_helper # -# Matching a single line: `-l '. +# Matching a specific line: `-l '. # # Literal matching. @@ -197,16 +189,6 @@ load test_helper [ "$status" -eq 1 ] } -@test 'assert_output() -l: without returns 1 and displays an error message' { - run echo $'a\nb\nc' - run assert_output -l 'a' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 3 ] - [ "${lines[0]}" == '-- ERROR: assert_output --' ] - [ "${lines[1]}" == "\`-l' requires an integer argument" ] - [ "${lines[2]}" == '--' ] -} - # Partial matching: `-p '. @test "assert_output() -l -p : returns 0 if is a substring in \`\${lines[]}'" { @@ -251,21 +233,21 @@ load test_helper # -# Containing a line: `-L'. +# Containing a line: `-l'. # # Literal matching. -@test "assert_output() -L : returns 0 if is a line in \`\${lines[@]}'" { +@test "assert_output() -l : returns 0 if is a line in \`\${lines[@]}'" { run echo $'a\nb\nc' - run assert_output -L 'b' + run assert_output -l 'b' [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } -@test "assert_output() -L : returns 1 and displays details if is not a line in \`\${lines[@]}'" { +@test "assert_output() -l : returns 1 and displays details if is not a line in \`\${lines[@]}'" { run echo 'b' - run assert_output -L 'a' + run assert_output -l 'a' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 4 ] [ "${lines[0]}" == '-- output does not contain line --' ] @@ -274,9 +256,9 @@ load test_helper [ "${lines[3]}" == '--' ] } -@test "assert_output() -L : displays \`\$output' in multi-line format if necessary" { +@test "assert_output() -l : displays \`\$output' in multi-line format if necessary" { run echo $'b 0\nb 1' - run assert_output -L 'a' + run assert_output -l 'a' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 6 ] [ "${lines[0]}" == '-- output does not contain line --' ] @@ -287,24 +269,24 @@ load test_helper [ "${lines[5]}" == '--' ] } -@test 'assert_output() -L : performs literal matching by default' { +@test 'assert_output() -l : performs literal matching by default' { run echo 'a' - run assert_output -L '*' + run assert_output -l '*' [ "$status" -eq 1 ] } # Partial matching: `-p '. -@test "assert_output() -L -p : returns 0 if is a substring in at least one line in \`\${lines[@]}'" { +@test "assert_output() -l -p : returns 0 if is a substring in at least one line in \`\${lines[@]}'" { run echo $'a\nabc\nc' - run assert_output -L -p 'b' + run assert_output -l -p 'b' [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } -@test "assert_output() -L -p : returns 1 and displays details if is not a substring in any line in \`\${lines[@]}'" { +@test "assert_output() -l -p : returns 1 and displays details if is not a substring in any line in \`\${lines[@]}'" { run echo 'b' - run assert_output -L -p 'a' + run assert_output -l -p 'a' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 4 ] [ "${lines[0]}" == '-- no output line contains substring --' ] @@ -313,9 +295,9 @@ load test_helper [ "${lines[3]}" == '--' ] } -@test "assert_output() -L -p : displays details in multi-line format if necessary" { +@test "assert_output() -l -p : displays details in multi-line format if necessary" { run echo $'b 0\nb 1' - run assert_output -L -p 'a' + run assert_output -l -p 'a' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 6 ] [ "${lines[0]}" == '-- no output line contains substring --' ] @@ -328,16 +310,16 @@ load test_helper # Regular expression matching: `-r '. -@test "assert_output() -L -r : returns 0 if matches any line in \`\${lines[@]}'" { +@test "assert_output() -l -r : returns 0 if matches any line in \`\${lines[@]}'" { run echo $'a\nb\nc' - run assert_output -L -r '.*b.*' + run assert_output -l -r '.*b.*' [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } -@test "assert_output() -L -r : returns 1 and displays details if does not match any lines in \`\${lines[@]}'" { +@test "assert_output() -l -r : returns 1 and displays details if does not match any lines in \`\${lines[@]}'" { run echo 'b' - run assert_output -L -r '.*a.*' + run assert_output -l -r '.*a.*' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 4 ] [ "${lines[0]}" == '-- no output line matches regular expression --' ] @@ -346,9 +328,9 @@ load test_helper [ "${lines[3]}" == '--' ] } -@test 'assert_output() -L -r : displays details in multi-line format if necessary' { +@test 'assert_output() -l -r : displays details in multi-line format if necessary' { run echo $'b 0\nb 1' - run assert_output -L -r '.*a.*' + run assert_output -l -r '.*a.*' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 6 ] [ "${lines[0]}" == '-- no output line matches regular expression --' ] @@ -364,12 +346,12 @@ load test_helper # Common. # -@test 'assert_output() -l and -L are mutually exclusive' { - run assert_output -l 1 -L 'b' +@test 'assert_output() -l and -l are mutually exclusive' { + run assert_output -l -l 1 'b' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 3 ] [ "${lines[0]}" == '-- ERROR: assert_output --' ] - [ "${lines[1]}" == "\`-l' and \`-L' are mutually exclusive" ] + [ "${lines[1]}" == "\`-l' and \`-l ' are mutually exclusive" ] [ "${lines[2]}" == '--' ] } diff --git a/test/60-lib-assertion-16-refute_output.bats b/test/60-lib-assertion-16-refute_output.bats index 850f48b2..b7c4b913 100755 --- a/test/60-lib-assertion-16-refute_output.bats +++ b/test/60-lib-assertion-16-refute_output.bats @@ -44,14 +44,6 @@ load test_helper [ "$status" -eq 0 ] } -@test 'refute_output(): reads the unexpected output from STDIN' { - run echo 'b' - export output - run bash -c ". '${BATS_LIB}/batslib.bash'; echo 'a' | refute_output" - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 0 ] -} - # Partial matching: `-p '. @test "refute_output() -p : returns 0 if is not a substring in \`\$output'" { @@ -122,7 +114,7 @@ load test_helper # -# Matching a single line: `-l '. +# Matching a specific line: `-l '. # # Literal matching. @@ -151,16 +143,6 @@ load test_helper [ "$status" -eq 0 ] } -@test 'refute_output() -l: without returns 1 and displays an error message' { - run echo $'a\nb\nc' - run refute_output -l 'a' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 3 ] - [ "${lines[0]}" == '-- ERROR: refute_output --' ] - [ "${lines[1]}" == "\`-l' requires an integer argument" ] - [ "${lines[2]}" == '--' ] -} - # Partial matching: `-p '. @test "refute_output() -l -p : returns 0 if is not a substring in \`\${lines[]}'" { @@ -205,21 +187,21 @@ load test_helper # -# Containing a line: `-L'. +# Containing a line: `-l'. # # Literal matching. -@test "refute_output() -L : returns 0 if is not a line in \`\${lines[@]}'" { +@test "refute_output() -l : returns 0 if is not a line in \`\${lines[@]}'" { run echo $'a\nb\nc' - run refute_output -L 'd' + run refute_output -l 'd' [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } -@test "refute_output() -L : returns 1 and displays details if is not a line in \`\${lines[@]}'" { +@test "refute_output() -l : returns 1 and displays details if is not a line in \`\${lines[@]}'" { run echo 'a' - run refute_output -L 'a' + run refute_output -l 'a' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 5 ] [ "${lines[0]}" == '-- line should not be in output --' ] @@ -229,9 +211,9 @@ load test_helper [ "${lines[4]}" == '--' ] } -@test "refute_output() -L : displays details in multi-line format if necessary" { +@test "refute_output() -l : displays details in multi-line format if necessary" { run echo $'a\nb\nc' - run refute_output -L 'b' + run refute_output -l 'b' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 8 ] [ "${lines[0]}" == '-- line should not be in output --' ] @@ -244,24 +226,24 @@ load test_helper [ "${lines[7]}" == '--' ] } -@test 'refute_output() -L : performs literal matching by default' { +@test 'refute_output() -l : performs literal matching by default' { run echo 'a' - run refute_output -L '*' + run refute_output -l '*' [ "$status" -eq 0 ] } # Partial matching: `-p '. -@test "refute_output() -L -p : returns 0 if is not a substring in any line in \`\${lines[@]}'" { +@test "refute_output() -l -p : returns 0 if is not a substring in any line in \`\${lines[@]}'" { run echo $'a\nb\nc' - run refute_output -L -p 'd' + run refute_output -l -p 'd' [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } -@test "refute_output() -L -p : returns 1 and displays details if is a substring in at least one line in \`\${lines[@]}'" { +@test "refute_output() -l -p : returns 1 and displays details if is a substring in at least one line in \`\${lines[@]}'" { run echo 'a' - run refute_output -L -p 'a' + run refute_output -l -p 'a' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 5 ] [ "${lines[0]}" == '-- no line should contain substring --' ] @@ -271,9 +253,9 @@ load test_helper [ "${lines[4]}" == '--' ] } -@test "refute_output() -L -p : displays details in multi-line format if necessary" { +@test "refute_output() -l -p : displays details in multi-line format if necessary" { run echo $'a\nabc\nc' - run refute_output -L -p 'b' + run refute_output -l -p 'b' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 8 ] [ "${lines[0]}" == '-- no line should contain substring --' ] @@ -288,16 +270,16 @@ load test_helper # Regular expression matching: `-r '. -@test "refute_output() -L -r : returns 0 if does not match any line in \`\${lines[@]}'" { +@test "refute_output() -l -r : returns 0 if does not match any line in \`\${lines[@]}'" { run echo $'a\nb\nc' - run refute_output -L -r '.*d.*' + run refute_output -l -r '.*d.*' [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } -@test "refute_output() -L -r : returns 1 and displays details if matches any lines in \`\${lines[@]}'" { +@test "refute_output() -l -r : returns 1 and displays details if matches any lines in \`\${lines[@]}'" { run echo 'a' - run refute_output -L -r '.*a.*' + run refute_output -l -r '.*a.*' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 5 ] [ "${lines[0]}" == '-- no line should match the regular expression --' ] @@ -307,9 +289,9 @@ load test_helper [ "${lines[4]}" == '--' ] } -@test 'refute_output() -L -r : displays details in multi-line format if necessary' { +@test 'refute_output() -l -r : displays details in multi-line format if necessary' { run echo $'a\nabc\nc' - run refute_output -L -r '.*b.*' + run refute_output -l -r '.*b.*' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 8 ] [ "${lines[0]}" == '-- no line should match the regular expression --' ] @@ -327,12 +309,12 @@ load test_helper # Common. # -@test 'refute_output() -l and -L are mutually exclusive' { - run refute_output -l 1 -L 'b' +@test 'refute_output() -l and -l are mutually exclusive' { + run refute_output -l -l 1 'b' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 3 ] [ "${lines[0]}" == '-- ERROR: refute_output --' ] - [ "${lines[1]}" == "\`-l' and \`-L' are mutually exclusive" ] + [ "${lines[1]}" == "\`-l' and \`-l ' are mutually exclusive" ] [ "${lines[2]}" == '--' ] } From e4c6b1959393401606771e80b10475ea83ad8bb3 Mon Sep 17 00:00:00 2001 From: Zoltan Tombol Date: Wed, 9 Sep 2015 22:47:29 +0200 Subject: [PATCH 12/16] Change flunk to fail --- README.md | 10 ++-- lib/bats/batslib.bash | 60 +++++++++---------- man/batslib.7 | 2 +- man/batslib.7.ronn | 2 +- ...unk.bats => 60-lib-assertion-10-fail.bats} | 12 ++-- 5 files changed, 43 insertions(+), 43 deletions(-) rename test/{60-lib-assertion-10-flunk.bats => 60-lib-assertion-10-fail.bats} (54%) diff --git a/README.md b/README.md index 43300bb7..8eba5448 100644 --- a/README.md +++ b/README.md @@ -272,7 +272,7 @@ information on failure to help debugging. They return `1` on failure and Assertions about exit code and output operate on the results of the most recent invocation of `run`. -#### `flunk` +#### `fail` Display an error message and fail. This function provides a convenient way to report failure in arbitrary situations. You can use it to @@ -280,16 +280,16 @@ implement your own helpers when the ones available do not meet your needs. Other functions use it internally as well. ```bash -@test 'flunk()' { - flunk 'this test always fails' +@test 'fail()' { + fail 'this test always fails' } ``` The message can also be specified on the standard input. ```bash -@test 'flunk() with pipe' { - echo 'this test always fails' | flunk +@test 'fail() with pipe' { + echo 'this test always fails' | fail } ``` diff --git a/lib/bats/batslib.bash b/lib/bats/batslib.bash index 85f123c3..e04cdb9b 100644 --- a/lib/bats/batslib.bash +++ b/lib/bats/batslib.bash @@ -34,7 +34,7 @@ source "${BATS_LIB}/batslib/output.bash" # STDIN - [opt = $@] message to display # Outputs: # STDERR - error message -flunk() { +fail() { (( $# == 0 )) && batslib_err || batslib_err "$@" return 1 } @@ -68,7 +68,7 @@ assert() { batslib_print_kv_single "$width" "${single[@]}" batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}" } | batslib_decorate 'assertion failed' \ - | flunk + | fail fi } @@ -92,7 +92,7 @@ assert_equal() { 'expected' "$1" \ 'actual' "$2" \ | batslib_decorate 'values do not equal' \ - | flunk + | fail fi } @@ -159,14 +159,14 @@ assert_output() { if (( is_match_line )) && (( is_match_contained )); then echo "\`-l' and \`-l ' are mutually exclusive" \ | batslib_decorate 'ERROR: assert_output' \ - | flunk + | fail return $? fi if (( is_mode_partial )) && (( is_mode_regex )); then echo "\`-p' and \`-r' are mutually exclusive" \ | batslib_decorate 'ERROR: assert_output' \ - | flunk + | fail return $? fi @@ -176,7 +176,7 @@ assert_output() { if (( is_mode_regex == 1 )) && [[ '' =~ $expected ]] || (( $? == 2 )); then echo "Invalid extended regular expression: \`$expected'" \ | batslib_decorate 'ERROR: assert_output' \ - | flunk + | fail return $? fi @@ -199,7 +199,7 @@ assert_output() { batslib_print_kv_single "$width" "${single[@]}" batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}" } | batslib_decorate 'no output line matches regular expression' \ - | flunk + | fail elif (( is_mode_partial )); then local -i idx for (( idx = 0; idx < ${#lines[@]}; ++idx )); do @@ -216,7 +216,7 @@ assert_output() { batslib_print_kv_single "$width" "${single[@]}" batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}" } | batslib_decorate 'no output line contains substring' \ - | flunk + | fail else local -i idx for (( idx = 0; idx < ${#lines[@]}; ++idx )); do @@ -233,7 +233,7 @@ assert_output() { batslib_print_kv_single "$width" "${single[@]}" batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}" } | batslib_decorate 'output does not contain line' \ - | flunk + | fail fi elif (( is_match_line )); then # Specific line. @@ -244,7 +244,7 @@ assert_output() { 'regex' "$expected" \ 'line' "${lines[$idx]}" \ | batslib_decorate 'regular expression does not match line' \ - | flunk + | fail fi elif (( is_mode_partial )); then if [[ ${lines[$idx]} != *"$expected"* ]]; then @@ -253,7 +253,7 @@ assert_output() { 'substring' "$expected" \ 'line' "${lines[$idx]}" \ | batslib_decorate 'line does not contain substring' \ - | flunk + | fail fi else if [[ ${lines[$idx]} != "$expected" ]]; then @@ -262,7 +262,7 @@ assert_output() { 'expected' "$expected" \ 'actual' "${lines[$idx]}" \ | batslib_decorate 'line differs' \ - | flunk + | fail fi fi else @@ -273,7 +273,7 @@ assert_output() { 'regex' "$expected" \ 'output' "$output" \ | batslib_decorate 'regular expression does not match output' \ - | flunk + | fail fi elif (( is_mode_partial )); then if [[ $output != *"$expected"* ]]; then @@ -281,7 +281,7 @@ assert_output() { 'substring' "$expected" \ 'output' "$output" \ | batslib_decorate 'output does not contain substring' \ - | flunk + | fail fi else if [[ $output != "$expected" ]]; then @@ -289,7 +289,7 @@ assert_output() { 'expected' "$expected" \ 'actual' "$output" \ | batslib_decorate 'output differs' \ - | flunk + | fail fi fi fi @@ -360,14 +360,14 @@ refute_output() { if (( is_match_line )) && (( is_match_contained )); then echo "\`-l' and \`-l ' are mutually exclusive" \ | batslib_decorate 'ERROR: refute_output' \ - | flunk + | fail return $? fi if (( is_mode_partial )) && (( is_mode_regex )); then echo "\`-p' and \`-r' are mutually exclusive" \ | batslib_decorate 'ERROR: refute_output' \ - | flunk + | fail return $? fi @@ -377,7 +377,7 @@ refute_output() { if (( is_mode_regex == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then echo "Invalid extended regular expression: \`$unexpected'" \ | batslib_decorate 'ERROR: refute_output' \ - | flunk + | fail return $? fi @@ -407,7 +407,7 @@ refute_output() { batslib_print_kv_multi "${may_be_multi[@]}" fi } | batslib_decorate 'no line should match the regular expression' \ - | flunk + | fail return $? fi done @@ -434,7 +434,7 @@ refute_output() { batslib_print_kv_multi "${may_be_multi[@]}" fi } | batslib_decorate 'no line should contain substring' \ - | flunk + | fail return $? fi done @@ -461,7 +461,7 @@ refute_output() { batslib_print_kv_multi "${may_be_multi[@]}" fi } | batslib_decorate 'line should not be in output' \ - | flunk + | fail return $? fi done @@ -475,7 +475,7 @@ refute_output() { 'regex' "$unexpected" \ 'line' "${lines[$idx]}" \ | batslib_decorate 'regular expression should not match line' \ - | flunk + | fail fi elif (( is_mode_partial )); then if [[ ${lines[$idx]} == *"$unexpected"* ]]; then @@ -484,7 +484,7 @@ refute_output() { 'substring' "$unexpected" \ 'line' "${lines[$idx]}" \ | batslib_decorate 'line should not contain substring' \ - | flunk + | fail fi else if [[ ${lines[$idx]} == "$unexpected" ]]; then @@ -492,7 +492,7 @@ refute_output() { 'index' "$idx" \ 'unexpected' "$unexpected" \ | batslib_decorate 'line should differ' \ - | flunk + | fail fi fi else @@ -503,7 +503,7 @@ refute_output() { 'regex' "$unexpected" \ 'output' "$output" \ | batslib_decorate 'regular expression should not match output' \ - | flunk + | fail fi elif (( is_mode_partial )); then if [[ $output == *"$unexpected"* ]]; then @@ -511,14 +511,14 @@ refute_output() { 'substring' "$unexpected" \ 'output' "$output" \ | batslib_decorate 'output should not contain substring' \ - | flunk + | fail fi else if [[ $output == "$unexpected" ]]; then batslib_print_kv_single_or_multi 6 \ 'output' "$output" \ | batslib_decorate 'output equals, but it was expected to differ' \ - | flunk + | fail fi fi fi @@ -543,7 +543,7 @@ assert_success() { batslib_print_kv_single "$width" 'status' "$status" batslib_print_kv_single_or_multi "$width" 'output' "$output" } | batslib_decorate 'command failed' \ - | flunk + | fail fi } @@ -569,7 +569,7 @@ assert_failure() { if (( status == 0 )); then batslib_print_kv_single_or_multi 6 'output' "$output" \ | batslib_decorate 'command succeeded, but it was expected to fail' \ - | flunk + | fail elif (( $# > 0 )) && (( status != expected )); then { local -ir width=8 batslib_print_kv_single "$width" \ @@ -578,6 +578,6 @@ assert_failure() { batslib_print_kv_single_or_multi "$width" \ 'output' "$output" } | batslib_decorate 'command failed as expected, but status differs' \ - | flunk + | fail fi } diff --git a/man/batslib.7 b/man/batslib.7 index 9d59838e..065cfc66 100644 --- a/man/batslib.7 +++ b/man/batslib.7 @@ -72,7 +72,7 @@ Assertions are functions that perform a test and output relevant information on Assertions about exit code and output operate on the results of the most recent invocation of \fBrun\fR\. . .TP -\fBflunk\fR [\fIMESSAGE\fR] +\fBfail\fR [\fIMESSAGE\fR] Display \fIMESSAGE\fR and fail\. This function provides a convenient way to report various failures\. Other test helpers also use it to display output on failure\. When no parameters are specified, \fIMESSAGE\fR is read from the standard input\. . .TP diff --git a/man/batslib.7.ronn b/man/batslib.7.ronn index bcbf4807..60da894c 100644 --- a/man/batslib.7.ronn +++ b/man/batslib.7.ronn @@ -63,7 +63,7 @@ otherwise. Assertions about exit code and output operate on the results of the most recent invocation of `run`. -* `flunk` []: +* `fail` []: Display and fail. This function provides a convenient way to report various failures. Other test helpers also use it to display output on failure. When no parameters are specified, is read diff --git a/test/60-lib-assertion-10-flunk.bats b/test/60-lib-assertion-10-fail.bats similarity index 54% rename from test/60-lib-assertion-10-flunk.bats rename to test/60-lib-assertion-10-fail.bats index d424181b..e25d259e 100755 --- a/test/60-lib-assertion-10-flunk.bats +++ b/test/60-lib-assertion-10-fail.bats @@ -2,19 +2,19 @@ load test_helper -@test 'flunk() returns 1' { - run flunk '' +@test 'fail() returns 1' { + run fail '' [ "$status" -eq 1 ] } -@test 'flunk() prints positional parameters' { - run flunk 'message' +@test 'fail() prints positional parameters' { + run fail 'message' [ "$status" -eq 1 ] [ "$output" == 'message' ] } -@test 'flunk() prints STDIN if no positional parameters are specified' { - run bash -c "source '${BATS_LIB}/batslib.bash'; echo 'message' | flunk" +@test 'fail() prints STDIN if no positional parameters are specified' { + run bash -c "source '${BATS_LIB}/batslib.bash'; echo 'message' | fail" [ "$status" -eq 1 ] [ "$output" == 'message' ] } From 9c254ce07750491ed831676bfdb5a4781eaef9de Mon Sep 17 00:00:00 2001 From: Zoltan Tombol Date: Thu, 17 Sep 2015 14:45:59 +0200 Subject: [PATCH 13/16] Reverse order of parameters in assert_equal --- README.md | 6 ++-- lib/bats/batslib.bash | 17 ++++++----- man/batslib.7.ronn | 4 +-- test/60-lib-assertion-12-assert_equal.bats | 33 +++++++++++++++------- 4 files changed, 35 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 8eba5448..6456d640 100644 --- a/README.md +++ b/README.md @@ -329,14 +329,12 @@ format. #### `assert_equal` -Fail if the two parameters, expected and actual value respectively, do +Fail if the two parameters, actual and expected value respectively, do not equal. ```bash @test 'assert_equal()' { - local expected='want' - local actual='have' - assert_equal "$expected" "$actual" + assert_equal 'have' 'want' } ``` diff --git a/lib/bats/batslib.bash b/lib/bats/batslib.bash index e04cdb9b..6f99708f 100644 --- a/lib/bats/batslib.bash +++ b/lib/bats/batslib.bash @@ -72,25 +72,24 @@ assert() { fi } -# Fail and display an error message if the two parameters, expected and -# actual value respectively, do not equal. The error message contains -# both parameters. +# Fail and display details if the expected and actual values do not +# equal. Details include both values. # # Globals: # none # Arguments: -# $1 - expected value -# $2 - actual value +# $1 - actual value +# $2 - expected value # Returns: -# 0 - expected equals actual value +# 0 - values equal # 1 - otherwise # Outputs: -# STDERR - expected and actual value, on failure +# STDERR - details, on failure assert_equal() { if [[ $1 != "$2" ]]; then batslib_print_kv_single_or_multi 8 \ - 'expected' "$1" \ - 'actual' "$2" \ + 'expected' "$2" \ + 'actual' "$1" \ | batslib_decorate 'values do not equal' \ | fail fi diff --git a/man/batslib.7.ronn b/man/batslib.7.ronn index 60da894c..e9d9ffad 100644 --- a/man/batslib.7.ronn +++ b/man/batslib.7.ronn @@ -76,8 +76,8 @@ recent invocation of `run`. **Note:** must be a simple command. Compound commands, such as `[[`, can be used only when executed with `bash -c`. -* `assert_equal` : - Fail if and do not equal. On failure both values +* `assert_equal` : + Fail if and do not equal. On failure both values are displayed. If either one is longer than one line, both are displayed in _mulit-line_ format. diff --git a/test/60-lib-assertion-12-assert_equal.bats b/test/60-lib-assertion-12-assert_equal.bats index 0404dfc7..3201ade2 100755 --- a/test/60-lib-assertion-12-assert_equal.bats +++ b/test/60-lib-assertion-12-assert_equal.bats @@ -2,36 +2,49 @@ load test_helper -@test 'assert_equal() returns 0 if the actual value equals the expected' { +@test 'assert_equal() : returns 0 if equals ' { run assert_equal 'a' 'a' [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } -@test 'assert_equal() returns 1 and displays the actual and expected value if they do not equal' { +@test 'assert_equal() : returns 1 and displays details if does not equal ' { run assert_equal 'a' 'b' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 4 ] [ "${lines[0]}" == '-- values do not equal --' ] - [ "${lines[1]}" == 'expected : a' ] - [ "${lines[2]}" == 'actual : b' ] + [ "${lines[1]}" == 'expected : b' ] + [ "${lines[2]}" == 'actual : a' ] [ "${lines[3]}" == '--' ] } -@test 'assert_equal() displays the expected and actual value in multi-line format if necessary' { - run assert_equal 'a' $'b 1\nb 2' +@test "assert_equal() : displays details in multi-line format if is longer than one line" { + run assert_equal $'a 0\na 1' 'b' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 7 ] [ "${lines[0]}" == '-- values do not equal --' ] [ "${lines[1]}" == 'expected (1 lines):' ] - [ "${lines[2]}" == ' a' ] + [ "${lines[2]}" == ' b' ] [ "${lines[3]}" == 'actual (2 lines):' ] - [ "${lines[4]}" == ' b 1' ] - [ "${lines[5]}" == ' b 2' ] + [ "${lines[4]}" == ' a 0' ] + [ "${lines[5]}" == ' a 1' ] [ "${lines[6]}" == '--' ] } -@test 'assert_equal() performs literal matching' { +@test "assert_equal() : displays details in multi-line format if is longer than one line" { + run assert_equal 'a' $'b 0\nb 1' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 7 ] + [ "${lines[0]}" == '-- values do not equal --' ] + [ "${lines[1]}" == 'expected (2 lines):' ] + [ "${lines[2]}" == ' b 0' ] + [ "${lines[3]}" == ' b 1' ] + [ "${lines[4]}" == 'actual (1 lines):' ] + [ "${lines[5]}" == ' a' ] + [ "${lines[6]}" == '--' ] +} + +@test 'assert_equal() : performs literal matching' { run assert_equal 'a' '*' [ "$status" -eq 1 ] } From ce1a3de5db641e2b4f54fed8e13725d6090cde60 Mon Sep 17 00:00:00 2001 From: Zoltan Tombol Date: Fri, 18 Sep 2015 00:57:03 +0200 Subject: [PATCH 14/16] Cleanup source, documentation and tests --- README.md | 129 ++++++---- lib/bats/batslib.bash | 234 ++++++++++-------- lib/bats/batslib/output.bash | 114 +++++---- man/batslib.7 | 44 ++-- man/batslib.7.ronn | 61 +++-- ...50-lib-internal-output-10-batslib_err.bats | 12 +- ...nternal-output-11-batslib_count_lines.bats | 6 +- ...rnal-output-12-batslib_is_single_line.bats | 4 +- ...batslib_get_max_single_line_key_width.bats | 12 +- ...nal-output-14-batslib_print_kv_single.bats | 20 +- ...rnal-output-15-batslib_print_kv_multi.bats | 10 +- ...t-16-batslib_print_kv_single_or_multi.bats | 20 +- ...lib-internal-output-17-batslib_prefix.bats | 32 +-- ...0-lib-internal-output-18-batslib_mark.bats | 19 +- ...b-internal-output-19-batslib_decorate.bats | 2 +- test/60-lib-assertion-10-fail.bats | 9 +- test/60-lib-assertion-11-assert.bats | 12 +- test/60-lib-assertion-12-assert_equal.bats | 4 +- ...> 60-lib-assertion-13-assert_success.bats} | 16 +- ...> 60-lib-assertion-14-assert_failure.bats} | 34 +-- ...=> 60-lib-assertion-15-assert_output.bats} | 16 +- test/60-lib-assertion-16-refute_output.bats | 18 +- 22 files changed, 442 insertions(+), 386 deletions(-) rename test/{60-lib-assertion-14-assert_success.bats => 60-lib-assertion-13-assert_success.bats} (56%) rename test/{60-lib-assertion-15-assert_failure.bats => 60-lib-assertion-14-assert_failure.bats} (56%) rename test/{60-lib-assertion-13-assert_output.bats => 60-lib-assertion-15-assert_output.bats} (94%) diff --git a/README.md b/README.md index 6456d640..7a75b373 100644 --- a/README.md +++ b/README.md @@ -303,6 +303,10 @@ this test always fails Fail if the given expression evaluates to false. +***Note:*** *The expression must be a simple command. [Compound +commands](https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands), +such as `[[`, can be used only when executed with `bash -c`. + ```bash @test 'assert()' { run touch '/var/log/test.log' @@ -310,11 +314,8 @@ Fail if the given expression evaluates to false. } ``` -***Note:*** *The expression must be a simple command. [Compound -commands](https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands), -such as `[[`, can be used only when executed with `bash -c`. - -On failure the failed expression, `$status` and `$output` are displayed. +On failure, the failed expression, `$status` and `$output` are +displayed. ``` -- assertion failed -- @@ -338,7 +339,7 @@ not equal. } ``` -On failure the expected and actual values are displayed. +On failure, the expected and actual values are displayed. ``` -- values do not equal -- @@ -356,17 +357,17 @@ Fail if `$status` is not 0. ```bash @test 'assert_success() status only' { - run bash -c "echo 'have'; exit 1" + run bash -c "echo 'Error!'; exit 1" assert_success } ``` -On failure `$status` and `$output` are displayed. +On failure, `$status` and `$output` are displayed. ``` -- command failed -- status : 1 -output : have +output : Error! -- ``` @@ -379,16 +380,16 @@ Fail if `$status` is 0. ```bash @test 'assert_failure() status only' { - run echo 'have' + run echo 'Success!' assert_failure } ``` -On failure `$output` is displayed. +On failure, `$output` is displayed. ``` -- command succeeded, but it was expected to fail -- -output : have +output : Success! -- ``` @@ -402,18 +403,18 @@ expected status specified by the parameter. ```bash @test 'assert_failure() with expected status' { - run bash -c "echo 'error'; exit 1" + run bash -c "echo 'Error!'; exit 1" assert_failure 2 } ``` -On failure the expected and actual status, and `$output` are displayed. +On failure, the expected and actual status, and `$output` are displayed. ``` -- command failed as expected, but status differs -- expected : 2 actual : 1 -output : error +output : Error! -- ``` @@ -453,14 +454,14 @@ If either value is longer than one line both are displayed in ##### Matching a specific line -***Note:*** *Due to a [bug in Bats][bats-93], empty lines are discarded -from `${lines[@]}`, causing line indices to change and preventing -testing for empty lines.* - When `-l ` is used, only the line specified by its `index` in `${lines[@]}` is matched, and the assertion fails if it does not equal the expected line. +***Note:*** *Due to a [bug in Bats][bats-93], empty lines are discarded +from `${lines[@]}`, causing line indices to change and preventing +testing for empty lines.* + ```bash @test 'assert_output() specific line' { run echo $'have-0\nhave-1\nhave-2' @@ -478,17 +479,17 @@ actual : have-1 -- ``` -##### Line found in output +##### Looking for line in output + +When `-l` is used without the `` argument, fail if the expected +line is not found in `${lines[@]}`. ***Note:*** *Due to a [bug in Bats][bats-93], empty lines are discarded from `${lines[@]}`, causing line indices to change and preventing testing for empty lines.* -When `-l` is used without ``, fail if the expected line is not -found in `${lines[@]}`. - ```bash -@test 'assert_output() line found in output' { +@test 'assert_output() looking for line' { run echo $'have-0\nhave-1\nhave-2' assert_output -l 'want' } @@ -518,12 +519,21 @@ a substring can not be found in the output. ```bash @test 'assert_output() partial matching' { run echo 'ERROR: no such file or directory' - assert_output -p 'ERROR' + assert_output -p 'SUCCESS' } ``` -This option can be used with `-l` as well, and does not change the set -of information displayed on failure. +This option does not change the set if information displayed on failure. + +``` +-- output does not contain substring -- +substring : SUCCESS +output : ERROR: no such file or directory +-- +``` + +This option is mutually exclusive with regular expression matching (`-r`). +When used simultaneously, an error is displayed. ##### Regular expression matching @@ -533,13 +543,25 @@ extended regular expression does not match the output. ```bash @test 'assert_output() regular expression matching' { - run echo 'Foobar v0.1.0' + run echo 'Foobar 0.1.0' assert_output -r '^Foobar v[0-9]+\.[0-9]+\.[0-9]$' } ``` -This option can be used with `-l` as well, and does not change the set -of information displayed on failure. +This option does not change the set if information displayed on failure. + +``` +-- regular expression does not match output -- +regex : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$ +output : Foobar 0.1.0 +-- +``` + +When the specified extended regular expression is invalid, an error is +displayed. + +This option is mutually exclusive with partial matching (`-p`). When +used simultaneously, an error is displayed. #### `refute_output` @@ -563,7 +585,7 @@ it equals the unexpected output. } ``` -On failure, the unexpected output is displayed. +On failure, `$output` is displayed. ``` -- output equals, but it was expected to differ -- @@ -576,14 +598,14 @@ format. ##### Matching a specific line +When `-l ` is used, only the line specified by its `index` in +`${lines[@]}` is matched, and the assertion fails if it equals the +unexpected line. + ***Note:*** *Due to a [bug in Bats][bats-93], empty lines are discarded from `${lines[@]}`, causing line indices to change and preventing testing for empty lines.* -When `-l ` is used, only the line specified by its `index` in -`${lines[@]}` is matched, and the assertion fails if it equals the -expected line. - ```bash @test 'refute_output() specific line' { run echo $'have-0\nwant-1\nhave-2' @@ -595,22 +617,22 @@ On failure, the index, and the unexpected line are displayed. ``` -- line should differ -- -index : 1 -unexpected : want-1 +index : 1 +line : want-1 -- ``` -##### Line found in output +##### Looking for line in output + +When `-l` is used without the `` argument, fail if the unexpected +line is found in `${lines[@]}`. ***Note:*** *Due to a [bug in Bats][bats-93], empty lines are discarded from `${lines[@]}`, causing line indices to change and preventing testing for empty lines.* -When `-l` is used without ``, fail if the unexpected line is -found in `${lines[@]}`. - ```bash -@test 'refute_output() line found in output' { +@test 'refute_output() looking for line' { run echo $'have-0\nwant\nhave-2' refute_output -l 'want' } @@ -636,8 +658,8 @@ If `$output` is not longer than one line, it is displayed in ##### Partial matching Partial matching, enabled using `-p`, provides more flexibility than the -default literal matching. The assertion fails if the unexpected output as -a substring can be found in the output. +default literal matching. The assertion fails if the unexpected output +as a substring can be found in the output. ```bash @test 'refute_output() partial matching' { @@ -646,7 +668,8 @@ a substring can be found in the output. } ``` -On failure, this option displays the substring in addition. +On failure, the substring is displayed in addition (if not already +displayed by other options). ``` -- output should not contain substring -- @@ -655,13 +678,14 @@ output : ERROR: no such file or directory -- ``` -This option can be used with `-l` as well. +This option is mutually exclusive with regular expression matching (`-r`). +When used simultaneously, an error is displayed. ##### Regular expression matching Regular expression matching, enabled using `-r`, provides the most -flexibility. The assertion fails if the expected output specified as an -extended regular expression does not match the output. +flexibility. The assertion fails if the unexpected output specified as +an extended regular expression matches the output. ```bash @test 'refute_output() regular expression matching' { @@ -670,7 +694,8 @@ extended regular expression does not match the output. } ``` -On failure, this option displays the regular expression in addition. +On failure, the regular expression is displayed in addition (if not +already displayed by other options). ``` -- regular expression should not match output -- @@ -679,7 +704,11 @@ output : Foobar v0.1.0 -- ``` -This option can be used with `-l` as well. +When the specified extended regular expression is invalid, an error is +displayed. + +This option is mutually exclusive with partial matching (`-p`). When +used simultaneously, an error is displayed. [bats-93]: https://github.com/sstephenson/bats/pull/93 diff --git a/lib/bats/batslib.bash b/lib/bats/batslib.bash index 6f99708f..521fd93f 100644 --- a/lib/bats/batslib.bash +++ b/lib/bats/batslib.bash @@ -20,40 +20,41 @@ source "${BATS_LIB}/batslib/output.bash" # ASSERTIONS ######################################################################## -# Fail and display an error message. The message is specified either by -# positional parameters or on the standard input (by piping or -# redirection). +# Fail and display a message. When no parameters are specified, the +# message is read from the standard input. Other functions use this to +# report failure. # # Globals: # none # Arguments: -# $@ - [opt = STDIN] message to display +# $@ - [=STDIN] message # Returns: # 1 - always # Inputs: -# STDIN - [opt = $@] message to display +# STDIN - [=$@] message # Outputs: -# STDERR - error message +# STDERR - message fail() { (( $# == 0 )) && batslib_err || batslib_err "$@" return 1 } -# Fail and display an error message if the expression evaluates to -# false. The expression must be a simple command. Compound commands, -# such as `[[', can be used only when executed with `bash -c'. The error -# message contains the expression, `$status' and `$output'. +# Fail and display details if the expression evaluates to false. Details +# include the expression, `$status' and `$output'. +# +# NOTE: The expression must be a simple command. Compound commands, such +# as `[[', can be used only when executed with `bash -c'. # # Globals: # status # output # Arguments: -# $0 - expression to evaluate +# $1 - expression # Returns: # 0 - expression evaluates to TRUE -# 1 - expression evaluates to FALSE +# 1 - otherwise # Outputs: -# STDERR - assertion details, on failure +# STDERR - details, on failure assert() { if ! "$@"; then { local -ar single=( @@ -95,39 +96,102 @@ assert_equal() { fi } -# Fail and display an error message if the expected does not match the -# actual output. +# Fail and display details if `$status' is not 0. Details include +# `$status' and `$output'. +# +# Globals: +# status +# output +# Arguments: +# none +# Returns: +# 0 - `$status' is 0 +# 1 - otherwise +# Outputs: +# STDERR - details, on failure +assert_success() { + if (( status != 0 )); then + { local -ir width=6 + batslib_print_kv_single "$width" 'status' "$status" + batslib_print_kv_single_or_multi "$width" 'output' "$output" + } | batslib_decorate 'command failed' \ + | fail + fi +} + +# Fail and display details if `$status' is 0. Details include `$output'. +# +# Optionally, when the expected status is specified, fail when it does +# not equal `$status'. In this case, details include the expected and +# actual status, and `$output'. +# +# Globals: +# status +# output +# Arguments: +# $1 - [opt] expected status +# Returns: +# 0 - `$status' is not 0, or +# `$status' equals the expected status +# 1 - otherwise +# Outputs: +# STDERR - details, on failure +assert_failure() { + (( $# > 0 )) && local -r expected="$1" + if (( status == 0 )); then + batslib_print_kv_single_or_multi 6 'output' "$output" \ + | batslib_decorate 'command succeeded, but it was expected to fail' \ + | fail + elif (( $# > 0 )) && (( status != expected )); then + { local -ir width=8 + batslib_print_kv_single "$width" \ + 'expected' "$expected" \ + 'actual' "$status" + batslib_print_kv_single_or_multi "$width" \ + 'output' "$output" + } | batslib_decorate 'command failed as expected, but status differs' \ + | fail + fi +} + +# Fail and display details if the expected does not match the actual +# output or a fragment of it. +# +# By default, the entire output is matched. The assertion fails if the +# expected output does not equal `$output'. Details include both values. # -# By default, the expected output is compared against `$output', and the -# error message contains both values. +# When `-l ' is used, only the -th line is matched. The +# assertion fails if the expected line does not equal +# `${lines[}'. Details include the compared lines and . # -# Option `-l ' compares against `${lines[]}'. The error -# message contains the compared lines and . +# When `-l' is used without the argument, the output is searched +# for the expected line. The expected line is matched against each line +# in `${lines[@]}'. If no match is found the assertion fails. Details +# include the expected line and `$output'. # -# Option `-l' without `' compares against all lines in -# `${lines[@]}' until a match is found. If no match is found, the -# function fails and the error message contains the expected line and -# `$output'. +# By default, literal matching is performed. Options `-p' and `-r' +# enable partial (i.e. substring) and extended regular expression +# matching, respectively. Specifying an invalid extended regular +# expression with `-r' displays an error. # -# By default, literal matching is performed. Option `-p' and `-r' change -# this to partial (substring) and regular expression (extended) -# matching, respectively. +# Options `-p' and `-r' are mutually exclusive. When used +# simultaneously, an error is displayed. # # Globals: # output # lines # Options: # -l - match against the -th element of `${lines[@]}' -# -l - match against all elements of `${lines[@]}' until one matches -# -p - substring match -# -r - extended regular expression match +# -l - search `${lines[@]}' for the expected line +# -p - partial matching +# -r - extended regular expression matching # Arguments: # $1 - expected output # Returns: # 0 - expected matches the actual output # 1 - otherwise # Outputs: -# STDERR - assertion details, on failure +# STDERR - details, on failure # error message, on error assert_output() { local -i is_match_line=0 @@ -294,40 +358,48 @@ assert_output() { fi } -# Fail and display an error message if the unexpected matches the actual -# output. +# Fail and display details if the unexpected matches the actual output +# or a fragment of it. +# +# By default, the entire output is matched. The assertion fails if the +# unexpected output equals `$output'. Details include `$output'. # -# By default, the unexpected output is compared against `$output', and -# the error message contains this value. +# When `-l ' is used, only the -th line is matched. The +# assertion fails if the unexpected line equals `${lines[}'. +# Details include the compared line and . # -# Option `-l ' compares against `${lines[]}'. The error -# message contains the compared lines and . +# When `-l' is used without the argument, the output is searched +# for the unexpected line. The unexpected line is matched against each +# line in `${lines[]}'. If a match is found the assertion fails. +# Details include the unexpected line, the index where it was found and +# `$output' (with the unexpected line highlighted in it if `$output` is +# longer than one line). # -# Option `-l' without `' compares against all lines in -# `${lines[@]}' until a match is found. If a match is found, the -# function fails and the error message contains the unexpected line, its -# index and `${lines[@]}'. +# By default, literal matching is performed. Options `-p' and `-r' +# enable partial (i.e. substring) and extended regular expression +# matching, respectively. On failure, the substring or the regular +# expression is added to the details (if not already displayed). +# Specifying an invalid extended regular expression with `-r' displays +# an error. # -# By default, literal matching is performed. Option `-p' and `-r' change -# this to partial (substring) and regular expression (extended) -# matching, respectively. On failure, the substring and regular -# expression is added to the error message. +# Options `-p' and `-r' are mutually exclusive. When used +# simultaneously, an error is displayed. # # Globals: # output # lines # Options: # -l - match against the -th element of `${lines[@]}' -# -l - match against all elements of `${lines[@]}' until one matches -# -p - substring match -# -r - extended regular expression match +# -l - search `${lines[@]}' for the unexpected line +# -p - partial matching +# -r - extended regular expression matching # Arguments: # $1 - unexpected output # Returns: -# 0 - unexpected does not match the actual output +# 0 - unexpected matches the actual output # 1 - otherwise # Outputs: -# STDERR - assertion details, on failure +# STDERR - details, on failure # error message, on error refute_output() { local -i is_match_line=0 @@ -487,9 +559,9 @@ refute_output() { fi else if [[ ${lines[$idx]} == "$unexpected" ]]; then - batslib_print_kv_single 10 \ - 'index' "$idx" \ - 'unexpected' "$unexpected" \ + batslib_print_kv_single 5 \ + 'index' "$idx" \ + 'line' "${lines[$idx]}" \ | batslib_decorate 'line should differ' \ | fail fi @@ -522,61 +594,3 @@ refute_output() { fi fi } - -# Fail and display an error message if `$status' is not 0. The error -# message contains `$status' and `$output'. -# -# Globals: -# status -# output -# Arguments: -# none -# Returns: -# 0 - `$status' is 0 -# 1 - otherwise -# Outputs: -# STDERR - `$status' and `$output', if `$status' is not 0 -assert_success() { - if (( status != 0 )); then - { local -ir width=6 - batslib_print_kv_single "$width" 'status' "$status" - batslib_print_kv_single_or_multi "$width" 'output' "$output" - } | batslib_decorate 'command failed' \ - | fail - fi -} - -# Fail and display `$output' if `$status' is 0. Additionally, if the -# expected status is specified in the first parameter, fail if it does -# not equal `$status'. In this case, display both values and `$output'. -# -# Globals: -# status -# output -# Arguments: -# $1 - [opt] expected exit status -# Returns: -# 0 - $status is not 0, and optionally expected and actual `$status' -# equals -# 1 - otherwise -# Outputs: -# STDERR - `$output', if `$status' is 0 -# `$output', `$status' and expected status, if the latter two -# do not equal -assert_failure() { - (( $# > 0 )) && local -r expected="$1" - if (( status == 0 )); then - batslib_print_kv_single_or_multi 6 'output' "$output" \ - | batslib_decorate 'command succeeded, but it was expected to fail' \ - | fail - elif (( $# > 0 )) && (( status != expected )); then - { local -ir width=8 - batslib_print_kv_single "$width" \ - 'expected' "$expected" \ - 'actual' "$status" - batslib_print_kv_single_or_multi "$width" \ - 'output' "$output" - } | batslib_decorate 'command failed as expected, but status differs' \ - | fail - fi -} diff --git a/lib/bats/batslib/output.bash b/lib/bats/batslib/output.bash index 95cef47d..80dc39ac 100644 --- a/lib/bats/batslib/output.bash +++ b/lib/bats/batslib/output.bash @@ -6,20 +6,19 @@ # helper functions. # -# Print an error message to the standard error. The message is specified -# either by positional parameters or on the standard input (by piping or -# redirection). +# Print a message to the standard error. When no parameters are +# specified, the message is read from the standard input. # # Globals: # none # Arguments: -# $@ - [opt = STDIN] message to print +# $@ - [=STDIN] message # Returns: # none # Inputs: -# STDIN - [opt = $@] message to print +# STDIN - [=$@] message # Outputs: -# STDERR - error message +# STDERR - message batslib_err() { { if (( $# > 0 )); then echo "$@" @@ -29,9 +28,9 @@ batslib_err() { } >&2 } -# Counts the number of lines in the given text. +# Count the number of lines in the given string. # -# TODO(ztombol): Remove this notice and fix tests after Bats merged #93! +# TODO(ztombol): Fix tests and remove this note after #93 is resolved! # NOTE: Due to a bug in Bats, `batslib_count_lines "$output"' does not # give the same result as `${#lines[@]}' when the output contains # empty lines. @@ -40,11 +39,11 @@ batslib_err() { # Globals: # none # Arguments: -# $1 - text +# $1 - string # Returns: # none # Outputs: -# STDOUT - number of lines in given text +# STDOUT - number of lines batslib_count_lines() { local -i n_lines=0 local line @@ -54,14 +53,14 @@ batslib_count_lines() { echo "$n_lines" } -# Determine whether all parameters are single-line strings. +# Determine whether all strings are single-line. # # Globals: # none # Arguments: -# $@ - strings to test +# $@ - strings # Returns: -# 0 - all parameters are single-line strings +# 0 - all strings are single-line # 1 - otherwise batslib_is_single_line() { for string in "$@"; do @@ -71,14 +70,16 @@ batslib_is_single_line() { } # Determine the length of the longest key that has a single-line value. -# Useful in determining the column width for printing key-value pairs in -# a two-column format when some keys may have multi-line values and thus -# should be excluded. +# +# This function is useful in determining the correct width of the key +# column in two-column format when some keys may have multi-line values +# and thus should be excluded. # # Globals: # none # Arguments: -# $@ - strings to test +# $odd - key +# $even - value of the previous key # Returns: # none # Outputs: @@ -93,23 +94,23 @@ batslib_get_max_single_line_key_width() { echo "$max_len" } -# Print key-value pairs in two-column format. The first column contains -# the keys. Its width is specified with the first positional parameter, -# usually acquired using `batslib_get_max_single_line_key_width()', to -# nicely line up the values in the second column. The rest of the -# parameters are used to supply the key-value pairs. Every even-numbered -# parameter is a key and the following parameter is its value. +# Print key-value pairs in two-column format. +# +# Keys are displayed in the first column, and their corresponding values +# in the second. To evenly line up values, the key column is fixed-width +# and its width is specified with the first parameter (possibly computed +# using `batslib_get_max_single_line_key_width'). # # Globals: # none # Arguments: -# $1 - column width +# $1 - width of key column # $even - key # $odd - value of the previous key # Returns: # none # Outputs: -# STDOUT - key-value pairs in two-column format +# STDOUT - formatted key-value pairs batslib_print_kv_single() { local -ir col_width="$1"; shift while (( $# != 0 )); do @@ -118,10 +119,12 @@ batslib_print_kv_single() { done } -# Print key-value pairs in multi-line format. First, the key and the -# number of lines in the value is printed. Next, the value on a separate -# line. Every odd-numbered parameter is a key and the following -# parameters is its value. +# Print key-value pairs in multi-line format. +# +# The key is displayed first with the number of lines of its +# corresponding value in parenthesis. Next, starting on the next line, +# the value is displayed. For better readability, it is recommended to +# indent values using `batslib_prefix'. # # Globals: # none @@ -131,7 +134,7 @@ batslib_print_kv_single() { # Returns: # none # Outputs: -# STDOUT - key-value pairs in multi-line format +# STDOUT - formatted key-value pairs batslib_print_kv_multi() { while (( $# != 0 )); do printf '%s (%d lines):\n' "$1" "$( batslib_count_lines "$2" )" @@ -140,24 +143,27 @@ batslib_print_kv_multi() { done } -# Print all key-value pairs in either two-column or multi-line format. -# If all values are one line long, all pairs are printed in two-column -# format using `batslib_print_kv_single()'. Otherwise, they are printed -# in multi-line format using `batslib_print_kv_multi()' after each line -# of all values being prefixed with two spaces. +# Print all key-value pairs in either two-column or multi-line format +# depending on whether all values are single-line. +# +# If all values are single-line, print all pairs in two-column format +# with the specified key column width (identical to using +# `batslib_print_kv_single'). +# +# Otherwise, print all pairs in multi-line format after indenting values +# with two spaces for readability (identical to using `batslib_prefix' +# and `batslib_print_kv_multi') # # Globals: # none # Arguments: -# $1 - column width for two-column format +# $1 - width of key column (for two-column format) # $even - key # $odd - value of the previous key # Returns: # none # Outputs: -# STDOUT - key-value pairs in two-column format, if all values are -# single-line -# key-value pairs in multi-line format, otherwise +# STDOUT - formatted key-value pairs batslib_print_kv_single_or_multi() { local -ir width="$1"; shift local -a pairs=( "$@" ) @@ -179,16 +185,16 @@ batslib_print_kv_single_or_multi() { fi } -# Prefix each line of the input with an arbitrary string. +# Prefix each line read from the standard input with the given string. # # Globals: # none # Arguments: -# $1 - [opt = ' '] prefix string +# $1 - [= ] prefix string # Returns: # none # Inputs: -# STDIN - lines to prefix +# STDIN - lines # Outputs: # STDOUT - prefixed lines batslib_prefix() { @@ -199,19 +205,21 @@ batslib_prefix() { done } -# Mark select lines of the input by overwriting their first few -# characters with an arbitrary string. Usually, the input is indented by -# spaces first using `batslib_prefix()'. +# Mark select lines of the text read from the standard input by +# overwriting their beginning with the given string. +# +# Usually the input is indented by a few spaces using `batslib_prefix' +# first. # # Globals: # none # Arguments: # $1 - marking string -# $@ - zero-based indices of lines to mark +# $@ - indices (zero-based) of lines to mark # Returns: # none # Inputs: -# STDIN - lines to work on +# STDIN - lines # Outputs: # STDOUT - lines after marking batslib_mark() { @@ -232,10 +240,10 @@ batslib_mark() { done } -# Enclose the input in header and footer lines. The header contains an -# arbitrary title specified with the first positional parameter. The -# output is preceded and followed by an additional newline to make it -# stand out more. +# Enclose the input text in header and footer lines. +# +# The header contains the given string as title. The output is preceded +# and followed by an additional newline to make it stand out more. # # Globals: # none @@ -244,9 +252,9 @@ batslib_mark() { # Returns: # none # Inputs: -# STDIN - text to enclose +# STDIN - text # Outputs: -# STDOUT - enclosed text +# STDOUT - decorated text batslib_decorate() { echo echo "-- $1 --" diff --git a/man/batslib.7 b/man/batslib.7 index 065cfc66..53123850 100644 --- a/man/batslib.7 +++ b/man/batslib.7 @@ -73,7 +73,7 @@ Assertions about exit code and output operate on the results of the most recent . .TP \fBfail\fR [\fIMESSAGE\fR] -Display \fIMESSAGE\fR and fail\. This function provides a convenient way to report various failures\. Other test helpers also use it to display output on failure\. When no parameters are specified, \fIMESSAGE\fR is read from the standard input\. +Display \fIMESSAGE\fR and fail\. This function provides a convenient way to report various failures\. When no parameters are specified, \fIMESSAGE\fR is read from the standard input\. . .TP \fBassert\fR \fIEXPRESSION\fR @@ -83,18 +83,29 @@ Fail if \fIEXPRESSION\fR evaluates to false\. On failure, \fIEXPRESSION\fR, \fB$ \fBNote:\fR \fIEXPRESSION\fR must be a simple command\. Compound commands, such as \fB[[\fR, can be used only when executed with \fBbash \-c\fR\. . .TP -\fBassert_equal\fR \fIEXPECTED\fR \fIACTUAL\fR -Fail if \fIEXPECTED\fR and \fIACTUAL\fR do not equal\. On failure both values are displayed\. If either one is longer than one line, both are displayed in \fImulit\-line\fR format\. +\fBassert_equal\fR \fIACTUAL\fR \fIEXPECTED\fR +Fail if \fIACTUAL\fR and \fIEXPECTED\fR do not equal\. On failure, both values are displayed\. If either one is longer than one line, both are displayed in \fImulti\-line\fR format\. +. +.TP +\fBassert_success\fR +Fail if \fB$status\fR is not \fB0\fR\. On failure, \fB$status\fR and \fB$output\fR are displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulti\-line\fR format\. +. +.TP +\fBassert_failure\fR [\fISTATUS\fR] +Fail if \fB$status\fR is \fB0\fR\. On failure, \fB$output\fR is displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulti\-line\fR format\. +. +.IP +When \fISTATUS\fR is specified, fail if it does not equal \fB$status\fR\. On failure, \fISTATUS\fR, \fB$status\fR and \fB$output\fR are displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulti\-line\fR format\. . .TP \fBassert_output\fR [\-l [\fIINDEX\fR]] [\-p|\-r] \fIEXPECTED\fR -Fail if \fIEXPECTED\fR does not equal \fB$output\fR\. On failure both values are displayed\. If either one is longer than one line, both are displayed in \fImulit\-line\fR format\. +Fail if \fIEXPECTED\fR does not equal \fB$output\fR\. On failure, both values are displayed\. If either one is longer than one line, both are displayed in \fImulti\-line\fR format\. . .IP When \fB\-l \fR is used, fail if \fIEXPECTED\fR does not equal the \fIINDEX\fR\-th element of \fB${lines[@]}\fR\. On failure, \fIINDEX\fR, \fIEXPECTED\fR and the actual line are displayed\. . .IP -When \fB\-l\fR is used without \fB\fR, fail if \fIEXPECTED\fR is not found in \fB${lines[@]}\fR\. On failure, \fIEXPECTED\fR and \fB$output\fR are displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulit\-line\fR format\. +When \fB\-l\fR is used without \fB\fR, fail if \fIEXPECTED\fR is not found in \fB${lines[@]}\fR\. On failure, \fIEXPECTED\fR and \fB$output\fR are displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulti\-line\fR format\. . .IP The default, literal matching can be changed to substring and regular expression matching\. @@ -103,7 +114,10 @@ The default, literal matching can be changed to substring and regular expression Using \fB\-p\fR enables partial matching, and the assertion fails if \fIEXPECTED\fR as a substring is not found in the output\. . .IP -Using \fB\-r\fR enables regular expression matching, and the assertion fails if \fIEXPECTED\fR as an extended regular expression does not match the output\. +Using \fB\-r\fR enables regular expression matching, and the assertion fails if \fIEXPECTED\fR as an extended regular expression does not match the output\. If \fIEXPECTED\fR is not a valid extended regular expression, an error is displayed\. +. +.IP +Options \fB\-p\fR and \fB\-r\fR are mutually exclusive\. Using them simultaneously results in error\. . .IP \fBNOTE:\fR Due to a bug in Bats, empty lines are discarded from \fB${lines[@]}\fR, causing line indices to change and preventing testing for empty lines\. See \fIBUGS\fR for more\. @@ -113,7 +127,7 @@ Using \fB\-r\fR enables regular expression matching, and the assertion fails if Fail if \fIUNEXPECTED\fR equals \fB$output\fR\. On failure, \fB$output\fR is displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulti\-line\fR format\. . .IP -When \fB\-l \fR is used, fail if \fIUNEXPECTED\fR equals the \fIINDEX\fR\-th element of \fB${lines[@]}\fR\. On failure, \fIINDEX\fR and \fIUNEXPECTED\fR are displayed\. +When \fB\-l \fR is used, fail if \fIUNEXPECTED\fR equals the \fIINDEX\fR\-th element of \fB${lines[@]}\fR\. On failure, \fIINDEX\fR and \fB${lines[]}\fR are displayed\. . .IP When \fB\-l\fR is used without \fB\fR, fail if \fIUNEXPECTED\fR is found in \fB${lines[@]}\fR\. On failure, \fIUNEXPECTED\fR, its index in \fB${lines[@]}\fR and \fB$output\fR are displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulti\-line\fR format with the unexpected line highlighted\. @@ -122,24 +136,16 @@ When \fB\-l\fR is used without \fB\fR, fail if \fIUNEXPECTED\fR is found The default, literal matching can be changed to substring and regular expression matching\. . .IP -Using \fB\-p\fR enables partial matching, and the assertion fails if \fIUNEXPECTED\fR as a substring is found in the output\. On failure, \fIUNEXPECTED\fR is displayed in addition\. +Using \fB\-p\fR enables partial matching, and the assertion fails if \fIUNEXPECTED\fR as a substring is found in the output\. On failure, \fIUNEXPECTED\fR is displayed in addition (if not displayed already)\. . .IP -Using \fB\-r\fR enables regular expression matching, and the assertion fails if \fIUNEXPECTED\fR as an extended regular expression matches the output\. On failure, \fIUNEXPECTED\fR is displayed in addition\. +Using \fB\-r\fR enables regular expression matching, and the assertion fails if \fIUNEXPECTED\fR as an extended regular expression matches the output\. On failure, \fIUNEXPECTED\fR is displayed in addition (if not displayed already)\. If \fIUNEXPECTED\fR is not a valid extended regular expression, an error is displayed\. . .IP -\fBNOTE:\fR Due to a bug in Bats, empty lines are discarded from \fB${lines[@]}\fR, causing line indices to change and preventing testing for empty lines\. See \fIBUGS\fR for more\. -. -.TP -\fBassert_success\fR -Fail if \fB$status\fR is not \fB0\fR\. On failure \fB$status\fR and \fB$output\fR are displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulit\-line\fR format\. -. -.TP -\fBassert_failure\fR [\fIEXPECTED\fR] -Fail if \fB$status\fR is \fB0\fR\. On failure \fB$output\fR is displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulit\-line\fR format\. +Options \fB\-p\fR and \fB\-r\fR are mutually exclusive\. Using them simultaneously results in error\. . .IP -When \fIEXPECTED\fR is specified, fail if it does not equal \fB$status\fR\. On failure \fIEXPECTED\fR, \fB$status\fR and \fB$output\fR are displayed\. +\fBNOTE:\fR Due to a bug in Bats, empty lines are discarded from \fB${lines[@]}\fR, causing line indices to change and preventing testing for empty lines\. See \fIBUGS\fR for more\. . .SH "BUGS" . diff --git a/man/batslib.7.ronn b/man/batslib.7.ronn index e9d9ffad..c07ec061 100644 --- a/man/batslib.7.ronn +++ b/man/batslib.7.ronn @@ -65,9 +65,8 @@ recent invocation of `run`. * `fail` []: Display and fail. This function provides a convenient way to - report various failures. Other test helpers also use it to display - output on failure. When no parameters are specified, is read - from the standard input. + report various failures. When no parameters are specified, + is read from the standard input. * `assert` : Fail if evaluates to false. On failure, , @@ -77,14 +76,28 @@ recent invocation of `run`. such as `[[`, can be used only when executed with `bash -c`. * `assert_equal` : - Fail if and do not equal. On failure both values + Fail if and do not equal. On failure, both values are displayed. If either one is longer than one line, both are - displayed in _mulit-line_ format. + displayed in _multi-line_ format. + +* `assert_success`: + Fail if `$status` is not `0`. On failure, `$status` and `$output` are + displayed. If `$output` is longer than one line, it is displayed in + _multi-line_ format. + +* `assert_failure` []: + Fail if `$status` is `0`. On failure, `$output` is displayed. If + `$output` is longer than one line, it is displayed in _multi-line_ + format. + + When is specified, fail if it does not equal `$status`. On + failure, , `$status` and `$output` are displayed. If `$output` + is longer than one line, it is displayed in _multi-line_ format. * `assert_output` [-l []] [-p|-r] : - Fail if does not equal `$output`. On failure both values + Fail if does not equal `$output`. On failure, both values are displayed. If either one is longer than one line, both are - displayed in _mulit-line_ format. + displayed in _multi-line_ format. When `-l ` is used, fail if does not equal the -th element of `${lines[@]}`. On failure, , @@ -92,7 +105,7 @@ recent invocation of `run`. When `-l` is used without ``, fail if is not found in `${lines[@]}`. On failure, and `$output` are displayed. - If `$output` is longer than one line, it is displayed in _mulit-line_ + If `$output` is longer than one line, it is displayed in _multi-line_ format. The default, literal matching can be changed to substring and regular @@ -103,7 +116,11 @@ recent invocation of `run`. Using `-r` enables regular expression matching, and the assertion fails if as an extended regular expression does not match - the output. + the output. If is not a valid extended regular expression, + an error is displayed. + + Options `-p` and `-r` are mutually exclusive. Using them + simultaneously results in error. **NOTE:** Due to a bug in Bats, empty lines are discarded from `${lines[@]}`, causing line indices to change and preventing testing @@ -115,8 +132,8 @@ recent invocation of `run`. _multi-line_ format. When `-l ` is used, fail if equals the -th - element of `${lines[@]}`. On failure, and are - displayed. + element of `${lines[@]}`. On failure, and `${lines[]}` + are displayed. When `-l` is used without ``, fail if is found in `${lines[@]}`. On failure, , its index in `${lines[@]}` @@ -129,29 +146,21 @@ recent invocation of `run`. Using `-p` enables partial matching, and the assertion fails if as a substring is found in the output. On failure, - is displayed in addition. + is displayed in addition (if not displayed already). Using `-r` enables regular expression matching, and the assertion fails if as an extended regular expression matches the - output. On failure, is displayed in addition. + output. On failure, is displayed in addition (if not + displayed already). If is not a valid extended regular + expression, an error is displayed. + + Options `-p` and `-r` are mutually exclusive. Using them + simultaneously results in error. **NOTE:** Due to a bug in Bats, empty lines are discarded from `${lines[@]}`, causing line indices to change and preventing testing for empty lines. See [BUGS][] for more. -* `assert_success`: - Fail if `$status` is not `0`. On failure `$status` and `$output` are - displayed. If `$output` is longer than one line, it is displayed in - _mulit-line_ format. - -* `assert_failure` []: - Fail if `$status` is `0`. On failure `$output` is displayed. If - `$output` is longer than one line, it is displayed in _mulit-line_ - format. - - When is specified, fail if it does not equal `$status`. On - failure , `$status` and `$output` are displayed. - ## BUGS diff --git a/test/50-lib-internal-output-10-batslib_err.bats b/test/50-lib-internal-output-10-batslib_err.bats index 21a1a876..a29d455a 100755 --- a/test/50-lib-internal-output-10-batslib_err.bats +++ b/test/50-lib-internal-output-10-batslib_err.bats @@ -2,14 +2,14 @@ load test_helper -@test 'batslib_err() prints positional parameters' { - run batslib_err 'message' +@test 'batslib_err() : displays ' { + run batslib_err 'm1' 'm2' [ "$status" -eq 0 ] - [ "$output" == 'message' ] + [ "$output" == 'm1 m2' ] } -@test 'batslib_err() prints STDIN when no positional parameters are specified' { - run bash -c "source '${BATS_LIB}/batslib.bash'; echo 'message' | batslib_err" +@test 'batslib_err(): reads from STDIN' { + run bash -c "source '${BATS_LIB}/batslib.bash'; echo 'm1' 'm2' | batslib_err" [ "$status" -eq 0 ] - [ "$output" == 'message' ] + [ "$output" == 'm1 m2' ] } diff --git a/test/50-lib-internal-output-11-batslib_count_lines.bats b/test/50-lib-internal-output-11-batslib_count_lines.bats index dff37bbc..ea172c3e 100755 --- a/test/50-lib-internal-output-11-batslib_count_lines.bats +++ b/test/50-lib-internal-output-11-batslib_count_lines.bats @@ -2,19 +2,19 @@ load test_helper -@test 'batslib_count_lines() prints the number of lines in the input' { +@test 'batslib_count_lines() : displays the number of lines in ' { run batslib_count_lines $'a\nb\nc\n' [ "$status" -eq 0 ] [ "$output" == '3' ] } -@test 'batslib_count_lines() counts last line when it is not terminated by a newline' { +@test 'batslib_count_lines() : counts the last line when it is not terminated by a newline' { run batslib_count_lines $'a\nb\nc' [ "$status" -eq 0 ] [ "$output" == '3' ] } -@test 'batslib_count_lines() counts empty lines' { +@test 'batslib_count_lines() : counts empty lines' { run batslib_count_lines $'\n\n\n' [ "$status" -eq 0 ] [ "$output" == '3' ] diff --git a/test/50-lib-internal-output-12-batslib_is_single_line.bats b/test/50-lib-internal-output-12-batslib_is_single_line.bats index 9af68bab..484b64da 100755 --- a/test/50-lib-internal-output-12-batslib_is_single_line.bats +++ b/test/50-lib-internal-output-12-batslib_is_single_line.bats @@ -2,12 +2,12 @@ load test_helper -@test 'batslib_is_single_line() returns 0 if all parameters are one-line strings' { +@test 'batslib_is_single_line() : returns 0 if all are single-line' { run batslib_is_single_line 'a' $'b\n' 'c' [ "$status" -eq 0 ] } -@test 'batslib_is_single_line() returns 1 if at least one parameter is longer than one line' { +@test 'batslib_is_single_line() : returns 1 if at least one of is longer than one line' { run batslib_is_single_line 'a' $'b\nb' 'c' [ "$status" -eq 1 ] } diff --git a/test/50-lib-internal-output-13-batslib_get_max_single_line_key_width.bats b/test/50-lib-internal-output-13-batslib_get_max_single_line_key_width.bats index 9b405a5e..e6af161f 100755 --- a/test/50-lib-internal-output-13-batslib_get_max_single_line_key_width.bats +++ b/test/50-lib-internal-output-13-batslib_get_max_single_line_key_width.bats @@ -2,15 +2,19 @@ load test_helper -@test 'batslib_get_max_single_line_key_width() prints the length of the longest key' { - local -ar pairs=( 'k _1' 'v 1' 'k 2' 'v 2' 'k __3' 'v 3' ) +@test 'batslib_get_max_single_line_key_width() : displays the length of the longest key' { + local -ar pairs=( 'k _1' 'v 1' + 'k 2' 'v 2' + 'k __3' 'v 3' ) run batslib_get_max_single_line_key_width "${pairs[@]}" [ "$status" -eq 0 ] [ "$output" == '5' ] } -@test 'batslib_get_max_single_line_key_width() only considers keys with one-line values' { - local -ar pairs=( 'k _1' 'v 1' 'k 2' 'v 2' 'k __3' $'v\n3' ) +@test 'batslib_get_max_single_line_key_width() : only considers keys with single-line values' { + local -ar pairs=( 'k _1' 'v 1' + 'k 2' 'v 2' + 'k __3' $'v\n3' ) run batslib_get_max_single_line_key_width "${pairs[@]}" [ "$status" -eq 0 ] [ "$output" == '4' ] diff --git a/test/50-lib-internal-output-14-batslib_print_kv_single.bats b/test/50-lib-internal-output-14-batslib_print_kv_single.bats index 70942de0..7637897c 100755 --- a/test/50-lib-internal-output-14-batslib_print_kv_single.bats +++ b/test/50-lib-internal-output-14-batslib_print_kv_single.bats @@ -2,12 +2,10 @@ load test_helper -@test 'batslib_print_kv_single() prints key-value pairs with keys in fixed width columns' { - local -ar pairs=( - 'k _1' 'v 1' - 'k 2 ' 'v 2' - 'k __3' 'v 3' - ) +@test 'batslib_print_kv_single() : displays in two-column format with wide key column' { + local -ar pairs=( 'k _1' 'v 1' + 'k 2 ' 'v 2' + 'k __3' 'v 3' ) run batslib_print_kv_single 5 "${pairs[@]}" [ "$status" -eq 0 ] [ "${#lines[@]}" == '3' ] @@ -16,12 +14,10 @@ load test_helper [ "${lines[2]}" == 'k __3 : v 3' ] } -@test 'batslib_print_kv_single() does not truncate keys when the column is too narrow' { - local -ar pairs=( - 'k _1' 'v 1' - 'k 2' 'v 2' - 'k __3' 'v 3' - ) +@test 'batslib_print_kv_single() : does not truncate keys when the column is too narrow' { + local -ar pairs=( 'k _1' 'v 1' + 'k 2' 'v 2' + 'k __3' 'v 3' ) run batslib_print_kv_single 0 "${pairs[@]}" [ "$status" -eq 0 ] [ "${#lines[@]}" == '3' ] diff --git a/test/50-lib-internal-output-15-batslib_print_kv_multi.bats b/test/50-lib-internal-output-15-batslib_print_kv_multi.bats index 885a6b1a..6ad4b3db 100755 --- a/test/50-lib-internal-output-15-batslib_print_kv_multi.bats +++ b/test/50-lib-internal-output-15-batslib_print_kv_multi.bats @@ -2,12 +2,10 @@ load test_helper -@test 'batslib_print_kv_multi() prints keys and values on separate lines' { - local -ar pairs=( - 'k _1' 'v 1' - 'k 2' $'v 2-1\nv 2-2' - 'k __3' 'v 3' - ) +@test 'batslib_print_kv_multi() : displays in multi-line format' { + local -ar pairs=( 'k _1' 'v 1' + 'k 2' $'v 2-1\nv 2-2' + 'k __3' 'v 3' ) run batslib_print_kv_multi "${pairs[@]}" [ "$status" -eq 0 ] [ "${#lines[@]}" == '7' ] diff --git a/test/50-lib-internal-output-16-batslib_print_kv_single_or_multi.bats b/test/50-lib-internal-output-16-batslib_print_kv_single_or_multi.bats index 5e7cb1b2..c20d1017 100755 --- a/test/50-lib-internal-output-16-batslib_print_kv_single_or_multi.bats +++ b/test/50-lib-internal-output-16-batslib_print_kv_single_or_multi.bats @@ -2,12 +2,10 @@ load test_helper -@test 'batslib_print_kv_single_or_multi() prints in two-column format if all values are one line long' { - local -ar pairs=( - 'k _1' 'v 1' - 'k 2 ' 'v 2' - 'k __3' 'v 3' - ) +@test 'batslib_print_kv_single_or_multi() : displays in two-column format if all values are one line long' { + local -ar pairs=( 'k _1' 'v 1' + 'k 2 ' 'v 2' + 'k __3' 'v 3' ) run batslib_print_kv_single_or_multi 5 "${pairs[@]}" [ "$status" -eq 0 ] [ "${#lines[@]}" == '3' ] @@ -16,12 +14,10 @@ load test_helper [ "${lines[2]}" == 'k __3 : v 3' ] } -@test 'batslib_print_kv_single_or_multi() prints in multi-line format if a at least one value is longer than one line' { - local -ar pairs=( - 'k _1' 'v 1' - 'k 2' $'v 2-1\nv 2-2' - 'k __3' 'v 3' - ) +@test 'batslib_print_kv_single_or_multi() : displays in multi-line format if at least one value is longer than one line' { + local -ar pairs=( 'k _1' 'v 1' + 'k 2' $'v 2-1\nv 2-2' + 'k __3' 'v 3' ) run batslib_print_kv_single_or_multi 5 "${pairs[@]}" [ "$status" -eq 0 ] [ "${#lines[@]}" == '7' ] diff --git a/test/50-lib-internal-output-17-batslib_prefix.bats b/test/50-lib-internal-output-17-batslib_prefix.bats index 23a0e3d3..ba33e040 100755 --- a/test/50-lib-internal-output-17-batslib_prefix.bats +++ b/test/50-lib-internal-output-17-batslib_prefix.bats @@ -2,7 +2,7 @@ load test_helper -@test 'batslib_prefix() prefixes each line with the given string' { +@test 'batslib_prefix() : prefixes each line of the input with ' { run bash -c "source '${BATS_LIB}/batslib.bash'; printf 'a\nb\nc\n' | batslib_prefix '_'" [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 3 ] @@ -11,29 +11,29 @@ load test_helper [ "${lines[2]}" == '_c' ] } -@test 'batslib_prefix() uses two spaces as default prefix' { - run bash -c "source '${BATS_LIB}/batslib.bash'; printf 'a\nb\nc\n' | batslib_prefix" +@test 'batslib_prefix() : prefixes the last line when it is not terminated by a newline' { + run bash -c "source '${BATS_LIB}/batslib.bash'; printf 'a\nb\nc' | batslib_prefix '_'" [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 3 ] - [ "${lines[0]}" == ' a' ] - [ "${lines[1]}" == ' b' ] - [ "${lines[2]}" == ' c' ] + [ "${lines[0]}" == '_a' ] + [ "${lines[1]}" == '_b' ] + [ "${lines[2]}" == '_c' ] } -@test 'batslib_prefix() prefixes the last line when it is not terminated by a newline' { - run bash -c "source '${BATS_LIB}/batslib.bash'; printf 'a\nb\nc' | batslib_prefix" +@test 'batslib_prefix() : prefixes empty lines' { + run bash -c "source '${BATS_LIB}/batslib.bash'; printf '\n\n\n' | batslib_prefix '_'" [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 3 ] - [ "${lines[0]}" == ' a' ] - [ "${lines[1]}" == ' b' ] - [ "${lines[2]}" == ' c' ] + [ "${lines[0]}" == '_' ] + [ "${lines[1]}" == '_' ] + [ "${lines[2]}" == '_' ] } -@test 'batslib_prefix() prefixes empty lines' { - run bash -c "source '${BATS_LIB}/batslib.bash'; printf '\n\n\n' | batslib_prefix" +@test 'batslib_prefix(): default to two spaces' { + run bash -c "source '${BATS_LIB}/batslib.bash'; printf 'a\nb\nc\n' | batslib_prefix" [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 3 ] - [ "${lines[0]}" == ' ' ] - [ "${lines[1]}" == ' ' ] - [ "${lines[2]}" == ' ' ] + [ "${lines[0]}" == ' a' ] + [ "${lines[1]}" == ' b' ] + [ "${lines[2]}" == ' c' ] } diff --git a/test/50-lib-internal-output-18-batslib_mark.bats b/test/50-lib-internal-output-18-batslib_mark.bats index 4502e975..6518e459 100755 --- a/test/50-lib-internal-output-18-batslib_mark.bats +++ b/test/50-lib-internal-output-18-batslib_mark.bats @@ -2,7 +2,7 @@ load test_helper -@test 'batslib_mark() highlights a single line' { +@test 'batslib_mark() : marks the -th line of the input with ' { run bash -c "source '${BATS_LIB}/batslib.bash'; printf ' a\n b\n c\n' | batslib_mark '>' 0" [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 3 ] @@ -11,7 +11,7 @@ load test_helper [ "${lines[2]}" == ' c' ] } -@test 'batslib_mark() highlights lines when indices are in ascending order' { +@test 'batslib_mark() : marks multiple lines when is in ascending order' { run bash -c "source '${BATS_LIB}/batslib.bash'; printf ' a\n b\n c\n' | batslib_mark '>' 1 2" [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 3 ] @@ -20,16 +20,17 @@ load test_helper [ "${lines[2]}" == '>c' ] } -@test 'batslib_mark() highlights lines when indices are in random order' { - run bash -c "source '${BATS_LIB}/batslib.bash'; printf ' a\n b\n c\n' | batslib_mark '>' 2 1" +@test 'batslib_mark() : marks multiple lines when is in random order' { + run bash -c "source '${BATS_LIB}/batslib.bash'; printf ' a\n b\n c\n d\n' | batslib_mark '>' 2 1 3" [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 3 ] + [ "${#lines[@]}" -eq 4 ] [ "${lines[0]}" == ' a' ] [ "${lines[1]}" == '>b' ] [ "${lines[2]}" == '>c' ] + [ "${lines[3]}" == '>d' ] } -@test 'batslib_mark() ignores duplicate line indices' { +@test 'batslib_mark() : ignores duplicate indices' { run bash -c "source '${BATS_LIB}/batslib.bash'; printf ' a\n b\n c\n' | batslib_mark '>' 1 2 1" [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 3 ] @@ -38,7 +39,7 @@ load test_helper [ "${lines[2]}" == '>c' ] } -@test 'batslib_mark() outputs the input untouched if the marking string is the empty string' { +@test 'batslib_mark() : outputs the input untouched if is the empty string' { run bash -c "source '${BATS_LIB}/batslib.bash'; printf ' a\n b\n c\n' | batslib_mark '' 1" [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 3 ] @@ -47,7 +48,7 @@ load test_helper [ "${lines[2]}" == ' c' ] } -@test 'batslib_mark() highlights the last line when it is not terminated by a newline' { +@test 'batslib_mark() : marks the last line when it is not terminated by a newline' { run bash -c "source '${BATS_LIB}/batslib.bash'; printf ' a\n b\n c' | batslib_mark '>' 2" [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 3 ] @@ -56,7 +57,7 @@ load test_helper [ "${lines[2]}" == '>c' ] } -@test 'batslib_mark() replaces the line with the marking string if the line is shorter or equally long' { +@test 'batslib_mark() : does not truncate if it is longer than the marked line' { run bash -c "source '${BATS_LIB}/batslib.bash'; printf '\n' | batslib_mark '>' 0" [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 1 ] diff --git a/test/50-lib-internal-output-19-batslib_decorate.bats b/test/50-lib-internal-output-19-batslib_decorate.bats index d7bcbe8a..5a656b13 100755 --- a/test/50-lib-internal-output-19-batslib_decorate.bats +++ b/test/50-lib-internal-output-19-batslib_decorate.bats @@ -2,7 +2,7 @@ load test_helper -@test 'batslib_decorate() encloses body in header and footer lines' { +@test 'batslib_decorate() : encloses the input in a footer line and a header line containing <title>' { run bash -c "source '${BATS_LIB}/batslib.bash'; echo 'body' | batslib_decorate 'title'" [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 3 ] diff --git a/test/60-lib-assertion-10-fail.bats b/test/60-lib-assertion-10-fail.bats index e25d259e..b21cfebc 100755 --- a/test/60-lib-assertion-10-fail.bats +++ b/test/60-lib-assertion-10-fail.bats @@ -2,18 +2,13 @@ load test_helper -@test 'fail() returns 1' { - run fail '' - [ "$status" -eq 1 ] -} - -@test 'fail() prints positional parameters' { +@test 'fail() <message>: returns 1 and displays <message>' { run fail 'message' [ "$status" -eq 1 ] [ "$output" == 'message' ] } -@test 'fail() prints STDIN if no positional parameters are specified' { +@test 'fail(): reads <message> from STDIN' { run bash -c "source '${BATS_LIB}/batslib.bash'; echo 'message' | fail" [ "$status" -eq 1 ] [ "$output" == 'message' ] diff --git a/test/60-lib-assertion-11-assert.bats b/test/60-lib-assertion-11-assert.bats index 0652819b..2d5b183f 100755 --- a/test/60-lib-assertion-11-assert.bats +++ b/test/60-lib-assertion-11-assert.bats @@ -9,19 +9,19 @@ load test_helper } @test 'assert() <expression>: returns 1 and displays details if <expression> evaluates to FALSE' { - run bash -c 'echo "error"; false' + run bash -c 'echo "a"; false' run assert false [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 5 ] [ "${lines[0]}" == '-- assertion failed --' ] [ "${lines[1]}" == 'expression : false' ] [ "${lines[2]}" == 'status : 1' ] - [ "${lines[3]}" == 'output : error' ] + [ "${lines[3]}" == 'output : a' ] [ "${lines[4]}" == '--' ] } -@test "assert() <expression>: displays details in multi-line format if \`\$output' is longer than one line" { - run bash -c "echo $'0. error\n1. error'; false" +@test "assert() <expression>: displays \`\$output' in multi-line format if it is longer than one line" { + run bash -c "echo $'a 0\na 1'; false" run assert false [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 7 ] @@ -29,7 +29,7 @@ load test_helper [ "${lines[1]}" == 'expression : false' ] [ "${lines[2]}" == 'status : 1' ] [ "${lines[3]}" == 'output (2 lines):' ] - [ "${lines[4]}" == ' 0. error' ] - [ "${lines[5]}" == ' 1. error' ] + [ "${lines[4]}" == ' a 0' ] + [ "${lines[5]}" == ' a 1' ] [ "${lines[6]}" == '--' ] } diff --git a/test/60-lib-assertion-12-assert_equal.bats b/test/60-lib-assertion-12-assert_equal.bats index 3201ade2..b21725ae 100755 --- a/test/60-lib-assertion-12-assert_equal.bats +++ b/test/60-lib-assertion-12-assert_equal.bats @@ -18,7 +18,7 @@ load test_helper [ "${lines[3]}" == '--' ] } -@test "assert_equal() <actual> <expected>: displays details in multi-line format if <actual> is longer than one line" { +@test 'assert_equal() <actual> <expected>: displays details in multi-line format if <actual> is longer than one line' { run assert_equal $'a 0\na 1' 'b' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 7 ] @@ -31,7 +31,7 @@ load test_helper [ "${lines[6]}" == '--' ] } -@test "assert_equal() <actual> <expected>: displays details in multi-line format if <expected> is longer than one line" { +@test 'assert_equal() <actual> <expected>: displays details in multi-line format if <expected> is longer than one line' { run assert_equal 'a' $'b 0\nb 1' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 7 ] diff --git a/test/60-lib-assertion-14-assert_success.bats b/test/60-lib-assertion-13-assert_success.bats similarity index 56% rename from test/60-lib-assertion-14-assert_success.bats rename to test/60-lib-assertion-13-assert_success.bats index 01dc2af5..d16d6535 100755 --- a/test/60-lib-assertion-14-assert_success.bats +++ b/test/60-lib-assertion-13-assert_success.bats @@ -2,33 +2,33 @@ load test_helper -@test 'assert_success() returns 0 if $status is 0' { +@test "assert_success(): returns 0 if \`\$status' is 0" { run true run assert_success [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } -@test 'assert_success() returns 1 and displays $status and $output if $status is not 0' { - run bash -c 'echo error; exit 1' +@test "assert_success(): returns 1 and displays details if \`\$status' is not 0" { + run bash -c 'echo a; exit 1' run assert_success [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 4 ] [ "${lines[0]}" == '-- command failed --' ] [ "${lines[1]}" == 'status : 1' ] - [ "${lines[2]}" == 'output : error' ] + [ "${lines[2]}" == 'output : a' ] [ "${lines[3]}" == '--' ] } -@test 'assert_success() displays $output in multi-line format if necessary' { - run bash -c "echo $'error 1\nerror 2'; exit 1" +@test "assert_success(): displays \`\$output' in multi-line format if it is longer than one line" { + run bash -c "echo $'a 0\na 1'; exit 1" run assert_success [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 6 ] [ "${lines[0]}" == '-- command failed --' ] [ "${lines[1]}" == 'status : 1' ] [ "${lines[2]}" == 'output (2 lines):' ] - [ "${lines[3]}" == ' error 1' ] - [ "${lines[4]}" == ' error 2' ] + [ "${lines[3]}" == ' a 0' ] + [ "${lines[4]}" == ' a 1' ] [ "${lines[5]}" == '--' ] } diff --git a/test/60-lib-assertion-15-assert_failure.bats b/test/60-lib-assertion-14-assert_failure.bats similarity index 56% rename from test/60-lib-assertion-15-assert_failure.bats rename to test/60-lib-assertion-14-assert_failure.bats index 56dfdb9f..7fdf44b0 100755 --- a/test/60-lib-assertion-15-assert_failure.bats +++ b/test/60-lib-assertion-14-assert_failure.bats @@ -2,56 +2,56 @@ load test_helper -@test 'assert_failure() returns 0 if $status is not 0' { +@test "assert_failure(): returns 0 if \`\$status' is not 0" { run false run assert_failure [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } -@test 'assert_failure() returns 1 and displays $output if $status is 0' { - run bash -c 'echo ok; exit 0' +@test "assert_failure(): returns 1 and displays details if \`\$status' is 0" { + run bash -c 'echo a; exit 0' run assert_failure [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 3 ] [ "${lines[0]}" == '-- command succeeded, but it was expected to fail --' ] - [ "${lines[1]}" == 'output : ok' ] + [ "${lines[1]}" == 'output : a' ] [ "${lines[2]}" == '--' ] } -@test 'assert_failure() displays $output in multi-line format if necessary' { - run bash -c "echo $'ok 1\nok 2'; exit 0" +@test "assert_failure(): displays \`\$output' in multi-line format if it is longer then one line" { + run bash -c "echo $'a 0\na 1'; exit 0" run assert_failure [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 5 ] [ "${lines[0]}" == '-- command succeeded, but it was expected to fail --' ] [ "${lines[1]}" == 'output (2 lines):' ] - [ "${lines[2]}" == ' ok 1' ] - [ "${lines[3]}" == ' ok 2' ] + [ "${lines[2]}" == ' a 0' ] + [ "${lines[3]}" == ' a 1' ] [ "${lines[4]}" == '--' ] } -@test 'assert_failure() test $status against the first positional parameter if specified' { - run bash -c 'echo ok; exit 1' +@test "assert_failure() <status>: returns 0 if \`\$status' equals <status>" { + run bash -c 'exit 1' run assert_failure 1 [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } -@test 'assert_failure() displays $output, and the expected and actual status if they differ' { - run bash -c 'echo error; exit 1' +@test "assert_failure() <status>: returns 1 and displays details if \`\$status' does not equal <status>" { + run bash -c 'echo a; exit 1' run assert_failure 2 [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 5 ] [ "${lines[0]}" == '-- command failed as expected, but status differs --' ] [ "${lines[1]}" == 'expected : 2' ] [ "${lines[2]}" == 'actual : 1' ] - [ "${lines[3]}" == 'output : error' ] + [ "${lines[3]}" == 'output : a' ] [ "${lines[4]}" == '--' ] } -@test 'assert_failure() when status differs, displays $output in multi-line format if necessary' { - run bash -c "echo $'error 1\nerror 2'; exit 1" +@test "assert_failure() <status>: displays \`\$output' in multi-line format if it is longer then one line" { + run bash -c "echo $'a 0\na 1'; exit 1" run assert_failure 2 [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 7 ] @@ -59,7 +59,7 @@ load test_helper [ "${lines[1]}" == 'expected : 2' ] [ "${lines[2]}" == 'actual : 1' ] [ "${lines[3]}" == 'output (2 lines):' ] - [ "${lines[4]}" == ' error 1' ] - [ "${lines[5]}" == ' error 2' ] + [ "${lines[4]}" == ' a 0' ] + [ "${lines[5]}" == ' a 1' ] [ "${lines[6]}" == '--' ] } diff --git a/test/60-lib-assertion-13-assert_output.bats b/test/60-lib-assertion-15-assert_output.bats similarity index 94% rename from test/60-lib-assertion-13-assert_output.bats rename to test/60-lib-assertion-15-assert_output.bats index 6cfff8d2..eed2e43d 100755 --- a/test/60-lib-assertion-13-assert_output.bats +++ b/test/60-lib-assertion-15-assert_output.bats @@ -41,7 +41,7 @@ load test_helper [ "${lines[6]}" == '--' ] } -@test "assert_output() <expected>: displays details in multi-line format if <expected> is longer than one line" { +@test 'assert_output() <expected>: displays details in multi-line format if <expected> is longer than one line' { run echo 'b' run assert_output $'a 0\na 1' [ "$status" -eq 1 ] @@ -143,7 +143,7 @@ load test_helper [ "${lines[6]}" == '--' ] } -@test "assert_output() -r <regex>: displays details in multi-line format if <regex> is longer than one line" { +@test 'assert_output() -r <regex>: displays details in multi-line format if <regex> is longer than one line' { run echo 'b' run assert_output -r $'.*a\nb.*' [ "$status" -eq 1 ] @@ -256,7 +256,7 @@ load test_helper [ "${lines[3]}" == '--' ] } -@test "assert_output() -l <expected>: displays \`\$output' in multi-line format if necessary" { +@test "assert_output() -l <expected>: displays \`\$output' in multi-line format if it is longer than oen line" { run echo $'b 0\nb 1' run assert_output -l 'a' [ "$status" -eq 1 ] @@ -277,14 +277,14 @@ load test_helper # Partial matching: `-p <partial>'. -@test "assert_output() -l -p <partial>: returns 0 if <partial> is a substring in at least one line in \`\${lines[@]}'" { +@test "assert_output() -l -p <partial>: returns 0 if <partial> is a substring in any line in \`\${lines[@]}'" { run echo $'a\nabc\nc' run assert_output -l -p 'b' [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } -@test "assert_output() -l -p <partial>: returns 1 and displays details if <partial> is not a substring in any line in \`\${lines[@]}'" { +@test "assert_output() -l -p <partial>: returns 1 and displays details if <partial> is not a substring in any lines in \`\${lines[@]}'" { run echo 'b' run assert_output -l -p 'a' [ "$status" -eq 1 ] @@ -295,7 +295,7 @@ load test_helper [ "${lines[3]}" == '--' ] } -@test "assert_output() -l -p <partial>: displays details in multi-line format if necessary" { +@test "assert_output() -l -p <partial>: displays \`\$output' in multi-line format if it is longer than one line" { run echo $'b 0\nb 1' run assert_output -l -p 'a' [ "$status" -eq 1 ] @@ -328,7 +328,7 @@ load test_helper [ "${lines[3]}" == '--' ] } -@test 'assert_output() -l -r <regex>: displays details in multi-line format if necessary' { +@test "assert_output() -l -r <regex>: displays \`\$output' in multi-line format if longer than one line" { run echo $'b 0\nb 1' run assert_output -l -r '.*a.*' [ "$status" -eq 1 ] @@ -364,7 +364,7 @@ load test_helper [ "${lines[2]}" == '--' ] } -@test "assert_output() -r <regex>: returns 1 and displays an error message if <regex> is not a valid extended regular expression" { +@test 'assert_output() -r <regex>: returns 1 and displays an error message if <regex> is not a valid extended regular expression' { run assert_output -r '[.*' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 3 ] diff --git a/test/60-lib-assertion-16-refute_output.bats b/test/60-lib-assertion-16-refute_output.bats index b7c4b913..2cc569f2 100755 --- a/test/60-lib-assertion-16-refute_output.bats +++ b/test/60-lib-assertion-16-refute_output.bats @@ -26,7 +26,7 @@ load test_helper [ "${lines[2]}" == '--' ] } -@test "refute_output() <unexpected>: displays details in multi-line format if necessary" { +@test 'refute_output() <unexpected>: displays details in multi-line format if necessary' { run echo $'a 0\na 1' run refute_output $'a 0\na 1' [ "$status" -eq 1 ] @@ -64,7 +64,7 @@ load test_helper [ "${lines[3]}" == '--' ] } -@test "refute_output() -p <partial>: displays details in multi-line format if necessary" { +@test 'refute_output() -p <partial>: displays details in multi-line format if necessary' { run echo $'a 0\na 1' run refute_output -p 'a' [ "$status" -eq 1 ] @@ -98,7 +98,7 @@ load test_helper [ "${lines[3]}" == '--' ] } -@test "refute_output() -r <regex>: displays details in multi-line format if necessary" { +@test 'refute_output() -r <regex>: displays details in multi-line format if necessary' { run echo $'a 0\na 1' run refute_output -r '.*a.*' [ "$status" -eq 1 ] @@ -132,8 +132,8 @@ load test_helper [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 4 ] [ "${lines[0]}" == '-- line should differ --' ] - [ "${lines[1]}" == 'index : 1' ] - [ "${lines[2]}" == 'unexpected : b' ] + [ "${lines[1]}" == 'index : 1' ] + [ "${lines[2]}" == 'line : b' ] [ "${lines[3]}" == '--' ] } @@ -211,7 +211,7 @@ load test_helper [ "${lines[4]}" == '--' ] } -@test "refute_output() -l <unexpected>: displays details in multi-line format if necessary" { +@test 'refute_output() -l <unexpected>: displays details in multi-line format if necessary' { run echo $'a\nb\nc' run refute_output -l 'b' [ "$status" -eq 1 ] @@ -241,7 +241,7 @@ load test_helper [ "${#lines[@]}" -eq 0 ] } -@test "refute_output() -l -p <partial>: returns 1 and displays details if <partial> is a substring in at least one line in \`\${lines[@]}'" { +@test "refute_output() -l -p <partial>: returns 1 and displays details if <partial> is a substring in any line in \`\${lines[@]}'" { run echo 'a' run refute_output -l -p 'a' [ "$status" -eq 1 ] @@ -253,7 +253,7 @@ load test_helper [ "${lines[4]}" == '--' ] } -@test "refute_output() -l -p <partial>: displays details in multi-line format if necessary" { +@test 'refute_output() -l -p <partial>: displays details in multi-line format if necessary' { run echo $'a\nabc\nc' run refute_output -l -p 'b' [ "$status" -eq 1 ] @@ -327,7 +327,7 @@ load test_helper [ "${lines[2]}" == '--' ] } -@test "refute_output() -r <regex>: returns 1 and displays an error message if <regex> is not a valid extended regular expression" { +@test 'refute_output() -r <regex>: returns 1 and displays an error message if <regex> is not a valid extended regular expression' { run refute_output -r '[.*' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 3 ] From 57f75ed03a21e7a2ceabc9ac42523b16d36a44b2 Mon Sep 17 00:00:00 2001 From: Zoltan Tombol <zoltan.tombol@gmail.com> Date: Tue, 10 Nov 2015 00:53:32 +0100 Subject: [PATCH 15/16] Remove load_std from man and fix some typos --- README.md | 3 +-- man/bats.7 | 25 ++++++------------------- man/bats.7.ronn | 17 +++++++---------- man/batslib.7 | 4 ++-- man/batslib.7.ronn | 2 +- 5 files changed, 17 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 7a75b373..34cf4557 100644 --- a/README.md +++ b/README.md @@ -205,8 +205,7 @@ case. in the test file. * `$BATS_TMPDIR` is the location to a directory that may be used to store temporary files. -* `$BATS_LIB` is the directory in which the standard libraries are -located. +* `$BATS_LIB` is the directory in which the Standard Library is located. ## Standard library diff --git a/man/bats.7 b/man/bats.7 index 43970c06..963d8ff8 100644 --- a/man/bats.7 +++ b/man/bats.7 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BATS" "7" "August 2015" "" "" +.TH "BATS" "7" "November 2015" "" "" . .SH "NAME" \fBbats\fR \- Bats test file format @@ -88,24 +88,8 @@ load test_helper .P will source the script \fBtest/test_helper\.bash\fR in your test file\. This can be useful for sharing functions to set up your environment or load fixtures\. . -.P -THE LOAD_STD COMMAND -. -.P -Common test helpers are compiled into libraries and included in the \fIStandard Library\fR for convenience\. To load a library use the \fBload_std\fR command\. For example, the command below loads the \fICore\fR library in your test file\. -. -.IP "" 4 -. -.nf - -load_std \'core\' -. -.fi -. -.IP "" 0 -. -.P -See the documentation of the Standard Library for the available libraries and the test helpers they provide\. +.SH "THE STANDARD LIBRARY" +The Standard Library is a collection of common test helpers intended to simplify testing\. It helps building test suits that provide relevant information when a test fails to speed up debugging\. See \fBbatslib\fR(7) for a detailed description\. . .SH "THE SKIP COMMAND" Tests can be skipped by using the \fBskip\fR command at the point in a test you wish to skip\. @@ -191,6 +175,9 @@ There are several global variables you can use to introspect on Bats tests: .IP "\(bu" 4 \fB$BATS_TMPDIR\fR is the location to a directory that may be used to store temporary files\. . +.IP "\(bu" 4 +\fB$BATS_LIB\fR is the directory in which the Standard Library is located\. +. .IP "" 0 . .SH "SEE ALSO" diff --git a/man/bats.7.ronn b/man/bats.7.ronn index aa2fcbda..511b2832 100644 --- a/man/bats.7.ronn +++ b/man/bats.7.ronn @@ -78,17 +78,13 @@ can be useful for sharing functions to set up your environment or load fixtures. -THE LOAD_STD COMMAND +THE STANDARD LIBRARY +-------------------- -Common test helpers are compiled into libraries and included in the -*Standard Library* for convenience. To load a library use the `load_std` -command. For example, the command below loads the *Core* library in your -test file. - - load_std 'core' - -See the documentation of the Standard Library for the available -libraries and the test helpers they provide. +The Standard Library is a collection of common test helpers intended to +simplify testing. It helps building test suits that provide relevant +information when a test fails to speed up debugging. See `batslib`(7) +for a detailed description. THE SKIP COMMAND @@ -161,6 +157,7 @@ case. in the test file. * `$BATS_TMPDIR` is the location to a directory that may be used to store temporary files. +* `$BATS_LIB` is the directory in which the Standard Library is located. SEE ALSO diff --git a/man/batslib.7 b/man/batslib.7 index 53123850..c2beb613 100644 --- a/man/batslib.7 +++ b/man/batslib.7 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BATSLIB" "7" "September 2015" "" "" +.TH "BATSLIB" "7" "November 2015" "" "" . .SH "NAME" \fBbatslib\fR \- Bats Standard Library of Test Helpers @@ -159,4 +159,4 @@ Due to a bug in Bats, empty lines are missing from \fB${lines[@]}\fR, causing li TODO(ztombol): Find a suitable licence\. . .SH "SEE ALSO" -\fBbash\fR(1), \fBbats\fR(1), \fBbats\fR(7), \fBregex(7)\fR +\fBbash\fR(1), \fBbats\fR(1), \fBbats\fR(7), \fBregex\fR(7) diff --git a/man/batslib.7.ronn b/man/batslib.7.ronn index c07ec061..78b7c401 100644 --- a/man/batslib.7.ronn +++ b/man/batslib.7.ronn @@ -185,4 +185,4 @@ TODO(ztombol): Find a suitable licence. ## SEE ALSO -`bash`(1), `bats`(1), `bats`(7), `regex(7)` +`bash`(1), `bats`(1), `bats`(7), `regex`(7) From eb0aad81d2a687124ffcf12f8af7fd22df5e6ee9 Mon Sep 17 00:00:00 2001 From: Zoltan Tombol <zoltan.tombol@gmail.com> Date: Wed, 2 Dec 2015 02:00:16 +0100 Subject: [PATCH 16/16] Split assert_output and refute_output, add long options, and change short options --- README.md | 424 ++++++++++----- lib/bats/batslib.bash | 551 ++++++++++++-------- man/batslib.7 | 58 ++- man/batslib.7.ronn | 110 ++-- test/60-lib-assertion-15-assert_output.bats | 305 +++-------- test/60-lib-assertion-16-refute_output.bats | 301 +++-------- test/60-lib-assertion-17-assert_line.bats | 328 ++++++++++++ test/60-lib-assertion-18-refute_line.bats | 336 ++++++++++++ 8 files changed, 1548 insertions(+), 865 deletions(-) create mode 100755 test/60-lib-assertion-17-assert_line.bats create mode 100755 test/60-lib-assertion-18-refute_line.bats diff --git a/README.md b/README.md index 34cf4557..d6b1db9a 100644 --- a/README.md +++ b/README.md @@ -422,15 +422,15 @@ format. #### `assert_output` -This functions helps to verify that a command or function produces the -correct output. It can match the entire output, a specific line, and -even look for a line in the output. Matching can be literal, partial or -regular expression. +This function helps to verify that a command or function produces the +correct output by checking that the specified expected output matches +the actual output. Matching can be literal (default), partial or regular +expression. This function is the logical complement of `refute_output`. -##### Matching the entire output +##### Literal matching -By default, the entire `$output` is matched, and the assertion fails if -it does not equal the expected output. +By default, literal matching is performed. The assertion fails if +`$output` does not equal the expected output. ```bash @test 'assert_output()' { @@ -439,7 +439,7 @@ it does not equal the expected output. } ``` -On failure, the expected and actual outputs are displayed. +On failure, the expected and actual output are displayed. ``` -- output differs -- @@ -451,194 +451,311 @@ actual : have If either value is longer than one line both are displayed in *multi-line* format. -##### Matching a specific line +##### Partial matching -When `-l <index>` is used, only the line specified by its `index` in -`${lines[@]}` is matched, and the assertion fails if it does not equal -the expected line. +Partial matching can be enabled with the `--partial` option (`-p` for +short). When used, the assertion fails if the expected *substring* is +not found in `$output`. -***Note:*** *Due to a [bug in Bats][bats-93], empty lines are discarded -from `${lines[@]}`, causing line indices to change and preventing -testing for empty lines.* +```bash +@test 'assert_output() partial matching' { + run echo 'ERROR: no such file or directory' + assert_output --partial 'SUCCESS' +} +``` + +On failure, the substring and the output are displayed. + +``` +-- output does not contain substring -- +substring : SUCCESS +output : ERROR: no such file or directory +-- +``` + +This option and regular expression matching (`--regexp` or `-e`) are +mutually exclusive. An error is displayed when used simultaneously. + +##### Regular expression matching + +Regular expression matching can be enabled with the `--regexp` option +(`-e` for short). When used, the assertion fails if the *extended +regular expression* does not match `$output`. + +*Note: The anchors `^` and `$` bind to the beginning and the end of the +entire output (not individual lines), respectively.* ```bash -@test 'assert_output() specific line' { - run echo $'have-0\nhave-1\nhave-2' - assert_output -l 1 'want-1' +@test 'assert_output() regular expression matching' { + run echo 'Foobar 0.1.0' + assert_output --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$' } ``` -On failure, the index, and the expected and actual line are displayed. +On failure, the regular expression and the output are displayed. ``` --- line differs -- -index : 1 -expected : want-1 -actual : have-1 +-- regular expression does not match output -- +regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$ +output : Foobar 0.1.0 -- ``` -##### Looking for line in output +An error is displayed if the specified extended regular expression is +invalid. + +This option and partial matching (`--partial` or `-p`) are mutually +exclusive. An error is displayed when used simultaneously. + +#### `refute_output` + +This function helps to verify that a command or function produces the +correct output by checking that the specified unexpected output does not +match the actual output. Matching can be literal (default), partial or +regular expression. This function is the logical complement of +`assert_output`. + -When `-l` is used without the `<index>` argument, fail if the expected -line is not found in `${lines[@]}`. +##### Literal matching -***Note:*** *Due to a [bug in Bats][bats-93], empty lines are discarded -from `${lines[@]}`, causing line indices to change and preventing -testing for empty lines.* +By default, literal matching is performed. The assertion fails if +`$output` equals the unexpected output. ```bash -@test 'assert_output() looking for line' { - run echo $'have-0\nhave-1\nhave-2' - assert_output -l 'want' +@test 'refute_output()' { + run echo 'want' + refute_output 'want' } ``` -On failure, the expected line and `$output` are displayed. +On failure, the output is displayed. ``` --- output does not contain line -- -line : want -output (3 lines): - have-0 - have-1 - have-2 +-- output equals, but it was expected to differ -- +output : want -- ``` -If `$output` is not longer than one line, it is displayed in -*two-column* format. +If output is longer than one line it is displayed in *multi-line* +format. ##### Partial matching -Partial matching, enabled using `-p`, provides more flexibility than the -default literal matching. The assertion fails if the expected output as -a substring can not be found in the output. +Partial matching can be enabled with the `--partial` option (`-p` for +short). When used, the assertion fails if the unexpected *substring* is +found in `$output`. ```bash -@test 'assert_output() partial matching' { +@test 'refute_output() partial matching' { run echo 'ERROR: no such file or directory' - assert_output -p 'SUCCESS' + refute_output --partial 'ERROR' } ``` -This option does not change the set if information displayed on failure. +On failure, the substring and the output are displayed. ``` --- output does not contain substring -- -substring : SUCCESS +-- output should not contain substring -- +substring : ERROR output : ERROR: no such file or directory -- ``` -This option is mutually exclusive with regular expression matching (`-r`). -When used simultaneously, an error is displayed. +This option and regular expression matching (`--regexp` or `-e`) are +mutually exclusive. An error is displayed when used simultaneously. ##### Regular expression matching -Regular expression matching, enabled using `-r`, provides the most -flexibility. The assertion fails if the expected output specified as an -extended regular expression does not match the output. +Regular expression matching can be enabled with the `--regexp` option +(`-e` for short). When used, the assertion fails if the *extended +regular expression* matches `$output`. + +*Note: The anchors `^` and `$` bind to the beginning and the end of the +entire output (not individual lines), respectively.* ```bash -@test 'assert_output() regular expression matching' { - run echo 'Foobar 0.1.0' - assert_output -r '^Foobar v[0-9]+\.[0-9]+\.[0-9]$' +@test 'refute_output() regular expression matching' { + run echo 'Foobar v0.1.0' + refute_output --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$' } ``` -This option does not change the set if information displayed on failure. +On failure, the regular expression and the output are displayed. ``` --- regular expression does not match output -- -regex : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$ -output : Foobar 0.1.0 +-- regular expression should not match output -- +regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$ +output : Foobar v0.1.0 -- ``` -When the specified extended regular expression is invalid, an error is -displayed. +An error is displayed if the specified extended regular expression is +invalid. -This option is mutually exclusive with partial matching (`-p`). When -used simultaneously, an error is displayed. +This option and partial matching (`--partial` or `-p`) are mutually +exclusive. An error is displayed when used simultaneously. -#### `refute_output` +#### `assert_line` + +Similarly to `assert_output`, this function helps to verify that a +command or function produces the correct output. It checks that the +expected line appears in the output (default) or in a specific line of +it. Matching can be literal (default), partial or regular expression. +This function is the logical complement of `refute_line`. + +***Warning:*** *Due to a [bug in Bats][bats-93], empty lines are +discarded from `${lines[@]}`, causing line indices to change and +preventing testing for empty lines.* -Similarly to `assert_output`, this function also helps to verify that a -command or function produces the correct output. `refute_output` is the -logical complement of `assert_output`. Instead of testing that the -output matches the expected output, `refute_output` tests that the -output does not match the unexpected output. It can match the entire -output, a specific line, and even look for a line in the output. -Matching can be literal, partial or regular expression. +[bats-93]: https://github.com/sstephenson/bats/pull/93 -##### Matching the entire output +##### Looking for a line in the output -By default, the entire `$output` is matched, and the assertion fails if -it equals the unexpected output. +By default, the entire output is searched for the expected line. The +assertion fails if the expected line is not found in `${lines[@]}`. ```bash -@test 'refute_output()' { - run echo 'want' - refute_output 'want' +@test 'assert_line() looking for line' { + run echo $'have-0\nhave-1\nhave-2' + assert_line 'want' } ``` -On failure, `$output` is displayed. +On failure, the expected line and the output are displayed. + +***Warning:*** *The output displayed does not contain empty lines. See +the Warning above for more.* ``` --- output equals, but it was expected to differ -- -output : want +-- output does not contain line -- +line : want +output (3 lines): + have-0 + have-1 + have-2 -- ``` -If `$output` is longer than one line, it is displayed in *multi-line* +If output is not longer than one line, it is displayed in *two-column* format. ##### Matching a specific line -When `-l <index>` is used, only the line specified by its `index` in -`${lines[@]}` is matched, and the assertion fails if it equals the -unexpected line. +When the `--index <idx>` option is used (`-n <idx>` for short) , the +expected line is matched only against the line identified by the given +index. The assertion fails if the expected line does not equal +`${lines[<idx>]}`. + +```bash +@test 'assert_line() specific line' { + run echo $'have-0\nhave-1\nhave-2' + assert_line --index 1 'want-1' +} +``` + +On failure, the index and the compared lines are displayed. + +``` +-- line differs -- +index : 1 +expected : want-1 +actual : have-1 +-- +``` + +##### Partial matching -***Note:*** *Due to a [bug in Bats][bats-93], empty lines are discarded -from `${lines[@]}`, causing line indices to change and preventing -testing for empty lines.* +Partial matching can be enabled with the `--partial` option (`-p` for +short). When used, a match fails if the expected *substring* is not +found in the matched line. ```bash -@test 'refute_output() specific line' { - run echo $'have-0\nwant-1\nhave-2' - refute_output -l 1 'want-1' +@test 'assert_line() partial matching' { + run echo $'have 1\nhave 2\nhave 3' + assert_line --partial 'want' } ``` -On failure, the index, and the unexpected line are displayed. +On failure, the same details are displayed as for literal matching, +except that the substring replaces the expected line. ``` --- line should differ -- -index : 1 -line : want-1 +-- no output line contains substring -- +substring : want +output (3 lines): + have 1 + have 2 + have 3 +-- +``` + +This option and regular expression matching (`--regexp` or `-e`) are +mutually exclusive. An error is displayed when used simultaneously. + +##### Regular expression matching + +Regular expression matching can be enabled with the `--regexp` option +(`-e` for short). When used, a match fails if the *extended regular +expression* does not match the line being tested. + +*Note: As expected, the anchors `^` and `$` bind to the beginning and +the end of the matched line, respectively.* + +```bash +@test 'assert_line() regular expression matching' { + run echo $'have-0\nhave-1\nhave-2' + assert_line --index 1 --regexp '^want-[0-9]$' +} +``` + +On failure, the same details are displayed as for literal matching, +except that the regular expression replaces the expected line. + +``` +-- regular expression does not match line -- +index : 1 +regexp : ^want-[0-9]$ +line : have-1 -- ``` -##### Looking for line in output +An error is displayed if the specified extended regular expression is +invalid. -When `-l` is used without the `<index>` argument, fail if the unexpected -line is found in `${lines[@]}`. +This option and partial matching (`--partial` or `-p`) are mutually +exclusive. An error is displayed when used simultaneously. -***Note:*** *Due to a [bug in Bats][bats-93], empty lines are discarded -from `${lines[@]}`, causing line indices to change and preventing -testing for empty lines.* +#### `refute_line` + +Similarly to `refute_output`, this function helps to verify that a +command or function produces the correct output. It checks that the +unexpected line does not appear in the output (default) or in a specific +line of it. Matching can be literal (default), partial or regular +expression. This function is the logical complement of `assert_line`. + +***Warning:*** *Due to a [bug in Bats][bats-93], empty lines are +discarded from `${lines[@]}`, causing line indices to change and +preventing testing for empty lines.* + +[bats-93]: https://github.com/sstephenson/bats/pull/93 + +##### Looking for a line in the output + +By default, the entire output is searched for the unexpected line. The +assertion fails if the unexpected line is found in `${lines[@]}`. ```bash -@test 'refute_output() looking for line' { +@test 'refute_line() looking for line' { run echo $'have-0\nwant\nhave-2' - refute_output -l 'want' + refute_line 'want' } ``` -On failure, the unexpected line, its index in `${lines[@]}` and -`$output` with the unexpected line highlighted are displayed. +On failure, the unexpected line, the index of its first match and the +output with the matching line highlighted are displayed. + +***Warning:*** *The output displayed does not contain empty lines. See +the Warning above for more.* ``` -- line should not be in output -- @@ -651,65 +768,96 @@ output (3 lines): -- ``` -If `$output` is not longer than one line, it is displayed in -*two-column* format without highlighting. +If output is not longer than one line, it is displayed in *two-column* +format. + +##### Matching a specific line + +When the `--index <idx>` option is used (`-n <idx>` for short) , the +unexpected line is matched only against the line identified by the given +index. The assertion fails if the unexpected line equals +`${lines[<idx>]}`. + +```bash +@test 'refute_line() specific line' { + run echo $'have-0\nwant-1\nhave-2' + refute_line --index 1 'want-1' +} +``` + +On failure, the index and the unexpected line are displayed. + +``` +-- line should differ -- +index : 1 +line : want-1 +-- +``` ##### Partial matching -Partial matching, enabled using `-p`, provides more flexibility than the -default literal matching. The assertion fails if the unexpected output -as a substring can be found in the output. +Partial matching can be enabled with the `--partial` option (`-p` for +short). When used, a match fails if the unexpected *substring* is found +in the matched line. ```bash -@test 'refute_output() partial matching' { - run echo 'ERROR: no such file or directory' - refute_output -p 'ERROR' +@test 'refute_line() partial matching' { + run echo $'have 1\nwant 2\nhave 3' + refute_line --partial 'want' } ``` -On failure, the substring is displayed in addition (if not already -displayed by other options). +On failure, in addition to the details of literal matching, the +substring is also displayed. When used with `--index <idx>` the +substring replaces the unexpected line. ``` --- output should not contain substring -- -substring : ERROR -output : ERROR: no such file or directory +-- no line should contain substring -- +substring : want +index : 1 +output (3 lines): + have 1 +> want 2 + have 3 -- ``` -This option is mutually exclusive with regular expression matching (`-r`). -When used simultaneously, an error is displayed. +This option and regular expression matching (`--regexp` or `-e`) are +mutually exclusive. An error is displayed when used simultaneously. ##### Regular expression matching -Regular expression matching, enabled using `-r`, provides the most -flexibility. The assertion fails if the unexpected output specified as -an extended regular expression matches the output. +Regular expression matching can be enabled with the `--regexp` option +(`-e` for short). When used, a match fails if the *extended regular +expression* matches the line being tested. + +*Note: As expected, the anchors `^` and `$` bind to the beginning and +the end of the matched line, respectively.* ```bash -@test 'refute_output() regular expression matching' { - run echo 'Foobar v0.1.0' - refute_output -r '^Foobar v[0-9]+\.[0-9]+\.[0-9]$' +@test 'refute_line() regular expression matching' { + run echo $'Foobar v0.1.0\nRelease date: 2015-11-29' + refute_line --index 0 --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$' } ``` -On failure, the regular expression is displayed in addition (if not -already displayed by other options). +On failure, in addition to the details of literal matching, the regular +expression is also displayed. When used with `--index <idx>` the regular +expression replaces the unexpected line. ``` --- regular expression should not match output -- -regex : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$ -output : Foobar v0.1.0 +-- regular expression should not match line -- +index : 0 +regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$ +line : Foobar v0.1.0 -- ``` -When the specified extended regular expression is invalid, an error is -displayed. +An error is displayed if the specified extended regular expression is +invalid. -This option is mutually exclusive with partial matching (`-p`). When -used simultaneously, an error is displayed. - -[bats-93]: https://github.com/sstephenson/bats/pull/93 +This option and partial matching (`--partial` or `-p`) are mutually +exclusive. An error is displayed when used simultaneously. ## Installing Bats from source diff --git a/lib/bats/batslib.bash b/lib/bats/batslib.bash index 521fd93f..63842ec7 100644 --- a/lib/bats/batslib.bash +++ b/lib/bats/batslib.bash @@ -154,37 +154,27 @@ assert_failure() { fi } -# Fail and display details if the expected does not match the actual -# output or a fragment of it. +# Fail and display details if `$output' does not match the expected +# output. # -# By default, the entire output is matched. The assertion fails if the +# By default, literal matching is performed. The assertion fails if the # expected output does not equal `$output'. Details include both values. # -# When `-l <index>' is used, only the <index>-th line is matched. The -# assertion fails if the expected line does not equal -# `${lines[<index>}'. Details include the compared lines and <index>. +# Option `--partial' enables partial matching. The assertion fails if +# the expected substring cannot be found in `$output'. # -# When `-l' is used without the <index> argument, the output is searched -# for the expected line. The expected line is matched against each line -# in `${lines[@]}'. If no match is found the assertion fails. Details -# include the expected line and `$output'. +# Option `--regexp' enables regular expression matching. The assertion +# fails if the extended regular expression does not match `$output'. An +# invalid regular expression causes an error to be displayed. # -# By default, literal matching is performed. Options `-p' and `-r' -# enable partial (i.e. substring) and extended regular expression -# matching, respectively. Specifying an invalid extended regular -# expression with `-r' displays an error. -# -# Options `-p' and `-r' are mutually exclusive. When used -# simultaneously, an error is displayed. +# It is an error to use partial and regular expression matching +# simultaneously. # # Globals: # output -# lines # Options: -# -l <index> - match against the <index>-th element of `${lines[@]}' -# -l - search `${lines[@]}' for the expected line -# -p - partial matching -# -r - extended regular expression matching +# -p, --partial - partial matching +# -e, --regexp - extended regular expression matching # Arguments: # $1 - expected output # Returns: @@ -194,41 +184,225 @@ assert_failure() { # STDERR - details, on failure # error message, on error assert_output() { - local -i is_match_line=0 - local -i is_match_contained=0 local -i is_mode_partial=0 - local -i is_mode_regex=0 + local -i is_mode_regexp=0 # Handle options. while (( $# > 0 )); do case "$1" in - -l) - if (( $# > 2 )) && [[ $2 =~ ^([0-9]|[1-9][0-9]+)$ ]]; then - is_match_line=1 - local -ri idx="$2" - shift - else - is_match_contained=1; - fi - shift - ;; - -p) is_mode_partial=1; shift ;; - -r) is_mode_regex=1; shift ;; + -p|--partial) is_mode_partial=1; shift ;; + -e|--regexp) is_mode_regexp=1; shift ;; --) break ;; *) break ;; esac done - if (( is_match_line )) && (( is_match_contained )); then - echo "\`-l' and \`-l <index>' are mutually exclusive" \ + if (( is_mode_partial )) && (( is_mode_regexp )); then + echo "\`--partial' and \`--regexp' are mutually exclusive" \ | batslib_decorate 'ERROR: assert_output' \ | fail return $? fi - if (( is_mode_partial )) && (( is_mode_regex )); then - echo "\`-p' and \`-r' are mutually exclusive" \ - | batslib_decorate 'ERROR: assert_output' \ + # Arguments. + local -r expected="$1" + + # Matching. + if (( is_mode_regexp )); then + if [[ '' =~ $expected ]] || (( $? == 2 )); then + echo "Invalid extended regular expression: \`$expected'" \ + | batslib_decorate 'ERROR: assert_output' \ + | fail + return $? + fi + if ! [[ $output =~ $expected ]]; then + batslib_print_kv_single_or_multi 6 \ + 'regexp' "$expected" \ + 'output' "$output" \ + | batslib_decorate 'regular expression does not match output' \ + | fail + fi + elif (( is_mode_partial )); then + if [[ $output != *"$expected"* ]]; then + batslib_print_kv_single_or_multi 9 \ + 'substring' "$expected" \ + 'output' "$output" \ + | batslib_decorate 'output does not contain substring' \ + | fail + fi + else + if [[ $output != "$expected" ]]; then + batslib_print_kv_single_or_multi 8 \ + 'expected' "$expected" \ + 'actual' "$output" \ + | batslib_decorate 'output differs' \ + | fail + fi + fi +} + +# Fail and display details if `$output' matches the unexpected output. +# +# By default, literal matching is performed. The assertion fails if the +# unexpected output equals `$output'. Details include `$output'. +# +# Option `--partial' enables partial matching. The assertion fails if +# the unexpected substring is found in `$output'. The unexpected +# substring is added to details. +# +# Option `--regexp' enables regular expression matching. The assertion +# fails if the extended regular expression does matches `$output'. The +# regular expression is added to details. An invalid regular expression +# causes an error to be displayed. +# +# It is an error to use partial and regular expression matching +# simultaneously. +# +# Globals: +# output +# Options: +# -p, --partial - partial matching +# -e, --regexp - extended regular expression matching +# Arguments: +# $1 - unexpected output +# Returns: +# 0 - unexpected matches the actual output +# 1 - otherwise +# Outputs: +# STDERR - details, on failure +# error message, on error +refute_output() { + local -i is_mode_partial=0 + local -i is_mode_regexp=0 + + # Handle options. + while (( $# > 0 )); do + case "$1" in + -p|--partial) is_mode_partial=1; shift ;; + -e|--regexp) is_mode_regexp=1; shift ;; + --) break ;; + *) break ;; + esac + done + + if (( is_mode_partial )) && (( is_mode_regexp )); then + echo "\`--partial' and \`--regexp' are mutually exclusive" \ + | batslib_decorate 'ERROR: refute_output' \ + | fail + return $? + fi + + # Arguments. + local -r unexpected="$1" + + if (( is_mode_regexp == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then + echo "Invalid extended regular expression: \`$unexpected'" \ + | batslib_decorate 'ERROR: refute_output' \ + | fail + return $? + fi + + # Matching. + if (( is_mode_regexp )); then + if [[ $output =~ $unexpected ]] || (( $? == 0 )); then + batslib_print_kv_single_or_multi 6 \ + 'regexp' "$unexpected" \ + 'output' "$output" \ + | batslib_decorate 'regular expression should not match output' \ + | fail + fi + elif (( is_mode_partial )); then + if [[ $output == *"$unexpected"* ]]; then + batslib_print_kv_single_or_multi 9 \ + 'substring' "$unexpected" \ + 'output' "$output" \ + | batslib_decorate 'output should not contain substring' \ + | fail + fi + else + if [[ $output == "$unexpected" ]]; then + batslib_print_kv_single_or_multi 6 \ + 'output' "$output" \ + | batslib_decorate 'output equals, but it was expected to differ' \ + | fail + fi + fi +} + +# Fail and display details if the expected line is not found in the +# output (default) or in a specific line of it. +# +# By default, the entire output is searched for the expected line. The +# expected line is matched against every element of `${lines[@]}'. If no +# match is found, the assertion fails. Details include the expected line +# and `${lines[@]}'. +# +# When `--index <idx>' is specified, only the <idx>-th line is matched. +# If the expected line does not match `${lines[<idx>]}', the assertion +# fails. Details include <idx> and the compared lines. +# +# By default, literal matching is performed. A literal match fails if +# the expected string does not equal the matched string. +# +# Option `--partial' enables partial matching. A partial match fails if +# the expected substring is not found in the target string. +# +# Option `--regexp' enables regular expression matching. A regular +# expression match fails if the extended regular expression does not +# match the target string. An invalid regular expression causes an error +# to be displayed. +# +# It is an error to use partial and regular expression matching +# simultaneously. +# +# Mandatory arguments to long options are mandatory for short options +# too. +# +# Globals: +# output +# lines +# Options: +# -n, --index <idx> - match the <idx>-th line +# -p, --partial - partial matching +# -e, --regexp - extended regular expression matching +# Arguments: +# $1 - expected line +# Returns: +# 0 - match found +# 1 - otherwise +# Outputs: +# STDERR - details, on failure +# error message, on error +# FIXME(ztombol): Display `${lines[@]}' instead of `$output'! +assert_line() { + local -i is_match_line=0 + local -i is_mode_partial=0 + local -i is_mode_regexp=0 + + # Handle options. + while (( $# > 0 )); do + case "$1" in + -n|--index) + if (( $# < 2 )) || ! [[ $2 =~ ^([0-9]|[1-9][0-9]+)$ ]]; then + echo "\`--index' requires an integer argument: \`$2'" \ + | batslib_decorate 'ERROR: assert_line' \ + | fail + return $? + fi + is_match_line=1 + local -ri idx="$2" + shift 2 + ;; + -p|--partial) is_mode_partial=1; shift ;; + -e|--regexp) is_mode_regexp=1; shift ;; + --) break ;; + *) break ;; + esac + done + + if (( is_mode_partial )) && (( is_mode_regexp )); then + echo "\`--partial' and \`--regexp' are mutually exclusive" \ + | batslib_decorate 'ERROR: assert_line' \ | fail return $? fi @@ -236,23 +410,53 @@ assert_output() { # Arguments. local -r expected="$1" - if (( is_mode_regex == 1 )) && [[ '' =~ $expected ]] || (( $? == 2 )); then + if (( is_mode_regexp == 1 )) && [[ '' =~ $expected ]] || (( $? == 2 )); then echo "Invalid extended regular expression: \`$expected'" \ - | batslib_decorate 'ERROR: assert_output' \ + | batslib_decorate 'ERROR: assert_line' \ | fail return $? fi # Matching. - if (( is_match_contained )); then - # Line contained in output. - if (( is_mode_regex )); then + if (( is_match_line )); then + # Specific line. + if (( is_mode_regexp )); then + if ! [[ ${lines[$idx]} =~ $expected ]]; then + batslib_print_kv_single 6 \ + 'index' "$idx" \ + 'regexp' "$expected" \ + 'line' "${lines[$idx]}" \ + | batslib_decorate 'regular expression does not match line' \ + | fail + fi + elif (( is_mode_partial )); then + if [[ ${lines[$idx]} != *"$expected"* ]]; then + batslib_print_kv_single 9 \ + 'index' "$idx" \ + 'substring' "$expected" \ + 'line' "${lines[$idx]}" \ + | batslib_decorate 'line does not contain substring' \ + | fail + fi + else + if [[ ${lines[$idx]} != "$expected" ]]; then + batslib_print_kv_single 8 \ + 'index' "$idx" \ + 'expected' "$expected" \ + 'actual' "${lines[$idx]}" \ + | batslib_decorate 'line differs' \ + | fail + fi + fi + else + # Contained in output. + if (( is_mode_regexp )); then local -i idx for (( idx = 0; idx < ${#lines[@]}; ++idx )); do [[ ${lines[$idx]} =~ $expected ]] && return 0 done { local -ar single=( - 'regex' "$expected" + 'regexp' "$expected" ) local -ar may_be_multi=( 'output' "$output" @@ -298,146 +502,87 @@ assert_output() { } | batslib_decorate 'output does not contain line' \ | fail fi - elif (( is_match_line )); then - # Specific line. - if (( is_mode_regex )); then - if ! [[ ${lines[$idx]} =~ $expected ]]; then - batslib_print_kv_single 5 \ - 'index' "$idx" \ - 'regex' "$expected" \ - 'line' "${lines[$idx]}" \ - | batslib_decorate 'regular expression does not match line' \ - | fail - fi - elif (( is_mode_partial )); then - if [[ ${lines[$idx]} != *"$expected"* ]]; then - batslib_print_kv_single 9 \ - 'index' "$idx" \ - 'substring' "$expected" \ - 'line' "${lines[$idx]}" \ - | batslib_decorate 'line does not contain substring' \ - | fail - fi - else - if [[ ${lines[$idx]} != "$expected" ]]; then - batslib_print_kv_single 8 \ - 'index' "$idx" \ - 'expected' "$expected" \ - 'actual' "${lines[$idx]}" \ - | batslib_decorate 'line differs' \ - | fail - fi - fi - else - # Entire output. - if (( is_mode_regex )); then - if ! [[ $output =~ $expected ]]; then - batslib_print_kv_single_or_multi 6 \ - 'regex' "$expected" \ - 'output' "$output" \ - | batslib_decorate 'regular expression does not match output' \ - | fail - fi - elif (( is_mode_partial )); then - if [[ $output != *"$expected"* ]]; then - batslib_print_kv_single_or_multi 9 \ - 'substring' "$expected" \ - 'output' "$output" \ - | batslib_decorate 'output does not contain substring' \ - | fail - fi - else - if [[ $output != "$expected" ]]; then - batslib_print_kv_single_or_multi 8 \ - 'expected' "$expected" \ - 'actual' "$output" \ - | batslib_decorate 'output differs' \ - | fail - fi - fi fi } -# Fail and display details if the unexpected matches the actual output -# or a fragment of it. +# Fail and display details if the unexpected line is found in the output +# (default) or in a specific line of it. # -# By default, the entire output is matched. The assertion fails if the -# unexpected output equals `$output'. Details include `$output'. +# By default, the entire output is searched for the unexpected line. The +# unexpected line is matched against every element of `${lines[@]}'. If +# a match is found, the assertion fails. Details include the unexpected +# line, the index of the first match and `${lines[@]}' with the matching +# line highlighted if `${lines[@]}' is longer than one line. +# +# When `--index <idx>' is specified, only the <idx>-th line is matched. +# If the unexpected line matches `${lines[<idx>]}', the assertion fails. +# Details include <idx> and the unexpected line. +# +# By default, literal matching is performed. A literal match fails if +# the unexpected string does not equal the matched string. # -# When `-l <index>' is used, only the <index>-th line is matched. The -# assertion fails if the unexpected line equals `${lines[<index>}'. -# Details include the compared line and <index>. +# Option `--partial' enables partial matching. A partial match fails if +# the unexpected substring is found in the target string. When used with +# `--index <idx>', the unexpected substring is also displayed on +# failure. # -# When `-l' is used without the <index> argument, the output is searched -# for the unexpected line. The unexpected line is matched against each -# line in `${lines[<index>]}'. If a match is found the assertion fails. -# Details include the unexpected line, the index where it was found and -# `$output' (with the unexpected line highlighted in it if `$output` is -# longer than one line). +# Option `--regexp' enables regular expression matching. A regular +# expression match fails if the extended regular expression matches the +# target string. When used with `--index <idx>', the regular expression +# is also displayed on failure. An invalid regular expression causes an +# error to be displayed. # -# By default, literal matching is performed. Options `-p' and `-r' -# enable partial (i.e. substring) and extended regular expression -# matching, respectively. On failure, the substring or the regular -# expression is added to the details (if not already displayed). -# Specifying an invalid extended regular expression with `-r' displays -# an error. +# It is an error to use partial and regular expression matching +# simultaneously. # -# Options `-p' and `-r' are mutually exclusive. When used -# simultaneously, an error is displayed. +# Mandatory arguments to long options are mandatory for short options +# too. # # Globals: # output # lines # Options: -# -l <index> - match against the <index>-th element of `${lines[@]}' -# -l - search `${lines[@]}' for the unexpected line -# -p - partial matching -# -r - extended regular expression matching +# -n, --index <idx> - match the <idx>-th line +# -p, --partial - partial matching +# -e, --regexp - extended regular expression matching # Arguments: -# $1 - unexpected output +# $1 - unexpected line # Returns: -# 0 - unexpected matches the actual output +# 0 - match not found # 1 - otherwise # Outputs: # STDERR - details, on failure # error message, on error -refute_output() { +# FIXME(ztombol): Display `${lines[@]}' instead of `$output'! +refute_line() { local -i is_match_line=0 - local -i is_match_contained=0 local -i is_mode_partial=0 - local -i is_mode_regex=0 + local -i is_mode_regexp=0 # Handle options. while (( $# > 0 )); do case "$1" in - -l) - if (( $# > 2 )) && [[ $2 =~ ^([0-9]|[1-9][0-9]+)$ ]]; then - is_match_line=1 - local -ri idx="$2" - shift - else - is_match_contained=1; + -n|--index) + if (( $# < 2 )) || ! [[ $2 =~ ^([0-9]|[1-9][0-9]+)$ ]]; then + echo "\`--index' requires an integer argument: \`$2'" \ + | batslib_decorate 'ERROR: refute_line' \ + | fail + return $? fi - shift + is_match_line=1 + local -ri idx="$2" + shift 2 ;; - -L) is_match_contained=1; shift ;; - -p) is_mode_partial=1; shift ;; - -r) is_mode_regex=1; shift ;; + -p|--partial) is_mode_partial=1; shift ;; + -e|--regexp) is_mode_regexp=1; shift ;; --) break ;; *) break ;; esac done - if (( is_match_line )) && (( is_match_contained )); then - echo "\`-l' and \`-l <index>' are mutually exclusive" \ - | batslib_decorate 'ERROR: refute_output' \ - | fail - return $? - fi - - if (( is_mode_partial )) && (( is_mode_regex )); then - echo "\`-p' and \`-r' are mutually exclusive" \ - | batslib_decorate 'ERROR: refute_output' \ + if (( is_mode_partial )) && (( is_mode_regexp )); then + echo "\`--partial' and \`--regexp' are mutually exclusive" \ + | batslib_decorate 'ERROR: refute_line' \ | fail return $? fi @@ -445,22 +590,51 @@ refute_output() { # Arguments. local -r unexpected="$1" - if (( is_mode_regex == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then + if (( is_mode_regexp == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then echo "Invalid extended regular expression: \`$unexpected'" \ - | batslib_decorate 'ERROR: refute_output' \ + | batslib_decorate 'ERROR: refute_line' \ | fail return $? fi # Matching. - if (( is_match_contained )); then + if (( is_match_line )); then + # Specific line. + if (( is_mode_regexp )); then + if [[ ${lines[$idx]} =~ $unexpected ]] || (( $? == 0 )); then + batslib_print_kv_single 6 \ + 'index' "$idx" \ + 'regexp' "$unexpected" \ + 'line' "${lines[$idx]}" \ + | batslib_decorate 'regular expression should not match line' \ + | fail + fi + elif (( is_mode_partial )); then + if [[ ${lines[$idx]} == *"$unexpected"* ]]; then + batslib_print_kv_single 9 \ + 'index' "$idx" \ + 'substring' "$unexpected" \ + 'line' "${lines[$idx]}" \ + | batslib_decorate 'line should not contain substring' \ + | fail + fi + else + if [[ ${lines[$idx]} == "$unexpected" ]]; then + batslib_print_kv_single 5 \ + 'index' "$idx" \ + 'line' "${lines[$idx]}" \ + | batslib_decorate 'line should differ' \ + | fail + fi + fi + else # Line contained in output. - if (( is_mode_regex )); then + if (( is_mode_regexp )); then local -i idx for (( idx = 0; idx < ${#lines[@]}; ++idx )); do if [[ ${lines[$idx]} =~ $unexpected ]]; then { local -ar single=( - 'regex' "$unexpected" + 'regexp' "$unexpected" 'index' "$idx" ) local -a may_be_multi=( @@ -537,60 +711,5 @@ refute_output() { fi done fi - elif (( is_match_line )); then - # Specific line. - if (( is_mode_regex )); then - if [[ ${lines[$idx]} =~ $unexpected ]] || (( $? == 0 )); then - batslib_print_kv_single 5 \ - 'index' "$idx" \ - 'regex' "$unexpected" \ - 'line' "${lines[$idx]}" \ - | batslib_decorate 'regular expression should not match line' \ - | fail - fi - elif (( is_mode_partial )); then - if [[ ${lines[$idx]} == *"$unexpected"* ]]; then - batslib_print_kv_single 9 \ - 'index' "$idx" \ - 'substring' "$unexpected" \ - 'line' "${lines[$idx]}" \ - | batslib_decorate 'line should not contain substring' \ - | fail - fi - else - if [[ ${lines[$idx]} == "$unexpected" ]]; then - batslib_print_kv_single 5 \ - 'index' "$idx" \ - 'line' "${lines[$idx]}" \ - | batslib_decorate 'line should differ' \ - | fail - fi - fi - else - # Entire output. - if (( is_mode_regex )); then - if [[ $output =~ $unexpected ]] || (( $? == 0 )); then - batslib_print_kv_single_or_multi 6 \ - 'regex' "$unexpected" \ - 'output' "$output" \ - | batslib_decorate 'regular expression should not match output' \ - | fail - fi - elif (( is_mode_partial )); then - if [[ $output == *"$unexpected"* ]]; then - batslib_print_kv_single_or_multi 9 \ - 'substring' "$unexpected" \ - 'output' "$output" \ - | batslib_decorate 'output should not contain substring' \ - | fail - fi - else - if [[ $output == "$unexpected" ]]; then - batslib_print_kv_single_or_multi 6 \ - 'output' "$output" \ - | batslib_decorate 'output equals, but it was expected to differ' \ - | fail - fi - fi fi } diff --git a/man/batslib.7 b/man/batslib.7 index c2beb613..ddcc7090 100644 --- a/man/batslib.7 +++ b/man/batslib.7 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BATSLIB" "7" "November 2015" "" "" +.TH "BATSLIB" "7" "December 2015" "" "" . .SH "NAME" \fBbatslib\fR \- Bats Standard Library of Test Helpers @@ -98,51 +98,77 @@ Fail if \fB$status\fR is \fB0\fR\. On failure, \fB$output\fR is displayed\. If \ When \fISTATUS\fR is specified, fail if it does not equal \fB$status\fR\. On failure, \fISTATUS\fR, \fB$status\fR and \fB$output\fR are displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulti\-line\fR format\. . .TP -\fBassert_output\fR [\-l [\fIINDEX\fR]] [\-p|\-r] \fIEXPECTED\fR +\fBassert_output\fR [\-p|\-\-partial|\-r|\-\-regexp] \fIEXPECTED\fR Fail if \fIEXPECTED\fR does not equal \fB$output\fR\. On failure, both values are displayed\. If either one is longer than one line, both are displayed in \fImulti\-line\fR format\. . .IP -When \fB\-l <INDEX>\fR is used, fail if \fIEXPECTED\fR does not equal the \fIINDEX\fR\-th element of \fB${lines[@]}\fR\. On failure, \fIINDEX\fR, \fIEXPECTED\fR and the actual line are displayed\. +The default, literal matching can be changed to partial and regular expression matching\. . .IP -When \fB\-l\fR is used without \fB<INDEX>\fR, fail if \fIEXPECTED\fR is not found in \fB${lines[@]}\fR\. On failure, \fIEXPECTED\fR and \fB$output\fR are displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulti\-line\fR format\. +The \fB\-\-partial\fR (\fB\-p\fR) option enables partial matching\. The assertion fails if \fIEXPECTED\fR as a substring is not found in the output\. . .IP -The default, literal matching can be changed to substring and regular expression matching\. +The \fB\-\-regexp\fR (\fB\-r\fR) option enables regular expression matching\. The assertion fails if \fIEXPECTED\fR as an extended regular expression does not match the output\. If \fIEXPECTED\fR is not a valid extended regular expression, an error is displayed\. . .IP -Using \fB\-p\fR enables partial matching, and the assertion fails if \fIEXPECTED\fR as a substring is not found in the output\. +Partial and regular expression matching is mutually exclusive\. Using them simultaneously results in error\. +. +.TP +\fBrefute_output\fR [\-p|\-\-partial|\-r|\-\-regexp] \fIUNEXPECTED\fR +Fail if \fIUNEXPECTED\fR equals \fB$output\fR\. On failure, \fB$output\fR is displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulti\-line\fR format\. . .IP -Using \fB\-r\fR enables regular expression matching, and the assertion fails if \fIEXPECTED\fR as an extended regular expression does not match the output\. If \fIEXPECTED\fR is not a valid extended regular expression, an error is displayed\. +The default, literal matching can be changed to partial and regular expression matching\. . .IP -Options \fB\-p\fR and \fB\-r\fR are mutually exclusive\. Using them simultaneously results in error\. +The \fB\-\-partial\fR (\fB\-p\fR) option enables partial matching\. The assertion fails if \fIUNEXPECTED\fR as a substring is found in the output\. On failure, \fIUNEXPECTED\fR is displayed in addition\. . .IP -\fBNOTE:\fR Due to a bug in Bats, empty lines are discarded from \fB${lines[@]}\fR, causing line indices to change and preventing testing for empty lines\. See \fIBUGS\fR for more\. +The \fB\-\-regexp\fR (\fB\-r\fR) option enables regular expression matching\. The assertion fails if \fIUNEXPECTED\fR as an extended regular expression matches the output\. On failure, \fIUNEXPECTED\fR is displayed in addition\. If \fIUNEXPECTED\fR is not a valid extended regular expression, an error is displayed\. +. +.IP +Partial and regular expression matching is mutually exclusive\. Using them simultaneously results in error\. . .TP -\fBrefute_output\fR [\-l [\fIINDEX\fR]] [\-p|\-r] \fIUNEXPECTED\fR -Fail if \fIUNEXPECTED\fR equals \fB$output\fR\. On failure, \fB$output\fR is displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulti\-line\fR format\. +\fBassert_line\fR [\-n \fIIDX\fR|\-\-index \fIIDX\fR] [\-p|\-\-partial|\-r|\-\-regexp] \fIEXPECTED\fR +Fail if \fIEXPECTED\fR does not equal any element of \fB${lines[@]}\fR\. On failure, \fIEXPECTED\fR and \fB${lines[@]}\fR are displayed\. If \fB${lines[@]}\fR is longer than one line, it is displayed in \fImulti\-line\fR format\. +. +.IP +When the \fB\-\-index <IDX>\fR (\fB\-n <IDX>\fR) option is used, only the \fIIDX\fR\-th line is compared\. The assertion fails if \fIEXPECTED\fR does not equal \fB${lines[<IDX>]}\fR\. On failure, \fIIDX\fR and the compared lines are displayed\. +. +.IP +The default, literal matching can be changed to substring and regular expression matching\. +. +.IP +The \fB\-\-partial\fR (\fB\-p\fR) option enables partial matching\. A match fails if \fIEXPECTED\fR as a substring is not found in the matched string\. +. +.IP +The \fB\-\-regexp\fR (\fB\-r\fR) option enables regular expression matching\. A match fails if \fIEXPECTED\fR as an extended regular expression does not match the matched line\. If \fIEXPECTED\fR is not a valid extended regular expression, an error is displayed\. . .IP -When \fB\-l <INDEX>\fR is used, fail if \fIUNEXPECTED\fR equals the \fIINDEX\fR\-th element of \fB${lines[@]}\fR\. On failure, \fIINDEX\fR and \fB${lines[<INDEX>]}\fR are displayed\. +Partial and regular expression matching is mutually exclusive\. Using them simultaneously results in error\. +. +.IP +\fBNOTE:\fR Due to a bug in Bats, empty lines are discarded from \fB${lines[@]}\fR, causing line indices to change and preventing testing for empty lines\. See \fIBUGS\fR for more\. +. +.TP +\fBrefute_line\fR [\-n \fIIDX\fR|\-\-index \fIIDX\fR] [\-p|\-\-partial|\-r|\-\-regexp] \fIUNEXPECTED\fR +Fail if \fIUNEXPECTED\fR does equals an element of \fB${lines[@]}\fR\. On failure, \fIUNEXPECTED\fR, the index of the first match and \fB${lines[@]}\fR are displayed\. If \fB${lines[@]}\fR is longer than one line, it is displayed in \fImulti\-line\fR format and the matching line is highlighted\. . .IP -When \fB\-l\fR is used without \fB<INDEX>\fR, fail if \fIUNEXPECTED\fR is found in \fB${lines[@]}\fR\. On failure, \fIUNEXPECTED\fR, its index in \fB${lines[@]}\fR and \fB$output\fR are displayed\. If \fB$output\fR is longer than one line, it is displayed in \fImulti\-line\fR format with the unexpected line highlighted\. +When the \fB\-\-index <IDX>\fR (\fB\-n <IDX>\fR) option is used, only the \fIIDX\fR\-th line is compared\. The assertion fails if \fIUNEXPECTED\fR equals \fB${lines[<IDX>]}\fR\. On failure, \fIIDX\fR and \fIUNEXPECTED\fR are displayed\. . .IP The default, literal matching can be changed to substring and regular expression matching\. . .IP -Using \fB\-p\fR enables partial matching, and the assertion fails if \fIUNEXPECTED\fR as a substring is found in the output\. On failure, \fIUNEXPECTED\fR is displayed in addition (if not displayed already)\. +The \fB\-\-partial\fR (\fB\-p\fR) option enables partial matching\. A match fails if \fIUNEXPECTED\fR as a substring is found in the matched string\. When used with `\-\-index \fIidx\fR\', the unexpected substring is also displayed on failure\. . .IP -Using \fB\-r\fR enables regular expression matching, and the assertion fails if \fIUNEXPECTED\fR as an extended regular expression matches the output\. On failure, \fIUNEXPECTED\fR is displayed in addition (if not displayed already)\. If \fIUNEXPECTED\fR is not a valid extended regular expression, an error is displayed\. +The \fB\-\-regexp\fR (\fB\-r\fR) option enables regular expression matching\. A match fails if \fIUNEXPECTED\fR as an extended regular expression matches the matched line\. When used with \fB\-\-index <idx>\fR, the regular expression is also displayed\. If \fIUNEXPECTED\fR is not a valid extended regular expression, an error is displayed\. . .IP -Options \fB\-p\fR and \fB\-r\fR are mutually exclusive\. Using them simultaneously results in error\. +Partial and regular expression matching is mutually exclusive\. Using them simultaneously results in error\. . .IP \fBNOTE:\fR Due to a bug in Bats, empty lines are discarded from \fB${lines[@]}\fR, causing line indices to change and preventing testing for empty lines\. See \fIBUGS\fR for more\. diff --git a/man/batslib.7.ronn b/man/batslib.7.ronn index 78b7c401..95470de2 100644 --- a/man/batslib.7.ronn +++ b/man/batslib.7.ronn @@ -94,68 +94,100 @@ recent invocation of `run`. failure, <STATUS>, `$status` and `$output` are displayed. If `$output` is longer than one line, it is displayed in _multi-line_ format. -* `assert_output` [-l [<INDEX>]] [-p|-r] <EXPECTED>: +* `assert_output` [-p|--partial|-r|--regexp] <EXPECTED>: Fail if <EXPECTED> does not equal `$output`. On failure, both values are displayed. If either one is longer than one line, both are displayed in _multi-line_ format. - When `-l <INDEX>` is used, fail if <EXPECTED> does not equal the - <INDEX>-th element of `${lines[@]}`. On failure, <INDEX>, <EXPECTED> - and the actual line are displayed. + The default, literal matching can be changed to partial and regular + expression matching. - When `-l` is used without `<INDEX>`, fail if <EXPECTED> is not found - in `${lines[@]}`. On failure, <EXPECTED> and `$output` are displayed. - If `$output` is longer than one line, it is displayed in _multi-line_ - format. + The `--partial` (`-p`) option enables partial matching. The assertion + fails if <EXPECTED> as a substring is not found in the output. + + The `--regexp` (`-r`) option enables regular expression matching. The + assertion fails if <EXPECTED> as an extended regular expression does + not match the output. If <EXPECTED> is not a valid extended regular + expression, an error is displayed. + + Partial and regular expression matching is mutually exclusive. Using + them simultaneously results in error. + +* `refute_output` [-p|--partial|-r|--regexp] <UNEXPECTED>: + Fail if <UNEXPECTED> equals `$output`. On failure, `$output` is + displayed. If `$output` is longer than one line, it is displayed in + _multi-line_ format. + + The default, literal matching can be changed to partial and regular + expression matching. + + The `--partial` (`-p`) option enables partial matching. The assertion + fails if <UNEXPECTED> as a substring is found in the output. On + failure, <UNEXPECTED> is displayed in addition. + + The `--regexp` (`-r`) option enables regular expression matching. The + assertion fails if <UNEXPECTED> as an extended regular expression + matches the output. On failure, <UNEXPECTED> is displayed in addition. + If <UNEXPECTED> is not a valid extended regular expression, an error + is displayed. + + Partial and regular expression matching is mutually exclusive. Using + them simultaneously results in error. + +* `assert_line` [-n <IDX>|--index <IDX>] [-p|--partial|-r|--regexp] <EXPECTED>: + Fail if <EXPECTED> does not equal any element of `${lines[@]}`. On + failure, <EXPECTED> and `${lines[@]}` are displayed. If `${lines[@]}` + is longer than one line, it is displayed in _multi-line_ format. + + When the `--index <IDX>` (`-n <IDX>`) option is used, only the + <IDX>-th line is compared. The assertion fails if <EXPECTED> does not + equal `${lines[<IDX>]}`. On failure, <IDX> and the compared lines are + displayed. The default, literal matching can be changed to substring and regular expression matching. - Using `-p` enables partial matching, and the assertion fails if - <EXPECTED> as a substring is not found in the output. + The `--partial` (`-p`) option enables partial matching. A match fails + if <EXPECTED> as a substring is not found in the matched string. - Using `-r` enables regular expression matching, and the assertion - fails if <EXPECTED> as an extended regular expression does not match - the output. If <EXPECTED> is not a valid extended regular expression, - an error is displayed. + The `--regexp` (`-r`) option enables regular expression matching. A + match fails if <EXPECTED> as an extended regular expression does not + match the matched line. If <EXPECTED> is not a valid extended regular + expression, an error is displayed. - Options `-p` and `-r` are mutually exclusive. Using them - simultaneously results in error. + Partial and regular expression matching is mutually exclusive. Using + them simultaneously results in error. **NOTE:** Due to a bug in Bats, empty lines are discarded from `${lines[@]}`, causing line indices to change and preventing testing for empty lines. See [BUGS][] for more. -* `refute_output` [-l [<INDEX>]] [-p|-r] <UNEXPECTED>: - Fail if <UNEXPECTED> equals `$output`. On failure, `$output` is - displayed. If `$output` is longer than one line, it is displayed in - _multi-line_ format. +* `refute_line` [-n <IDX>|--index <IDX>] [-p|--partial|-r|--regexp] <UNEXPECTED>: + Fail if <UNEXPECTED> does equals an element of `${lines[@]}`. On + failure, <UNEXPECTED>, the index of the first match and `${lines[@]}` + are displayed. If `${lines[@]}` is longer than one line, it is + displayed in _multi-line_ format and the matching line is highlighted. - When `-l <INDEX>` is used, fail if <UNEXPECTED> equals the <INDEX>-th - element of `${lines[@]}`. On failure, <INDEX> and `${lines[<INDEX>]}` - are displayed. - - When `-l` is used without `<INDEX>`, fail if <UNEXPECTED> is found in - `${lines[@]}`. On failure, <UNEXPECTED>, its index in `${lines[@]}` - and `$output` are displayed. If `$output` is longer than one line, it - is displayed in _multi-line_ format with the unexpected line - highlighted. + When the `--index <IDX>` (`-n <IDX>`) option is used, only the + <IDX>-th line is compared. The assertion fails if <UNEXPECTED> equals + `${lines[<IDX>]}`. On failure, <IDX> and <UNEXPECTED> are displayed. The default, literal matching can be changed to substring and regular expression matching. - Using `-p` enables partial matching, and the assertion fails if - <UNEXPECTED> as a substring is found in the output. On failure, - <UNEXPECTED> is displayed in addition (if not displayed already). + The `--partial` (`-p`) option enables partial matching. A match fails + if <UNEXPECTED> as a substring is found in the matched string. When + used with `--index <idx>', the unexpected substring is also displayed + on failure. - Using `-r` enables regular expression matching, and the assertion - fails if <UNEXPECTED> as an extended regular expression matches the - output. On failure, <UNEXPECTED> is displayed in addition (if not - displayed already). If <UNEXPECTED> is not a valid extended regular - expression, an error is displayed. + The `--regexp` (`-r`) option enables regular expression matching. A + match fails if <UNEXPECTED> as an extended regular expression matches + the matched line. When used with `--index <idx>`, the regular + expression is also displayed. If <UNEXPECTED> is not a valid extended + regular expression, an error is displayed. - Options `-p` and `-r` are mutually exclusive. Using them - simultaneously results in error. + Partial and regular expression matching is mutually exclusive. Using + them simultaneously results in error. **NOTE:** Due to a bug in Bats, empty lines are discarded from `${lines[@]}`, causing line indices to change and preventing testing diff --git a/test/60-lib-assertion-15-assert_output.bats b/test/60-lib-assertion-15-assert_output.bats index eed2e43d..3efe49e2 100755 --- a/test/60-lib-assertion-15-assert_output.bats +++ b/test/60-lib-assertion-15-assert_output.bats @@ -4,11 +4,10 @@ load test_helper # -# Matching entire output. +# Literal matching # -# Literal matching. - +# Correctness @test "assert_output() <expected>: returns 0 if <expected> equals \`\$output'" { run echo 'a' run assert_output 'a' @@ -27,6 +26,7 @@ load test_helper [ "${lines[3]}" == '--' ] } +# Output formatting @test "assert_output() <expected>: displays details in multi-line format if \`\$output' is longer than one line" { run echo $'b 0\nb 1' run assert_output 'a' @@ -55,24 +55,45 @@ load test_helper [ "${lines[6]}" == '--' ] } +# Options @test 'assert_output() <expected>: performs literal matching by default' { run echo 'a' run assert_output '*' [ "$status" -eq 1 ] } -# Partial matching: `-p <partial>'. -@test "assert_output() -p <partial>: returns 0 if <partial> is a substring in \`\$output'" { +# +# Partial matching: `-p' and `--partial' +# + +# Options +test_p_partial () { + run echo 'abc' + run assert_output "$1" 'b' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'assert_output() -p <partial>: enables partial matching' { + test_p_partial -p +} + +@test 'assert_output() --partial <partial>: enables partial matching' { + test_p_partial --partial +} + +# Correctness +@test "assert_output() --partial <partial>: returns 0 if <partial> is a substring in \`\$output'" { run echo $'a\nb\nc' - run assert_output -p 'b' + run assert_output --partial 'b' [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } -@test "assert_output() -p <partial>: returns 1 and displays details if <partial> is not a substring in \`\$output'" { +@test "assert_output() --partial <partial>: returns 1 and displays details if <partial> is not a substring in \`\$output'" { run echo 'b' - run assert_output -p 'a' + run assert_output --partial 'a' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 4 ] [ "${lines[0]}" == '-- output does not contain substring --' ] @@ -81,9 +102,10 @@ load test_helper [ "${lines[3]}" == '--' ] } -@test "assert_output() -p <partial>: displays details in multi-line format if \`\$output' is longer than one line" { +# Output formatting +@test "assert_output() --partial <partial>: displays details in multi-line format if \`\$output' is longer than one line" { run echo $'b 0\nb 1' - run assert_output -p 'a' + run assert_output --partial 'a' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 7 ] [ "${lines[0]}" == '-- output does not contain substring --' ] @@ -95,9 +117,9 @@ load test_helper [ "${lines[6]}" == '--' ] } -@test 'assert_output() -p <partial>: displays details in multi-line format if <partial> is longer than one line' { +@test 'assert_output() --partial <partial>: displays details in multi-line format if <partial> is longer than one line' { run echo 'b' - run assert_output -p $'a 0\na 1' + run assert_output --partial $'a 0\na 1' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 7 ] [ "${lines[0]}" == '-- output does not contain substring --' ] @@ -109,33 +131,54 @@ load test_helper [ "${lines[6]}" == '--' ] } -# Regular expression matching: `-r <regex>'. -@test "assert_output() -r <regex>: returns 0 if <regex> matches \`\$output'" { +# +# Regular expression matching: `-e' and `--regexp' +# + +# Options +test_r_regexp () { + run echo 'abc' + run assert_output "$1" '^a' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'assert_output() -e <regexp>: enables regular expression matching' { + test_r_regexp -e +} + +@test 'assert_output() --regexp <regexp>: enables regular expression matching' { + test_r_regexp --regexp +} + +# Correctness +@test "assert_output() --regexp <regexp>: returns 0 if <regexp> matches \`\$output'" { run echo $'a\nb\nc' - run assert_output -r '.*b.*' + run assert_output --regexp '.*b.*' [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } -@test "assert_output() -r <regex>: returns 1 and displays details if <regex> does not match \`\$output'" { +@test "assert_output() --regexp <regexp>: returns 1 and displays details if <regexp> does not match \`\$output'" { run echo 'b' - run assert_output -r '.*a.*' + run assert_output --regexp '.*a.*' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 4 ] [ "${lines[0]}" == '-- regular expression does not match output --' ] - [ "${lines[1]}" == 'regex : .*a.*' ] + [ "${lines[1]}" == 'regexp : .*a.*' ] [ "${lines[2]}" == 'output : b' ] [ "${lines[3]}" == '--' ] } -@test "assert_output() -r <regex>: displays details in multi-line format if \`\$output' is longer than one line" { +# Output formatting +@test "assert_output() --regexp <regexp>: displays details in multi-line format if \`\$output' is longer than one line" { run echo $'b 0\nb 1' - run assert_output -r '.*a.*' + run assert_output --regexp '.*a.*' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 7 ] [ "${lines[0]}" == '-- regular expression does not match output --' ] - [ "${lines[1]}" == 'regex (1 lines):' ] + [ "${lines[1]}" == 'regexp (1 lines):' ] [ "${lines[2]}" == ' .*a.*' ] [ "${lines[3]}" == 'output (2 lines):' ] [ "${lines[4]}" == ' b 0' ] @@ -143,13 +186,13 @@ load test_helper [ "${lines[6]}" == '--' ] } -@test 'assert_output() -r <regex>: displays details in multi-line format if <regex> is longer than one line' { +@test 'assert_output() --regexp <regexp>: displays details in multi-line format if <regexp> is longer than one line' { run echo 'b' - run assert_output -r $'.*a\nb.*' + run assert_output --regexp $'.*a\nb.*' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 7 ] [ "${lines[0]}" == '-- regular expression does not match output --' ] - [ "${lines[1]}" == 'regex (2 lines):' ] + [ "${lines[1]}" == 'regexp (2 lines):' ] [ "${lines[2]}" == ' .*a' ] [ "${lines[3]}" == ' b.*' ] [ "${lines[4]}" == 'output (1 lines):' ] @@ -157,218 +200,26 @@ load test_helper [ "${lines[6]}" == '--' ] } - -# -# Matching a specific line: `-l <index>'. -# - -# Literal matching. - -@test "assert_output() -l <index> <expected>: returns 0 if <expected> equals \`\${lines[<index>]}'" { - run echo $'a\nb\nc' - run assert_output -l 1 'b' - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 0 ] -} - -@test "assert_output() -l <index> <expected>: returns 1 and displays details if <expected> does not equal \`\${lines[<index>]}'" { - run echo $'a\nb\nc' - run assert_output -l 1 'a' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 5 ] - [ "${lines[0]}" == '-- line differs --' ] - [ "${lines[1]}" == 'index : 1' ] - [ "${lines[2]}" == 'expected : a' ] - [ "${lines[3]}" == 'actual : b' ] - [ "${lines[4]}" == '--' ] -} - -@test 'assert_output() -l <index> <expected>: performs literal matching by default' { - run echo $'a\nb\nc' - run assert_output -l 1 '*' - [ "$status" -eq 1 ] -} - -# Partial matching: `-p <partial>'. - -@test "assert_output() -l <index> -p <partial>: returns 0 if <partial> is a substring in \`\${lines[<index>]}'" { - run echo $'a\nabc\nc' - run assert_output -l 1 -p 'b' - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 0 ] -} - -@test "assert_output() -l <index> -p <partial>: returns 1 and displays details if <partial> is not a substring in \`\${lines[<index>]}'" { - run echo $'b 0\nb 1' - run assert_output -l 1 -p 'a' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 5 ] - [ "${lines[0]}" == '-- line does not contain substring --' ] - [ "${lines[1]}" == 'index : 1' ] - [ "${lines[2]}" == 'substring : a' ] - [ "${lines[3]}" == 'line : b 1' ] - [ "${lines[4]}" == '--' ] -} - -# Regular expression matching: `-r <regex>'. - -@test "assert_output() -l <index> -r <regex>: returns 0 if <regex> matches \`\${lines[<index>]}'" { - run echo $'a\nabc\nc' - run assert_output -l 1 -r '.*b.*' - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 0 ] -} - -@test "assert_output() -l <index> -r <regex>: returns 1 and displays details if <regex> does not match \`\${lines[<index>]}'" { - run echo $'a\nb\nc' - run assert_output -l 1 -r '.*a.*' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 5 ] - [ "${lines[0]}" == '-- regular expression does not match line --' ] - [ "${lines[1]}" == 'index : 1' ] - [ "${lines[2]}" == 'regex : .*a.*' ] - [ "${lines[3]}" == 'line : b' ] - [ "${lines[4]}" == '--' ] -} - - -# -# Containing a line: `-l'. -# - -# Literal matching. - -@test "assert_output() -l <expected>: returns 0 if <expected> is a line in \`\${lines[@]}'" { - run echo $'a\nb\nc' - run assert_output -l 'b' - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 0 ] -} - -@test "assert_output() -l <expected>: returns 1 and displays details if <expected> is not a line in \`\${lines[@]}'" { - run echo 'b' - run assert_output -l 'a' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 4 ] - [ "${lines[0]}" == '-- output does not contain line --' ] - [ "${lines[1]}" == 'line : a' ] - [ "${lines[2]}" == 'output : b' ] - [ "${lines[3]}" == '--' ] -} - -@test "assert_output() -l <expected>: displays \`\$output' in multi-line format if it is longer than oen line" { - run echo $'b 0\nb 1' - run assert_output -l 'a' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 6 ] - [ "${lines[0]}" == '-- output does not contain line --' ] - [ "${lines[1]}" == 'line : a' ] - [ "${lines[2]}" == 'output (2 lines):' ] - [ "${lines[3]}" == ' b 0' ] - [ "${lines[4]}" == ' b 1' ] - [ "${lines[5]}" == '--' ] -} - -@test 'assert_output() -l <expected>: performs literal matching by default' { - run echo 'a' - run assert_output -l '*' - [ "$status" -eq 1 ] -} - -# Partial matching: `-p <partial>'. - -@test "assert_output() -l -p <partial>: returns 0 if <partial> is a substring in any line in \`\${lines[@]}'" { - run echo $'a\nabc\nc' - run assert_output -l -p 'b' - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 0 ] -} - -@test "assert_output() -l -p <partial>: returns 1 and displays details if <partial> is not a substring in any lines in \`\${lines[@]}'" { - run echo 'b' - run assert_output -l -p 'a' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 4 ] - [ "${lines[0]}" == '-- no output line contains substring --' ] - [ "${lines[1]}" == 'substring : a' ] - [ "${lines[2]}" == 'output : b' ] - [ "${lines[3]}" == '--' ] -} - -@test "assert_output() -l -p <partial>: displays \`\$output' in multi-line format if it is longer than one line" { - run echo $'b 0\nb 1' - run assert_output -l -p 'a' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 6 ] - [ "${lines[0]}" == '-- no output line contains substring --' ] - [ "${lines[1]}" == 'substring : a' ] - [ "${lines[2]}" == 'output (2 lines):' ] - [ "${lines[3]}" == ' b 0' ] - [ "${lines[4]}" == ' b 1' ] - [ "${lines[5]}" == '--' ] -} - -# Regular expression matching: `-r <regex>'. - -@test "assert_output() -l -r <regex>: returns 0 if <regex> matches any line in \`\${lines[@]}'" { - run echo $'a\nb\nc' - run assert_output -l -r '.*b.*' - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 0 ] -} - -@test "assert_output() -l -r <regex>: returns 1 and displays details if <regex> does not match any lines in \`\${lines[@]}'" { - run echo 'b' - run assert_output -l -r '.*a.*' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 4 ] - [ "${lines[0]}" == '-- no output line matches regular expression --' ] - [ "${lines[1]}" == 'regex : .*a.*' ] - [ "${lines[2]}" == 'output : b' ] - [ "${lines[3]}" == '--' ] -} - -@test "assert_output() -l -r <regex>: displays \`\$output' in multi-line format if longer than one line" { - run echo $'b 0\nb 1' - run assert_output -l -r '.*a.*' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 6 ] - [ "${lines[0]}" == '-- no output line matches regular expression --' ] - [ "${lines[1]}" == 'regex : .*a.*' ] - [ "${lines[2]}" == 'output (2 lines):' ] - [ "${lines[3]}" == ' b 0' ] - [ "${lines[4]}" == ' b 1' ] - [ "${lines[5]}" == '--' ] -} - - -# -# Common. -# - -@test 'assert_output() -l and -l <index> are mutually exclusive' { - run assert_output -l -l 1 'b' +# Error handling +@test 'assert_output() --regexp <regexp>: returns 1 and displays an error message if <regexp> is not a valid extended regular expression' { + run assert_output --regexp '[.*' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 3 ] [ "${lines[0]}" == '-- ERROR: assert_output --' ] - [ "${lines[1]}" == "\`-l' and \`-l <index>' are mutually exclusive" ] + [ "${lines[1]}" == "Invalid extended regular expression: \`[.*'" ] [ "${lines[2]}" == '--' ] } -@test 'assert_output() -p and -r are mutually exclusive' { - run assert_output -p -r 'b' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 3 ] - [ "${lines[0]}" == '-- ERROR: assert_output --' ] - [ "${lines[1]}" == "\`-p' and \`-r' are mutually exclusive" ] - [ "${lines[2]}" == '--' ] -} -@test 'assert_output() -r <regex>: returns 1 and displays an error message if <regex> is not a valid extended regular expression' { - run assert_output -r '[.*' +# +# Common +# + +@test "assert_output(): \`--partial' and \`--regexp' are mutually exclusive" { + run assert_output --partial --regexp [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 3 ] [ "${lines[0]}" == '-- ERROR: assert_output --' ] - [ "${lines[1]}" == "Invalid extended regular expression: \`[.*'" ] + [ "${lines[1]}" == "\`--partial' and \`--regexp' are mutually exclusive" ] [ "${lines[2]}" == '--' ] } diff --git a/test/60-lib-assertion-16-refute_output.bats b/test/60-lib-assertion-16-refute_output.bats index 2cc569f2..58c78a0e 100755 --- a/test/60-lib-assertion-16-refute_output.bats +++ b/test/60-lib-assertion-16-refute_output.bats @@ -4,11 +4,10 @@ load test_helper # -# Matching entire output. +# Literal matching # -# Literal matching. - +# Correctness @test "refute_output() <unexpected>: returns 0 if <unexpected> does not equal \`\$output'" { run echo 'b' run refute_output 'a' @@ -26,6 +25,7 @@ load test_helper [ "${lines[2]}" == '--' ] } +# Output formatting @test 'refute_output() <unexpected>: displays details in multi-line format if necessary' { run echo $'a 0\na 1' run refute_output $'a 0\na 1' @@ -38,74 +38,62 @@ load test_helper [ "${lines[4]}" == '--' ] } +# Options @test 'refute_output() <unexpected>: performs literal matching by default' { run echo 'a' run refute_output '*' [ "$status" -eq 0 ] } -# Partial matching: `-p <partial>'. -@test "refute_output() -p <partial>: returns 0 if <partial> is not a substring in \`\$output'" { - run echo $'a\nb\nc' - run refute_output -p 'd' +# +# Partial matching: `-p' and `--partial' +# + +# Options +test_p_partial () { + run echo 'abc' + run refute_output "$1" 'd' [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } -@test "refute_output() -p <partial>: returns 1 and displays details if <partial> is a substring in \`\$output'" { - run echo 'a' - run refute_output -p 'a' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 4 ] - [ "${lines[0]}" == '-- output should not contain substring --' ] - [ "${lines[1]}" == 'substring : a' ] - [ "${lines[2]}" == 'output : a' ] - [ "${lines[3]}" == '--' ] +@test 'refute_output() -p <partial>: enables partial matching' { + test_p_partial -p } -@test 'refute_output() -p <partial>: displays details in multi-line format if necessary' { - run echo $'a 0\na 1' - run refute_output -p 'a' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 7 ] - [ "${lines[0]}" == '-- output should not contain substring --' ] - [ "${lines[1]}" == 'substring (1 lines):' ] - [ "${lines[2]}" == ' a' ] - [ "${lines[3]}" == 'output (2 lines):' ] - [ "${lines[4]}" == ' a 0' ] - [ "${lines[5]}" == ' a 1' ] - [ "${lines[6]}" == '--' ] +@test 'refute_output() --partial <partial>: enables partial matching' { + test_p_partial --partial } -# Regular expression matching: `-r <regex>'. - -@test "refute_output() -r <regex>: returns 0 if <regex> does not match \`\$output'" { +# Correctness +@test "refute_output() --partial <partial>: returns 0 if <partial> is not a substring in \`\$output'" { run echo $'a\nb\nc' - run refute_output -r '.*d.*' + run refute_output --partial 'd' [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } -@test "refute_output() -r <regex>: returns 1 and displays details if <regex> matches \`\$output'" { +@test "refute_output() --partial <partial>: returns 1 and displays details if <partial> is a substring in \`\$output'" { run echo 'a' - run refute_output -r '.*a.*' + run refute_output --partial 'a' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 4 ] - [ "${lines[0]}" == '-- regular expression should not match output --' ] - [ "${lines[1]}" == 'regex : .*a.*' ] - [ "${lines[2]}" == 'output : a' ] + [ "${lines[0]}" == '-- output should not contain substring --' ] + [ "${lines[1]}" == 'substring : a' ] + [ "${lines[2]}" == 'output : a' ] [ "${lines[3]}" == '--' ] } -@test 'refute_output() -r <regex>: displays details in multi-line format if necessary' { +# Output formatting +@test 'refute_output() --partial <partial>: displays details in multi-line format if necessary' { run echo $'a 0\na 1' - run refute_output -r '.*a.*' + run refute_output --partial 'a' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 7 ] - [ "${lines[0]}" == '-- regular expression should not match output --' ] - [ "${lines[1]}" == 'regex (1 lines):' ] - [ "${lines[2]}" == ' .*a.*' ] + [ "${lines[0]}" == '-- output should not contain substring --' ] + [ "${lines[1]}" == 'substring (1 lines):' ] + [ "${lines[2]}" == ' a' ] [ "${lines[3]}" == 'output (2 lines):' ] [ "${lines[4]}" == ' a 0' ] [ "${lines[5]}" == ' a 1' ] @@ -114,224 +102,79 @@ load test_helper # -# Matching a specific line: `-l <index>'. -# - -# Literal matching. - -@test "refute_output() -l <index> <unexpected>: returns 0 if <unexpected> does not equal \`\${lines[<index>]}'" { - run echo $'a\nb\nc' - run refute_output -l 1 'd' - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 0 ] -} - -@test "refute_output() -l <index> <unexpected>: returns 1 and displays details if <unexpected> equals \`\${lines[<index>]}'" { - run echo $'a\nb\nc' - run refute_output -l 1 'b' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 4 ] - [ "${lines[0]}" == '-- line should differ --' ] - [ "${lines[1]}" == 'index : 1' ] - [ "${lines[2]}" == 'line : b' ] - [ "${lines[3]}" == '--' ] -} - -@test 'refute_output() -l <index> <unexpected>: performs literal matching by default' { - run echo $'a\nb\nc' - run refute_output -l 1 '*' - [ "$status" -eq 0 ] -} - -# Partial matching: `-p <partial>'. - -@test "refute_output() -l <index> -p <partial>: returns 0 if <partial> is not a substring in \`\${lines[<index>]}'" { - run echo $'a\nabc\nc' - run refute_output -l 1 -p 'd' - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 0 ] -} - -@test "refute_output() -l <index> -p <partial>: returns 1 and displays details if <partial> is a substring in \`\${lines[<index>]}'" { - run echo $'a\nabc\nc' - run refute_output -l 1 -p 'b' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 5 ] - [ "${lines[0]}" == '-- line should not contain substring --' ] - [ "${lines[1]}" == 'index : 1' ] - [ "${lines[2]}" == 'substring : b' ] - [ "${lines[3]}" == 'line : abc' ] - [ "${lines[4]}" == '--' ] -} - -# Regular expression matching: `-r <regex>'. - -@test "refute_output() -l <index> -r <regex>: returns 0 if <regex> does not match \`\${lines[<index>]}'" { - run echo $'a\nabc\nc' - run refute_output -l 1 -r '.*d.*' - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 0 ] -} - -@test "refute_output() -l <index> -r <regex>: returns 1 and displays details if <regex> matches \`\${lines[<index>]}'" { - run echo $'a\nabc\nc' - run refute_output -l 1 -r '.*b.*' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 5 ] - [ "${lines[0]}" == '-- regular expression should not match line --' ] - [ "${lines[1]}" == 'index : 1' ] - [ "${lines[2]}" == 'regex : .*b.*' ] - [ "${lines[3]}" == 'line : abc' ] - [ "${lines[4]}" == '--' ] -} - - -# -# Containing a line: `-l'. +# Regular expression matching: `-e' and `--regexp' # -# Literal matching. - -@test "refute_output() -l <unexpected>: returns 0 if <unexpected> is not a line in \`\${lines[@]}'" { - run echo $'a\nb\nc' - run refute_output -l 'd' +# Options +test_r_regexp () { + run echo 'abc' + run refute_output "$1" '^d' [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } -@test "refute_output() -l <unexpected>: returns 1 and displays details if <unexpected> is not a line in \`\${lines[@]}'" { - run echo 'a' - run refute_output -l 'a' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 5 ] - [ "${lines[0]}" == '-- line should not be in output --' ] - [ "${lines[1]}" == 'line : a' ] - [ "${lines[2]}" == 'index : 0' ] - [ "${lines[3]}" == 'output : a' ] - [ "${lines[4]}" == '--' ] +@test 'refute_output() -e <regexp>: enables regular expression matching' { + test_r_regexp -e } -@test 'refute_output() -l <unexpected>: displays details in multi-line format if necessary' { - run echo $'a\nb\nc' - run refute_output -l 'b' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 8 ] - [ "${lines[0]}" == '-- line should not be in output --' ] - [ "${lines[1]}" == 'line : b' ] - [ "${lines[2]}" == 'index : 1' ] - [ "${lines[3]}" == 'output (3 lines):' ] - [ "${lines[4]}" == ' a' ] - [ "${lines[5]}" == '> b' ] - [ "${lines[6]}" == ' c' ] - [ "${lines[7]}" == '--' ] +@test 'refute_output() --regexp <regexp>: enables regular expression matching' { + test_r_regexp --regexp } -@test 'refute_output() -l <unexpected>: performs literal matching by default' { - run echo 'a' - run refute_output -l '*' - [ "$status" -eq 0 ] -} - -# Partial matching: `-p <partial>'. - -@test "refute_output() -l -p <partial>: returns 0 if <partial> is not a substring in any line in \`\${lines[@]}'" { +# Correctness +@test "refute_output() --regexp <regexp>: returns 0 if <regexp> does not match \`\$output'" { run echo $'a\nb\nc' - run refute_output -l -p 'd' + run refute_output --regexp '.*d.*' [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } -@test "refute_output() -l -p <partial>: returns 1 and displays details if <partial> is a substring in any line in \`\${lines[@]}'" { +@test "refute_output() --regexp <regexp>: returns 1 and displays details if <regexp> matches \`\$output'" { run echo 'a' - run refute_output -l -p 'a' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 5 ] - [ "${lines[0]}" == '-- no line should contain substring --' ] - [ "${lines[1]}" == 'substring : a' ] - [ "${lines[2]}" == 'index : 0' ] - [ "${lines[3]}" == 'output : a' ] - [ "${lines[4]}" == '--' ] -} - -@test 'refute_output() -l -p <partial>: displays details in multi-line format if necessary' { - run echo $'a\nabc\nc' - run refute_output -l -p 'b' + run refute_output --regexp '.*a.*' [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 8 ] - [ "${lines[0]}" == '-- no line should contain substring --' ] - [ "${lines[1]}" == 'substring : b' ] - [ "${lines[2]}" == 'index : 1' ] - [ "${lines[3]}" == 'output (3 lines):' ] - [ "${lines[4]}" == ' a' ] - [ "${lines[5]}" == '> abc' ] - [ "${lines[6]}" == ' c' ] - [ "${lines[7]}" == '--' ] -} - -# Regular expression matching: `-r <regex>'. - -@test "refute_output() -l -r <regex>: returns 0 if <regex> does not match any line in \`\${lines[@]}'" { - run echo $'a\nb\nc' - run refute_output -l -r '.*d.*' - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 0 ] -} - -@test "refute_output() -l -r <regex>: returns 1 and displays details if <regex> matches any lines in \`\${lines[@]}'" { - run echo 'a' - run refute_output -l -r '.*a.*' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 5 ] - [ "${lines[0]}" == '-- no line should match the regular expression --' ] - [ "${lines[1]}" == 'regex : .*a.*' ] - [ "${lines[2]}" == 'index : 0' ] - [ "${lines[3]}" == 'output : a' ] - [ "${lines[4]}" == '--' ] + [ "${#lines[@]}" -eq 4 ] + [ "${lines[0]}" == '-- regular expression should not match output --' ] + [ "${lines[1]}" == 'regexp : .*a.*' ] + [ "${lines[2]}" == 'output : a' ] + [ "${lines[3]}" == '--' ] } -@test 'refute_output() -l -r <regex>: displays details in multi-line format if necessary' { - run echo $'a\nabc\nc' - run refute_output -l -r '.*b.*' +# Output formatting +@test 'refute_output() --regexp <regexp>: displays details in multi-line format if necessary' { + run echo $'a 0\na 1' + run refute_output --regexp '.*a.*' [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 8 ] - [ "${lines[0]}" == '-- no line should match the regular expression --' ] - [ "${lines[1]}" == 'regex : .*b.*' ] - [ "${lines[2]}" == 'index : 1' ] - [ "${lines[3]}" == 'output (3 lines):' ] - [ "${lines[4]}" == ' a' ] - [ "${lines[5]}" == '> abc' ] - [ "${lines[6]}" == ' c' ] - [ "${lines[7]}" == '--' ] + [ "${#lines[@]}" -eq 7 ] + [ "${lines[0]}" == '-- regular expression should not match output --' ] + [ "${lines[1]}" == 'regexp (1 lines):' ] + [ "${lines[2]}" == ' .*a.*' ] + [ "${lines[3]}" == 'output (2 lines):' ] + [ "${lines[4]}" == ' a 0' ] + [ "${lines[5]}" == ' a 1' ] + [ "${lines[6]}" == '--' ] } - -# -# Common. -# - -@test 'refute_output() -l and -l <index> are mutually exclusive' { - run refute_output -l -l 1 'b' +# Error handling +@test 'refute_output() --regexp <regexp>: returns 1 and displays an error message if <regexp> is not a valid extended regular expression' { + run refute_output --regexp '[.*' [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 3 ] [ "${lines[0]}" == '-- ERROR: refute_output --' ] - [ "${lines[1]}" == "\`-l' and \`-l <index>' are mutually exclusive" ] + [ "${lines[1]}" == "Invalid extended regular expression: \`[.*'" ] [ "${lines[2]}" == '--' ] } -@test 'refute_output() -p and -r are mutually exclusive' { - run refute_output -p -r 'b' - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 3 ] - [ "${lines[0]}" == '-- ERROR: refute_output --' ] - [ "${lines[1]}" == "\`-p' and \`-r' are mutually exclusive" ] - [ "${lines[2]}" == '--' ] -} -@test 'refute_output() -r <regex>: returns 1 and displays an error message if <regex> is not a valid extended regular expression' { - run refute_output -r '[.*' +# +# Common +# + +@test "refute_output(): \`--partial' and \`--regexp' are mutually exclusive" { + run refute_output --partial --regexp [ "$status" -eq 1 ] [ "${#lines[@]}" -eq 3 ] [ "${lines[0]}" == '-- ERROR: refute_output --' ] - [ "${lines[1]}" == "Invalid extended regular expression: \`[.*'" ] + [ "${lines[1]}" == "\`--partial' and \`--regexp' are mutually exclusive" ] [ "${lines[2]}" == '--' ] } diff --git a/test/60-lib-assertion-17-assert_line.bats b/test/60-lib-assertion-17-assert_line.bats new file mode 100755 index 00000000..9f439112 --- /dev/null +++ b/test/60-lib-assertion-17-assert_line.bats @@ -0,0 +1,328 @@ +#!/usr/bin/env bats + +load test_helper + + +############################################################################### +# Containing a line +############################################################################### + +# +# Literal matching +# + +# Correctness +@test "assert_line() <expected>: returns 0 if <expected> is a line in \`\${lines[@]}'" { + run echo $'a\nb\nc' + run assert_line 'b' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "assert_line() <expected>: returns 1 and displays details if <expected> is not a line in \`\${lines[@]}'" { + run echo 'b' + run assert_line 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 4 ] + [ "${lines[0]}" == '-- output does not contain line --' ] + [ "${lines[1]}" == 'line : a' ] + [ "${lines[2]}" == 'output : b' ] + [ "${lines[3]}" == '--' ] +} + +# Output formatting +@test "assert_line() <expected>: displays \`\$output' in multi-line format if it is longer than one line" { + run echo $'b 0\nb 1' + run assert_line 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 6 ] + [ "${lines[0]}" == '-- output does not contain line --' ] + [ "${lines[1]}" == 'line : a' ] + [ "${lines[2]}" == 'output (2 lines):' ] + [ "${lines[3]}" == ' b 0' ] + [ "${lines[4]}" == ' b 1' ] + [ "${lines[5]}" == '--' ] +} + +# Options +@test 'assert_line() <expected>: performs literal matching by default' { + run echo 'a' + run assert_line '*' + [ "$status" -eq 1 ] +} + + +# +# Partial matching: `-p' and `--partial' +# + +# Options +test_p_partial () { + run echo $'a\n_b_\nc' + run assert_line "$1" 'b' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'assert_line() -p <partial>: enables partial matching' { + test_p_partial -p +} + +@test 'assert_line() --partial <partial>: enables partial matching' { + test_p_partial --partial +} + +# Correctness +@test "assert_line() --partial <partial>: returns 0 if <partial> is a substring in any line in \`\${lines[@]}'" { + run echo $'a\n_b_\nc' + run assert_line --partial 'b' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "assert_line() --partial <partial>: returns 1 and displays details if <partial> is not a substring in any lines in \`\${lines[@]}'" { + run echo 'b' + run assert_line --partial 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 4 ] + [ "${lines[0]}" == '-- no output line contains substring --' ] + [ "${lines[1]}" == 'substring : a' ] + [ "${lines[2]}" == 'output : b' ] + [ "${lines[3]}" == '--' ] +} + +# Output formatting +@test "assert_line() --partial <partial>: displays \`\$output' in multi-line format if it is longer than one line" { + run echo $'b 0\nb 1' + run assert_line --partial 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 6 ] + [ "${lines[0]}" == '-- no output line contains substring --' ] + [ "${lines[1]}" == 'substring : a' ] + [ "${lines[2]}" == 'output (2 lines):' ] + [ "${lines[3]}" == ' b 0' ] + [ "${lines[4]}" == ' b 1' ] + [ "${lines[5]}" == '--' ] +} + + +# +# Regular expression matching: `-e' and `--regexp' +# + +# Options +test_r_regexp () { + run echo $'a\n_b_\nc' + run assert_line "$1" '^.b' + echo "$output" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'assert_line() -e <regexp>: enables regular expression matching' { + test_r_regexp -e +} + +@test 'assert_line() --regexp <regexp>: enables regular expression matching' { + test_r_regexp --regexp +} + +# Correctness +@test "assert_line() --regexp <regexp>: returns 0 if <regexp> matches any line in \`\${lines[@]}'" { + run echo $'a\n_b_\nc' + run assert_line --regexp '^.b' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "assert_line() --regexp <regexp>: returns 1 and displays details if <regexp> does not match any lines in \`\${lines[@]}'" { + run echo 'b' + run assert_line --regexp '^.a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 4 ] + [ "${lines[0]}" == '-- no output line matches regular expression --' ] + [ "${lines[1]}" == 'regexp : ^.a' ] + [ "${lines[2]}" == 'output : b' ] + [ "${lines[3]}" == '--' ] +} + +# Output formatting +@test "assert_line() --regexp <regexp>: displays \`\$output' in multi-line format if longer than one line" { + run echo $'b 0\nb 1' + run assert_line --regexp '^.a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 6 ] + [ "${lines[0]}" == '-- no output line matches regular expression --' ] + [ "${lines[1]}" == 'regexp : ^.a' ] + [ "${lines[2]}" == 'output (2 lines):' ] + [ "${lines[3]}" == ' b 0' ] + [ "${lines[4]}" == ' b 1' ] + [ "${lines[5]}" == '--' ] +} + + +############################################################################### +# Matching single line: `-n' and `--index' +############################################################################### + +# Options +test_n_index () { + run echo $'a\nb\nc' + run assert_line "$1" 1 'b' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'assert_line() -n <idx> <expected>: matches against the <idx>-th line only' { + test_n_index -n +} + +@test 'assert_line() --index <idx> <expected>: matches against the <idx>-th line only' { + test_n_index --index +} + +@test 'assert_line() --index <idx>: returns 1 and displays an error message if <idx> is not an integer' { + run assert_line --index 1a + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '-- ERROR: assert_line --' ] + [ "${lines[1]}" == "\`--index' requires an integer argument: \`1a'" ] + [ "${lines[2]}" == '--' ] +} + + +# +# Literal matching +# + +# Correctness +@test "assert_line() --index <idx> <expected>: returns 0 if <expected> equals \`\${lines[<idx>]}'" { + run echo $'a\nb\nc' + run assert_line --index 1 'b' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "assert_line() --index <idx> <expected>: returns 1 and displays details if <expected> does not equal \`\${lines[<idx>]}'" { + run echo $'a\nb\nc' + run assert_line --index 1 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 5 ] + [ "${lines[0]}" == '-- line differs --' ] + [ "${lines[1]}" == 'index : 1' ] + [ "${lines[2]}" == 'expected : a' ] + [ "${lines[3]}" == 'actual : b' ] + [ "${lines[4]}" == '--' ] +} + +# Options +@test 'assert_line() --index <idx> <expected>: performs literal matching by default' { + run echo $'a\nb\nc' + run assert_line --index 1 '*' + [ "$status" -eq 1 ] +} + + +# +# Partial matching: `-p' and `--partial' +# + +# Options +test_index_p_partial () { + run echo $'a\n_b_\nc' + run assert_line --index 1 "$1" 'b' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'assert_line() --index <idx> -p <partial>: enables partial matching' { + test_index_p_partial -p +} + +@test 'assert_line() --index <idx> --partial <partial>: enables partial matching' { + test_index_p_partial --partial +} + +# Correctness +@test "assert_line() --index <idx> --partial <partial>: returns 0 if <partial> is a substring in \`\${lines[<idx>]}'" { + run echo $'a\n_b_\nc' + run assert_line --index 1 --partial 'b' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "assert_line() --index <idx> --partial <partial>: returns 1 and displays details if <partial> is not a substring in \`\${lines[<idx>]}'" { + run echo $'b 0\nb 1' + run assert_line --index 1 --partial 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 5 ] + [ "${lines[0]}" == '-- line does not contain substring --' ] + [ "${lines[1]}" == 'index : 1' ] + [ "${lines[2]}" == 'substring : a' ] + [ "${lines[3]}" == 'line : b 1' ] + [ "${lines[4]}" == '--' ] +} + + +# +# Regular expression matching: `-e' and `--regexp' +# + +# Options +test_index_r_regexp () { + run echo $'a\n_b_\nc' + run assert_line --index 1 "$1" '^.b' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'assert_line() --index <idx> -e <regexp>: enables regular expression matching' { + test_index_r_regexp -e +} + +@test 'assert_line() --index <idx> --regexp <regexp>: enables regular expression matching' { + test_index_r_regexp --regexp +} + +# Correctness +@test "assert_line() --index <idx> --regexp <regexp>: returns 0 if <regexp> matches \`\${lines[<idx>]}'" { + run echo $'a\n_b_\nc' + run assert_line --index 1 --regexp '^.b' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "assert_line() --index <idx> --regexp <regexp>: returns 1 and displays details if <regexp> does not match \`\${lines[<idx>]}'" { + run echo $'a\nb\nc' + run assert_line --index 1 --regexp '^.a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 5 ] + [ "${lines[0]}" == '-- regular expression does not match line --' ] + [ "${lines[1]}" == 'index : 1' ] + [ "${lines[2]}" == 'regexp : ^.a' ] + [ "${lines[3]}" == 'line : b' ] + [ "${lines[4]}" == '--' ] +} + + +############################################################################### +# Common +############################################################################### + +@test "assert_line(): \`--partial' and \`--regexp' are mutually exclusive" { + run assert_line --partial --regexp + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '-- ERROR: assert_line --' ] + [ "${lines[1]}" == "\`--partial' and \`--regexp' are mutually exclusive" ] + [ "${lines[2]}" == '--' ] +} + +@test 'assert_line() --regexp <regexp>: returns 1 and displays an error message if <regexp> is not a valid extended regular expression' { + run assert_line --regexp '[.*' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '-- ERROR: assert_line --' ] + [ "${lines[1]}" == "Invalid extended regular expression: \`[.*'" ] + [ "${lines[2]}" == '--' ] +} diff --git a/test/60-lib-assertion-18-refute_line.bats b/test/60-lib-assertion-18-refute_line.bats new file mode 100755 index 00000000..92981465 --- /dev/null +++ b/test/60-lib-assertion-18-refute_line.bats @@ -0,0 +1,336 @@ +#!/usr/bin/env bats + +load test_helper + + +############################################################################### +# Containing a line +############################################################################### + +# +# Literal matching +# + +# Correctness +@test "refute_line() <unexpected>: returns 0 if <unexpected> is not a line in \`\${lines[@]}'" { + run echo $'a\nb\nc' + run refute_line 'd' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "refute_line() <unexpected>: returns 1 and displays details if <unexpected> is not a line in \`\${lines[@]}'" { + run echo 'a' + run refute_line 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 5 ] + [ "${lines[0]}" == '-- line should not be in output --' ] + [ "${lines[1]}" == 'line : a' ] + [ "${lines[2]}" == 'index : 0' ] + [ "${lines[3]}" == 'output : a' ] + [ "${lines[4]}" == '--' ] +} + +# Output formatting +@test "refute_line() <unexpected>: displays \`\$output' in multi-line format if it is longer than one line" { + run echo $'a 0\na 1\na 2' + run refute_line 'a 1' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 8 ] + [ "${lines[0]}" == '-- line should not be in output --' ] + [ "${lines[1]}" == 'line : a 1' ] + [ "${lines[2]}" == 'index : 1' ] + [ "${lines[3]}" == 'output (3 lines):' ] + [ "${lines[4]}" == ' a 0' ] + [ "${lines[5]}" == '> a 1' ] + [ "${lines[6]}" == ' a 2' ] + [ "${lines[7]}" == '--' ] +} + +# Options +@test 'refute_line() <unexpected>: performs literal matching by default' { + run echo 'a' + run refute_line '*' + [ "$status" -eq 0 ] +} + + +# +# Partial matching: `-p' and `--partial' +# + +# Options +test_p_partial () { + run echo $'a\nb\nc' + run refute_line "$1" 'd' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'refute_line() -p <partial>: enables partial matching' { + test_p_partial -p +} + +@test 'refute_line() --partial <partial>: enables partial matching' { + test_p_partial --partial +} + +# Correctness +@test "refute_line() --partial <partial>: returns 0 if <partial> is not a substring in any line in \`\${lines[@]}'" { + run echo $'a\nb\nc' + run refute_line --partial 'd' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "refute_line() --partial <partial>: returns 1 and displays details if <partial> is a substring in any line in \`\${lines[@]}'" { + run echo 'a' + run refute_line --partial 'a' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 5 ] + [ "${lines[0]}" == '-- no line should contain substring --' ] + [ "${lines[1]}" == 'substring : a' ] + [ "${lines[2]}" == 'index : 0' ] + [ "${lines[3]}" == 'output : a' ] + [ "${lines[4]}" == '--' ] +} + +# Output formatting +@test "refute_line() --partial <partial>: displays \`\$output' in multi-line format if it is longer than one line" { + run echo $'a\nabc\nc' + run refute_line --partial 'b' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 8 ] + [ "${lines[0]}" == '-- no line should contain substring --' ] + [ "${lines[1]}" == 'substring : b' ] + [ "${lines[2]}" == 'index : 1' ] + [ "${lines[3]}" == 'output (3 lines):' ] + [ "${lines[4]}" == ' a' ] + [ "${lines[5]}" == '> abc' ] + [ "${lines[6]}" == ' c' ] + [ "${lines[7]}" == '--' ] +} + + +# +# Regular expression matching: `-e' and `--regexp' +# + +# Options +test_r_regexp () { + run echo $'a\nb\nc' + run refute_line "$1" '^.d' + echo "$output" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'refute_line() -e <regexp>: enables regular expression matching' { + test_r_regexp -e +} + +@test 'refute_line() --regexp <regexp>: enables regular expression matching' { + test_r_regexp --regexp +} + +# Correctness +@test "refute_line() --regexp <regexp>: returns 0 if <regexp> does not match any line in \`\${lines[@]}'" { + run echo $'a\nb\nc' + run refute_line --regexp '.*d.*' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "refute_line() --regexp <regexp>: returns 1 and displays details if <regexp> matches any lines in \`\${lines[@]}'" { + run echo 'a' + run refute_line --regexp '.*a.*' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 5 ] + [ "${lines[0]}" == '-- no line should match the regular expression --' ] + [ "${lines[1]}" == 'regexp : .*a.*' ] + [ "${lines[2]}" == 'index : 0' ] + [ "${lines[3]}" == 'output : a' ] + [ "${lines[4]}" == '--' ] +} + +# Output formatting +@test "refute_line() --regexp <regexp>: displays \`\$output' in multi-line format if longer than one line" { + run echo $'a\nabc\nc' + run refute_line --regexp '.*b.*' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 8 ] + [ "${lines[0]}" == '-- no line should match the regular expression --' ] + [ "${lines[1]}" == 'regexp : .*b.*' ] + [ "${lines[2]}" == 'index : 1' ] + [ "${lines[3]}" == 'output (3 lines):' ] + [ "${lines[4]}" == ' a' ] + [ "${lines[5]}" == '> abc' ] + [ "${lines[6]}" == ' c' ] + [ "${lines[7]}" == '--' ] +} + + +############################################################################### +# Matching single line: `-n' and `--index' +############################################################################### + +# Options +test_n_index () { + run echo $'a\nb\nc' + run refute_line "$1" 1 'd' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'refute_line() -n <idx> <expected>: matches against the <idx>-th line only' { + test_n_index -n +} + +@test 'refute_line() --index <idx> <expected>: matches against the <idx>-th line only' { + test_n_index --index +} + +@test 'refute_line() --index <idx>: returns 1 and displays an error message if <idx> is not an integer' { + run refute_line --index 1a + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '-- ERROR: refute_line --' ] + [ "${lines[1]}" == "\`--index' requires an integer argument: \`1a'" ] + [ "${lines[2]}" == '--' ] +} + + +# +# Literal matching +# + +# Correctness +@test "refute_line() --index <idx> <unexpected>: returns 0 if <unexpected> does not equal \`\${lines[<idx>]}'" { + run echo $'a\nb\nc' + run refute_line --index 1 'd' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "refute_line() --index <idx> <unexpected>: returns 1 and displays details if <unexpected> equals \`\${lines[<idx>]}'" { + run echo $'a\nb\nc' + run refute_line --index 1 'b' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 4 ] + [ "${lines[0]}" == '-- line should differ --' ] + [ "${lines[1]}" == 'index : 1' ] + [ "${lines[2]}" == 'line : b' ] + [ "${lines[3]}" == '--' ] +} + +# Options +@test 'refute_line() --index <idx> <unexpected>: performs literal matching by default' { + run echo $'a\nb\nc' + run refute_line --index 1 '*' + [ "$status" -eq 0 ] +} + + +# +# Partial matching: `-p' and `--partial' +# + +# Options +test_index_p_partial () { + run echo $'a\nb\nc' + run refute_line --index 1 "$1" 'd' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'refute_line() --index <idx> -p <partial>: enables partial matching' { + test_index_p_partial -p +} + +@test 'refute_line() --index <idx> --partial <partial>: enables partial matching' { + test_index_p_partial --partial +} + +# Correctness +@test "refute_line() --index <idx> --partial <partial>: returns 0 if <partial> is not a substring in \`\${lines[<idx>]}'" { + run echo $'a\nabc\nc' + run refute_line --index 1 --partial 'd' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "refute_line() --index <idx> --partial <partial>: returns 1 and displays details if <partial> is a substring in \`\${lines[<idx>]}'" { + run echo $'a\nabc\nc' + run refute_line --index 1 --partial 'b' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 5 ] + [ "${lines[0]}" == '-- line should not contain substring --' ] + [ "${lines[1]}" == 'index : 1' ] + [ "${lines[2]}" == 'substring : b' ] + [ "${lines[3]}" == 'line : abc' ] + [ "${lines[4]}" == '--' ] +} + + +# +# Regular expression matching: `-e' and `--regexp' +# + +# Options +test_index_r_regexp () { + run echo $'a\nb\nc' + run refute_line --index 1 "$1" '^.b' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'refute_line() --index <idx> -e <regexp>: enables regular expression matching' { + test_index_r_regexp -e +} + +@test 'refute_line() --index <idx> --regexp <regexp>: enables regular expression matching' { + test_index_r_regexp --regexp +} + +# Correctness +@test "refute_line() --index <idx> --regexp <regexp>: returns 0 if <regexp> does not match \`\${lines[<idx>]}'" { + run echo $'a\nabc\nc' + run refute_line --index 1 --regexp '.*d.*' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "refute_line() --index <idx> --regexp <regexp>: returns 1 and displays details if <regexp> matches \`\${lines[<idx>]}'" { + run echo $'a\nabc\nc' + run refute_line --index 1 --regexp '.*b.*' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 5 ] + [ "${lines[0]}" == '-- regular expression should not match line --' ] + [ "${lines[1]}" == 'index : 1' ] + [ "${lines[2]}" == 'regexp : .*b.*' ] + [ "${lines[3]}" == 'line : abc' ] + [ "${lines[4]}" == '--' ] +} + + +############################################################################### +# Common +############################################################################### + +@test "refute_line(): \`--partial' and \`--regexp' are mutually exclusive" { + run refute_line --partial --regexp + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '-- ERROR: refute_line --' ] + [ "${lines[1]}" == "\`--partial' and \`--regexp' are mutually exclusive" ] + [ "${lines[2]}" == '--' ] +} + +@test 'refute_line() --regexp <regexp>: returns 1 and displays an error message if <regexp> is not a valid extended regular expression' { + run refute_line --regexp '[.*' + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '-- ERROR: refute_line --' ] + [ "${lines[1]}" == "Invalid extended regular expression: \`[.*'" ] + [ "${lines[2]}" == '--' ] +}