Skip to content

Commit

Permalink
Create unit tests for BATS helper functions
Browse files Browse the repository at this point in the history
Signed-off-by: Jan Dubois <jan.dubois@suse.com>
  • Loading branch information
jandubois committed Jul 8, 2023
1 parent 24f14e6 commit a78a7d6
Show file tree
Hide file tree
Showing 3 changed files with 283 additions and 19 deletions.
2 changes: 1 addition & 1 deletion bats/scripts/bats-lint.pl
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
# - $assert_success
# - $ {assert}_success
# - if [ $status -eq 0 ]
if (/(\$\{?)?(assert|refute|output\b|status\b)/) {
if (/(\$\{?)?(assert|refute|\boutput\b|\bstatus\b)/) {

Check failure on line 30 in bats/scripts/bats-lint.pl

View workflow job for this annotation

GitHub Actions / Check Spelling

`bstatus` is not a recognized word. (unrecognized-spelling)

Check failure on line 30 in bats/scripts/bats-lint.pl

View workflow job for this annotation

GitHub Actions / Check Spelling

`boutput` is not a recognized word. (unrecognized-spelling)
undef $run;
}
# Doesn't match on:
Expand Down
37 changes: 19 additions & 18 deletions bats/tests/helpers/utils.bash
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
is_true() {
# case-insensitive check; false values: '', '0', 'no', and 'false'
local value="$(echo "$1" | tr '[:upper:]' '[:lower:]')"
if [[ $value =~ ^(0|no|false)?$ ]]; then
false
else
true
fi
[[ ! $value =~ ^(0|no|false)?$ ]]
}

is_false() {
Expand All @@ -26,7 +22,7 @@ validate_enum() {
local var=$1
shift
for value in "$@"; do
if [ "${!var}" = "$value" ]; then
if [[ ${!var} == "$value" ]]; then
return
fi
done
Expand All @@ -40,7 +36,13 @@ assert_nothing() {
}

jq_output() {
jq -r "$@" <<<"${output}"
# -e sets exit status to 1 when the output is false or null
run jq -e -r "$@" <<<"${output}"
echo "$output"
if [[ $status == 1 && $output == false ]]; then
status=0
fi
return "$status"
}

get_setting() {
Expand Down Expand Up @@ -90,10 +92,10 @@ try() {
shift
done

local count
for ((count = 0; count < max; ++count)); do
local count=0
while true; do
run "$@"
if ((status == 0)); then
if ((status == 0 || ++count >= max)); then
break
fi
sleep "$delay"
Expand All @@ -106,7 +108,7 @@ image_without_tag() {
local image=$1
# If the tag looks like a port number and follows something that looks
# like a domain name, then don't strip the tag (e.g. foo.io:5000).
if [[ ${image##*:} =~ ^[0-9]+$ && ${image%:*} =~ \.[a-z]+$ ]]; then
if [[ ${image##*:} =~ ^[0-9]+(/|$) && ${image%:*} =~ \.[a-z]+$ ]]; then
echo "$image"
else
echo "${image%:*}"
Expand Down Expand Up @@ -156,22 +158,21 @@ unique_filename() {
local suffix=""

while true; do
local filename="$basename$suffix$extension"
if [ ! -e "$filename" ]; then
local filename="${basename}${suffix}${extension}"
if [[ ! -e $filename ]]; then
echo "$filename"
return
fi
index=$((index + 1))
suffix="_$index"
suffix="_$((++index))"
done
}

capture_logs() {
if capturing_logs && [ -d "$PATH_LOGS" ]; then
if capturing_logs && [[ -d $PATH_LOGS ]]; then
local logdir=$(unique_filename "${PATH_BATS_LOGS}/${RD_TEST_FILENAME}")
mkdir -p "$logdir"
cp -LR "$PATH_LOGS/" "$logdir"
echo "${BATS_TEST_DESCRIPTION:-teardown}" >"$logdir/test_description"
cp -LR "${PATH_LOGS}/" "$logdir"
echo "${BATS_TEST_DESCRIPTION:-teardown}" >"${logdir}/test_description"
fi
}

Expand Down
263 changes: 263 additions & 0 deletions bats/tests/helpers/utils.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
load '../helpers/load'

########################################################################

local_setup() {
COUNTER="${BATS_FILE_TMPDIR}/counter"
echo 0 >"$COUNTER"
}

# Increment counter file. Return success when counter >= max.
inc_counter() {
local max=${1-9999}
local counter=$(($(cat "$COUNTER") + 1))
echo $counter >"$COUNTER"
((counter >= max))
}

assert_counter_is() {
run cat "${COUNTER}"
assert_output "$1"
}

########################################################################

check_truthiness() {
local predicate=$1
local value

# test true values
for value in 1 true True TRUE yes Yes YES any; do
run "$predicate" "$value"
if [[ $predicate == is_true ]]; then
assert_success
else
assert_failure
fi
done

# test false values
for value in 0 false False FALSE no No NO ''; do
run "$predicate" "$value"
if [[ $predicate == is_true ]]; then
assert_failure
else
assert_success
fi
done
}

@test 'is_true' {
check_truthiness is_true
}

@test 'is_false' {
check_truthiness is_false
}

@test 'bool' {
run bool true
assert_output true

run bool false
assert_output false
}

########################################################################

@test 'validate_enum OS should pass' {
run validate_enum OS darwin linux windows
assert_success
}

@test 'validate_enum FRUIT should fail' {
FRUIT=apple
run validate_enum FRUIT banana cherry pear
assert_failure
# Can't check output; it is written using "fatal":
# FRUIT=apple is not a valid setting; select from [banana cherry pear]
}

########################################################################

@test 'is_xxx' {
# Exactly one of the is_xxx functions should return true
count=0
for os in linux macos windows; do
if "is_$os"; then
((++count))
fi
done
((count == 1))
}

########################################################################

get_json_test_data() {
# The run/assert silliness is because shellcheck gets confused by direct assignment to $output
run echo '{"String":"string", "False":false, "Null":null}'
assert_success
}

@test 'jq_output extracts string value' {
get_json_test_data
run jq_output .String
assert_success
assert_output string
}

@test 'jq_output extracts "false" value' {
get_json_test_data
run jq_output .False
assert_success
assert_output false
}

@test 'jq_output cannot extract "null" value' {
get_json_test_data
run jq_output .Null
assert_failure
assert_output null
}

@test 'jq_output fails when key is not found' {
get_json_test_data
run jq_output .DoesNotExist
assert_failure
assert_output null
}

@test 'jq_output fails on null' {
output=null
run jq_output .Anything
assert_failure
assert_output null
}

@test 'jq_output fails on undefined' {
output=undefined
run jq_output .Anything
assert_failure
assert_output --partial "parse error"
}

@test 'jq_output fails on non-JSON data' {
output="This is not JSON"
run jq_output .Anything
assert_failure
assert_output --partial "parse error"
}

########################################################################

@test 'this_function' {
foo() {
this_function
}
run foo
assert_success
assert_output foo
}

@test 'calling_function' {
bar() {
baz
}
baz() {
calling_function
}
run bar
assert_success
assert_output bar
}

########################################################################

@test 'try will run command at least once' {
run try --max 0 --delay 5 inc_counter
assert_failure
assert_counter_is 1
# "try" should not have called "sleep 5" at all
((SECONDS < 2))
}

@test 'try will stop as soon as the command succeeds' {
run try --max 3 --delay 3 inc_counter 2
assert_success
assert_counter_is 2
# "try" should have called "sleep 3" exactly once
((SECONDS >= 3))
((SECONDS < 6))
}

@test 'try will return after max retries' {
run try --max 3 --delay 2 inc_counter
assert_failure
assert_counter_is 3
# "try" should have called "sleep 2" exactly twice
((SECONDS >= 4))
((SECONDS < 6))
}

########################################################################

check_image_without_tag() {
local image=$1
local expect=${2-$1}
run image_without_tag "$image"
assert_success
assert_output "$expect"
}

@test 'image_without_tag busybox' {
check_image_without_tag busybox
}

@test 'image_without_tag busybox:latest' {
check_image_without_tag busybox:latest busybox
}

@test 'image_without_tag busybox:5000' {
check_image_without_tag busybox:5000 busybox
}

@test 'image_without_tag registry.io:5000' {
check_image_without_tag registry.io:5000
}

@test 'image_without_tag registry.io:5000/busybox' {
check_image_without_tag registry.io:5000/busybox
}

@test 'image_without_tag registry.io:5000/busybox:8080' {
check_image_without_tag registry.io:5000/busybox:8080 registry.io:5000/busybox
}

########################################################################

@test 'unique_filename without extension' {
run unique_filename "$COUNTER"
assert_success
assert_output "${COUNTER}_2"
touch "$output"

run unique_filename "$COUNTER"
assert_success
assert_output "${COUNTER}_3"
}

@test 'unique_filename with extension' {
run unique_filename "$COUNTER" .png
assert_success
assert_output "${COUNTER}.png"
touch "$output"

run unique_filename "$COUNTER" .png
assert_success
assert_output "${COUNTER}_2.png"
touch "$output"

run unique_filename "$COUNTER" .png
assert_success
assert_output "${COUNTER}_3.png"
}

0 comments on commit a78a7d6

Please sign in to comment.