1#
2# Automated Testing Framework (atf)
3#
4# Copyright (c) 2007, 2008, 2009, 2010 The NetBSD Foundation, Inc.
5# All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions
9# are met:
10# 1. Redistributions of source code must retain the above copyright
11#    notice, this list of conditions and the following disclaimer.
12# 2. Redistributions in binary form must reproduce the above copyright
13#    notice, this list of conditions and the following disclaimer in the
14#    documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17# CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20# IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28#
29
30set -e
31
32# ------------------------------------------------------------------------
33# GLOBAL VARIABLES
34# ------------------------------------------------------------------------
35
36# The list of all test cases defined by the test program.
37Defined_Test_Cases=
38
39# Values for the expect property.
40Expect=pass
41Expect_Reason=
42
43# A boolean variable that indicates whether we are parsing a test case's
44# head or not.
45Parsing_Head=false
46
47# The program name.
48Prog_Name=${0##*/}
49
50# The file to which the test case will print its result.
51Results_File=
52
53# The test program's source directory: i.e. where its auxiliary data files
54# and helper utilities can be found.  Can be overriden through the '-s' flag.
55Source_Dir="$(dirname ${0})"
56
57# Indicates the test case we are currently processing.
58Test_Case=
59
60# List of meta-data variables for the current test case.
61Test_Case_Vars=
62
63# The list of all test cases provided by the test program.
64# Subset of ${Defined_Test_Cases}.
65Test_Cases=
66Test_Cases_With_Cleanup=
67
68# ------------------------------------------------------------------------
69# PUBLIC INTERFACE
70# ------------------------------------------------------------------------
71
72#
73# atf_add_test_case tc-name
74#
75#   Adds the given test case to the list of test cases that form the test
76#   program.  The name provided here must be accompanied by two functions
77#   named after it: <tc-name>_head and <tc-name>_body, and optionally by
78#   a <tc-name>_cleanup function.
79#
80atf_add_test_case()
81{
82    _atf_is_tc_defined "${1}" || \
83        _atf_error 128 "Test case ${1} was not correctly defined by" \
84                       "this test program"
85    Test_Cases="${Test_Cases} ${1}"
86}
87
88#
89# atf_check cmd expcode expout experr
90#
91#   Executes atf-check with given arguments and automatically calls
92#   atf_fail in case of failure.
93#
94atf_check()
95{
96    ${Atf_Check} "${@}" || \
97        atf_fail "atf-check failed; see the output of the test for details"
98}
99
100#
101# atf_check_equal expr1 expr2
102#
103#   Checks that expr1's value matches expr2's and, if not, raises an
104#   error.  Ideally expr1 and expr2 should be provided quoted (not
105#   expanded) so that the error message is helpful; otherwise it will
106#   only show the values, not the expressions themselves.
107#
108atf_check_equal()
109{
110    eval _val1=\"${1}\"
111    eval _val2=\"${2}\"
112    test "${_val1}" = "${_val2}" || \
113        atf_fail "${1} != ${2} (${_val1} != ${_val2})"
114}
115
116#
117# atf_config_get varname [defvalue]
118#
119#   Prints the value of a configuration variable.  If it is not
120#   defined, prints the given default value.
121#
122atf_config_get()
123{
124    _varname="__tc_config_var_$(_atf_normalize ${1})"
125    if [ ${#} -eq 1 ]; then
126        eval _value=\"\${${_varname}-__unset__}\"
127        [ "${_value}" = __unset__ ] && \
128            _atf_error 1 "Could not find configuration variable \`${1}'"
129        echo ${_value}
130    elif [ ${#} -eq 2 ]; then
131        eval echo \${${_varname}-${2}}
132    else
133        _atf_error 1 "Incorrect number of parameters for atf_config_get"
134    fi
135}
136
137#
138# atf_config_has varname
139#
140#   Returns a boolean indicating if the given configuration variable is
141#   defined or not.
142#
143atf_config_has()
144{
145    _varname="__tc_config_var_$(_atf_normalize ${1})"
146    eval _value=\"\${${_varname}-__unset__}\"
147    [ "${_value}" != __unset__ ]
148}
149
150#
151# atf_expect_death reason
152#
153#   Sets the expectations to 'death'.
154#
155atf_expect_death()
156{
157    _atf_validate_expect
158
159    Expect=death
160    _atf_create_resfile expected_death -1 "${@}"
161}
162
163#
164# atf_expect_timeout reason
165#
166#   Sets the expectations to 'timeout'.
167#
168atf_expect_timeout()
169{
170    _atf_validate_expect
171
172    Expect=timeout
173    _atf_create_resfile expected_timeout -1 "${@}"
174}
175
176#
177# atf_expect_exit exitcode reason
178#
179#   Sets the expectations to 'exit'.
180#
181atf_expect_exit()
182{
183    _exitcode="${1}"; shift
184
185    _atf_validate_expect
186
187    Expect=exit
188    _atf_create_resfile expected_exit "${_exitcode}" "${@}"
189}
190
191#
192# atf_expect_fail reason
193#
194#   Sets the expectations to 'fail'.
195#
196atf_expect_fail()
197{
198    _atf_validate_expect
199
200    Expect=fail
201    Expect_Reason="${*}"
202}
203
204#
205# atf_expect_pass
206#
207#   Sets the expectations to 'pass'.
208#
209atf_expect_pass()
210{
211    _atf_validate_expect
212
213    Expect=pass
214    Expect_Reason=
215}
216
217#
218# atf_expect_signal signo reason
219#
220#   Sets the expectations to 'signal'.
221#
222atf_expect_signal()
223{
224    _signo="${1}"; shift
225
226    _atf_validate_expect
227
228    Expect=signal
229    _atf_create_resfile expected_signal "${_signo}" "${@}"
230}
231
232#
233# atf_expected_failure msg1 [.. msgN]
234#
235#   Makes the test case report an expected failure with the given error
236#   message.  Multiple words can be provided, which are concatenated with
237#   a single blank space.
238#
239atf_expected_failure()
240{
241    _atf_create_resfile expected_failure -1 "${Expect_Reason}:" "${@}"
242    exit 0
243}
244
245#
246# atf_fail msg1 [.. msgN]
247#
248#   Makes the test case fail with the given error message.  Multiple
249#   words can be provided, in which case they are joined by a single
250#   blank space.
251#
252atf_fail()
253{
254    case "${Expect}" in
255        fail)
256            atf_expected_failure "${@}"
257            ;;
258        pass)
259            _atf_create_resfile failed -1 "${@}"
260            exit 1
261            ;;
262        *)
263            _atf_error 128 "Unreachable"
264            ;;
265    esac
266}
267
268#
269# atf_get varname
270#
271#   Prints the value of a test case-specific variable.  Given that one
272#   should not get the value of non-existent variables, it is fine to
273#   always use this function as 'val=$(atf_get var)'.
274#
275atf_get()
276{
277    eval echo \${__tc_var_${Test_Case}_$(_atf_normalize ${1})}
278}
279
280#
281# atf_get_srcdir
282#
283#   Prints the value of the test case's source directory.
284#
285atf_get_srcdir()
286{
287    _atf_internal_get srcdir
288}
289
290#
291# atf_init_test_cases
292#
293#   The function in charge of registering the test cases that have to
294#   be made available to the user.  Must be redefined.
295#
296atf_init_test_cases()
297{
298    _atf_error 128 "No test cases defined"
299}
300
301#
302# atf_pass
303#
304#   Makes the test case pass.  Shouldn't be used in general, as a test
305#   case that does not explicitly fail is assumed to pass.
306#
307atf_pass()
308{
309    case "${Expect}" in
310        fail)
311            Expect=pass
312            atf_fail "Test case was expecting a failure but got a pass instead"
313            ;;
314        pass)
315            _atf_create_resfile passed -1
316            exit 0
317            ;;
318        *)
319            _atf_error 128 "Unreachable"
320            ;;
321    esac
322}
323
324#
325# atf_require_prog prog
326#
327#   Checks that the given program name (either provided as an absolute
328#   path or as a plain file name) can be found.  If it is not available,
329#   automatically skips the test case with an appropriate message.
330#
331#   Relative paths are not allowed because the test case cannot predict
332#   where it will be executed from.
333#
334atf_require_prog()
335{
336    _prog=
337    case ${1} in
338    /*)
339        _prog="${1}"
340        [ -x ${_prog} ] || \
341            atf_skip "The required program ${1} could not be found"
342        ;;
343    */*)
344        atf_fail "atf_require_prog does not accept relative path names \`${1}'"
345        ;;
346    *)
347        _prog=$(_atf_find_in_path "${1}")
348        [ -n "${_prog}" ] || \
349            atf_skip "The required program ${1} could not be found" \
350                     "in the PATH"
351        ;;
352    esac
353}
354
355#
356# atf_set varname val1 [.. valN]
357#
358#   Sets the test case's variable 'varname' to the specified values
359#   which are concatenated using a single blank space.  This function
360#   is supposed to be called form the test case's head only.
361#
362atf_set()
363{
364    ${Parsing_Head} || \
365        _atf_error 128 "atf_set called from the test case's body"
366
367    Test_Case_Vars="${Test_Case_Vars} ${1}"
368    _var=$(_atf_normalize ${1}); shift
369    eval __tc_var_${Test_Case}_${_var}=\"\${*}\"
370}
371
372#
373# atf_skip msg1 [.. msgN]
374#
375#   Skips the test case because of the reason provided.  Multiple words
376#   can be given, in which case they are joined by a single blank space.
377#
378atf_skip()
379{
380    _atf_create_resfile skipped -1 "${@}"
381    exit 0
382}
383
384#
385# atf_test_case tc-name cleanup
386#
387#   Defines a new test case named tc-name.  The name provided here must be
388#   accompanied by two functions named after it: <tc-name>_head and
389#   <tc-name>_body.  If cleanup is set to 'cleanup', then this also expects
390#   a <tc-name>_cleanup function to be defined.
391#
392atf_test_case()
393{
394    Defined_Test_Cases="${Defined_Test_Cases} ${1}"
395
396    eval "${1}_head() { :; }"
397    eval "${1}_body() { :; }"
398    if [ "${2}" = cleanup ]; then
399        Test_Cases_With_Cleanup="${Test_Cases_With_Cleanup} ${1}"
400        eval "${1}_cleanup() { :; }"
401    else
402        eval "${1}_cleanup() {
403            _atf_error 1 'Test case ${1} declared without a cleanup routine'; }"
404    fi
405}
406
407# ------------------------------------------------------------------------
408# PRIVATE INTERFACE
409# ------------------------------------------------------------------------
410
411#
412# _atf_config_set varname val1 [.. valN]
413#
414#   Sets the test case's private variable 'varname' to the specified
415#   values which are concatenated using a single blank space.
416#
417_atf_config_set()
418{
419    _var=$(_atf_normalize ${1}); shift
420    eval __tc_config_var_${_var}=\"\${*}\"
421    Config_Vars="${Config_Vars} __tc_config_var_${_var}"
422}
423
424#
425# _atf_config_set_str varname=val
426#
427#   Sets the test case's private variable 'varname' to the specified
428#   value.  The parameter is of the form 'varname=val'.
429#
430_atf_config_set_from_str()
431{
432    _oldifs=${IFS}
433    IFS='='
434    set -- ${*}
435    _var=${1}
436    shift
437    _val="${@}"
438    IFS=${_oldifs}
439    _atf_config_set "${_var}" "${_val}"
440}
441
442#
443# _atf_create_resfile result arg [reason ...]
444#
445#   Creates the results file.
446#
447_atf_create_resfile()
448{
449    _result="${1}"; shift
450    if [ "${1}" -eq -1 ]; then
451        _arg=""
452        shift
453    else
454        _arg="(${1})"
455        shift
456    fi
457    if [ ${#} -gt 0 ]; then
458        _reason=": ${*}"
459    else
460        _reason=""
461    fi
462
463    if [ -n "${Results_File}" ]; then
464        echo "${_result}${_arg}${_reason}" >"${Results_File}" || \
465            _atf_error 128 "Cannot create results file '${Results_File}'"
466    else
467        echo "${_result}${_arg}${_reason}"
468    fi
469}
470
471#
472# _atf_ensure_boolean var
473#
474#   Ensures that the test case defined the variable 'var' to a boolean
475#   value.
476#
477_atf_ensure_boolean()
478{
479    _atf_ensure_not_empty ${1}
480
481    case $(atf_get ${1}) in
482    [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee])
483        atf_set ${1} true
484        ;;
485    [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee])
486        atf_set ${1} false
487        ;;
488    *)
489        _atf_error 128 "Invalid value for boolean variable \`${1}'"
490        ;;
491    esac
492}
493
494#
495# _atf_ensure_not_empty var
496#
497#   Ensures that the test case defined the variable 'var' to a non-empty
498#   value.
499#
500_atf_ensure_not_empty()
501{
502    [ -n "$(atf_get ${1})" ] || \
503        _atf_error 128 "Undefined or empty variable \`${1}'"
504}
505
506#
507# _atf_error error_code [msg1 [.. msgN]]
508#
509#   Prints the given error message (which can be composed of multiple
510#   arguments, in which case are joined by a single space) and exits
511#   with the specified error code.
512#
513#   This must not be used by test programs themselves (hence making
514#   the function private) to indicate a test case's failure.  They
515#   have to use the atf_fail function.
516#
517_atf_error()
518{
519    _error_code="${1}"; shift
520
521    echo "${Prog_Name}: ERROR:" "$@" 1>&2
522    exit ${_error_code}
523}
524
525#
526# _atf_find_in_path program
527#
528#   Looks for a program in the path and prints the full path to it or
529#   nothing if it could not be found.  It also returns true in case of
530#   success.
531#
532_atf_find_in_path()
533{
534    _prog="${1}"
535
536    _oldifs=${IFS}
537    IFS=:
538    for _dir in ${PATH}
539    do
540        if [ -x ${_dir}/${_prog} ]; then
541            IFS=${_oldifs}
542            echo ${_dir}/${_prog}
543            return 0
544        fi
545    done
546    IFS=${_oldifs}
547
548    return 1
549}
550
551#
552# _atf_has_tc name
553#
554#   Returns true if the given test case exists.
555#
556_atf_has_tc()
557{
558    for _tc in ${Test_Cases}; do
559        if [ ${_tc} = ${1} ]; then
560            return 0
561        fi
562    done
563    return 1
564}
565
566#
567# _atf_get_bool varname
568#
569#   Evaluates a test case-specific variable as a boolean and returns its
570#   value.
571#
572_atf_get_bool()
573{
574    eval $(atf_get ${1})
575}
576
577#
578# _atf_internal_get varname
579#
580#   Prints the value of a test case-specific internal variable.  Given
581#   that one should not get the value of non-existent variables, it is
582#   fine to always use this function as 'val=$(_atf_internal_get var)'.
583#
584_atf_internal_get()
585{
586    eval echo \${__tc_internal_var_${Test_Case}_${1}}
587}
588
589#
590# _atf_internal_set varname val1 [.. valN]
591#
592#   Sets the test case's private variable 'varname' to the specified
593#   values which are concatenated using a single blank space.
594#
595_atf_internal_set()
596{
597    _var=${1}; shift
598    eval __tc_internal_var_${Test_Case}_${_var}=\"\${*}\"
599}
600
601#
602# _atf_list_tcs
603#
604#   Describes all test cases and prints the list to the standard output.
605#
606_atf_list_tcs()
607{
608    echo 'Content-Type: application/X-atf-tp; version="1"'
609    echo
610
611    set -- ${Test_Cases}
612    while [ ${#} -gt 0 ]; do
613        _atf_parse_head ${1}
614
615        echo "ident: $(atf_get ident)"
616        for _var in ${Test_Case_Vars}; do
617            [ "${_var}" != "ident" ] && echo "${_var}: $(atf_get ${_var})"
618        done
619
620        [ ${#} -gt 1 ] && echo
621        shift
622    done
623}
624
625#
626# _atf_normalize str
627#
628#   Normalizes a string so that it is a valid shell variable name.
629#
630_atf_normalize()
631{
632    echo ${1} | tr .- __
633}
634
635#
636# _atf_parse_head tcname
637#
638#   Evaluates a test case's head to gather its variables and prepares the
639#   test program to run it.
640#
641_atf_parse_head()
642{
643    ${Parsing_Head} && _atf_error 128 "_atf_parse_head called recursively"
644    Parsing_Head=true
645
646    Test_Case="${1}"
647    Test_Case_Vars=
648
649    if _atf_has_cleanup "${1}"; then
650        atf_set has.cleanup "true"
651    fi
652
653    atf_set ident "${1}"
654    ${1}_head
655    _atf_ensure_not_empty ident
656    test $(atf_get ident) = "${1}" || \
657        _atf_error 128 "Test case redefined ident"
658
659    Parsing_Head=false
660}
661
662#
663# _atf_run_tc tc
664#
665#   Runs the specified test case.  Prints its exit status to the
666#   standard output and returns a boolean indicating if the test was
667#   successful or not.
668#
669_atf_run_tc()
670{
671    case ${1} in
672    *:*)
673        _tcname=${1%%:*}
674        _tcpart=${1#*:}
675
676        if [ "${_tcpart}" != body -a "${_tcpart}" != cleanup ]; then
677            _atf_syntax_error "Unknown test case part \`${_tcpart}'"
678        fi
679        ;;
680
681    *)
682        _tcname=${1}
683        _tcpart=body
684        ;;
685    esac
686
687    if _atf_has_tc ${_tcname}; then
688        _atf_parse_head ${_tcname}
689
690        _atf_internal_set srcdir "${Source_Dir}"
691
692        case ${_tcpart} in
693        body)
694            if ${_tcname}_body; then
695                _atf_validate_expect
696                _atf_create_resfile passed -1
697            else
698                Expect=pass
699                atf_fail "Test case body returned a non-ok exit code, but" \
700                    "this is not allowed"
701            fi
702            ;;
703        cleanup)
704            if _atf_has_cleanup "${_tcname}"; then
705                if ${_tcname}_cleanup; then
706                    :
707                else
708                    _atf_error 128 "The test case cleanup returned a non-ok" \
709                        "exit code, but this is not allowed"
710                fi
711            fi
712            ;;
713        *)
714            _atf_error 128 "Unknown test case part"
715            ;;
716        esac
717    else
718        _atf_syntax_error "Unknown test case \`${1}'"
719    fi
720}
721
722#
723# _atf_sighup_handler
724#
725#   Handler for the SIGHUP signal that registers its occurrence so that
726#   it can be processed at a later stage.
727#
728_atf_sighup_handler()
729{
730    Held_Signals="${Held_Signals} SIGHUP"
731}
732
733#
734# _atf_sigint_handler
735#
736#   Handler for the SIGINT signal that registers its occurrence so that
737#   it can be processed at a later stage.
738#
739_atf_sigint_handler()
740{
741    Held_Signals="${Held_Signals} SIGINT"
742}
743
744#
745# _atf_sigterm_handler
746#
747#   Handler for the SIGTERM signal that registers its occurrence so that
748#   it can be processed at a later stage.
749#
750_atf_sigterm_handler()
751{
752    Held_Signals="${Held_Signals} SIGTERM"
753}
754
755#
756# _atf_syntax_error msg1 [.. msgN]
757#
758#   Formats and prints a syntax error message and terminates the
759#   program prematurely.
760#
761_atf_syntax_error()
762{
763    echo "${Prog_Name}: ERROR: ${@}" 1>&2
764    echo "${Prog_Name}: See atf-test-program(1) for usage details." 1>&2
765    exit 1
766}
767
768#
769# _atf_is_tc_defined tc-name
770#
771#   Returns a boolean indicating if the given test case was defined by the
772#   test program or not.
773#
774_atf_is_tc_defined()
775{
776    for _tc in ${Defined_Test_Cases}; do
777        [ ${_tc} = ${1} ] && return 0
778    done
779    return 1
780}
781
782#
783# _atf_has_cleanup tc-name
784#
785#   Returns a boolean indicating if the given test case has a cleanup
786#   routine or not.
787#
788_atf_has_cleanup()
789{
790    for _tc in ${Test_Cases_With_Cleanup}; do
791        [ ${_tc} = ${1} ] && return 0
792    done
793    return 1
794}
795
796#
797# _atf_validate_expect
798#
799#   Ensures that the current test case state is correct regarding the expect
800#   status.
801#
802_atf_validate_expect()
803{
804    case "${Expect}" in
805        death)
806            Expect=pass
807            atf_fail "Test case was expected to terminate abruptly but it" \
808                "continued execution"
809            ;;
810        exit)
811            Expect=pass
812            atf_fail "Test case was expected to exit cleanly but it continued" \
813                "execution"
814            ;;
815        fail)
816            Expect=pass
817            atf_fail "Test case was expecting a failure but none were raised"
818            ;;
819        pass)
820            ;;
821        signal)
822            Expect=pass
823            atf_fail "Test case was expected to receive a termination signal" \
824                "but it continued execution"
825            ;;
826        timeout)
827            Expect=pass
828            atf_fail "Test case was expected to hang but it continued execution"
829            ;;
830        *)
831            _atf_error 128 "Unreachable"
832            ;;
833    esac
834}
835
836#
837# _atf_warning [msg1 [.. msgN]]
838#
839#   Prints the given warning message (which can be composed of multiple
840#   arguments, in which case are joined by a single space).
841#
842#   This must not be used by test programs themselves (hence making
843#   the function private).
844#
845_atf_warning()
846{
847    echo "${Prog_Name}: WARNING:" "$@" 1>&2
848}
849
850#
851# main [options] test_case
852#
853#   Test program's entry point.
854#
855main()
856{
857    # Process command-line options first.
858    _numargs=${#}
859    _lflag=false
860    while getopts :lr:s:v: arg; do
861        case ${arg} in
862        l)
863            _lflag=true
864            ;;
865
866        r)
867            Results_File=${OPTARG}
868            ;;
869
870        s)
871            Source_Dir=${OPTARG}
872            ;;
873
874        v)
875            _atf_config_set_from_str "${OPTARG}"
876            ;;
877
878        \?)
879            _atf_syntax_error "Unknown option -${OPTARG}."
880            # NOTREACHED
881            ;;
882        esac
883    done
884    shift `expr ${OPTIND} - 1`
885
886    # First of all, make sure that the source directory is correct.  It
887    # doesn't matter if the user did not change it, because the default
888    # value may not work.  (TODO: It possibly should, even though it is
889    # not a big deal because atf-run deals with this.)
890    case ${Source_Dir} in
891        /*)
892            ;;
893        *)
894            Source_Dir=$(pwd)/${Source_Dir}
895            ;;
896    esac
897    [ -f ${Source_Dir}/${Prog_Name} ] || \
898        _atf_error 1 "Cannot find the test program in the source" \
899                     "directory \`${Source_Dir}'"
900
901    # Set some global variables useful to the user.  Not specific to the
902    # test case because they may be needed during initialization too.
903    # XXX I'm not too fond on this though.  Sure, it is very useful in some
904    # situations -- such as in NetBSD's fs/tmpfs/* tests where each test
905    # program includes a helper subroutines file -- but there are also
906    # other, maybe better ways to achieve the same.  Because, for example,
907    # at the moment it is not possible to detect failures in the inclusion
908    # and report them nicely.  Plus this change is difficult to implement
909    # in the current C++ API.
910    _atf_internal_set srcdir "${Source_Dir}"
911
912    # Call the test program's hook to register all available test cases.
913    atf_init_test_cases
914
915    # Run or list test cases.
916    if `${_lflag}`; then
917        if [ ${#} -gt 0 ]; then
918            _atf_syntax_error "Cannot provide test case names with -l"
919        fi
920        _atf_list_tcs
921    else
922        if [ ${#} -eq 0 ]; then
923            _atf_syntax_error "Must provide a test case name"
924        elif [ ${#} -gt 1 ]; then
925            _atf_syntax_error "Cannot provide more than one test case name"
926        else
927            _atf_run_tc "${1}"
928        fi
929    fi
930}
931
932# vim: syntax=sh:expandtab:shiftwidth=4:softtabstop=4
933