diff --git a/README.md b/README.md index fadf5c8..4544538 100644 --- a/README.md +++ b/README.md @@ -693,6 +693,41 @@ 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. +### `assert_regex` + +This function is similar to `assert_equal` but uses pattern matching instead of +equality, by wrapping `[[ value =~ pattern ]]`. + +Fail if the value (first parameter) does not match the pattern (second +parameter). + +```bash +@test 'assert_regex()' { + assert_regex 'what' 'x$' +} +``` + +On failure, the value and the pattern are displayed. + +``` +-- values does not match regular expression -- +value : what +pattern : x$ +-- +``` + +If the value is longer than one line then it is displayed in *multi-line* +format. + +An error is displayed if the specified extended regular expression is invalid. + +For description of the matching behavior, refer to the documentation of the +`=~` operator in the +[Bash manual]: https://www.gnu.org/software/bash/manual/html_node/Conditional-Constructs.html. +Note that the `BASH_REMATCH` array is available immediately after the +assertion succeeds but is fragile, i.e. prone to being overwritten as a side +effect of other actions. + [bats]: https://github.com/bats-core/bats-core diff --git a/load.bash b/load.bash index 1201df1..a5fe0f2 100644 --- a/load.bash +++ b/load.bash @@ -29,3 +29,4 @@ source "$(dirname "${BASH_SOURCE[0]}")/src/assert_output.bash" source "$(dirname "${BASH_SOURCE[0]}")/src/refute_output.bash" source "$(dirname "${BASH_SOURCE[0]}")/src/assert_line.bash" source "$(dirname "${BASH_SOURCE[0]}")/src/refute_line.bash" +source "$(dirname "${BASH_SOURCE[0]}")/src/assert_regex.bash" diff --git a/src/assert_regex.bash b/src/assert_regex.bash new file mode 100644 index 0000000..636318e --- /dev/null +++ b/src/assert_regex.bash @@ -0,0 +1,56 @@ +# `assert_regex` +# +# This function is similar to `assert_equal` but uses pattern matching instead +# of equality, by wrapping `[[ value =~ pattern ]]`. +# +# Fail if the value (first parameter) does not match the pattern (second +# parameter). +# +# ```bash +# @test 'assert_regex()' { +# assert_regex 'what' 'x$' +# } +# ``` +# +# On failure, the value and the pattern are displayed. +# +# ``` +# -- values does not match regular expression -- +# value : what +# pattern : x$ +# -- +# ``` +# +# If the value is longer than one line then it is displayed in *multi-line* +# format. +# +# An error is displayed if the specified extended regular expression is invalid. +# +# For description of the matching behavior, refer to the documentation of the +# `=~` operator in the +# [Bash manual]: https://www.gnu.org/software/bash/manual/html_node/Conditional-Constructs.html. +# Note that the `BASH_REMATCH` array is available immediately after the +# assertion succeeds but is fragile, i.e. prone to being overwritten as a side +# effect of other actions. +assert_regex() { + local -r value="${1}" + local -r pattern="${2}" + + if [[ '' =~ ${pattern} ]] || (( ${?} == 2 )); then + echo "Invalid extended regular expression: \`${pattern}'" \ + | batslib_decorate 'ERROR: assert_regex' \ + | fail + elif ! [[ "${value}" =~ ${pattern} ]]; then + if shopt -p nocasematch &>/dev/null; then + local case_sensitive=insensitive + else + local case_sensitive=sensitive + fi + batslib_print_kv_single_or_multi 8 \ + 'value' "${value}" \ + 'pattern' "${pattern}" \ + 'case' "${case_sensitive}" \ + | batslib_decorate 'value does not match regular expression' \ + | fail + fi +} diff --git a/test/assert_regex.bats b/test/assert_regex.bats new file mode 100644 index 0000000..ef9c3a8 --- /dev/null +++ b/test/assert_regex.bats @@ -0,0 +1,82 @@ +#!/usr/bin/env bats + +load test_helper + +# +# Literal matching +# + +# Correctness +@test "assert_regex() : succeeds if a substring matches extended regular expression " { + run assert_regex 'abc' '^[a-z]b[c-z]+' + assert_test_pass +} + +@test "assert_regex() : fails if no substring matches extended regular expression " { + run assert_regex 'bcd' '^[a-z]b[c-z]+' + assert_test_fail <<'ERR_MSG' + +-- value does not match regular expression -- +value : bcd +pattern : ^[a-z]b[c-z]+ +case : sensitive +-- +ERR_MSG +} + +@test "assert_regex() : provides results in BASH_REMATCH" { + unset -v BASH_REMATCH + + assert_regex 'abcd' 'b.d' + declare -p BASH_REMATCH + [ "${BASH_REMATCH[0]}" = 'bcd' ] +} + +@test "assert_regex() : matches case-insensitively when 'nocasematch' is set" { + shopt -s nocasematch + + assert_regex 'aBc' 'ABC' +} + +@test "assert_regex() : outputs multi-line nicely when it fails" { + run assert_regex $'bcd\n123' '^[a-z]b[c-z]+' + assert_test_fail <<'ERR_MSG' + +-- value does not match regular expression -- +value (2 lines): + bcd + 123 +pattern (1 lines): + ^[a-z]b[c-z]+ +case (1 lines): + sensitive +-- +ERR_MSG + + shopt -s nocasematch + run assert_regex $'bcd\n123' '^[a-z]b[c-z]+' + assert_test_fail <<'ERR_MSG' + +-- value does not match regular expression -- +value (2 lines): + bcd + 123 +pattern (1 lines): + ^[a-z]b[c-z]+ +case (1 lines): + insensitive +-- +ERR_MSG +} + +# Error handling +@test "assert_regex() : returns 1 and displays an error message if is not a valid extended regular expression" { + run assert_regex value '[.*' + + assert_test_fail <<'ERR_MSG' + +-- ERROR: assert_regex -- +Invalid extended regular expression: `[.*' +-- +ERR_MSG +}