Patch from current ksh93 maintainer <https://github.com/ksh93/ksh>. cde/programs/dtksh/ksh93/**: - Upgraded. A load of bugs fixed, some minor features added. See NEWS from 2021-02-01 upwards. cde/programs/dtksh/Makefile.am: - Don't cd into ksh93 any more to invoke the package or shtests scripts; they now automatically find their directories. - Pass $(CFLAGS) to build ksh with optimisation. - Remove -D_std_malloc flag as vmalloc is now deprecated and disabled by default. - Add a 'make check' target to Makefile.am that runs the ksh93 regression tests on dtksh to make sure the additions don't interfere with anything. It skips running the tests with shcomp because CDE doesn't use that. The tests all pass here on Slackware 14.2. :-) cde/programs/dtksh/init.patch: - Removed; I've upstreamed it. It was the only one that wasn't upstreamed yet, and more code cleanups are coming, breaking downstream patches. If something needs updating, just email me a diff. cde/programs/dtksh/dtkcmds.h: - Update the ADDBUILTIN macro to remove the __PROTO__ macro use. The proto(1) tool, responsible for all such pre-C89 K&R C compatibility voodoo, has been removed, so that macro is no longer defined. cde/programs/dtksh/setup.sh: - Workaround script removed. I rewrote 'bin/package flat make' in a way that works correctly and changed Makefile.am to use that instead. Hope this helps. Happy new year.
1467 lines
34 KiB
Bash
1467 lines
34 KiB
Bash
########################################################################
|
|
# #
|
|
# This software is part of the ast package #
|
|
# Copyright (c) 1994-2012 AT&T Intellectual Property #
|
|
# Copyright (c) 2020-2021 Contributors to ksh 93u+m #
|
|
# 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 $
|
|
]
|
|
[-author?Glenn Fowler <gsf@research.att.com>]
|
|
[-copyright?Copyright (c) 1995-2012 AT&T Intellectual Property]
|
|
[-license?http://www.eclipse.org/org/documents/epl-v10.html]
|
|
[+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 \adescription\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%%:?(.)}:/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
|