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