2 # Automated Testing Framework (atf)
4 # Copyright (c) 2007 The NetBSD Foundation, Inc.
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions
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.
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.
32 # ------------------------------------------------------------------------
34 # ------------------------------------------------------------------------
36 # Values for the expect property.
40 # A boolean variable that indicates whether we are parsing a test case's
47 # The file to which the test case will print its result.
50 # The test program's source directory: i.e. where its auxiliary data files
51 # and helper utilities can be found. Can be overriden through the '-s' flag.
52 Source_Dir="$(dirname ${0})"
54 # Indicates the test case we are currently processing.
57 # List of meta-data variables for the current test case.
60 # The list of all test cases provided by the test program.
63 # ------------------------------------------------------------------------
65 # ------------------------------------------------------------------------
68 # atf_add_test_case tc-name
70 # Adds the given test case to the list of test cases that form the test
71 # program. The name provided here must be accompanied by two functions
72 # named after it: <tc-name>_head and <tc-name>_body, and optionally by
73 # a <tc-name>_cleanup function.
77 Test_Cases="${Test_Cases} ${1}"
81 # atf_check cmd expcode expout experr
83 # Executes atf-check with given arguments and automatically calls
84 # atf_fail in case of failure.
88 ${Atf_Check} "${@}" || \
89 atf_fail "atf-check failed; see the output of the test for details"
93 # atf_check_equal expr1 expr2
95 # Checks that expr1's value matches expr2's and, if not, raises an
96 # error. Ideally expr1 and expr2 should be provided quoted (not
97 # expanded) so that the error message is helpful; otherwise it will
98 # only show the values, not the expressions themselves.
104 test "${_val1}" = "${_val2}" || \
105 atf_fail "${1} != ${2} (${_val1} != ${_val2})"
109 # atf_config_get varname [defvalue]
111 # Prints the value of a configuration variable. If it is not
112 # defined, prints the given default value.
116 _varname="__tc_config_var_$(_atf_normalize ${1})"
117 if [ ${#} -eq 1 ]; then
118 eval _value=\"\${${_varname}-__unset__}\"
119 [ "${_value}" = __unset__ ] && \
120 _atf_error 1 "Could not find configuration variable \`${1}'"
122 elif [ ${#} -eq 2 ]; then
123 eval echo \${${_varname}-${2}}
125 _atf_error 1 "Incorrect number of parameters for atf_config_get"
130 # atf_config_has varname
132 # Returns a boolean indicating if the given configuration variable is
137 _varname="__tc_config_var_$(_atf_normalize ${1})"
138 eval _value=\"\${${_varname}-__unset__}\"
139 [ "${_value}" != __unset__ ]
143 # atf_expect_death reason
145 # Sets the expectations to 'death'.
152 _atf_create_resfile "expected_death: ${*}"
156 # atf_expect_timeout reason
158 # Sets the expectations to 'timeout'.
165 _atf_create_resfile "expected_timeout: ${*}"
169 # atf_expect_exit exitcode reason
171 # Sets the expectations to 'exit'.
175 _exitcode="${1}"; shift
180 if [ "${_exitcode}" = "-1" ]; then
181 _atf_create_resfile "expected_exit: ${*}"
183 _atf_create_resfile "expected_exit(${_exitcode}): ${*}"
188 # atf_expect_fail reason
190 # Sets the expectations to 'fail'.
203 # Sets the expectations to 'pass'.
214 # atf_expect_signal signo reason
216 # Sets the expectations to 'signal'.
225 if [ "${_signo}" = "-1" ]; then
226 _atf_create_resfile "expected_signal: ${*}"
228 _atf_create_resfile "expected_signal(${_signo}): ${*}"
233 # atf_expected_failure msg1 [.. msgN]
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.
239 atf_expected_failure()
241 _atf_create_resfile "expected_failure: ${Expect_Reason}: ${*}"
246 # atf_fail msg1 [.. msgN]
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
256 atf_expected_failure "${@}"
259 _atf_create_resfile "failed: ${*}"
263 _atf_error 128 "Unreachable"
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)'.
277 eval echo \${__tc_var_${Test_Case}_$(_atf_normalize ${1})}
283 # Prints the value of the test case's source directory.
293 # Makes the test case pass. Shouldn't be used in general, as a test
294 # case that does not explicitly fail is assumed to pass.
301 atf_fail "Test case was expecting a failure but got a pass instead"
304 _atf_create_resfile passed
308 _atf_error 128 "Unreachable"
314 # atf_require_prog prog
316 # Checks that the given program name (either provided as an absolute
317 # path or as a plain file name) can be found. If it is not available,
318 # automatically skips the test case with an appropriate message.
320 # Relative paths are not allowed because the test case cannot predict
321 # where it will be executed from.
330 atf_skip "The required program ${1} could not be found"
333 atf_fail "atf_require_prog does not accept relative path names \`${1}'"
336 _prog=$(_atf_find_in_path "${1}")
337 [ -n "${_prog}" ] || \
338 atf_skip "The required program ${1} could not be found" \
345 # atf_set varname val1 [.. valN]
347 # Sets the test case's variable 'varname' to the specified values
348 # which are concatenated using a single blank space. This function
349 # is supposed to be called form the test case's head only.
354 _atf_error 128 "atf_set called from the test case's body"
356 Test_Case_Vars="${Test_Case_Vars} ${1}"
357 _var=$(_atf_normalize ${1}); shift
358 eval __tc_var_${Test_Case}_${_var}=\"\${*}\"
362 # atf_skip msg1 [.. msgN]
364 # Skips the test case because of the reason provided. Multiple words
365 # can be given, in which case they are joined by a single blank space.
369 _atf_create_resfile "skipped: ${*}"
374 # atf_test_case tc-name cleanup
376 # Defines a new test case named tc-name. The name provided here must be
377 # accompanied by two functions named after it: <tc-name>_head and
378 # <tc-name>_body. If cleanup is set to 'cleanup', then this also expects
379 # a <tc-name>_cleanup function to be defined.
383 eval "${1}_head() { :; }"
384 eval "${1}_body() { atf_fail 'Test case not implemented'; }"
385 if [ "${2}" = cleanup ]; then
386 eval __has_cleanup_${1}=true
387 eval "${1}_cleanup() { :; }"
389 eval "${1}_cleanup() {
390 _atf_error 1 'Test case ${1} declared without a cleanup routine'; }"
394 # ------------------------------------------------------------------------
396 # ------------------------------------------------------------------------
399 # _atf_config_set varname val1 [.. valN]
401 # Sets the test case's private variable 'varname' to the specified
402 # values which are concatenated using a single blank space.
406 _var=$(_atf_normalize ${1}); shift
407 eval __tc_config_var_${_var}=\"\${*}\"
408 Config_Vars="${Config_Vars} __tc_config_var_${_var}"
412 # _atf_config_set_str varname=val
414 # Sets the test case's private variable 'varname' to the specified
415 # value. The parameter is of the form 'varname=val'.
417 _atf_config_set_from_str()
426 _atf_config_set "${_var}" "${_val}"
430 # _atf_create_resfile contents
432 # Creates the results file.
434 _atf_create_resfile()
436 if [ -n "${Results_File}" ]; then
437 echo "${*}" >"${Results_File}" || \
438 _atf_error 128 "Cannot create results file '${Results_File}'"
445 # _atf_error error_code [msg1 [.. msgN]]
447 # Prints the given error message (which can be composed of multiple
448 # arguments, in which case are joined by a single space) and exits
449 # with the specified error code.
451 # This must not be used by test programs themselves (hence making
452 # the function private) to indicate a test case's failure. They
453 # have to use the atf_fail function.
457 _error_code="${1}"; shift
459 echo "${Prog_Name}: ERROR:" "$@" 1>&2
464 # _atf_warning msg1 [.. msgN]
466 # Prints the given warning message (which can be composed of multiple
467 # arguments, in which case are joined by a single space).
471 echo "${Prog_Name}: WARNING:" "$@" 1>&2
475 # _atf_find_in_path program
477 # Looks for a program in the path and prints the full path to it or
478 # nothing if it could not be found. It also returns true in case of
489 if [ -x ${_dir}/${_prog} ]; then
491 echo ${_dir}/${_prog}
503 # Returns true if the given test case exists.
507 for _tc in ${Test_Cases}; do
508 [ "${_tc}" != "${1}" ] || return 0
516 # Describes all test cases and prints the list to the standard output.
520 echo 'Content-Type: application/X-atf-tp; version="1"'
524 while [ ${#} -gt 0 ]; do
527 echo "ident: $(atf_get ident)"
528 for _var in ${Test_Case_Vars}; do
529 [ "${_var}" != "ident" ] && echo "${_var}: $(atf_get ${_var})"
532 [ ${#} -gt 1 ] && echo
540 # Normalizes a string so that it is a valid shell variable name.
548 # _atf_parse_head tcname
550 # Evaluates a test case's head to gather its variables and prepares the
551 # test program to run it.
560 if _atf_has_cleanup "${1}"; then
561 atf_set has.cleanup "true"
573 # Runs the specified test case. Prints its exit status to the
574 # standard output and returns a boolean indicating if the test was
584 if [ "${_tcpart}" != body -a "${_tcpart}" != cleanup ]; then
585 _atf_syntax_error "Unknown test case part \`${_tcpart}'"
595 _atf_has_tc "${_tcname}" || _atf_syntax_error "Unknown test case \`${1}'"
597 if [ "${__RUNNING_INSIDE_ATF_RUN}" != "internal-yes-value" ]; then
598 _atf_warning "Running test cases without atf-run(1) is unsupported"
599 _atf_warning "No isolation nor timeout control is being applied;" \
600 "you may get unexpected failures; see atf-test-case(4)"
603 _atf_parse_head ${_tcname}
607 if ${_tcname}_body; then
609 _atf_create_resfile passed
612 atf_fail "Test case body returned a non-ok exit code, but" \
613 "this is not allowed"
617 if _atf_has_cleanup "${_tcname}"; then
618 ${_tcname}_cleanup || _atf_error 128 "The test case cleanup" \
619 "returned a non-ok exit code, but this is not allowed"
623 _atf_error 128 "Unknown test case part"
629 # _atf_syntax_error msg1 [.. msgN]
631 # Formats and prints a syntax error message and terminates the
632 # program prematurely.
636 echo "${Prog_Name}: ERROR: ${@}" 1>&2
637 echo "${Prog_Name}: See atf-test-program(1) for usage details." 1>&2
642 # _atf_has_cleanup tc-name
644 # Returns a boolean indicating if the given test case has a cleanup
650 eval "[ x\"\${__has_cleanup_${1}}\" = xtrue ] || _found=false"
651 [ "${_found}" = true ]
655 # _atf_validate_expect
657 # Ensures that the current test case state is correct regarding the expect
660 _atf_validate_expect()
665 atf_fail "Test case was expected to terminate abruptly but it" \
666 "continued execution"
670 atf_fail "Test case was expected to exit cleanly but it continued" \
675 atf_fail "Test case was expecting a failure but none were raised"
681 atf_fail "Test case was expected to receive a termination signal" \
682 "but it continued execution"
686 atf_fail "Test case was expected to hang but it continued execution"
689 _atf_error 128 "Unreachable"
695 # _atf_warning [msg1 [.. msgN]]
697 # Prints the given warning message (which can be composed of multiple
698 # arguments, in which case are joined by a single space).
700 # This must not be used by test programs themselves (hence making
701 # the function private).
705 echo "${Prog_Name}: WARNING:" "$@" 1>&2
709 # main [options] test_case
711 # Test program's entry point.
715 # Process command-line options first.
718 while getopts :lr:s:v: arg; do
725 Results_File=${OPTARG}
733 _atf_config_set_from_str "${OPTARG}"
737 _atf_syntax_error "Unknown option -${OPTARG}."
742 shift `expr ${OPTIND} - 1`
744 # First of all, make sure that the source directory is correct. It
745 # doesn't matter if the user did not change it, because the default
746 # value may not work. (TODO: It possibly should, even though it is
747 # not a big deal because atf-run deals with this.)
748 case ${Source_Dir} in
752 Source_Dir=$(pwd)/${Source_Dir}
755 [ -f ${Source_Dir}/${Prog_Name} ] || \
756 _atf_error 1 "Cannot find the test program in the source" \
757 "directory \`${Source_Dir}'"
759 # Call the test program's hook to register all available test cases.
762 # Run or list test cases.
764 if [ ${#} -gt 0 ]; then
765 _atf_syntax_error "Cannot provide test case names with -l"
769 if [ ${#} -eq 0 ]; then
770 _atf_syntax_error "Must provide a test case name"
771 elif [ ${#} -gt 1 ]; then
772 _atf_syntax_error "Cannot provide more than one test case name"
779 # vim: syntax=sh:expandtab:shiftwidth=4:softtabstop=4