Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions assert-extras.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/usr/bin/env bash

# assert-extras.sh 1.1 - supplementary bash unit testing functions
# Note: This script should be sourced together with assert.sh,
# it is dependent on the functionality provided by that script.

# assert_success <command> [stdin]
assert_success() {
assert_raises "$1" 0 "${2:-}"
}

# assert_failure <command> [stdin]
assert_failure() {
(( tests_ran++ )) || :
[[ -z "$DISCOVERONLY" ]] || return
status=0
(eval "$1" <<< ${2:-}) > /dev/null 2>&1 || status=$?
if [[ "$status" != "0" ]]; then
[[ -z "$DEBUG" ]] || echo -n .
return
fi
_assert_fail "program terminated with a zero return code; expecting non-zero return code" \
"$1" "$2"
}

# assert_contains <command> <expected output...>
assert_contains() {
_assert_with_grep '-F' "$@"
}

# assert_matches <command> <expected output...>
assert_matches() {
_assert_with_grep '-E' "$@"
}

# assert_startswith <command> <expected start to stdout>
assert_startswith() {
assert_success "[[ '$($1)' == '$2'* ]]"
}

# assert_endswith <command> <expected start to stdout>
assert_endswith() {
assert_success "[[ '$($1)' == *'$2' ]]"
}

# _assert_with_grep <grep modifiers> <command> <expected output...>
_assert_with_grep() {
local grep_modifier="$1"
local output="$($2)"
shift 2

while [ $# != 0 ]; do
assert_raises "echo '$output' | $GREP $grep_modifier '$1'" 0 || return 1
shift
done
}

# Returns the resolved command, preferring any gnu-versions of the cmd (prefixed with 'g') on
# non-Linux systems such as Mac OS, and falling back to the standard version if not.
_cmd() {
local cmd="$1"

local gnu_cmd="g$cmd"
local gnu_cmd_found=$(which "$gnu_cmd" 2> /dev/null)
if [ "$gnu_cmd_found" ]; then
echo "$gnu_cmd_found"
else
if [ "$(uname)" == 'Darwin' ]; then
echo "Warning: Cannot find gnu version of command '$cmd' ($gnu_cmd) on path." \
"Falling back to standard command" >&2
fi
echo "cmd"
Copy link

@joseluis joseluis Dec 17, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be echo "$cmd".

Right now this doesn't work in linux. Because the command it tries to use is cmd instead of grep. Tests in test-extras.sh fails with:

test #1 "echo 'foo' | cmd -F 'foo'" failed:
	program terminated with code 127 instead of 0
(...)
7 of 11 assert_contains tests failed in 0.111s.
6 of 9 assert_matches tests failed in 0.093s.

fi
}

GREP=$(_cmd grep)
11 changes: 8 additions & 3 deletions assert.sh
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ assert_end() {
tests_endtime="$(date +%s%N)"
# required visible decimal place for seconds (leading zeros if needed)
local tests_time="$( \
printf "%010d" "$(( ${tests_endtime/%N/000000000}
printf "%010d" "$(( ${tests_endtime/%N/000000000}
- ${tests_starttime/%N/000000000} ))")" # in ns
tests="$tests_ran ${*:+$* }tests"
[[ -n "$DISCOVERONLY" ]] && echo "collected $tests." && _assert_reset && return
Expand All @@ -94,6 +94,8 @@ assert_end() {
echo "$tests_failed of $tests failed$report_time."
fi
tests_failed_previous=$tests_failed
tests_ran_total=$(($tests_ran_total + $tests_ran))
tests_failed_total=$(($tests_failed_total + $tests_failed))
[[ $tests_failed -gt 0 ]] && tests_suite_status=1
_assert_reset
}
Expand All @@ -103,7 +105,7 @@ assert() {
(( tests_ran++ )) || :
[[ -z "$DISCOVERONLY" ]] || return
expected=$(echo -ne "${2:-}")
result="$(eval 2>/dev/null $1 <<< ${3:-})" || true
result="$(eval 2>/dev/null "$1" <<< ${3:-})" || true
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrapping the command ($1) in quotes for both assert and assert_raises enables support for multiline input. See new tests added to test.sh to prove this.

if [[ "$result" == "$expected" ]]; then
[[ -z "$DEBUG" ]] || echo -n .
return
Expand All @@ -119,7 +121,7 @@ assert_raises() {
(( tests_ran++ )) || :
[[ -z "$DISCOVERONLY" ]] || return
status=0
(eval $1 <<< ${3:-}) > /dev/null 2>&1 || status=$?
(eval "$1" <<< ${3:-}) > /dev/null 2>&1 || status=$?
expected=${2:-0}
if [[ "$status" -eq "$expected" ]]; then
[[ -z "$DEBUG" ]] || echo -n .
Expand All @@ -139,6 +141,7 @@ _assert_fail() {
fi
tests_errors[$tests_failed]="$report"
(( tests_failed++ )) || :
return 1
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows us to check if an assertion has failed or not. For example we can now do:
assert_raises 'assert "echo foo" "bar"' 1
That's not exciting, but useful for testing the new assert_success assertion:
assert_raises 'assert_success "false"' 1

}

skip_if() {
Expand Down Expand Up @@ -178,6 +181,8 @@ _skip() {

_assert_reset
: ${tests_suite_status:=0} # remember if any of the tests failed so far
: ${tests_ran_total:=0} # remember the total number of tests ran (inc. failures)
: ${tests_failed_total:=0} # remember the total number of test failures
_assert_cleanup() {
local status=$?
# modify exit code if it's not already non-zero
Expand Down
122 changes: 122 additions & 0 deletions test-extras.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#!/usr/bin/env bash

source assert.sh
source assert-extras.sh

assert "echo foo" "foo"
assert_raises "true" 0
assert_raises "exit 127" 127

assert_end sanity

###
### assert_success tests
###

# Tests expecting success
assert_success "true"
assert_success "echo foo"
assert_success "cat" "foo"

# Tests expecting failure
assert_raises 'assert_success "false"' 1
assert_raises 'assert_success "exit 1"' 1

assert_end assert_success

###
### assert_failure tests
###

# Tests expecting success
assert_failure "false"
assert_failure "exit 1"
assert_failure "exit -1"
assert_failure "exit 42"
assert_failure "exit -42"

# Tests expecting failure
assert_raises 'assert_failure "true"' 1
assert_raises 'assert_failure "echo foo"' 1

assert_end assert_failure

###
### assert_contains tests
###

# Tests expecting success
assert_contains "echo foo" "foo"
assert_contains "echo foobar" "foo"
assert_contains "echo foo bar" "foo"
assert_contains "echo foo bar" "bar"
assert_contains "echo foo bar" "foo bar"

# Tests expecting failure
assert_failure 'assert_contains "echo foo" "foot"'
assert_failure 'assert_contains "echo foo" "f.."'

# Multi-word argument tests
assert_contains "echo foo bar" "foo bar"
assert_failure 'assert_contains "echo foo; echo bar" "foo bar"'

# Multi-argument tests
assert_contains "echo foo bar baz" "foo" "baz"
assert_failure 'assert_contains "echo foo bar baz" "bar" "foo baz"'

assert_end assert_contains

###
### assert_matches tests
###

# Tests expecting success
assert_matches "echo foo" "f.."
assert_matches "echo foobar" "f."
assert_matches "echo foo bar" "^foo bar$"
assert_matches "echo foo bar" "[az ]+"

# Tests expecting failure
assert_failure 'assert_matches "echo foot" "foo$"'

# Multi-word argument tests
assert_matches "echo foo bar" "foo .*"
assert_failure 'assert_matches "echo foo; echo bar" "foo .*"'

# Multi-argument tests
assert_matches "echo foo bar baz" "^f.." "baz$"
assert_failure 'assert_matches "echo foo bar baz" "bar" "foo baz"'

assert_end assert_matches

###
### assert_startswith tests
###

# Tests expecting success
assert_startswith "echo foo" "f"
assert_startswith "echo foo" "foo"
assert_startswith "echo foo; echo bar" "foo"

# Tests expecting failure
assert_failure 'assert_startswith "echo foo" "oo"'
assert_failure 'assert_startswith "echo foo; echo bar" "foo bar"'
assert_failure 'assert_startswith "echo foo" "."'

assert_end assert_startswith

###
### assert_endswith tests
###

# Tests expecting success
assert_endswith "echo foo" "oo"
assert_endswith "echo foo" "foo"
assert_endswith "echo foo; echo bar" "bar"

# Tests expecting failure
assert_failure 'assert_endswith "echo foo" "f"'
assert_failure 'assert_endswith "echo foo; echo bar" "foo bar"'
assert_failure 'assert_endswith "echo foo" "."'

assert_end assert_endswith
17 changes: 11 additions & 6 deletions tests.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#!/bin/bash

set -e

. assert.sh
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We remove this because failed assertions now return an rc of 1 which would stop this test script with set -e.


assert "echo" # no output expected
Expand Down Expand Up @@ -60,7 +58,7 @@ assert "_clean; skip_if false; assert_raises true; assert_end;" \
assert "_clean; skip_if bash -c 'exit 1'; assert_raises false; assert_end;" \
"all 0 tests passed."
# subshells and pipes can be used in skip as well (albeit escaped)
assert "_clean; skip_if 'cat /etc/passwd | grep \$(echo \$USER)';
assert "_clean; skip_if 'id | grep \$(echo \$USER)';
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also works on Mac OS X, as the user isn't guaranteed to be in /etc/passwd on Mac OS.

assert_raises false; assert_end;" \
"all 0 tests passed."
assert_end output
Expand Down Expand Up @@ -96,8 +94,9 @@ assert "_clean; x=0; assert 'x=1'; assert_raises 'x=2'; echo \$x" 0
assert "_clean; x=0; assert 'export x=1'; assert_raises 'export x=2';
echo \$x" 0
# options do not leak
assert_raises "set +e"
assert_raises "shopt -o errexit"
assert_raises "shopt -o errexit" 1
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of the removal of set -e above, set +e should be active now, so we first test, for that (shopt -o errexit returns 1 with +e and 0 with -e), then do a set -e inside an assertion and then we check it doesn't bleed through by making sure the next line set +e is still in effect.

assert_raises "set -e"
assert_raises "shopt -o errexit" 1
# skip properly resets all options
assert_raises "_clean; set +e; skip; assert_raises false; shopt -o errexit" 1
assert_raises "_clean; set -e; skip; assert_raises false; shopt -o errexit"
Expand Down Expand Up @@ -153,12 +152,18 @@ _date=22;
assert_end" "all 1 tests passed in 2.000s."
# commit: supported formatting codes
assert "echo %s" "%s"
assert "echo -n %s | wc -c" "2"
# We trim the output from wc as the BSD version (on Mac OS) contains leading spaces
assert "echo -n %s | wc -c | tr -d '[[:space:]]'" "2"
# date with no nanosecond support
date() { # date mock
echo "123N"
}
assert '_clean DEBUG=1 INVARIANT=; tests_starttime="0N"; assert_end' \
'\nall 0 tests passed in 123.000s.'
unset -f date # bring back original date
# commit: Supporting multiline inputs
# We trim the output from wc as the BSD version (on Mac OS) contains leading spaces
assert 'echo "this
is a multiline echo" | wc -l | tr -d "[[:space:]]"' "2"
assert 'echo -e "this\nis a multiline echo" | wc -l | tr -d "[[:space:]]"' "2"
assert_end regression