: regress - run regression tests in command.tst
command=regress
case $(getopts '[-][123:xyz]' opt --xyz 2>/dev/null; echo 0$opt) in
0123) USAGE=$'
[-?
@(]
'$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.]
[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]
[v:verbose?List differences between actual (<) and expected (>) output,
errors and exit codes. Also disable long output line truncation.]
[D:debug?Enable debug tracing.]
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 { ... }?Defines the test body; used for complex tests.]
[+CD \adirectory\a?Create and change to working directory for
one test.]
[+CLEANUP \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 \aarg\a ...?Runs the current command under test with
\aarg\a ... appended to the default args.]
[+COPY \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 [ \b1\b | \"\" ]]?No argument or an argument of
\b1\b declares that diagnostics are to expected for the
remainder of the current \bTEST\b; \"\" reverts to the default
state that diagnostics are not expected.]
[+DO \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-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.]
[+EXEC [ \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 previious \bEXEC\b in the current
\bTEST\b group are used, or no arguments if this is the
first \bEXEC\b in the group.]
[+EXIT \astatus\a?The command exit status is expected to match
the pattern \astatus\a.]
[+EXPORT [-]] \aname\a=\avalue\a ...?Export environment
variables for one test.]
[+FATAL \amessage\a ...?\amessage\a is printed on the standard
error and \bregress\b exits with status \b1\b.]
[+IGNORE \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 \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 \adescription\a?\adescription\a is printed on the
standard error.]
[+INITIALIZE?Called by \bregress\b to initialize a each
\bTEST\b group.]
[+INPUT [ \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.]
[+INTRO?Called by \bregress\b to introduce all \bTEST\b
groups.]
[+IO \bINPUT|OUTPUT|ERROR\b [ \b-n\b ]] \afile\a | - \adata\a ...?
Internal support for the \bINPUT\b, \bOUTPUT\b and \bERROR\b
functions.]
[+KEEP \apattern\a ...?The temporary directory is cleared for
each test. Files matching \apattern\a are retained between
tests.]
[+MOVE \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 \acomment\a?\acomment\a is added to the current test
trace output.]
[+OUTPUT [ \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.]
[+PROG \acommand\a [ \aarg\a ... ]]?\acommand\a is run with
optional arguments.]
[+REMOVE \afile\a ...?\afile\a ... are removed after the
current test is done.]
[+RUN?Called by \bregress\b to run the current test.]
[+SAME \anew old\a?\anew\a is expected to be the same as
\aold\a after the current test completes.]
[+SET [\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 \anumber\a [ \adescription\a ... ]]?Define a new test
group labelled \anumber\a with optional \adescripion\a.]
[+TITLE [+]] \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 [ \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 [ \amask\a ]]?Run subsequent tests with \bumask\b(1)
\amask\a. If \amask\a is omitted then the original \bumask\b is
used.]
[+UNIT \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.]
[+VIEW \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 PIPE
EMPTY SAME
}
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
{
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
print -u2 "$msg"
GROUP=INIT
TESTS=0
ERRORS=0
;;
esac
}
function TITLE # text
{
TITLE=$@
}
function CLEANUP # status
{
if [[ ! $TEST_keep && $GROUP!=INIT ]]
then cd $SOURCE
RM "$TWD"
fi
TALLY
exit $1
}
function RUN # [ op ]
{
typeset i
[[ $UMASK != $UMASK_ORIG ]] && umask $UMASK_ORIG
case $GROUP in
INIT) RM "$TWD"
mkdir "$TWD" || FATAL "$TWD": cannot create directory
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
fi
FLUSHED=$ITEM
((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
if [[ $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 [[ ${PIPE[INPUT]} ]]
then if [[ ${PIPE[OUTPUT]} ]]
then if [[ ! $TEST_quiet ]]
then print -nu2 "$LABEL"
fi
cat <$TWD/INPUT | COMMAND "${ARGS[@]}" 2>$TWD/ERROR | cat >$TWD/OUTPUT
RESULTS 'pipe input'
else if [[ ! $TEST_quiet ]]
then print -nu2 "$LABEL"
fi
cat <$TWD/INPUT | COMMAND "${ARGS[@]}" >$TWD/OUTPUT 2>$TWD/ERROR
RESULTS 'pipe io'
fi
elif [[ ${PIPE[OUTPUT]} ]]
then if [[ ! $TEST_quiet ]]
then print -nu2 "$LABEL"
fi
COMMAND "${ARGS[@]}" <$TWD/INPUT 2>$TWD/ERROR | cat >$TWD/OUTPUT
RESULTS 'pipe output'
else if [[ $TEST_regular ]]
then if [[ ! $TEST_quiet ]]
then print -nu2 "$LABEL"
fi
COMMAND "${ARGS[@]}" <$TWD/INPUT >$TWD/OUTPUT 2>$TWD/ERROR
RESULTS
fi
if [[ $TEST_pipe_input ]]
then if [[ ! $TEST_quiet ]]
then print -nu2 "$LABEL"
fi
cat <$TWD/INPUT | COMMAND "${ARGS[@]}" >$TWD/OUTPUT 2>$TWD/ERROR
RESULTS 'pipe input'
fi
if [[ $TEST_pipe_output ]]
then if [[ ! $TEST_quiet ]]
then print -nu2 "$LABEL"
fi
COMMAND "${ARGS[@]}" <$TWD/INPUT 2>$TWD/ERROR | cat >$TWD/OUTPUT
RESULTS 'pipe output'
fi
if [[ $TEST_pipe_io ]]
then if [[ ! $TEST_quiet ]]
then print -nu2 "$LABEL"
fi
cat <$TWD/INPUT | COMMAND "${ARGS[@]}" 2>$TWD/ERROR | cat >$TWD/OUTPUT
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=""
;;
esac
if [[ $COMMAND_ORIG ]]
then COMMAND=$COMMAND_ORIG
COMMAND_ORIG=
ARGS=(${ARGS_ORIG[@]})
fi
}
function DO # cmd ...
{
[[ $GROUP == $TEST_select ]] || return 1
[[ $UMASK != $UMASK_ORIG ]] && umask $UMASK
return 0
}
function UNIT # cmd arg ...
{
typeset cmd=$1
case $cmd in
-) shift
#BUG# ARGV=("${ARGV[@]}" "$@")
set -- "${ARGV[@]}" "$@"
ARGV=("$@")
return
;;
esac
if [[ $UNIT ]]
then set -- "${ARGV[@]}"
case $1 in
"") set -- "$cmd" ;;
[-+]*) set -- "$cmd" "${ARGV[@]}" ;;
esac
UNIT=
fi
COMMAND=$1
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=${TMPDIR:-/tmp}/tst-${TWD%.*}-$$-$RANDOM ;;
/*) TWD=$1 ;;
*) TWD=${TMPDIR:-/tmp}/$1 ;;
esac
}
function TEST # number description arg ...
{
RUN
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 ]]
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 ]]
then INITIALIZE
fi
fi
((SUBTESTS=0))
[[ $TEST == $TEST_select ]]
}
function EXEC # arg ...
{
if [[ $GROUP != $TEST_select ]]
then return
fi
if ((SUBTESTS++))
then RUN
fi
case $# in
0) set -- "${ARGS[@]}" ;;
esac
ITEM=$LINE
NOTE="$(print -r -f '%q ' -- $COMMAND_ORIG "$@")"
ARGS=("$@")
}
function CD
{
RUN
if [[ $GROUP == $TEST_select ]]
then mkdir -p "$@" && cd "$@" || FATAL cannot initialize working directory "$@"
fi
}
function EXPORT
{
typeset x
RUN
if [[ $GROUP != $TEST_select ]]
then return
fi
for x
do EXPORT[EXPORTS++]=$x
done
}
function FLUSH
{
if [[ $GROUP != $TEST_select ]]
then return
fi
if ((SUBTESTS++))
then RUN
fi
}
function PROG # cmd arg ...
{
typeset command args
if [[ $GROUP != $TEST_select ]]
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 pipe x
if [[ $GROUP != $TEST_select ]]
then return
fi
[[ $UMASK != $UMASK_ORIG ]] && umask $UMASK_ORIG
if [[ $1 == PIPE ]]
then pipe=1
shift
fi
op=$1
shift
PIPE[$op]=$pipe
file=$TWD/$op
case $1 in
-x) x=1; shift ;;
esac
case $1 in
-f*|-n) f=$1; shift ;;
esac
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
case $op in
OUTPUT|ERROR)
file=$file.ex
if [[ $file != /* ]]
then file=$TWD/$file
fi
;;
esac
#unset SAME[$op]
SAME[$op]=
RM $TWD/$file.sav
if [[ $file == */* ]]
then mkdir -p ${file%/*}
fi
if [[ $file != */ ]]
then case $#:$f in
0:) : > $file ;;
*:-f) printf -- "$@" > $file ;;
*:-f*) printf -- "${f *) print $f -r -- "$@" > $file ;;
esac
if [[ $x ]]
then chmod +x $file
fi
fi
}
function INPUT {
IO $0 "$@"
}
function COPY {
if [[ $GROUP != $TEST_select ]]
then return
fi
COPY="$COPY $@"
}
function MOVE {
typeset f
if [[ $GROUP != $TEST_select ]]
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 {
typeset i file v
if [[ $GROUP != $TEST_select ]]
then return
fi
case $ 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 {
IO $0 "$@"
}
function ERROR {
IO $0 "$@"
}
function RM {
if [[ ! $TEST_rmu ]]
then chmod -R u+rwx "$@" >/dev/null 2>&1
fi
rm $TEST_rmu $TEST_rmflags "$@"
}
function REMOVE {
typeset i
for i
do RM $i $i.sav
done
}
function IGNORE {
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 {
typeset i
for i
do case $KEEP in
"") KEEP="$i" ;;
*) KEEP="$KEEP|$i" ;;
esac
done
}
function DIAGNOSTICS {
DIAGNOSTICS=${1:-1}
EXIT='*'
}
function IGNORESPACE
{
: ${IGNORESPACE=-b}
}
function EXIT {
EXIT=$1
}
function INFO {
typeset -R15 info=$1
if [[ ! $1 ]]
then info=no
fi
shift
if [[ ! $TEST_quiet ]]
then print -r -u2 "$info " "$@"
fi
}
function COMMAND {
((TESTS++))
case " ${EXPORT[*]}" in
*' 'LC_ALL=*)
;;
*' 'LC_+([A-Z])=*)
EXPORT[EXPORTS++]="LC_ALL="
;;
esac
if [[ $TEST_keep ]]
then (
PS4=''
set -x
print -r -- "${EXPORT[@]}" "PATH=$PATH" $COMMAND "$@"
) 2>&1 >/dev/null |
sed 's,^print -r -- ,,' >$TWD/COMMAND
chmod +x $TWD/COMMAND
fi
[[ $UMASK != $UMASK_ORIG ]] && umask $UMASK
eval "${EXPORT[@]}" PATH='$PATH' '$'COMMAND '"$@"'
STATUS=$?
[[ $UMASK != $UMASK_ORIG ]] && umask $UMASK_ORIG
return $STATUS
}
function RESULTS {
typeset i j k s failed ignore io
if [[ $1 ]]
then io="$1 "
fi
for i in $COMPARE $TWD/OUTPUT $TWD/ERROR
do case " $IGNORE $ignore $MOVE " in
*" $i "*) continue ;;
esac
ignore="$ignore $i"
j=${SAME[${i 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 ]]
then failed=$failed${failed:+,}DIAGNOSTICS
fi
continue
fi
diff $IGNORESPACE $i $j >$i.diff 2>&1
if [[ -s $i.diff ]]
then failed=$failed${failed:+,}${i if [[ $TEST_verbose ]]
then print -u2 " ===" diff $IGNORESPACE ${i cat $i.diff >&2
fi
fi
done
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 {
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 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 {
nameref var=$1
typeset i bwd file pwd view root offset
if [[ $var ]]
then return 0
fi
case $ 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 *) offset=${bwd 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 {
typeset f v
for f
do if VIEW v $f || [[ $PREFIX && $f != /* ]] && VIEW v $PREFIX$f
then . $v
else FATAL $f: not found
fi
done
}
function UMASK {
if (( $ then UMASK=$1
else UMASK=$UMASK_ORIG
fi
}
function PIPE {
IO $0 "$@"
}
integer ERRORS=0 EXPORTS=0 TESTS=0 SUBTESTS=0 LINE=0 ITEM=0 LASTITEM=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
typeset TEST_file TEST_keep TEST_pipe_input TEST_pipe_io TEST_pipe_output
typeset TEST_quiet TEST_regular=1 TEST_rmflags='-rf --' TEST_rmu TEST_select
typeset -A EXPORT SAME VIEWS PIPE READONLY
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
;;
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
;;
v) SET - verbose=$OPTARG
;;
D) SET - trace=$OPTARG
;;
*) GROUP=FINI
exit 2
;;
esac
done
shift $OPTIND-1
case $0) FATAL test unit name omitted ;;
esac
export COLUMNS=80
SOURCE=$PWD
PATH=$SOURCE:${PATHPATH=${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=${UNITUNIT=${UNIT%.tst}
if [[ $VPATH ]]
then set -A VIEWS ${VPATH//:/' '}
OFFSET=${SOURCE if [[ $OFFSET ]]
then OFFSET=${OFFSET fi
fi
if [[ $REGRESS == */* ]]
then PREFIX=${REGRESS%/*}
if [[ ${ then for i in ${VIEWS[@]}
do PREFIX=${PREFIX done
fi
PREFIX=${PREFIX if [[ $PREFIX ]]
then PREFIX=$PREFIX/
fi
fi
TWD=$PWD/$UNIT.tmp
PMP=$(/bin/pwd)/$UNIT.tmp
UMASK_ORIG=$(umask)
UMASK=$UMASK_ORIG
ARGV=("$@")
trap 'RUN; CLEANUP 0' EXIT
trap 'CLEANUP $?' HUP INT PIPE TERM
if [[ ! $TEST_select ]]
then TEST_select="[0123456789]*"
fi
TEST_select="@($TEST_select|+(0))"
if [[ $TEST_trace ]]
then PS4='+$LINENO+ '
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
alias BODY='BODY=BODY; function BODY'
alias DO='(( $ITEM != $FLUSHED )) && RUN DO; DO &&'
alias DONE='DONE=DONE; function DONE'
alias EXEC='LINE=$LINENO; EXEC'
alias INIT='INIT=INIT; function INIT'
alias PROG='LINE=$LINENO; FLUSH; PROG'
. $REGRESS