NetBSD-5.0.2/dist/atf/atf-sh/atf.footer.subr
#
# Automated Testing Framework (atf)
#
# Copyright (c) 2007, 2008 The NetBSD Foundation, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
# CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
#
# File: atf.footer.subr
#
# This file provides the test program's entry point and auxiliary
# functions used during testing.
#
# ------------------------------------------------------------------------
# GLOBAL VARIABLES
# ------------------------------------------------------------------------
# Values of configuration variables obtained from atf-config.
Atf_Arch=$(atf-config -t atf_arch)
Atf_Cleanup=$(atf-config -t atf_libexecdir)/atf-cleanup
Atf_Exec=$(atf-config -t atf_libexecdir)/atf-exec
Atf_Format=$(atf-config -t atf_libexecdir)/atf-format
Atf_Killpg=$(atf-config -t atf_libexecdir)/atf-killpg
Atf_Machine=$(atf-config -t atf_machine)
# List of configuration variables set through the command line. Needed
# during shortcut execution.
Config_Vars=
# List of blocked signals, to be processed when unblocked.
Held_Signals=
# A boolean variable that indicates whether we are parsing a test case's
# head or not.
Parsing_Head=false
# The file descriptor on which the test program will print the results of
# the test cases.
Results_Fd=1
# The file to which the test case will print its result.
Results_File=
# The test program's source directory: i.e. where its auxiliary data files
# and helper utilities can be found. Defaults to the current directory
# but can be overriden through the '-s' flag.
Source_Dir=.
# Indicates the test case we are currently processing.
Test_Case=
# The list of all test cases provided by the test program.
# Subset of ${Defined_Test_Cases}.
Test_Cases=
# The test case's work directory. The semantics of this variable are a
# bit screwed. When running the parent program (not the test case's body),
# this points to the test _program_'s directory. When we have reexecuted
# the code through _atf_shortcut_exec, this points to the test _case_'s
# work directory, which is a subdirectory of the other one.
Work_Dir=
# ------------------------------------------------------------------------
# PUBLIC INTERFACE
# ------------------------------------------------------------------------
#
# atf_add_test_case tc-name
#
# Adds the given test case to the list of test cases that form the test
# program. The name provided here must be accompanied by two functions
# named after it: <tc-name>_head and <tc-name>_body, and optionally by
# a <tc-name>_cleanup function.
#
atf_add_test_case()
{
_atf_is_tc_defined "${1}" || \
_atf_error 128 "Test case ${1} was not correctly defined by" \
"this test program"
Test_Cases="${Test_Cases} ${1}"
}
#
# atf_check cmd expcode expout experr
#
# Executes a command and checks its error code, stdout and stderr against
# the expected values for each.
#
# 'expcode' specifies the numeric error code the program is supposed to
# return.
#
# 'expout' is one of 'expout', 'ignore', 'null' or 'stdout'. The meaning
# of these parameters is as follows:
# expout - What the command writes to the stdout channel must match
# exactly what is found in the 'expout' file.
# ignore - The test does not check what the command writes to the
# stdout channel.
# null - The command must not write anything to the stdout channel.
# stdout - What the command writes to the stdout channel is written
# to a 'stdout' file, available for further inspection.
#
# 'experr' is one of 'experr', 'ignore', 'null' or 'stderr'. The meaning
# of these parameters is the same as their corresponding ones in the
# stdout case.
#
atf_check()
{
test ${#} -eq 4 || atf_fail "Incorrect number of parameters"
_cmd="${1}"
_expcode="${2}"
_expout="${3}"
_experr="${4}"
echo "Checking command [${_cmd}]"
# Sanity-check the expout parameter and prepare the work directory for
# the test.
case ${_expout} in
expout)
test -f ${Work_Dir}/expout || atf_fail "No expout file found"
;;
ignore)
;;
null)
;;
stdout)
;;
*)
atf_fail "Invalid value in atf_check's expout parameter"
;;
esac
# Sanity-check the experr parameter and prepare the work directory for
# the test.
case ${_experr} in
experr)
test -f ${Work_Dir}/experr || atf_fail "No experr file found"
;;
ignore)
;;
null)
;;
stderr)
;;
*)
atf_fail "Invalid value in atf_check's experr parameter"
;;
esac
# Run the command and capture its error code, output and error
# channels.
( eval ${_cmd} >${Work_Dir}/aux-stdout 2>${Work_Dir}/aux-stderr )
_code=${?}
# Check the command's error code.
if [ ${_code} -ne ${_expcode} ]; then
echo "stdout:"
cat aux-stdout
echo "stderr:"
cat aux-stderr
atf_fail "Exit code ${_code} does not match the expected" \
"code ${_expcode}"
fi
# Check what the command wrote to stdout.
case ${_expout} in
expout)
if cmp -s ${Work_Dir}/expout ${Work_Dir}/aux-stdout; then
:
else
echo "stdout:"
diff -u ${Work_Dir}/expout ${Work_Dir}/aux-stdout
atf_fail "stdout does not match"
fi
;;
ignore)
;;
null)
touch ${Work_Dir}/empty-file
if cmp -s ${Work_Dir}/empty-file ${Work_Dir}/aux-stdout; then
:
else
echo "stdout:"
cat ${Work_Dir}/aux-stdout
atf_fail "stdout was not silent"
fi
;;
stdout)
test -f ${Work_Dir}/stdout && rm -f ${Work_Dir}/stdout
test -f ${Work_Dir}/stdout && \
atf_fail "Could not delete stale stdout file"
mv ${Work_Dir}/aux-stdout ${Work_Dir}/stdout
;;
*)
_atf_error 128 "Internal error in the atf_check function"
;;
esac
# Check what the command wrote to stderr.
case ${_experr} in
experr)
if cmp -s ${Work_Dir}/experr ${Work_Dir}/aux-stderr; then
:
else
echo "stderr:"
diff -u ${Work_Dir}/experr ${Work_Dir}/aux-stderr
atf_fail "stderr does not match"
fi
;;
ignore)
;;
null)
touch ${Work_Dir}/empty-file
if cmp -s ${Work_Dir}/empty-file ${Work_Dir}/aux-stderr; then
:
else
echo "stderr:"
cat ${Work_Dir}/aux-stderr
atf_fail "stderr was not silent"
fi
;;
stderr)
test -f ${Work_Dir}/stderr && rm -f ${Work_Dir}/stderr
test -f ${Work_Dir}/stderr && \
atf_fail "Could not delete stale stderr file"
mv ${Work_Dir}/aux-stderr ${Work_Dir}/stderr
;;
*)
_atf_error 128 "Internal error in the atf_check function"
;;
esac
}
#
# atf_check_equal expr1 expr2
#
# Checks that expr1's value matches expr2's and, if not, raises an
# error. Ideally expr1 and expr2 should be provided quoted (not
# expanded) so that the error message is helpful; otherwise it will
# only show the values, not the expressions themselves.
#
atf_check_equal()
{
eval _val1=\"${1}\"
eval _val2=\"${2}\"
test "${_val1}" = "${_val2}" || \
atf_fail "${1} != ${2} (${_val1} != ${_val2})"
}
#
# atf_config_get varname [defvalue]
#
# Prints the value of a configuration variable. If it is not
# defined, prints the given default value.
#
atf_config_get()
{
_varname="__tc_config_var_$(_atf_normalize ${1})"
if [ ${#} -eq 1 ]; then
eval _value=\"\${${_varname}-__unset__}\"
[ "${_value}" = __unset__ ] && \
_atf_error 1 "Could not find configuration variable \`${1}'"
echo ${_value}
elif [ ${#} -eq 2 ]; then
eval echo \${${_varname}-${2}}
else
_atf_error 1 "Incorrect number of parameters for atf_config_get"
fi
}
#
# atf_config_has varname
#
# Returns a boolean indicating if the given configuration variable is
# defined or not.
#
atf_config_has()
{
_varname="__tc_config_var_$(_atf_normalize ${1})"
eval _value=\"\${${_varname}-__unset__}\"
[ "${_value}" != __unset__ ]
}
#
# atf_fail msg1 [.. msgN]
#
# Makes the test case fail with the given error message. Multiple
# words can be provided, in which case they are joined by a single
# blank space.
#
atf_fail()
{
echo "failed, ${*}" >>${Results_File}
exit 1
}
#
# atf_get varname
#
# Prints the value of a test case-specific variable. Given that one
# should not get the value of non-existent variables, it is fine to
# always use this function as 'val=$(atf_get var)'.
#
atf_get()
{
eval echo \${__tc_var_${Test_Case}_$(_atf_normalize ${1})}
}
#
# atf_get_srcdir
#
# Prints the value of the test case's source directory.
#
atf_get_srcdir()
{
_atf_internal_get srcdir
}
#
# atf_pass msg1 [.. msgN]
#
# Makes the test case pass. Shouldn't be used in general, as a test
# case that does not explicitly fail is assumed to pass.
#
atf_pass()
{
echo "passed" >>${Results_File}
exit 0
}
#
# atf_require_prog prog
#
# Checks that the given program name (either provided as an absolute
# path or as a plain file name) can be found. If it is not available,
# automatically skips the test case with an appropriate message.
#
# Relative paths are not allowed because the test case cannot predict
# where it will be executed from.
#
atf_require_prog()
{
_prog=
case ${1} in
/*)
_prog="${1}"
[ -x ${_prog} ] || \
atf_skip "The required program ${1} could not be found"
;;
*/*)
_atf_error 128 "atf_require_prog does not accept relative" \
"path names \`${1}'"
;;
*)
_prog=$(_atf_find_in_path "${1}")
[ -n "${_prog}" ] || \
atf_skip "The required program ${1} could not be found" \
"in the PATH"
;;
esac
}
#
# atf_set varname val1 [.. valN]
#
# Sets the test case's variable 'varname' to the specified values
# which are concatenated using a single blank space. This function
# is supposed to be called form the test case's head only.
#
atf_set()
{
${Parsing_Head} || \
_atf_error 128 "atf_set called from the test case's body"
_var=$(_atf_normalize ${1}); shift
eval __tc_var_${Test_Case}_${_var}=\"\${*}\"
}
#
# atf_skip msg1 [.. msgN]
#
# Skips the test case because of the reason provided. Multiple words
# can be given, in which case they are joined by a single blank space.
#
atf_skip()
{
echo "skipped, ${*}" >>${Results_File}
exit 0
}
# ------------------------------------------------------------------------
# PRIVATE INTERFACE
# ------------------------------------------------------------------------
#
# _atf_config_set varname val1 [.. valN]
#
# Sets the test case's private variable 'varname' to the specified
# values which are concatenated using a single blank space.
#
_atf_config_set()
{
_var=$(_atf_normalize ${1}); shift
eval __tc_config_var_${_var}=\"\${*}\"
Config_Vars="${Config_Vars} __tc_config_var_${_var}"
}
#
# _atf_config_set_str varname=val
#
# Sets the test case's private variable 'varname' to the specified
# value. The parameter is of the form 'varname=val'.
#
_atf_config_set_from_str()
{
_oldifs=${IFS}
IFS='='
set -- ${*}
_var=${1}
shift
_val="${@}"
IFS=${_oldifs}
_atf_config_set "${_var}" "${_val}"
}
#
# _atf_echo [-l indent] [-t tag] [msg1 [.. msgN]]
#
# Prints a formatted message using atf-format(1). See its manual
# page for details on the syntax of this function.
#
_atf_echo()
{
${Atf_Format} "${@}"
}
#
# _atf_ensure_boolean var
#
# Ensures that the test case defined the variable 'var' to a boolean
# value.
#
_atf_ensure_boolean()
{
_atf_ensure_not_empty ${1}
case $(atf_get ${1}) in
[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee])
atf_set ${1} true
;;
[Nn][Oo]|[Ff][Aa][Ll][Ss][Ee])
atf_set ${1} false
;;
*)
_atf_error 128 "Invalid value for boolean variable \`${1}'"
;;
esac
}
#
# _atf_ensure_integral var
#
# Ensures that the test case defined the variable 'var' to an integral
# value.
#
_atf_ensure_integral()
{
_atf_ensure_not_empty ${1}
case $(atf_get ${1}) in
[0-9]*)
;;
*)
_atf_error 128 "Invalid value for integral variable \`${1}'"
;;
esac
}
#
# _atf_ensure_not_empty var
#
# Ensures that the test case defined the variable 'var' to a non-empty
# value.
#
_atf_ensure_not_empty()
{
[ -n "$(atf_get ${1})" ] || \
_atf_error 128 "Undefined or empty variable \`${1}'"
}
#
# _atf_error error_code [msg1 [.. msgN]]
#
# Prints the given error message (which can be composed of multiple
# arguments, in which case are joined by a single space) and exits
# with the specified error code.
#
# This must not be used by test programs themselves (hence making
# the function private) to indicate a test case's failure. They
# have to use the atf_fail function.
#
_atf_error()
{
_error_code="${1}"; shift
_atf_echo -r -t "${Prog_Name}: " "ERROR:" "$@" 1>&2
exit ${_error_code}
}
#
# _atf_expand_glob glob
#
# Prints all test case identifiers that match the provided glob
# pattern.
#
_atf_expand_glob()
{
_glob="${1}"
_matched=""
set -- ${Test_Cases}
while [ ${#} -gt 0 ]; do
case "${1}" in
${_glob}) _matched="${_matched} ${1}" ;;
*) ;;
esac
shift
done
set -- ${_matched}
while [ ${#} -gt 0 ]; do
echo ${1}
shift
done
}
#
# _atf_get_bool varname
#
# Evaluates a test case-specific variable as a boolean and returns its
# value.
#
_atf_get_bool()
{
eval $(atf_get ${1})
}
#
# _atf_init_output [tc1 .. tcN]
#
# Initializes the descriptor where we will send the test case's
# results.
#
_atf_init_output()
{
echo "Content-Type: application/X-atf-tcs; version=\"1\"" \
>&${Results_Fd}
echo "" >&${Results_Fd}
echo "tcs-count: ${#}" >&${Results_Fd}
}
#
# _atf_internal_get varname
#
# Prints the value of a test case-specific internal variable. Given
# that one should not get the value of non-existent variables, it is
# fine to always use this function as 'val=$(_atf_internal_get var)'.
#
_atf_internal_get()
{
eval echo \${__tc_internal_var_${Test_Case}_${1}}
}
#
# _atf_internal_set varname val1 [.. valN]
#
# Sets the test case's private variable 'varname' to the specified
# values which are concatenated using a single blank space.
#
_atf_internal_set()
{
_var=${1}; shift
eval __tc_internal_var_${Test_Case}_${_var}=\"\${*}\"
}
#
# _atf_list_tcs [tc1 .. tcN]
#
# Describes all given test cases and prints the list to the standard
# output.
#
_atf_list_tcs()
{
# Calculate the length of the longest test case name. Needed for
# correct indentation later on.
_maxlen=0
for _tc in ${*}; do
if [ ${#_tc} -gt ${_maxlen} ]; then
_maxlen=${#_tc}
fi
done
# Print the list of test cases.
_maxlen=$((${_maxlen} + 4))
for _tc in ${*}; do
_atf_parse_head ${_tc}
_atf_echo -t ${_tc} -l ${_maxlen} $(atf_get descr)
done
}
#
# _atf_normalize str
#
# Normalizes a string so that it is a valid shell variable name.
#
_atf_normalize()
{
echo ${1} | tr .- __
}
#
# _atf_parse_head tcname
#
# Evaluates a test case's head to gather its variables and prepares the
# test program to run it.
#
_atf_parse_head()
{
${Parsing_Head} && _atf_error 128 "_atf_parse_head called recursively"
Parsing_Head=true
Test_Case="${1}"
atf_set ident "${1}"
atf_set timeout 300
${1}_head
_atf_ensure_not_empty descr
_atf_ensure_not_empty ident
_atf_ensure_integral timeout
test $(atf_get ident) = "${1}" || \
_atf_error 128 "Test case redefined ident"
Parsing_Head=false
}
#
# _atf_parse_props tc
#
# Runs a test case's body but, before that, handles the default
# properties. This function must be run in a subshell because the
# test case is designed to abruptly exit the shell when any of
# atf_pass, atf_skip or atf_fail are executed.
#
_atf_run_body()
{
HOME=$(pwd)
export HOME
unset LANG
unset LC_ALL
unset LC_COLLATE
unset LC_CTYPE
unset LC_MESSAGES
unset LC_MONETARY
unset LC_NUMERIC
unset LC_TIME
unset TZ
umask 0022
_arches=$(atf_get require.arch)
if [ -n "${_arches}" ]; then
found=no
for _a in ${_arches}; do
if [ ${_a} = ${Atf_Arch} ]; then
found=yes
break
fi
done
[ ${found} = yes ] || \
atf_skip "Requires one of the '${_arches}' architectures"
fi
_machines=$(atf_get require.machine)
if [ -n "${_machines}" ]; then
found=no
for _m in ${_machines}; do
if [ ${_m} = ${Atf_Machine} ]; then
found=yes
break
fi
done
[ ${found} = yes ] || \
atf_skip "Requires one of the '${_machines}' machine types"
fi
_vars="$(atf_get require.config)"
if [ -n "${_vars}" ]; then
for _v in ${_vars}; do
if ! atf_config_has ${_v}; then
atf_skip "Required configuration variable ${_v} not defined"
fi
done
fi
_progs="$(atf_get require.progs)"
if [ -n "${_progs}" ]; then
for _p in ${_progs}; do
atf_require_prog ${_p}
done
fi
case $(atf_get require.user) in
root)
[ $(id -u) -eq 0 ] || \
atf_skip "Requires root privileges"
;;
unprivileged)
[ $(id -u) -ne 0 ] || \
atf_skip "Requires an unprivileged user"
;;
"")
;;
*)
atf_fail "Invalid value in the require.user property"
;;
esac
# Previous versions of this code reverted all signal handlers to their
# default behavior at this point. We do not need to do this any more
# because this piece of code is run in a clean sub-shell (through the
# _atf_shortcut_exec call), i.e. a completely re-executed shell, and
# we have not messed with signal handlers at all until this point.
${1}_body
}
#
# _atf_program_timeout pid workdir
#
# Prepares and launches in the background a script that will kill the
# current test case's body (specified by the pid) after it times out.
#
_atf_program_timeout()
{
_timeout=$(atf_get timeout)
if [ ${_timeout} -gt 0 ]; then
cat >${2}/timeout.sh <<EOF
#! /bin/sh
sleep ${_timeout}
touch ${2}/atf.timed.out
${Atf_Killpg} ${1} >/dev/null 2>&1
EOF
${Atf_Exec} -g /bin/sh ${2}/timeout.sh >/dev/null 2>&1 &
echo $!
else
echo none
fi
}
#
# _atf_run_tc tc nrest
#
# Runs the specified test case. Prints its exit status to the
# standard output and returns a boolean indicating if the test was
# successful or not. The 'nrest' parameter indicates how many test
# cases are left for execution.
#
_atf_run_tc()
{
_atf_parse_head ${1}
# Block some signals while we mess with temporary files so that we can
# clean them up later on.
Held_Signals=
trap _atf_sighup_handler SIGHUP
trap _atf_sigint_handler SIGINT
trap _atf_sigterm_handler SIGTERM
echo "tc-start: ${Test_Case}" >&${Results_Fd}
_atf_internal_set srcdir "${Source_Dir}"
Results_File=$(mktemp ${Work_Dir}/atf.XXXXXX)
_workdir=$(mktemp -d ${Work_Dir}/atf.XXXXXX)
if [ ${?} -eq 0 ]; then
_atf_shortcut_exec ${1} ${_workdir}; _body_pid=$!
_timeout_pid=$(_atf_program_timeout ${_body_pid} ${_workdir})
wait ${_body_pid}
_ret=$?
if [ -f ${_workdir}/atf.timed.out ]; then
[ ${_timeout_pid} != none ] || \
_atf_error 128 "Inconsistent status in timeout handling"
( atf_fail "Test case timed out after $(atf_get timeout) seconds" )
_ret=${?}
else
[ ${_timeout_pid} != none ] && \
${Atf_Killpg} ${_timeout_pid} >/dev/null 2>&1
fi
( cd ${_workdir} ; ${1}_cleanup )
if [ ${2} -gt 1 ]; then
echo __atf_tc_separator__
echo __atf_tc_separator__ 1>&2
fi
${Atf_Cleanup} ${_workdir}
else
( atf_fail "Could not create the work directory" )
_ret=${?}
fi
# Set a default exit status if the test case did not report any.
if [ -z "$(cat ${Results_File})" ]; then
if [ -n "${Held_Signals}" ]; then
( atf_fail "Test case was interrupted by${Held_Signals}" )
else
( atf_fail "Test case did not report any status; bogus test" )
fi
fi
# Print the result of the test case and clean up the temporary file.
echo "tc-end: ${Test_Case}, $(cat ${Results_File})" >&${Results_Fd}
rm -f ${Results_File}
Results_File=
Test_Case=
# Restore blocked signals and process them.
trap - SIGHUP SIGINT SIGTERM
for s in ${Held_Signals}; do
kill -s ${s} $$
done
return ${_ret}
}
#
# _atf_run_tcs [tc1 .. tcN]
#
# Executes all the given test cases. Returns 0 if all tests were
# successful, or 1 otherwise.
#
_atf_run_tcs()
{
# Now check that the base work directory exists. We do not want to
# bother creating it.
[ -d "${Work_Dir}" ] || \
_atf_error 1 "Cannot find the work directory \`${Work_Dir}'"
_atf_init_output "${@}"
_ok=true
while [ ${#} -gt 0 ]; do
_atf_run_tc ${1} ${#} || _ok=false
shift
done
${_ok}
}
#
# _atf_shortcut_entry
#
# Secondary entry point for the program. This is only called internally
# to process a test case's body. We must do a full re-exec of the script
# in order to change its process group by means of an external tool.
# Yes, this is ugly, but there is no other way to do it -- unless we
# modified the shell interpreter to provide a built-in for changing the
# process group of the current process...
#
# Keep in sync with _atf_shortcut_exec.
#
_atf_shortcut_entry()
{
# Set global program status.
_config_file=${_ATF_CONFIG_FILE}; unset _ATF_CONFIG_FILE
Results_Fd=${_ATF_RESULTS_FD}; unset _ATF_RESULTS_FD
Results_File=${_ATF_RESULTS_FILE}; unset _ATF_RESULTS_FILE
Source_Dir=${_ATF_SOURCE_DIR}; unset _ATF_SOURCE_DIR
Work_Dir=${_ATF_WORK_DIR}; unset _ATF_WORK_DIR
# Gather specific details of this re-exec.
_shortcut_tc=${_ATF_SHORTCUT}; unset _ATF_SHORTCUT
# Global initialization, as found in main.
if [ -n "${_config_file}" -a -f "${_config_file}" ]; then
. ${_config_file}
rm ${_config_file}
fi
_atf_internal_set srcdir "${Source_Dir}"
atf_init_test_cases
# Test-case specific initialization, as found in _atf_run_tc.
_atf_parse_head ${_shortcut_tc}
_atf_internal_set srcdir "${Source_Dir}"
# Really run the test case's body. This is the only part that
# should remain if we were really able to change the process group
# of a sub-shell.
cd ${Work_Dir}
_atf_run_body ${_shortcut_tc}
atf_pass
}
#
# _atf_shortcut_exec tc
#
# Re-executes the current script in a different process group in order
# to process the given test case's body. $! is set to the child process
# on return.
#
# Keep in sync with _atf_shortcut_entry.
#
_atf_shortcut_exec()
{
# Save the value of the configuration variables set through -v.
# We must do this through a files as there is no other easy way to
# preserve spaces in them. But this can bring raise problems...
if [ -n "${Config_Vars}" ]; then
_config_file=${Work_Dir}/atf.config.vars
for _var in ${Config_Vars}; do
_val=$(eval echo \${${_var}})
echo ${_var}=\'${_val}\' >>${_config_file}
done
fi
# Now do the real re-execution.
${Atf_Exec} -g env \
_ATF_CONFIG_FILE=${_config_file} \
_ATF_RESULTS_FD=${Results_Fd} \
_ATF_RESULTS_FILE=${Results_File} \
_ATF_SHORTCUT=${1} \
_ATF_SOURCE_DIR=${Source_Dir} \
_ATF_WORK_DIR=${2} \
${Source_Dir}/${Prog_Name} &
}
#
# _atf_sighup_handler
#
# Handler for the SIGHUP signal that registers its occurrence so that
# it can be processed at a later stage.
#
_atf_sighup_handler()
{
Held_Signals="${Held_Signals} SIGHUP"
}
#
# _atf_sigint_handler
#
# Handler for the SIGINT signal that registers its occurrence so that
# it can be processed at a later stage.
#
_atf_sigint_handler()
{
Held_Signals="${Held_Signals} SIGINT"
}
#
# _atf_sigterm_handler
#
# Handler for the SIGTERM signal that registers its occurrence so that
# it can be processed at a later stage.
#
_atf_sigterm_handler()
{
Held_Signals="${Held_Signals} SIGTERM"
}
#
# _atf_syntax_error msg1 [.. msgN]
#
# Formats and prints a syntax error message and terminates the
# program prematurely.
#
_atf_syntax_error()
{
_atf_echo -r -t "${Prog_Name}: " "ERROR: ${@}" 1>&2
_atf_echo -r -t "${Prog_Name}: " "Type \`${Prog_Name} -h' for more" \
"details." 1>&2
exit 1
}
#
# _atf_is_tc_defined tc-name
#
# Returns a boolean indicating if the given test case was defined by the
# test program or not.
#
_atf_is_tc_defined()
{
for _tc in ${Defined_Test_Cases}; do
[ ${_tc} = ${1} ] && return 0
done
return 1
}
#
# _atf_usage
#
# Prints usage information and exits the program.
#
_atf_usage()
{
_atf_echo -t "Usage: " "${Prog_Name} [options] [test_case1" \
"[.. test_caseN]]"
echo
_atf_echo "This is an independent atf test program."
echo
_atf_echo "Available options:"
_atf_echo -t " -h " "Shows this help message"
_atf_echo -t " -l " "List test cases and their purpose"
_atf_echo -t " -r fd " "The file descriptor to which the" \
"test program will send the results" \
"of the test cases"
_atf_echo -t " -s srcdir " "Directory where the test's data" \
"files are located"
_atf_echo -t " -v var=value " "Sets the configuration variable" \
"\`var' to \`value'"
_atf_echo -t " -w workdir " "Directory where the test's" \
"temporary files are located"
echo
_atf_echo "For more details please see atf-test-program(1) and atf(7)."
}
#
# _atf_warning [msg1 [.. msgN]]
#
# Prints the given warning message (which can be composed of multiple
# arguments, in which case are joined by a single space).
#
# This must not be used by test programs themselves (hence making
# the function private).
#
_atf_warning()
{
_atf_echo -r -t "${Prog_Name}: " "WARNING:" "$@" 1>&2
}
#
# main [options] [test_case1 [.. test_caseN]]
#
# Test program's entry point.
#
main()
{
# Handle shortcut execution path as early as possible.
if [ ${_ATF_SHORTCUT-__unset__} != __unset__ ]; then
_atf_shortcut_entry
# NOTREACHED
fi
# The test program's base directory where it will put temporary files.
Work_Dir=$(atf-config -t atf_workdir)
# Process command-line options first.
_numargs=${#}
_hflag=false
_lflag=false
while getopts :hlr:s:v:w: arg; do
case ${arg} in
h)
_hflag=true
;;
l)
_lflag=true
;;
r)
Results_Fd=${OPTARG}
;;
s)
Source_Dir=${OPTARG}
;;
v)
_atf_config_set_from_str "${OPTARG}"
;;
w)
Work_Dir=${OPTARG}
;;
\?)
_atf_syntax_error "Unknown option -${OPTARG}."
# NOTREACHED
;;
esac
done
shift `expr ${OPTIND} - 1`
if [ ${_hflag} = true ]; then
[ ${_numargs} -eq 1 ] || _atf_syntax_error "-h must be given alone."
_atf_usage
true
return
fi
# First of all, make sure that the source directory is correct. It
# doesn't matter if the user did not change it, because the default
# value may not work. (TODO: It possibly should, even though it is
# not a big deal because atf-run deals with this.)
case ${Source_Dir} in
/*)
;;
*)
Source_Dir=$(pwd)/${Source_Dir}
;;
esac
[ -f ${Source_Dir}/${Prog_Name} ] || \
_atf_error 1 "Cannot find the test program in the source" \
"directory \`${Source_Dir}'"
# Set some global variables useful to the user. Not specific to the
# test case because they may be needed during initialization too.
# XXX I'm not too fond on this though. Sure, it is very useful in some
# situations -- such as in NetBSD's fs/tmpfs/* tests where each test
# program includes a helper subroutines file -- but there are also
# other, maybe better ways to achieve the same. Because, for example,
# at the moment it is not possible to detect failures in the inclusion
# and report them nicely. Plus this change is difficult to implement
# in the current C++ API.
_atf_internal_set srcdir "${Source_Dir}"
# Call the test program's hook to register all available test cases.
atf_init_test_cases
# Set _tcs to the test cases to run.
if [ ${#} -gt 0 ]; then
# Expand glob patterns and report erroneous test cases.
_tcs=
while [ ${#} -gt 0 ]; do
_matches=$(_atf_expand_glob "${1}")
[ ${#_matches} -eq 0 ] &&
_atf_error 1 "Unknown test case \`${1}'"
_tcs="${_tcs} ${_matches}"
shift
done
else
_tcs=${Test_Cases}
fi
# Run or list test cases, restricting them to _tcs.
if `${_lflag}`; then
_atf_list_tcs ${_tcs}
else
_atf_run_tcs ${_tcs}
fi
}
# vim: syntax=sh:expandtab:shiftwidth=4:softtabstop=4