#!/bin/zsh -f
: ${ZTST_verbose:=0}
emulate -R zsh
[[ -n $LC_ALL ]] && LC_ALL=C
[[ -n $LC_COLLATE ]] && LC_COLLATE=C
[[ -n $LC_NUMERIC ]] && LC_NUMERIC=C
[[ -n $LC_MESSAGES ]] && LC_MESSAGES=C
[[ -n $LANG ]] && LANG=C
typeset +x WORDCHARS
[[ -d Modules/zsh ]] && module_path=( $PWD/Modules )
export MODULE_PATH
zmodload -i zsh/parameter
ZTST_testopts=(${(kv)options})
setopt extendedglob nonomatch
while [[ $1 = [-+]* ]]; do
set $1
shift
done
ZTST_mainopts=(${(kv)options})
ZTST_testdir=$PWD
ZTST_testname=$1
integer ZTST_testfailed
tail() {
emulate -L zsh
if [[ -z $TAIL_SUPPORTS_MINUS_N ]]; then
local test
test=$(echo "foo\nbar" | command tail -n 1 2>/dev/null)
if [[ $test = bar ]]; then
TAIL_SUPPORTS_MINUS_N=1
else
TAIL_SUPPORTS_MINUS_N=0
fi
fi
integer argi=${argv[(i)-<->]}
if [[ $argi -le $ argv[$argi]=(-n ${argv[$argi][2,-1]})
fi
command tail "$argv[@]"
}
if [[ $0 = */* ]]; then
ZTST_srcdir=${0%/*}
else
ZTST_srcdir=$PWD
fi
[[ $ZTST_srcdir = /* ]] || ZTST_srcdir="$ZTST_testdir/$ZTST_srcdir"
fpath=( $ZTST_srcdir/../Functions/*~*/CVS(/)
$ZTST_srcdir/../Completion
$ZTST_srcdir/../Completion/*/*~*/CVS(/) )
: ${TMPPREFIX:=/tmp/zsh}
ZTST_in=${TMPPREFIX}.ztst.in.$$
ZTST_out=${TMPPREFIX}.ztst.out.$$
ZTST_err=${TMPPREFIX}.ztst.err.$$
ZTST_tout=${TMPPREFIX}.ztst.tout.$$
ZTST_terr=${TMPPREFIX}.ztst.terr.$$
ZTST_cleanup() {
cd $ZTST_testdir
rm -rf $ZTST_testdir/dummy.tmp $ZTST_testdir/*.tmp(N) \
${TMPPREFIX}.ztst*$$(N)
}
rm -rf dummy.tmp *.tmp
ZTST_testfailed() {
print -r "Test $ZTST_testname failed: $1"
if [[ -n $ZTST_message ]]; then
print -r "Was testing: $ZTST_message"
fi
print -r "$ZTST_testname: test failed."
if [[ -n $ZTST_failmsg ]]; then
print -r "The following may (or may not) help identifying the cause:
$ZTST_failmsg"
fi
ZTST_testfailed=1
return 1
}
ZTST_verbose() {
local lev=$1
shift
if [[ -n $ZTST_verbose && $ZTST_verbose -ge $lev ]]; then
print -r -u $ZTST_fd -- $*
fi
}
ZTST_hashmark() {
if [[ ZTST_verbose -le 0 && -t $ZTST_fd ]]; then
print -n -u$ZTST_fd -- ${(pl:SECONDS::\ fi
(( SECONDS > COLUMNS+1 && (SECONDS -= COLUMNS) ))
}
if [[ ! -r $ZTST_testname ]]; then
ZTST_testfailed "can't read test file."
exit 1
fi
exec {ZTST_fd}>&1
exec {ZTST_input}<$ZTST_testname
ZTST_curline=''
ZTST_cursect=''
ZTST_getline() {
local IFS=
while true; do
read -u $ZTST_input -r ZTST_curline || return 1
[[ $ZTST_curline == \ done
}
ZTST_getsect() {
local match mbegin mend
while [[ $ZTST_curline != '%'( ZTST_getline || return 1
[[ $ZTST_curline = [[:blank:]] if [[ $ ZTST_testfailed "bad line found before or after section:
$ZTST_curline"
exit 1
fi
done
ZTST_getline
ZTST_cursect=${match[1]}
ZTST_verbose 2 "ZTST_getsect: read section name: $ZTST_cursect"
return 0
}
ZTST_getchunk() {
ZTST_code=''
while [[ $ZTST_curline = [[:blank:]] ZTST_getline || break
done
while [[ $ZTST_curline = [[:blank:]] ZTST_code="${ZTST_code:+${ZTST_code}
}${ZTST_curline}"
ZTST_getline || break
done
ZTST_verbose 2 "ZTST_getchunk: read code chunk:
$ZTST_code"
[[ -n $ZTST_code ]]
}
ZTST_getredir() {
local char=${ZTST_curline[1]} fn
ZTST_redir=${ZTST_curline[2,-1]}
while ZTST_getline; do
[[ $ZTST_curline[1] = $char ]] || break
ZTST_redir="${ZTST_redir}
${ZTST_curline[2,-1]}"
done
ZTST_verbose 2 "ZTST_getredir: read redir for '$char':
$ZTST_redir"
case $char in
('<') fn=$ZTST_in
;;
('>') fn=$ZTST_out
;;
('?') fn=$ZTST_err
;;
(*) ZTST_testfailed "bad redir operator: $char"
return 1
;;
esac
if [[ $ZTST_flags = *q* && $char = '<' ]]; then
print -r -- "${(e)ZTST_redir}" >>$fn
else
print -r -- "$ZTST_redir" >>$fn
fi
return 0
}
ZTST_execchunk() {
options=($ZTST_testopts)
eval "$ZTST_code"
ZTST_status=$?
ZTST_testopts=(${(kv)options[*]})
options=(${ZTST_mainopts[*]})
ZTST_verbose 2 "ZTST_execchunk: status $ZTST_status"
return $ZTST_status
}
ZTST_prepclean() {
while ZTST_getchunk; do
ZTST_execchunk >/dev/null || [[ -n $1 ]] || {
[[ -n "$ZTST_unimplemented" ]] ||
ZTST_testfailed "non-zero status from preparation code:
$ZTST_code" && return 0
}
done
}
ZTST_diff() {
local diff_out diff_ret
diff_out=$(diff "$@")
diff_ret="$?"
if [[ "$diff_ret" != "0" ]]; then
print -r "$diff_out"
fi
return "$diff_ret"
}
ZTST_test() {
local last match mbegin mend found substlines
while true; do
rm -f $ZTST_in $ZTST_out $ZTST_err
touch $ZTST_in $ZTST_out $ZTST_err
ZTST_message=''
ZTST_failmsg=''
found=0
ZTST_verbose 2 "ZTST_test: looking for new test"
while true; do
ZTST_verbose 2 "ZTST_test: examining line:
$ZTST_curline"
case $ZTST_curline in
(%*) if [[ $found = 0 ]]; then
break 2
else
last=1
break
fi
;;
([[:space:]] if [[ $found = 0 ]]; then
ZTST_getline || break 2
continue
else
break
fi
;;
([[:space:]] if [[ $ZTST_curline == ( ZTST_xstatus=$match[1]
ZTST_flags=$match[2]
ZTST_message=${match[3]:+${match[3][2,-1]}}
else
ZTST_testfailed "expecting test status at:
$ZTST_curline"
return 1
fi
ZTST_getline
found=1
;;
('<'*) ZTST_getredir || return 1
found=1
;;
('>'*) ZTST_getredir || return 1
found=1
;;
('?'*) ZTST_getredir || return 1
found=1
;;
('F:'*) ZTST_failmsg="${ZTST_failmsg:+${ZTST_failmsg}
} ${ZTST_curline[3,-1]}"
ZTST_getline
found=1
;;
(*) ZTST_testfailed "bad line in test block:
$ZTST_curline"
return 1
;;
esac
done
if [[ -n $ZTST_code ]]; then
ZTST_hashmark
ZTST_verbose 1 "Running test: $ZTST_message"
ZTST_verbose 2 "ZTST_test: expecting status: $ZTST_xstatus"
ZTST_verbose 2 "Input: $ZTST_in, output: $ZTST_out, error: $ZTST_terr"
ZTST_execchunk <$ZTST_in >$ZTST_tout 2>$ZTST_terr
if [[ $ZTST_xstatus != - && $ZTST_xstatus != $ZTST_status ]]; then
ZTST_testfailed "bad status $ZTST_status, expected $ZTST_xstatus from:
$ZTST_code${$(<$ZTST_terr):+
Error output:
$(<$ZTST_terr)}"
return 1
fi
ZTST_verbose 2 "ZTST_test: test produced standard output:
$(<$ZTST_tout)
ZTST_test: and standard error:
$(<$ZTST_terr)"
if [[ $ZTST_flags = *q* && -s $ZTST_out ]]; then
substlines="$(<$ZTST_out)"
rm -rf $ZTST_out
print -r -- "${(e)substlines}" >$ZTST_out
fi
if [[ $ZTST_flags != *d* ]] && ! ZTST_diff -c $ZTST_out $ZTST_tout; then
ZTST_testfailed "output differs from expected as shown above for:
$ZTST_code${$(<$ZTST_terr):+
Error output:
$(<$ZTST_terr)}"
return 1
fi
if [[ $ZTST_flags = *q* && -s $ZTST_err ]]; then
substlines="$(<$ZTST_err)"
rm -rf $ZTST_err
print -r -- "${(e)substlines}" >$ZTST_err
fi
if [[ $ZTST_flags != *D* ]] && ! ZTST_diff -c $ZTST_err $ZTST_terr; then
ZTST_testfailed "error output differs from expected as shown above for:
$ZTST_code"
return 1
fi
fi
ZTST_verbose 1 "Test successful."
[[ -n $last ]] && break
done
ZTST_verbose 2 "ZTST_test: all tests successful"
ZTST_message=''
}
typeset -A ZTST_sects
ZTST_sects=(prep 0 test 0 clean 0)
print "$ZTST_testname: starting."
ZTST_skipok=
ZTST_unimplemented=
while [[ -z "$ZTST_unimplemented" ]] && ZTST_getsect $ZTST_skipok; do
case $ZTST_cursect in
(prep) if (( ${ZTST_sects[prep]} + ${ZTST_sects[test]} + \
${ZTST_sects[clean]} )); then
ZTST_testfailed "\`prep' section must come first"
exit 1
fi
ZTST_prepclean
ZTST_sects[prep]=1
;;
(test)
if (( ${ZTST_sects[test]} + ${ZTST_sects[clean]} )); then
ZTST_testfailed "bad placement of \`test' section"
exit 1
fi
ZTST_test
(( $? )) && ZTST_skipok=1
ZTST_sects[test]=1
;;
(clean)
if (( ${ZTST_sects[test]} == 0 || ${ZTST_sects[clean]} )); then
ZTST_testfailed "bad use of \`clean' section"
else
ZTST_prepclean 1
ZTST_sects[clean]=1
fi
ZTST_skipok=
;;
*) ZTST_testfailed "bad section name: $ZTST_cursect"
;;
esac
done
if [[ -n "$ZTST_unimplemented" ]]; then
print "$ZTST_testname: skipped ($ZTST_unimplemented)"
ZTST_testfailed=2
elif (( ! $ZTST_testfailed )); then
print "$ZTST_testname: all tests successful."
fi
ZTST_cleanup
exit $(( ZTST_testfailed ))