From 7290892865a8206a95e3cc417005bc28b2cc249d Mon Sep 17 00:00:00 2001 From: Alex Thiessen Date: Mon, 30 May 2022 19:09:09 +0200 Subject: [PATCH 1/4] test: Add `refute_regex.bats` --- test/refute_regex.bats | 98 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 test/refute_regex.bats diff --git a/test/refute_regex.bats b/test/refute_regex.bats new file mode 100644 index 0000000..c1fd1b2 --- /dev/null +++ b/test/refute_regex.bats @@ -0,0 +1,98 @@ +#!/usr/bin/env bats + +load test_helper + +# +# Literal matching +# + +# Correctness +@test "refute_regex() : fails if a substring matches extended regular expression " { + run refute_regex 'abc' '^[a-z]b' + assert_test_fail <<'ERR_MSG' + +-- value matches regular expression -- +value : abc +pattern : ^[a-z]b +match : ab +case : sensitive +-- +ERR_MSG +} + +@test "refute_regex() : succeeds if no substring matches extended regular expression " { + run refute_regex 'bcd' '^[a-z]b[c-z]+' + assert_test_pass +} + +@test "refute_regex() : provides results in BASH_REMATCH on failure" { + unset -v BASH_REMATCH + + refute_regex 'abcd' 'b.d' \ + || { + declare -p BASH_REMATCH && \ + [ "${BASH_REMATCH[0]}" = 'bcd' ] + } +} + +@test "refute_regex() : matches case-insensitively when 'nocasematch' is set" { + shopt -s nocasematch + + run refute_regex 'aBc' 'ABC' + assert_test_fail <<'ERR_MSG' + +-- value matches regular expression -- +value : aBc +pattern : ABC +match : aBc +case : insensitive +-- +ERR_MSG +} + +@test "refute_regex() : outputs multi-line nicely when it fails" { + run refute_regex $'abc\n123' '^[a-z]b[c-z]+' + assert_test_fail <<'ERR_MSG' + +-- value matches regular expression -- +value (2 lines): + abc + 123 +pattern (1 lines): + ^[a-z]b[c-z]+ +match (1 lines): + abc +case (1 lines): + sensitive +-- +ERR_MSG + + shopt -s nocasematch + run refute_regex $'aBc\n123' '^[a-z]b[c-z]+' + assert_test_fail <<'ERR_MSG' + +-- value matches regular expression -- +value (2 lines): + aBc + 123 +pattern (1 lines): + ^[a-z]b[c-z]+ +match (1 lines): + aBc +case (1 lines): + insensitive +-- +ERR_MSG +} + +# Error handling +@test "refute_regex() : returns 1 and displays an error message if is not a valid extended regular expression" { + run refute_regex value '[.*' + + assert_test_fail <<'ERR_MSG' + +-- ERROR: refute_regex -- +Invalid extended regular expression: `[.*' +-- +ERR_MSG +} From bbe292a24434659135cc834996659fd359e808f5 Mon Sep 17 00:00:00 2001 From: Alex Thiessen Date: Mon, 30 May 2022 19:15:44 +0200 Subject: [PATCH 2/4] src: Add `refute_regex.bash` Add the function complementary to `assert_regex()`. --- src/refute_regex.bash | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/refute_regex.bash diff --git a/src/refute_regex.bash b/src/refute_regex.bash new file mode 100644 index 0000000..20f6b32 --- /dev/null +++ b/src/refute_regex.bash @@ -0,0 +1,23 @@ +refute_regex() { + local -r value="${1}" + local -r pattern="${2}" + + if [[ '' =~ ${pattern} ]] || (( ${?} == 2 )); then + echo "Invalid extended regular expression: \`${pattern}'" \ + | batslib_decorate 'ERROR: refute_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}" \ + 'match' "${BASH_REMATCH[0]}" \ + 'case' "${case_sensitive}" \ + | batslib_decorate 'value matches regular expression' \ + | fail + fi +} From 81d772487beab3925aec5efd4297e8ee8578ea88 Mon Sep 17 00:00:00 2001 From: Alex Thiessen Date: Mon, 30 May 2022 19:11:54 +0200 Subject: [PATCH 3/4] load.bash: Include `refute_regex.bash` --- load.bash | 1 + 1 file changed, 1 insertion(+) diff --git a/load.bash b/load.bash index a5fe0f2..c67d9e8 100644 --- a/load.bash +++ b/load.bash @@ -30,3 +30,4 @@ 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" +source "$(dirname "${BASH_SOURCE[0]}")/src/refute_regex.bash" From baffb32e5dc6094df03239ab6286ce4ab8381ae3 Mon Sep 17 00:00:00 2001 From: Alex Thiessen Date: Mon, 30 May 2022 19:15:23 +0200 Subject: [PATCH 4/4] README.md: Document `refute_regex` Add a copy to the implementation, too. --- README.md | 44 +++++++++++++++++++++++++++++++++++++++++++ src/refute_regex.bash | 43 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/README.md b/README.md index 4544538..c662ab8 100644 --- a/README.md +++ b/README.md @@ -728,6 +728,50 @@ 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. +### `refute_regex` + +This function is similar to `refute_equal` but uses pattern matching instead of +equality, by wrapping `! [[ value =~ pattern ]]`. + +Fail if the value (first parameter) matches the pattern (second parameter). + +```bash +@test 'refute_regex()' { + refute_regex 'WhatsApp' 'Threema' +} +``` + +On failure, the value, the pattern and the match are displayed. + +``` +@test 'refute_regex()' { + refute_regex 'WhatsApp' 'What.' +} + +-- value matches regular expression -- +value : WhatsApp +pattern : What. +match : Whats +case : sensitive +-- +``` + +If the value or pattern 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 +fails but is fragile, i.e. prone to being overwritten as a side effect of other +actions like calling `run`. Thus, it's good practice to avoid using +`BASH_REMATCH` in conjunction with `refute_regex()`. The valuable information +the array contains is the matching part of the value which is printed in the +failing test log, as mentioned above. + [bats]: https://github.com/bats-core/bats-core diff --git a/src/refute_regex.bash b/src/refute_regex.bash index 20f6b32..0918793 100644 --- a/src/refute_regex.bash +++ b/src/refute_regex.bash @@ -1,3 +1,46 @@ +# `refute_regex` +# +# This function is similar to `refute_equal` but uses pattern matching instead +# of equality, by wrapping `! [[ value =~ pattern ]]`. +# +# Fail if the value (first parameter) matches the pattern (second parameter). +# +# ```bash +# @test 'refute_regex()' { +# refute_regex 'WhatsApp' 'Threema' +# } +# ``` +# +# On failure, the value, the pattern and the match are displayed. +# +# ``` +# @test 'refute_regex()' { +# refute_regex 'WhatsApp' 'What.' +# } +# +# -- value matches regular expression -- +# value : WhatsApp +# pattern : What. +# match : Whats +# case : sensitive +# -- +# ``` +# +# If the value or pattern 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 fails but is fragile, i.e. prone to being overwritten as a side +# effect of other actions like calling `run`. Thus, it's good practice to avoid +# using `BASH_REMATCH` in conjunction with `refute_regex()`. The valuable +# information the array contains is the matching part of the value which is +# printed in the failing test log, as mentioned above. refute_regex() { local -r value="${1}" local -r pattern="${2}"