diff --git a/test/shunit2 b/test/shunit2 index 6239683a..fd7dc2c0 100755 --- a/test/shunit2 +++ b/test/shunit2 @@ -1,80 +1,57 @@ #! /bin/sh # vim:et:ft=sh:sts=2:sw=2 # -# Copyright 2008-2020 Kate Ward. All Rights Reserved. +# shUnit2 -- Unit testing framework for Unix shell scripts. +# +# Copyright 2008-2021 Kate Ward. All Rights Reserved. # Released under the Apache 2.0 license. # http://www.apache.org/licenses/LICENSE-2.0 # -# shUnit2 -- Unit testing framework for Unix shell scripts. -# https://github.com/kward/shunit2 -# # Author: kate.ward@forestent.com (Kate Ward) +# https://github.com/kward/shunit2 # # shUnit2 is a xUnit based unit test framework for Bourne shell scripts. It is # based on the popular JUnit unit testing framework for Java. # -# $() are not fully portable (POSIX != portable). -# shellcheck disable=SC2006 -# expr may be antiquated, but it is the only solution in some cases. +# `expr` may be antiquated, but it is the only solution in some cases. # shellcheck disable=SC2003 +# Allow usage of legacy backticked `...` notation instead of $(...). +# shellcheck disable=SC2006 # Return if shunit2 already loaded. -command [ -n "${SHUNIT_VERSION:-}" ] && exit 0 -SHUNIT_VERSION='2.1.8' +if test -n "${SHUNIT_VERSION:-}"; then + exit 0 +fi +SHUNIT_VERSION='2.1.9pre' # Return values that scripts can use. SHUNIT_TRUE=0 SHUNIT_FALSE=1 SHUNIT_ERROR=2 -# Logging functions. -_shunit_warn() { - ${__SHUNIT_CMD_ECHO_ESC} \ - "${__shunit_ansi_yellow}shunit2:WARN${__shunit_ansi_none} $*" >&2 -} -_shunit_error() { - ${__SHUNIT_CMD_ECHO_ESC} \ - "${__shunit_ansi_red}shunit2:ERROR${__shunit_ansi_none} $*" >&2 -} -_shunit_fatal() { - ${__SHUNIT_CMD_ECHO_ESC} \ - "${__shunit_ansi_red}shunit2:FATAL${__shunit_ansi_none} $*" >&2 - exit ${SHUNIT_ERROR} -} +# Determine if `builtin` command exists. +__SHUNIT_BUILTIN='builtin' +# shellcheck disable=2039 +if ! ("${__SHUNIT_BUILTIN}" echo 123 >/dev/null 2>&1); then + __SHUNIT_BUILTIN='' +fi # Determine some reasonable command defaults. __SHUNIT_CMD_ECHO_ESC='echo -e' -# shellcheck disable=SC2039 -command [ "`echo -e test`" = '-e test' ] && __SHUNIT_CMD_ECHO_ESC='echo' - -__SHUNIT_UNAME_S=`uname -s` -case "${__SHUNIT_UNAME_S}" in - BSD) __SHUNIT_CMD_EXPR='gexpr' ;; - *) __SHUNIT_CMD_EXPR='expr' ;; -esac -__SHUNIT_CMD_TPUT='tput' +# shellcheck disable=SC2039,SC3037 +if ${__SHUNIT_BUILTIN} [ "`echo -e test`" = '-e test' ]; then + __SHUNIT_CMD_ECHO_ESC='echo' +fi # Commands a user can override if needed. -SHUNIT_CMD_EXPR=${SHUNIT_CMD_EXPR:-${__SHUNIT_CMD_EXPR}} +__SHUNIT_CMD_TPUT='tput' SHUNIT_CMD_TPUT=${SHUNIT_CMD_TPUT:-${__SHUNIT_CMD_TPUT}} -# Enable color output. Options are 'never', 'always', or 'auto'. +# Enable color output. Options are 'auto', 'always', or 'never'. SHUNIT_COLOR=${SHUNIT_COLOR:-auto} -# Specific shell checks. -if command [ -n "${ZSH_VERSION:-}" ]; then - setopt |grep "^shwordsplit$" >/dev/null - if command [ $? -ne ${SHUNIT_TRUE} ]; then - _shunit_fatal 'zsh shwordsplit option is required for proper operation' - fi - if command [ -z "${SHUNIT_PARENT:-}" ]; then - _shunit_fatal "zsh does not pass \$0 through properly. please declare \ -\"SHUNIT_PARENT=\$0\" before calling shUnit2" - fi -fi - # -# Constants +# Internal constants. # __SHUNIT_MODE_SOURCED='sourced' @@ -92,22 +69,6 @@ __SHUNIT_ANSI_GREEN='\033[1;32m' __SHUNIT_ANSI_YELLOW='\033[1;33m' __SHUNIT_ANSI_CYAN='\033[1;36m' -# Set the constants readonly. -__shunit_constants=`set |grep '^__SHUNIT_' |cut -d= -f1` -echo "${__shunit_constants}" |grep '^Binary file' >/dev/null && \ - __shunit_constants=`set |grep -a '^__SHUNIT_' |cut -d= -f1` -for __shunit_const in ${__shunit_constants}; do - if command [ -z "${ZSH_VERSION:-}" ]; then - readonly "${__shunit_const}" - else - case ${ZSH_VERSION} in - [123].*) readonly "${__shunit_const}" ;; - *) readonly -g "${__shunit_const}" # Declare readonly constants globally. - esac - fi -done -unset __shunit_const __shunit_constants - # # Internal variables. # @@ -120,6 +81,13 @@ __shunit_script='' # Filename of unittest script (standalone mode). __shunit_skip=${SHUNIT_FALSE} # Is skipping enabled. __shunit_suite='' # Suite of tests to execute. __shunit_clean=${SHUNIT_FALSE} # _shunit_cleanup() was already called. +__shunit_suiteName='' # Text name of current test suite. +__shunit_xmlSuiteName='' # XML-ready text name of current test suite. + +# JUnit XML variables. +__shunit_junitXmlOutputFile='' # File to use for JUnit XML output in addition to stdout. +__shunit_junitXmlTestCases='' # Test cases info in the JUnit XML format for output +__shunit_junitXmlCurrentTestCaseErrors='' # Current test case error info in the JUnit XML format for output # ANSI colors (populated by _shunit_configureColor()). __shunit_ansi_none='' @@ -139,13 +107,65 @@ __shunit_assertsTotal=0 __shunit_assertsPassed=0 __shunit_assertsFailed=0 __shunit_assertsSkipped=0 +__shunit_assertsCurrentTest=0 + +# +# Internal functions. +# + +# Logging. +_shunit_warn() { + ${__SHUNIT_CMD_ECHO_ESC} "${__shunit_ansi_yellow}shunit2:WARN${__shunit_ansi_none} $*" >&2 +} +_shunit_error() { + ${__SHUNIT_CMD_ECHO_ESC} "${__shunit_ansi_red}shunit2:ERROR${__shunit_ansi_none} $*" >&2 +} +_shunit_fatal() { + ${__SHUNIT_CMD_ECHO_ESC} "${__shunit_ansi_red}shunit2:FATAL${__shunit_ansi_none} $*" >&2 + exit ${SHUNIT_ERROR} +} # # Macros. # # shellcheck disable=SC2016,SC2089 -_SHUNIT_LINENO_='eval __shunit_lineno=""; if command [ "${1:-}" = "--lineno" ]; then command [ -n "$2" ] && __shunit_lineno="[$2] "; shift 2; fi' +_SHUNIT_LINENO_='eval __shunit_lineno=""; if ${__SHUNIT_BUILTIN} [ "${1:-}" = "--lineno" ] && ${__SHUNIT_BUILTIN} [ -n "${2:-}" ]; then __shunit_lineno="[${2}]"; shift 2; fi;' + +# +# Setup. +# + +# Specific shell checks. +if ${__SHUNIT_BUILTIN} [ -n "${ZSH_VERSION:-}" ]; then + setopt |grep "^shwordsplit$" >/dev/null + if ${__SHUNIT_BUILTIN} [ $? -ne ${SHUNIT_TRUE} ]; then + _shunit_fatal 'zsh shwordsplit option is required for proper operation' + fi + if ${__SHUNIT_BUILTIN} [ -z "${SHUNIT_PARENT:-}" ]; then + _shunit_fatal "zsh does not pass \$0 through properly. please declare \ +\"SHUNIT_PARENT=\$0\" before calling shUnit2" + fi +fi + +# Set the constants readonly. +__shunit_constants=`set |grep '^__SHUNIT_' |cut -d= -f1` +echo "${__shunit_constants}" |grep '^Binary file' >/dev/null && \ + __shunit_constants=`set |grep -a '^__SHUNIT_' |cut -d= -f1` +for __shunit_const in ${__shunit_constants}; do + if ${__SHUNIT_BUILTIN} [ -z "${ZSH_VERSION:-}" ]; then + readonly "${__shunit_const}" + else + case ${ZSH_VERSION} in + [123].*) readonly "${__shunit_const}" ;; + *) + # Declare readonly constants globally. + # shellcheck disable=SC2039,SC3045 + readonly -g "${__shunit_const}" + esac + fi +done +unset __shunit_const __shunit_constants #----------------------------------------------------------------------------- # Assertion functions. @@ -162,15 +182,17 @@ _SHUNIT_LINENO_='eval __shunit_lineno=""; if command [ "${1:-}" = "--lineno" ]; assertEquals() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} - if command [ $# -lt 2 -o $# -gt 3 ]; then + if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then _shunit_error "assertEquals() requires two or three arguments; $# given" _shunit_assertFail return ${SHUNIT_ERROR} fi - _shunit_shouldSkip && return ${SHUNIT_TRUE} + if _shunit_shouldSkip; then + return ${SHUNIT_TRUE} + fi shunit_message_=${__shunit_lineno} - if command [ $# -eq 3 ]; then + if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then shunit_message_="${shunit_message_}$1" shift fi @@ -178,7 +200,7 @@ assertEquals() { shunit_actual_=$2 shunit_return=${SHUNIT_TRUE} - if command [ "${shunit_expected_}" = "${shunit_actual_}" ]; then + if ${__SHUNIT_BUILTIN} [ "${shunit_expected_}" = "${shunit_actual_}" ]; then _shunit_assertPass else failNotEquals "${shunit_message_}" "${shunit_expected_}" "${shunit_actual_}" @@ -202,15 +224,17 @@ _ASSERT_EQUALS_='eval assertEquals --lineno "${LINENO:-}"' assertNotEquals() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} - if command [ $# -lt 2 -o $# -gt 3 ]; then + if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then _shunit_error "assertNotEquals() requires two or three arguments; $# given" _shunit_assertFail return ${SHUNIT_ERROR} fi - _shunit_shouldSkip && return ${SHUNIT_TRUE} + if _shunit_shouldSkip; then + return ${SHUNIT_TRUE} + fi shunit_message_=${__shunit_lineno} - if command [ $# -eq 3 ]; then + if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then shunit_message_="${shunit_message_}$1" shift fi @@ -218,7 +242,7 @@ assertNotEquals() { shunit_actual_=$2 shunit_return=${SHUNIT_TRUE} - if command [ "${shunit_expected_}" != "${shunit_actual_}" ]; then + if ${__SHUNIT_BUILTIN} [ "${shunit_expected_}" != "${shunit_actual_}" ]; then _shunit_assertPass else failSame "${shunit_message_}" "${shunit_expected_}" "${shunit_actual_}" @@ -242,23 +266,24 @@ _ASSERT_NOT_EQUALS_='eval assertNotEquals --lineno "${LINENO:-}"' assertContains() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} - if command [ $# -lt 2 -o $# -gt 3 ]; then + if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then _shunit_error "assertContains() requires two or three arguments; $# given" _shunit_assertFail return ${SHUNIT_ERROR} fi - _shunit_shouldSkip && return ${SHUNIT_TRUE} + if _shunit_shouldSkip; then + return ${SHUNIT_TRUE} + fi shunit_message_=${__shunit_lineno} - if command [ $# -eq 3 ]; then + if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then shunit_message_="${shunit_message_}$1" shift fi shunit_container_=$1 shunit_content_=$2 - shunit_return=${SHUNIT_TRUE} - if echo "$shunit_container_" | grep -F -- "$shunit_content_" > /dev/null; then + if echo "${shunit_container_}" |grep -F -- "${shunit_content_}" >/dev/null; then _shunit_assertPass else failNotFound "${shunit_message_}" "${shunit_content_}" @@ -282,15 +307,17 @@ _ASSERT_CONTAINS_='eval assertContains --lineno "${LINENO:-}"' assertNotContains() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} - if command [ $# -lt 2 -o $# -gt 3 ]; then + if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then _shunit_error "assertNotContains() requires two or three arguments; $# given" _shunit_assertFail return ${SHUNIT_ERROR} fi - _shunit_shouldSkip && return ${SHUNIT_TRUE} + if _shunit_shouldSkip; then + return ${SHUNIT_TRUE} + fi shunit_message_=${__shunit_lineno} - if command [ $# -eq 3 ]; then + if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then shunit_message_="${shunit_message_}$1" shift fi @@ -298,7 +325,7 @@ assertNotContains() { shunit_content_=$2 shunit_return=${SHUNIT_TRUE} - if echo "$shunit_container_" | grep -F -- "$shunit_content_" > /dev/null; then + if echo "$shunit_container_" |grep -F -- "$shunit_content_" > /dev/null; then failFound "${shunit_message_}" "${shunit_content_}" shunit_return=${SHUNIT_FALSE} else @@ -311,7 +338,7 @@ assertNotContains() { # shellcheck disable=SC2016,SC2034 _ASSERT_NOT_CONTAINS_='eval assertNotContains --lineno "${LINENO:-}"' -# Assert that a value is null (i.e. an empty string) +# Assert that a value is null (i.e. an empty string). # # Args: # message: string: failure message [optional] @@ -321,19 +348,24 @@ _ASSERT_NOT_CONTAINS_='eval assertNotContains --lineno "${LINENO:-}"' assertNull() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} - if command [ $# -lt 1 -o $# -gt 2 ]; then + if ${__SHUNIT_BUILTIN} [ $# -gt 2 ]; then + # Allowing 0 arguments as $1 might actually be null. _shunit_error "assertNull() requires one or two arguments; $# given" _shunit_assertFail return ${SHUNIT_ERROR} fi - _shunit_shouldSkip && return ${SHUNIT_TRUE} + if _shunit_shouldSkip; then + return ${SHUNIT_TRUE} + fi shunit_message_=${__shunit_lineno} - if command [ $# -eq 2 ]; then + if ${__SHUNIT_BUILTIN} [ $# -eq 2 ]; then shunit_message_="${shunit_message_}$1" shift fi - assertTrue "${shunit_message_}" "[ -z '$1' ]" + + ${__SHUNIT_BUILTIN} test -z "${1:-}" + assertTrue "${shunit_message_}" $? shunit_return=$? unset shunit_message_ @@ -342,7 +374,7 @@ assertNull() { # shellcheck disable=SC2016,SC2034 _ASSERT_NULL_='eval assertNull --lineno "${LINENO:-}"' -# Assert that a value is not null (i.e. a non-empty string) +# Assert that a value is not null (i.e. a non-empty string). # # Args: # message: string: failure message [optional] @@ -352,24 +384,27 @@ _ASSERT_NULL_='eval assertNull --lineno "${LINENO:-}"' assertNotNull() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} - if command [ $# -gt 2 ]; then # allowing 0 arguments as $1 might actually be null + if ${__SHUNIT_BUILTIN} [ $# -gt 2 ]; then + # Allowing 0 arguments as $1 might actually be null. _shunit_error "assertNotNull() requires one or two arguments; $# given" _shunit_assertFail return ${SHUNIT_ERROR} fi - _shunit_shouldSkip && return ${SHUNIT_TRUE} + if _shunit_shouldSkip; then + return ${SHUNIT_TRUE} + fi shunit_message_=${__shunit_lineno} - if command [ $# -eq 2 ]; then + if ${__SHUNIT_BUILTIN} [ $# -eq 2 ]; then shunit_message_="${shunit_message_}$1" shift fi - shunit_actual_=`_shunit_escapeCharactersInString "${1:-}"` - test -n "${shunit_actual_}" + + ${__SHUNIT_BUILTIN} test -n "${1:-}" assertTrue "${shunit_message_}" $? shunit_return=$? - unset shunit_actual_ shunit_message_ + unset shunit_message_ return ${shunit_return} } # shellcheck disable=SC2016,SC2034 @@ -386,15 +421,17 @@ _ASSERT_NOT_NULL_='eval assertNotNull --lineno "${LINENO:-}"' assertSame() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} - if command [ $# -lt 2 -o $# -gt 3 ]; then + if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then _shunit_error "assertSame() requires two or three arguments; $# given" _shunit_assertFail return ${SHUNIT_ERROR} fi - _shunit_shouldSkip && return ${SHUNIT_TRUE} + if _shunit_shouldSkip; then + return ${SHUNIT_TRUE} + fi shunit_message_=${__shunit_lineno} - if command [ $# -eq 3 ]; then + if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then shunit_message_="${shunit_message_}$1" shift fi @@ -418,15 +455,17 @@ _ASSERT_SAME_='eval assertSame --lineno "${LINENO:-}"' assertNotSame() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} - if command [ $# -lt 2 -o $# -gt 3 ]; then + if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then _shunit_error "assertNotSame() requires two or three arguments; $# given" _shunit_assertFail return ${SHUNIT_ERROR} fi - _shunit_shouldSkip && return ${SHUNIT_TRUE} + if _shunit_shouldSkip; then + return ${SHUNIT_TRUE} + fi shunit_message_=${__shunit_lineno} - if command [ $# -eq 3 ]; then + if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then shunit_message_="${shunit_message_:-}$1" shift fi @@ -463,44 +502,48 @@ _ASSERT_NOT_SAME_='eval assertNotSame --lineno "${LINENO:-}"' assertTrue() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} - if command [ $# -lt 1 -o $# -gt 2 ]; then + if ${__SHUNIT_BUILTIN} [ $# -lt 1 -o $# -gt 2 ]; then _shunit_error "assertTrue() takes one or two arguments; $# given" _shunit_assertFail return ${SHUNIT_ERROR} fi - _shunit_shouldSkip && return ${SHUNIT_TRUE} + if _shunit_shouldSkip; then + return ${SHUNIT_TRUE} + fi shunit_message_=${__shunit_lineno} - if command [ $# -eq 2 ]; then + if ${__SHUNIT_BUILTIN} [ $# -eq 2 ]; then shunit_message_="${shunit_message_}$1" shift fi shunit_condition_=$1 # See if condition is an integer, i.e. a return value. - shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'` shunit_return=${SHUNIT_TRUE} - if command [ -z "${shunit_condition_}" ]; then + if ${__SHUNIT_BUILTIN} [ -z "${shunit_condition_}" ]; then # Null condition. shunit_return=${SHUNIT_FALSE} - elif command [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ] + elif (expr \( "${shunit_condition_}" + '0' \) '=' "${shunit_condition_}" >/dev/null 2>&1) then # Possible return value. Treating 0 as true, and non-zero as false. - command [ "${shunit_condition_}" -ne 0 ] && shunit_return=${SHUNIT_FALSE} + if ${__SHUNIT_BUILTIN} [ "${shunit_condition_}" -ne 0 ]; then + shunit_return=${SHUNIT_FALSE} + fi else # Hopefully... a condition. - ( eval "${shunit_condition_}" ) >/dev/null 2>&1 - command [ $? -ne 0 ] && shunit_return=${SHUNIT_FALSE} + if ! eval "${shunit_condition_}" >/dev/null 2>&1; then + shunit_return=${SHUNIT_FALSE} + fi fi # Record the test. - if command [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then + if ${__SHUNIT_BUILTIN} [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then _shunit_assertPass else _shunit_assertFail "${shunit_message_}" fi - unset shunit_message_ shunit_condition_ shunit_match_ + unset shunit_message_ shunit_condition_ return ${shunit_return} } # shellcheck disable=SC2016,SC2034 @@ -530,44 +573,48 @@ _ASSERT_TRUE_='eval assertTrue --lineno "${LINENO:-}"' assertFalse() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} - if command [ $# -lt 1 -o $# -gt 2 ]; then + if ${__SHUNIT_BUILTIN} [ $# -lt 1 -o $# -gt 2 ]; then _shunit_error "assertFalse() requires one or two arguments; $# given" _shunit_assertFail return ${SHUNIT_ERROR} fi - _shunit_shouldSkip && return ${SHUNIT_TRUE} + if _shunit_shouldSkip; then + return ${SHUNIT_TRUE} + fi shunit_message_=${__shunit_lineno} - if command [ $# -eq 2 ]; then + if ${__SHUNIT_BUILTIN} [ $# -eq 2 ]; then shunit_message_="${shunit_message_}$1" shift fi shunit_condition_=$1 # See if condition is an integer, i.e. a return value. - shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'` shunit_return=${SHUNIT_TRUE} - if command [ -z "${shunit_condition_}" ]; then + if ${__SHUNIT_BUILTIN} [ -z "${shunit_condition_}" ]; then # Null condition. - shunit_return=${SHUNIT_FALSE} - elif command [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ] - then + shunit_return=${SHUNIT_TRUE} + elif (expr \( "${shunit_condition_}" + '0' \) '=' "${shunit_condition_}" >/dev/null 2>&1); then # Possible return value. Treating 0 as true, and non-zero as false. - command [ "${shunit_condition_}" -eq 0 ] && shunit_return=${SHUNIT_FALSE} + if ${__SHUNIT_BUILTIN} [ "${shunit_condition_}" -eq 0 ]; then + shunit_return=${SHUNIT_FALSE} + fi else # Hopefully... a condition. - ( eval "${shunit_condition_}" ) >/dev/null 2>&1 - command [ $? -eq 0 ] && shunit_return=${SHUNIT_FALSE} + # shellcheck disable=SC2086 + if eval ${shunit_condition_} >/dev/null 2>&1; then + shunit_return=${SHUNIT_FALSE} + fi fi # Record the test. - if command [ "${shunit_return}" -eq "${SHUNIT_TRUE}" ]; then + if ${__SHUNIT_BUILTIN} [ "${shunit_return}" -eq "${SHUNIT_TRUE}" ]; then _shunit_assertPass else _shunit_assertFail "${shunit_message_}" fi - unset shunit_message_ shunit_condition_ shunit_match_ + unset shunit_message_ shunit_condition_ return "${shunit_return}" } # shellcheck disable=SC2016,SC2034 @@ -586,14 +633,16 @@ _ASSERT_FALSE_='eval assertFalse --lineno "${LINENO:-}"' fail() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} - if command [ $# -gt 1 ]; then + if ${__SHUNIT_BUILTIN} [ $# -gt 1 ]; then _shunit_error "fail() requires zero or one arguments; $# given" return ${SHUNIT_ERROR} fi - _shunit_shouldSkip && return ${SHUNIT_TRUE} + if _shunit_shouldSkip; then + return ${SHUNIT_TRUE} + fi shunit_message_=${__shunit_lineno} - if command [ $# -eq 1 ]; then + if ${__SHUNIT_BUILTIN} [ $# -eq 1 ]; then shunit_message_="${shunit_message_}$1" shift fi @@ -617,14 +666,16 @@ _FAIL_='eval fail --lineno "${LINENO:-}"' failNotEquals() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} - if command [ $# -lt 2 -o $# -gt 3 ]; then + if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then _shunit_error "failNotEquals() requires one or two arguments; $# given" return ${SHUNIT_ERROR} fi - _shunit_shouldSkip && return ${SHUNIT_TRUE} + if _shunit_shouldSkip; then + return ${SHUNIT_TRUE} + fi shunit_message_=${__shunit_lineno} - if command [ $# -eq 3 ]; then + if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then shunit_message_="${shunit_message_}$1" shift fi @@ -650,22 +701,25 @@ _FAIL_NOT_EQUALS_='eval failNotEquals --lineno "${LINENO:-}"' failFound() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} - if command [ $# -lt 1 -o $# -gt 2 ]; then + if ${__SHUNIT_BUILTIN} [ $# -lt 1 -o $# -gt 2 ]; then _shunit_error "failFound() requires one or two arguments; $# given" return ${SHUNIT_ERROR} fi - _shunit_shouldSkip && return ${SHUNIT_TRUE} + if _shunit_shouldSkip; then + return ${SHUNIT_TRUE} + fi shunit_message_=${__shunit_lineno} - if command [ $# -eq 2 ]; then + if ${__SHUNIT_BUILTIN} [ $# -eq 2 ]; then shunit_message_="${shunit_message_}$1" shift fi + shunit_content_=$1 shunit_message_=${shunit_message_%% } - _shunit_assertFail "${shunit_message_:+${shunit_message_} }Found" + _shunit_assertFail "${shunit_message_:+${shunit_message_} }found:<${shunit_content_}>" - unset shunit_message_ + unset shunit_message_ shunit_content_ return ${SHUNIT_FALSE} } # shellcheck disable=SC2016,SC2034 @@ -681,21 +735,23 @@ _FAIL_FOUND_='eval failFound --lineno "${LINENO:-}"' failNotFound() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} - if command [ $# -lt 1 -o $# -gt 2 ]; then + if ${__SHUNIT_BUILTIN} [ $# -lt 1 -o $# -gt 2 ]; then _shunit_error "failNotFound() requires one or two arguments; $# given" return ${SHUNIT_ERROR} fi - _shunit_shouldSkip && return ${SHUNIT_TRUE} + if _shunit_shouldSkip; then + return ${SHUNIT_TRUE} + fi shunit_message_=${__shunit_lineno} - if command [ $# -eq 2 ]; then + if ${__SHUNIT_BUILTIN} [ $# -eq 2 ]; then shunit_message_="${shunit_message_}$1" shift fi shunit_content_=$1 shunit_message_=${shunit_message_%% } - _shunit_assertFail "${shunit_message_:+${shunit_message_} }Not found:<${shunit_content_}>" + _shunit_assertFail "${shunit_message_:+${shunit_message_} }not found:<${shunit_content_}>" unset shunit_message_ shunit_content_ return ${SHUNIT_FALSE} @@ -711,18 +767,19 @@ _FAIL_NOT_FOUND_='eval failNotFound --lineno "${LINENO:-}"' # actual: string: actual value # Returns: # integer: success (TRUE/FALSE/ERROR constant) -failSame() -{ +failSame() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} - if command [ $# -lt 2 -o $# -gt 3 ]; then + if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then _shunit_error "failSame() requires two or three arguments; $# given" return ${SHUNIT_ERROR} fi - _shunit_shouldSkip && return ${SHUNIT_TRUE} + if _shunit_shouldSkip; then + return ${SHUNIT_TRUE} + fi shunit_message_=${__shunit_lineno} - if command [ $# -eq 3 ]; then + if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then shunit_message_="${shunit_message_}$1" shift fi @@ -749,14 +806,16 @@ _FAIL_SAME_='eval failSame --lineno "${LINENO:-}"' failNotSame() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} - if command [ $# -lt 2 -o $# -gt 3 ]; then + if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then _shunit_error "failNotSame() requires one or two arguments; $# given" return ${SHUNIT_ERROR} fi - _shunit_shouldSkip && return ${SHUNIT_TRUE} + if _shunit_shouldSkip; then + return ${SHUNIT_TRUE} + fi shunit_message_=${__shunit_lineno} - if command [ $# -eq 3 ]; then + if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then shunit_message_="${shunit_message_}$1" shift fi @@ -780,8 +839,11 @@ _FAIL_NOT_SAME_='eval failNotSame --lineno "${LINENO:-}"' # the total of asserts and fails will not be altered. # # Args: -# None -startSkipping() { __shunit_skip=${SHUNIT_TRUE}; } +# message: string: message to provide to user [optional] +startSkipping() { + if ${__SHUNIT_BUILTIN} [ $# -gt 0 ]; then _shunit_warn "[skipping] $*"; fi + __shunit_skip=${SHUNIT_TRUE} +} # Resume the normal recording behavior of assert and fail calls. # @@ -829,7 +891,7 @@ suite_addTest() { shunit_func_=${1:-} __shunit_suite="${__shunit_suite:+${__shunit_suite} }${shunit_func_}" - __shunit_testsTotal=`expr ${__shunit_testsTotal} + 1` + __shunit_testsTotal=`expr "${__shunit_testsTotal}" + 1` unset shunit_func_ } @@ -896,14 +958,15 @@ suite_addTest() { # string: the temporary directory that was created _shunit_mktempDir() { # Try the standard `mktemp` function. - ( exec mktemp -dqt shunit.XXXXXX 2>/dev/null ) && return + if ( exec mktemp -dqt shunit.XXXXXX 2>/dev/null ); then + return + fi # The standard `mktemp` didn't work. Use our own. - # shellcheck disable=SC2039 - if command [ -r '/dev/urandom' -a -x '/usr/bin/od' ]; then - _shunit_random_=`/usr/bin/od -vAn -N4 -tx4 /dev/null` - if command [ $? -eq 0 ]; then + if _shunit_tput_=`${SHUNIT_CMD_TPUT} colors 2>/dev/null`; then echo "${_shunit_tput_}" else echo 16 @@ -1044,33 +1107,45 @@ _shunit_execSuite() { for _shunit_test_ in ${__shunit_suite}; do __shunit_testSuccess=${SHUNIT_TRUE} + # Reset per-test info + __shunit_assertsCurrentTest=0 + __shunit_junitXmlCurrentTestCaseErrors='' + # Disable skipping. endSkipping - # Execute the per-test setup function. - setUp - command [ $? -eq ${SHUNIT_TRUE} ] \ - || _shunit_fatal "setup() returned non-zero return code." + # Execute the per-test setUp() function. + if ! setUp; then + _shunit_fatal "setUp() returned non-zero return code." + fi # Execute the test. echo "${__SHUNIT_TEST_PREFIX}${_shunit_test_}" - eval "${_shunit_test_}" - if command [ $? -ne ${SHUNIT_TRUE} ]; then + # shellcheck disable=SC2086 + if ! eval ${_shunit_test_}; then _shunit_error "${_shunit_test_}() returned non-zero return code." __shunit_testSuccess=${SHUNIT_ERROR} - _shunit_incFailedCount fi - # Execute the per-test tear-down function. - tearDown - command [ $? -eq ${SHUNIT_TRUE} ] \ - || _shunit_fatal "tearDown() returned non-zero return code." + # Execute the per-test tearDown() function. + if ! tearDown; then + _shunit_fatal "tearDown() returned non-zero return code." + fi + + # Store current test case info in JUnit XML. + __shunit_junitXmlTestCases="${__shunit_junitXmlTestCases} + ${__shunit_junitXmlCurrentTestCaseErrors} + " # Update stats. - if command [ ${__shunit_testSuccess} -eq ${SHUNIT_TRUE} ]; then - __shunit_testsPassed=`expr ${__shunit_testsPassed} + 1` + if ${__SHUNIT_BUILTIN} [ ${__shunit_testSuccess} -eq ${SHUNIT_TRUE} ]; then + __shunit_testsPassed=`expr "${__shunit_testsPassed}" + 1` else - __shunit_testsFailed=`expr ${__shunit_testsFailed} + 1` + __shunit_testsFailed=`expr "${__shunit_testsFailed}" + 1` fi done @@ -1084,32 +1159,49 @@ _shunit_execSuite() { # Output: # string: the report of successful and failed tests, as well as totals. _shunit_generateReport() { - command [ "${__shunit_reportGenerated}" -eq ${SHUNIT_TRUE} ] && return + if ${__SHUNIT_BUILTIN} [ "${__shunit_reportGenerated}" -eq ${SHUNIT_TRUE} ]; then + return + fi _shunit_ok_=${SHUNIT_TRUE} # If no exit code was provided, determine an appropriate one. - command [ "${__shunit_testsFailed}" -gt 0 \ - -o ${__shunit_testSuccess} -eq ${SHUNIT_FALSE} ] \ - && _shunit_ok_=${SHUNIT_FALSE} + if ${__SHUNIT_BUILTIN} [ "${__shunit_testsFailed}" -gt 0 -o ${__shunit_testSuccess} -eq ${SHUNIT_FALSE} ]; then + _shunit_ok_=${SHUNIT_FALSE} + fi echo _shunit_msg_="Ran ${__shunit_ansi_cyan}${__shunit_testsTotal}${__shunit_ansi_none}" - if command [ "${__shunit_testsTotal}" -eq 1 ]; then + if ${__SHUNIT_BUILTIN} [ "${__shunit_testsTotal}" -eq 1 ]; then ${__SHUNIT_CMD_ECHO_ESC} "${_shunit_msg_} test." else ${__SHUNIT_CMD_ECHO_ESC} "${_shunit_msg_} tests." fi - if command [ ${_shunit_ok_} -eq ${SHUNIT_TRUE} ]; then + if ${__SHUNIT_BUILTIN} [ -n "${__shunit_junitXmlOutputFile}" ]; then + echo " +${__shunit_junitXmlTestCases} +" > "${__shunit_junitXmlOutputFile}" + echo + echo "JUnit XML file ${__shunit_junitXmlOutputFile} was saved." + fi + + if ${__SHUNIT_BUILTIN} [ ${_shunit_ok_} -eq ${SHUNIT_TRUE} ]; then _shunit_msg_="${__shunit_ansi_green}OK${__shunit_ansi_none}" - command [ "${__shunit_assertsSkipped}" -gt 0 ] \ - && _shunit_msg_="${_shunit_msg_} (${__shunit_ansi_yellow}skipped=${__shunit_assertsSkipped}${__shunit_ansi_none})" + if ${__SHUNIT_BUILTIN} [ "${__shunit_assertsSkipped}" -gt 0 ]; then + _shunit_msg_="${_shunit_msg_} (${__shunit_ansi_yellow}skipped=${__shunit_assertsSkipped}${__shunit_ansi_none})" + fi else _shunit_msg_="${__shunit_ansi_red}FAILED${__shunit_ansi_none}" _shunit_msg_="${_shunit_msg_} (${__shunit_ansi_red}failures=${__shunit_assertsFailed}${__shunit_ansi_none}" - command [ "${__shunit_assertsSkipped}" -gt 0 ] \ - && _shunit_msg_="${_shunit_msg_},${__shunit_ansi_yellow}skipped=${__shunit_assertsSkipped}${__shunit_ansi_none}" + if ${__SHUNIT_BUILTIN} [ "${__shunit_assertsSkipped}" -gt 0 ]; then + _shunit_msg_="${_shunit_msg_},${__shunit_ansi_yellow}skipped=${__shunit_assertsSkipped}${__shunit_ansi_none}" + fi _shunit_msg_="${_shunit_msg_})" fi @@ -1127,7 +1219,9 @@ _shunit_generateReport() { # Returns: # boolean: whether the test should be skipped (TRUE/FALSE constant) _shunit_shouldSkip() { - command [ ${__shunit_skip} -eq ${SHUNIT_FALSE} ] && return ${SHUNIT_FALSE} + if ${__SHUNIT_BUILTIN} test ${__shunit_skip} -eq ${SHUNIT_FALSE}; then + return ${SHUNIT_FALSE} + fi _shunit_assertSkip } @@ -1136,8 +1230,9 @@ _shunit_shouldSkip() { # Args: # None _shunit_assertPass() { - __shunit_assertsPassed=`expr ${__shunit_assertsPassed} + 1` - __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1` + __shunit_assertsPassed=`expr "${__shunit_assertsPassed}" + 1` + __shunit_assertsTotal=`expr "${__shunit_assertsTotal}" + 1` + __shunit_assertsCurrentTest=`expr "${__shunit_assertsCurrentTest}" + 1` } # Records a test failure. @@ -1148,8 +1243,19 @@ _shunit_assertFail() { __shunit_testSuccess=${SHUNIT_FALSE} _shunit_incFailedCount - \[ $# -gt 0 ] && ${__SHUNIT_CMD_ECHO_ESC} \ - "${__shunit_ansi_red}ASSERT:${__shunit_ansi_none}$*" + _shunit_xml_message_="`_shunit_escapeXmlData "$@"`" + + __shunit_junitXmlCurrentTestCaseErrors="${__shunit_junitXmlCurrentTestCaseErrors} + " + + if ${__SHUNIT_BUILTIN} [ $# -gt 0 ]; then + ${__SHUNIT_CMD_ECHO_ESC} "${__shunit_ansi_red}ASSERT:${__shunit_ansi_none}$*" + fi + + unset _shunit_xml_message_ } # Increment the count of failed asserts. @@ -1159,9 +1265,9 @@ _shunit_assertFail() { _shunit_incFailedCount() { __shunit_assertsFailed=`expr "${__shunit_assertsFailed}" + 1` __shunit_assertsTotal=`expr "${__shunit_assertsTotal}" + 1` + __shunit_assertsCurrentTest=`expr "${__shunit_assertsCurrentTest}" + 1` } - # Records a skipped test. # # Args: @@ -1169,6 +1275,20 @@ _shunit_incFailedCount() { _shunit_assertSkip() { __shunit_assertsSkipped=`expr "${__shunit_assertsSkipped}" + 1` __shunit_assertsTotal=`expr "${__shunit_assertsTotal}" + 1` + __shunit_assertsCurrentTest=`expr "${__shunit_assertsCurrentTest}" + 1` +} + +# Dump the current test metrics. +# +# Args: +# none +_shunit_metrics() { + echo "< \ +total: ${__shunit_assertsTotal} \ +passed: ${__shunit_assertsPassed} \ +failed: ${__shunit_assertsFailed} \ +skipped: ${__shunit_assertsSkipped} \ +>" } # Prepare a script filename for sourcing. @@ -1186,49 +1306,6 @@ _shunit_prepForSourcing() { unset _shunit_script_ } -# Escape a character in a string. -# -# Args: -# c: string: unescaped character -# s: string: to escape character in -# Returns: -# string: with escaped character(s) -_shunit_escapeCharInStr() { - command [ -n "$2" ] || return # No point in doing work on an empty string. - - # Note: using shorter variable names to prevent conflicts with - # _shunit_escapeCharactersInString(). - _shunit_c_=$1 - _shunit_s_=$2 - - # Escape the character. - # shellcheck disable=SC1003,SC2086 - echo ''${_shunit_s_}'' |command sed 's/\'${_shunit_c_}'/\\\'${_shunit_c_}'/g' - - unset _shunit_c_ _shunit_s_ -} - -# Escape a character in a string. -# -# Args: -# str: string: to escape characters in -# Returns: -# string: with escaped character(s) -_shunit_escapeCharactersInString() { - command [ -n "$1" ] || return # No point in doing work on an empty string. - - _shunit_str_=$1 - - # Note: using longer variable names to prevent conflicts with - # _shunit_escapeCharInStr(). - for _shunit_char_ in '"' '$' "'" '`'; do - _shunit_str_=`_shunit_escapeCharInStr "${_shunit_char_}" "${_shunit_str_}"` - done - - echo "${_shunit_str_}" - unset _shunit_char_ _shunit_str_ -} - # Extract list of functions to run tests against. # # Args: @@ -1241,26 +1318,40 @@ _shunit_extractTestFunctions() { # Extract the lines with test function names, strip of anything besides the # function name, and output everything on a single line. _shunit_regex_='^\s*((function test[A-Za-z0-9_-]*)|(test[A-Za-z0-9_-]* *\(\)))' - # shellcheck disable=SC2196 - egrep "${_shunit_regex_}" "${_shunit_script_}" \ + grep -E "${_shunit_regex_}" "${_shunit_script_}" \ |command sed 's/^[^A-Za-z0-9_-]*//;s/^function //;s/\([A-Za-z0-9_-]*\).*/\1/g' \ |xargs unset _shunit_regex_ _shunit_script_ } +# Escape XML data. +# +# Args: +# data: string: data to escape +# Returns: +# string: escaped data +_shunit_escapeXmlData() { + # Required XML characters to escape are described here: + # http://www.w3.org/TR/REC-xml/#syntax + # https://www.liquid-technologies.com/Reference/Glossary/XML_EscapingData.html + echo "$*" \ + |command sed 's/&/\&/g;s//\>/g;s/"/\"/g'";s/'/\'/g" +} + #------------------------------------------------------------------------------ # Main. # # Determine the operating mode. -if command [ $# -eq 0 -o "${1:-}" = '--' ]; then +if ${__SHUNIT_BUILTIN} [ $# -eq 0 -o "${1:-}" = '--' ]; then __shunit_script=${__SHUNIT_PARENT} __shunit_mode=${__SHUNIT_MODE_SOURCED} else __shunit_script=$1 - command [ -r "${__shunit_script}" ] || \ - _shunit_fatal "unable to read from ${__shunit_script}" + if ! ${__SHUNIT_BUILTIN} [ -r "${__shunit_script}" ]; then + _shunit_fatal "unable to read from ${__shunit_script}" + fi __shunit_mode=${__SHUNIT_MODE_STANDALONE} fi @@ -1270,9 +1361,11 @@ __shunit_tmpDir=`_shunit_mktempDir` # Provide a public temporary directory for unit test scripts. # TODO(kward): document this. SHUNIT_TMPDIR="${__shunit_tmpDir}/tmp" -command mkdir "${SHUNIT_TMPDIR}" +if ! command mkdir "${SHUNIT_TMPDIR}"; then + _shunit_fatal "error creating SHUNIT_TMPDIR '${SHUNIT_TMPDIR}'" +fi -# Setup traps to clean up after ourselves. +# Configure traps to clean up after ourselves. trap '_shunit_cleanup EXIT' 0 trap '_shunit_cleanup INT' 2 trap '_shunit_cleanup TERM' 15 @@ -1288,28 +1381,46 @@ noexec 2>/dev/null || _shunit_fatal \ 'Please declare TMPDIR with path on partition with exec permission.' # We must manually source the tests in standalone mode. -if command [ "${__shunit_mode}" = "${__SHUNIT_MODE_STANDALONE}" ]; then +if ${__SHUNIT_BUILTIN} [ "${__shunit_mode}" = "${__SHUNIT_MODE_STANDALONE}" ]; then # shellcheck disable=SC1090 - command . "`_shunit_prepForSourcing \"${__shunit_script}\"`" + ${__SHUNIT_BUILTIN} . "`_shunit_prepForSourcing \"${__shunit_script}\"`" fi # Configure default output coloring behavior. _shunit_configureColor "${SHUNIT_COLOR}" # Execute the oneTimeSetUp function (if it exists). -oneTimeSetUp -command [ $? -eq ${SHUNIT_TRUE} ] \ - || _shunit_fatal "oneTimeSetUp() returned non-zero return code." +if ! oneTimeSetUp; then + _shunit_fatal "oneTimeSetUp() returned non-zero return code." +fi # Command line selected tests or suite selected tests -if command [ "$#" -ge 2 ]; then +if ${__SHUNIT_BUILTIN} [ "$#" -ge 2 ]; then # Argument $1 is either the filename of tests or '--'; either way, skip it. shift - # Remaining arguments ($2 .. $#) are assumed to be test function names. + # Remaining arguments ($2 .. $#) are assumed to be: + # - test function names. + # - configuration options, that is started with the `--` prefix. # Interate through all remaining args in "$@" in a POSIX (likely portable) way. # Helpful tip: https://unix.stackexchange.com/questions/314032/how-to-use-arguments-like-1-2-in-a-for-loop for _shunit_arg_ do - suite_addTest "${_shunit_arg_}" + case "${_shunit_arg_}" in + --output-junit-xml=*) + # It is a request for JUnit XML output. + __shunit_junitXmlOutputFile="${_shunit_arg_#--output-junit-xml=}" + ;; + --suite-name=*) + # It is a request for a custom suite name. + __shunit_suiteName="${_shunit_arg_#--suite-name=}" + ;; + --*) + _shunit_fatal "unrecognized option \"${_shunit_arg_}\"" + ;; + *) + # It is the test name, process it in a usual way. + suite_addTest "${_shunit_arg_}" + ;; + esac done unset _shunit_arg_ else @@ -1319,7 +1430,7 @@ else fi # If no tests or suite specified, dynamically build a list of functions. -if command [ -z "${__shunit_suite}" ]; then +if ${__SHUNIT_BUILTIN} [ -z "${__shunit_suite}" ]; then shunit_funcs_=`_shunit_extractTestFunctions "${__shunit_script}"` for shunit_func_ in ${shunit_funcs_}; do suite_addTest "${shunit_func_}" @@ -1327,17 +1438,26 @@ if command [ -z "${__shunit_suite}" ]; then fi unset shunit_func_ shunit_funcs_ +# If suite name is not defined, dynamically generate it from the script name. +if ${__SHUNIT_BUILTIN} [ -z "${__shunit_suiteName}" ]; then + __shunit_suiteName="${__shunit_script##*/}" +fi + +# Prepare the suite name for XML output. +__shunit_xmlSuiteName="`_shunit_escapeXmlData "${__shunit_suiteName}"`" + # Execute the suite of unit tests. _shunit_execSuite # Execute the oneTimeTearDown function (if it exists). -oneTimeTearDown -command [ $? -eq ${SHUNIT_TRUE} ] \ - || _shunit_fatal "oneTimeTearDown() returned non-zero return code." +if ! oneTimeTearDown; then + _shunit_fatal "oneTimeTearDown() returned non-zero return code." +fi # Generate a report summary. _shunit_generateReport # That's it folks. -command [ "${__shunit_testsFailed}" -eq 0 ] -exit $? +if ! ${__SHUNIT_BUILTIN} [ "${__shunit_testsFailed}" -eq 0 ]; then + return ${SHUNIT_FALSE} +fi