2 # Automated Testing Framework (atf)
4 # Copyright (c) 2007, 2008 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.
31 # File: atf.footer.subr
33 # This file provides the test program's entry point and auxiliary
34 # functions used during testing.
37 # ------------------------------------------------------------------------
39 # ------------------------------------------------------------------------
41 # Values of configuration variables obtained from atf-config.
42 Atf_Arch=$(atf-config -t atf_arch)
43 Atf_Cleanup=$(atf-config -t atf_libexecdir)/atf-cleanup
44 Atf_Exec=$(atf-config -t atf_libexecdir)/atf-exec
45 Atf_Format=$(atf-config -t atf_libexecdir)/atf-format
46 Atf_Machine=$(atf-config -t atf_machine)
48 # List of configuration variables set through the command line. Needed
49 # during shortcut execution.
52 # List of blocked signals, to be processed when unblocked.
55 # A boolean variable that indicates whether we are parsing a test case's
59 # The file descriptor on which the test program will print the results of
63 # The file to which the test case will print its result.
66 # The test program's source directory: i.e. where its auxiliary data files
67 # and helper utilities can be found. Defaults to the current directory
68 # but can be overriden through the '-s' flag.
71 # Indicates the test case we are currently processing.
74 # The list of all test cases provided by the test program.
75 # Subset of ${Defined_Test_Cases}.
78 # The test case's work directory. The semantics of this variable are a
79 # bit screwed. When running the parent program (not the test case's body),
80 # this points to the test _program_'s directory. When we have reexecuted
81 # the code through _atf_shortcut_exec, this points to the test _case_'s
82 # work directory, which is a subdirectory of the other one.
85 # ------------------------------------------------------------------------
87 # ------------------------------------------------------------------------
90 # atf_add_test_case tc-name
92 # Adds the given test case to the list of test cases that form the test
93 # program. The name provided here must be accompanied by two functions
94 # named after it: <tc-name>_head and <tc-name>_body, and optionally by
95 # a <tc-name>_cleanup function.
99 _atf_is_tc_defined "${1}" || \
100 _atf_error 128 "Test case ${1} was not correctly defined by" \
102 Test_Cases="${Test_Cases} ${1}"
106 # atf_check cmd expcode expout experr
108 # Executes atf-check with given arguments and automatically calls
109 # atf_fail in case of failure.
113 atf-check "${@}" || \
114 atf_fail "atf-check failed; see the output of the test for details"
118 # atf_check_equal expr1 expr2
120 # Checks that expr1's value matches expr2's and, if not, raises an
121 # error. Ideally expr1 and expr2 should be provided quoted (not
122 # expanded) so that the error message is helpful; otherwise it will
123 # only show the values, not the expressions themselves.
129 test "${_val1}" = "${_val2}" || \
130 atf_fail "${1} != ${2} (${_val1} != ${_val2})"
134 # atf_config_get varname [defvalue]
136 # Prints the value of a configuration variable. If it is not
137 # defined, prints the given default value.
141 _varname="__tc_config_var_$(_atf_normalize ${1})"
142 if [ ${#} -eq 1 ]; then
143 eval _value=\"\${${_varname}-__unset__}\"
144 [ "${_value}" = __unset__ ] && \
145 _atf_error 1 "Could not find configuration variable \`${1}'"
147 elif [ ${#} -eq 2 ]; then
148 eval echo \${${_varname}-${2}}
150 _atf_error 1 "Incorrect number of parameters for atf_config_get"
155 # atf_config_has varname
157 # Returns a boolean indicating if the given configuration variable is
162 _varname="__tc_config_var_$(_atf_normalize ${1})"
163 eval _value=\"\${${_varname}-__unset__}\"
164 [ "${_value}" != __unset__ ]
168 # atf_fail msg1 [.. msgN]
170 # Makes the test case fail with the given error message. Multiple
171 # words can be provided, in which case they are joined by a single
176 echo "failed, ${*}" >>${Results_File}
183 # Prints the value of a test case-specific variable. Given that one
184 # should not get the value of non-existent variables, it is fine to
185 # always use this function as 'val=$(atf_get var)'.
189 eval echo \${__tc_var_${Test_Case}_$(_atf_normalize ${1})}
195 # Prints the value of the test case's source directory.
199 _atf_internal_get srcdir
203 # atf_pass msg1 [.. msgN]
205 # Makes the test case pass. Shouldn't be used in general, as a test
206 # case that does not explicitly fail is assumed to pass.
210 echo "passed" >>${Results_File}
215 # atf_require_prog prog
217 # Checks that the given program name (either provided as an absolute
218 # path or as a plain file name) can be found. If it is not available,
219 # automatically skips the test case with an appropriate message.
221 # Relative paths are not allowed because the test case cannot predict
222 # where it will be executed from.
231 atf_skip "The required program ${1} could not be found"
234 _atf_error 128 "atf_require_prog does not accept relative" \
238 _prog=$(_atf_find_in_path "${1}")
239 [ -n "${_prog}" ] || \
240 atf_skip "The required program ${1} could not be found" \
247 # atf_set varname val1 [.. valN]
249 # Sets the test case's variable 'varname' to the specified values
250 # which are concatenated using a single blank space. This function
251 # is supposed to be called form the test case's head only.
256 _atf_error 128 "atf_set called from the test case's body"
258 _var=$(_atf_normalize ${1}); shift
259 eval __tc_var_${Test_Case}_${_var}=\"\${*}\"
263 # atf_skip msg1 [.. msgN]
265 # Skips the test case because of the reason provided. Multiple words
266 # can be given, in which case they are joined by a single blank space.
270 echo "skipped, ${*}" >>${Results_File}
274 # ------------------------------------------------------------------------
276 # ------------------------------------------------------------------------
279 # _atf_config_set varname val1 [.. valN]
281 # Sets the test case's private variable 'varname' to the specified
282 # values which are concatenated using a single blank space.
286 _var=$(_atf_normalize ${1}); shift
287 eval __tc_config_var_${_var}=\"\${*}\"
288 Config_Vars="${Config_Vars} __tc_config_var_${_var}"
292 # _atf_config_set_str varname=val
294 # Sets the test case's private variable 'varname' to the specified
295 # value. The parameter is of the form 'varname=val'.
297 _atf_config_set_from_str()
306 _atf_config_set "${_var}" "${_val}"
310 # _atf_echo [-l indent] [-t tag] [msg1 [.. msgN]]
312 # Prints a formatted message using atf-format(1). See its manual
313 # page for details on the syntax of this function.
321 # _atf_ensure_boolean var
323 # Ensures that the test case defined the variable 'var' to a boolean
326 _atf_ensure_boolean()
328 _atf_ensure_not_empty ${1}
330 case $(atf_get ${1}) in
331 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee])
334 [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee])
338 _atf_error 128 "Invalid value for boolean variable \`${1}'"
344 # _atf_ensure_integral var
346 # Ensures that the test case defined the variable 'var' to an integral
349 _atf_ensure_integral()
351 _atf_ensure_not_empty ${1}
353 case $(atf_get ${1}) in
357 _atf_error 128 "Invalid value for integral variable \`${1}'"
363 # _atf_ensure_not_empty var
365 # Ensures that the test case defined the variable 'var' to a non-empty
368 _atf_ensure_not_empty()
370 [ -n "$(atf_get ${1})" ] || \
371 _atf_error 128 "Undefined or empty variable \`${1}'"
375 # _atf_error error_code [msg1 [.. msgN]]
377 # Prints the given error message (which can be composed of multiple
378 # arguments, in which case are joined by a single space) and exits
379 # with the specified error code.
381 # This must not be used by test programs themselves (hence making
382 # the function private) to indicate a test case's failure. They
383 # have to use the atf_fail function.
387 _error_code="${1}"; shift
389 _atf_echo -r -t "${Prog_Name}: " "ERROR:" "$@" 1>&2
394 # _atf_expand_glob glob
396 # Prints all test case identifiers that match the provided glob
404 while [ ${#} -gt 0 ]; do
406 ${_glob}) _matched="${_matched} ${1}" ;;
414 while [ ${#} -gt 0 ]; do
421 # _atf_get_bool varname
423 # Evaluates a test case-specific variable as a boolean and returns its
432 # _atf_init_output [tc1 .. tcN]
434 # Initializes the descriptor where we will send the test case's
439 echo "Content-Type: application/X-atf-tcs; version=\"1\"" \
441 echo "" >&${Results_Fd}
442 echo "tcs-count: ${#}" >&${Results_Fd}
446 # _atf_internal_get varname
448 # Prints the value of a test case-specific internal variable. Given
449 # that one should not get the value of non-existent variables, it is
450 # fine to always use this function as 'val=$(_atf_internal_get var)'.
454 eval echo \${__tc_internal_var_${Test_Case}_${1}}
458 # _atf_internal_set varname val1 [.. valN]
460 # Sets the test case's private variable 'varname' to the specified
461 # values which are concatenated using a single blank space.
466 eval __tc_internal_var_${Test_Case}_${_var}=\"\${*}\"
470 # _atf_list_tcs [tc1 .. tcN]
472 # Describes all given test cases and prints the list to the standard
477 # Calculate the length of the longest test case name. Needed for
478 # correct indentation later on.
481 if [ ${#_tc} -gt ${_maxlen} ]; then
486 # Print the list of test cases.
487 _maxlen=$((${_maxlen} + 4))
489 _atf_parse_head ${_tc}
490 _atf_echo -t ${_tc} -l ${_maxlen} $(atf_get descr)
497 # Normalizes a string so that it is a valid shell variable name.
505 # _atf_parse_head tcname
507 # Evaluates a test case's head to gather its variables and prepares the
508 # test program to run it.
512 ${Parsing_Head} && _atf_error 128 "_atf_parse_head called recursively"
520 _atf_ensure_not_empty descr
521 _atf_ensure_not_empty ident
522 _atf_ensure_integral timeout
523 test $(atf_get ident) = "${1}" || \
524 _atf_error 128 "Test case redefined ident"
530 # _atf_parse_props tc
532 # Runs a test case's body but, before that, handles the default
533 # properties. This function must be run in a subshell because the
534 # test case is designed to abruptly exit the shell when any of
535 # atf_pass, atf_skip or atf_fail are executed.
553 _arches=$(atf_get require.arch)
554 if [ -n "${_arches}" ]; then
556 for _a in ${_arches}; do
557 if [ ${_a} = ${Atf_Arch} ]; then
562 [ ${found} = yes ] || \
563 atf_skip "Requires one of the '${_arches}' architectures"
566 _machines=$(atf_get require.machine)
567 if [ -n "${_machines}" ]; then
569 for _m in ${_machines}; do
570 if [ ${_m} = ${Atf_Machine} ]; then
575 [ ${found} = yes ] || \
576 atf_skip "Requires one of the '${_machines}' machine types"
579 _vars="$(atf_get require.config)"
580 if [ -n "${_vars}" ]; then
581 for _v in ${_vars}; do
582 if ! atf_config_has ${_v}; then
583 atf_skip "Required configuration variable ${_v} not defined"
588 _progs="$(atf_get require.progs)"
589 if [ -n "${_progs}" ]; then
590 for _p in ${_progs}; do
591 atf_require_prog ${_p}
595 case $(atf_get require.user) in
597 [ $(id -u) -eq 0 ] || \
598 atf_skip "Requires root privileges"
601 [ $(id -u) -ne 0 ] || \
602 atf_skip "Requires an unprivileged user"
607 atf_fail "Invalid value in the require.user property"
611 # Previous versions of this code reverted all signal handlers to their
612 # default behavior at this point. We do not need to do this any more
613 # because this piece of code is run in a clean sub-shell (through the
614 # _atf_shortcut_exec call), i.e. a completely re-executed shell, and
615 # we have not messed with signal handlers at all until this point.
621 # _atf_run_tc tc nrest
623 # Runs the specified test case. Prints its exit status to the
624 # standard output and returns a boolean indicating if the test was
625 # successful or not. The 'nrest' parameter indicates how many test
626 # cases are left for execution.
632 # Block some signals while we mess with temporary files so that we can
633 # clean them up later on.
635 trap _atf_sighup_handler SIGHUP
636 trap _atf_sigint_handler SIGINT
637 trap _atf_sigterm_handler SIGTERM
639 echo "tc-start: ${Test_Case}" >&${Results_Fd}
641 _atf_internal_set srcdir "${Source_Dir}"
643 Results_File=$(mktemp ${Work_Dir}/atf.XXXXXX)
645 _workdir=$(mktemp -d ${Work_Dir}/atf.XXXXXX)
646 if [ ${?} -eq 0 ]; then
647 _atf_shortcut_exec ${1} ${_workdir}
649 if [ -f ${_workdir}/atf.timed.out ]; then
650 ( atf_fail "Test case timed out after $(atf_get timeout) seconds" )
653 ( cd ${_workdir} ; ${1}_cleanup )
654 if [ ${2} -gt 1 ]; then
655 echo __atf_tc_separator__
656 echo __atf_tc_separator__ 1>&2
658 ${Atf_Cleanup} ${_workdir}
660 ( atf_fail "Could not create the work directory" )
664 # Set a default exit status if the test case did not report any.
665 if [ -z "$(cat ${Results_File})" ]; then
666 if [ -n "${Held_Signals}" ]; then
667 ( atf_fail "Test case was interrupted by${Held_Signals}" )
669 ( atf_fail "Test case did not report any status; bogus test" )
673 # Print the result of the test case and clean up the temporary file.
674 echo "tc-end: ${Test_Case}, $(cat ${Results_File})" >&${Results_Fd}
675 rm -f ${Results_File}
680 # Restore blocked signals and process them.
681 trap - SIGHUP SIGINT SIGTERM
682 for s in ${Held_Signals}; do
690 # _atf_run_tcs [tc1 .. tcN]
692 # Executes all the given test cases. Returns 0 if all tests were
693 # successful, or 1 otherwise.
697 # Now check that the base work directory exists. We do not want to
698 # bother creating it.
699 [ -d "${Work_Dir}" ] || \
700 _atf_error 1 "Cannot find the work directory \`${Work_Dir}'"
702 _atf_init_output "${@}"
705 while [ ${#} -gt 0 ]; do
706 _atf_run_tc ${1} ${#} || _ok=false
714 # _atf_shortcut_entry
716 # Secondary entry point for the program. This is only called internally
717 # to process a test case's body. We must do a full re-exec of the script
718 # in order to change its process group by means of an external tool.
719 # Yes, this is ugly, but there is no other way to do it -- unless we
720 # modified the shell interpreter to provide a built-in for changing the
721 # process group of the current process...
723 # Keep in sync with _atf_shortcut_exec.
725 _atf_shortcut_entry()
727 # Set global program status.
728 _config_file=${_ATF_CONFIG_FILE}; unset _ATF_CONFIG_FILE
729 Results_Fd=${_ATF_RESULTS_FD}; unset _ATF_RESULTS_FD
730 Results_File=${_ATF_RESULTS_FILE}; unset _ATF_RESULTS_FILE
731 Source_Dir=${_ATF_SOURCE_DIR}; unset _ATF_SOURCE_DIR
732 Work_Dir=${_ATF_WORK_DIR}; unset _ATF_WORK_DIR
734 # Gather specific details of this re-exec.
735 _shortcut_tc=${_ATF_SHORTCUT}; unset _ATF_SHORTCUT
737 # Global initialization, as found in main.
738 if [ -n "${_config_file}" -a -f "${_config_file}" ]; then
742 _atf_internal_set srcdir "${Source_Dir}"
745 # Test-case specific initialization, as found in _atf_run_tc.
746 _atf_parse_head ${_shortcut_tc}
747 _atf_internal_set srcdir "${Source_Dir}"
749 # Really run the test case's body. This is the only part that
750 # should remain if we were really able to change the process group
753 _atf_run_body ${_shortcut_tc}
758 # _atf_shortcut_exec tc
760 # Re-executes the current script in a different process group in order
761 # to process the given test case's body.
763 # Keep in sync with _atf_shortcut_entry.
767 # Save the value of the configuration variables set through -v.
768 # We must do this through a files as there is no other easy way to
769 # preserve spaces in them. But this can bring raise problems...
770 if [ -n "${Config_Vars}" ]; then
771 _config_file=${Work_Dir}/atf.config.vars
772 for _var in ${Config_Vars}; do
773 _val=$(eval echo \${${_var}})
774 echo ${_var}=\'${_val}\' >>${_config_file}
778 # Now do the real re-execution.
779 ${Atf_Exec} -t $(atf_get timeout):${2}/atf.timed.out env \
780 _ATF_CONFIG_FILE=${_config_file} \
781 _ATF_RESULTS_FD=${Results_Fd} \
782 _ATF_RESULTS_FILE=${Results_File} \
784 _ATF_SOURCE_DIR=${Source_Dir} \
786 ${Source_Dir}/${Prog_Name}
790 # _atf_sighup_handler
792 # Handler for the SIGHUP signal that registers its occurrence so that
793 # it can be processed at a later stage.
795 _atf_sighup_handler()
797 Held_Signals="${Held_Signals} SIGHUP"
801 # _atf_sigint_handler
803 # Handler for the SIGINT signal that registers its occurrence so that
804 # it can be processed at a later stage.
806 _atf_sigint_handler()
808 Held_Signals="${Held_Signals} SIGINT"
812 # _atf_sigterm_handler
814 # Handler for the SIGTERM signal that registers its occurrence so that
815 # it can be processed at a later stage.
817 _atf_sigterm_handler()
819 Held_Signals="${Held_Signals} SIGTERM"
823 # _atf_syntax_error msg1 [.. msgN]
825 # Formats and prints a syntax error message and terminates the
826 # program prematurely.
830 _atf_echo -r -t "${Prog_Name}: " "ERROR: ${@}" 1>&2
831 _atf_echo -r -t "${Prog_Name}: " "Type \`${Prog_Name} -h' for more" \
837 # _atf_is_tc_defined tc-name
839 # Returns a boolean indicating if the given test case was defined by the
840 # test program or not.
844 for _tc in ${Defined_Test_Cases}; do
845 [ ${_tc} = ${1} ] && return 0
853 # Prints usage information and exits the program.
857 _atf_echo -t "Usage: " "${Prog_Name} [options] [test_case1" \
860 _atf_echo "This is an independent atf test program."
862 _atf_echo "Available options:"
863 _atf_echo -t " -h " "Shows this help message"
864 _atf_echo -t " -l " "List test cases and their purpose"
865 _atf_echo -t " -r fd " "The file descriptor to which the" \
866 "test program will send the results" \
868 _atf_echo -t " -s srcdir " "Directory where the test's data" \
870 _atf_echo -t " -v var=value " "Sets the configuration variable" \
872 _atf_echo -t " -w workdir " "Directory where the test's" \
873 "temporary files are located"
875 _atf_echo "For more details please see atf-test-program(1) and atf(7)."
879 # _atf_warning [msg1 [.. msgN]]
881 # Prints the given warning message (which can be composed of multiple
882 # arguments, in which case are joined by a single space).
884 # This must not be used by test programs themselves (hence making
885 # the function private).
889 _atf_echo -r -t "${Prog_Name}: " "WARNING:" "$@" 1>&2
893 # main [options] [test_case1 [.. test_caseN]]
895 # Test program's entry point.
899 # Handle shortcut execution path as early as possible.
900 if [ ${_ATF_SHORTCUT-__unset__} != __unset__ ]; then
905 # The test program's base directory where it will put temporary files.
906 Work_Dir=$(atf-config -t atf_workdir)
908 # Process command-line options first.
912 while getopts :hlr:s:v:w: arg; do
931 _atf_config_set_from_str "${OPTARG}"
939 _atf_syntax_error "Unknown option -${OPTARG}."
944 shift `expr ${OPTIND} - 1`
946 if [ ${_hflag} = true ]; then
947 [ ${_numargs} -eq 1 ] || _atf_syntax_error "-h must be given alone."
953 # First of all, make sure that the source directory is correct. It
954 # doesn't matter if the user did not change it, because the default
955 # value may not work. (TODO: It possibly should, even though it is
956 # not a big deal because atf-run deals with this.)
957 case ${Source_Dir} in
961 Source_Dir=$(pwd)/${Source_Dir}
964 [ -f ${Source_Dir}/${Prog_Name} ] || \
965 _atf_error 1 "Cannot find the test program in the source" \
966 "directory \`${Source_Dir}'"
968 # Set some global variables useful to the user. Not specific to the
969 # test case because they may be needed during initialization too.
970 # XXX I'm not too fond on this though. Sure, it is very useful in some
971 # situations -- such as in NetBSD's fs/tmpfs/* tests where each test
972 # program includes a helper subroutines file -- but there are also
973 # other, maybe better ways to achieve the same. Because, for example,
974 # at the moment it is not possible to detect failures in the inclusion
975 # and report them nicely. Plus this change is difficult to implement
976 # in the current C++ API.
977 _atf_internal_set srcdir "${Source_Dir}"
979 # Call the test program's hook to register all available test cases.
982 # Set _tcs to the test cases to run.
983 if [ ${#} -gt 0 ]; then
984 # Expand glob patterns and report erroneous test cases.
986 while [ ${#} -gt 0 ]; do
987 _matches=$(_atf_expand_glob "${1}")
988 [ ${#_matches} -eq 0 ] &&
989 _atf_error 1 "Unknown test case \`${1}'"
991 _tcs="${_tcs} ${_matches}"
998 # Run or list test cases, restricting them to _tcs.
1000 _atf_list_tcs ${_tcs}
1002 _atf_run_tcs ${_tcs}
1006 # vim: syntax=sh:expandtab:shiftwidth=4:softtabstop=4