1#! /bin/sh
2# vim:et:ft=sh:sts=2:sw=2
3#
4# Copyright 2008-2020 Kate Ward. All Rights Reserved.
5# Released under the Apache 2.0 license.
6# http://www.apache.org/licenses/LICENSE-2.0
7#
8# shUnit2 -- Unit testing framework for Unix shell scripts.
9# https://github.com/kward/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# $() are not fully portable (POSIX != portable).
17#   shellcheck disable=SC2006
18# expr may be antiquated, but it is the only solution in some cases.
19#   shellcheck disable=SC2003
20
21# Return if shunit2 already loaded.
22if test -n "${SHUNIT_VERSION:-}"; then
23  exit 0
24fi
25SHUNIT_VERSION='2.1.9pre'
26
27# Return values that scripts can use.
28SHUNIT_TRUE=0
29SHUNIT_FALSE=1
30SHUNIT_ERROR=2
31
32# Determine if `builtin` command exists.
33__SHUNIT_BUILTIN='builtin'
34# shellcheck disable=2039
35if ! ("${__SHUNIT_BUILTIN}" echo 123 >/dev/null 2>&1); then
36  __SHUNIT_BUILTIN=''
37fi
38
39# Determine some reasonable command defaults.
40__SHUNIT_CMD_ECHO_ESC='echo -e'
41# shellcheck disable=SC2039
42if ${__SHUNIT_BUILTIN} [ "`echo -e test`" = '-e test' ]; then
43  __SHUNIT_CMD_ECHO_ESC='echo'
44fi
45
46__SHUNIT_UNAME_S=`uname -s`
47case "${__SHUNIT_UNAME_S}" in
48  BSD) __SHUNIT_CMD_EXPR='gexpr' ;;
49  *) __SHUNIT_CMD_EXPR='expr' ;;
50esac
51__SHUNIT_CMD_TPUT='tput'
52
53# Commands a user can override if needed.
54SHUNIT_CMD_EXPR=${SHUNIT_CMD_EXPR:-${__SHUNIT_CMD_EXPR}}
55SHUNIT_CMD_TPUT=${SHUNIT_CMD_TPUT:-${__SHUNIT_CMD_TPUT}}
56
57# Enable color output. Options are 'never', 'always', or 'auto'.
58SHUNIT_COLOR=${SHUNIT_COLOR:-auto}
59
60# Logging functions.
61_shunit_warn() {
62  ${__SHUNIT_CMD_ECHO_ESC} "${__shunit_ansi_yellow}shunit2:WARN${__shunit_ansi_none} $*" >&2
63}
64_shunit_error() {
65  ${__SHUNIT_CMD_ECHO_ESC} "${__shunit_ansi_red}shunit2:ERROR${__shunit_ansi_none} $*" >&2
66}
67_shunit_fatal() {
68  ${__SHUNIT_CMD_ECHO_ESC} "${__shunit_ansi_red}shunit2:FATAL${__shunit_ansi_none} $*" >&2
69  exit ${SHUNIT_ERROR}
70}
71
72# Specific shell checks.
73if ${__SHUNIT_BUILTIN} [ -n "${ZSH_VERSION:-}" ]; then
74  setopt |grep "^shwordsplit$" >/dev/null
75  if ${__SHUNIT_BUILTIN} [ $? -ne ${SHUNIT_TRUE} ]; then
76    _shunit_fatal 'zsh shwordsplit option is required for proper operation'
77  fi
78  if ${__SHUNIT_BUILTIN} [ -z "${SHUNIT_PARENT:-}" ]; then
79    _shunit_fatal "zsh does not pass \$0 through properly. please declare \
80\"SHUNIT_PARENT=\$0\" before calling shUnit2"
81  fi
82fi
83
84#
85# Constants
86#
87
88__SHUNIT_MODE_SOURCED='sourced'
89__SHUNIT_MODE_STANDALONE='standalone'
90__SHUNIT_PARENT=${SHUNIT_PARENT:-$0}
91
92# User provided test prefix to display in front of the name of the test being
93# executed. Define by setting the SHUNIT_TEST_PREFIX variable.
94__SHUNIT_TEST_PREFIX=${SHUNIT_TEST_PREFIX:-}
95
96# ANSI colors.
97__SHUNIT_ANSI_NONE='\033[0m'
98__SHUNIT_ANSI_RED='\033[1;31m'
99__SHUNIT_ANSI_GREEN='\033[1;32m'
100__SHUNIT_ANSI_YELLOW='\033[1;33m'
101__SHUNIT_ANSI_CYAN='\033[1;36m'
102
103# Set the constants readonly.
104__shunit_constants=`set |grep '^__SHUNIT_' |cut -d= -f1`
105echo "${__shunit_constants}" |grep '^Binary file' >/dev/null && \
106    __shunit_constants=`set |grep -a '^__SHUNIT_' |cut -d= -f1`
107for __shunit_const in ${__shunit_constants}; do
108  if ${__SHUNIT_BUILTIN} [ -z "${ZSH_VERSION:-}" ]; then
109    readonly "${__shunit_const}"
110  else
111    case ${ZSH_VERSION} in
112      [123].*) readonly "${__shunit_const}" ;;
113      *)
114        # Declare readonly constants globally.
115        # shellcheck disable=SC2039
116        readonly -g "${__shunit_const}"
117    esac
118  fi
119done
120unset __shunit_const __shunit_constants
121
122#
123# Internal variables.
124#
125
126# Variables.
127__shunit_lineno=''  # Line number of executed test.
128__shunit_mode=${__SHUNIT_MODE_SOURCED}  # Operating mode.
129__shunit_reportGenerated=${SHUNIT_FALSE}  # Is report generated.
130__shunit_script=''  # Filename of unittest script (standalone mode).
131__shunit_skip=${SHUNIT_FALSE}  # Is skipping enabled.
132__shunit_suite=''  # Suite of tests to execute.
133__shunit_clean=${SHUNIT_FALSE}  # _shunit_cleanup() was already called.
134
135# ANSI colors (populated by _shunit_configureColor()).
136__shunit_ansi_none=''
137__shunit_ansi_red=''
138__shunit_ansi_green=''
139__shunit_ansi_yellow=''
140__shunit_ansi_cyan=''
141
142# Counts of tests.
143__shunit_testSuccess=${SHUNIT_TRUE}
144__shunit_testsTotal=0
145__shunit_testsPassed=0
146__shunit_testsFailed=0
147
148# Counts of asserts.
149__shunit_assertsTotal=0
150__shunit_assertsPassed=0
151__shunit_assertsFailed=0
152__shunit_assertsSkipped=0
153
154#
155# Macros.
156#
157
158# shellcheck disable=SC2016,SC2089
159_SHUNIT_LINENO_='eval __shunit_lineno=""; if ${__SHUNIT_BUILTIN} [ "${1:-}" = "--lineno" ]; then if ${__SHUNIT_BUILTIN} [ -n "$2" ]; then __shunit_lineno="[$2] "; fi; shift 2; fi'
160
161#-----------------------------------------------------------------------------
162# Assertion functions.
163#
164
165# Assert that two values are equal to one another.
166#
167# Args:
168#   message: string: failure message [optional]
169#   expected: string: expected value
170#   actual: string: actual value
171# Returns:
172#   integer: success (TRUE/FALSE/ERROR constant)
173assertEquals() {
174  # shellcheck disable=SC2090
175  ${_SHUNIT_LINENO_}
176  if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then
177    _shunit_error "assertEquals() requires two or three arguments; $# given"
178    _shunit_assertFail
179    return ${SHUNIT_ERROR}
180  fi
181  if _shunit_shouldSkip; then
182    return ${SHUNIT_TRUE}
183  fi
184
185  shunit_message_=${__shunit_lineno}
186  if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then
187    shunit_message_="${shunit_message_}$1"
188    shift
189  fi
190  shunit_expected_=$1
191  shunit_actual_=$2
192
193  shunit_return=${SHUNIT_TRUE}
194  if ${__SHUNIT_BUILTIN} [ "${shunit_expected_}" = "${shunit_actual_}" ]; then
195    _shunit_assertPass
196  else
197    failNotEquals "${shunit_message_}" "${shunit_expected_}" "${shunit_actual_}"
198    shunit_return=${SHUNIT_FALSE}
199  fi
200
201  unset shunit_message_ shunit_expected_ shunit_actual_
202  return ${shunit_return}
203}
204# shellcheck disable=SC2016,SC2034
205_ASSERT_EQUALS_='eval assertEquals --lineno "${LINENO:-}"'
206
207# Assert that two values are not equal to one another.
208#
209# Args:
210#   message: string: failure message [optional]
211#   expected: string: expected value
212#   actual: string: actual value
213# Returns:
214#   integer: success (TRUE/FALSE/ERROR constant)
215assertNotEquals() {
216  # shellcheck disable=SC2090
217  ${_SHUNIT_LINENO_}
218  if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then
219    _shunit_error "assertNotEquals() requires two or three arguments; $# given"
220    _shunit_assertFail
221    return ${SHUNIT_ERROR}
222  fi
223  if _shunit_shouldSkip; then
224    return ${SHUNIT_TRUE}
225  fi
226
227  shunit_message_=${__shunit_lineno}
228  if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then
229    shunit_message_="${shunit_message_}$1"
230    shift
231  fi
232  shunit_expected_=$1
233  shunit_actual_=$2
234
235  shunit_return=${SHUNIT_TRUE}
236  if ${__SHUNIT_BUILTIN} [ "${shunit_expected_}" != "${shunit_actual_}" ]; then
237    _shunit_assertPass
238  else
239    failSame "${shunit_message_}" "${shunit_expected_}" "${shunit_actual_}"
240    shunit_return=${SHUNIT_FALSE}
241  fi
242
243  unset shunit_message_ shunit_expected_ shunit_actual_
244  return ${shunit_return}
245}
246# shellcheck disable=SC2016,SC2034
247_ASSERT_NOT_EQUALS_='eval assertNotEquals --lineno "${LINENO:-}"'
248
249# Assert that a container contains a content.
250#
251# Args:
252#   message: string: failure message [optional]
253#   container: string: container to analyze
254#   content: string: content to find
255# Returns:
256#   integer: success (TRUE/FALSE/ERROR constant)
257assertContains() {
258  # shellcheck disable=SC2090
259  ${_SHUNIT_LINENO_}
260  if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then
261    _shunit_error "assertContains() requires two or three arguments; $# given"
262    _shunit_assertFail
263    return ${SHUNIT_ERROR}
264  fi
265  if _shunit_shouldSkip; then
266    return ${SHUNIT_TRUE}
267  fi
268
269  shunit_message_=${__shunit_lineno}
270  if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then
271    shunit_message_="${shunit_message_}$1"
272    shift
273  fi
274  shunit_container_=$1
275  shunit_content_=$2
276  shunit_return=${SHUNIT_TRUE}
277  if echo "${shunit_container_}" |grep -F -- "${shunit_content_}" >/dev/null; then
278    _shunit_assertPass
279  else
280    failNotFound "${shunit_message_}" "${shunit_content_}"
281    shunit_return=${SHUNIT_FALSE}
282  fi
283
284  unset shunit_message_ shunit_container_ shunit_content_
285  return ${shunit_return}
286}
287# shellcheck disable=SC2016,SC2034
288_ASSERT_CONTAINS_='eval assertContains --lineno "${LINENO:-}"'
289
290# Assert that a container does not contain a content.
291#
292# Args:
293#   message: string: failure message [optional]
294#   container: string: container to analyze
295#   content: string: content to look for
296# Returns:
297#   integer: success (TRUE/FALSE/ERROR constant)
298assertNotContains() {
299  # shellcheck disable=SC2090
300  ${_SHUNIT_LINENO_}
301  if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then
302    _shunit_error "assertNotContains() requires two or three arguments; $# given"
303    _shunit_assertFail
304    return ${SHUNIT_ERROR}
305  fi
306  if _shunit_shouldSkip; then
307    return ${SHUNIT_TRUE}
308  fi
309
310  shunit_message_=${__shunit_lineno}
311  if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then
312    shunit_message_="${shunit_message_}$1"
313    shift
314  fi
315  shunit_container_=$1
316  shunit_content_=$2
317
318  shunit_return=${SHUNIT_TRUE}
319  if echo "$shunit_container_" | grep -F -- "$shunit_content_" > /dev/null; then
320    failFound "${shunit_message_}" "${shunit_content_}"
321    shunit_return=${SHUNIT_FALSE}
322  else
323    _shunit_assertPass
324  fi
325
326  unset shunit_message_ shunit_container_ shunit_content_
327  return ${shunit_return}
328}
329# shellcheck disable=SC2016,SC2034
330_ASSERT_NOT_CONTAINS_='eval assertNotContains --lineno "${LINENO:-}"'
331
332# Assert that a value is null (i.e. an empty string)
333#
334# Args:
335#   message: string: failure message [optional]
336#   actual: string: actual value
337# Returns:
338#   integer: success (TRUE/FALSE/ERROR constant)
339assertNull() {
340  # shellcheck disable=SC2090
341  ${_SHUNIT_LINENO_}
342  if ${__SHUNIT_BUILTIN} [ $# -lt 1 -o $# -gt 2 ]; then
343    _shunit_error "assertNull() requires one or two arguments; $# given"
344    _shunit_assertFail
345    return ${SHUNIT_ERROR}
346  fi
347  if _shunit_shouldSkip; then
348    return ${SHUNIT_TRUE}
349  fi
350
351  shunit_message_=${__shunit_lineno}
352  if ${__SHUNIT_BUILTIN} [ $# -eq 2 ]; then
353    shunit_message_="${shunit_message_}$1"
354    shift
355  fi
356  assertTrue "${shunit_message_}" "[ -z '$1' ]"
357  shunit_return=$?
358
359  unset shunit_message_
360  return ${shunit_return}
361}
362# shellcheck disable=SC2016,SC2034
363_ASSERT_NULL_='eval assertNull --lineno "${LINENO:-}"'
364
365# Assert that a value is not null (i.e. a non-empty string)
366#
367# Args:
368#   message: string: failure message [optional]
369#   actual: string: actual value
370# Returns:
371#   integer: success (TRUE/FALSE/ERROR constant)
372assertNotNull() {
373  # shellcheck disable=SC2090
374  ${_SHUNIT_LINENO_}
375  if ${__SHUNIT_BUILTIN} [ $# -gt 2 ]; then
376    # Allowing 0 arguments as $1 might actually be null.
377    _shunit_error "assertNotNull() requires one or two arguments; $# given"
378    _shunit_assertFail
379    return ${SHUNIT_ERROR}
380  fi
381  if _shunit_shouldSkip; then
382    return ${SHUNIT_TRUE}
383  fi
384
385  shunit_message_=${__shunit_lineno}
386  if ${__SHUNIT_BUILTIN} [ $# -eq 2 ]; then
387    shunit_message_="${shunit_message_}$1"
388    shift
389  fi
390  shunit_actual_=`_shunit_escapeCharactersInString "${1:-}"`
391  test -n "${shunit_actual_}"
392  assertTrue "${shunit_message_}" $?
393  shunit_return=$?
394
395  unset shunit_actual_ shunit_message_
396  return ${shunit_return}
397}
398# shellcheck disable=SC2016,SC2034
399_ASSERT_NOT_NULL_='eval assertNotNull --lineno "${LINENO:-}"'
400
401# Assert that two values are the same (i.e. equal to one another).
402#
403# Args:
404#   message: string: failure message [optional]
405#   expected: string: expected value
406#   actual: string: actual value
407# Returns:
408#   integer: success (TRUE/FALSE/ERROR constant)
409assertSame() {
410  # shellcheck disable=SC2090
411  ${_SHUNIT_LINENO_}
412  if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then
413    _shunit_error "assertSame() requires two or three arguments; $# given"
414    _shunit_assertFail
415    return ${SHUNIT_ERROR}
416  fi
417  if _shunit_shouldSkip; then
418    return ${SHUNIT_TRUE}
419  fi
420
421  shunit_message_=${__shunit_lineno}
422  if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then
423    shunit_message_="${shunit_message_}$1"
424    shift
425  fi
426  assertEquals "${shunit_message_}" "$1" "$2"
427  shunit_return=$?
428
429  unset shunit_message_
430  return ${shunit_return}
431}
432# shellcheck disable=SC2016,SC2034
433_ASSERT_SAME_='eval assertSame --lineno "${LINENO:-}"'
434
435# Assert that two values are not the same (i.e. not equal to one another).
436#
437# Args:
438#   message: string: failure message [optional]
439#   expected: string: expected value
440#   actual: string: actual value
441# Returns:
442#   integer: success (TRUE/FALSE/ERROR constant)
443assertNotSame() {
444  # shellcheck disable=SC2090
445  ${_SHUNIT_LINENO_}
446  if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then
447    _shunit_error "assertNotSame() requires two or three arguments; $# given"
448    _shunit_assertFail
449    return ${SHUNIT_ERROR}
450  fi
451  if _shunit_shouldSkip; then
452    return ${SHUNIT_TRUE}
453  fi
454
455  shunit_message_=${__shunit_lineno}
456  if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then
457    shunit_message_="${shunit_message_:-}$1"
458    shift
459  fi
460  assertNotEquals "${shunit_message_}" "$1" "$2"
461  shunit_return=$?
462
463  unset shunit_message_
464  return ${shunit_return}
465}
466# shellcheck disable=SC2016,SC2034
467_ASSERT_NOT_SAME_='eval assertNotSame --lineno "${LINENO:-}"'
468
469# Assert that a value or shell test condition is true.
470#
471# In shell, a value of 0 is true and a non-zero value is false. Any integer
472# value passed can thereby be tested.
473#
474# Shell supports much more complicated tests though, and a means to support
475# them was needed. As such, this function tests that conditions are true or
476# false through evaluation rather than just looking for a true or false.
477#
478# The following test will succeed:
479#   assertTrue 0
480#   assertTrue "[ 34 -gt 23 ]"
481# The following test will fail with a message:
482#   assertTrue 123
483#   assertTrue "test failed" "[ -r '/non/existent/file' ]"
484#
485# Args:
486#   message: string: failure message [optional]
487#   condition: string: integer value or shell conditional statement
488# Returns:
489#   integer: success (TRUE/FALSE/ERROR constant)
490assertTrue() {
491  # shellcheck disable=SC2090
492  ${_SHUNIT_LINENO_}
493  if ${__SHUNIT_BUILTIN} [ $# -lt 1 -o $# -gt 2 ]; then
494    _shunit_error "assertTrue() takes one or two arguments; $# given"
495    _shunit_assertFail
496    return ${SHUNIT_ERROR}
497  fi
498  if _shunit_shouldSkip; then
499    return ${SHUNIT_TRUE}
500  fi
501
502  shunit_message_=${__shunit_lineno}
503  if ${__SHUNIT_BUILTIN} [ $# -eq 2 ]; then
504    shunit_message_="${shunit_message_}$1"
505    shift
506  fi
507  shunit_condition_=$1
508
509  # See if condition is an integer, i.e. a return value.
510  shunit_return=${SHUNIT_TRUE}
511  if ${__SHUNIT_BUILTIN} [ -z "${shunit_condition_}" ]; then
512    # Null condition.
513    shunit_return=${SHUNIT_FALSE}
514  elif (expr \( "${shunit_condition_}" + '0' \) '=' "${shunit_condition_}" >/dev/null 2>&1)
515  then
516    # Possible return value. Treating 0 as true, and non-zero as false.
517    if ${__SHUNIT_BUILTIN} [ "${shunit_condition_}" -ne 0 ]; then
518      shunit_return=${SHUNIT_FALSE}
519    fi
520  else
521    # Hopefully... a condition.
522    if ! eval "${shunit_condition_}" >/dev/null 2>&1; then
523      shunit_return=${SHUNIT_FALSE}
524    fi
525  fi
526
527  # Record the test.
528  if ${__SHUNIT_BUILTIN} [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then
529    _shunit_assertPass
530  else
531    _shunit_assertFail "${shunit_message_}"
532  fi
533
534  unset shunit_message_ shunit_condition_
535  return ${shunit_return}
536}
537# shellcheck disable=SC2016,SC2034
538_ASSERT_TRUE_='eval assertTrue --lineno "${LINENO:-}"'
539
540# Assert that a value or shell test condition is false.
541#
542# In shell, a value of 0 is true and a non-zero value is false. Any integer
543# value passed can thereby be tested.
544#
545# Shell supports much more complicated tests though, and a means to support
546# them was needed. As such, this function tests that conditions are true or
547# false through evaluation rather than just looking for a true or false.
548#
549# The following test will succeed:
550#   assertFalse 1
551#   assertFalse "[ 'apples' = 'oranges' ]"
552# The following test will fail with a message:
553#   assertFalse 0
554#   assertFalse "test failed" "[ 1 -eq 1 -a 2 -eq 2 ]"
555#
556# Args:
557#   message: string: failure message [optional]
558#   condition: string: integer value or shell conditional statement
559# Returns:
560#   integer: success (TRUE/FALSE/ERROR constant)
561assertFalse() {
562  # shellcheck disable=SC2090
563  ${_SHUNIT_LINENO_}
564  if ${__SHUNIT_BUILTIN} [ $# -lt 1 -o $# -gt 2 ]; then
565    _shunit_error "assertFalse() requires one or two arguments; $# given"
566    _shunit_assertFail
567    return ${SHUNIT_ERROR}
568  fi
569  if _shunit_shouldSkip; then
570    return ${SHUNIT_TRUE}
571  fi
572
573  shunit_message_=${__shunit_lineno}
574  if ${__SHUNIT_BUILTIN} [ $# -eq 2 ]; then
575    shunit_message_="${shunit_message_}$1"
576    shift
577  fi
578  shunit_condition_=$1
579
580  # See if condition is an integer, i.e. a return value.
581  shunit_return=${SHUNIT_TRUE}
582  if ${__SHUNIT_BUILTIN} [ -z "${shunit_condition_}" ]; then
583    # Null condition.
584    shunit_return=${SHUNIT_TRUE}
585  elif (expr \( "${shunit_condition_}" + '0' \) '=' "${shunit_condition_}" >/dev/null 2>&1); then
586    # Possible return value. Treating 0 as true, and non-zero as false.
587    if ${__SHUNIT_BUILTIN} [ "${shunit_condition_}" -eq 0 ]; then
588      shunit_return=${SHUNIT_FALSE}
589    fi
590  else
591    # Hopefully... a condition.
592    # shellcheck disable=SC2086
593    if eval ${shunit_condition_} >/dev/null 2>&1; then
594      shunit_return=${SHUNIT_FALSE}
595    fi
596  fi
597
598  # Record the test.
599  if ${__SHUNIT_BUILTIN} [ "${shunit_return}" -eq "${SHUNIT_TRUE}" ]; then
600    _shunit_assertPass
601  else
602    _shunit_assertFail "${shunit_message_}"
603  fi
604
605  unset shunit_message_ shunit_condition_
606  return "${shunit_return}"
607}
608# shellcheck disable=SC2016,SC2034
609_ASSERT_FALSE_='eval assertFalse --lineno "${LINENO:-}"'
610
611#-----------------------------------------------------------------------------
612# Failure functions.
613#
614
615# Records a test failure.
616#
617# Args:
618#   message: string: failure message [optional]
619# Returns:
620#   integer: success (TRUE/FALSE/ERROR constant)
621fail() {
622  # shellcheck disable=SC2090
623  ${_SHUNIT_LINENO_}
624  if ${__SHUNIT_BUILTIN} [ $# -gt 1 ]; then
625    _shunit_error "fail() requires zero or one arguments; $# given"
626    return ${SHUNIT_ERROR}
627  fi
628  if _shunit_shouldSkip; then
629    return ${SHUNIT_TRUE}
630  fi
631
632  shunit_message_=${__shunit_lineno}
633  if ${__SHUNIT_BUILTIN} [ $# -eq 1 ]; then
634    shunit_message_="${shunit_message_}$1"
635    shift
636  fi
637
638  _shunit_assertFail "${shunit_message_}"
639
640  unset shunit_message_
641  return ${SHUNIT_FALSE}
642}
643# shellcheck disable=SC2016,SC2034
644_FAIL_='eval fail --lineno "${LINENO:-}"'
645
646# Records a test failure, stating two values were not equal.
647#
648# Args:
649#   message: string: failure message [optional]
650#   expected: string: expected value
651#   actual: string: actual value
652# Returns:
653#   integer: success (TRUE/FALSE/ERROR constant)
654failNotEquals() {
655  # shellcheck disable=SC2090
656  ${_SHUNIT_LINENO_}
657  if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then
658    _shunit_error "failNotEquals() requires one or two arguments; $# given"
659    return ${SHUNIT_ERROR}
660  fi
661  if _shunit_shouldSkip; then
662    return ${SHUNIT_TRUE}
663  fi
664
665  shunit_message_=${__shunit_lineno}
666  if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then
667    shunit_message_="${shunit_message_}$1"
668    shift
669  fi
670  shunit_expected_=$1
671  shunit_actual_=$2
672
673  shunit_message_=${shunit_message_%% }
674  _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected:<${shunit_expected_}> but was:<${shunit_actual_}>"
675
676  unset shunit_message_ shunit_expected_ shunit_actual_
677  return ${SHUNIT_FALSE}
678}
679# shellcheck disable=SC2016,SC2034
680_FAIL_NOT_EQUALS_='eval failNotEquals --lineno "${LINENO:-}"'
681
682# Records a test failure, stating a value was found.
683#
684# Args:
685#   message: string: failure message [optional]
686#   content: string: found value
687# Returns:
688#   integer: success (TRUE/FALSE/ERROR constant)
689failFound() {
690  # shellcheck disable=SC2090
691  ${_SHUNIT_LINENO_}
692  if ${__SHUNIT_BUILTIN} [ $# -lt 1 -o $# -gt 2 ]; then
693    _shunit_error "failFound() requires one or two arguments; $# given"
694    return ${SHUNIT_ERROR}
695  fi
696  if _shunit_shouldSkip; then
697    return ${SHUNIT_TRUE}
698  fi
699
700  shunit_message_=${__shunit_lineno}
701  if ${__SHUNIT_BUILTIN} [ $# -eq 2 ]; then
702    shunit_message_="${shunit_message_}$1"
703    shift
704  fi
705
706  shunit_message_=${shunit_message_%% }
707  _shunit_assertFail "${shunit_message_:+${shunit_message_} }found"
708
709  unset shunit_message_
710  return ${SHUNIT_FALSE}
711}
712# shellcheck disable=SC2016,SC2034
713_FAIL_FOUND_='eval failFound --lineno "${LINENO:-}"'
714
715# Records a test failure, stating a content was not found.
716#
717# Args:
718#   message: string: failure message [optional]
719#   content: string: content not found
720# Returns:
721#   integer: success (TRUE/FALSE/ERROR constant)
722failNotFound() {
723  # shellcheck disable=SC2090
724  ${_SHUNIT_LINENO_}
725  if ${__SHUNIT_BUILTIN} [ $# -lt 1 -o $# -gt 2 ]; then
726    _shunit_error "failNotFound() requires one or two arguments; $# given"
727    return ${SHUNIT_ERROR}
728  fi
729  if _shunit_shouldSkip; then
730    return ${SHUNIT_TRUE}
731  fi
732
733  shunit_message_=${__shunit_lineno}
734  if ${__SHUNIT_BUILTIN} [ $# -eq 2 ]; then
735    shunit_message_="${shunit_message_}$1"
736    shift
737  fi
738  shunit_content_=$1
739
740  shunit_message_=${shunit_message_%% }
741  _shunit_assertFail "${shunit_message_:+${shunit_message_} }not found:<${shunit_content_}>"
742
743  unset shunit_message_ shunit_content_
744  return ${SHUNIT_FALSE}
745}
746# shellcheck disable=SC2016,SC2034
747_FAIL_NOT_FOUND_='eval failNotFound --lineno "${LINENO:-}"'
748
749# Records a test failure, stating two values should have been the same.
750#
751# Args:
752#   message: string: failure message [optional]
753#   expected: string: expected value
754#   actual: string: actual value
755# Returns:
756#   integer: success (TRUE/FALSE/ERROR constant)
757failSame() {
758  # shellcheck disable=SC2090
759  ${_SHUNIT_LINENO_}
760  if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then
761    _shunit_error "failSame() requires two or three arguments; $# given"
762    return ${SHUNIT_ERROR}
763  fi
764  if _shunit_shouldSkip; then
765    return ${SHUNIT_TRUE}
766  fi
767
768  shunit_message_=${__shunit_lineno}
769  if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then
770    shunit_message_="${shunit_message_}$1"
771    shift
772  fi
773
774  shunit_message_=${shunit_message_%% }
775  _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected not same"
776
777  unset shunit_message_
778  return ${SHUNIT_FALSE}
779}
780# shellcheck disable=SC2016,SC2034
781_FAIL_SAME_='eval failSame --lineno "${LINENO:-}"'
782
783# Records a test failure, stating two values were not equal.
784#
785# This is functionally equivalent to calling failNotEquals().
786#
787# Args:
788#   message: string: failure message [optional]
789#   expected: string: expected value
790#   actual: string: actual value
791# Returns:
792#   integer: success (TRUE/FALSE/ERROR constant)
793failNotSame() {
794  # shellcheck disable=SC2090
795  ${_SHUNIT_LINENO_}
796  if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then
797    _shunit_error "failNotSame() requires one or two arguments; $# given"
798    return ${SHUNIT_ERROR}
799  fi
800  if _shunit_shouldSkip; then
801    return ${SHUNIT_TRUE}
802  fi
803
804  shunit_message_=${__shunit_lineno}
805  if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then
806    shunit_message_="${shunit_message_}$1"
807    shift
808  fi
809  failNotEquals "${shunit_message_}" "$1" "$2"
810  shunit_return=$?
811
812  unset shunit_message_
813  return ${shunit_return}
814}
815# shellcheck disable=SC2016,SC2034
816_FAIL_NOT_SAME_='eval failNotSame --lineno "${LINENO:-}"'
817
818#-----------------------------------------------------------------------------
819# Skipping functions.
820#
821
822# Force remaining assert and fail functions to be "skipped".
823#
824# This function forces the remaining assert and fail functions to be "skipped",
825# i.e. they will have no effect. Each function skipped will be recorded so that
826# the total of asserts and fails will not be altered.
827#
828# Args:
829#   None
830startSkipping() { __shunit_skip=${SHUNIT_TRUE}; }
831
832# Resume the normal recording behavior of assert and fail calls.
833#
834# Args:
835#   None
836endSkipping() { __shunit_skip=${SHUNIT_FALSE}; }
837
838# Returns the state of assert and fail call skipping.
839#
840# Args:
841#   None
842# Returns:
843#   boolean: (TRUE/FALSE constant)
844isSkipping() { return ${__shunit_skip}; }
845
846#-----------------------------------------------------------------------------
847# Suite functions.
848#
849
850# Stub. This function should contains all unit test calls to be made.
851#
852# DEPRECATED (as of 2.1.0)
853#
854# This function can be optionally overridden by the user in their test suite.
855#
856# If this function exists, it will be called when shunit2 is sourced. If it
857# does not exist, shunit2 will search the parent script for all functions
858# beginning with the word 'test', and they will be added dynamically to the
859# test suite.
860#
861# This function should be overridden by the user in their unit test suite.
862# Note: see _shunit_mktempFunc() for actual implementation
863#
864# Args:
865#   None
866#suite() { :; }  # DO NOT UNCOMMENT THIS FUNCTION
867
868# Adds a function name to the list of tests schedule for execution.
869#
870# This function should only be called from within the suite() function.
871#
872# Args:
873#   function: string: name of a function to add to current unit test suite
874suite_addTest() {
875  shunit_func_=${1:-}
876
877  __shunit_suite="${__shunit_suite:+${__shunit_suite} }${shunit_func_}"
878  __shunit_testsTotal=`expr ${__shunit_testsTotal} + 1`
879
880  unset shunit_func_
881}
882
883# Stub. This function will be called once before any tests are run.
884#
885# Common one-time environment preparation tasks shared by all tests can be
886# defined here.
887#
888# This function should be overridden by the user in their unit test suite.
889# Note: see _shunit_mktempFunc() for actual implementation
890#
891# Args:
892#   None
893#oneTimeSetUp() { :; }  # DO NOT UNCOMMENT THIS FUNCTION
894
895# Stub. This function will be called once after all tests are finished.
896#
897# Common one-time environment cleanup tasks shared by all tests can be defined
898# here.
899#
900# This function should be overridden by the user in their unit test suite.
901# Note: see _shunit_mktempFunc() for actual implementation
902#
903# Args:
904#   None
905#oneTimeTearDown() { :; }  # DO NOT UNCOMMENT THIS FUNCTION
906
907# Stub. This function will be called before each test is run.
908#
909# Common environment preparation tasks shared by all tests can be defined here.
910#
911# This function should be overridden by the user in their unit test suite.
912# Note: see _shunit_mktempFunc() for actual implementation
913#
914# Args:
915#   None
916#setUp() { :; }  # DO NOT UNCOMMENT THIS FUNCTION
917
918# Note: see _shunit_mktempFunc() for actual implementation
919# Stub. This function will be called after each test is run.
920#
921# Common environment cleanup tasks shared by all tests can be defined here.
922#
923# This function should be overridden by the user in their unit test suite.
924# Note: see _shunit_mktempFunc() for actual implementation
925#
926# Args:
927#   None
928#tearDown() { :; }  # DO NOT UNCOMMENT THIS FUNCTION
929
930#------------------------------------------------------------------------------
931# Internal shUnit2 functions.
932#
933
934# Create a temporary directory to store various run-time files in.
935#
936# This function is a cross-platform temporary directory creation tool. Not all
937# OSes have the `mktemp` function, so one is included here.
938#
939# Args:
940#   None
941# Outputs:
942#   string: the temporary directory that was created
943_shunit_mktempDir() {
944  # Try the standard `mktemp` function.
945  if ( exec mktemp -dqt shunit.XXXXXX 2>/dev/null ); then
946    return
947  fi
948
949  # The standard `mktemp` didn't work. Use our own.
950  # shellcheck disable=SC2039
951  if ${__SHUNIT_BUILTIN} [ -r '/dev/urandom' -a -x '/usr/bin/od' ]; then
952    _shunit_random_=`/usr/bin/od -vAn -N4 -tx4 </dev/urandom |command sed 's/^[^0-9a-f]*//'`
953  elif ${__SHUNIT_BUILTIN} [ -n "${RANDOM:-}" ]; then
954    # $RANDOM works
955    _shunit_random_=${RANDOM}${RANDOM}${RANDOM}$$
956  else
957    # `$RANDOM` doesn't work.
958    _shunit_date_=`date '+%Y%m%d%H%M%S'`
959    _shunit_random_=`expr "${_shunit_date_}" / $$`
960  fi
961
962  _shunit_tmpDir_="${TMPDIR:-/tmp}/shunit.${_shunit_random_}"
963  if ! ( umask 077 && command mkdir "${_shunit_tmpDir_}" ); then
964    _shunit_fatal 'could not create temporary directory! exiting'
965  fi
966
967  echo "${_shunit_tmpDir_}"
968  unset _shunit_date_ _shunit_random_ _shunit_tmpDir_
969}
970
971# This function is here to work around issues in Cygwin.
972#
973# Args:
974#   None
975_shunit_mktempFunc() {
976  for _shunit_func_ in oneTimeSetUp oneTimeTearDown setUp tearDown suite noexec
977  do
978    _shunit_file_="${__shunit_tmpDir}/${_shunit_func_}"
979    command cat <<EOF >"${_shunit_file_}"
980#! /bin/sh
981exit ${SHUNIT_TRUE}
982EOF
983    command chmod +x "${_shunit_file_}"
984  done
985
986  unset _shunit_file_
987}
988
989# Final cleanup function to leave things as we found them.
990#
991# Besides removing the temporary directory, this function is in charge of the
992# final exit code of the unit test. The exit code is based on how the script
993# was ended (e.g. normal exit, or via Ctrl-C).
994#
995# Args:
996#   name: string: name of the trap called (specified when trap defined)
997_shunit_cleanup() {
998  _shunit_name_=$1
999
1000  _shunit_signal_=0
1001  case "${_shunit_name_}" in
1002    EXIT) ;;
1003    INT) _shunit_signal_=130 ;;  # 2+128
1004    TERM) _shunit_signal_=143 ;;  # 15+128
1005    *)
1006      _shunit_error "unrecognized trap value (${_shunit_name_})"
1007      ;;
1008  esac
1009  if ${__SHUNIT_BUILTIN} [ "${_shunit_name_}" != 'EXIT' ]; then
1010    _shunit_warn "trapped and now handling the (${_shunit_name_}) signal"
1011  fi
1012
1013  # Do our work.
1014  if ${__SHUNIT_BUILTIN} [ ${__shunit_clean} -eq ${SHUNIT_FALSE} ]; then
1015    # Ensure tear downs are only called once.
1016    __shunit_clean=${SHUNIT_TRUE}
1017
1018    tearDown || _shunit_warn 'tearDown() returned non-zero return code.'
1019    oneTimeTearDown || \
1020        _shunit_warn 'oneTimeTearDown() returned non-zero return code.'
1021
1022    command rm -fr "${__shunit_tmpDir}"
1023  fi
1024
1025  if ${__SHUNIT_BUILTIN} [ "${_shunit_name_}" != 'EXIT' ]; then
1026    # Handle all non-EXIT signals.
1027    trap - 0  # Disable EXIT trap.
1028    exit ${_shunit_signal_}
1029  elif ${__SHUNIT_BUILTIN} [ ${__shunit_reportGenerated} -eq ${SHUNIT_FALSE} ]; then
1030    _shunit_assertFail 'unknown failure encountered running a test'
1031    _shunit_generateReport
1032    exit ${SHUNIT_ERROR}
1033  fi
1034
1035  unset _shunit_name_ _shunit_signal_
1036}
1037
1038# configureColor based on user color preference.
1039#
1040# Args:
1041#   color: string: color mode (one of `always`, `auto`, or `none`).
1042_shunit_configureColor() {
1043  _shunit_color_=${SHUNIT_FALSE}  # By default, no color.
1044  case $1 in
1045    'always') _shunit_color_=${SHUNIT_TRUE} ;;
1046    'auto')
1047      if ${__SHUNIT_BUILTIN} [ "`_shunit_colors`" -ge 8 ]; then
1048        _shunit_color_=${SHUNIT_TRUE}
1049      fi
1050      ;;
1051    'none') ;;
1052    *) _shunit_fatal "unrecognized color option '$1'" ;;
1053  esac
1054
1055  case ${_shunit_color_} in
1056    ${SHUNIT_TRUE})
1057      __shunit_ansi_none=${__SHUNIT_ANSI_NONE}
1058      __shunit_ansi_red=${__SHUNIT_ANSI_RED}
1059      __shunit_ansi_green=${__SHUNIT_ANSI_GREEN}
1060      __shunit_ansi_yellow=${__SHUNIT_ANSI_YELLOW}
1061      __shunit_ansi_cyan=${__SHUNIT_ANSI_CYAN}
1062      ;;
1063    ${SHUNIT_FALSE})
1064      __shunit_ansi_none=''
1065      __shunit_ansi_red=''
1066      __shunit_ansi_green=''
1067      __shunit_ansi_yellow=''
1068      __shunit_ansi_cyan=''
1069      ;;
1070  esac
1071
1072  unset _shunit_color_ _shunit_tput_
1073}
1074
1075# colors returns the number of supported colors for the TERM.
1076_shunit_colors() {
1077  if _shunit_tput_=`${SHUNIT_CMD_TPUT} colors 2>/dev/null`; then
1078    echo "${_shunit_tput_}"
1079  else
1080    echo 16
1081  fi
1082  unset _shunit_tput_
1083}
1084
1085# The actual running of the tests happens here.
1086#
1087# Args:
1088#   None
1089_shunit_execSuite() {
1090  for _shunit_test_ in ${__shunit_suite}; do
1091    __shunit_testSuccess=${SHUNIT_TRUE}
1092
1093    # Disable skipping.
1094    endSkipping
1095
1096    # Execute the per-test setup function.
1097    if ! setUp; then
1098      _shunit_fatal "setup() returned non-zero return code."
1099    fi
1100
1101    # Execute the test.
1102    echo "${__SHUNIT_TEST_PREFIX}${_shunit_test_}"
1103    # shellcheck disable=SC2086
1104    if ! eval ${_shunit_test_}; then
1105      _shunit_error "${_shunit_test_}() returned non-zero return code."
1106      __shunit_testSuccess=${SHUNIT_ERROR}
1107      _shunit_incFailedCount
1108    fi
1109
1110    # Execute the per-test tear-down function.
1111    if ! tearDown; then
1112      _shunit_fatal "tearDown() returned non-zero return code."
1113    fi
1114
1115    # Update stats.
1116    if ${__SHUNIT_BUILTIN} [ ${__shunit_testSuccess} -eq ${SHUNIT_TRUE} ]; then
1117      __shunit_testsPassed=`expr ${__shunit_testsPassed} + 1`
1118    else
1119      __shunit_testsFailed=`expr ${__shunit_testsFailed} + 1`
1120    fi
1121  done
1122
1123  unset _shunit_test_
1124}
1125
1126# Generates the user friendly report with appropriate OK/FAILED message.
1127#
1128# Args:
1129#   None
1130# Output:
1131#   string: the report of successful and failed tests, as well as totals.
1132_shunit_generateReport() {
1133  if ${__SHUNIT_BUILTIN} [ "${__shunit_reportGenerated}" -eq ${SHUNIT_TRUE} ]; then
1134    return
1135  fi
1136
1137  _shunit_ok_=${SHUNIT_TRUE}
1138
1139  # If no exit code was provided, determine an appropriate one.
1140  if ${__SHUNIT_BUILTIN} [ "${__shunit_testsFailed}" -gt 0 -o ${__shunit_testSuccess} -eq ${SHUNIT_FALSE} ]; then
1141    _shunit_ok_=${SHUNIT_FALSE}
1142  fi
1143
1144  echo
1145  _shunit_msg_="Ran ${__shunit_ansi_cyan}${__shunit_testsTotal}${__shunit_ansi_none}"
1146  if ${__SHUNIT_BUILTIN} [ "${__shunit_testsTotal}" -eq 1 ]; then
1147    ${__SHUNIT_CMD_ECHO_ESC} "${_shunit_msg_} test."
1148  else
1149    ${__SHUNIT_CMD_ECHO_ESC} "${_shunit_msg_} tests."
1150  fi
1151
1152  if ${__SHUNIT_BUILTIN} [ ${_shunit_ok_} -eq ${SHUNIT_TRUE} ]; then
1153    _shunit_msg_="${__shunit_ansi_green}OK${__shunit_ansi_none}"
1154    if ${__SHUNIT_BUILTIN} [ "${__shunit_assertsSkipped}" -gt 0 ]; then
1155      _shunit_msg_="${_shunit_msg_} (${__shunit_ansi_yellow}skipped=${__shunit_assertsSkipped}${__shunit_ansi_none})"
1156    fi
1157  else
1158    _shunit_msg_="${__shunit_ansi_red}FAILED${__shunit_ansi_none}"
1159    _shunit_msg_="${_shunit_msg_} (${__shunit_ansi_red}failures=${__shunit_assertsFailed}${__shunit_ansi_none}"
1160    if ${__SHUNIT_BUILTIN} [ "${__shunit_assertsSkipped}" -gt 0 ]; then
1161      _shunit_msg_="${_shunit_msg_},${__shunit_ansi_yellow}skipped=${__shunit_assertsSkipped}${__shunit_ansi_none}"
1162    fi
1163    _shunit_msg_="${_shunit_msg_})"
1164  fi
1165
1166  echo
1167  ${__SHUNIT_CMD_ECHO_ESC} "${_shunit_msg_}"
1168  __shunit_reportGenerated=${SHUNIT_TRUE}
1169
1170  unset _shunit_msg_ _shunit_ok_
1171}
1172
1173# Test for whether a function should be skipped.
1174#
1175# Args:
1176#   None
1177# Returns:
1178#   boolean: whether the test should be skipped (TRUE/FALSE constant)
1179_shunit_shouldSkip() {
1180  if test ${__shunit_skip} -eq ${SHUNIT_FALSE}; then
1181    return ${SHUNIT_FALSE}
1182  fi
1183  _shunit_assertSkip
1184}
1185
1186# Records a successful test.
1187#
1188# Args:
1189#   None
1190_shunit_assertPass() {
1191  __shunit_assertsPassed=`expr ${__shunit_assertsPassed} + 1`
1192  __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1`
1193}
1194
1195# Records a test failure.
1196#
1197# Args:
1198#   message: string: failure message to provide user
1199_shunit_assertFail() {
1200  __shunit_testSuccess=${SHUNIT_FALSE}
1201  _shunit_incFailedCount
1202
1203  if ${__SHUNIT_BUILTIN} [ $# -gt 0 ]; then
1204    ${__SHUNIT_CMD_ECHO_ESC} "${__shunit_ansi_red}ASSERT:${__shunit_ansi_none}$*"
1205  fi
1206}
1207
1208# Increment the count of failed asserts.
1209#
1210# Args:
1211#   none
1212_shunit_incFailedCount() {
1213  __shunit_assertsFailed=`expr "${__shunit_assertsFailed}" + 1`
1214  __shunit_assertsTotal=`expr "${__shunit_assertsTotal}" + 1`
1215}
1216
1217# Records a skipped test.
1218#
1219# Args:
1220#   None
1221_shunit_assertSkip() {
1222  __shunit_assertsSkipped=`expr "${__shunit_assertsSkipped}" + 1`
1223  __shunit_assertsTotal=`expr "${__shunit_assertsTotal}" + 1`
1224}
1225
1226# Dump the current test metrics.
1227#
1228# Args:
1229#   none
1230_shunit_metrics() {
1231  echo "< \
1232total: ${__shunit_assertsTotal} \
1233passed: ${__shunit_assertsPassed} \
1234failed: ${__shunit_assertsFailed} \
1235skipped: ${__shunit_assertsSkipped} \
1236>"
1237}
1238
1239# Prepare a script filename for sourcing.
1240#
1241# Args:
1242#   script: string: path to a script to source
1243# Returns:
1244#   string: filename prefixed with ./ (if necessary)
1245_shunit_prepForSourcing() {
1246  _shunit_script_=$1
1247  case "${_shunit_script_}" in
1248    /*|./*) echo "${_shunit_script_}" ;;
1249    *) echo "./${_shunit_script_}" ;;
1250  esac
1251  unset _shunit_script_
1252}
1253
1254# Escape a character in a string.
1255#
1256# Args:
1257#   c: string: unescaped character
1258#   s: string: to escape character in
1259# Returns:
1260#   string: with escaped character(s)
1261_shunit_escapeCharInStr() {
1262  if ${__SHUNIT_BUILTIN} [ -z "$2" ]; then
1263    return  # No point in doing work on an empty string.
1264  fi
1265
1266  # Note: using shorter variable names to prevent conflicts with
1267  # _shunit_escapeCharactersInString().
1268  _shunit_c_=$1
1269  _shunit_s_=$2
1270
1271  # Escape the character.
1272  # shellcheck disable=SC1003,SC2086
1273  echo ''${_shunit_s_}'' |command sed 's/\'${_shunit_c_}'/\\\'${_shunit_c_}'/g'
1274
1275  unset _shunit_c_ _shunit_s_
1276}
1277
1278# Escape a character in a string.
1279#
1280# Args:
1281#   str: string: to escape characters in
1282# Returns:
1283#   string: with escaped character(s)
1284_shunit_escapeCharactersInString() {
1285  if ${__SHUNIT_BUILTIN} [ -z "$1" ]; then
1286    return  # No point in doing work on an empty string.
1287  fi
1288
1289  _shunit_str_=$1
1290
1291  # Note: using longer variable names to prevent conflicts with
1292  # _shunit_escapeCharInStr().
1293  for _shunit_char_ in '"' '$' "'" '`'; do
1294    _shunit_str_=`_shunit_escapeCharInStr "${_shunit_char_}" "${_shunit_str_}"`
1295  done
1296
1297  echo "${_shunit_str_}"
1298  unset _shunit_char_ _shunit_str_
1299}
1300
1301# Extract list of functions to run tests against.
1302#
1303# Args:
1304#   script: string: name of script to extract functions from
1305# Returns:
1306#   string: of function names
1307_shunit_extractTestFunctions() {
1308  _shunit_script_=$1
1309
1310  # Extract the lines with test function names, strip of anything besides the
1311  # function name, and output everything on a single line.
1312  _shunit_regex_='^\s*((function test[A-Za-z0-9_-]*)|(test[A-Za-z0-9_-]* *\(\)))'
1313  # shellcheck disable=SC2196
1314  egrep "${_shunit_regex_}" "${_shunit_script_}" \
1315  |command sed 's/^[^A-Za-z0-9_-]*//;s/^function //;s/\([A-Za-z0-9_-]*\).*/\1/g' \
1316  |xargs
1317
1318  unset _shunit_regex_ _shunit_script_
1319}
1320
1321#------------------------------------------------------------------------------
1322# Main.
1323#
1324
1325# Determine the operating mode.
1326if ${__SHUNIT_BUILTIN} [ $# -eq 0 -o "${1:-}" = '--' ]; then
1327  __shunit_script=${__SHUNIT_PARENT}
1328  __shunit_mode=${__SHUNIT_MODE_SOURCED}
1329else
1330  __shunit_script=$1
1331  if ! ${__SHUNIT_BUILTIN} [ -r "${__shunit_script}" ]; then
1332    _shunit_fatal "unable to read from ${__shunit_script}"
1333  fi
1334  __shunit_mode=${__SHUNIT_MODE_STANDALONE}
1335fi
1336
1337# Create a temporary storage location.
1338__shunit_tmpDir=`_shunit_mktempDir`
1339
1340# Provide a public temporary directory for unit test scripts.
1341# TODO(kward): document this.
1342SHUNIT_TMPDIR="${__shunit_tmpDir}/tmp"
1343if ! command mkdir "${SHUNIT_TMPDIR}"; then
1344  _shunit_fatal "error creating SHUNIT_TMPDIR '${SHUNIT_TMPDIR}'"
1345fi
1346
1347# Setup traps to clean up after ourselves.
1348trap '_shunit_cleanup EXIT' 0
1349trap '_shunit_cleanup INT' 2
1350trap '_shunit_cleanup TERM' 15
1351
1352# Create phantom functions to work around issues with Cygwin.
1353_shunit_mktempFunc
1354PATH="${__shunit_tmpDir}:${PATH}"
1355
1356# Make sure phantom functions are executable. This will bite if `/tmp` (or the
1357# current `$TMPDIR`) points to a path on a partition that was mounted with the
1358# 'noexec' option. The noexec command was created with `_shunit_mktempFunc()`.
1359noexec 2>/dev/null || _shunit_fatal \
1360    'Please declare TMPDIR with path on partition with exec permission.'
1361
1362# We must manually source the tests in standalone mode.
1363if ${__SHUNIT_BUILTIN} [ "${__shunit_mode}" = "${__SHUNIT_MODE_STANDALONE}" ]; then
1364  # shellcheck disable=SC1090
1365  ${__SHUNIT_BUILTIN} . "`_shunit_prepForSourcing \"${__shunit_script}\"`"
1366fi
1367
1368# Configure default output coloring behavior.
1369_shunit_configureColor "${SHUNIT_COLOR}"
1370
1371# Execute the oneTimeSetUp function (if it exists).
1372if ! oneTimeSetUp; then
1373  _shunit_fatal "oneTimeSetUp() returned non-zero return code."
1374fi
1375
1376# Command line selected tests or suite selected tests
1377if ${__SHUNIT_BUILTIN} [ "$#" -ge 2 ]; then
1378  # Argument $1 is either the filename of tests or '--'; either way, skip it.
1379  shift
1380  # Remaining arguments ($2 .. $#) are assumed to be test function names.
1381  # Interate through all remaining args in "$@" in a POSIX (likely portable) way.
1382  # Helpful tip: https://unix.stackexchange.com/questions/314032/how-to-use-arguments-like-1-2-in-a-for-loop
1383  for _shunit_arg_ do
1384    suite_addTest "${_shunit_arg_}"
1385  done
1386  unset _shunit_arg_
1387else
1388  # Execute the suite function defined in the parent test script.
1389  # DEPRECATED as of 2.1.0.
1390  suite
1391fi
1392
1393# If no tests or suite specified, dynamically build a list of functions.
1394if ${__SHUNIT_BUILTIN} [ -z "${__shunit_suite}" ]; then
1395  shunit_funcs_=`_shunit_extractTestFunctions "${__shunit_script}"`
1396  for shunit_func_ in ${shunit_funcs_}; do
1397    suite_addTest "${shunit_func_}"
1398  done
1399fi
1400unset shunit_func_ shunit_funcs_
1401
1402# Execute the suite of unit tests.
1403_shunit_execSuite
1404
1405# Execute the oneTimeTearDown function (if it exists).
1406if ! oneTimeTearDown; then
1407  _shunit_fatal "oneTimeTearDown() returned non-zero return code."
1408fi
1409
1410# Generate a report summary.
1411_shunit_generateReport
1412
1413# That's it folks.
1414if ! ${__SHUNIT_BUILTIN} [ "${__shunit_testsFailed}" -eq 0 ]; then
1415  return ${SHUNIT_FALSE}
1416fi
1417