handling no parms better
[feisty_meow.git] / scripts / shunit / shunit2
1 #! /bin/sh
2 # $Id: shunit2 335 2011-05-01 20:10:33Z 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
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}${4:+ 4: $4}"
109     return ${SHUNIT_ERROR}
110   fi
111   _shunit_shouldSkip && return ${SHUNIT_TRUE}
112
113   shunit_message_=${__shunit_lineno}
114   local assertion_name=""
115   if [ $# -eq 3 ]; then
116     assertion_name="$1"
117     shunit_message_="${shunit_message_}$1"
118     shift
119   fi
120   shunit_expected_=$1
121   shunit_actual_=$2
122
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"
127     fi
128     _shunit_assertPass
129   else
130     failNotEquals "${shunit_message_}" "${shunit_expected_}" "${shunit_actual_}"
131     shunit_return=${SHUNIT_FALSE}
132   fi
133
134   unset shunit_message_ shunit_expected_ shunit_actual_
135   return ${shunit_return}
136 }
137 _ASSERT_EQUALS_='eval assertEquals --lineno "${LINENO:-}"'
138
139 # Assert that two values are not equal to one another.
140 #
141 # Args:
142 #   message: string: failure message [optional]
143 #   expected: string: expected value
144 #   actual: string: actual value
145 # Returns:
146 #   integer: success (TRUE/FALSE/ERROR constant)
147 assertNotEquals()
148 {
149   ${_SHUNIT_LINENO_}
150   if [ $# -lt 2 -o $# -gt 3 ]; then
151     _shunit_error "assertNotEquals() requires two or three arguments; $# given"
152     return ${SHUNIT_ERROR}
153   fi
154   _shunit_shouldSkip && return ${SHUNIT_TRUE}
155
156   shunit_message_=${__shunit_lineno}
157   local assertion_name=""
158   if [ $# -eq 3 ]; then
159     assertion_name="$1"
160     shunit_message_="${shunit_message_}$1"
161     shift
162   fi
163   shunit_expected_=$1
164   shunit_actual_=$2
165
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"
170     fi
171     _shunit_assertPass
172   else
173     failSame "${shunit_message_}" "$@"
174     shunit_return=${SHUNIT_FALSE}
175   fi
176
177   unset shunit_message_ shunit_expected_ shunit_actual_
178   return ${shunit_return}
179 }
180 _ASSERT_NOT_EQUALS_='eval assertNotEquals --lineno "${LINENO:-}"'
181
182 # Assert that a value is null (i.e. an empty string)
183 #
184 # Args:
185 #   message: string: failure message [optional]
186 #   actual: string: actual value
187 # Returns:
188 #   integer: success (TRUE/FALSE/ERROR constant)
189 assertNull()
190 {
191   ${_SHUNIT_LINENO_}
192   if [ $# -lt 1 -o $# -gt 2 ]; then
193     _shunit_error "assertNull() requires one or two arguments; $# given"
194     return ${SHUNIT_ERROR}
195   fi
196   _shunit_shouldSkip && return ${SHUNIT_TRUE}
197
198   shunit_message_=${__shunit_lineno}
199   if [ $# -eq 2 ]; then
200     shunit_message_="${shunit_message_}$1"
201     shift
202   fi
203   assertTrue "${shunit_message_}" "[ -z '$1' ]"
204   shunit_return=$?
205
206   unset shunit_message_
207   return ${shunit_return}
208 }
209 _ASSERT_NULL_='eval assertNull --lineno "${LINENO:-}"'
210
211 # Assert that a value is not null (i.e. a non-empty string)
212 #
213 # Args:
214 #   message: string: failure message [optional]
215 #   actual: string: actual value
216 # Returns:
217 #   integer: success (TRUE/FALSE/ERROR constant)
218 assertNotNull()
219 {
220   ${_SHUNIT_LINENO_}
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}
224   fi
225   _shunit_shouldSkip && return ${SHUNIT_TRUE}
226
227   shunit_message_=${__shunit_lineno}
228   if [ $# -eq 2 ]; then
229     shunit_message_="${shunit_message_}$1"
230     shift
231   fi
232   shunit_actual_=`_shunit_escapeCharactersInString "${1:-}"`
233   test -n "${shunit_actual_}"
234   assertTrue "${shunit_message_}" $?
235   shunit_return=$?
236
237   unset shunit_actual_ shunit_message_
238   return ${shunit_return}
239 }
240 _ASSERT_NOT_NULL_='eval assertNotNull --lineno "${LINENO:-}"'
241
242 # Assert that two values are the same (i.e. equal to one another).
243 #
244 # Args:
245 #   message: string: failure message [optional]
246 #   expected: string: expected value
247 #   actual: string: actual value
248 # Returns:
249 #   integer: success (TRUE/FALSE/ERROR constant)
250 assertSame()
251 {
252   ${_SHUNIT_LINENO_}
253   if [ $# -lt 2 -o $# -gt 3 ]; then
254     _shunit_error "assertSame() requires two or three arguments; $# given"
255     return ${SHUNIT_ERROR}
256   fi
257   _shunit_shouldSkip && return ${SHUNIT_TRUE}
258
259   shunit_message_=${__shunit_lineno}
260   if [ $# -eq 3 ]; then
261     shunit_message_="${shunit_message_}$1"
262     shift
263   fi
264   assertEquals "${shunit_message_}" "$1" "$2"
265   shunit_return=$?
266
267   unset shunit_message_
268   return ${shunit_return}
269 }
270 _ASSERT_SAME_='eval assertSame --lineno "${LINENO:-}"'
271
272 # Assert that two values are not the same (i.e. not equal to one another).
273 #
274 # Args:
275 #   message: string: failure message [optional]
276 #   expected: string: expected value
277 #   actual: string: actual value
278 # Returns:
279 #   integer: success (TRUE/FALSE/ERROR constant)
280 assertNotSame()
281 {
282   ${_SHUNIT_LINENO_}
283   if [ $# -lt 2 -o $# -gt 3 ]; then
284     _shunit_error "assertNotSame() requires two or three arguments; $# given"
285     return ${SHUNIT_ERROR}
286   fi
287   _shunit_shouldSkip && return ${SHUNIT_TRUE}
288
289   shunit_message_=${__shunit_lineno}
290   if [ $# -eq 3 ]; then
291     shunit_message_="${shunit_message_:-}$1"
292     shift
293   fi
294   assertNotEquals "${shunit_message_}" "$1" "$2"
295   shunit_return=$?
296
297   unset shunit_message_
298   return ${shunit_return}
299 }
300 _ASSERT_NOT_SAME_='eval assertNotSame --lineno "${LINENO:-}"'
301
302 # Assert that a value or shell test condition is true.
303 #
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.
306 #
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.
310 #
311 # The following test will succeed:
312 #   assertTrue 0
313 #   assertTrue "[ 34 -gt 23 ]"
314 # The folloing test will fail with a message:
315 #   assertTrue 123
316 #   assertTrue "test failed" "[ -r '/non/existant/file' ]"
317 #
318 # Args:
319 #   message: string: failure message [optional]
320 #   condition: string: integer value or shell conditional statement
321 # Returns:
322 #   integer: success (TRUE/FALSE/ERROR constant)
323 assertTrue()
324 {
325   ${_SHUNIT_LINENO_}
326   if [ $# -gt 2 ]; then
327     _shunit_error "assertTrue() takes one two arguments; $# given"
328     return ${SHUNIT_ERROR}
329   fi
330   _shunit_shouldSkip && return ${SHUNIT_TRUE}
331
332   shunit_message_=${__shunit_lineno}
333   local assertion_name=""
334   if [ $# -eq 2 ]; then
335     assertion_name="$1"
336     shunit_message_="${shunit_message_}$1"
337     shift
338   fi
339   shunit_condition_=$1
340
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
345     # null condition
346     shunit_return=${SHUNIT_FALSE}
347   elif [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ]
348   then
349     # possible return value. treating 0 as true, and non-zero as false.
350     [ ${shunit_condition_} -ne 0 ] && shunit_return=${SHUNIT_FALSE}
351   else
352     # (hopefully) a condition
353     ( eval ${shunit_condition_} ) >/dev/null 2>&1
354     [ $? -ne 0 ] && shunit_return=${SHUNIT_FALSE}
355   fi
356
357   # record the test
358   if [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then
359     if [ ! -z "$DEBUGGING" -a ! -z "$assertion_name" ]; then
360       echo "    OKAY: $assertion_name"
361     fi
362     _shunit_assertPass
363   else
364     _shunit_assertFail "${shunit_message_}"
365   fi
366
367   unset shunit_message_ shunit_condition_ shunit_match_
368   return ${shunit_return}
369 }
370 _ASSERT_TRUE_='eval assertTrue --lineno "${LINENO:-}"'
371
372 # Assert that a value or shell test condition is false.
373 #
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.
376 #
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.
380 #
381 # The following test will succeed:
382 #   assertFalse 1
383 #   assertFalse "[ 'apples' = 'oranges' ]"
384 # The folloing test will fail with a message:
385 #   assertFalse 0
386 #   assertFalse "test failed" "[ 1 -eq 1 -a 2 -eq 2 ]"
387 #
388 # Args:
389 #   message: string: failure message [optional]
390 #   condition: string: integer value or shell conditional statement
391 # Returns:
392 #   integer: success (TRUE/FALSE/ERROR constant)
393 assertFalse()
394 {
395   ${_SHUNIT_LINENO_}
396   if [ $# -lt 1 -o $# -gt 2 ]; then
397     _shunit_error "assertFalse() quires one or two arguments; $# given"
398     return ${SHUNIT_ERROR}
399   fi
400   _shunit_shouldSkip && return ${SHUNIT_TRUE}
401
402   shunit_message_=${__shunit_lineno}
403   local assertion_name=""
404   if [ $# -eq 2 ]; then
405     assertion_name="$1"
406     shunit_message_="${shunit_message_}$1"
407     shift
408   fi
409   shunit_condition_=$1
410
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
415     # null condition
416     shunit_return=${SHUNIT_FALSE}
417   elif [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ]
418   then
419     # possible return value. treating 0 as true, and non-zero as false.
420     [ ${shunit_condition_} -eq 0 ] && shunit_return=${SHUNIT_FALSE}
421   else
422     # (hopefully) a condition
423     ( eval ${shunit_condition_} ) >/dev/null 2>&1
424     [ $? -eq 0 ] && shunit_return=${SHUNIT_FALSE}
425   fi
426
427   # record the test
428   if [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then
429     if [ ! -z "$DEBUGGING" -a ! -z "$assertion_name" ]; then
430       echo "    OKAY: $assertion_name"
431     fi
432     _shunit_assertPass
433   else
434     _shunit_assertFail "${shunit_message_}"
435   fi
436
437   unset shunit_message_ shunit_condition_ shunit_match_
438   return ${shunit_return}
439 }
440 _ASSERT_FALSE_='eval assertFalse --lineno "${LINENO:-}"'
441
442 #-----------------------------------------------------------------------------
443 # failure functions
444 #
445
446 # Records a test failure.
447 #
448 # Args:
449 #   message: string: failure message [optional]
450 # Returns:
451 #   integer: success (TRUE/FALSE/ERROR constant)
452 fail()
453 {
454   ${_SHUNIT_LINENO_}
455   if [ $# -gt 1 ]; then
456     _shunit_error "fail() requires zero or one arguments; $# given"
457     return ${SHUNIT_ERROR}
458   fi
459   _shunit_shouldSkip && return ${SHUNIT_TRUE}
460
461   shunit_message_=${__shunit_lineno}
462   if [ $# -eq 1 ]; then
463     shunit_message_="${shunit_message_}$1"
464     shift
465   fi
466
467   _shunit_assertFail "${shunit_message_}"
468
469   unset shunit_message_
470   return ${SHUNIT_FALSE}
471 }
472 _FAIL_='eval fail --lineno "${LINENO:-}"'
473
474 # Records a test failure, stating two values were not equal.
475 #
476 # Args:
477 #   message: string: failure message [optional]
478 #   expected: string: expected value
479 #   actual: string: actual value
480 # Returns:
481 #   integer: success (TRUE/FALSE/ERROR constant)
482 failNotEquals()
483 {
484   ${_SHUNIT_LINENO_}
485   if [ $# -lt 2 -o $# -gt 3 ]; then
486     _shunit_error "failNotEquals() requires one or two arguments; $# given"
487     return ${SHUNIT_ERROR}
488   fi
489   _shunit_shouldSkip && return ${SHUNIT_TRUE}
490
491   shunit_message_=${__shunit_lineno}
492   if [ $# -eq 3 ]; then
493     shunit_message_="${shunit_message_}$1"
494     shift
495   fi
496   shunit_expected_=$1
497   shunit_actual_=$2
498
499   _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected:<${shunit_expected_}> but was:<${shunit_actual_}>"
500
501   unset shunit_message_ shunit_expected_ shunit_actual_
502   return ${SHUNIT_FALSE}
503 }
504 _FAIL_NOT_EQUALS_='eval failNotEquals --lineno "${LINENO:-}"'
505
506 # Records a test failure, stating two values should have been the same.
507 #
508 # Args:
509 #   message: string: failure message [optional]
510 #   expected: string: expected value
511 #   actual: string: actual value
512 # Returns:
513 #   integer: success (TRUE/FALSE/ERROR constant)
514 failSame()
515 {
516   ${_SHUNIT_LINENO_}
517   if [ $# -lt 2 -o $# -gt 3 ]; then
518     _shunit_error "failSame() requires two or three arguments; $# given"
519     return ${SHUNIT_ERROR}
520   fi
521   _shunit_shouldSkip && return ${SHUNIT_TRUE}
522
523   shunit_message_=${__shunit_lineno}
524   if [ $# -eq 3 ]; then
525     shunit_message_="${shunit_message_}$1"
526     shift
527   fi
528
529   _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected not same"
530
531   unset shunit_message_
532   return ${SHUNIT_FALSE}
533 }
534 _FAIL_SAME_='eval failSame --lineno "${LINENO:-}"'
535
536 # Records a test failure, stating two values were not equal.
537 #
538 # This is functionally equivalent to calling failNotEquals().
539 #
540 # Args:
541 #   message: string: failure message [optional]
542 #   expected: string: expected value
543 #   actual: string: actual value
544 # Returns:
545 #   integer: success (TRUE/FALSE/ERROR constant)
546 failNotSame()
547 {
548   ${_SHUNIT_LINENO_}
549   if [ $# -lt 2 -o $# -gt 3 ]; then
550     _shunit_error "failNotEquals() requires one or two arguments; $# given"
551     return ${SHUNIT_ERROR}
552   fi
553   _shunit_shouldSkip && return ${SHUNIT_TRUE}
554
555   shunit_message_=${__shunit_lineno}
556   if [ $# -eq 3 ]; then
557     shunit_message_="${shunit_message_}$1"
558     shift
559   fi
560   failNotEquals "${shunit_message_}" "$1" "$2"
561   shunit_return=$?
562
563   unset shunit_message_
564   return ${shunit_return}
565 }
566 _FAIL_NOT_SAME_='eval failNotSame --lineno "${LINENO:-}"'
567
568 #-----------------------------------------------------------------------------
569 # skipping functions
570 #
571
572 # Force remaining assert and fail functions to be "skipped".
573 #
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.
577 #
578 # Args:
579 #   None
580 startSkipping()
581 {
582   __shunit_skip=${SHUNIT_TRUE}
583 }
584
585 # Resume the normal recording behavior of assert and fail calls.
586 #
587 # Args:
588 #   None
589 endSkipping()
590 {
591   __shunit_skip=${SHUNIT_FALSE}
592 }
593
594 # Returns the state of assert and fail call skipping.
595 #
596 # Args:
597 #   None
598 # Returns:
599 #   boolean: (TRUE/FALSE constant)
600 isSkipping()
601 {
602   return ${__shunit_skip}
603 }
604
605 #-----------------------------------------------------------------------------
606 # suite functions
607 #
608
609 # Stub. This function should contains all unit test calls to be made.
610 #
611 # DEPRECATED (as of 2.1.0)
612 #
613 # This function can be optionally overridden by the user in their test suite.
614 #
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
618 # test suite.
619 #
620 # This function should be overridden by the user in their unit test suite.
621 # Note: see _shunit_mktempFunc() for actual implementation
622 #
623 # Args:
624 #   None
625 #suite() { :; }  # DO NOT UNCOMMENT THIS FUNCTION
626
627 # Adds a function name to the list of tests schedule for execution.
628 #
629 # This function should only be called from within the suite() function.
630 #
631 # Args:
632 #   function: string: name of a function to add to current unit test suite
633 suite_addTest()
634 {
635   shunit_func_=${1:-}
636
637   __shunit_suite="${__shunit_suite:+${__shunit_suite} }${shunit_func_}"
638   __shunit_testsTotal=`expr ${__shunit_testsTotal} + 1`
639
640   unset shunit_func_
641 }
642
643 # Stub. This function will be called once before any tests are run.
644 #
645 # Common one-time environment preparation tasks shared by all tests can be
646 # defined here.
647 #
648 # This function should be overridden by the user in their unit test suite.
649 # Note: see _shunit_mktempFunc() for actual implementation
650 #
651 # Args:
652 #   None
653 #oneTimeSetUp() { :; }  # DO NOT UNCOMMENT THIS FUNCTION
654
655 # Stub. This function will be called once after all tests are finished.
656 #
657 # Common one-time environment cleanup tasks shared by all tests can be defined
658 # here.
659 #
660 # This function should be overridden by the user in their unit test suite.
661 # Note: see _shunit_mktempFunc() for actual implementation
662 #
663 # Args:
664 #   None
665 #oneTimeTearDown() { :; }  # DO NOT UNCOMMENT THIS FUNCTION
666
667 # Stub. This function will be called before each test is run.
668 #
669 # Common environment preparation tasks shared by all tests can be defined here.
670 #
671 # This function should be overridden by the user in their unit test suite.
672 # Note: see _shunit_mktempFunc() for actual implementation
673 #
674 # Args:
675 #   None
676 #setUp() { :; }
677
678 # Note: see _shunit_mktempFunc() for actual implementation
679 # Stub. This function will be called after each test is run.
680 #
681 # Common environment cleanup tasks shared by all tests can be defined here.
682 #
683 # This function should be overridden by the user in their unit test suite.
684 # Note: see _shunit_mktempFunc() for actual implementation
685 #
686 # Args:
687 #   None
688 #tearDown() { :; }  # DO NOT UNCOMMENT THIS FUNCTION
689
690 #------------------------------------------------------------------------------
691 # internal shUnit2 functions
692 #
693
694 # Create a temporary directory to store various run-time files in.
695 #
696 # This function is a cross-platform temporary directory creation tool. Not all
697 # OSes have the mktemp function, so one is included here.
698 #
699 # Args:
700 #   None
701 # Outputs:
702 #   string: the temporary directory that was created
703 _shunit_mktempDir()
704 {
705   # try the standard mktemp function
706   ( exec mktemp -dqt shunit.XXXXXX 2>/dev/null ) && return
707
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
713     # $RANDOM works
714     _shunit_random_=${RANDOM}${RANDOM}${RANDOM}$$
715   else
716     # $RANDOM doesn't work
717     _shunit_date_=`date '+%Y%m%d%H%M%S'`
718     _shunit_random_=`expr ${_shunit_date_} / $$`
719   fi
720
721   _shunit_tmpDir_="${TMPDIR:-/tmp}/shunit.${_shunit_random_}"
722   ( umask 077 && mkdir "${_shunit_tmpDir_}" ) || \
723       _shunit_fatal 'could not create temporary directory! exiting'
724
725   echo ${_shunit_tmpDir_}
726   unset _shunit_date_ _shunit_random_ _shunit_tmpDir_
727 }
728
729 # This function is here to work around issues in Cygwin.
730 #
731 # Args:
732 #   None
733 _shunit_mktempFunc()
734 {
735   for _shunit_func_ in oneTimeSetUp oneTimeTearDown setUp tearDown suite noexec
736   do
737     _shunit_file_="${__shunit_tmpDir}/${_shunit_func_}"
738     cat <<EOF >"${_shunit_file_}"
739 #! /bin/sh
740 exit ${SHUNIT_TRUE}
741 EOF
742     chmod +x "${_shunit_file_}"
743   done
744
745   unset _shunit_file_
746 }
747
748 # Final cleanup function to leave things as we found them.
749 #
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).
753 #
754 # Args:
755 #   name: string: name of the trap called (specified when trap defined)
756 _shunit_cleanup()
757 {
758   _shunit_name_=$1
759
760   case ${_shunit_name_} in
761     EXIT) _shunit_signal_=0 ;;
762     INT) _shunit_signal_=2 ;;
763     TERM) _shunit_signal_=15 ;;
764     *)
765       _shunit_warn "unrecognized trap value (${_shunit_name_})"
766       _shunit_signal_=0
767       ;;
768   esac
769
770   # do our work
771   rm -fr "${__shunit_tmpDir}"
772
773   # exit for all non-EXIT signals
774   if [ ${_shunit_name_} != 'EXIT' ]; then
775     _shunit_warn "trapped and now handling the (${_shunit_name_}) signal"
776     # disable EXIT trap
777     trap 0
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
783     exit ${SHUNIT_ERROR}
784   fi
785
786   unset _shunit_name_ _shunit_signal_
787 }
788
789 # The actual running of the tests happens here.
790 #
791 # Args:
792 #   None
793 _shunit_execSuite()
794 {
795   for _shunit_test_ in ${__shunit_suite}; do
796     __shunit_testSuccess=${SHUNIT_TRUE}
797
798     # disable skipping
799     endSkipping
800
801     # execute the per-test setup function
802     setUp
803
804     # execute the test
805     echo
806     echo
807     echo "-------------------------------------------------------"
808     echo "$(date): [${_shunit_test_}]"
809     eval ${_shunit_test_}
810
811     # execute the per-test tear-down function
812     tearDown
813
814     # update stats
815     if [ ${__shunit_testSuccess} -eq ${SHUNIT_TRUE} ]; then
816       __shunit_testsPassed=`expr ${__shunit_testsPassed} + 1`
817     else
818       __shunit_testsFailed=`expr ${__shunit_testsFailed} + 1`
819     fi
820   done
821
822   unset _shunit_test_
823 }
824
825 # Generates the user friendly report with appropriate OKAY/FAILED message.
826 #
827 # Args:
828 #   None
829 # Output:
830 #   string: the report of successful and failed tests, as well as totals.
831 _shunit_generateReport()
832 {
833   _shunit_ok_=${SHUNIT_TRUE}
834
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}
839
840   echo
841   if [ ${__shunit_testsTotal} -eq 1 ]; then
842     echo "$(date): Ran ${__shunit_testsTotal} test."
843   else
844     echo "$(date): Ran ${__shunit_testsTotal} tests."
845   fi
846
847   _shunit_failures_=''
848   _shunit_skipped_=''
849   [ ${__shunit_assertsFailed} -gt 0 ] \
850       && _shunit_failures_="failures=${__shunit_assertsFailed}"
851   [ ${__shunit_assertsSkipped} -gt 0 ] \
852       && _shunit_skipped_="skipped=${__shunit_assertsSkipped}"
853
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_})"
858   else
859     _shunit_msg_="$(basename $0) FAILED (${_shunit_failures_}"
860     [ -n "${_shunit_skipped_}" ] \
861         && _shunit_msg_="${_shunit_msg_},${_shunit_skipped_}"
862     _shunit_msg_="${_shunit_msg_})"
863   fi
864
865   if [ -z "$suite_end" ]; then
866     # make sure we don't get confused, since suite aborted early.
867     suite_end=$(date +%s)
868   fi
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]"
880   echo
881   echo "$(date): ${_shunit_msg_}"
882   __shunit_reportGenerated=${SHUNIT_TRUE}
883
884   unset _shunit_failures_ _shunit_msg_ _shunit_ok_ _shunit_skipped_
885 }
886
887 # Test for whether a function should be skipped.
888 #
889 # Args:
890 #   None
891 # Returns:
892 #   boolean: whether the test should be skipped (TRUE/FALSE constant)
893 _shunit_shouldSkip()
894 {
895   [ ${__shunit_skip} -eq ${SHUNIT_FALSE} ] && return ${SHUNIT_FALSE}
896   _shunit_assertSkip
897 }
898
899 # Records a successful test.
900 #
901 # Args:
902 #   None
903 _shunit_assertPass()
904 {
905   __shunit_assertsPassed=`expr ${__shunit_assertsPassed} + 1`
906   __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1`
907 }
908
909 # Records a test failure.
910 #
911 # Args:
912 #   message: string: failure message to provide user
913 _shunit_assertFail()
914 {
915   _shunit_msg_=$1
916
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_}"
921
922   unset _shunit_msg_
923 }
924
925 # Records a skipped test.
926 #
927 # Args:
928 #   None
929 _shunit_assertSkip()
930 {
931   __shunit_assertsSkipped=`expr ${__shunit_assertsSkipped} + 1`
932   __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1`
933 }
934
935 # Prepare a script filename for sourcing.
936 #
937 # Args:
938 #   script: string: path to a script to source
939 # Returns:
940 #   string: filename prefixed with ./ (if necessary)
941 _shunit_prepForSourcing()
942 {
943   _shunit_script_=$1
944   case "${_shunit_script_}" in
945     /*|./*) echo "${_shunit_script_}" ;;
946     *) echo "./${_shunit_script_}" ;;
947   esac
948   unset _shunit_script_
949 }
950
951 # Escape a character in a string.
952 #
953 # Args:
954 #   c: string: unescaped character
955 #   s: string: to escape character in
956 # Returns:
957 #   string: with escaped character(s)
958 _shunit_escapeCharInStr()
959 {
960   [ -n "$2" ] || return  # no point in doing work on an empty string
961
962   # Note: using shorter variable names to prevent conflicts with
963   # _shunit_escapeCharactersInString().
964   _shunit_c_=$1
965   _shunit_s_=$2
966
967
968   # escape the character
969   echo ''${_shunit_s_}'' |sed 's/\'${_shunit_c_}'/\\\'${_shunit_c_}'/g'
970
971   unset _shunit_c_ _shunit_s_
972 }
973
974 # Escape a character in a string.
975 #
976 # Args:
977 #   str: string: to escape characters in
978 # Returns:
979 #   string: with escaped character(s)
980 _shunit_escapeCharactersInString()
981 {
982   [ -n "$1" ] || return  # no point in doing work on an empty string
983
984   _shunit_str_=$1
985
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_}"`
990   done
991
992   echo "${_shunit_str_}"
993   unset _shunit_char_ _shunit_str_
994 }
995
996 # Extract list of functions to run tests against.
997 #
998 # Args:
999 #   script: string: name of script to extract functions from
1000 # Returns:
1001 #   string: of function names
1002 _shunit_extractTestFunctions()
1003 {
1004   _shunit_script_=$1
1005
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' \
1011   |xargs
1012
1013   unset _shunit_regex_ _shunit_script_
1014 }
1015
1016 #------------------------------------------------------------------------------
1017 # main
1018 #
1019
1020 # determine the operating mode
1021 if [ $# -eq 0 ]; then
1022   __shunit_script=${__SHUNIT_PARENT}
1023   __shunit_mode=${__SHUNIT_MODE_SOURCED}
1024 else
1025   __shunit_script=$1
1026   [ -r "${__shunit_script}" ] || \
1027       _shunit_fatal "unable to read from ${__shunit_script}"
1028   __shunit_mode=${__SHUNIT_MODE_STANDALONE}
1029 fi
1030
1031 # create a temporary storage location
1032 __shunit_tmpDir=`_shunit_mktempDir`
1033
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}"
1038
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
1043
1044 # create phantom functions to work around issues with Cygwin
1045 _shunit_mktempFunc
1046 PATH="${__shunit_tmpDir}:${PATH}"
1047
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'
1053
1054 # we must manually source the tests in standalone mode
1055 if [ "${__shunit_mode}" = "${__SHUNIT_MODE_STANDALONE}" ]; then
1056   . "`_shunit_prepForSourcing \"${__shunit_script}\"`"
1057 fi
1058
1059 # record when the tests started running.
1060 suite_start=$(date +%s)
1061
1062 # execute the oneTimeSetUp function (if it exists)
1063 oneTimeSetUp
1064
1065 # execute the suite function defined in the parent test script
1066 # deprecated as of 2.1.0
1067 suite
1068
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_}
1074   done
1075 fi
1076 unset shunit_func_ shunit_funcs_
1077
1078 # execute the tests
1079 _shunit_execSuite
1080
1081 # execute the oneTimeTearDown function (if it exists)
1082 oneTimeTearDown
1083
1084 suite_end=$(date +%s)
1085
1086 # generate the report
1087 _shunit_generateReport
1088
1089 # that's it folks
1090 [ ${__shunit_testsFailed} -eq 0 ]
1091 exit $?