github.com/k8snetworkplumbingwg/sriov-network-operator@v1.2.1-0.20240408194816-2d2e5a45d453/test/scripts/shunit2 (about)

     1  #! /bin/sh
     2  # vim:et:ft=sh:sts=2:sw=2
     3  #
     4  # Copyright 2008-2020 Kate Ward. All Rights Reserved.
     5  # Released under the Apache 2.0 license.
     6  # http://www.apache.org/licenses/LICENSE-2.0
     7  #
     8  # shUnit2 -- Unit testing framework for Unix shell scripts.
     9  # https://github.com/kward/shunit2
    10  #
    11  # Author: kate.ward@forestent.com (Kate Ward)
    12  #
    13  # shUnit2 is a xUnit based unit test framework for Bourne shell scripts. It is
    14  # based on the popular JUnit unit testing framework for Java.
    15  #
    16  # $() are not fully portable (POSIX != portable).
    17  #   shellcheck disable=SC2006
    18  # expr may be antiquated, but it is the only solution in some cases.
    19  #   shellcheck disable=SC2003
    20  
    21  # Return if shunit2 already loaded.
    22  command [ -n "${SHUNIT_VERSION:-}" ] && exit 0
    23  SHUNIT_VERSION='2.1.8'
    24  
    25  # Return values that scripts can use.
    26  SHUNIT_TRUE=0
    27  SHUNIT_FALSE=1
    28  SHUNIT_ERROR=2
    29  
    30  # Logging functions.
    31  _shunit_warn() {
    32    ${__SHUNIT_CMD_ECHO_ESC} \
    33        "${__shunit_ansi_yellow}shunit2:WARN${__shunit_ansi_none} $*" >&2
    34  }
    35  _shunit_error() {
    36    ${__SHUNIT_CMD_ECHO_ESC} \
    37        "${__shunit_ansi_red}shunit2:ERROR${__shunit_ansi_none} $*" >&2
    38  }
    39  _shunit_fatal() {
    40    ${__SHUNIT_CMD_ECHO_ESC} \
    41        "${__shunit_ansi_red}shunit2:FATAL${__shunit_ansi_none} $*" >&2
    42    exit ${SHUNIT_ERROR}
    43  }
    44  
    45  # Determine some reasonable command defaults.
    46  __SHUNIT_CMD_ECHO_ESC='echo -e'
    47  # shellcheck disable=SC2039
    48  command [ "`echo -e test`" = '-e test' ] && __SHUNIT_CMD_ECHO_ESC='echo'
    49  
    50  __SHUNIT_UNAME_S=`uname -s`
    51  case "${__SHUNIT_UNAME_S}" in
    52    BSD) __SHUNIT_CMD_EXPR='gexpr' ;;
    53    *) __SHUNIT_CMD_EXPR='expr' ;;
    54  esac
    55  __SHUNIT_CMD_TPUT='tput'
    56  
    57  # Commands a user can override if needed.
    58  SHUNIT_CMD_EXPR=${SHUNIT_CMD_EXPR:-${__SHUNIT_CMD_EXPR}}
    59  SHUNIT_CMD_TPUT=${SHUNIT_CMD_TPUT:-${__SHUNIT_CMD_TPUT}}
    60  
    61  # Enable color output. Options are 'never', 'always', or 'auto'.
    62  SHUNIT_COLOR=${SHUNIT_COLOR:-auto}
    63  
    64  # Specific shell checks.
    65  if command [ -n "${ZSH_VERSION:-}" ]; then
    66    setopt |grep "^shwordsplit$" >/dev/null
    67    if command [ $? -ne ${SHUNIT_TRUE} ]; then
    68      _shunit_fatal 'zsh shwordsplit option is required for proper operation'
    69    fi
    70    if command [ -z "${SHUNIT_PARENT:-}" ]; then
    71      _shunit_fatal "zsh does not pass \$0 through properly. please declare \
    72  \"SHUNIT_PARENT=\$0\" before calling shUnit2"
    73    fi
    74  fi
    75  
    76  #
    77  # Constants
    78  #
    79  
    80  __SHUNIT_MODE_SOURCED='sourced'
    81  __SHUNIT_MODE_STANDALONE='standalone'
    82  __SHUNIT_PARENT=${SHUNIT_PARENT:-$0}
    83  
    84  # User provided test prefix to display in front of the name of the test being
    85  # executed. Define by setting the SHUNIT_TEST_PREFIX variable.
    86  __SHUNIT_TEST_PREFIX=${SHUNIT_TEST_PREFIX:-}
    87  
    88  # ANSI colors.
    89  __SHUNIT_ANSI_NONE='\033[0m'
    90  __SHUNIT_ANSI_RED='\033[1;31m'
    91  __SHUNIT_ANSI_GREEN='\033[1;32m'
    92  __SHUNIT_ANSI_YELLOW='\033[1;33m'
    93  __SHUNIT_ANSI_CYAN='\033[1;36m'
    94  
    95  # Set the constants readonly.
    96  __shunit_constants=`set |grep '^__SHUNIT_' |cut -d= -f1`
    97  echo "${__shunit_constants}" |grep '^Binary file' >/dev/null && \
    98      __shunit_constants=`set |grep -a '^__SHUNIT_' |cut -d= -f1`
    99  for __shunit_const in ${__shunit_constants}; do
   100    if command [ -z "${ZSH_VERSION:-}" ]; then
   101      readonly "${__shunit_const}"
   102    else
   103      case ${ZSH_VERSION} in
   104        [123].*) readonly "${__shunit_const}" ;;
   105        *) readonly -g "${__shunit_const}"  # Declare readonly constants globally.
   106      esac
   107    fi
   108  done
   109  unset __shunit_const __shunit_constants
   110  
   111  #
   112  # Internal variables.
   113  #
   114  
   115  # Variables.
   116  __shunit_lineno=''  # Line number of executed test.
   117  __shunit_mode=${__SHUNIT_MODE_SOURCED}  # Operating mode.
   118  __shunit_reportGenerated=${SHUNIT_FALSE}  # Is report generated.
   119  __shunit_script=''  # Filename of unittest script (standalone mode).
   120  __shunit_skip=${SHUNIT_FALSE}  # Is skipping enabled.
   121  __shunit_suite=''  # Suite of tests to execute.
   122  __shunit_clean=${SHUNIT_FALSE}  # _shunit_cleanup() was already called.
   123  
   124  # ANSI colors (populated by _shunit_configureColor()).
   125  __shunit_ansi_none=''
   126  __shunit_ansi_red=''
   127  __shunit_ansi_green=''
   128  __shunit_ansi_yellow=''
   129  __shunit_ansi_cyan=''
   130  
   131  # Counts of tests.
   132  __shunit_testSuccess=${SHUNIT_TRUE}
   133  __shunit_testsTotal=0
   134  __shunit_testsPassed=0
   135  __shunit_testsFailed=0
   136  
   137  # Counts of asserts.
   138  __shunit_assertsTotal=0
   139  __shunit_assertsPassed=0
   140  __shunit_assertsFailed=0
   141  __shunit_assertsSkipped=0
   142  
   143  #
   144  # Macros.
   145  #
   146  
   147  # shellcheck disable=SC2016,SC2089
   148  _SHUNIT_LINENO_='eval __shunit_lineno=""; if command [ "${1:-}" = "--lineno" ]; then command [ -n "$2" ] && __shunit_lineno="[$2] "; shift 2; fi'
   149  
   150  #-----------------------------------------------------------------------------
   151  # Assertion functions.
   152  #
   153  
   154  # Assert that two values are equal to one another.
   155  #
   156  # Args:
   157  #   message: string: failure message [optional]
   158  #   expected: string: expected value
   159  #   actual: string: actual value
   160  # Returns:
   161  #   integer: success (TRUE/FALSE/ERROR constant)
   162  assertEquals() {
   163    # shellcheck disable=SC2090
   164    ${_SHUNIT_LINENO_}
   165    if command [ $# -lt 2 -o $# -gt 3 ]; then
   166      _shunit_error "assertEquals() requires two or three arguments; $# given"
   167      _shunit_assertFail
   168      return ${SHUNIT_ERROR}
   169    fi
   170    _shunit_shouldSkip && return ${SHUNIT_TRUE}
   171  
   172    shunit_message_=${__shunit_lineno}
   173    if command [ $# -eq 3 ]; then
   174      shunit_message_="${shunit_message_}$1"
   175      shift
   176    fi
   177    shunit_expected_=$1
   178    shunit_actual_=$2
   179  
   180    shunit_return=${SHUNIT_TRUE}
   181    if command [ "${shunit_expected_}" = "${shunit_actual_}" ]; then
   182      _shunit_assertPass
   183    else
   184      failNotEquals "${shunit_message_}" "${shunit_expected_}" "${shunit_actual_}"
   185      shunit_return=${SHUNIT_FALSE}
   186    fi
   187  
   188    unset shunit_message_ shunit_expected_ shunit_actual_
   189    return ${shunit_return}
   190  }
   191  # shellcheck disable=SC2016,SC2034
   192  _ASSERT_EQUALS_='eval assertEquals --lineno "${LINENO:-}"'
   193  
   194  # Assert that two values are not equal to one another.
   195  #
   196  # Args:
   197  #   message: string: failure message [optional]
   198  #   expected: string: expected value
   199  #   actual: string: actual value
   200  # Returns:
   201  #   integer: success (TRUE/FALSE/ERROR constant)
   202  assertNotEquals() {
   203    # shellcheck disable=SC2090
   204    ${_SHUNIT_LINENO_}
   205    if command [ $# -lt 2 -o $# -gt 3 ]; then
   206      _shunit_error "assertNotEquals() requires two or three arguments; $# given"
   207      _shunit_assertFail
   208      return ${SHUNIT_ERROR}
   209    fi
   210    _shunit_shouldSkip && return ${SHUNIT_TRUE}
   211  
   212    shunit_message_=${__shunit_lineno}
   213    if command [ $# -eq 3 ]; then
   214      shunit_message_="${shunit_message_}$1"
   215      shift
   216    fi
   217    shunit_expected_=$1
   218    shunit_actual_=$2
   219  
   220    shunit_return=${SHUNIT_TRUE}
   221    if command [ "${shunit_expected_}" != "${shunit_actual_}" ]; then
   222      _shunit_assertPass
   223    else
   224      failSame "${shunit_message_}" "${shunit_expected_}" "${shunit_actual_}"
   225      shunit_return=${SHUNIT_FALSE}
   226    fi
   227  
   228    unset shunit_message_ shunit_expected_ shunit_actual_
   229    return ${shunit_return}
   230  }
   231  # shellcheck disable=SC2016,SC2034
   232  _ASSERT_NOT_EQUALS_='eval assertNotEquals --lineno "${LINENO:-}"'
   233  
   234  # Assert that a container contains a content.
   235  #
   236  # Args:
   237  #   message: string: failure message [optional]
   238  #   container: string: container to analyze
   239  #   content: string: content to find
   240  # Returns:
   241  #   integer: success (TRUE/FALSE/ERROR constant)
   242  assertContains() {
   243    # shellcheck disable=SC2090
   244    ${_SHUNIT_LINENO_}
   245    if command [ $# -lt 2 -o $# -gt 3 ]; then
   246      _shunit_error "assertContains() requires two or three arguments; $# given"
   247      _shunit_assertFail
   248      return ${SHUNIT_ERROR}
   249    fi
   250    _shunit_shouldSkip && return ${SHUNIT_TRUE}
   251  
   252    shunit_message_=${__shunit_lineno}
   253    if command [ $# -eq 3 ]; then
   254      shunit_message_="${shunit_message_}$1"
   255      shift
   256    fi
   257    shunit_container_=$1
   258    shunit_content_=$2
   259  
   260    shunit_return=${SHUNIT_TRUE}
   261    if echo "$shunit_container_" | grep -F -- "$shunit_content_" > /dev/null; then
   262      _shunit_assertPass
   263    else
   264      failNotFound "${shunit_message_}" "${shunit_content_}"
   265      shunit_return=${SHUNIT_FALSE}
   266    fi
   267  
   268    unset shunit_message_ shunit_container_ shunit_content_
   269    return ${shunit_return}
   270  }
   271  # shellcheck disable=SC2016,SC2034
   272  _ASSERT_CONTAINS_='eval assertContains --lineno "${LINENO:-}"'
   273  
   274  # Assert that a container does not contain a content.
   275  #
   276  # Args:
   277  #   message: string: failure message [optional]
   278  #   container: string: container to analyze
   279  #   content: string: content to look for
   280  # Returns:
   281  #   integer: success (TRUE/FALSE/ERROR constant)
   282  assertNotContains() {
   283    # shellcheck disable=SC2090
   284    ${_SHUNIT_LINENO_}
   285    if command [ $# -lt 2 -o $# -gt 3 ]; then
   286      _shunit_error "assertNotContains() requires two or three arguments; $# given"
   287      _shunit_assertFail
   288      return ${SHUNIT_ERROR}
   289    fi
   290    _shunit_shouldSkip && return ${SHUNIT_TRUE}
   291  
   292    shunit_message_=${__shunit_lineno}
   293    if command [ $# -eq 3 ]; then
   294      shunit_message_="${shunit_message_}$1"
   295      shift
   296    fi
   297    shunit_container_=$1
   298    shunit_content_=$2
   299  
   300    shunit_return=${SHUNIT_TRUE}
   301    if echo "$shunit_container_" | grep -F -- "$shunit_content_" > /dev/null; then
   302      failFound "${shunit_message_}" "${shunit_content_}"
   303      shunit_return=${SHUNIT_FALSE}
   304    else
   305      _shunit_assertPass
   306    fi
   307  
   308    unset shunit_message_ shunit_container_ shunit_content_
   309    return ${shunit_return}
   310  }
   311  # shellcheck disable=SC2016,SC2034
   312  _ASSERT_NOT_CONTAINS_='eval assertNotContains --lineno "${LINENO:-}"'
   313  
   314  # Assert that a value is null (i.e. an empty string)
   315  #
   316  # Args:
   317  #   message: string: failure message [optional]
   318  #   actual: string: actual value
   319  # Returns:
   320  #   integer: success (TRUE/FALSE/ERROR constant)
   321  assertNull() {
   322    # shellcheck disable=SC2090
   323    ${_SHUNIT_LINENO_}
   324    if command [ $# -lt 1 -o $# -gt 2 ]; then
   325      _shunit_error "assertNull() requires one or two arguments; $# given"
   326      _shunit_assertFail
   327      return ${SHUNIT_ERROR}
   328    fi
   329    _shunit_shouldSkip && return ${SHUNIT_TRUE}
   330  
   331    shunit_message_=${__shunit_lineno}
   332    if command [ $# -eq 2 ]; then
   333      shunit_message_="${shunit_message_}$1"
   334      shift
   335    fi
   336    assertTrue "${shunit_message_}" "[ -z '$1' ]"
   337    shunit_return=$?
   338  
   339    unset shunit_message_
   340    return ${shunit_return}
   341  }
   342  # shellcheck disable=SC2016,SC2034
   343  _ASSERT_NULL_='eval assertNull --lineno "${LINENO:-}"'
   344  
   345  # Assert that a value is not null (i.e. a non-empty string)
   346  #
   347  # Args:
   348  #   message: string: failure message [optional]
   349  #   actual: string: actual value
   350  # Returns:
   351  #   integer: success (TRUE/FALSE/ERROR constant)
   352  assertNotNull() {
   353    # shellcheck disable=SC2090
   354    ${_SHUNIT_LINENO_}
   355    if command [ $# -gt 2 ]; then  # allowing 0 arguments as $1 might actually be null
   356      _shunit_error "assertNotNull() requires one or two arguments; $# given"
   357      _shunit_assertFail
   358      return ${SHUNIT_ERROR}
   359    fi
   360    _shunit_shouldSkip && return ${SHUNIT_TRUE}
   361  
   362    shunit_message_=${__shunit_lineno}
   363    if command [ $# -eq 2 ]; then
   364      shunit_message_="${shunit_message_}$1"
   365      shift
   366    fi
   367    shunit_actual_=`_shunit_escapeCharactersInString "${1:-}"`
   368    test -n "${shunit_actual_}"
   369    assertTrue "${shunit_message_}" $?
   370    shunit_return=$?
   371  
   372    unset shunit_actual_ shunit_message_
   373    return ${shunit_return}
   374  }
   375  # shellcheck disable=SC2016,SC2034
   376  _ASSERT_NOT_NULL_='eval assertNotNull --lineno "${LINENO:-}"'
   377  
   378  # Assert that two values are the same (i.e. equal to one another).
   379  #
   380  # Args:
   381  #   message: string: failure message [optional]
   382  #   expected: string: expected value
   383  #   actual: string: actual value
   384  # Returns:
   385  #   integer: success (TRUE/FALSE/ERROR constant)
   386  assertSame() {
   387    # shellcheck disable=SC2090
   388    ${_SHUNIT_LINENO_}
   389    if command [ $# -lt 2 -o $# -gt 3 ]; then
   390      _shunit_error "assertSame() requires two or three arguments; $# given"
   391      _shunit_assertFail
   392      return ${SHUNIT_ERROR}
   393    fi
   394    _shunit_shouldSkip && return ${SHUNIT_TRUE}
   395  
   396    shunit_message_=${__shunit_lineno}
   397    if command [ $# -eq 3 ]; then
   398      shunit_message_="${shunit_message_}$1"
   399      shift
   400    fi
   401    assertEquals "${shunit_message_}" "$1" "$2"
   402    shunit_return=$?
   403  
   404    unset shunit_message_
   405    return ${shunit_return}
   406  }
   407  # shellcheck disable=SC2016,SC2034
   408  _ASSERT_SAME_='eval assertSame --lineno "${LINENO:-}"'
   409  
   410  # Assert that two values are not the same (i.e. not equal to one another).
   411  #
   412  # Args:
   413  #   message: string: failure message [optional]
   414  #   expected: string: expected value
   415  #   actual: string: actual value
   416  # Returns:
   417  #   integer: success (TRUE/FALSE/ERROR constant)
   418  assertNotSame() {
   419    # shellcheck disable=SC2090
   420    ${_SHUNIT_LINENO_}
   421    if command [ $# -lt 2 -o $# -gt 3 ]; then
   422      _shunit_error "assertNotSame() requires two or three arguments; $# given"
   423      _shunit_assertFail
   424      return ${SHUNIT_ERROR}
   425    fi
   426    _shunit_shouldSkip && return ${SHUNIT_TRUE}
   427  
   428    shunit_message_=${__shunit_lineno}
   429    if command [ $# -eq 3 ]; then
   430      shunit_message_="${shunit_message_:-}$1"
   431      shift
   432    fi
   433    assertNotEquals "${shunit_message_}" "$1" "$2"
   434    shunit_return=$?
   435  
   436    unset shunit_message_
   437    return ${shunit_return}
   438  }
   439  # shellcheck disable=SC2016,SC2034
   440  _ASSERT_NOT_SAME_='eval assertNotSame --lineno "${LINENO:-}"'
   441  
   442  # Assert that a value or shell test condition is true.
   443  #
   444  # In shell, a value of 0 is true and a non-zero value is false. Any integer
   445  # value passed can thereby be tested.
   446  #
   447  # Shell supports much more complicated tests though, and a means to support
   448  # them was needed. As such, this function tests that conditions are true or
   449  # false through evaluation rather than just looking for a true or false.
   450  #
   451  # The following test will succeed:
   452  #   assertTrue 0
   453  #   assertTrue "[ 34 -gt 23 ]"
   454  # The following test will fail with a message:
   455  #   assertTrue 123
   456  #   assertTrue "test failed" "[ -r '/non/existent/file' ]"
   457  #
   458  # Args:
   459  #   message: string: failure message [optional]
   460  #   condition: string: integer value or shell conditional statement
   461  # Returns:
   462  #   integer: success (TRUE/FALSE/ERROR constant)
   463  assertTrue() {
   464    # shellcheck disable=SC2090
   465    ${_SHUNIT_LINENO_}
   466    if command [ $# -lt 1 -o $# -gt 2 ]; then
   467      _shunit_error "assertTrue() takes one or two arguments; $# given"
   468      _shunit_assertFail
   469      return ${SHUNIT_ERROR}
   470    fi
   471    _shunit_shouldSkip && return ${SHUNIT_TRUE}
   472  
   473    shunit_message_=${__shunit_lineno}
   474    if command [ $# -eq 2 ]; then
   475      shunit_message_="${shunit_message_}$1"
   476      shift
   477    fi
   478    shunit_condition_=$1
   479  
   480    # See if condition is an integer, i.e. a return value.
   481    shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'`
   482    shunit_return=${SHUNIT_TRUE}
   483    if command [ -z "${shunit_condition_}" ]; then
   484      # Null condition.
   485      shunit_return=${SHUNIT_FALSE}
   486    elif command [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ]
   487    then
   488      # Possible return value. Treating 0 as true, and non-zero as false.
   489      command [ "${shunit_condition_}" -ne 0 ] && shunit_return=${SHUNIT_FALSE}
   490    else
   491      # Hopefully... a condition.
   492      ( eval "${shunit_condition_}" ) >/dev/null 2>&1
   493      command [ $? -ne 0 ] && shunit_return=${SHUNIT_FALSE}
   494    fi
   495  
   496    # Record the test.
   497    if command [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then
   498      _shunit_assertPass
   499    else
   500      _shunit_assertFail "${shunit_message_}"
   501    fi
   502  
   503    unset shunit_message_ shunit_condition_ shunit_match_
   504    return ${shunit_return}
   505  }
   506  # shellcheck disable=SC2016,SC2034
   507  _ASSERT_TRUE_='eval assertTrue --lineno "${LINENO:-}"'
   508  
   509  # Assert that a value or shell test condition is false.
   510  #
   511  # In shell, a value of 0 is true and a non-zero value is false. Any integer
   512  # value passed can thereby be tested.
   513  #
   514  # Shell supports much more complicated tests though, and a means to support
   515  # them was needed. As such, this function tests that conditions are true or
   516  # false through evaluation rather than just looking for a true or false.
   517  #
   518  # The following test will succeed:
   519  #   assertFalse 1
   520  #   assertFalse "[ 'apples' = 'oranges' ]"
   521  # The following test will fail with a message:
   522  #   assertFalse 0
   523  #   assertFalse "test failed" "[ 1 -eq 1 -a 2 -eq 2 ]"
   524  #
   525  # Args:
   526  #   message: string: failure message [optional]
   527  #   condition: string: integer value or shell conditional statement
   528  # Returns:
   529  #   integer: success (TRUE/FALSE/ERROR constant)
   530  assertFalse() {
   531    # shellcheck disable=SC2090
   532    ${_SHUNIT_LINENO_}
   533    if command [ $# -lt 1 -o $# -gt 2 ]; then
   534      _shunit_error "assertFalse() requires one or two arguments; $# given"
   535      _shunit_assertFail
   536      return ${SHUNIT_ERROR}
   537    fi
   538    _shunit_shouldSkip && return ${SHUNIT_TRUE}
   539  
   540    shunit_message_=${__shunit_lineno}
   541    if command [ $# -eq 2 ]; then
   542      shunit_message_="${shunit_message_}$1"
   543      shift
   544    fi
   545    shunit_condition_=$1
   546  
   547    # See if condition is an integer, i.e. a return value.
   548    shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'`
   549    shunit_return=${SHUNIT_TRUE}
   550    if command [ -z "${shunit_condition_}" ]; then
   551      # Null condition.
   552      shunit_return=${SHUNIT_FALSE}
   553    elif command [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ]
   554    then
   555      # Possible return value. Treating 0 as true, and non-zero as false.
   556      command [ "${shunit_condition_}" -eq 0 ] && shunit_return=${SHUNIT_FALSE}
   557    else
   558      # Hopefully... a condition.
   559      ( eval "${shunit_condition_}" ) >/dev/null 2>&1
   560      command [ $? -eq 0 ] && shunit_return=${SHUNIT_FALSE}
   561    fi
   562  
   563    # Record the test.
   564    if command [ "${shunit_return}" -eq "${SHUNIT_TRUE}" ]; then
   565      _shunit_assertPass
   566    else
   567      _shunit_assertFail "${shunit_message_}"
   568    fi
   569  
   570    unset shunit_message_ shunit_condition_ shunit_match_
   571    return "${shunit_return}"
   572  }
   573  # shellcheck disable=SC2016,SC2034
   574  _ASSERT_FALSE_='eval assertFalse --lineno "${LINENO:-}"'
   575  
   576  #-----------------------------------------------------------------------------
   577  # Failure functions.
   578  #
   579  
   580  # Records a test failure.
   581  #
   582  # Args:
   583  #   message: string: failure message [optional]
   584  # Returns:
   585  #   integer: success (TRUE/FALSE/ERROR constant)
   586  fail() {
   587    # shellcheck disable=SC2090
   588    ${_SHUNIT_LINENO_}
   589    if command [ $# -gt 1 ]; then
   590      _shunit_error "fail() requires zero or one arguments; $# given"
   591      return ${SHUNIT_ERROR}
   592    fi
   593    _shunit_shouldSkip && return ${SHUNIT_TRUE}
   594  
   595    shunit_message_=${__shunit_lineno}
   596    if command [ $# -eq 1 ]; then
   597      shunit_message_="${shunit_message_}$1"
   598      shift
   599    fi
   600  
   601    _shunit_assertFail "${shunit_message_}"
   602  
   603    unset shunit_message_
   604    return ${SHUNIT_FALSE}
   605  }
   606  # shellcheck disable=SC2016,SC2034
   607  _FAIL_='eval fail --lineno "${LINENO:-}"'
   608  
   609  # Records a test failure, stating two values were not equal.
   610  #
   611  # Args:
   612  #   message: string: failure message [optional]
   613  #   expected: string: expected value
   614  #   actual: string: actual value
   615  # Returns:
   616  #   integer: success (TRUE/FALSE/ERROR constant)
   617  failNotEquals() {
   618    # shellcheck disable=SC2090
   619    ${_SHUNIT_LINENO_}
   620    if command [ $# -lt 2 -o $# -gt 3 ]; then
   621      _shunit_error "failNotEquals() requires one or two arguments; $# given"
   622      return ${SHUNIT_ERROR}
   623    fi
   624    _shunit_shouldSkip && return ${SHUNIT_TRUE}
   625  
   626    shunit_message_=${__shunit_lineno}
   627    if command [ $# -eq 3 ]; then
   628      shunit_message_="${shunit_message_}$1"
   629      shift
   630    fi
   631    shunit_expected_=$1
   632    shunit_actual_=$2
   633  
   634    shunit_message_=${shunit_message_%% }
   635    _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected:<${shunit_expected_}> but was:<${shunit_actual_}>"
   636  
   637    unset shunit_message_ shunit_expected_ shunit_actual_
   638    return ${SHUNIT_FALSE}
   639  }
   640  # shellcheck disable=SC2016,SC2034
   641  _FAIL_NOT_EQUALS_='eval failNotEquals --lineno "${LINENO:-}"'
   642  
   643  # Records a test failure, stating a value was found.
   644  #
   645  # Args:
   646  #   message: string: failure message [optional]
   647  #   content: string: found value
   648  # Returns:
   649  #   integer: success (TRUE/FALSE/ERROR constant)
   650  failFound() {
   651    # shellcheck disable=SC2090
   652    ${_SHUNIT_LINENO_}
   653    if command [ $# -lt 1 -o $# -gt 2 ]; then
   654      _shunit_error "failFound() requires one or two arguments; $# given"
   655      return ${SHUNIT_ERROR}
   656    fi
   657    _shunit_shouldSkip && return ${SHUNIT_TRUE}
   658  
   659    shunit_message_=${__shunit_lineno}
   660    if command [ $# -eq 2 ]; then
   661      shunit_message_="${shunit_message_}$1"
   662      shift
   663    fi
   664  
   665    shunit_message_=${shunit_message_%% }
   666    _shunit_assertFail "${shunit_message_:+${shunit_message_} }Found"
   667  
   668    unset shunit_message_
   669    return ${SHUNIT_FALSE}
   670  }
   671  # shellcheck disable=SC2016,SC2034
   672  _FAIL_FOUND_='eval failFound --lineno "${LINENO:-}"'
   673  
   674  # Records a test failure, stating a content was not found.
   675  #
   676  # Args:
   677  #   message: string: failure message [optional]
   678  #   content: string: content not found
   679  # Returns:
   680  #   integer: success (TRUE/FALSE/ERROR constant)
   681  failNotFound() {
   682    # shellcheck disable=SC2090
   683    ${_SHUNIT_LINENO_}
   684    if command [ $# -lt 1 -o $# -gt 2 ]; then
   685      _shunit_error "failNotFound() requires one or two arguments; $# given"
   686      return ${SHUNIT_ERROR}
   687    fi
   688    _shunit_shouldSkip && return ${SHUNIT_TRUE}
   689  
   690    shunit_message_=${__shunit_lineno}
   691    if command [ $# -eq 2 ]; then
   692      shunit_message_="${shunit_message_}$1"
   693      shift
   694    fi
   695    shunit_content_=$1
   696  
   697    shunit_message_=${shunit_message_%% }
   698    _shunit_assertFail "${shunit_message_:+${shunit_message_} }Not found:<${shunit_content_}>"
   699  
   700    unset shunit_message_ shunit_content_
   701    return ${SHUNIT_FALSE}
   702  }
   703  # shellcheck disable=SC2016,SC2034
   704  _FAIL_NOT_FOUND_='eval failNotFound --lineno "${LINENO:-}"'
   705  
   706  # Records a test failure, stating two values should have been the same.
   707  #
   708  # Args:
   709  #   message: string: failure message [optional]
   710  #   expected: string: expected value
   711  #   actual: string: actual value
   712  # Returns:
   713  #   integer: success (TRUE/FALSE/ERROR constant)
   714  failSame()
   715  {
   716    # shellcheck disable=SC2090
   717    ${_SHUNIT_LINENO_}
   718    if command [ $# -lt 2 -o $# -gt 3 ]; then
   719      _shunit_error "failSame() requires two or three arguments; $# given"
   720      return ${SHUNIT_ERROR}
   721    fi
   722    _shunit_shouldSkip && return ${SHUNIT_TRUE}
   723  
   724    shunit_message_=${__shunit_lineno}
   725    if command [ $# -eq 3 ]; then
   726      shunit_message_="${shunit_message_}$1"
   727      shift
   728    fi
   729  
   730    shunit_message_=${shunit_message_%% }
   731    _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected not same"
   732  
   733    unset shunit_message_
   734    return ${SHUNIT_FALSE}
   735  }
   736  # shellcheck disable=SC2016,SC2034
   737  _FAIL_SAME_='eval failSame --lineno "${LINENO:-}"'
   738  
   739  # Records a test failure, stating two values were not equal.
   740  #
   741  # This is functionally equivalent to calling failNotEquals().
   742  #
   743  # Args:
   744  #   message: string: failure message [optional]
   745  #   expected: string: expected value
   746  #   actual: string: actual value
   747  # Returns:
   748  #   integer: success (TRUE/FALSE/ERROR constant)
   749  failNotSame() {
   750    # shellcheck disable=SC2090
   751    ${_SHUNIT_LINENO_}
   752    if command [ $# -lt 2 -o $# -gt 3 ]; then
   753      _shunit_error "failNotSame() requires one or two arguments; $# given"
   754      return ${SHUNIT_ERROR}
   755    fi
   756    _shunit_shouldSkip && return ${SHUNIT_TRUE}
   757  
   758    shunit_message_=${__shunit_lineno}
   759    if command [ $# -eq 3 ]; then
   760      shunit_message_="${shunit_message_}$1"
   761      shift
   762    fi
   763    failNotEquals "${shunit_message_}" "$1" "$2"
   764    shunit_return=$?
   765  
   766    unset shunit_message_
   767    return ${shunit_return}
   768  }
   769  # shellcheck disable=SC2016,SC2034
   770  _FAIL_NOT_SAME_='eval failNotSame --lineno "${LINENO:-}"'
   771  
   772  #-----------------------------------------------------------------------------
   773  # Skipping functions.
   774  #
   775  
   776  # Force remaining assert and fail functions to be "skipped".
   777  #
   778  # This function forces the remaining assert and fail functions to be "skipped",
   779  # i.e. they will have no effect. Each function skipped will be recorded so that
   780  # the total of asserts and fails will not be altered.
   781  #
   782  # Args:
   783  #   None
   784  startSkipping() { __shunit_skip=${SHUNIT_TRUE}; }
   785  
   786  # Resume the normal recording behavior of assert and fail calls.
   787  #
   788  # Args:
   789  #   None
   790  endSkipping() { __shunit_skip=${SHUNIT_FALSE}; }
   791  
   792  # Returns the state of assert and fail call skipping.
   793  #
   794  # Args:
   795  #   None
   796  # Returns:
   797  #   boolean: (TRUE/FALSE constant)
   798  isSkipping() { return ${__shunit_skip}; }
   799  
   800  #-----------------------------------------------------------------------------
   801  # Suite functions.
   802  #
   803  
   804  # Stub. This function should contains all unit test calls to be made.
   805  #
   806  # DEPRECATED (as of 2.1.0)
   807  #
   808  # This function can be optionally overridden by the user in their test suite.
   809  #
   810  # If this function exists, it will be called when shunit2 is sourced. If it
   811  # does not exist, shunit2 will search the parent script for all functions
   812  # beginning with the word 'test', and they will be added dynamically to the
   813  # test suite.
   814  #
   815  # This function should be overridden by the user in their unit test suite.
   816  # Note: see _shunit_mktempFunc() for actual implementation
   817  #
   818  # Args:
   819  #   None
   820  #suite() { :; }  # DO NOT UNCOMMENT THIS FUNCTION
   821  
   822  # Adds a function name to the list of tests schedule for execution.
   823  #
   824  # This function should only be called from within the suite() function.
   825  #
   826  # Args:
   827  #   function: string: name of a function to add to current unit test suite
   828  suite_addTest() {
   829    shunit_func_=${1:-}
   830  
   831    __shunit_suite="${__shunit_suite:+${__shunit_suite} }${shunit_func_}"
   832    __shunit_testsTotal=`expr ${__shunit_testsTotal} + 1`
   833  
   834    unset shunit_func_
   835  }
   836  
   837  # Stub. This function will be called once before any tests are run.
   838  #
   839  # Common one-time environment preparation tasks shared by all tests can be
   840  # defined here.
   841  #
   842  # This function should be overridden by the user in their unit test suite.
   843  # Note: see _shunit_mktempFunc() for actual implementation
   844  #
   845  # Args:
   846  #   None
   847  #oneTimeSetUp() { :; }  # DO NOT UNCOMMENT THIS FUNCTION
   848  
   849  # Stub. This function will be called once after all tests are finished.
   850  #
   851  # Common one-time environment cleanup tasks shared by all tests can be defined
   852  # here.
   853  #
   854  # This function should be overridden by the user in their unit test suite.
   855  # Note: see _shunit_mktempFunc() for actual implementation
   856  #
   857  # Args:
   858  #   None
   859  #oneTimeTearDown() { :; }  # DO NOT UNCOMMENT THIS FUNCTION
   860  
   861  # Stub. This function will be called before each test is run.
   862  #
   863  # Common environment preparation tasks shared by all tests can be defined here.
   864  #
   865  # This function should be overridden by the user in their unit test suite.
   866  # Note: see _shunit_mktempFunc() for actual implementation
   867  #
   868  # Args:
   869  #   None
   870  #setUp() { :; }  # DO NOT UNCOMMENT THIS FUNCTION
   871  
   872  # Note: see _shunit_mktempFunc() for actual implementation
   873  # Stub. This function will be called after each test is run.
   874  #
   875  # Common environment cleanup tasks shared by all tests can be defined here.
   876  #
   877  # This function should be overridden by the user in their unit test suite.
   878  # Note: see _shunit_mktempFunc() for actual implementation
   879  #
   880  # Args:
   881  #   None
   882  #tearDown() { :; }  # DO NOT UNCOMMENT THIS FUNCTION
   883  
   884  #------------------------------------------------------------------------------
   885  # Internal shUnit2 functions.
   886  #
   887  
   888  # Create a temporary directory to store various run-time files in.
   889  #
   890  # This function is a cross-platform temporary directory creation tool. Not all
   891  # OSes have the `mktemp` function, so one is included here.
   892  #
   893  # Args:
   894  #   None
   895  # Outputs:
   896  #   string: the temporary directory that was created
   897  _shunit_mktempDir() {
   898    # Try the standard `mktemp` function.
   899    ( exec mktemp -dqt shunit.XXXXXX 2>/dev/null ) && return
   900  
   901    # The standard `mktemp` didn't work. Use our own.
   902    # shellcheck disable=SC2039
   903    if command [ -r '/dev/urandom' -a -x '/usr/bin/od' ]; then
   904      _shunit_random_=`/usr/bin/od -vAn -N4 -tx4 </dev/urandom \
   905          |command sed 's/^[^0-9a-f]*//'`
   906    elif command [ -n "${RANDOM:-}" ]; then
   907      # $RANDOM works
   908      _shunit_random_=${RANDOM}${RANDOM}${RANDOM}$$
   909    else
   910      # `$RANDOM` doesn't work.
   911      _shunit_date_=`date '+%Y%m%d%H%M%S'`
   912      _shunit_random_=`expr "${_shunit_date_}" / $$`
   913    fi
   914  
   915    _shunit_tmpDir_="${TMPDIR:-/tmp}/shunit.${_shunit_random_}"
   916    ( umask 077 && command mkdir "${_shunit_tmpDir_}" ) || \
   917        _shunit_fatal 'could not create temporary directory! exiting'
   918  
   919    echo "${_shunit_tmpDir_}"
   920    unset _shunit_date_ _shunit_random_ _shunit_tmpDir_
   921  }
   922  
   923  # This function is here to work around issues in Cygwin.
   924  #
   925  # Args:
   926  #   None
   927  _shunit_mktempFunc() {
   928    for _shunit_func_ in oneTimeSetUp oneTimeTearDown setUp tearDown suite noexec
   929    do
   930      _shunit_file_="${__shunit_tmpDir}/${_shunit_func_}"
   931      command cat <<EOF >"${_shunit_file_}"
   932  #! /bin/sh
   933  exit ${SHUNIT_TRUE}
   934  EOF
   935      command chmod +x "${_shunit_file_}"
   936    done
   937  
   938    unset _shunit_file_
   939  }
   940  
   941  # Final cleanup function to leave things as we found them.
   942  #
   943  # Besides removing the temporary directory, this function is in charge of the
   944  # final exit code of the unit test. The exit code is based on how the script
   945  # was ended (e.g. normal exit, or via Ctrl-C).
   946  #
   947  # Args:
   948  #   name: string: name of the trap called (specified when trap defined)
   949  _shunit_cleanup() {
   950    _shunit_name_=$1
   951  
   952    case "${_shunit_name_}" in
   953      EXIT) ;;
   954      INT) _shunit_signal_=130 ;;  # 2+128
   955      TERM) _shunit_signal_=143 ;;  # 15+128
   956      *)
   957        _shunit_error "unrecognized trap value (${_shunit_name_})"
   958        _shunit_signal_=0
   959        ;;
   960    esac
   961    if command [ "${_shunit_name_}" != 'EXIT' ]; then
   962      _shunit_warn "trapped and now handling the (${_shunit_name_}) signal"
   963    fi
   964  
   965    # Do our work.
   966    if command [ ${__shunit_clean} -eq ${SHUNIT_FALSE} ]; then
   967      # Ensure tear downs are only called once.
   968      __shunit_clean=${SHUNIT_TRUE}
   969  
   970      tearDown
   971      command [ $? -eq ${SHUNIT_TRUE} ] \
   972          || _shunit_warn "tearDown() returned non-zero return code."
   973      oneTimeTearDown
   974      command [ $? -eq ${SHUNIT_TRUE} ] \
   975          || _shunit_warn "oneTimeTearDown() returned non-zero return code."
   976  
   977      command rm -fr "${__shunit_tmpDir}"
   978    fi
   979  
   980    if command [ "${_shunit_name_}" != 'EXIT' ]; then
   981      # Handle all non-EXIT signals.
   982      trap - 0  # Disable EXIT trap.
   983      exit ${_shunit_signal_}
   984    elif command [ ${__shunit_reportGenerated} -eq ${SHUNIT_FALSE} ]; then
   985      _shunit_assertFail 'unknown failure encountered running a test'
   986      _shunit_generateReport
   987      exit ${SHUNIT_ERROR}
   988    fi
   989  
   990    unset _shunit_name_ _shunit_signal_
   991  }
   992  
   993  # configureColor based on user color preference.
   994  #
   995  # Args:
   996  #   color: string: color mode (one of `always`, `auto`, or `none`).
   997  _shunit_configureColor() {
   998    _shunit_color_=${SHUNIT_FALSE}  # By default, no color.
   999    case $1 in
  1000      'always') _shunit_color_=${SHUNIT_TRUE} ;;
  1001      'auto')
  1002        command [ "`_shunit_colors`" -ge 8 ] && _shunit_color_=${SHUNIT_TRUE}
  1003        ;;
  1004      'none') ;;
  1005      *) _shunit_fatal "unrecognized color option '$1'" ;;
  1006    esac
  1007  
  1008    case ${_shunit_color_} in
  1009      ${SHUNIT_TRUE})
  1010        __shunit_ansi_none=${__SHUNIT_ANSI_NONE}
  1011        __shunit_ansi_red=${__SHUNIT_ANSI_RED}
  1012        __shunit_ansi_green=${__SHUNIT_ANSI_GREEN}
  1013        __shunit_ansi_yellow=${__SHUNIT_ANSI_YELLOW}
  1014        __shunit_ansi_cyan=${__SHUNIT_ANSI_CYAN}
  1015        ;;
  1016      ${SHUNIT_FALSE})
  1017        __shunit_ansi_none=''
  1018        __shunit_ansi_red=''
  1019        __shunit_ansi_green=''
  1020        __shunit_ansi_yellow=''
  1021        __shunit_ansi_cyan=''
  1022        ;;
  1023    esac
  1024  
  1025    unset _shunit_color_ _shunit_tput_
  1026  }
  1027  
  1028  # colors returns the number of supported colors for the TERM.
  1029  _shunit_colors() {
  1030    _shunit_tput_=`${SHUNIT_CMD_TPUT} colors 2>/dev/null`
  1031    if command [ $? -eq 0 ]; then
  1032      echo "${_shunit_tput_}"
  1033    else
  1034      echo 16
  1035    fi
  1036    unset _shunit_tput_
  1037  }
  1038  
  1039  # The actual running of the tests happens here.
  1040  #
  1041  # Args:
  1042  #   None
  1043  _shunit_execSuite() {
  1044    for _shunit_test_ in ${__shunit_suite}; do
  1045      __shunit_testSuccess=${SHUNIT_TRUE}
  1046  
  1047      # Disable skipping.
  1048      endSkipping
  1049  
  1050      # Execute the per-test setup function.
  1051      setUp
  1052      command [ $? -eq ${SHUNIT_TRUE} ] \
  1053          || _shunit_fatal "setup() returned non-zero return code."
  1054  
  1055      # Execute the test.
  1056      echo "${__SHUNIT_TEST_PREFIX}${_shunit_test_}"
  1057      eval "${_shunit_test_}"
  1058      if command [ $? -ne ${SHUNIT_TRUE} ]; then
  1059        _shunit_error "${_shunit_test_}() returned non-zero return code."
  1060        __shunit_testSuccess=${SHUNIT_ERROR}
  1061        _shunit_incFailedCount
  1062      fi
  1063  
  1064      # Execute the per-test tear-down function.
  1065      tearDown
  1066      command [ $? -eq ${SHUNIT_TRUE} ] \
  1067          || _shunit_fatal "tearDown() returned non-zero return code."
  1068  
  1069      # Update stats.
  1070      if command [ ${__shunit_testSuccess} -eq ${SHUNIT_TRUE} ]; then
  1071        __shunit_testsPassed=`expr ${__shunit_testsPassed} + 1`
  1072      else
  1073        __shunit_testsFailed=`expr ${__shunit_testsFailed} + 1`
  1074      fi
  1075    done
  1076  
  1077    unset _shunit_test_
  1078  }
  1079  
  1080  # Generates the user friendly report with appropriate OK/FAILED message.
  1081  #
  1082  # Args:
  1083  #   None
  1084  # Output:
  1085  #   string: the report of successful and failed tests, as well as totals.
  1086  _shunit_generateReport() {
  1087    command [ "${__shunit_reportGenerated}" -eq ${SHUNIT_TRUE} ] && return
  1088  
  1089    _shunit_ok_=${SHUNIT_TRUE}
  1090  
  1091    # If no exit code was provided, determine an appropriate one.
  1092    command [ "${__shunit_testsFailed}" -gt 0 \
  1093        -o ${__shunit_testSuccess} -eq ${SHUNIT_FALSE} ] \
  1094            && _shunit_ok_=${SHUNIT_FALSE}
  1095  
  1096    echo
  1097    _shunit_msg_="Ran ${__shunit_ansi_cyan}${__shunit_testsTotal}${__shunit_ansi_none}"
  1098    if command [ "${__shunit_testsTotal}" -eq 1 ]; then
  1099      ${__SHUNIT_CMD_ECHO_ESC} "${_shunit_msg_} test."
  1100    else
  1101      ${__SHUNIT_CMD_ECHO_ESC} "${_shunit_msg_} tests."
  1102    fi
  1103  
  1104    if command [ ${_shunit_ok_} -eq ${SHUNIT_TRUE} ]; then
  1105      _shunit_msg_="${__shunit_ansi_green}OK${__shunit_ansi_none}"
  1106      command [ "${__shunit_assertsSkipped}" -gt 0 ] \
  1107          && _shunit_msg_="${_shunit_msg_} (${__shunit_ansi_yellow}skipped=${__shunit_assertsSkipped}${__shunit_ansi_none})"
  1108    else
  1109      _shunit_msg_="${__shunit_ansi_red}FAILED${__shunit_ansi_none}"
  1110      _shunit_msg_="${_shunit_msg_} (${__shunit_ansi_red}failures=${__shunit_assertsFailed}${__shunit_ansi_none}"
  1111      command [ "${__shunit_assertsSkipped}" -gt 0 ] \
  1112          && _shunit_msg_="${_shunit_msg_},${__shunit_ansi_yellow}skipped=${__shunit_assertsSkipped}${__shunit_ansi_none}"
  1113      _shunit_msg_="${_shunit_msg_})"
  1114    fi
  1115  
  1116    echo
  1117    ${__SHUNIT_CMD_ECHO_ESC} "${_shunit_msg_}"
  1118    __shunit_reportGenerated=${SHUNIT_TRUE}
  1119  
  1120    unset _shunit_msg_ _shunit_ok_
  1121  }
  1122  
  1123  # Test for whether a function should be skipped.
  1124  #
  1125  # Args:
  1126  #   None
  1127  # Returns:
  1128  #   boolean: whether the test should be skipped (TRUE/FALSE constant)
  1129  _shunit_shouldSkip() {
  1130    command [ ${__shunit_skip} -eq ${SHUNIT_FALSE} ] && return ${SHUNIT_FALSE}
  1131    _shunit_assertSkip
  1132  }
  1133  
  1134  # Records a successful test.
  1135  #
  1136  # Args:
  1137  #   None
  1138  _shunit_assertPass() {
  1139    __shunit_assertsPassed=`expr ${__shunit_assertsPassed} + 1`
  1140    __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1`
  1141  }
  1142  
  1143  # Records a test failure.
  1144  #
  1145  # Args:
  1146  #   message: string: failure message to provide user
  1147  _shunit_assertFail() {
  1148    __shunit_testSuccess=${SHUNIT_FALSE}
  1149    _shunit_incFailedCount
  1150  
  1151    \[ $# -gt 0 ] && ${__SHUNIT_CMD_ECHO_ESC} \
  1152        "${__shunit_ansi_red}ASSERT:${__shunit_ansi_none}$*"
  1153  }
  1154  
  1155  # Increment the count of failed asserts.
  1156  #
  1157  # Args:
  1158  #   none
  1159  _shunit_incFailedCount() {
  1160    __shunit_assertsFailed=`expr "${__shunit_assertsFailed}" + 1`
  1161    __shunit_assertsTotal=`expr "${__shunit_assertsTotal}" + 1`
  1162  }
  1163  
  1164  
  1165  # Records a skipped test.
  1166  #
  1167  # Args:
  1168  #   None
  1169  _shunit_assertSkip() {
  1170    __shunit_assertsSkipped=`expr "${__shunit_assertsSkipped}" + 1`
  1171    __shunit_assertsTotal=`expr "${__shunit_assertsTotal}" + 1`
  1172  }
  1173  
  1174  # Prepare a script filename for sourcing.
  1175  #
  1176  # Args:
  1177  #   script: string: path to a script to source
  1178  # Returns:
  1179  #   string: filename prefixed with ./ (if necessary)
  1180  _shunit_prepForSourcing() {
  1181    _shunit_script_=$1
  1182    case "${_shunit_script_}" in
  1183      /*|./*) echo "${_shunit_script_}" ;;
  1184      *) echo "./${_shunit_script_}" ;;
  1185    esac
  1186    unset _shunit_script_
  1187  }
  1188  
  1189  # Escape a character in a string.
  1190  #
  1191  # Args:
  1192  #   c: string: unescaped character
  1193  #   s: string: to escape character in
  1194  # Returns:
  1195  #   string: with escaped character(s)
  1196  _shunit_escapeCharInStr() {
  1197    command [ -n "$2" ] || return  # No point in doing work on an empty string.
  1198  
  1199    # Note: using shorter variable names to prevent conflicts with
  1200    # _shunit_escapeCharactersInString().
  1201    _shunit_c_=$1
  1202    _shunit_s_=$2
  1203  
  1204    # Escape the character.
  1205    # shellcheck disable=SC1003,SC2086
  1206    echo ''${_shunit_s_}'' |command sed 's/\'${_shunit_c_}'/\\\'${_shunit_c_}'/g'
  1207  
  1208    unset _shunit_c_ _shunit_s_
  1209  }
  1210  
  1211  # Escape a character in a string.
  1212  #
  1213  # Args:
  1214  #   str: string: to escape characters in
  1215  # Returns:
  1216  #   string: with escaped character(s)
  1217  _shunit_escapeCharactersInString() {
  1218    command [ -n "$1" ] || return  # No point in doing work on an empty string.
  1219  
  1220    _shunit_str_=$1
  1221  
  1222    # Note: using longer variable names to prevent conflicts with
  1223    # _shunit_escapeCharInStr().
  1224    for _shunit_char_ in '"' '$' "'" '`'; do
  1225      _shunit_str_=`_shunit_escapeCharInStr "${_shunit_char_}" "${_shunit_str_}"`
  1226    done
  1227  
  1228    echo "${_shunit_str_}"
  1229    unset _shunit_char_ _shunit_str_
  1230  }
  1231  
  1232  # Extract list of functions to run tests against.
  1233  #
  1234  # Args:
  1235  #   script: string: name of script to extract functions from
  1236  # Returns:
  1237  #   string: of function names
  1238  _shunit_extractTestFunctions() {
  1239    _shunit_script_=$1
  1240  
  1241    # Extract the lines with test function names, strip of anything besides the
  1242    # function name, and output everything on a single line.
  1243    _shunit_regex_='^\s*((function test[A-Za-z0-9_-]*)|(test[A-Za-z0-9_-]* *\(\)))'
  1244    # shellcheck disable=SC2196
  1245    egrep "${_shunit_regex_}" "${_shunit_script_}" \
  1246    |command sed 's/^[^A-Za-z0-9_-]*//;s/^function //;s/\([A-Za-z0-9_-]*\).*/\1/g' \
  1247    |xargs
  1248  
  1249    unset _shunit_regex_ _shunit_script_
  1250  }
  1251  
  1252  #------------------------------------------------------------------------------
  1253  # Main.
  1254  #
  1255  
  1256  # Determine the operating mode.
  1257  if command [ $# -eq 0 -o "${1:-}" = '--' ]; then
  1258    __shunit_script=${__SHUNIT_PARENT}
  1259    __shunit_mode=${__SHUNIT_MODE_SOURCED}
  1260  else
  1261    __shunit_script=$1
  1262    command [ -r "${__shunit_script}" ] || \
  1263        _shunit_fatal "unable to read from ${__shunit_script}"
  1264    __shunit_mode=${__SHUNIT_MODE_STANDALONE}
  1265  fi
  1266  
  1267  # Create a temporary storage location.
  1268  __shunit_tmpDir=`_shunit_mktempDir`
  1269  
  1270  # Provide a public temporary directory for unit test scripts.
  1271  # TODO(kward): document this.
  1272  SHUNIT_TMPDIR="${__shunit_tmpDir}/tmp"
  1273  command mkdir "${SHUNIT_TMPDIR}"
  1274  
  1275  # Setup traps to clean up after ourselves.
  1276  trap '_shunit_cleanup EXIT' 0
  1277  trap '_shunit_cleanup INT' 2
  1278  trap '_shunit_cleanup TERM' 15
  1279  
  1280  # Create phantom functions to work around issues with Cygwin.
  1281  _shunit_mktempFunc
  1282  PATH="${__shunit_tmpDir}:${PATH}"
  1283  
  1284  # Make sure phantom functions are executable. This will bite if `/tmp` (or the
  1285  # current `$TMPDIR`) points to a path on a partition that was mounted with the
  1286  # 'noexec' option. The noexec command was created with `_shunit_mktempFunc()`.
  1287  noexec 2>/dev/null || _shunit_fatal \
  1288      'Please declare TMPDIR with path on partition with exec permission.'
  1289  
  1290  # We must manually source the tests in standalone mode.
  1291  if command [ "${__shunit_mode}" = "${__SHUNIT_MODE_STANDALONE}" ]; then
  1292    # shellcheck disable=SC1090
  1293    command . "`_shunit_prepForSourcing \"${__shunit_script}\"`"
  1294  fi
  1295  
  1296  # Configure default output coloring behavior.
  1297  _shunit_configureColor "${SHUNIT_COLOR}"
  1298  
  1299  # Execute the oneTimeSetUp function (if it exists).
  1300  oneTimeSetUp
  1301  command [ $? -eq ${SHUNIT_TRUE} ] \
  1302      || _shunit_fatal "oneTimeSetUp() returned non-zero return code."
  1303  
  1304  # Command line selected tests or suite selected tests
  1305  if command [ "$#" -ge 2 ]; then
  1306    # Argument $1 is either the filename of tests or '--'; either way, skip it.
  1307    shift
  1308    # Remaining arguments ($2 .. $#) are assumed to be test function names.
  1309    # Interate through all remaining args in "$@" in a POSIX (likely portable) way.
  1310    # Helpful tip: https://unix.stackexchange.com/questions/314032/how-to-use-arguments-like-1-2-in-a-for-loop
  1311    for _shunit_arg_ do
  1312      suite_addTest "${_shunit_arg_}"
  1313    done
  1314    unset _shunit_arg_
  1315  else
  1316    # Execute the suite function defined in the parent test script.
  1317    # DEPRECATED as of 2.1.0.
  1318    suite
  1319  fi
  1320  
  1321  # If no tests or suite specified, dynamically build a list of functions.
  1322  if command [ -z "${__shunit_suite}" ]; then
  1323    shunit_funcs_=`_shunit_extractTestFunctions "${__shunit_script}"`
  1324    for shunit_func_ in ${shunit_funcs_}; do
  1325      suite_addTest "${shunit_func_}"
  1326    done
  1327  fi
  1328  unset shunit_func_ shunit_funcs_
  1329  
  1330  # Execute the suite of unit tests.
  1331  _shunit_execSuite
  1332  
  1333  # Execute the oneTimeTearDown function (if it exists).
  1334  oneTimeTearDown
  1335  command [ $? -eq ${SHUNIT_TRUE} ] \
  1336      || _shunit_fatal "oneTimeTearDown() returned non-zero return code."
  1337  
  1338  # Generate a report summary.
  1339  _shunit_generateReport
  1340  
  1341  # That's it folks.
  1342  command [ "${__shunit_testsFailed}" -eq 0 ]
  1343  exit $?