1240116Smarcel#
2240116Smarcel# Automated Testing Framework (atf)
3240116Smarcel#
4240116Smarcel# Copyright (c) 2007 The NetBSD Foundation, Inc.
5240116Smarcel# All rights reserved.
6240116Smarcel#
7240116Smarcel# Redistribution and use in source and binary forms, with or without
8240116Smarcel# modification, are permitted provided that the following conditions
9240116Smarcel# are met:
10240116Smarcel# 1. Redistributions of source code must retain the above copyright
11240116Smarcel#    notice, this list of conditions and the following disclaimer.
12240116Smarcel# 2. Redistributions in binary form must reproduce the above copyright
13240116Smarcel#    notice, this list of conditions and the following disclaimer in the
14240116Smarcel#    documentation and/or other materials provided with the distribution.
15240116Smarcel#
16240116Smarcel# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17240116Smarcel# CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18240116Smarcel# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19240116Smarcel# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20240116Smarcel# IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21240116Smarcel# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22240116Smarcel# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23240116Smarcel# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24240116Smarcel# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25240116Smarcel# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26240116Smarcel# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27240116Smarcel# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28240116Smarcel#
29240116Smarcel
30240116Smarcelset -e
31240116Smarcel
32240116Smarcel# ------------------------------------------------------------------------
33240116Smarcel# GLOBAL VARIABLES
34240116Smarcel# ------------------------------------------------------------------------
35240116Smarcel
36240116Smarcel# Values for the expect property.
37240116SmarcelExpect=pass
38240116SmarcelExpect_Reason=
39240116Smarcel
40240116Smarcel# A boolean variable that indicates whether we are parsing a test case's
41240116Smarcel# head or not.
42240116SmarcelParsing_Head=false
43240116Smarcel
44240116Smarcel# The program name.
45240116SmarcelProg_Name=${0##*/}
46240116Smarcel
47240116Smarcel# The file to which the test case will print its result.
48240116SmarcelResults_File=
49240116Smarcel
50240116Smarcel# The test program's source directory: i.e. where its auxiliary data files
51240116Smarcel# and helper utilities can be found.  Can be overriden through the '-s' flag.
52240116SmarcelSource_Dir="$(dirname ${0})"
53240116Smarcel
54240116Smarcel# Indicates the test case we are currently processing.
55240116SmarcelTest_Case=
56240116Smarcel
57240116Smarcel# List of meta-data variables for the current test case.
58240116SmarcelTest_Case_Vars=
59240116Smarcel
60240116Smarcel# The list of all test cases provided by the test program.
61240116SmarcelTest_Cases=
62240116Smarcel
63240116Smarcel# ------------------------------------------------------------------------
64240116Smarcel# PUBLIC INTERFACE
65240116Smarcel# ------------------------------------------------------------------------
66240116Smarcel
67240116Smarcel#
68240116Smarcel# atf_add_test_case tc-name
69240116Smarcel#
70240116Smarcel#   Adds the given test case to the list of test cases that form the test
71240116Smarcel#   program.  The name provided here must be accompanied by two functions
72240116Smarcel#   named after it: <tc-name>_head and <tc-name>_body, and optionally by
73240116Smarcel#   a <tc-name>_cleanup function.
74240116Smarcel#
75240116Smarcelatf_add_test_case()
76240116Smarcel{
77240116Smarcel    Test_Cases="${Test_Cases} ${1}"
78240116Smarcel}
79240116Smarcel
80240116Smarcel#
81240116Smarcel# atf_check cmd expcode expout experr
82240116Smarcel#
83240116Smarcel#   Executes atf-check with given arguments and automatically calls
84240116Smarcel#   atf_fail in case of failure.
85240116Smarcel#
86240116Smarcelatf_check()
87240116Smarcel{
88240116Smarcel    ${Atf_Check} "${@}" || \
89240116Smarcel        atf_fail "atf-check failed; see the output of the test for details"
90240116Smarcel}
91240116Smarcel
92240116Smarcel#
93240116Smarcel# atf_check_equal expr1 expr2
94240116Smarcel#
95240116Smarcel#   Checks that expr1's value matches expr2's and, if not, raises an
96240116Smarcel#   error.  Ideally expr1 and expr2 should be provided quoted (not
97240116Smarcel#   expanded) so that the error message is helpful; otherwise it will
98240116Smarcel#   only show the values, not the expressions themselves.
99240116Smarcel#
100240116Smarcelatf_check_equal()
101240116Smarcel{
102240116Smarcel    eval _val1=\"${1}\"
103240116Smarcel    eval _val2=\"${2}\"
104240116Smarcel    test "${_val1}" = "${_val2}" || \
105240116Smarcel        atf_fail "${1} != ${2} (${_val1} != ${_val2})"
106240116Smarcel}
107240116Smarcel
108240116Smarcel#
109240116Smarcel# atf_config_get varname [defvalue]
110240116Smarcel#
111240116Smarcel#   Prints the value of a configuration variable.  If it is not
112240116Smarcel#   defined, prints the given default value.
113240116Smarcel#
114240116Smarcelatf_config_get()
115240116Smarcel{
116240116Smarcel    _varname="__tc_config_var_$(_atf_normalize ${1})"
117240116Smarcel    if [ ${#} -eq 1 ]; then
118240116Smarcel        eval _value=\"\${${_varname}-__unset__}\"
119240116Smarcel        [ "${_value}" = __unset__ ] && \
120240116Smarcel            _atf_error 1 "Could not find configuration variable \`${1}'"
121240116Smarcel        echo ${_value}
122240116Smarcel    elif [ ${#} -eq 2 ]; then
123240116Smarcel        eval echo \${${_varname}-${2}}
124240116Smarcel    else
125240116Smarcel        _atf_error 1 "Incorrect number of parameters for atf_config_get"
126240116Smarcel    fi
127240116Smarcel}
128240116Smarcel
129240116Smarcel#
130240116Smarcel# atf_config_has varname
131240116Smarcel#
132240116Smarcel#   Returns a boolean indicating if the given configuration variable is
133240116Smarcel#   defined or not.
134240116Smarcel#
135240116Smarcelatf_config_has()
136240116Smarcel{
137240116Smarcel    _varname="__tc_config_var_$(_atf_normalize ${1})"
138240116Smarcel    eval _value=\"\${${_varname}-__unset__}\"
139240116Smarcel    [ "${_value}" != __unset__ ]
140240116Smarcel}
141240116Smarcel
142240116Smarcel#
143240116Smarcel# atf_expect_death reason
144240116Smarcel#
145240116Smarcel#   Sets the expectations to 'death'.
146240116Smarcel#
147240116Smarcelatf_expect_death()
148240116Smarcel{
149240116Smarcel    _atf_validate_expect
150240116Smarcel
151240116Smarcel    Expect=death
152240116Smarcel    _atf_create_resfile "expected_death: ${*}"
153240116Smarcel}
154240116Smarcel
155240116Smarcel#
156240116Smarcel# atf_expect_timeout reason
157240116Smarcel#
158240116Smarcel#   Sets the expectations to 'timeout'.
159240116Smarcel#
160240116Smarcelatf_expect_timeout()
161240116Smarcel{
162240116Smarcel    _atf_validate_expect
163240116Smarcel
164240116Smarcel    Expect=timeout
165240116Smarcel    _atf_create_resfile "expected_timeout: ${*}"
166240116Smarcel}
167240116Smarcel
168240116Smarcel#
169240116Smarcel# atf_expect_exit exitcode reason
170240116Smarcel#
171240116Smarcel#   Sets the expectations to 'exit'.
172240116Smarcel#
173240116Smarcelatf_expect_exit()
174240116Smarcel{
175240116Smarcel    _exitcode="${1}"; shift
176240116Smarcel
177240116Smarcel    _atf_validate_expect
178240116Smarcel
179240116Smarcel    Expect=exit
180240116Smarcel    if [ "${_exitcode}" = "-1" ]; then
181240116Smarcel        _atf_create_resfile "expected_exit: ${*}"
182240116Smarcel    else
183240116Smarcel        _atf_create_resfile "expected_exit(${_exitcode}): ${*}"
184240116Smarcel    fi
185240116Smarcel}
186240116Smarcel
187240116Smarcel#
188240116Smarcel# atf_expect_fail reason
189240116Smarcel#
190240116Smarcel#   Sets the expectations to 'fail'.
191240116Smarcel#
192240116Smarcelatf_expect_fail()
193240116Smarcel{
194240116Smarcel    _atf_validate_expect
195240116Smarcel
196240116Smarcel    Expect=fail
197240116Smarcel    Expect_Reason="${*}"
198240116Smarcel}
199240116Smarcel
200240116Smarcel#
201240116Smarcel# atf_expect_pass
202240116Smarcel#
203240116Smarcel#   Sets the expectations to 'pass'.
204240116Smarcel#
205240116Smarcelatf_expect_pass()
206240116Smarcel{
207240116Smarcel    _atf_validate_expect
208240116Smarcel
209240116Smarcel    Expect=pass
210240116Smarcel    Expect_Reason=
211240116Smarcel}
212240116Smarcel
213240116Smarcel#
214240116Smarcel# atf_expect_signal signo reason
215240116Smarcel#
216240116Smarcel#   Sets the expectations to 'signal'.
217240116Smarcel#
218240116Smarcelatf_expect_signal()
219240116Smarcel{
220240116Smarcel    _signo="${1}"; shift
221240116Smarcel
222240116Smarcel    _atf_validate_expect
223240116Smarcel
224240116Smarcel    Expect=signal
225240116Smarcel    if [ "${_signo}" = "-1" ]; then
226240116Smarcel        _atf_create_resfile "expected_signal: ${*}"
227240116Smarcel    else
228240116Smarcel        _atf_create_resfile "expected_signal(${_signo}): ${*}"
229240116Smarcel    fi
230240116Smarcel}
231240116Smarcel
232240116Smarcel#
233240116Smarcel# atf_expected_failure msg1 [.. msgN]
234240116Smarcel#
235240116Smarcel#   Makes the test case report an expected failure with the given error
236240116Smarcel#   message.  Multiple words can be provided, which are concatenated with
237240116Smarcel#   a single blank space.
238240116Smarcel#
239240116Smarcelatf_expected_failure()
240240116Smarcel{
241240116Smarcel    _atf_create_resfile "expected_failure: ${Expect_Reason}: ${*}"
242240116Smarcel    exit 0
243240116Smarcel}
244240116Smarcel
245240116Smarcel#
246240116Smarcel# atf_fail msg1 [.. msgN]
247240116Smarcel#
248240116Smarcel#   Makes the test case fail with the given error message.  Multiple
249240116Smarcel#   words can be provided, in which case they are joined by a single
250240116Smarcel#   blank space.
251240116Smarcel#
252240116Smarcelatf_fail()
253240116Smarcel{
254240116Smarcel    case "${Expect}" in
255240116Smarcel        fail)
256240116Smarcel            atf_expected_failure "${@}"
257240116Smarcel            ;;
258240116Smarcel        pass)
259240116Smarcel            _atf_create_resfile "failed: ${*}"
260240116Smarcel            exit 1
261240116Smarcel            ;;
262240116Smarcel        *)
263240116Smarcel            _atf_error 128 "Unreachable"
264240116Smarcel            ;;
265240116Smarcel    esac
266240116Smarcel}
267240116Smarcel
268240116Smarcel#
269240116Smarcel# atf_get varname
270240116Smarcel#
271240116Smarcel#   Prints the value of a test case-specific variable.  Given that one
272240116Smarcel#   should not get the value of non-existent variables, it is fine to
273240116Smarcel#   always use this function as 'val=$(atf_get var)'.
274240116Smarcel#
275240116Smarcelatf_get()
276240116Smarcel{
277240116Smarcel    eval echo \${__tc_var_${Test_Case}_$(_atf_normalize ${1})}
278240116Smarcel}
279240116Smarcel
280240116Smarcel#
281240116Smarcel# atf_get_srcdir
282240116Smarcel#
283240116Smarcel#   Prints the value of the test case's source directory.
284240116Smarcel#
285240116Smarcelatf_get_srcdir()
286240116Smarcel{
287240116Smarcel    echo ${Source_Dir}
288240116Smarcel}
289240116Smarcel
290240116Smarcel#
291240116Smarcel# atf_pass
292240116Smarcel#
293240116Smarcel#   Makes the test case pass.  Shouldn't be used in general, as a test
294240116Smarcel#   case that does not explicitly fail is assumed to pass.
295240116Smarcel#
296240116Smarcelatf_pass()
297240116Smarcel{
298240116Smarcel    case "${Expect}" in
299240116Smarcel        fail)
300240116Smarcel            Expect=pass
301240116Smarcel            atf_fail "Test case was expecting a failure but got a pass instead"
302240116Smarcel            ;;
303240116Smarcel        pass)
304240116Smarcel            _atf_create_resfile passed
305240116Smarcel            exit 0
306240116Smarcel            ;;
307240116Smarcel        *)
308240116Smarcel            _atf_error 128 "Unreachable"
309240116Smarcel            ;;
310240116Smarcel    esac
311240116Smarcel}
312240116Smarcel
313240116Smarcel#
314240116Smarcel# atf_require_prog prog
315240116Smarcel#
316240116Smarcel#   Checks that the given program name (either provided as an absolute
317240116Smarcel#   path or as a plain file name) can be found.  If it is not available,
318240116Smarcel#   automatically skips the test case with an appropriate message.
319240116Smarcel#
320240116Smarcel#   Relative paths are not allowed because the test case cannot predict
321240116Smarcel#   where it will be executed from.
322240116Smarcel#
323240116Smarcelatf_require_prog()
324240116Smarcel{
325240116Smarcel    _prog=
326240116Smarcel    case ${1} in
327240116Smarcel    /*)
328240116Smarcel        _prog="${1}"
329240116Smarcel        [ -x ${_prog} ] || \
330240116Smarcel            atf_skip "The required program ${1} could not be found"
331240116Smarcel        ;;
332240116Smarcel    */*)
333240116Smarcel        atf_fail "atf_require_prog does not accept relative path names \`${1}'"
334240116Smarcel        ;;
335240116Smarcel    *)
336240116Smarcel        _prog=$(_atf_find_in_path "${1}")
337240116Smarcel        [ -n "${_prog}" ] || \
338240116Smarcel            atf_skip "The required program ${1} could not be found" \
339240116Smarcel                     "in the PATH"
340240116Smarcel        ;;
341240116Smarcel    esac
342240116Smarcel}
343240116Smarcel
344240116Smarcel#
345240116Smarcel# atf_set varname val1 [.. valN]
346240116Smarcel#
347240116Smarcel#   Sets the test case's variable 'varname' to the specified values
348240116Smarcel#   which are concatenated using a single blank space.  This function
349240116Smarcel#   is supposed to be called form the test case's head only.
350240116Smarcel#
351240116Smarcelatf_set()
352240116Smarcel{
353240116Smarcel    ${Parsing_Head} || \
354240116Smarcel        _atf_error 128 "atf_set called from the test case's body"
355240116Smarcel
356240116Smarcel    Test_Case_Vars="${Test_Case_Vars} ${1}"
357240116Smarcel    _var=$(_atf_normalize ${1}); shift
358240116Smarcel    eval __tc_var_${Test_Case}_${_var}=\"\${*}\"
359240116Smarcel}
360240116Smarcel
361240116Smarcel#
362240116Smarcel# atf_skip msg1 [.. msgN]
363240116Smarcel#
364240116Smarcel#   Skips the test case because of the reason provided.  Multiple words
365240116Smarcel#   can be given, in which case they are joined by a single blank space.
366240116Smarcel#
367240116Smarcelatf_skip()
368240116Smarcel{
369240116Smarcel    _atf_create_resfile "skipped: ${*}"
370240116Smarcel    exit 0
371240116Smarcel}
372240116Smarcel
373240116Smarcel#
374240116Smarcel# atf_test_case tc-name cleanup
375240116Smarcel#
376240116Smarcel#   Defines a new test case named tc-name.  The name provided here must be
377240116Smarcel#   accompanied by two functions named after it: <tc-name>_head and
378240116Smarcel#   <tc-name>_body.  If cleanup is set to 'cleanup', then this also expects
379240116Smarcel#   a <tc-name>_cleanup function to be defined.
380240116Smarcel#
381240116Smarcelatf_test_case()
382240116Smarcel{
383240116Smarcel    eval "${1}_head() { :; }"
384240116Smarcel    eval "${1}_body() { atf_fail 'Test case not implemented'; }"
385240116Smarcel    if [ "${2}" = cleanup ]; then
386240116Smarcel        eval __has_cleanup_${1}=true
387240116Smarcel        eval "${1}_cleanup() { :; }"
388240116Smarcel    else
389240116Smarcel        eval "${1}_cleanup() {
390240116Smarcel            _atf_error 1 'Test case ${1} declared without a cleanup routine'; }"
391240116Smarcel    fi
392240116Smarcel}
393240116Smarcel
394240116Smarcel# ------------------------------------------------------------------------
395240116Smarcel# PRIVATE INTERFACE
396240116Smarcel# ------------------------------------------------------------------------
397240116Smarcel
398240116Smarcel#
399240116Smarcel# _atf_config_set varname val1 [.. valN]
400240116Smarcel#
401240116Smarcel#   Sets the test case's private variable 'varname' to the specified
402240116Smarcel#   values which are concatenated using a single blank space.
403240116Smarcel#
404240116Smarcel_atf_config_set()
405240116Smarcel{
406240116Smarcel    _var=$(_atf_normalize ${1}); shift
407240116Smarcel    eval __tc_config_var_${_var}=\"\${*}\"
408240116Smarcel    Config_Vars="${Config_Vars} __tc_config_var_${_var}"
409240116Smarcel}
410240116Smarcel
411240116Smarcel#
412240116Smarcel# _atf_config_set_str varname=val
413240116Smarcel#
414240116Smarcel#   Sets the test case's private variable 'varname' to the specified
415240116Smarcel#   value.  The parameter is of the form 'varname=val'.
416240116Smarcel#
417240116Smarcel_atf_config_set_from_str()
418240116Smarcel{
419240116Smarcel    _oldifs=${IFS}
420240116Smarcel    IFS='='
421240116Smarcel    set -- ${*}
422240116Smarcel    _var=${1}
423240116Smarcel    shift
424240116Smarcel    _val="${@}"
425240116Smarcel    IFS=${_oldifs}
426240116Smarcel    _atf_config_set "${_var}" "${_val}"
427240116Smarcel}
428240116Smarcel
429240116Smarcel#
430240116Smarcel# _atf_create_resfile contents
431240116Smarcel#
432240116Smarcel#   Creates the results file.
433240116Smarcel#
434240116Smarcel_atf_create_resfile()
435240116Smarcel{
436240116Smarcel    if [ -n "${Results_File}" ]; then
437240116Smarcel        echo "${*}" >"${Results_File}" || \
438240116Smarcel            _atf_error 128 "Cannot create results file '${Results_File}'"
439240116Smarcel    else
440240116Smarcel        echo "${*}"
441240116Smarcel    fi
442240116Smarcel}
443240116Smarcel
444240116Smarcel#
445240116Smarcel# _atf_error error_code [msg1 [.. msgN]]
446240116Smarcel#
447240116Smarcel#   Prints the given error message (which can be composed of multiple
448240116Smarcel#   arguments, in which case are joined by a single space) and exits
449240116Smarcel#   with the specified error code.
450240116Smarcel#
451240116Smarcel#   This must not be used by test programs themselves (hence making
452240116Smarcel#   the function private) to indicate a test case's failure.  They
453240116Smarcel#   have to use the atf_fail function.
454240116Smarcel#
455240116Smarcel_atf_error()
456240116Smarcel{
457240116Smarcel    _error_code="${1}"; shift
458240116Smarcel
459240116Smarcel    echo "${Prog_Name}: ERROR:" "$@" 1>&2
460240116Smarcel    exit ${_error_code}
461240116Smarcel}
462240116Smarcel
463240116Smarcel#
464240116Smarcel# _atf_warning msg1 [.. msgN]
465240116Smarcel#
466240116Smarcel#   Prints the given warning message (which can be composed of multiple
467240116Smarcel#   arguments, in which case are joined by a single space).
468240116Smarcel#
469240116Smarcel_atf_warning()
470240116Smarcel{
471240116Smarcel    echo "${Prog_Name}: WARNING:" "$@" 1>&2
472240116Smarcel}
473240116Smarcel
474240116Smarcel#
475240116Smarcel# _atf_find_in_path program
476240116Smarcel#
477240116Smarcel#   Looks for a program in the path and prints the full path to it or
478240116Smarcel#   nothing if it could not be found.  It also returns true in case of
479240116Smarcel#   success.
480240116Smarcel#
481240116Smarcel_atf_find_in_path()
482240116Smarcel{
483240116Smarcel    _prog="${1}"
484240116Smarcel
485240116Smarcel    _oldifs=${IFS}
486240116Smarcel    IFS=:
487240116Smarcel    for _dir in ${PATH}
488240116Smarcel    do
489240116Smarcel        if [ -x ${_dir}/${_prog} ]; then
490240116Smarcel            IFS=${_oldifs}
491240116Smarcel            echo ${_dir}/${_prog}
492240116Smarcel            return 0
493240116Smarcel        fi
494240116Smarcel    done
495240116Smarcel    IFS=${_oldifs}
496240116Smarcel
497240116Smarcel    return 1
498240116Smarcel}
499240116Smarcel
500240116Smarcel#
501240116Smarcel# _atf_has_tc name
502240116Smarcel#
503240116Smarcel#   Returns true if the given test case exists.
504240116Smarcel#
505240116Smarcel_atf_has_tc()
506240116Smarcel{
507240116Smarcel    for _tc in ${Test_Cases}; do
508240116Smarcel        [ "${_tc}" != "${1}" ] || return 0
509240116Smarcel    done
510240116Smarcel    return 1
511240116Smarcel}
512240116Smarcel
513240116Smarcel#
514240116Smarcel# _atf_list_tcs
515240116Smarcel#
516240116Smarcel#   Describes all test cases and prints the list to the standard output.
517240116Smarcel#
518240116Smarcel_atf_list_tcs()
519240116Smarcel{
520240116Smarcel    echo 'Content-Type: application/X-atf-tp; version="1"'
521240116Smarcel    echo
522240116Smarcel
523240116Smarcel    set -- ${Test_Cases}
524240116Smarcel    while [ ${#} -gt 0 ]; do
525240116Smarcel        _atf_parse_head ${1}
526240116Smarcel
527240116Smarcel        echo "ident: $(atf_get ident)"
528240116Smarcel        for _var in ${Test_Case_Vars}; do
529240116Smarcel            [ "${_var}" != "ident" ] && echo "${_var}: $(atf_get ${_var})"
530240116Smarcel        done
531240116Smarcel
532240116Smarcel        [ ${#} -gt 1 ] && echo
533240116Smarcel        shift
534240116Smarcel    done
535240116Smarcel}
536240116Smarcel
537240116Smarcel#
538240116Smarcel# _atf_normalize str
539240116Smarcel#
540240116Smarcel#   Normalizes a string so that it is a valid shell variable name.
541240116Smarcel#
542240116Smarcel_atf_normalize()
543240116Smarcel{
544240116Smarcel    echo ${1} | tr .- __
545240116Smarcel}
546240116Smarcel
547240116Smarcel#
548240116Smarcel# _atf_parse_head tcname
549240116Smarcel#
550240116Smarcel#   Evaluates a test case's head to gather its variables and prepares the
551240116Smarcel#   test program to run it.
552240116Smarcel#
553240116Smarcel_atf_parse_head()
554240116Smarcel{
555240116Smarcel    Parsing_Head=true
556240116Smarcel
557240116Smarcel    Test_Case="${1}"
558240116Smarcel    Test_Case_Vars=
559240116Smarcel
560240116Smarcel    if _atf_has_cleanup "${1}"; then
561240116Smarcel        atf_set has.cleanup "true"
562240116Smarcel    fi
563240116Smarcel
564240116Smarcel    ${1}_head
565240116Smarcel    atf_set ident "${1}"
566240116Smarcel
567240116Smarcel    Parsing_Head=false
568240116Smarcel}
569240116Smarcel
570240116Smarcel#
571240116Smarcel# _atf_run_tc tc
572240116Smarcel#
573240116Smarcel#   Runs the specified test case.  Prints its exit status to the
574240116Smarcel#   standard output and returns a boolean indicating if the test was
575240116Smarcel#   successful or not.
576240116Smarcel#
577240116Smarcel_atf_run_tc()
578240116Smarcel{
579240116Smarcel    case ${1} in
580240116Smarcel    *:*)
581240116Smarcel        _tcname=${1%%:*}
582240116Smarcel        _tcpart=${1#*:}
583240116Smarcel
584240116Smarcel        if [ "${_tcpart}" != body -a "${_tcpart}" != cleanup ]; then
585240116Smarcel            _atf_syntax_error "Unknown test case part \`${_tcpart}'"
586240116Smarcel        fi
587240116Smarcel        ;;
588240116Smarcel
589240116Smarcel    *)
590240116Smarcel        _tcname=${1}
591240116Smarcel        _tcpart=body
592240116Smarcel        ;;
593240116Smarcel    esac
594240116Smarcel
595240116Smarcel    _atf_has_tc "${_tcname}" || _atf_syntax_error "Unknown test case \`${1}'"
596240116Smarcel
597240116Smarcel    if [ "${__RUNNING_INSIDE_ATF_RUN}" != "internal-yes-value" ]; then
598240116Smarcel        _atf_warning "Running test cases without atf-run(1) is unsupported"
599240116Smarcel        _atf_warning "No isolation nor timeout control is being applied;" \
600240116Smarcel            "you may get unexpected failures; see atf-test-case(4)"
601240116Smarcel    fi
602240116Smarcel
603240116Smarcel    _atf_parse_head ${_tcname}
604240116Smarcel
605240116Smarcel    case ${_tcpart} in
606240116Smarcel    body)
607240116Smarcel        if ${_tcname}_body; then
608240116Smarcel            _atf_validate_expect
609240116Smarcel            _atf_create_resfile passed
610240116Smarcel        else
611240116Smarcel            Expect=pass
612240116Smarcel            atf_fail "Test case body returned a non-ok exit code, but" \
613240116Smarcel                "this is not allowed"
614240116Smarcel        fi
615240116Smarcel        ;;
616240116Smarcel    cleanup)
617240116Smarcel        if _atf_has_cleanup "${_tcname}"; then
618240116Smarcel            ${_tcname}_cleanup || _atf_error 128 "The test case cleanup" \
619240116Smarcel                "returned a non-ok exit code, but this is not allowed"
620240116Smarcel        fi
621240116Smarcel        ;;
622240116Smarcel    *)
623240116Smarcel        _atf_error 128 "Unknown test case part"
624240116Smarcel        ;;
625240116Smarcel    esac
626240116Smarcel}
627240116Smarcel
628240116Smarcel#
629240116Smarcel# _atf_syntax_error msg1 [.. msgN]
630240116Smarcel#
631240116Smarcel#   Formats and prints a syntax error message and terminates the
632240116Smarcel#   program prematurely.
633240116Smarcel#
634240116Smarcel_atf_syntax_error()
635240116Smarcel{
636240116Smarcel    echo "${Prog_Name}: ERROR: ${@}" 1>&2
637240116Smarcel    echo "${Prog_Name}: See atf-test-program(1) for usage details." 1>&2
638240116Smarcel    exit 1
639240116Smarcel}
640240116Smarcel
641240116Smarcel#
642240116Smarcel# _atf_has_cleanup tc-name
643240116Smarcel#
644240116Smarcel#   Returns a boolean indicating if the given test case has a cleanup
645240116Smarcel#   routine or not.
646240116Smarcel#
647240116Smarcel_atf_has_cleanup()
648240116Smarcel{
649240116Smarcel    _found=true
650240116Smarcel    eval "[ x\"\${__has_cleanup_${1}}\" = xtrue ] || _found=false"
651240116Smarcel    [ "${_found}" = true ]
652240116Smarcel}
653240116Smarcel
654240116Smarcel#
655240116Smarcel# _atf_validate_expect
656240116Smarcel#
657240116Smarcel#   Ensures that the current test case state is correct regarding the expect
658240116Smarcel#   status.
659240116Smarcel#
660240116Smarcel_atf_validate_expect()
661240116Smarcel{
662240116Smarcel    case "${Expect}" in
663240116Smarcel        death)
664240116Smarcel            Expect=pass
665240116Smarcel            atf_fail "Test case was expected to terminate abruptly but it" \
666240116Smarcel                "continued execution"
667240116Smarcel            ;;
668240116Smarcel        exit)
669240116Smarcel            Expect=pass
670240116Smarcel            atf_fail "Test case was expected to exit cleanly but it continued" \
671240116Smarcel                "execution"
672240116Smarcel            ;;
673240116Smarcel        fail)
674240116Smarcel            Expect=pass
675240116Smarcel            atf_fail "Test case was expecting a failure but none were raised"
676240116Smarcel            ;;
677240116Smarcel        pass)
678240116Smarcel            ;;
679240116Smarcel        signal)
680240116Smarcel            Expect=pass
681240116Smarcel            atf_fail "Test case was expected to receive a termination signal" \
682240116Smarcel                "but it continued execution"
683240116Smarcel            ;;
684240116Smarcel        timeout)
685240116Smarcel            Expect=pass
686240116Smarcel            atf_fail "Test case was expected to hang but it continued execution"
687240116Smarcel            ;;
688240116Smarcel        *)
689240116Smarcel            _atf_error 128 "Unreachable"
690240116Smarcel            ;;
691240116Smarcel    esac
692240116Smarcel}
693240116Smarcel
694240116Smarcel#
695240116Smarcel# _atf_warning [msg1 [.. msgN]]
696240116Smarcel#
697240116Smarcel#   Prints the given warning message (which can be composed of multiple
698240116Smarcel#   arguments, in which case are joined by a single space).
699240116Smarcel#
700240116Smarcel#   This must not be used by test programs themselves (hence making
701240116Smarcel#   the function private).
702240116Smarcel#
703240116Smarcel_atf_warning()
704240116Smarcel{
705240116Smarcel    echo "${Prog_Name}: WARNING:" "$@" 1>&2
706240116Smarcel}
707240116Smarcel
708240116Smarcel#
709240116Smarcel# main [options] test_case
710240116Smarcel#
711240116Smarcel#   Test program's entry point.
712240116Smarcel#
713240116Smarcelmain()
714240116Smarcel{
715240116Smarcel    # Process command-line options first.
716240116Smarcel    _numargs=${#}
717240116Smarcel    _lflag=false
718240116Smarcel    while getopts :lr:s:v: arg; do
719240116Smarcel        case ${arg} in
720240116Smarcel        l)
721240116Smarcel            _lflag=true
722240116Smarcel            ;;
723240116Smarcel
724240116Smarcel        r)
725240116Smarcel            Results_File=${OPTARG}
726240116Smarcel            ;;
727240116Smarcel
728240116Smarcel        s)
729240116Smarcel            Source_Dir=${OPTARG}
730240116Smarcel            ;;
731240116Smarcel
732240116Smarcel        v)
733240116Smarcel            _atf_config_set_from_str "${OPTARG}"
734240116Smarcel            ;;
735240116Smarcel
736240116Smarcel        \?)
737240116Smarcel            _atf_syntax_error "Unknown option -${OPTARG}."
738240116Smarcel            # NOTREACHED
739240116Smarcel            ;;
740240116Smarcel        esac
741240116Smarcel    done
742240116Smarcel    shift `expr ${OPTIND} - 1`
743240116Smarcel
744240116Smarcel    # First of all, make sure that the source directory is correct.  It
745240116Smarcel    # doesn't matter if the user did not change it, because the default
746240116Smarcel    # value may not work.  (TODO: It possibly should, even though it is
747240116Smarcel    # not a big deal because atf-run deals with this.)
748240116Smarcel    case ${Source_Dir} in
749240116Smarcel        /*)
750240116Smarcel            ;;
751240116Smarcel        *)
752240116Smarcel            Source_Dir=$(pwd)/${Source_Dir}
753240116Smarcel            ;;
754240116Smarcel    esac
755240116Smarcel    [ -f ${Source_Dir}/${Prog_Name} ] || \
756240116Smarcel        _atf_error 1 "Cannot find the test program in the source" \
757240116Smarcel                     "directory \`${Source_Dir}'"
758240116Smarcel
759240116Smarcel    # Call the test program's hook to register all available test cases.
760240116Smarcel    atf_init_test_cases
761240116Smarcel
762240116Smarcel    # Run or list test cases.
763240116Smarcel    if `${_lflag}`; then
764240116Smarcel        if [ ${#} -gt 0 ]; then
765240116Smarcel            _atf_syntax_error "Cannot provide test case names with -l"
766240116Smarcel        fi
767240116Smarcel        _atf_list_tcs
768240116Smarcel    else
769240116Smarcel        if [ ${#} -eq 0 ]; then
770240116Smarcel            _atf_syntax_error "Must provide a test case name"
771240116Smarcel        elif [ ${#} -gt 1 ]; then
772240116Smarcel            _atf_syntax_error "Cannot provide more than one test case name"
773240116Smarcel        else
774240116Smarcel            _atf_run_tc "${1}"
775240116Smarcel        fi
776240116Smarcel    fi
777240116Smarcel}
778240116Smarcel
779240116Smarcel# vim: syntax=sh:expandtab:shiftwidth=4:softtabstop=4
780