mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
1463 lines
34 KiB
Bash
1463 lines
34 KiB
Bash
########################################################################
|
|
# #
|
|
# This software is part of the ast package #
|
|
# Copyright (c) 1994-2012 AT&T Intellectual Property #
|
|
# and is licensed under the #
|
|
# Eclipse Public License, Version 1.0 #
|
|
# by AT&T Intellectual Property #
|
|
# #
|
|
# A copy of the License is available at #
|
|
# http://www.eclipse.org/org/documents/epl-v10.html #
|
|
# (with md5 checksum b35adb5213ca9657e911e9befb180842) #
|
|
# #
|
|
# Information and Software Systems Research #
|
|
# AT&T Research #
|
|
# Florham Park NJ #
|
|
# #
|
|
# Glenn Fowler <gsf@research.att.com> #
|
|
# #
|
|
########################################################################
|
|
: regress - run regression tests in command.tst
|
|
|
|
command=regress
|
|
case $(getopts '[-][123:xyz]' opt --xyz 2>/dev/null; echo 0$opt) in
|
|
0123) USAGE=$'
|
|
[-?
|
|
@(#)$Id: regress (AT&T Research) 2012-02-02 $
|
|
]
|
|
'$USAGE_LICENSE$'
|
|
[+NAME?regress - run regression tests]
|
|
[+DESCRIPTION?\bregress\b runs the tests in \aunit\a, or
|
|
\aunit\a\b.tst\b if \aunit\a does not exist. If \acommand\a is omitted
|
|
then it is assumed to be the base name of \aunit\a. All testing is done
|
|
in the temporary directory \aunit\a\b.tmp\b.]
|
|
[+?Default test output lists the \anumber\a and \adescription\a for
|
|
each active \bTEST\b group and the \anumber\a:\aline\a for each
|
|
individual \bEXEC\b test. Each test that fails results in a diagnostic
|
|
that contains the word \bFAILED\b; no other diagnostics contain this
|
|
word.]
|
|
[b:ignore-space?Ignore space differences when comparing expected
|
|
output.]
|
|
[i:pipe-input?Repeat each test with the standard input redirected through a
|
|
pipe.]
|
|
[k:keep?Enable \bcore\b dumps, exit after the first test that fails,
|
|
and do not remove the temporary directory \aunit\a\b.tmp\b.]
|
|
[l:local-fs?Force \aunit\a\b.tmp\b to be in a local filesystem.]
|
|
[o:pipe-output?Repeat each test with the standard output redirected through
|
|
a pipe.]
|
|
[p:pipe-io?Repeat each test with the standard input and standard output
|
|
redirected through pipes.]
|
|
[q:quiet?Output information on \bFAILED\b tests only.]
|
|
[r!:regular?Run each test with the standard input and standard output
|
|
redirected through regular files.]
|
|
[t:test?Run only tests matching \apattern\a. Tests are numbered and
|
|
consist of at least two digits (0 filled if necessary.) Tests matching
|
|
\b+(0)\b are always run.]:[pattern]
|
|
[x:trace?Enable debug tracing.]
|
|
[v:verbose?List differences between actual (<) and expected (>) output,
|
|
errors and exit codes. Also disable long output line truncation.]
|
|
|
|
unit [ command [ arg ... ] ]
|
|
|
|
[+INPUT FILES?The regression test file \aunit\a\b.tst\b is a \bksh\b(1)
|
|
script that is executed in an environment with the following functions
|
|
defined:]
|
|
{
|
|
[+BODY \b{ ... }?Defines the test body; used for complex tests.]
|
|
[+CD \b\adirectory\a?Create and change to working directory for
|
|
one test.]
|
|
[+CLEANUP \b\astatus\a?Called at exit time to remove the
|
|
temporary directory \aunit\a\b.tmp\b, list the tests totals via
|
|
\bTALLY\b, and exit with status \astatus\a.]
|
|
[+COMMAND \b\aarg\a ...?Runs the current command under test with
|
|
\aarg\a ... appended to the default args.]
|
|
[+CONTINUE?The background job must be running.]
|
|
[+COPY \b\afrom to\a?Copy file \afrom\a to \ato\a. \afrom\a may
|
|
be a regular file or \bINPUT\b, \bOUTPUT\b or \bERROR\b. Post
|
|
test comparisons are still done for \afrom\a.]
|
|
[+DIAGNOSTICS \b[ \b1\b | \b0\b | \apattern\a ]]?No argument or an
|
|
argument of \b1\b declares that diagnostics are to expected for
|
|
the remainder of the current \bTEST\b; \b0\b reverts to the default
|
|
state that diagnostics are not expected; otherwise the argument
|
|
is a \bksh\b(1) pattern that must match the non-empty contents
|
|
of the standard error.]
|
|
[+DO \b\astatement\a?Defines additional statements to be executed
|
|
for the current test. \astatement\a may be a { ... } group.]
|
|
[+EMPTY \bINPUT|OUTPUT|ERROR|SAME?The corresponding file is
|
|
expected to be empty.]
|
|
[+ERROR \b[ \b-e\b \afilter\a ]] [ \b-n\b ]] \afile\a | - \adata\a ...?The
|
|
standard error is expected to match either the contents
|
|
of \afile\a or the line \adata\a. \bERROR -n\b does not
|
|
append a newline to \adata\a. \afilter\a is a shell command
|
|
or pipeline that reads standard input and writes standard
|
|
output that is applied to ERROR before comparison with the
|
|
expected contents.]
|
|
[+EXEC \b[ \aarg\a ... ]]?Runs the command under test with
|
|
optional arguments. \bINPUT\b, \bOUTPUT\b, \bERROR\b, \bEXIT\b
|
|
and \bSAME\b calls following this \bEXEC\b up until the next
|
|
\bEXEC\b or the end of the script provide details for the
|
|
expected results. If no arguments are specified then the
|
|
arguments from the previous \bEXEC\b in the current \bTEST\b
|
|
group are used, or no arguments if this is the first \bEXEC\b
|
|
in the group.]
|
|
[+EXIT \b\astatus\a?The command exit status is expected to match
|
|
the pattern \astatus\a.]
|
|
[+EXITED?The background job must have exited.]
|
|
[+EXPORT \b[-]] \aname\a=\avalue\a ...?Export environment
|
|
variables for one test.]
|
|
[+FATAL \b\amessage\a ...?\amessage\a is printed on the standard
|
|
error and \bregress\b exits with status \b1\b.]
|
|
[+FIFO \bINPUT|OUTPUT|ERROR\b [ \b-n\b ]] \afile\a | - \adata\a ...?The
|
|
\bIO\B file is a fifo.]
|
|
[+IF \b\acommand\a [\anote\a]]?If the \bsh\b(1) \acommand\a exits
|
|
0 then tests until the next \bELIF\b, \bELSE\b or \bFI\b are
|
|
enabled. Otherwise those tests are skipped. \bIF\b ... \bFI\b
|
|
may be nested, but must not cross \bTEST\b boundaries. \anote\a
|
|
is listed on the standard error if the corresponding test block
|
|
is enabled; \bIF\b, \bELIF\b, \bELSE\b may nave a \anote\a
|
|
operand.]
|
|
[+IGNORE \b\afile\a ...?\afile\a is ignored for subsequent result
|
|
comparisons. \afile\a may be \bOUTPUT\b or \bERROR\b.]
|
|
[+IGNORESPACE?Ignore space differences when comparing expected
|
|
output.]
|
|
[+INCLUDE \b\afile\a ...?One or more \afile\a operands are read
|
|
via the \bksh\b(1) \b.\b(1) command. \bVIEW\b is used to locate
|
|
the files.]
|
|
[+INFO \b\adescription\a?\adescription\a is printed on the
|
|
standard error.]
|
|
[+INITIALIZE?Called by \bregress\b to initialize a each
|
|
\bTEST\b group.]
|
|
[+INPUT \b[ \b-e\b \afilter\a ]] [ \b-n\b ]] \afile\a | - \adata\a ...?The
|
|
standard input is set to either the contents of \afile\a
|
|
or the line \adata\a. \bINPUT -n\b does not append a newline
|
|
to \adata\a. \afilter\a is a shell command or pipeline that
|
|
reads standard input and writes standard output that is
|
|
applied to OUTPUT before comparison with the expected contents.]
|
|
[+INTRO?Called by \bregress\b to introduce all \bTEST\b
|
|
groups.]
|
|
[+IO \b[ \bFIFO\b | \bPIPE\b ]] \bINPUT|OUTPUT|ERROR\b [ \b-e\b \afilter\a ]] [ \b-n\b ]] \afile\a | - \adata\a ...?Internal
|
|
support for the \bINPUT\b, \bOUTPUT\b and \bERROR\b functions.]
|
|
[+JOB \b\aop\a [ ... ]]?Like \bEXEC\b except the command is run
|
|
as a background job for the duration of the group or until it
|
|
is killed via \bKILL\b.]
|
|
[+KEEP \b\apattern\a ...?The temporary directory is cleared for
|
|
each test. Files matching \apattern\a are retained between
|
|
tests.]
|
|
[+KILL \b[ \asignal\a ]]?Kill the background job with \asignal\a
|
|
[ \bSIGKILL\b ]].]
|
|
[+MOVE \b\afrom to\a?Rename file \afrom\a to \ato\a. \afrom\a may
|
|
be a regular file or \bINPUT\b, \bOUTPUT\b or \bERROR\b. Post
|
|
test comparisons are ignored for \afrom\a.]
|
|
[+NOTE \b\acomment\a?\acomment\a is added to the current test
|
|
trace output.]
|
|
[+OUTPUT \b[ \b-e\b \afilter\a ]] [ \b-n\b ]] \afile\a | - \adata\a ...?The
|
|
standard output is expected to match either the contents
|
|
of \afile\a or the line \adata\a. \bOUTPUT -n\b does not
|
|
append a newline to \adata\a. \afilter\a is a shell command
|
|
or pipeline that reads standard input and writes standard
|
|
output that is applied to ERROR before comparison with the
|
|
expected contents.]
|
|
[+PIPE \bINPUT|OUTPUT|ERROR\b [ \b-n\b ]] \afile\a | - \adata\a ...?The
|
|
\bIO\B file is a pipe.]
|
|
[+PROG \b\acommand\a [ \aarg\a ... ]]?\acommand\a is run with
|
|
optional arguments.]
|
|
[+REMOVE \b\afile\a ...?\afile\a ... are removed after the
|
|
current test is done.]
|
|
[+RUN?Called by \bregress\b to run the current test.]
|
|
[+SAME \b\anew old\a?\anew\a is expected to be the same as
|
|
\aold\a after the current test completes.]
|
|
[+SET \b[\bno\b]]\aname\a[=\avalue\a]]?Set the command line
|
|
option --\aname\a. The setting is in effect for all tests until
|
|
the next explicit \bSET\b.]
|
|
[+TALLY?Called by \bregress\b display the \bTEST\b results.]
|
|
[+TEST \b\anumber\a [ \adescription\a ... ]]?Define a new test
|
|
group labelled \anumber\a with optional \adescripion\a.]
|
|
[+TITLE \b[+]] \atext\a?Set the \bTEST\b output title to
|
|
\atext\a. If \b+\b is specified then \atext\a is appended to
|
|
the default title. The default title is the test file base
|
|
name, and, if different from the test file base name, the test
|
|
unit base name.]
|
|
[+TWD \b[ \adir\a ... ]]?Set the temporary test dir to \adir\a.
|
|
The default is \aunit\a\b.tmp\b, where \aunit\a is the test
|
|
input file sans directory and suffix. If \adir\a matches \b/*\b
|
|
then it is the directory name; if \adir\a is non-null then the
|
|
prefix \b${TMPDIR:-/tmp}\b is added; otherwise if \adir\a is
|
|
omitted then
|
|
\b${TMPDIR:-/tmp}/tst-\b\aunit\a-$$-$RANDOM.\b\aunit\a is
|
|
used.]
|
|
[+UMASK \b[ \amask\a ]]?Run subsequent tests with \bumask\b(1)
|
|
\amask\a. If \amask\a is omitted then the original \bumask\b is
|
|
used.]
|
|
[+UNIT \b\acommand\a [ \aarg\a ... ]]?Define the command and
|
|
optional default arguments to be tested. \bUNIT\b explicitly
|
|
overrides the default command name derived from the test script
|
|
file name. A \acommand\a operand with optional arguments
|
|
overrides the \bUNIT\b \acommand\a and arguments, with the
|
|
exception that if the \bUNIT\b \acommand\a is \b-\b or \b+\b
|
|
the \bUNIT\b arguments are appended to the operand or default
|
|
unit command and arguments.]
|
|
[+VIEW \b\avar\a [ \afile\a ]]?\avar\a is set to the full
|
|
pathname of \avar\a [ \afile\a ]] in the current \b$VPATH\b
|
|
view if defined.]
|
|
}
|
|
[+SEE ALSO?\bnmake\b(1), \bksh\b(1)]
|
|
'
|
|
;;
|
|
*) USAGE='ko:[[no]name[=value]]t:[test]v unit [path [arg ...]]'
|
|
;;
|
|
esac
|
|
|
|
function FATAL # message
|
|
{
|
|
print -r -u2 "$command: $*"
|
|
GROUP=FINI
|
|
exit 1
|
|
}
|
|
|
|
function EMPTY
|
|
{
|
|
typeset i
|
|
typeset -n ARRAY=$1
|
|
for i in ${!ARRAY[@]}
|
|
do unset ARRAY[$i]
|
|
done
|
|
}
|
|
|
|
function INITIALIZE # void
|
|
{
|
|
typeset i j
|
|
cd "$TWD"
|
|
case $KEEP in
|
|
"") RM *
|
|
;;
|
|
*) for i in *
|
|
do case $i in
|
|
!($KEEP)) j="$j $i" ;;
|
|
esac
|
|
done
|
|
case $j in
|
|
?*) RM $j ;;
|
|
esac
|
|
;;
|
|
esac
|
|
: >INPUT >OUTPUT.ex >ERROR.ex
|
|
BODY=""
|
|
COPY=""
|
|
DIAGNOSTICS=""
|
|
DONE=""
|
|
ERROR=""
|
|
EXIT=0
|
|
IGNORE=""
|
|
INIT=""
|
|
INPUT=""
|
|
MOVE=""
|
|
OUTPUT=""
|
|
EMPTY FILE
|
|
EMPTY FILTER
|
|
EMPTY SAME
|
|
EMPTY TYPE
|
|
}
|
|
|
|
function INTRO
|
|
{
|
|
typeset base command
|
|
|
|
if [[ ! $TEST_quiet ]]
|
|
then base=${REGRESS##*/}
|
|
base=${base%.tst}
|
|
command=${COMMAND##*/}
|
|
command=${command%' '*}
|
|
set -- $TITLE
|
|
TITLE=
|
|
case $1 in
|
|
''|+) if [[ $command == $base ]]
|
|
then TITLE=$COMMAND
|
|
else TITLE="$COMMAND, $base"
|
|
fi
|
|
if (( $# ))
|
|
then shift
|
|
fi
|
|
;;
|
|
esac
|
|
while (( $# ))
|
|
do if [[ $TITLE ]]
|
|
then TITLE="$TITLE, $1"
|
|
else TITLE="$1"
|
|
fi
|
|
shift
|
|
done
|
|
print -u2 "TEST $TITLE"
|
|
fi
|
|
}
|
|
|
|
function TALLY # extra message text
|
|
{
|
|
typeset msg
|
|
case $GROUP in
|
|
INIT) ;;
|
|
*) msg="TEST $TITLE, $TESTS test"
|
|
case $TESTS in
|
|
1) ;;
|
|
*) msg=${msg}s ;;
|
|
esac
|
|
msg="$msg, $ERRORS error"
|
|
case $ERRORS in
|
|
1) ;;
|
|
*) msg=${msg}s ;;
|
|
esac
|
|
if (( $# ))
|
|
then msg="$msg, $*"
|
|
fi
|
|
print -u2 "$msg"
|
|
GROUP=INIT
|
|
TESTS=0
|
|
ERRORS=0
|
|
;;
|
|
esac
|
|
}
|
|
|
|
function TITLE # text
|
|
{
|
|
TITLE=$@
|
|
}
|
|
|
|
function UNWIND
|
|
{
|
|
while (( COND > 1 ))
|
|
do print -r -u2 "$command: line $LINE: no matching FI for IF on line ${COND_LINE[COND]}"
|
|
(( COND-- ))
|
|
done
|
|
if (( COND > 0 ))
|
|
then (( COND = 0 ))
|
|
FATAL "line $LINE: no matching FI for IF on line ${COND_LINE[COND+1]}"
|
|
fi
|
|
if [[ $JOBPID ]]
|
|
then if [[ $JOBPID != 0 ]]
|
|
then kill -KILL $JOBPID 2>/dev/null
|
|
wait
|
|
fi
|
|
JOBPID=
|
|
fi
|
|
JOBSTATUS=
|
|
JOBOP=
|
|
wait
|
|
}
|
|
|
|
function CLEANUP # status
|
|
{
|
|
typeset note
|
|
|
|
if [[ $GROUP != INIT ]]
|
|
then if [[ ! $TEST_keep ]]
|
|
then cd $SOURCE
|
|
if [[ $TEST_local ]]
|
|
then RM ${TEST_local}
|
|
fi
|
|
RM "$TWD"
|
|
fi
|
|
if (( $1 )) && [[ $GROUP != FINI ]]
|
|
then note=terminated
|
|
fi
|
|
fi
|
|
TALLY $note
|
|
[[ $TEST_keep ]] || UNWIND
|
|
exit $1
|
|
}
|
|
|
|
function RUN # [ op ]
|
|
{
|
|
typeset i r=1
|
|
[[ $UMASK != $UMASK_ORIG ]] && umask $UMASK_ORIG
|
|
#print -u2 AHA#$LINENO $0 GROUP=$GROUP ITEM=$ITEM FLUSHED=$FLUSHED JOBOP=$JOBOP
|
|
case $GROUP in
|
|
INIT) RM "$TWD"
|
|
if [[ $TEST_local ]]
|
|
then TEST_local=${TMPDIR:-/tmp}/rt-$$/${TWD##*/}
|
|
mkdir -p "$TEST_local" && ln -s "$TEST_local" "$TWD" || FATAL "$TWD": cannot create directory
|
|
TEST_local=${TEST_local%/*}
|
|
else mkdir "$TWD" || FATAL "$TWD": cannot create directory
|
|
fi
|
|
cd "$TWD"
|
|
TWD=$PWD
|
|
: > rmu
|
|
if rm -u rmu >/dev/null 2>&1
|
|
then TEST_rmu=-u
|
|
else rm rmu
|
|
fi
|
|
if [[ $UNIT ]]
|
|
then set -- "${ARGV[@]}"
|
|
case $1 in
|
|
""|[-+]*)
|
|
UNIT $UNIT "${ARGV[@]}"
|
|
;;
|
|
*) UNIT "${ARGV[@]}"
|
|
;;
|
|
esac
|
|
fi
|
|
INTRO
|
|
;;
|
|
FINI) ;;
|
|
$TEST_select)
|
|
if [[ $ITEM == $FLUSHED ]]
|
|
then return 0
|
|
fi
|
|
FLUSHED=$ITEM
|
|
if (( COND_SKIP[COND] ))
|
|
then return 1
|
|
fi
|
|
((COUNT++))
|
|
if (( $ITEM <= $LASTITEM ))
|
|
then LABEL=$TEST#$COUNT
|
|
else LASTITEM=$ITEM
|
|
LABEL=$TEST:$ITEM
|
|
fi
|
|
TEST_file=""
|
|
exec >/dev/null
|
|
for i in $INPUT
|
|
do case " $OUTPUT " in
|
|
*" $i "*)
|
|
if [[ -f $i.sav ]]
|
|
then cp $i.sav $i
|
|
COMPARE="$COMPARE $i"
|
|
elif [[ -f $i ]]
|
|
then cp $i $i.sav
|
|
COMPARE="$COMPARE $i"
|
|
fi
|
|
;;
|
|
esac
|
|
done
|
|
for i in $OUTPUT
|
|
do case " $COMPARE " in
|
|
*" $i "*)
|
|
;;
|
|
*) COMPARE="$COMPARE $i"
|
|
;;
|
|
esac
|
|
done
|
|
for i in $INIT
|
|
do $i $TEST INIT
|
|
done
|
|
#print -u2 AHA#$LINENO $0 GROUP=$GROUP ITEM=$ITEM JOBOP=$JOBOP JOBPID=$JOBPID JOBSTATUS=$JOBSTATUS
|
|
if [[ $JOBPID != 0 && ( $JOBPID || $JOBSTATUS ) ]]
|
|
then if [[ ! $TEST_quiet ]]
|
|
then print -nu2 "$LABEL"
|
|
fi
|
|
RESULTS
|
|
elif [[ $BODY ]]
|
|
then SHOW=$NOTE
|
|
if [[ ! $TEST_quiet ]]
|
|
then print -r -u2 " $SHOW"
|
|
fi
|
|
for i in $BODY
|
|
do $i $TEST BODY
|
|
done
|
|
else SHOW=
|
|
if [[ ${TYPE[INPUT]} == PIPE ]]
|
|
then if [[ ${TYPE[OUTPUT]} == PIPE ]]
|
|
then if [[ ! $TEST_quiet ]]
|
|
then print -nu2 "$LABEL"
|
|
fi
|
|
cat <$TWD/INPUT | COMMAND "${ARGS[@]}" | cat >$TWD/OUTPUT
|
|
RESULTS 'pipe input'
|
|
else if [[ ! $TEST_quiet ]]
|
|
then print -nu2 "$LABEL"
|
|
fi
|
|
cat <$TWD/INPUT | COMMAND "${ARGS[@]}" >$TWD/OUTPUT
|
|
RESULTS 'pipe io'
|
|
fi
|
|
elif [[ ${TYPE[OUTPUT]} == PIPE ]]
|
|
then if [[ ! $TEST_quiet ]]
|
|
then print -nu2 "$LABEL"
|
|
fi
|
|
COMMAND "${ARGS[@]}" <$TWD/INPUT | cat >$TWD/OUTPUT
|
|
RESULTS 'pipe output'
|
|
else if [[ $TEST_regular ]]
|
|
then if [[ ! $TEST_quiet ]]
|
|
then print -nu2 "$LABEL"
|
|
fi
|
|
if [[ ${TYPE[INPUT]} == FIFO ]]
|
|
then COMMAND "${ARGS[@]}" >$TWD/OUTPUT
|
|
else COMMAND "${ARGS[@]}" <$TWD/INPUT >$TWD/OUTPUT
|
|
fi
|
|
RESULTS
|
|
fi
|
|
if [[ $TEST_pipe_input ]]
|
|
then if [[ ! $TEST_quiet ]]
|
|
then print -nu2 "$LABEL"
|
|
fi
|
|
(trap '' PIPE; cat <$TWD/INPUT 2>/dev/null; exit 0) | COMMAND "${ARGS[@]}" >$TWD/OUTPUT
|
|
STATUS=$?
|
|
RESULTS 'pipe input'
|
|
fi
|
|
if [[ $TEST_pipe_output ]]
|
|
then if [[ ! $TEST_quiet ]]
|
|
then print -nu2 "$LABEL"
|
|
fi
|
|
COMMAND "${ARGS[@]}" <$TWD/INPUT | cat >$TWD/OUTPUT
|
|
STATUS=$?
|
|
RESULTS 'pipe output'
|
|
fi
|
|
if [[ $TEST_pipe_io ]]
|
|
then if [[ ! $TEST_quiet ]]
|
|
then print -nu2 "$LABEL"
|
|
fi
|
|
(trap '' PIPE; cat <$TWD/INPUT 2>/dev/null; exit 0) | COMMAND "${ARGS[@]}" | cat >$TWD/OUTPUT
|
|
STATUS=$?
|
|
RESULTS 'pipe io'
|
|
fi
|
|
fi
|
|
set -- $COPY
|
|
COPY=""
|
|
while :
|
|
do case $# in
|
|
0|1) break ;;
|
|
*) cp $1 $2 ;;
|
|
esac
|
|
shift 2
|
|
done
|
|
set -- $MOVE
|
|
MOVE=""
|
|
while (( $# > 1 ))
|
|
do mv $1 $2
|
|
shift 2
|
|
done
|
|
fi
|
|
for i in $DONE
|
|
do $i $TEST DONE $STATUS
|
|
done
|
|
COMPARE=""
|
|
r=0
|
|
;;
|
|
esac
|
|
if [[ $COMMAND_ORIG ]]
|
|
then COMMAND=$COMMAND_ORIG
|
|
COMMAND_ORIG=
|
|
ARGS=(${ARGS_ORIG[@]})
|
|
fi
|
|
return $r
|
|
}
|
|
|
|
function DO # cmd ...
|
|
{
|
|
[[ $GROUP == $TEST_select ]] || return 1
|
|
(( COND_SKIP[COND] )) && return 1
|
|
[[ $UMASK != $UMASK_ORIG ]] && umask $UMASK
|
|
return 0
|
|
}
|
|
|
|
function UNIT # cmd arg ...
|
|
{
|
|
typeset cmd=$1
|
|
case $cmd in
|
|
[-+]) shift
|
|
if (( UNIT_READONLY ))
|
|
then COMMAND="$COMMAND $*"
|
|
else #BUG# ARGV=("${ARGV[@]}" "$@")
|
|
set -- "${ARGV[@]}" "$@"
|
|
ARGV=("$@")
|
|
fi
|
|
return
|
|
;;
|
|
esac
|
|
(( UNIT_READONLY )) && return
|
|
if [[ $UNIT ]] && (( $# <= 1 ))
|
|
then set -- "${ARGV[@]}"
|
|
case $1 in
|
|
"") set -- "$cmd" ;;
|
|
[-+]*) set -- "$cmd" "${ARGV[@]}" ;;
|
|
*) cmd=$1 ;;
|
|
esac
|
|
fi
|
|
UNIT=
|
|
COMMAND=$cmd
|
|
shift
|
|
typeset cmd=$(whence $COMMAND)
|
|
if [[ ! $cmd ]]
|
|
then FATAL $COMMAND: not found
|
|
elif [[ ! $cmd ]]
|
|
then FATAL $cmd: not found
|
|
fi
|
|
case $# in
|
|
0) ;;
|
|
*) COMMAND="$COMMAND $*" ;;
|
|
esac
|
|
}
|
|
|
|
function TWD # [ dir ]
|
|
{
|
|
case $1 in
|
|
'') TWD=${TWD##*/}; TWD=${TMPDIR:-/tmp}/tst-${TWD%.*}-$$-$RANDOM ;;
|
|
/*) TWD=$1 ;;
|
|
*) TWD=${TMPDIR:-/tmp}/$1 ;;
|
|
esac
|
|
}
|
|
|
|
function TEST # number description arg ...
|
|
{
|
|
RUN
|
|
LINE=$TESTLINE
|
|
UNWIND
|
|
COUNT=0
|
|
LASTITEM=0
|
|
case $1 in
|
|
-) ((LAST++)); TEST=$LAST ;;
|
|
+([0123456789])) LAST=$1 TEST=$1 ;;
|
|
*) LAST=0${1/[!0123456789]/} TEST=$1 ;;
|
|
esac
|
|
NOTE=
|
|
if [[ ! $TEST_quiet && $TEST == $TEST_select ]] && (( ! COND_SKIP[COND] ))
|
|
then print -r -u2 "$TEST $2"
|
|
fi
|
|
unset ARGS
|
|
unset EXPORT
|
|
EXPORTS=0
|
|
TEST_file=""
|
|
if [[ $TEST != ${GROUP}* ]]
|
|
then GROUP=${TEST%%+([abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ])}
|
|
if [[ $GROUP == $TEST_select ]] && (( ! COND_SKIP[COND] ))
|
|
then INITIALIZE
|
|
fi
|
|
fi
|
|
((SUBTESTS=0))
|
|
[[ $TEST == $TEST_select ]] && (( ! COND_SKIP[COND] ))
|
|
}
|
|
|
|
function EXEC # arg ...
|
|
{
|
|
if [[ $GROUP != $TEST_select ]] || (( COND_SKIP[COND] ))
|
|
then return
|
|
fi
|
|
if ((SUBTESTS++))
|
|
then RUN
|
|
fi
|
|
case $# in
|
|
0) set -- "${ARGS[@]}" ;;
|
|
esac
|
|
ITEM=$LINE
|
|
NOTE="$(print -r -f '%q ' -- $COMMAND_ORIG "$@")${JOBPID:+&}"
|
|
ARGS=("$@")
|
|
}
|
|
|
|
function JOB # arg ...
|
|
{
|
|
JOBPID=0
|
|
EXEC "$@"
|
|
}
|
|
|
|
function CONTINUE
|
|
{
|
|
RUN || return
|
|
JOBOP=CONTINUE
|
|
ITEM=$LINE
|
|
NOTE="$(print -r -f '%q ' -- $JOBOP)"
|
|
#print -u2 AHA#$LINENO JOBOP=$JOBOP ITEM=$ITEM NOTE=$NOTE
|
|
}
|
|
|
|
function EXITED
|
|
{
|
|
RUN || return
|
|
JOBOP=EXITED
|
|
ITEM=$LINE
|
|
NOTE="$(print -r -f '%q ' -- $JOBOP)"
|
|
#print -u2 AHA#$LINENO JOBOP=$JOBOP ITEM=$ITEM NOTE=$NOTE
|
|
}
|
|
|
|
function KILL # [ signal ]
|
|
{
|
|
RUN || return
|
|
JOBOP=$2
|
|
[[ $JOBOP ]] || JOBOP=KILL
|
|
ITEM=$LINE
|
|
NOTE="$(print -r -f '%q ' -- $JOBOP)"
|
|
}
|
|
|
|
function CD
|
|
{
|
|
RUN
|
|
if [[ $GROUP == $TEST_select ]] && (( ! COND_SKIP[COND] ))
|
|
then mkdir -p "$@" && cd "$@" || FATAL cannot initialize working directory "$@"
|
|
fi
|
|
}
|
|
|
|
function EXPORT
|
|
{
|
|
typeset x n v
|
|
if [[ $GROUP == INIT ]]
|
|
then for x
|
|
do n=${x%%=*}
|
|
v=${x#*=}
|
|
ENVIRON[ENVIRONS++]=$n="'$v'"
|
|
done
|
|
else RUN
|
|
if [[ $GROUP != $TEST_select ]] || (( COND_SKIP[COND] ))
|
|
then return
|
|
fi
|
|
for x
|
|
do n=${x%%=*}
|
|
v=${x#*=}
|
|
EXPORT[EXPORTS++]=$n="'$v'"
|
|
done
|
|
fi
|
|
}
|
|
|
|
function FLUSH
|
|
{
|
|
if [[ $GROUP != $TEST_select ]] || (( COND_SKIP[COND] ))
|
|
then return
|
|
fi
|
|
if ((SUBTESTS++))
|
|
then RUN
|
|
fi
|
|
}
|
|
|
|
function PROG # cmd arg ...
|
|
{
|
|
typeset command args
|
|
if [[ $GROUP != $TEST_select ]] || (( COND_SKIP[COND] ))
|
|
then return
|
|
fi
|
|
ITEM=$LINE
|
|
NOTE="$(print -r -f '%q ' -- "$@")"
|
|
COMMAND_ORIG=$COMMAND
|
|
COMMAND=$1
|
|
shift
|
|
ARGS_ORIG=(${ARGS[@]})
|
|
ARGS=("$@")
|
|
}
|
|
|
|
function NOTE # description
|
|
{
|
|
NOTE=$*
|
|
}
|
|
|
|
function IO # [ PIPE ] INPUT|OUTPUT|ERROR [-f*|-n] file|- data ...
|
|
{
|
|
typeset op i v f file type x
|
|
if [[ $GROUP != $TEST_select ]] || (( COND_SKIP[COND] ))
|
|
then return
|
|
fi
|
|
[[ $UMASK != $UMASK_ORIG ]] && umask $UMASK_ORIG
|
|
while :
|
|
do case $1 in
|
|
FIFO|PIPE) type=$1; shift ;;
|
|
*) break ;;
|
|
esac
|
|
done
|
|
op=$1
|
|
shift
|
|
[[ $type ]] && TYPE[$op]=$type
|
|
FILTER[$op]=
|
|
file=$TWD/$op
|
|
while :
|
|
do case $1 in
|
|
-x) x=1
|
|
shift
|
|
;;
|
|
-e) (( $# > 1 )) && shift
|
|
FILTER[$op]=$1
|
|
shift
|
|
;;
|
|
-e*) FILTER[$op]=${1#-e}
|
|
shift
|
|
;;
|
|
-f*|-n) f=$1
|
|
shift
|
|
;;
|
|
*) break
|
|
;;
|
|
esac
|
|
done
|
|
case $# in
|
|
0) ;;
|
|
*) case $1 in
|
|
-) ;;
|
|
*) file=$1
|
|
eval i='$'$op
|
|
case " $i " in
|
|
*" $file "*)
|
|
;;
|
|
*) eval $op='"$'$op' $file"'
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|
|
shift
|
|
;;
|
|
esac
|
|
case " $IGNORE " in
|
|
*" $file "*)
|
|
for i in $IGNORE
|
|
do case $i in
|
|
$file) ;;
|
|
*) v="$v $i" ;;
|
|
esac
|
|
done
|
|
IGNORE=$v
|
|
;;
|
|
esac
|
|
FILE[$op]=$file
|
|
case $op in
|
|
OUTPUT|ERROR)
|
|
file=$file.ex
|
|
if [[ $file != /* ]]
|
|
then file=$TWD/$file
|
|
fi
|
|
;;
|
|
esac
|
|
#unset SAME[$op]
|
|
SAME[$op]=
|
|
if [[ $file == /* ]]
|
|
then RM $file.sav
|
|
else RM $TWD/$file.sav
|
|
fi
|
|
if [[ $file == */* ]]
|
|
then mkdir -p ${file%/*}
|
|
fi
|
|
if [[ $file != */ ]]
|
|
then if [[ $type == FIFO ]]
|
|
then rm -f $file
|
|
mkfifo $file
|
|
fi
|
|
if [[ ${TYPE[$op]} != FIFO ]]
|
|
then if [[ $JOBOP ]]
|
|
then case $#:$f in
|
|
0:) ;;
|
|
*:-f) printf -- "$@" ;;
|
|
*:-f*) printf -- "${f#-f}""$@" ;;
|
|
*) print $f -r -- "$@" ;;
|
|
esac >> $file
|
|
else case $#:$f in
|
|
0:) ;;
|
|
*:-f) printf -- "$@" ;;
|
|
*:-f*) printf -- "${f#-f}""$@" ;;
|
|
*) print $f -r -- "$@" ;;
|
|
esac > $file
|
|
fi
|
|
elif [[ $#:$f != 0: ]]
|
|
then case $#:$f in
|
|
*:-f) printf -- "$@" ;;
|
|
*:-f*) printf -- "${f#-f}""$@" ;;
|
|
*) print $f -r -- "$@" ;;
|
|
esac >> $file &
|
|
fi
|
|
if [[ $x ]]
|
|
then chmod +x $file
|
|
fi
|
|
fi
|
|
}
|
|
|
|
function INPUT # file|- data ...
|
|
{
|
|
IO $0 "$@"
|
|
}
|
|
|
|
function COPY # from to
|
|
{
|
|
if [[ $GROUP != $TEST_select ]] || (( COND_SKIP[COND] ))
|
|
then return
|
|
fi
|
|
COPY="$COPY $@"
|
|
}
|
|
|
|
function MOVE # from to
|
|
{
|
|
typeset f
|
|
if [[ $GROUP != $TEST_select ]] || (( COND_SKIP[COND] ))
|
|
then return
|
|
fi
|
|
for f
|
|
do case $f in
|
|
INPUT|OUTPUT|ERROR)
|
|
f=$TWD/$f
|
|
;;
|
|
/*) ;;
|
|
*) f=$PWD/$f
|
|
;;
|
|
esac
|
|
MOVE="$MOVE $f"
|
|
done
|
|
}
|
|
|
|
function SAME # new old
|
|
{
|
|
typeset i file v
|
|
if [[ $GROUP != $TEST_select ]] || (( COND_SKIP[COND] ))
|
|
then return
|
|
fi
|
|
case $# in
|
|
2) case $1 in
|
|
INPUT) cat $2 > $1; return ;;
|
|
esac
|
|
SAME[$1]=$2
|
|
file=$1
|
|
COMPARE="$COMPARE $1"
|
|
;;
|
|
3) SAME[$2]=$3
|
|
file=$2
|
|
eval i='$'$1
|
|
case " $i " in
|
|
*" $2 "*)
|
|
;;
|
|
*) eval $1='"$'$1' $2"'
|
|
;;
|
|
esac
|
|
COMPARE="$COMPARE $2"
|
|
;;
|
|
esac
|
|
case " $IGNORE " in
|
|
*" $file "*)
|
|
for i in $IGNORE
|
|
do case $i in
|
|
$file) ;;
|
|
*) v="$v $i" ;;
|
|
esac
|
|
done
|
|
IGNORE=$v
|
|
;;
|
|
esac
|
|
}
|
|
|
|
function OUTPUT # file|- data ...
|
|
{
|
|
IO $0 "$@"
|
|
}
|
|
|
|
function ERROR # file|- data ...
|
|
{
|
|
IO $0 "$@"
|
|
}
|
|
|
|
function RM # rm(1) args
|
|
{
|
|
if [[ ! $TEST_rmu ]]
|
|
then chmod -R u+rwx "$@" >/dev/null 2>&1
|
|
fi
|
|
rm $TEST_rmu $TEST_rmflags "$@"
|
|
}
|
|
|
|
function REMOVE # file ...
|
|
{
|
|
typeset i
|
|
for i
|
|
do RM $i $i.sav
|
|
done
|
|
}
|
|
|
|
function IGNORE # file ...
|
|
{
|
|
typeset i
|
|
for i
|
|
do case $i in
|
|
INPUT|OUTPUT|ERROR)
|
|
i=$TWD/$i
|
|
;;
|
|
esac
|
|
case " $IGNORE " in
|
|
*" $i "*)
|
|
;;
|
|
*) IGNORE="$IGNORE $i"
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
function KEEP # pattern ...
|
|
{
|
|
typeset i
|
|
for i
|
|
do case $KEEP in
|
|
"") KEEP="$i" ;;
|
|
*) KEEP="$KEEP|$i" ;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
function DIAGNOSTICS # [ 1 | 0 ]
|
|
{
|
|
case $#:$1 in
|
|
0:|1:1) DIAGNOSTICS=1
|
|
EXIT='*'
|
|
;;
|
|
1:|1:0) DIAGNOSTICS=""
|
|
EXIT=0
|
|
;;
|
|
*) DIAGNOSTICS=$1
|
|
EXIT='*'
|
|
;;
|
|
esac
|
|
}
|
|
|
|
function IGNORESPACE
|
|
{
|
|
: ${IGNORESPACE=-b}
|
|
}
|
|
|
|
function EXIT # status
|
|
{
|
|
EXIT=$1
|
|
}
|
|
|
|
function INFO # info description
|
|
{
|
|
typeset -R15 info=$1
|
|
if [[ ! $1 ]]
|
|
then info=no
|
|
fi
|
|
shift
|
|
if [[ ! $TEST_quiet ]]
|
|
then print -r -u2 "$info " "$@"
|
|
fi
|
|
}
|
|
|
|
function COMMAND # arg ...
|
|
{
|
|
typeset input
|
|
((TESTS++))
|
|
case " ${ENVIRON[*]} ${EXPORT[*]}" in
|
|
*' 'LC_ALL=*)
|
|
;;
|
|
*' 'LC_+([A-Z])=*)
|
|
EXPORT[EXPORTS++]="LC_ALL="
|
|
;;
|
|
esac
|
|
if [[ $TEST_keep ]]
|
|
then (
|
|
PS4=''
|
|
set -x
|
|
print -r -- "${ENVIRON[@]}" "${EXPORT[@]}" "PATH=$PATH" $COMMAND "$@"
|
|
) 2>&1 >/dev/null |
|
|
sed -e 's,^print -r -- ,,' -e 's,$, "$@",' >$TWD/COMMAND
|
|
chmod +x $TWD/COMMAND
|
|
fi
|
|
if [[ $UMASK != $UMASK_ORIG ]]
|
|
then : >$TWD/ERROR
|
|
umask $UMASK
|
|
fi
|
|
if [[ ${TYPE[INPUT]} == FIFO && ${FILE[INPUT]} == */INPUT ]]
|
|
then input="< ${FILE[INPUT]}"
|
|
fi
|
|
if [[ $TEST_trace ]]
|
|
then set +x
|
|
eval print -u2 "$PS4" "${ENVIRON[@]}" "${EXPORT[@]}" PATH='$PATH' '$'COMMAND '"$@"' '$input' '"2>$TWD/ERROR"' '"${JOBPID:+&}"'
|
|
fi
|
|
eval "${ENVIRON[@]}" "${EXPORT[@]}" PATH='$PATH' '$'COMMAND '"$@"' $input "2>$TWD/ERROR" "${JOBPID:+&}"
|
|
STATUS=$?
|
|
[[ $TEST_trace ]] && set -x
|
|
if [[ $JOBPID ]]
|
|
then JOBPID=$!
|
|
fi
|
|
[[ $UMASK != $UMASK_ORIG ]] && umask $UMASK_ORIG
|
|
return $STATUS
|
|
}
|
|
|
|
function RESULTS # pipe*
|
|
{
|
|
typeset i j k s failed ignore io op
|
|
if [[ $1 ]]
|
|
then io="$1 "
|
|
fi
|
|
[[ $JOBOP || $JOBPID || $JOBSTATUS ]] && sleep 1
|
|
for i in $COMPARE $TWD/OUTPUT $TWD/ERROR
|
|
do case " $IGNORE $ignore $MOVE " in
|
|
*" $i "*) continue ;;
|
|
esac
|
|
ignore="$ignore $i"
|
|
op=${i##*/}
|
|
if [[ ${FILTER[$op]} ]]
|
|
then eval "{ ${FILTER[$op]} ;} < $i > $i.fi"
|
|
mv $i.fi $i
|
|
fi
|
|
j=${SAME[$op]}
|
|
if [[ ! $j ]]
|
|
then if [[ $i == /* ]]
|
|
then k=$i
|
|
else k=$TWD/$i
|
|
fi
|
|
for s in ex sav err
|
|
do [[ -f $k.$s ]] && break
|
|
done
|
|
j=$k.$s
|
|
fi
|
|
if [[ "$DIAGNOSTICS" && $i == */ERROR ]]
|
|
then if [[ $STATUS == 0 && ! -s $TWD/ERROR || $DIAGNOSTICS != 1 && $(<$i) != $DIAGNOSTICS ]]
|
|
then failed=$failed${failed:+,}DIAGNOSTICS
|
|
if [[ $TEST_verbose && $DIAGNOSTICS != 1 ]]
|
|
then print -u2 " ===" "diagnostic pattern '$DIAGNOSTICS' did not match" ${i#$TWD/} "==="
|
|
cat $i >&2
|
|
fi
|
|
fi
|
|
continue
|
|
fi
|
|
diff $IGNORESPACE $i $j >$i.diff 2>&1
|
|
if [[ -s $i.diff ]]
|
|
then failed=$failed${failed:+,}${i#$TWD/}
|
|
if [[ $TEST_verbose ]]
|
|
then print -u2 " ===" diff $IGNORESPACE ${i#$TWD/} "<actual >expected ==="
|
|
cat $i.diff >&2
|
|
fi
|
|
fi
|
|
done
|
|
if [[ $JOBOP ]]
|
|
then if [[ $JOBPID ]] && ! kill -0 $JOBPID 2>/dev/null
|
|
then wait $JOBPID
|
|
JOBSTATUS=$?
|
|
JOBPID=
|
|
fi
|
|
#print -u2 AHA#$LINENO JOBOP=$JOBOP JOBPID=$JOBPID JOBSTATUS=$JOBSTATUS
|
|
case $JOBOP in
|
|
CONTINUE)
|
|
if [[ ! $JOBPID ]]
|
|
then failed=$failed${failed:+,}EXITED
|
|
fi
|
|
;;
|
|
EXITED) if [[ $JOBPID ]]
|
|
then failed=$failed${failed:+,}RUNNING
|
|
fi
|
|
;;
|
|
*) if [[ ! $JOBPID ]]
|
|
then failed=$failed${failed:+,}EXITED
|
|
fi
|
|
if ! kill -$JOBOP $JOBPID 2>/dev/null
|
|
then failed=$failed${failed:+,}KILL-$JOBOP
|
|
fi
|
|
;;
|
|
esac
|
|
JOBOP=
|
|
fi
|
|
if [[ ! $failed && $STATUS != $EXIT ]]
|
|
then failed="exit code $EXIT expected -- got $STATUS"
|
|
fi
|
|
if [[ $failed ]]
|
|
then ((ERRORS++))
|
|
if [[ ! $TEST_quiet ]]
|
|
then SHOW="FAILED ${io}[ $failed ] $NOTE"
|
|
print -r -u2 " $SHOW"
|
|
fi
|
|
if [[ $TEST_keep ]]
|
|
then GROUP=FINI
|
|
exit
|
|
fi
|
|
elif [[ ! $TEST_quiet ]]
|
|
then SHOW=$NOTE
|
|
print -r -u2 " $SHOW"
|
|
fi
|
|
}
|
|
|
|
function SET # [no]name[=value]
|
|
{
|
|
typeset i r
|
|
if [[ $TEST ]]
|
|
then RUN
|
|
fi
|
|
for i
|
|
do if [[ $i == - ]]
|
|
then r=1
|
|
elif [[ $i == + ]]
|
|
then r=
|
|
else if [[ $i == no?* ]]
|
|
then i=${i#no}
|
|
v=
|
|
elif [[ $i == *=* ]]
|
|
then v=${i#*=}
|
|
if [[ $v == 0 ]]
|
|
then v=
|
|
fi
|
|
i=${i%%=*}
|
|
else v=1
|
|
fi
|
|
i=${i//-/_}
|
|
if [[ $r ]]
|
|
then READONLY[$i]=1
|
|
elif [[ ${READONLY[$i]} ]]
|
|
then continue
|
|
fi
|
|
eval TEST_$i=$v
|
|
fi
|
|
done
|
|
}
|
|
|
|
function VIEW # var [ file ]
|
|
{
|
|
nameref var=$1
|
|
typeset i bwd file pwd view root offset
|
|
if [[ $var ]]
|
|
then return 0
|
|
fi
|
|
case $# in
|
|
1) file=$1 ;;
|
|
*) file=$2 ;;
|
|
esac
|
|
pwd=${TWD%/*}
|
|
bwd=${PMP%/*}
|
|
if [[ -r $file ]]
|
|
then if [[ ! -d $file ]]
|
|
then var=$PWD/$file
|
|
return 0
|
|
fi
|
|
for i in $file/*
|
|
do if [[ -r $i ]]
|
|
then var=$PWD/$file
|
|
return 0
|
|
fi
|
|
break
|
|
done
|
|
fi
|
|
for view in ${VIEWS[@]}
|
|
do case $view in
|
|
/*) ;;
|
|
*) view=$pwd/$view ;;
|
|
esac
|
|
case $offset in
|
|
'') case $pwd in
|
|
$view/*) offset=${pwd#$view} ;;
|
|
*) offset=${bwd#$view} ;;
|
|
esac
|
|
;;
|
|
esac
|
|
if [[ -r $view$offset/$file ]]
|
|
then if [[ ! -d $view$offset/$file ]]
|
|
then var=$view$offset/$file
|
|
return 0
|
|
fi
|
|
for i in $view$offset/$file/*
|
|
do if [[ -f $i ]]
|
|
then var=$view$offset/$file
|
|
return 0
|
|
fi
|
|
break
|
|
done
|
|
fi
|
|
done
|
|
var=
|
|
return 1
|
|
}
|
|
|
|
function INCLUDE # file ...
|
|
{
|
|
typeset f v x
|
|
for f
|
|
do if VIEW v $f || [[ $PREFIX && $f != /* ]] && VIEW v $PREFIX$f
|
|
then x=$x$'\n'". $v"
|
|
else FATAL $f: not found
|
|
fi
|
|
done
|
|
[[ $x ]] && trap "$x" 0
|
|
}
|
|
|
|
function UMASK # [ mask ]
|
|
{
|
|
if (( $# ))
|
|
then UMASK=$1
|
|
else UMASK=$UMASK_ORIG
|
|
fi
|
|
}
|
|
|
|
function PIPE # INPUT|OUTPUT|ERROR file|- data ...
|
|
{
|
|
IO $0 "$@"
|
|
}
|
|
|
|
function FIFO # INPUT|OUTPUT|ERROR file|- data ...
|
|
{
|
|
IO $0 "$@"
|
|
}
|
|
|
|
function IF # command(s) [note]
|
|
{
|
|
[[ $GROUP == $TEST_select ]] || return
|
|
RUN
|
|
(( COND++ ))
|
|
COND_LINE[COND]=$LINE
|
|
if (( COND > 1 && COND_SKIP[COND-1] ))
|
|
then (( COND_KEPT[COND] = 1 ))
|
|
(( COND_SKIP[COND] = 1 ))
|
|
elif eval "{ $1 ;} >/dev/null 2>&1"
|
|
then (( COND_KEPT[COND] = 1 ))
|
|
(( COND_SKIP[COND] = 0 ))
|
|
[[ $2 && ! $TEST_quiet ]] && print -u2 "NOTE $2"
|
|
else (( COND_KEPT[COND] = 0 ))
|
|
(( COND_SKIP[COND] = 1 ))
|
|
fi
|
|
}
|
|
|
|
function ELIF # command(s) [note]
|
|
{
|
|
[[ $GROUP == $TEST_select ]] || return
|
|
RUN
|
|
if (( COND <= 0 ))
|
|
then FATAL line $LINE: no matching IF for ELIF
|
|
fi
|
|
if (( COND_KEPT[COND] ))
|
|
then (( COND_SKIP[COND] = 0 ))
|
|
elif eval "$* > /dev/null 2>&1"
|
|
then (( COND_KEPT[COND] = 1 ))
|
|
(( COND_SKIP[COND] = 0 ))
|
|
[[ $2 && ! $TEST_quiet ]] && print -u2 "NOTE $2"
|
|
else (( COND_SKIP[COND] = 1 ))
|
|
fi
|
|
}
|
|
|
|
function ELSE # [note]
|
|
{
|
|
[[ $GROUP == $TEST_select ]] || return
|
|
RUN
|
|
if (( COND <= 0 ))
|
|
then FATAL line $LINE: no matching IF for ELSE
|
|
fi
|
|
if (( COND_KEPT[COND] ))
|
|
then (( COND_SKIP[COND] = 1 ))
|
|
else (( COND_KEPT[COND] = 1 ))
|
|
(( COND_SKIP[COND] = 0 ))
|
|
[[ $1 && ! $TEST_quiet ]] && print -u2 "NOTE $1"
|
|
fi
|
|
}
|
|
|
|
function FI
|
|
{
|
|
[[ $GROUP == $TEST_select ]] || return
|
|
RUN
|
|
if (( COND <= 0 ))
|
|
then FATAL line $LINE: no matching IF for FI on line $LINE
|
|
fi
|
|
(( ! COND_KEPT[COND] )) && [[ $1 && ! $TEST_quiet ]] && print -u2 "NOTE $1"
|
|
(( COND-- ))
|
|
}
|
|
|
|
# main
|
|
|
|
integer ERRORS=0 ENVIRONS=0 EXPORTS=0 TESTS=0 SUBTESTS=0 LINE=0 TESTLINE=0
|
|
integer ITEM=0 LASTITEM=0 COND=0 UNIT_READONLY=0 COUNT
|
|
typeset ARGS COMMAND COPY DIAGNOSTICS ERROR EXEC FLUSHED=0 GROUP=INIT
|
|
typeset IGNORE INPUT KEEP OUTPUT TEST SOURCE MOVE NOTE UMASK UMASK_ORIG
|
|
typeset ARGS_ORIG COMMAND_ORIG TITLE UNIT ARGV PREFIX OFFSET IGNORESPACE
|
|
typeset COMPARE MAIN JOBPID='' JOBSTATUS=''
|
|
typeset TEST_file TEST_keep TEST_pipe_input TEST_pipe_io TEST_pipe_output TEST_local
|
|
typeset TEST_quiet TEST_regular=1 TEST_rmflags='-rf --' TEST_rmu TEST_select
|
|
|
|
typeset -A SAME VIEWS FILE TYPE READONLY FILTER
|
|
typeset -a COND_LINE COND_SKIP COND_KEPT ENVIRON EXPORT
|
|
typeset -Z LAST=00
|
|
|
|
unset FIGNORE
|
|
|
|
while getopts -a $command "$USAGE" OPT
|
|
do case $OPT in
|
|
b) (( $OPTARG )) && IGNORESPACE=-b
|
|
;;
|
|
i) SET - pipe-input=$OPTARG
|
|
;;
|
|
k) SET - keep=$OPTARG
|
|
;;
|
|
l) SET - local
|
|
;;
|
|
o) SET - pipe-output=$OPTARG
|
|
;;
|
|
p) SET - pipe-io=$OPTARG
|
|
;;
|
|
q) SET - quiet=$OPTARG
|
|
;;
|
|
r) SET - regular=$OPTARG
|
|
;;
|
|
t) if [[ $TEST_select ]]
|
|
then TEST_select="$TEST_select|${OPTARG//,/\|}"
|
|
else TEST_select="${OPTARG//,/\|}"
|
|
fi
|
|
;;
|
|
x) SET - trace=$OPTARG
|
|
;;
|
|
v) SET - verbose=$OPTARG
|
|
;;
|
|
*) GROUP=FINI
|
|
exit 2
|
|
;;
|
|
esac
|
|
done
|
|
shift $OPTIND-1
|
|
case $# in
|
|
0) FATAL test unit name omitted ;;
|
|
esac
|
|
export COLUMNS=80
|
|
SOURCE=$PWD
|
|
PATH=$SOURCE:${PATH#?(.):}
|
|
PATH=${PATH%%:?(.)}:/usr/5bin:/bin:/usr/bin
|
|
UNIT=$1
|
|
shift
|
|
if [[ -f $UNIT && ! -x $UNIT ]]
|
|
then REGRESS=$UNIT
|
|
else REGRESS=${UNIT%.tst}
|
|
REGRESS=$REGRESS.tst
|
|
[[ -f $REGRESS ]] || FATAL $REGRESS: regression tests not found
|
|
fi
|
|
UNIT=${UNIT##*/}
|
|
UNIT=${UNIT%.tst}
|
|
MAIN=$UNIT
|
|
if [[ $VPATH ]]
|
|
then set -A VIEWS ${VPATH//:/' '}
|
|
OFFSET=${SOURCE#${VIEWS[0]}}
|
|
if [[ $OFFSET ]]
|
|
then OFFSET=${OFFSET#/}/
|
|
fi
|
|
fi
|
|
if [[ $REGRESS == */* ]]
|
|
then PREFIX=${REGRESS%/*}
|
|
if [[ ${#VIEWS[@]} ]]
|
|
then for i in ${VIEWS[@]}
|
|
do PREFIX=${PREFIX#$i/}
|
|
done
|
|
fi
|
|
PREFIX=${PREFIX#$OFFSET}
|
|
if [[ $PREFIX ]]
|
|
then PREFIX=$PREFIX/
|
|
fi
|
|
fi
|
|
TWD=$PWD/$UNIT.tmp
|
|
PMP=$(pwd -P)/$UNIT.tmp
|
|
UMASK_ORIG=$(umask)
|
|
UMASK=$UMASK_ORIG
|
|
ARGV=("$@")
|
|
if [[ ${ARGV[0]} && ${ARGV[0]} != [-+]* ]]
|
|
then UNIT "${ARGV[@]}"
|
|
UNIT_READONLY=1
|
|
fi
|
|
trap 'code=$?; CLEANUP $code' EXIT
|
|
if [[ ! $TEST_select ]]
|
|
then TEST_select="[0123456789]*"
|
|
fi
|
|
TEST_select="@($TEST_select|+(0))"
|
|
if [[ $TEST_trace ]]
|
|
then export PS4=':$LINENO: '
|
|
typeset -ft $(typeset +f)
|
|
set -x
|
|
fi
|
|
if [[ $TEST_verbose ]]
|
|
then typeset SHOW
|
|
else typeset -L70 SHOW
|
|
fi
|
|
if [[ ! $TEST_keep ]] && (ulimit -c 0) >/dev/null 2>&1
|
|
then ulimit -c 0
|
|
fi
|
|
set --pipefail
|
|
|
|
# some last minute shenanigans
|
|
|
|
alias BODY='BODY=BODY; function BODY'
|
|
alias CONTINUE='LINE=$LINENO; CONTINUE'
|
|
alias DO='(( $ITEM != $FLUSHED )) && RUN DO; DO &&'
|
|
alias DONE='DONE=DONE; function DONE'
|
|
alias EXEC='LINE=$LINENO; EXEC'
|
|
alias EXITED='LINE=$LINENO; EXITED'
|
|
alias INIT='INIT=INIT; function INIT'
|
|
alias JOB='LINE=$LINENO; JOB'
|
|
alias KILL='LINE=$LINENO; KILL'
|
|
alias PROG='LINE=$LINENO; FLUSH; PROG'
|
|
alias TEST='TESTLINE=$LINENO; TEST'
|
|
alias IF='LINE=$LINENO; FLUSH; IF'
|
|
alias ELIF='LINE=$LINENO; FLUSH; ELIF'
|
|
alias ELSE='LINE=$LINENO; FLUSH; ELSE'
|
|
alias FI='LINE=$LINENO; FLUSH; FI'
|
|
|
|
# do the tests
|
|
|
|
. $REGRESS
|
|
RUN
|
|
GROUP=FINI
|