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