1#! /bin/sh
2# $Id: shunit2 322 2011-04-24 00:09:45Z kate.ward@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@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
19SHUNIT_VERSION='2.1.6'
20
21SHUNIT_TRUE=0
22SHUNIT_FALSE=1
23SHUNIT_ERROR=2
24
25# enable strict mode by default
26SHUNIT_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
33if [ -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
42fi
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
54shunit_constants_=`set |grep '^__SHUNIT_' |cut -d= -f1`
55echo "${shunit_constants_}" |grep '^Binary file' >/dev/null && \
56    shunit_constants_=`set |grep -a '^__SHUNIT_' |cut -d= -f1`
57for 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_}
65done
66unset 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)
103assertEquals()
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)
142assertNotEquals()
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)
179assertNull()
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)
208assertNotNull()
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)
240assertSame()
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)
270assertNotSame()
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)
313assertTrue()
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)
378assertFalse()
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)
432fail()
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)
462failNotEquals()
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)
494failSame()
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)
526failNotSame()
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
560startSkipping()
561{
562  __shunit_skip=${SHUNIT_TRUE}
563}
564
565# Resume the normal recording behavior of assert and fail calls.
566#
567# Args:
568#   None
569endSkipping()
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)
580isSkipping()
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
613suite_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
720exit ${SHUNIT_TRUE}
721EOF
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
983if [ $# -eq 0 ]; then
984  __shunit_script=${__SHUNIT_PARENT}
985  __shunit_mode=${__SHUNIT_MODE_SOURCED}
986else
987  __shunit_script=$1
988  [ -r "${__shunit_script}" ] || \
989      _shunit_fatal "unable to read from ${__shunit_script}"
990  __shunit_mode=${__SHUNIT_MODE_STANDALONE}
991fi
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
998SHUNIT_TMPDIR="${__shunit_tmpDir}/tmp"
999mkdir "${SHUNIT_TMPDIR}"
1000
1001# setup traps to clean up after ourselves
1002trap '_shunit_cleanup EXIT' 0
1003trap '_shunit_cleanup INT' 2
1004trap '_shunit_cleanup TERM' 15
1005
1006# create phantom functions to work around issues with Cygwin
1007_shunit_mktempFunc
1008PATH="${__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().
1013noexec 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
1017if [ "${__shunit_mode}" = "${__SHUNIT_MODE_STANDALONE}" ]; then
1018  . "`_shunit_prepForSourcing \"${__shunit_script}\"`"
1019fi
1020
1021# execute the oneTimeSetUp function (if it exists)
1022oneTimeSetUp
1023
1024# execute the suite function defined in the parent test script
1025# deprecated as of 2.1.0
1026suite
1027
1028# if no suite function was defined, dynamically build a list of functions
1029if [ -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
1034fi
1035unset shunit_func_ shunit_funcs_
1036
1037# execute the tests
1038_shunit_execSuite
1039
1040# execute the oneTimeTearDown function (if it exists)
1041oneTimeTearDown
1042
1043# generate the report
1044_shunit_generateReport
1045
1046# that's it folks
1047[ ${__shunit_testsFailed} -eq 0 ]
1048exit $?
1049