Home | History | Annotate | Download | only in lib
      1 #! /bin/sh
      2 # $Id: shunit2 322 2011-04-24 00:09:45Z kate.ward (at] forestent.com $
      3 # vim:et:ft=sh:sts=2:sw=2
      4 #
      5 # Copyright 2008 Kate Ward. All Rights Reserved.
      6 # Released under the LGPL (GNU Lesser General Public License)
      7 #
      8 # shUnit2 -- Unit testing framework for Unix shell scripts.
      9 # http://code.google.com/p/shunit2/
     10 #
     11 # Author: kate.ward (at] 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 # return if shunit already loaded
     17 [ -n "${SHUNIT_VERSION:-}" ] && exit 0
     18 
     19 SHUNIT_VERSION='2.1.6'
     20 
     21 SHUNIT_TRUE=0
     22 SHUNIT_FALSE=1
     23 SHUNIT_ERROR=2
     24 
     25 # enable strict mode by default
     26 SHUNIT_STRICT=${SHUNIT_STRICT:-${SHUNIT_TRUE}}
     27 
     28 _shunit_warn() { echo "shunit2:WARN $@" >&2; }
     29 _shunit_error() { echo "shunit2:ERROR $@" >&2; }
     30 _shunit_fatal() { echo "shunit2:FATAL $@" >&2; exit ${SHUNIT_ERROR}; }
     31 
     32 # specific shell checks
     33 if [ -n "${ZSH_VERSION:-}" ]; then
     34   setopt |grep "^shwordsplit$" >/dev/null
     35   if [ $? -ne ${SHUNIT_TRUE} ]; then
     36     _shunit_fatal 'zsh shwordsplit option is required for proper operation'
     37   fi
     38   if [ -z "${SHUNIT_PARENT:-}" ]; then
     39     _shunit_fatal "zsh does not pass \$0 through properly. please declare \
     40 \"SHUNIT_PARENT=\$0\" before calling shUnit2"
     41   fi
     42 fi
     43 
     44 #
     45 # constants
     46 #
     47 
     48 __SHUNIT_ASSERT_MSG_PREFIX='ASSERT:'
     49 __SHUNIT_MODE_SOURCED='sourced'
     50 __SHUNIT_MODE_STANDALONE='standalone'
     51 __SHUNIT_PARENT=${SHUNIT_PARENT:-$0}
     52 
     53 # set the constants readonly
     54 shunit_constants_=`set |grep '^__SHUNIT_' |cut -d= -f1`
     55 echo "${shunit_constants_}" |grep '^Binary file' >/dev/null && \
     56     shunit_constants_=`set |grep -a '^__SHUNIT_' |cut -d= -f1`
     57 for shunit_constant_ in ${shunit_constants_}; do
     58   shunit_ro_opts_=''
     59   case ${ZSH_VERSION:-} in
     60     '') ;;  # this isn't zsh
     61     [123].*) ;;  # early versions (1.x, 2.x, 3.x)
     62     *) shunit_ro_opts_='-g' ;;  # all later versions. declare readonly globally
     63   esac
     64   readonly ${shunit_ro_opts_} ${shunit_constant_}
     65 done
     66 unset shunit_constant_ shunit_constants_ shunit_ro_opts_
     67 
     68 # variables
     69 __shunit_lineno=''  # line number of executed test
     70 __shunit_mode=${__SHUNIT_MODE_SOURCED}  # operating mode
     71 __shunit_reportGenerated=${SHUNIT_FALSE}  # is report generated
     72 __shunit_script=''  # filename of unittest script (standalone mode)
     73 __shunit_skip=${SHUNIT_FALSE}  # is skipping enabled
     74 __shunit_suite=''  # suite of tests to execute
     75 
     76 # counts of tests
     77 __shunit_testSuccess=${SHUNIT_TRUE}
     78 __shunit_testsTotal=0
     79 __shunit_testsPassed=0
     80 __shunit_testsFailed=0
     81 
     82 # counts of asserts
     83 __shunit_assertsTotal=0
     84 __shunit_assertsPassed=0
     85 __shunit_assertsFailed=0
     86 __shunit_assertsSkipped=0
     87 
     88 # macros
     89 _SHUNIT_LINENO_='eval __shunit_lineno=""; if [ "${1:-}" = "--lineno" ]; then [ -n "$2" ] && __shunit_lineno="[$2] "; shift 2; fi'
     90 
     91 #-----------------------------------------------------------------------------
     92 # assert functions
     93 #
     94 
     95 # Assert that two values are equal to one another.
     96 #
     97 # Args:
     98 #   message: string: failure message [optional]
     99 #   expected: string: expected value
    100 #   actual: string: actual value
    101 # Returns:
    102 #   integer: success (TRUE/FALSE/ERROR constant)
    103 assertEquals()
    104 {
    105   ${_SHUNIT_LINENO_}
    106   if [ $# -lt 2 -o $# -gt 3 ]; then
    107     _shunit_error "assertEquals() requires two or three arguments; $# given"
    108     _shunit_error "1: ${1:+$1} 2: ${2:+$2} 3: ${3:+$3}"
    109     return ${SHUNIT_ERROR}
    110   fi
    111   _shunit_shouldSkip && return ${SHUNIT_TRUE}
    112 
    113   shunit_message_=${__shunit_lineno}
    114   if [ $# -eq 3 ]; then
    115     shunit_message_="${shunit_message_}$1"
    116     shift
    117   fi
    118   shunit_expected_=$1
    119   shunit_actual_=$2
    120 
    121   shunit_return=${SHUNIT_TRUE}
    122   if [ "${shunit_expected_}" = "${shunit_actual_}" ]; then
    123     _shunit_assertPass
    124   else
    125     failNotEquals "${shunit_message_}" "${shunit_expected_}" "${shunit_actual_}"
    126     shunit_return=${SHUNIT_FALSE}
    127   fi
    128 
    129   unset shunit_message_ shunit_expected_ shunit_actual_
    130   return ${shunit_return}
    131 }
    132 _ASSERT_EQUALS_='eval assertEquals --lineno "${LINENO:-}"'
    133 
    134 # Assert that two values are not equal to one another.
    135 #
    136 # Args:
    137 #   message: string: failure message [optional]
    138 #   expected: string: expected value
    139 #   actual: string: actual value
    140 # Returns:
    141 #   integer: success (TRUE/FALSE/ERROR constant)
    142 assertNotEquals()
    143 {
    144   ${_SHUNIT_LINENO_}
    145   if [ $# -lt 2 -o $# -gt 3 ]; then
    146     _shunit_error "assertNotEquals() requires two or three arguments; $# given"
    147     return ${SHUNIT_ERROR}
    148   fi
    149   _shunit_shouldSkip && return ${SHUNIT_TRUE}
    150 
    151   shunit_message_=${__shunit_lineno}
    152   if [ $# -eq 3 ]; then
    153     shunit_message_="${shunit_message_}$1"
    154     shift
    155   fi
    156   shunit_expected_=$1
    157   shunit_actual_=$2
    158 
    159   shunit_return=${SHUNIT_TRUE}
    160   if [ "${shunit_expected_}" != "${shunit_actual_}" ]; then
    161     _shunit_assertPass
    162   else
    163     failSame "${shunit_message_}" "$@"
    164     shunit_return=${SHUNIT_FALSE}
    165   fi
    166 
    167   unset shunit_message_ shunit_expected_ shunit_actual_
    168   return ${shunit_return}
    169 }
    170 _ASSERT_NOT_EQUALS_='eval assertNotEquals --lineno "${LINENO:-}"'
    171 
    172 # Assert that a value is null (i.e. an empty string)
    173 #
    174 # Args:
    175 #   message: string: failure message [optional]
    176 #   actual: string: actual value
    177 # Returns:
    178 #   integer: success (TRUE/FALSE/ERROR constant)
    179 assertNull()
    180 {
    181   ${_SHUNIT_LINENO_}
    182   if [ $# -lt 1 -o $# -gt 2 ]; then
    183     _shunit_error "assertNull() requires one or two arguments; $# given"
    184     return ${SHUNIT_ERROR}
    185   fi
    186   _shunit_shouldSkip && return ${SHUNIT_TRUE}
    187 
    188   shunit_message_=${__shunit_lineno}
    189   if [ $# -eq 2 ]; then
    190     shunit_message_="${shunit_message_}$1"
    191     shift
    192   fi
    193   assertTrue "${shunit_message_}" "[ -z '$1' ]"
    194   shunit_return=$?
    195 
    196   unset shunit_message_
    197   return ${shunit_return}
    198 }
    199 _ASSERT_NULL_='eval assertNull --lineno "${LINENO:-}"'
    200 
    201 # Assert that a value is not null (i.e. a non-empty string)
    202 #
    203 # Args:
    204 #   message: string: failure message [optional]
    205 #   actual: string: actual value
    206 # Returns:
    207 #   integer: success (TRUE/FALSE/ERROR constant)
    208 assertNotNull()
    209 {
    210   ${_SHUNIT_LINENO_}
    211   if [ $# -gt 2 ]; then  # allowing 0 arguments as $1 might actually be null
    212     _shunit_error "assertNotNull() requires one or two arguments; $# given"
    213     return ${SHUNIT_ERROR}
    214   fi
    215   _shunit_shouldSkip && return ${SHUNIT_TRUE}
    216 
    217   shunit_message_=${__shunit_lineno}
    218   if [ $# -eq 2 ]; then
    219     shunit_message_="${shunit_message_}$1"
    220     shift
    221   fi
    222   shunit_actual_=`_shunit_escapeCharactersInString "${1:-}"`
    223   test -n "${shunit_actual_}"
    224   assertTrue "${shunit_message_}" $?
    225   shunit_return=$?
    226 
    227   unset shunit_actual_ shunit_message_
    228   return ${shunit_return}
    229 }
    230 _ASSERT_NOT_NULL_='eval assertNotNull --lineno "${LINENO:-}"'
    231 
    232 # Assert that two values are the same (i.e. equal to one another).
    233 #
    234 # Args:
    235 #   message: string: failure message [optional]
    236 #   expected: string: expected value
    237 #   actual: string: actual value
    238 # Returns:
    239 #   integer: success (TRUE/FALSE/ERROR constant)
    240 assertSame()
    241 {
    242   ${_SHUNIT_LINENO_}
    243   if [ $# -lt 2 -o $# -gt 3 ]; then
    244     _shunit_error "assertSame() requires two or three arguments; $# given"
    245     return ${SHUNIT_ERROR}
    246   fi
    247   _shunit_shouldSkip && return ${SHUNIT_TRUE}
    248 
    249   shunit_message_=${__shunit_lineno}
    250   if [ $# -eq 3 ]; then
    251     shunit_message_="${shunit_message_}$1"
    252     shift
    253   fi
    254   assertEquals "${shunit_message_}" "$1" "$2"
    255   shunit_return=$?
    256 
    257   unset shunit_message_
    258   return ${shunit_return}
    259 }
    260 _ASSERT_SAME_='eval assertSame --lineno "${LINENO:-}"'
    261 
    262 # Assert that two values are not the same (i.e. not equal to one another).
    263 #
    264 # Args:
    265 #   message: string: failure message [optional]
    266 #   expected: string: expected value
    267 #   actual: string: actual value
    268 # Returns:
    269 #   integer: success (TRUE/FALSE/ERROR constant)
    270 assertNotSame()
    271 {
    272   ${_SHUNIT_LINENO_}
    273   if [ $# -lt 2 -o $# -gt 3 ]; then
    274     _shunit_error "assertNotSame() requires two or three arguments; $# given"
    275     return ${SHUNIT_ERROR}
    276   fi
    277   _shunit_shouldSkip && return ${SHUNIT_TRUE}
    278 
    279   shunit_message_=${__shunit_lineno}
    280   if [ $# -eq 3 ]; then
    281     shunit_message_="${shunit_message_:-}$1"
    282     shift
    283   fi
    284   assertNotEquals "${shunit_message_}" "$1" "$2"
    285   shunit_return=$?
    286 
    287   unset shunit_message_
    288   return ${shunit_return}
    289 }
    290 _ASSERT_NOT_SAME_='eval assertNotSame --lineno "${LINENO:-}"'
    291 
    292 # Assert that a value or shell test condition is true.
    293 #
    294 # In shell, a value of 0 is true and a non-zero value is false. Any integer
    295 # value passed can thereby be tested.
    296 #
    297 # Shell supports much more complicated tests though, and a means to support
    298 # them was needed. As such, this function tests that conditions are true or
    299 # false through evaluation rather than just looking for a true or false.
    300 #
    301 # The following test will succeed:
    302 #   assertTrue 0
    303 #   assertTrue "[ 34 -gt 23 ]"
    304 # The folloing test will fail with a message:
    305 #   assertTrue 123
    306 #   assertTrue "test failed" "[ -r '/non/existant/file' ]"
    307 #
    308 # Args:
    309 #   message: string: failure message [optional]
    310 #   condition: string: integer value or shell conditional statement
    311 # Returns:
    312 #   integer: success (TRUE/FALSE/ERROR constant)
    313 assertTrue()
    314 {
    315   ${_SHUNIT_LINENO_}
    316   if [ $# -gt 2 ]; then
    317     _shunit_error "assertTrue() takes one two arguments; $# given"
    318     return ${SHUNIT_ERROR}
    319   fi
    320   _shunit_shouldSkip && return ${SHUNIT_TRUE}
    321 
    322   shunit_message_=${__shunit_lineno}
    323   if [ $# -eq 2 ]; then
    324     shunit_message_="${shunit_message_}$1"
    325     shift
    326   fi
    327   shunit_condition_=$1
    328 
    329   # see if condition is an integer, i.e. a return value
    330   shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'`
    331   shunit_return=${SHUNIT_TRUE}
    332   if [ -z "${shunit_condition_}" ]; then
    333     # null condition
    334     shunit_return=${SHUNIT_FALSE}
    335   elif [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ]
    336   then
    337     # possible return value. treating 0 as true, and non-zero as false.
    338     [ ${shunit_condition_} -ne 0 ] && shunit_return=${SHUNIT_FALSE}
    339   else
    340     # (hopefully) a condition
    341     ( eval ${shunit_condition_} ) >/dev/null 2>&1
    342     [ $? -ne 0 ] && shunit_return=${SHUNIT_FALSE}
    343   fi
    344 
    345   # record the test
    346   if [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then
    347     _shunit_assertPass
    348   else
    349     _shunit_assertFail "${shunit_message_}"
    350   fi
    351 
    352   unset shunit_message_ shunit_condition_ shunit_match_
    353   return ${shunit_return}
    354 }
    355 _ASSERT_TRUE_='eval assertTrue --lineno "${LINENO:-}"'
    356 
    357 # Assert that a value or shell test condition is false.
    358 #
    359 # In shell, a value of 0 is true and a non-zero value is false. Any integer
    360 # value passed can thereby be tested.
    361 #
    362 # Shell supports much more complicated tests though, and a means to support
    363 # them was needed. As such, this function tests that conditions are true or
    364 # false through evaluation rather than just looking for a true or false.
    365 #
    366 # The following test will succeed:
    367 #   assertFalse 1
    368 #   assertFalse "[ 'apples' = 'oranges' ]"
    369 # The folloing test will fail with a message:
    370 #   assertFalse 0
    371 #   assertFalse "test failed" "[ 1 -eq 1 -a 2 -eq 2 ]"
    372 #
    373 # Args:
    374 #   message: string: failure message [optional]
    375 #   condition: string: integer value or shell conditional statement
    376 # Returns:
    377 #   integer: success (TRUE/FALSE/ERROR constant)
    378 assertFalse()
    379 {
    380   ${_SHUNIT_LINENO_}
    381   if [ $# -lt 1 -o $# -gt 2 ]; then
    382     _shunit_error "assertFalse() quires one or two arguments; $# given"
    383     return ${SHUNIT_ERROR}
    384   fi
    385   _shunit_shouldSkip && return ${SHUNIT_TRUE}
    386 
    387   shunit_message_=${__shunit_lineno}
    388   if [ $# -eq 2 ]; then
    389     shunit_message_="${shunit_message_}$1"
    390     shift
    391   fi
    392   shunit_condition_=$1
    393 
    394   # see if condition is an integer, i.e. a return value
    395   shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'`
    396   shunit_return=${SHUNIT_TRUE}
    397   if [ -z "${shunit_condition_}" ]; then
    398     # null condition
    399     shunit_return=${SHUNIT_FALSE}
    400   elif [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ]
    401   then
    402     # possible return value. treating 0 as true, and non-zero as false.
    403     [ ${shunit_condition_} -eq 0 ] && shunit_return=${SHUNIT_FALSE}
    404   else
    405     # (hopefully) a condition
    406     ( eval ${shunit_condition_} ) >/dev/null 2>&1
    407     [ $? -eq 0 ] && shunit_return=${SHUNIT_FALSE}
    408   fi
    409 
    410   # record the test
    411   if [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then
    412     _shunit_assertPass
    413   else
    414     _shunit_assertFail "${shunit_message_}"
    415   fi
    416 
    417   unset shunit_message_ shunit_condition_ shunit_match_
    418   return ${shunit_return}
    419 }
    420 _ASSERT_FALSE_='eval assertFalse --lineno "${LINENO:-}"'
    421 
    422 #-----------------------------------------------------------------------------
    423 # failure functions
    424 #
    425 
    426 # Records a test failure.
    427 #
    428 # Args:
    429 #   message: string: failure message [optional]
    430 # Returns:
    431 #   integer: success (TRUE/FALSE/ERROR constant)
    432 fail()
    433 {
    434   ${_SHUNIT_LINENO_}
    435   if [ $# -gt 1 ]; then
    436     _shunit_error "fail() requires zero or one arguments; $# given"
    437     return ${SHUNIT_ERROR}
    438   fi
    439   _shunit_shouldSkip && return ${SHUNIT_TRUE}
    440 
    441   shunit_message_=${__shunit_lineno}
    442   if [ $# -eq 1 ]; then
    443     shunit_message_="${shunit_message_}$1"
    444     shift
    445   fi
    446 
    447   _shunit_assertFail "${shunit_message_}"
    448 
    449   unset shunit_message_
    450   return ${SHUNIT_FALSE}
    451 }
    452 _FAIL_='eval fail --lineno "${LINENO:-}"'
    453 
    454 # Records a test failure, stating two values were not equal.
    455 #
    456 # Args:
    457 #   message: string: failure message [optional]
    458 #   expected: string: expected value
    459 #   actual: string: actual value
    460 # Returns:
    461 #   integer: success (TRUE/FALSE/ERROR constant)
    462 failNotEquals()
    463 {
    464   ${_SHUNIT_LINENO_}
    465   if [ $# -lt 2 -o $# -gt 3 ]; then
    466     _shunit_error "failNotEquals() requires one or two arguments; $# given"
    467     return ${SHUNIT_ERROR}
    468   fi
    469   _shunit_shouldSkip && return ${SHUNIT_TRUE}
    470 
    471   shunit_message_=${__shunit_lineno}
    472   if [ $# -eq 3 ]; then
    473     shunit_message_="${shunit_message_}$1"
    474     shift
    475   fi
    476   shunit_expected_=$1
    477   shunit_actual_=$2
    478 
    479   _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected:<${shunit_expected_}> but was:<${shunit_actual_}>"
    480 
    481   unset shunit_message_ shunit_expected_ shunit_actual_
    482   return ${SHUNIT_FALSE}
    483 }
    484 _FAIL_NOT_EQUALS_='eval failNotEquals --lineno "${LINENO:-}"'
    485 
    486 # Records a test failure, stating two values should have been the same.
    487 #
    488 # Args:
    489 #   message: string: failure message [optional]
    490 #   expected: string: expected value
    491 #   actual: string: actual value
    492 # Returns:
    493 #   integer: success (TRUE/FALSE/ERROR constant)
    494 failSame()
    495 {
    496   ${_SHUNIT_LINENO_}
    497   if [ $# -lt 2 -o $# -gt 3 ]; then
    498     _shunit_error "failSame() requires two or three arguments; $# given"
    499     return ${SHUNIT_ERROR}
    500   fi
    501   _shunit_shouldSkip && return ${SHUNIT_TRUE}
    502 
    503   shunit_message_=${__shunit_lineno}
    504   if [ $# -eq 3 ]; then
    505     shunit_message_="${shunit_message_}$1"
    506     shift
    507   fi
    508 
    509   _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected not same"
    510 
    511   unset shunit_message_
    512   return ${SHUNIT_FALSE}
    513 }
    514 _FAIL_SAME_='eval failSame --lineno "${LINENO:-}"'
    515 
    516 # Records a test failure, stating two values were not equal.
    517 #
    518 # This is functionally equivalent to calling failNotEquals().
    519 #
    520 # Args:
    521 #   message: string: failure message [optional]
    522 #   expected: string: expected value
    523 #   actual: string: actual value
    524 # Returns:
    525 #   integer: success (TRUE/FALSE/ERROR constant)
    526 failNotSame()
    527 {
    528   ${_SHUNIT_LINENO_}
    529   if [ $# -lt 2 -o $# -gt 3 ]; then
    530     _shunit_error "failNotEquals() requires one or two arguments; $# given"
    531     return ${SHUNIT_ERROR}
    532   fi
    533   _shunit_shouldSkip && return ${SHUNIT_TRUE}
    534 
    535   shunit_message_=${__shunit_lineno}
    536   if [ $# -eq 3 ]; then
    537     shunit_message_="${shunit_message_}$1"
    538     shift
    539   fi
    540   failNotEquals "${shunit_message_}" "$1" "$2"
    541   shunit_return=$?
    542 
    543   unset shunit_message_
    544   return ${shunit_return}
    545 }
    546 _FAIL_NOT_SAME_='eval failNotSame --lineno "${LINENO:-}"'
    547 
    548 #-----------------------------------------------------------------------------
    549 # skipping functions
    550 #
    551 
    552 # Force remaining assert and fail functions to be "skipped".
    553 #
    554 # This function forces the remaining assert and fail functions to be "skipped",
    555 # i.e. they will have no effect. Each function skipped will be recorded so that
    556 # the total of asserts and fails will not be altered.
    557 #
    558 # Args:
    559 #   None
    560 startSkipping()
    561 {
    562   __shunit_skip=${SHUNIT_TRUE}
    563 }
    564 
    565 # Resume the normal recording behavior of assert and fail calls.
    566 #
    567 # Args:
    568 #   None
    569 endSkipping()
    570 {
    571   __shunit_skip=${SHUNIT_FALSE}
    572 }
    573 
    574 # Returns the state of assert and fail call skipping.
    575 #
    576 # Args:
    577 #   None
    578 # Returns:
    579 #   boolean: (TRUE/FALSE constant)
    580 isSkipping()
    581 {
    582   return ${__shunit_skip}
    583 }
    584 
    585 #-----------------------------------------------------------------------------
    586 # suite functions
    587 #
    588 
    589 # Stub. This function should contains all unit test calls to be made.
    590 #
    591 # DEPRECATED (as of 2.1.0)
    592 #
    593 # This function can be optionally overridden by the user in their test suite.
    594 #
    595 # If this function exists, it will be called when shunit2 is sourced. If it
    596 # does not exist, shunit2 will search the parent script for all functions
    597 # beginning with the word 'test', and they will be added dynamically to the
    598 # test suite.
    599 #
    600 # This function should be overridden by the user in their unit test suite.
    601 # Note: see _shunit_mktempFunc() for actual implementation
    602 #
    603 # Args:
    604 #   None
    605 #suite() { :; }  # DO NOT UNCOMMENT THIS FUNCTION
    606 
    607 # Adds a function name to the list of tests schedule for execution.
    608 #
    609 # This function should only be called from within the suite() function.
    610 #
    611 # Args:
    612 #   function: string: name of a function to add to current unit test suite
    613 suite_addTest()
    614 {
    615   shunit_func_=${1:-}
    616 
    617   __shunit_suite="${__shunit_suite:+${__shunit_suite} }${shunit_func_}"
    618   __shunit_testsTotal=`expr ${__shunit_testsTotal} + 1`
    619 
    620   unset shunit_func_
    621 }
    622 
    623 # Stub. This function will be called once before any tests are run.
    624 #
    625 # Common one-time environment preparation tasks shared by all tests can be
    626 # defined here.
    627 #
    628 # This function should be overridden by the user in their unit test suite.
    629 # Note: see _shunit_mktempFunc() for actual implementation
    630 #
    631 # Args:
    632 #   None
    633 #oneTimeSetUp() { :; }  # DO NOT UNCOMMENT THIS FUNCTION
    634 
    635 # Stub. This function will be called once after all tests are finished.
    636 #
    637 # Common one-time environment cleanup tasks shared by all tests can be defined
    638 # here.
    639 #
    640 # This function should be overridden by the user in their unit test suite.
    641 # Note: see _shunit_mktempFunc() for actual implementation
    642 #
    643 # Args:
    644 #   None
    645 #oneTimeTearDown() { :; }  # DO NOT UNCOMMENT THIS FUNCTION
    646 
    647 # Stub. This function will be called before each test is run.
    648 #
    649 # Common environment preparation tasks shared by all tests can be defined here.
    650 #
    651 # This function should be overridden by the user in their unit test suite.
    652 # Note: see _shunit_mktempFunc() for actual implementation
    653 #
    654 # Args:
    655 #   None
    656 #setUp() { :; }
    657 
    658 # Note: see _shunit_mktempFunc() for actual implementation
    659 # Stub. This function will be called after each test is run.
    660 #
    661 # Common environment cleanup tasks shared by all tests can be defined here.
    662 #
    663 # This function should be overridden by the user in their unit test suite.
    664 # Note: see _shunit_mktempFunc() for actual implementation
    665 #
    666 # Args:
    667 #   None
    668 #tearDown() { :; }  # DO NOT UNCOMMENT THIS FUNCTION
    669 
    670 #------------------------------------------------------------------------------
    671 # internal shUnit2 functions
    672 #
    673 
    674 # Create a temporary directory to store various run-time files in.
    675 #
    676 # This function is a cross-platform temporary directory creation tool. Not all
    677 # OSes have the mktemp function, so one is included here.
    678 #
    679 # Args:
    680 #   None
    681 # Outputs:
    682 #   string: the temporary directory that was created
    683 _shunit_mktempDir()
    684 {
    685   # try the standard mktemp function
    686   ( exec mktemp -dqt shunit.XXXXXX 2>/dev/null ) && return
    687 
    688   # the standard mktemp didn't work.  doing our own.
    689   if [ -r '/dev/urandom' -a -x '/usr/bin/od' ]; then
    690     _shunit_random_=`/usr/bin/od -vAn -N4 -tx4 </dev/urandom \
    691         |sed 's/^[^0-9a-f]*//'`
    692   elif [ -n "${RANDOM:-}" ]; then
    693     # $RANDOM works
    694     _shunit_random_=${RANDOM}${RANDOM}${RANDOM}$$
    695   else
    696     # $RANDOM doesn't work
    697     _shunit_date_=`date '+%Y%m%d%H%M%S'`
    698     _shunit_random_=`expr ${_shunit_date_} / $$`
    699   fi
    700 
    701   _shunit_tmpDir_="${TMPDIR:-/tmp}/shunit.${_shunit_random_}"
    702   ( umask 077 && mkdir "${_shunit_tmpDir_}" ) || \
    703       _shunit_fatal 'could not create temporary directory! exiting'
    704 
    705   echo ${_shunit_tmpDir_}
    706   unset _shunit_date_ _shunit_random_ _shunit_tmpDir_
    707 }
    708 
    709 # This function is here to work around issues in Cygwin.
    710 #
    711 # Args:
    712 #   None
    713 _shunit_mktempFunc()
    714 {
    715   for _shunit_func_ in oneTimeSetUp oneTimeTearDown setUp tearDown suite noexec
    716   do
    717     _shunit_file_="${__shunit_tmpDir}/${_shunit_func_}"
    718     cat <<EOF >"${_shunit_file_}"
    719 #! /bin/sh
    720 exit ${SHUNIT_TRUE}
    721 EOF
    722     chmod +x "${_shunit_file_}"
    723   done
    724 
    725   unset _shunit_file_
    726 }
    727 
    728 # Final cleanup function to leave things as we found them.
    729 #
    730 # Besides removing the temporary directory, this function is in charge of the
    731 # final exit code of the unit test. The exit code is based on how the script
    732 # was ended (e.g. normal exit, or via Ctrl-C).
    733 #
    734 # Args:
    735 #   name: string: name of the trap called (specified when trap defined)
    736 _shunit_cleanup()
    737 {
    738   _shunit_name_=$1
    739 
    740   case ${_shunit_name_} in
    741     EXIT) _shunit_signal_=0 ;;
    742     INT) _shunit_signal_=2 ;;
    743     TERM) _shunit_signal_=15 ;;
    744     *)
    745       _shunit_warn "unrecognized trap value (${_shunit_name_})"
    746       _shunit_signal_=0
    747       ;;
    748   esac
    749 
    750   # do our work
    751   rm -fr "${__shunit_tmpDir}"
    752 
    753   # exit for all non-EXIT signals
    754   if [ ${_shunit_name_} != 'EXIT' ]; then
    755     _shunit_warn "trapped and now handling the (${_shunit_name_}) signal"
    756     # disable EXIT trap
    757     trap 0
    758     # add 128 to signal and exit
    759     exit `expr ${_shunit_signal_} + 128`
    760   elif [ ${__shunit_reportGenerated} -eq ${SHUNIT_FALSE} ] ; then
    761     _shunit_assertFail 'Unknown failure encountered running a test'
    762     _shunit_generateReport
    763     exit ${SHUNIT_ERROR}
    764   fi
    765 
    766   unset _shunit_name_ _shunit_signal_
    767 }
    768 
    769 # The actual running of the tests happens here.
    770 #
    771 # Args:
    772 #   None
    773 _shunit_execSuite()
    774 {
    775   for _shunit_test_ in ${__shunit_suite}; do
    776     __shunit_testSuccess=${SHUNIT_TRUE}
    777 
    778     # disable skipping
    779     endSkipping
    780 
    781     # execute the per-test setup function
    782     setUp
    783 
    784     # execute the test
    785     echo "${_shunit_test_}"
    786     eval ${_shunit_test_}
    787 
    788     # execute the per-test tear-down function
    789     tearDown
    790 
    791     # update stats
    792     if [ ${__shunit_testSuccess} -eq ${SHUNIT_TRUE} ]; then
    793       __shunit_testsPassed=`expr ${__shunit_testsPassed} + 1`
    794     else
    795       __shunit_testsFailed=`expr ${__shunit_testsFailed} + 1`
    796     fi
    797   done
    798 
    799   unset _shunit_test_
    800 }
    801 
    802 # Generates the user friendly report with appropriate OK/FAILED message.
    803 #
    804 # Args:
    805 #   None
    806 # Output:
    807 #   string: the report of successful and failed tests, as well as totals.
    808 _shunit_generateReport()
    809 {
    810   _shunit_ok_=${SHUNIT_TRUE}
    811 
    812   # if no exit code was provided one, determine an appropriate one
    813   [ ${__shunit_testsFailed} -gt 0 \
    814       -o ${__shunit_testSuccess} -eq ${SHUNIT_FALSE} ] \
    815           && _shunit_ok_=${SHUNIT_FALSE}
    816 
    817   echo
    818   if [ ${__shunit_testsTotal} -eq 1 ]; then
    819     echo "Ran ${__shunit_testsTotal} test."
    820   else
    821     echo "Ran ${__shunit_testsTotal} tests."
    822   fi
    823 
    824   _shunit_failures_=''
    825   _shunit_skipped_=''
    826   [ ${__shunit_assertsFailed} -gt 0 ] \
    827       && _shunit_failures_="failures=${__shunit_assertsFailed}"
    828   [ ${__shunit_assertsSkipped} -gt 0 ] \
    829       && _shunit_skipped_="skipped=${__shunit_assertsSkipped}"
    830 
    831   if [ ${_shunit_ok_} -eq ${SHUNIT_TRUE} ]; then
    832     _shunit_msg_='OK'
    833     [ -n "${_shunit_skipped_}" ] \
    834         && _shunit_msg_="${_shunit_msg_} (${_shunit_skipped_})"
    835   else
    836     _shunit_msg_="FAILED (${_shunit_failures_}"
    837     [ -n "${_shunit_skipped_}" ] \
    838         && _shunit_msg_="${_shunit_msg_},${_shunit_skipped_}"
    839     _shunit_msg_="${_shunit_msg_})"
    840   fi
    841 
    842   echo
    843   echo ${_shunit_msg_}
    844   __shunit_reportGenerated=${SHUNIT_TRUE}
    845 
    846   unset _shunit_failures_ _shunit_msg_ _shunit_ok_ _shunit_skipped_
    847 }
    848 
    849 # Test for whether a function should be skipped.
    850 #
    851 # Args:
    852 #   None
    853 # Returns:
    854 #   boolean: whether the test should be skipped (TRUE/FALSE constant)
    855 _shunit_shouldSkip()
    856 {
    857   [ ${__shunit_skip} -eq ${SHUNIT_FALSE} ] && return ${SHUNIT_FALSE}
    858   _shunit_assertSkip
    859 }
    860 
    861 # Records a successful test.
    862 #
    863 # Args:
    864 #   None
    865 _shunit_assertPass()
    866 {
    867   __shunit_assertsPassed=`expr ${__shunit_assertsPassed} + 1`
    868   __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1`
    869 }
    870 
    871 # Records a test failure.
    872 #
    873 # Args:
    874 #   message: string: failure message to provide user
    875 _shunit_assertFail()
    876 {
    877   _shunit_msg_=$1
    878 
    879   __shunit_testSuccess=${SHUNIT_FALSE}
    880   __shunit_assertsFailed=`expr ${__shunit_assertsFailed} + 1`
    881   __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1`
    882   echo "${__SHUNIT_ASSERT_MSG_PREFIX}${_shunit_msg_}"
    883 
    884   unset _shunit_msg_
    885 }
    886 
    887 # Records a skipped test.
    888 #
    889 # Args:
    890 #   None
    891 _shunit_assertSkip()
    892 {
    893   __shunit_assertsSkipped=`expr ${__shunit_assertsSkipped} + 1`
    894   __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1`
    895 }
    896 
    897 # Prepare a script filename for sourcing.
    898 #
    899 # Args:
    900 #   script: string: path to a script to source
    901 # Returns:
    902 #   string: filename prefixed with ./ (if necessary)
    903 _shunit_prepForSourcing()
    904 {
    905   _shunit_script_=$1
    906   case "${_shunit_script_}" in
    907     /*|./*) echo "${_shunit_script_}" ;;
    908     *) echo "./${_shunit_script_}" ;;
    909   esac
    910   unset _shunit_script_
    911 }
    912 
    913 # Escape a character in a string.
    914 #
    915 # Args:
    916 #   c: string: unescaped character
    917 #   s: string: to escape character in
    918 # Returns:
    919 #   string: with escaped character(s)
    920 _shunit_escapeCharInStr()
    921 {
    922   [ -n "$2" ] || return  # no point in doing work on an empty string
    923 
    924   # Note: using shorter variable names to prevent conflicts with
    925   # _shunit_escapeCharactersInString().
    926   _shunit_c_=$1
    927   _shunit_s_=$2
    928 
    929 
    930   # escape the character
    931   echo ''${_shunit_s_}'' |sed 's/\'${_shunit_c_}'/\\\'${_shunit_c_}'/g'
    932 
    933   unset _shunit_c_ _shunit_s_
    934 }
    935 
    936 # Escape a character in a string.
    937 #
    938 # Args:
    939 #   str: string: to escape characters in
    940 # Returns:
    941 #   string: with escaped character(s)
    942 _shunit_escapeCharactersInString()
    943 {
    944   [ -n "$1" ] || return  # no point in doing work on an empty string
    945 
    946   _shunit_str_=$1
    947 
    948   # Note: using longer variable names to prevent conflicts with
    949   # _shunit_escapeCharInStr().
    950   for _shunit_char_ in '"' '$' "'" '`'; do
    951     _shunit_str_=`_shunit_escapeCharInStr "${_shunit_char_}" "${_shunit_str_}"`
    952   done
    953 
    954   echo "${_shunit_str_}"
    955   unset _shunit_char_ _shunit_str_
    956 }
    957 
    958 # Extract list of functions to run tests against.
    959 #
    960 # Args:
    961 #   script: string: name of script to extract functions from
    962 # Returns:
    963 #   string: of function names
    964 _shunit_extractTestFunctions()
    965 {
    966   _shunit_script_=$1
    967 
    968   # extract the lines with test function names, strip of anything besides the
    969   # function name, and output everything on a single line.
    970   _shunit_regex_='^[ 	]*(function )*test[A-Za-z0-9_]* *\(\)'
    971   egrep "${_shunit_regex_}" "${_shunit_script_}" \
    972   |sed 's/^[^A-Za-z0-9_]*//;s/^function //;s/\([A-Za-z0-9_]*\).*/\1/g' \
    973   |xargs
    974 
    975   unset _shunit_regex_ _shunit_script_
    976 }
    977 
    978 #------------------------------------------------------------------------------
    979 # main
    980 #
    981 
    982 # determine the operating mode
    983 if [ $# -eq 0 ]; then
    984   __shunit_script=${__SHUNIT_PARENT}
    985   __shunit_mode=${__SHUNIT_MODE_SOURCED}
    986 else
    987   __shunit_script=$1
    988   [ -r "${__shunit_script}" ] || \
    989       _shunit_fatal "unable to read from ${__shunit_script}"
    990   __shunit_mode=${__SHUNIT_MODE_STANDALONE}
    991 fi
    992 
    993 # create a temporary storage location
    994 __shunit_tmpDir=`_shunit_mktempDir`
    995 
    996 # provide a public temporary directory for unit test scripts
    997 # TODO(kward): document this
    998 SHUNIT_TMPDIR="${__shunit_tmpDir}/tmp"
    999 mkdir "${SHUNIT_TMPDIR}"
   1000 
   1001 # setup traps to clean up after ourselves
   1002 trap '_shunit_cleanup EXIT' 0
   1003 trap '_shunit_cleanup INT' 2
   1004 trap '_shunit_cleanup TERM' 15
   1005 
   1006 # create phantom functions to work around issues with Cygwin
   1007 _shunit_mktempFunc
   1008 PATH="${__shunit_tmpDir}:${PATH}"
   1009 
   1010 # make sure phantom functions are executable. this will bite if /tmp (or the
   1011 # current $TMPDIR) points to a path on a partition that was mounted with the
   1012 # 'noexec' option. the noexec command was created with _shunit_mktempFunc().
   1013 noexec 2>/dev/null || _shunit_fatal \
   1014     'please declare TMPDIR with path on partition with exec permission'
   1015 
   1016 # we must manually source the tests in standalone mode
   1017 if [ "${__shunit_mode}" = "${__SHUNIT_MODE_STANDALONE}" ]; then
   1018   . "`_shunit_prepForSourcing \"${__shunit_script}\"`"
   1019 fi
   1020 
   1021 # execute the oneTimeSetUp function (if it exists)
   1022 oneTimeSetUp
   1023 
   1024 # execute the suite function defined in the parent test script
   1025 # deprecated as of 2.1.0
   1026 suite
   1027 
   1028 # if no suite function was defined, dynamically build a list of functions
   1029 if [ -z "${__shunit_suite}" ]; then
   1030   shunit_funcs_=`_shunit_extractTestFunctions "${__shunit_script}"`
   1031   for shunit_func_ in ${shunit_funcs_}; do
   1032     suite_addTest ${shunit_func_}
   1033   done
   1034 fi
   1035 unset shunit_func_ shunit_funcs_
   1036 
   1037 # execute the tests
   1038 _shunit_execSuite
   1039 
   1040 # execute the oneTimeTearDown function (if it exists)
   1041 oneTimeTearDown
   1042 
   1043 # generate the report
   1044 _shunit_generateReport
   1045 
   1046 # that's it folks
   1047 [ ${__shunit_testsFailed} -eq 0 ]
   1048 exit $?
   1049