2 # $Id: shunit2 335 2011-05-01 20:10:33Z kate.ward@forestent.com $
3 # vim:et:ft=sh:sts=2:sw=2
5 # Copyright 2008 Kate Ward. All Rights Reserved.
6 # Released under the LGPL (GNU Lesser General Public License)
8 # shUnit2 -- Unit testing framework for Unix shell scripts.
9 # http://code.google.com/p/shunit2/
11 # Author: kate.ward@forestent.com (Kate Ward)
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.
16 # return if shunit already loaded
17 [ -n "${SHUNIT_VERSION:-}" ] && exit 0
19 SHUNIT_VERSION='2.1.6'
25 # enable strict mode by default
26 SHUNIT_STRICT=${SHUNIT_STRICT:-${SHUNIT_TRUE}}
28 _shunit_warn() { echo "shunit2:WARN $@" >&2; }
29 _shunit_error() { echo "shunit2:ERROR $@" >&2; }
30 _shunit_fatal() { echo "shunit2:FATAL $@" >&2; exit ${SHUNIT_ERROR}; }
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'
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"
48 __SHUNIT_ASSERT_MSG_PREFIX='ASSERT:'
49 __SHUNIT_MODE_SOURCED='sourced'
50 __SHUNIT_MODE_STANDALONE='standalone'
51 __SHUNIT_PARENT=${SHUNIT_PARENT:-$0}
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
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
64 readonly ${shunit_ro_opts_} ${shunit_constant_}
66 unset shunit_constant_ shunit_constants_ shunit_ro_opts_
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
77 __shunit_testSuccess=${SHUNIT_TRUE}
79 __shunit_testsPassed=0
80 __shunit_testsFailed=0
83 __shunit_assertsTotal=0
84 __shunit_assertsPassed=0
85 __shunit_assertsFailed=0
86 __shunit_assertsSkipped=0
89 _SHUNIT_LINENO_='eval __shunit_lineno=""; if [ "${1:-}" = "--lineno" ]; then [ -n "$2" ] && __shunit_lineno="[$2] "; shift 2; fi'
91 #-----------------------------------------------------------------------------
95 # Assert that two values are equal to one another.
98 # message: string: failure message [optional]
99 # expected: string: expected value
100 # actual: string: actual value
102 # integer: success (TRUE/FALSE/ERROR constant)
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}${4:+ 4: $4}"
109 return ${SHUNIT_ERROR}
111 _shunit_shouldSkip && return ${SHUNIT_TRUE}
113 shunit_message_=${__shunit_lineno}
114 local assertion_name=""
115 if [ $# -eq 3 ]; then
117 shunit_message_="${shunit_message_}$1"
123 shunit_return=${SHUNIT_TRUE}
124 if [ "${shunit_expected_}" = "${shunit_actual_}" ]; then
125 if [ ! -z "$DEBUGGING" -a ! -z "$assertion_name" ]; then
126 echo " OKAY: $assertion_name"
130 failNotEquals "${shunit_message_}" "${shunit_expected_}" "${shunit_actual_}"
131 shunit_return=${SHUNIT_FALSE}
134 unset shunit_message_ shunit_expected_ shunit_actual_
135 return ${shunit_return}
137 _ASSERT_EQUALS_='eval assertEquals --lineno "${LINENO:-}"'
139 # Assert that two values are not equal to one another.
142 # message: string: failure message [optional]
143 # expected: string: expected value
144 # actual: string: actual value
146 # integer: success (TRUE/FALSE/ERROR constant)
150 if [ $# -lt 2 -o $# -gt 3 ]; then
151 _shunit_error "assertNotEquals() requires two or three arguments; $# given"
152 return ${SHUNIT_ERROR}
154 _shunit_shouldSkip && return ${SHUNIT_TRUE}
156 shunit_message_=${__shunit_lineno}
157 local assertion_name=""
158 if [ $# -eq 3 ]; then
160 shunit_message_="${shunit_message_}$1"
166 shunit_return=${SHUNIT_TRUE}
167 if [ "${shunit_expected_}" != "${shunit_actual_}" ]; then
168 if [ ! -z "$DEBUGGING" -a ! -z "$assertion_name" ]; then
169 echo " OKAY: $assertion_name"
173 failSame "${shunit_message_}" "$@"
174 shunit_return=${SHUNIT_FALSE}
177 unset shunit_message_ shunit_expected_ shunit_actual_
178 return ${shunit_return}
180 _ASSERT_NOT_EQUALS_='eval assertNotEquals --lineno "${LINENO:-}"'
182 # Assert that a value is null (i.e. an empty string)
185 # message: string: failure message [optional]
186 # actual: string: actual value
188 # integer: success (TRUE/FALSE/ERROR constant)
192 if [ $# -lt 1 -o $# -gt 2 ]; then
193 _shunit_error "assertNull() requires one or two arguments; $# given"
194 return ${SHUNIT_ERROR}
196 _shunit_shouldSkip && return ${SHUNIT_TRUE}
198 shunit_message_=${__shunit_lineno}
199 if [ $# -eq 2 ]; then
200 shunit_message_="${shunit_message_}$1"
203 assertTrue "${shunit_message_}" "[ -z '$1' ]"
206 unset shunit_message_
207 return ${shunit_return}
209 _ASSERT_NULL_='eval assertNull --lineno "${LINENO:-}"'
211 # Assert that a value is not null (i.e. a non-empty string)
214 # message: string: failure message [optional]
215 # actual: string: actual value
217 # integer: success (TRUE/FALSE/ERROR constant)
221 if [ $# -gt 2 ]; then # allowing 0 arguments as $1 might actually be null
222 _shunit_error "assertNotNull() requires one or two arguments; $# given"
223 return ${SHUNIT_ERROR}
225 _shunit_shouldSkip && return ${SHUNIT_TRUE}
227 shunit_message_=${__shunit_lineno}
228 if [ $# -eq 2 ]; then
229 shunit_message_="${shunit_message_}$1"
232 shunit_actual_=`_shunit_escapeCharactersInString "${1:-}"`
233 test -n "${shunit_actual_}"
234 assertTrue "${shunit_message_}" $?
237 unset shunit_actual_ shunit_message_
238 return ${shunit_return}
240 _ASSERT_NOT_NULL_='eval assertNotNull --lineno "${LINENO:-}"'
242 # Assert that two values are the same (i.e. equal to one another).
245 # message: string: failure message [optional]
246 # expected: string: expected value
247 # actual: string: actual value
249 # integer: success (TRUE/FALSE/ERROR constant)
253 if [ $# -lt 2 -o $# -gt 3 ]; then
254 _shunit_error "assertSame() requires two or three arguments; $# given"
255 return ${SHUNIT_ERROR}
257 _shunit_shouldSkip && return ${SHUNIT_TRUE}
259 shunit_message_=${__shunit_lineno}
260 if [ $# -eq 3 ]; then
261 shunit_message_="${shunit_message_}$1"
264 assertEquals "${shunit_message_}" "$1" "$2"
267 unset shunit_message_
268 return ${shunit_return}
270 _ASSERT_SAME_='eval assertSame --lineno "${LINENO:-}"'
272 # Assert that two values are not the same (i.e. not equal to one another).
275 # message: string: failure message [optional]
276 # expected: string: expected value
277 # actual: string: actual value
279 # integer: success (TRUE/FALSE/ERROR constant)
283 if [ $# -lt 2 -o $# -gt 3 ]; then
284 _shunit_error "assertNotSame() requires two or three arguments; $# given"
285 return ${SHUNIT_ERROR}
287 _shunit_shouldSkip && return ${SHUNIT_TRUE}
289 shunit_message_=${__shunit_lineno}
290 if [ $# -eq 3 ]; then
291 shunit_message_="${shunit_message_:-}$1"
294 assertNotEquals "${shunit_message_}" "$1" "$2"
297 unset shunit_message_
298 return ${shunit_return}
300 _ASSERT_NOT_SAME_='eval assertNotSame --lineno "${LINENO:-}"'
302 # Assert that a value or shell test condition is true.
304 # In shell, a value of 0 is true and a non-zero value is false. Any integer
305 # value passed can thereby be tested.
307 # Shell supports much more complicated tests though, and a means to support
308 # them was needed. As such, this function tests that conditions are true or
309 # false through evaluation rather than just looking for a true or false.
311 # The following test will succeed:
313 # assertTrue "[ 34 -gt 23 ]"
314 # The folloing test will fail with a message:
316 # assertTrue "test failed" "[ -r '/non/existant/file' ]"
319 # message: string: failure message [optional]
320 # condition: string: integer value or shell conditional statement
322 # integer: success (TRUE/FALSE/ERROR constant)
326 if [ $# -gt 2 ]; then
327 _shunit_error "assertTrue() takes one two arguments; $# given"
328 return ${SHUNIT_ERROR}
330 _shunit_shouldSkip && return ${SHUNIT_TRUE}
332 shunit_message_=${__shunit_lineno}
333 local assertion_name=""
334 if [ $# -eq 2 ]; then
336 shunit_message_="${shunit_message_}$1"
341 # see if condition is an integer, i.e. a return value
342 shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'`
343 shunit_return=${SHUNIT_TRUE}
344 if [ -z "${shunit_condition_}" ]; then
346 shunit_return=${SHUNIT_FALSE}
347 elif [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ]
349 # possible return value. treating 0 as true, and non-zero as false.
350 [ ${shunit_condition_} -ne 0 ] && shunit_return=${SHUNIT_FALSE}
352 # (hopefully) a condition
353 ( eval ${shunit_condition_} ) >/dev/null 2>&1
354 [ $? -ne 0 ] && shunit_return=${SHUNIT_FALSE}
358 if [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then
359 if [ ! -z "$DEBUGGING" -a ! -z "$assertion_name" ]; then
360 echo " OKAY: $assertion_name"
364 _shunit_assertFail "${shunit_message_}"
367 unset shunit_message_ shunit_condition_ shunit_match_
368 return ${shunit_return}
370 _ASSERT_TRUE_='eval assertTrue --lineno "${LINENO:-}"'
372 # Assert that a value or shell test condition is false.
374 # In shell, a value of 0 is true and a non-zero value is false. Any integer
375 # value passed can thereby be tested.
377 # Shell supports much more complicated tests though, and a means to support
378 # them was needed. As such, this function tests that conditions are true or
379 # false through evaluation rather than just looking for a true or false.
381 # The following test will succeed:
383 # assertFalse "[ 'apples' = 'oranges' ]"
384 # The folloing test will fail with a message:
386 # assertFalse "test failed" "[ 1 -eq 1 -a 2 -eq 2 ]"
389 # message: string: failure message [optional]
390 # condition: string: integer value or shell conditional statement
392 # integer: success (TRUE/FALSE/ERROR constant)
396 if [ $# -lt 1 -o $# -gt 2 ]; then
397 _shunit_error "assertFalse() quires one or two arguments; $# given"
398 return ${SHUNIT_ERROR}
400 _shunit_shouldSkip && return ${SHUNIT_TRUE}
402 shunit_message_=${__shunit_lineno}
403 local assertion_name=""
404 if [ $# -eq 2 ]; then
406 shunit_message_="${shunit_message_}$1"
411 # see if condition is an integer, i.e. a return value
412 shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'`
413 shunit_return=${SHUNIT_TRUE}
414 if [ -z "${shunit_condition_}" ]; then
416 shunit_return=${SHUNIT_FALSE}
417 elif [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ]
419 # possible return value. treating 0 as true, and non-zero as false.
420 [ ${shunit_condition_} -eq 0 ] && shunit_return=${SHUNIT_FALSE}
422 # (hopefully) a condition
423 ( eval ${shunit_condition_} ) >/dev/null 2>&1
424 [ $? -eq 0 ] && shunit_return=${SHUNIT_FALSE}
428 if [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then
429 if [ ! -z "$DEBUGGING" -a ! -z "$assertion_name" ]; then
430 echo " OKAY: $assertion_name"
434 _shunit_assertFail "${shunit_message_}"
437 unset shunit_message_ shunit_condition_ shunit_match_
438 return ${shunit_return}
440 _ASSERT_FALSE_='eval assertFalse --lineno "${LINENO:-}"'
442 #-----------------------------------------------------------------------------
446 # Records a test failure.
449 # message: string: failure message [optional]
451 # integer: success (TRUE/FALSE/ERROR constant)
455 if [ $# -gt 1 ]; then
456 _shunit_error "fail() requires zero or one arguments; $# given"
457 return ${SHUNIT_ERROR}
459 _shunit_shouldSkip && return ${SHUNIT_TRUE}
461 shunit_message_=${__shunit_lineno}
462 if [ $# -eq 1 ]; then
463 shunit_message_="${shunit_message_}$1"
467 _shunit_assertFail "${shunit_message_}"
469 unset shunit_message_
470 return ${SHUNIT_FALSE}
472 _FAIL_='eval fail --lineno "${LINENO:-}"'
474 # Records a test failure, stating two values were not equal.
477 # message: string: failure message [optional]
478 # expected: string: expected value
479 # actual: string: actual value
481 # integer: success (TRUE/FALSE/ERROR constant)
485 if [ $# -lt 2 -o $# -gt 3 ]; then
486 _shunit_error "failNotEquals() requires one or two arguments; $# given"
487 return ${SHUNIT_ERROR}
489 _shunit_shouldSkip && return ${SHUNIT_TRUE}
491 shunit_message_=${__shunit_lineno}
492 if [ $# -eq 3 ]; then
493 shunit_message_="${shunit_message_}$1"
499 _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected:<${shunit_expected_}> but was:<${shunit_actual_}>"
501 unset shunit_message_ shunit_expected_ shunit_actual_
502 return ${SHUNIT_FALSE}
504 _FAIL_NOT_EQUALS_='eval failNotEquals --lineno "${LINENO:-}"'
506 # Records a test failure, stating two values should have been the same.
509 # message: string: failure message [optional]
510 # expected: string: expected value
511 # actual: string: actual value
513 # integer: success (TRUE/FALSE/ERROR constant)
517 if [ $# -lt 2 -o $# -gt 3 ]; then
518 _shunit_error "failSame() requires two or three arguments; $# given"
519 return ${SHUNIT_ERROR}
521 _shunit_shouldSkip && return ${SHUNIT_TRUE}
523 shunit_message_=${__shunit_lineno}
524 if [ $# -eq 3 ]; then
525 shunit_message_="${shunit_message_}$1"
529 _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected not same"
531 unset shunit_message_
532 return ${SHUNIT_FALSE}
534 _FAIL_SAME_='eval failSame --lineno "${LINENO:-}"'
536 # Records a test failure, stating two values were not equal.
538 # This is functionally equivalent to calling failNotEquals().
541 # message: string: failure message [optional]
542 # expected: string: expected value
543 # actual: string: actual value
545 # integer: success (TRUE/FALSE/ERROR constant)
549 if [ $# -lt 2 -o $# -gt 3 ]; then
550 _shunit_error "failNotEquals() requires one or two arguments; $# given"
551 return ${SHUNIT_ERROR}
553 _shunit_shouldSkip && return ${SHUNIT_TRUE}
555 shunit_message_=${__shunit_lineno}
556 if [ $# -eq 3 ]; then
557 shunit_message_="${shunit_message_}$1"
560 failNotEquals "${shunit_message_}" "$1" "$2"
563 unset shunit_message_
564 return ${shunit_return}
566 _FAIL_NOT_SAME_='eval failNotSame --lineno "${LINENO:-}"'
568 #-----------------------------------------------------------------------------
572 # Force remaining assert and fail functions to be "skipped".
574 # This function forces the remaining assert and fail functions to be "skipped",
575 # i.e. they will have no effect. Each function skipped will be recorded so that
576 # the total of asserts and fails will not be altered.
582 __shunit_skip=${SHUNIT_TRUE}
585 # Resume the normal recording behavior of assert and fail calls.
591 __shunit_skip=${SHUNIT_FALSE}
594 # Returns the state of assert and fail call skipping.
599 # boolean: (TRUE/FALSE constant)
602 return ${__shunit_skip}
605 #-----------------------------------------------------------------------------
609 # Stub. This function should contains all unit test calls to be made.
611 # DEPRECATED (as of 2.1.0)
613 # This function can be optionally overridden by the user in their test suite.
615 # If this function exists, it will be called when shunit2 is sourced. If it
616 # does not exist, shunit2 will search the parent script for all functions
617 # beginning with the word 'test', and they will be added dynamically to the
620 # This function should be overridden by the user in their unit test suite.
621 # Note: see _shunit_mktempFunc() for actual implementation
625 #suite() { :; } # DO NOT UNCOMMENT THIS FUNCTION
627 # Adds a function name to the list of tests schedule for execution.
629 # This function should only be called from within the suite() function.
632 # function: string: name of a function to add to current unit test suite
637 __shunit_suite="${__shunit_suite:+${__shunit_suite} }${shunit_func_}"
638 __shunit_testsTotal=`expr ${__shunit_testsTotal} + 1`
643 # Stub. This function will be called once before any tests are run.
645 # Common one-time environment preparation tasks shared by all tests can be
648 # This function should be overridden by the user in their unit test suite.
649 # Note: see _shunit_mktempFunc() for actual implementation
653 #oneTimeSetUp() { :; } # DO NOT UNCOMMENT THIS FUNCTION
655 # Stub. This function will be called once after all tests are finished.
657 # Common one-time environment cleanup tasks shared by all tests can be defined
660 # This function should be overridden by the user in their unit test suite.
661 # Note: see _shunit_mktempFunc() for actual implementation
665 #oneTimeTearDown() { :; } # DO NOT UNCOMMENT THIS FUNCTION
667 # Stub. This function will be called before each test is run.
669 # Common environment preparation tasks shared by all tests can be defined here.
671 # This function should be overridden by the user in their unit test suite.
672 # Note: see _shunit_mktempFunc() for actual implementation
678 # Note: see _shunit_mktempFunc() for actual implementation
679 # Stub. This function will be called after each test is run.
681 # Common environment cleanup tasks shared by all tests can be defined here.
683 # This function should be overridden by the user in their unit test suite.
684 # Note: see _shunit_mktempFunc() for actual implementation
688 #tearDown() { :; } # DO NOT UNCOMMENT THIS FUNCTION
690 #------------------------------------------------------------------------------
691 # internal shUnit2 functions
694 # Create a temporary directory to store various run-time files in.
696 # This function is a cross-platform temporary directory creation tool. Not all
697 # OSes have the mktemp function, so one is included here.
702 # string: the temporary directory that was created
705 # try the standard mktemp function
706 ( exec mktemp -dqt shunit.XXXXXX 2>/dev/null ) && return
708 # the standard mktemp didn't work. doing our own.
709 if [ -r '/dev/urandom' -a -x '/usr/bin/od' ]; then
710 _shunit_random_=`/usr/bin/od -vAn -N4 -tx4 </dev/urandom \
711 |sed 's/^[^0-9a-f]*//'`
712 elif [ -n "${RANDOM:-}" ]; then
714 _shunit_random_=${RANDOM}${RANDOM}${RANDOM}$$
716 # $RANDOM doesn't work
717 _shunit_date_=`date '+%Y%m%d%H%M%S'`
718 _shunit_random_=`expr ${_shunit_date_} / $$`
721 _shunit_tmpDir_="${TMPDIR:-/tmp}/shunit.${_shunit_random_}"
722 ( umask 077 && mkdir "${_shunit_tmpDir_}" ) || \
723 _shunit_fatal 'could not create temporary directory! exiting'
725 echo ${_shunit_tmpDir_}
726 unset _shunit_date_ _shunit_random_ _shunit_tmpDir_
729 # This function is here to work around issues in Cygwin.
735 for _shunit_func_ in oneTimeSetUp oneTimeTearDown setUp tearDown suite noexec
737 _shunit_file_="${__shunit_tmpDir}/${_shunit_func_}"
738 cat <<EOF >"${_shunit_file_}"
742 chmod +x "${_shunit_file_}"
748 # Final cleanup function to leave things as we found them.
750 # Besides removing the temporary directory, this function is in charge of the
751 # final exit code of the unit test. The exit code is based on how the script
752 # was ended (e.g. normal exit, or via Ctrl-C).
755 # name: string: name of the trap called (specified when trap defined)
760 case ${_shunit_name_} in
761 EXIT) _shunit_signal_=0 ;;
762 INT) _shunit_signal_=2 ;;
763 TERM) _shunit_signal_=15 ;;
765 _shunit_warn "unrecognized trap value (${_shunit_name_})"
771 rm -fr "${__shunit_tmpDir}"
773 # exit for all non-EXIT signals
774 if [ ${_shunit_name_} != 'EXIT' ]; then
775 _shunit_warn "trapped and now handling the (${_shunit_name_}) signal"
778 # add 128 to signal and exit
779 exit `expr ${_shunit_signal_} + 128`
780 elif [ ${__shunit_reportGenerated} -eq ${SHUNIT_FALSE} ] ; then
781 _shunit_assertFail 'Unknown failure encountered running a test'
782 _shunit_generateReport
786 unset _shunit_name_ _shunit_signal_
789 # The actual running of the tests happens here.
795 for _shunit_test_ in ${__shunit_suite}; do
796 __shunit_testSuccess=${SHUNIT_TRUE}
801 # execute the per-test setup function
807 echo "-------------------------------------------------------"
808 echo "$(date): [${_shunit_test_}]"
809 eval ${_shunit_test_}
811 # execute the per-test tear-down function
815 if [ ${__shunit_testSuccess} -eq ${SHUNIT_TRUE} ]; then
816 __shunit_testsPassed=`expr ${__shunit_testsPassed} + 1`
818 __shunit_testsFailed=`expr ${__shunit_testsFailed} + 1`
825 # Generates the user friendly report with appropriate OKAY/FAILED message.
830 # string: the report of successful and failed tests, as well as totals.
831 _shunit_generateReport()
833 _shunit_ok_=${SHUNIT_TRUE}
835 # if no exit code was provided one, determine an appropriate one
836 [ ${__shunit_testsFailed} -gt 0 \
837 -o ${__shunit_testSuccess} -eq ${SHUNIT_FALSE} ] \
838 && _shunit_ok_=${SHUNIT_FALSE}
841 if [ ${__shunit_testsTotal} -eq 1 ]; then
842 echo "$(date): Ran ${__shunit_testsTotal} test."
844 echo "$(date): Ran ${__shunit_testsTotal} tests."
849 [ ${__shunit_assertsFailed} -gt 0 ] \
850 && _shunit_failures_="failures=${__shunit_assertsFailed}"
851 [ ${__shunit_assertsSkipped} -gt 0 ] \
852 && _shunit_skipped_="skipped=${__shunit_assertsSkipped}"
854 if [ ${_shunit_ok_} -eq ${SHUNIT_TRUE} ]; then
855 _shunit_msg_="$(basename $0) PASSED 100% OKAY"
856 [ -n "${_shunit_skipped_}" ] \
857 && _shunit_msg_="${_shunit_msg_} (${_shunit_skipped_})"
859 _shunit_msg_="$(basename $0) FAILED (${_shunit_failures_}"
860 [ -n "${_shunit_skipped_}" ] \
861 && _shunit_msg_="${_shunit_msg_},${_shunit_skipped_}"
862 _shunit_msg_="${_shunit_msg_})"
865 if [ -z "$suite_end" ]; then
866 # make sure we don't get confused, since suite aborted early.
867 suite_end=$(date +%s)
869 # we keep duration_s for later printing.
870 duration_s=$(($suite_end - $suite_start))
871 # calculate full minutes count based on seconds.
872 duration_m=$(($duration_s / 60))
873 # calculate how many hours that is.
874 duration_h=$(($duration_m / 60))
875 # fix the minutes since we chopped those hours out.
876 duration_m=$(($duration_m - $duration_h * 60))
877 if [ $duration_m -lt 10 ]; then duration_m="0$duration_m"; fi
878 if [ $duration_h -lt 10 ]; then duration_h="0$duration_h"; fi
879 echo "Test suite ran for $duration_s total seconds [$duration_h:$duration_m hh:mm]"
881 echo "$(date): ${_shunit_msg_}"
882 __shunit_reportGenerated=${SHUNIT_TRUE}
884 unset _shunit_failures_ _shunit_msg_ _shunit_ok_ _shunit_skipped_
887 # Test for whether a function should be skipped.
892 # boolean: whether the test should be skipped (TRUE/FALSE constant)
895 [ ${__shunit_skip} -eq ${SHUNIT_FALSE} ] && return ${SHUNIT_FALSE}
899 # Records a successful test.
905 __shunit_assertsPassed=`expr ${__shunit_assertsPassed} + 1`
906 __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1`
909 # Records a test failure.
912 # message: string: failure message to provide user
917 __shunit_testSuccess=${SHUNIT_FALSE}
918 __shunit_assertsFailed=`expr ${__shunit_assertsFailed} + 1`
919 __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1`
920 echo "${__SHUNIT_ASSERT_MSG_PREFIX}${_shunit_msg_}"
925 # Records a skipped test.
931 __shunit_assertsSkipped=`expr ${__shunit_assertsSkipped} + 1`
932 __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1`
935 # Prepare a script filename for sourcing.
938 # script: string: path to a script to source
940 # string: filename prefixed with ./ (if necessary)
941 _shunit_prepForSourcing()
944 case "${_shunit_script_}" in
945 /*|./*) echo "${_shunit_script_}" ;;
946 *) echo "./${_shunit_script_}" ;;
948 unset _shunit_script_
951 # Escape a character in a string.
954 # c: string: unescaped character
955 # s: string: to escape character in
957 # string: with escaped character(s)
958 _shunit_escapeCharInStr()
960 [ -n "$2" ] || return # no point in doing work on an empty string
962 # Note: using shorter variable names to prevent conflicts with
963 # _shunit_escapeCharactersInString().
968 # escape the character
969 echo ''${_shunit_s_}'' |sed 's/\'${_shunit_c_}'/\\\'${_shunit_c_}'/g'
971 unset _shunit_c_ _shunit_s_
974 # Escape a character in a string.
977 # str: string: to escape characters in
979 # string: with escaped character(s)
980 _shunit_escapeCharactersInString()
982 [ -n "$1" ] || return # no point in doing work on an empty string
986 # Note: using longer variable names to prevent conflicts with
987 # _shunit_escapeCharInStr().
988 for _shunit_char_ in '"' '$' "'" '`'; do
989 _shunit_str_=`_shunit_escapeCharInStr "${_shunit_char_}" "${_shunit_str_}"`
992 echo "${_shunit_str_}"
993 unset _shunit_char_ _shunit_str_
996 # Extract list of functions to run tests against.
999 # script: string: name of script to extract functions from
1001 # string: of function names
1002 _shunit_extractTestFunctions()
1006 # extract the lines with test function names, strip of anything besides the
1007 # function name, and output everything on a single line.
1008 _shunit_regex_='^[ ]*(function )*test[A-Za-z0-9_]* *\(\)'
1009 egrep "${_shunit_regex_}" "${_shunit_script_}" \
1010 |sed 's/^[^A-Za-z0-9_]*//;s/^function //;s/\([A-Za-z0-9_]*\).*/\1/g' \
1013 unset _shunit_regex_ _shunit_script_
1016 #------------------------------------------------------------------------------
1020 # determine the operating mode
1021 if [ $# -eq 0 ]; then
1022 __shunit_script=${__SHUNIT_PARENT}
1023 __shunit_mode=${__SHUNIT_MODE_SOURCED}
1026 [ -r "${__shunit_script}" ] || \
1027 _shunit_fatal "unable to read from ${__shunit_script}"
1028 __shunit_mode=${__SHUNIT_MODE_STANDALONE}
1031 # create a temporary storage location
1032 __shunit_tmpDir=`_shunit_mktempDir`
1034 # provide a public temporary directory for unit test scripts
1035 # TODO(kward): document this
1036 SHUNIT_TMPDIR="${__shunit_tmpDir}/tmp"
1037 mkdir "${SHUNIT_TMPDIR}"
1039 # setup traps to clean up after ourselves
1040 trap '_shunit_cleanup EXIT' 0
1041 trap '_shunit_cleanup INT' 2
1042 trap '_shunit_cleanup TERM' 15
1044 # create phantom functions to work around issues with Cygwin
1046 PATH="${__shunit_tmpDir}:${PATH}"
1048 # make sure phantom functions are executable. this will bite if /tmp (or the
1049 # current $TMPDIR) points to a path on a partition that was mounted with the
1050 # 'noexec' option. the noexec command was created with _shunit_mktempFunc().
1051 noexec 2>/dev/null || _shunit_fatal \
1052 'please declare TMPDIR with path on partition with exec permission'
1054 # we must manually source the tests in standalone mode
1055 if [ "${__shunit_mode}" = "${__SHUNIT_MODE_STANDALONE}" ]; then
1056 . "`_shunit_prepForSourcing \"${__shunit_script}\"`"
1059 # record when the tests started running.
1060 suite_start=$(date +%s)
1062 # execute the oneTimeSetUp function (if it exists)
1065 # execute the suite function defined in the parent test script
1066 # deprecated as of 2.1.0
1069 # if no suite function was defined, dynamically build a list of functions
1070 if [ -z "${__shunit_suite}" ]; then
1071 shunit_funcs_=`_shunit_extractTestFunctions "${__shunit_script}"`
1072 for shunit_func_ in ${shunit_funcs_}; do
1073 suite_addTest ${shunit_func_}
1076 unset shunit_func_ shunit_funcs_
1081 # execute the oneTimeTearDown function (if it exists)
1084 suite_end=$(date +%s)
1086 # generate the report
1087 _shunit_generateReport
1090 [ ${__shunit_testsFailed} -eq 0 ]