#!/usr/bin/env bash # # bash unit testing enterprise edition framework for professionals # Copyright (C) 2011-2016 Pascal Grange # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # # https://github.com/pgrange/bash_unit VERSION=v1.7.1 ESCAPE=$(printf "\033") NOCOLOR="${ESCAPE}[0m" RED="${ESCAPE}[91m" GREEN="${ESCAPE}[92m" YELLOW="${ESCAPE}[93m" BLUE="${ESCAPE}[94m" # Make bash_unit immune to some basic unix commands faking CAT="$(which cat)" SED="$(which sed)" GREP="$(which grep)" RM="$(which rm)" fail() { local message=${1:-} local stdout=${2:-} local stderr=${3:-} notify_test_failed "$__bash_unit_current_test__" "$message" [[ ! -z $stdout ]] && [ -s "$stdout" ] && notify_stdout < "$stdout" [[ ! -z $stderr ]] && [ -s "$stderr" ] && notify_stderr < "$stderr" stacktrace | notify_stack exit 1 } assert() { local assertion=$1 local message=${2:-} _assert_expression \ "$assertion" \ "[ \$status == 0 ]" \ "\"$message\"" } assert_fails() { local assertion=$1 local message=${2:-} _assert_expression \ "$assertion" \ "[ \$status != 0 ]" \ "\"$message\"" } assert_fail() { #deprecated, use assert_fails instead assert_fails "$@" } assert_status_code() { local expected_status=$1 local assertion="$2" local message="${3:-}" _assert_expression \ "$assertion" \ "[ \$status == $expected_status ]" \ "\"$message\" expected status code $expected_status but was \$status" } _assert_expression() { local assertion=$1 local condition=$2 local message=$3 ( local stdout=$(mktemp) local stderr=$(mktemp) trap "$RM -f \"$stdout\" \"$stderr\"" EXIT local status eval "($assertion)" >"$stdout" 2>"$stderr" && status=$? || status=$? if ! eval "$condition" then fail "$(eval echo $message)" "$stdout" "$stderr" fi ) || exit $? } assert_equals() { local expected=$1 local actual=$2 local message=${3:-} [[ -z $message ]] || message="$message\n" if [ "$expected" != "$actual" ] then fail "$message expected [$expected] but was [$actual]" fi } assert_not_equals() { local unexpected=$1 local actual=$2 local message=${3:-} [[ -z $message ]] || message="$message\n" [ "$unexpected" != "$actual" ] || \ fail "$message expected different value than [$unexpected] but was the same" } fake() { local command=$1 shift if [ $# -gt 0 ] then eval "function $command() { export FAKE_PARAMS=\"\$@\" ; $@ ; }" else eval "function $command() { echo \"$($CAT)\" ; }" fi export -f $command } stacktrace() { local i=1 while ! [ -z "${BASH_SOURCE[$i]:-}" ] do echo ${BASH_SOURCE[$i]}:${BASH_LINENO[$((i-1))]}:${FUNCNAME[$i]}\(\) i=$((i + 1)) done | "$GREP" -v "^$BASH_SOURCE" } run_test_suite() { local failure=0 declare -F | "$GREP" ' setup_suite$' >/dev/null && setup_suite for pending_test in $(set | "$GREP" -E '^(pending|todo).* \(\)' | "$GREP" -E "$test_pattern" | "$SED" -e 's: .*::') do notify_test_starting "$pending_test" notify_test_pending "$pending_test" done for test in $(set | "$GREP" -E '^test.* \(\)' | "$GREP" -E "$test_pattern" | "$SED" -e 's: .*::') do ( local status=0 declare -F | "$GREP" ' setup$' >/dev/null && setup (__bash_unit_current_test__="$test" run_test) || status=$? declare -F | "$GREP" ' teardown$' >/dev/null && teardown exit $status ) failure=$(( $? || failure)) done declare -F | "$GREP" ' teardown_suite$' >/dev/null && teardown_suite return $failure } run_test() { set -e notify_test_starting "$__bash_unit_current_test__" "$__bash_unit_current_test__" && notify_test_succeeded "$__bash_unit_current_test__" } usage() { echo "$1" >&2 echo "$0 [-f ] [-p ] [-p ]... ..." >&2 echo >&2 echo "Runs tests in test files that match s" >&2 echo " is optional only supported value is tap" >&2 echo "-v to get current version information" >&2 echo "See https://github.com/pgrange/bash_unit" >&2 exit 1 } # Formating pretty_success() { pretty_format "$GREEN" "\u2713" "${1:-}" } pretty_warning() { pretty_format "$YELLOW" "\u2717" "$1" } pretty_failure() { pretty_format "$RED" "\u2717" "${1:-}" } pretty_format() { local color="$1" local pretty_symbol="$2" local alt_symbol="$3" local term_utf8=false if is_terminal && [[ "$LANG" =~ .*UTF-8.* ]] then term_utf8=true fi ( $CAT if $term_utf8 then echo -en " $pretty_symbol " else [[ ! -z "$alt_symbol" ]] && echo -en " $alt_symbol " fi ) | color "$color" } color() { _start_color() { if is_terminal ; then echo -en "$color" ; fi } _stop_color() { if is_terminal ; then echo -en "$NOCOLOR" ; fi } local color=$1 shift _start_color if [ $# -gt 0 ] then echo $* else $CAT fi _stop_color } is_terminal() { [ -t 1 ] || [[ "${FORCE_COLOR}" == true ]] } text_format() { notify_suite_starting() { local test_file="$1" echo "Running tests in $test_file" } notify_test_starting() { local test="$1" echo -n "Running $test... " | color "$BLUE" } notify_test_pending() { echo -n "PENDING" | pretty_warning echo } notify_test_succeeded() { echo -n "SUCCESS" | pretty_success echo } notify_test_failed() { local message="$2" echo -n "FAILURE" | pretty_failure echo [[ -z $message ]] || printf -- "$message\n" } notify_stdout() { "$SED" 's:^:out> :' | color "$GREEN" } notify_stderr() { "$SED" 's:^:err> :' | color "$RED" } notify_stack() { color "$YELLOW" } } tap_format() { notify_suite_starting() { local test_file="$1" echo "# Running tests in $test_file" } notify_test_starting() { echo -n } notify_test_pending() { local test="$1" echo -n "ok" | pretty_warning - echo -n "$test" | color "$BLUE" echo " # skip test to be written" | color "$YELLOW" } notify_test_succeeded() { local test="$1" echo -n "ok" | pretty_success - echo "$test" | color "$BLUE" } notify_test_failed() { local test="$1" local message="$2" echo -n "not ok" | pretty_failure - echo "$test" | color "$BLUE" [[ -z $message ]] || printf -- "$message\n" | "$SED" -u -e 's/^/# /' } notify_stdout() { "$SED" 's:^:# out> :' | color "$GREEN" } notify_stderr() { "$SED" 's:^:# err> :' | color "$RED" } notify_stack() { "$SED" 's:^:# :' | color "$YELLOW" } } output_format=text test_pattern="" separator="" while getopts "vp:f:" option do case "$option" in p) test_pattern="${test_pattern}${separator}${OPTARG}" separator="|" ;; f) output_format="${OPTARG}" ;; v) echo "bash_unit $VERSION" exit ;; ?|:) usage ;; esac done shift $((OPTIND-1)) for test_file in "$@" do test -e "$test_file" || usage "file does not exist: $test_file" test -r "$test_file" || usage "can not read file: $test_file" done case "$output_format" in text) text_format ;; tap) tap_format ;; *) usage "unsupproted output format: $output_format" ;; esac #run tests received as parameters failure=0 for test_file in "$@" do notify_suite_starting "$test_file" ( set -e # Ensure bash_unit with exit with failure # in case of syntax error. if [[ "${STICK_TO_CWD}" != true ]] then cd "$(dirname "$test_file")" source "$(basename "$test_file")" else source "$test_file" fi set +e run_test_suite ) failure=$(( $? || failure)) done exit $failure