mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
Notable changes:
src/cmd/ksh93/include/fault.h:
- Get rid of the superflous sh pointer argument in the
sh_pushcontext() and sh_popcontext() macros. (re: 2d3ec8b6)
src/cmd/ksh93/tests/io.sh:
- Tweak a process substitution test to allow up to a second for
unused process substitution processes to disappear. On the Alpine
Linux console (at least the musl libc version), this is needed to
avoid a test failure as long as no GUI is active. As soon as you
start X11, this phenomenon disappears, even on the console. Very
strange, but also very probably not ksh's fault.
src/cmd/ksh93/tests/shtests:
- Instead of just SIGCONT and SIGPIPE, set all signals to default,
just to be sure to avoid spurious test failures due to signals
that were ignored on entry. (It made no difference to the
aforementioned Alpine Linux test failure, so ignored signals had
nothing to do with that -- but still a good idea.)
.github/workflows/ci.yml:
- On the GitHub CI runs, when testing with SHOPTs disabled, disable
SHOPT_SPAWN as well, which tests that everything still works
correctly with the regular fork(2) method.
COPYRIGHT:
- Remove duplicate of BSD license.
474 lines
12 KiB
Text
Executable file
474 lines
12 KiB
Text
Executable file
: ksh regression test harness :
|
|
|
|
command=shtests
|
|
|
|
setslocale='*@(locale).sh'
|
|
timesensitive='*@(options|sigchld|subshell).sh'
|
|
valgrindflags='--xml=yes --log-file=/dev/null --track-origins=yes --read-var-info=yes'
|
|
|
|
USAGE=$'
|
|
[-s8?
|
|
@(#)$Id: shtests (ksh 93u+m) 2021-12-28 $
|
|
]
|
|
[-author?David Korn <dgk@research.att.com>]
|
|
[-author?Glenn Fowler <gsf@research.att.com>]
|
|
[-copyright?(c) 2000-2012 AT&T Intellectual Property]
|
|
[-copyright?(c) 2020-2021 Contributors to https://github.com/ksh93/ksh]
|
|
[-license?http://www.eclipse.org/org/documents/epl-v10.html]
|
|
[+NAME?shtests - ksh regression test harness]
|
|
[+DESCRIPTION?\bshtests\b is the \bksh\b(1) regression test harness for
|
|
\b$SHELL\b or \bksh\b if \bSHELL\b is not defined and exported. If
|
|
none of the \b--posix\b \b--utf8\b \b--compile\b options are specified then
|
|
all three are enabled for most tests, whereas only \b--posix\b \b--utf8\b
|
|
are enabled for the \bpty\b tests for the interactive shell.]
|
|
[+INPUT FILES?\bshtests\b regression test files are shell scripts that
|
|
run in an environment controlled by \bshtests\b. An identification
|
|
message is printed before and after each test on the standard output.
|
|
The default environment settings are:]
|
|
{
|
|
[+unset LANG]
|
|
[+unset LC_ALL]
|
|
[+LC_NUMERIC=C?\b.\b radix point assumed by all test scripts.]
|
|
[+VMALLOC_OPTIONS=abort?\bvmalloc\b(1) arena checking enabled
|
|
with \babort(2)\b on error.]
|
|
}
|
|
[c:compile?Run test scripts using \bshcomp\b(1).]
|
|
[d:debug?Enable \bshtests\b execution trace.]
|
|
[k:keep?Keep temporary files after test run; shtests will report the location.]
|
|
[l:locale?Disable \b--utf8\b and run the \b--posix\b and \b--compile\b
|
|
tests, if enabled, in the locale of the caller. However, for locales
|
|
where \b.\b is not the radix point, \bLC_NUMERIC\b is set to \bC\b
|
|
to avoid invalid regressions.]
|
|
[p:posix?Run the test scripts in the posix/C locale.]
|
|
[t!:time?Include the current date/time in the test identification
|
|
messages.]
|
|
[u:utf8?Run the test scripts in the ast-specific C.UTF-8 locale.]
|
|
[v!:vmalloc_options?Run tests with \bVMALLOC_OPTIONS=abort\b. Test
|
|
script names matching \b'$timesensitive$'\b are run with
|
|
\bVMALLOC_OPTIONS\b unset.]
|
|
[V:valgrind?Set \b--novmalloc_options\b and run the test scripts with
|
|
\bvalgrind\b(1) on \bksh\b. If \b$SHELL-g\b exists and is executable,
|
|
then it is used instead of \b$SHELL\b.]
|
|
[x:trace?Enable script execution trace.]
|
|
|
|
[ test.sh ... ] [ name=value ... ]
|
|
|
|
[+SEE ALSO?\bksh\b(1), \bregress\b(1), \brt\b(1)]
|
|
'
|
|
|
|
function usage
|
|
{
|
|
OPTIND=0
|
|
getopts -a $command "$USAGE" OPT '--??long'
|
|
exit 2
|
|
}
|
|
|
|
function valxml
|
|
{
|
|
typeset state=INIT data dir file fn line what
|
|
integer errors=0
|
|
|
|
#print === $1 ===; cat $1; print === ===
|
|
while read data
|
|
do case $state in
|
|
INIT) case $data in
|
|
'<error>')
|
|
state=ERROR
|
|
;;
|
|
esac
|
|
;;
|
|
ERROR) case $data in
|
|
'<kind>'Leak*'</kind>')
|
|
state=SKIP
|
|
;;
|
|
'<kind>'*'</kind>')
|
|
state=KEEP
|
|
what=UNKNOWN
|
|
;;
|
|
esac
|
|
;;
|
|
FRAME) case $data in
|
|
'<dir>'*'</dir>')
|
|
dir=${data#'<dir>'}
|
|
dir=${dir%'</dir>'}
|
|
;;
|
|
'<file>'*'</file>')
|
|
file=${data#'<file>'}
|
|
file=${file%'</file>'}
|
|
;;
|
|
'<fn>'*'</fn>')
|
|
fn=${data#'<fn>'}
|
|
fn=${fn%'</fn>'}
|
|
;;
|
|
'<line>'*'</line>')
|
|
line=${data#'<line>'}
|
|
line=${line%'</line>'}
|
|
;;
|
|
'</frame>')
|
|
[[ $dir ]] && dir+=/
|
|
dir+=$file
|
|
[[ $dir ]] && dir+=:
|
|
[[ $line ]] && dir+=$line:
|
|
[[ $fn ]] && dir+=$fn
|
|
[[ $dir ]] && echo $'\t '$dir
|
|
state=KEEP
|
|
;;
|
|
esac
|
|
;;
|
|
KEEP) case $data in
|
|
'<auxwhat>'*'</auxwhat>')
|
|
what=${data#'<auxwhat>'}
|
|
what=${what%'</auxwhat>'}
|
|
echo $'\t'"$what"
|
|
;;
|
|
'<frame>')
|
|
state=FRAME
|
|
dir=
|
|
file=
|
|
fn=
|
|
line=
|
|
;;
|
|
'<what>Syscall param mount(type) points to unaddressable byte(s)</what>')
|
|
state=SKIP
|
|
;;
|
|
'<what>'*'</what>')
|
|
(( errors++ ))
|
|
what=${data#'<what>'}
|
|
what=${what%'</what>'}
|
|
echo $'\n\t'"$what"
|
|
;;
|
|
'<xwhat>')
|
|
state=WHAT
|
|
;;
|
|
'</error>')
|
|
state=INIT
|
|
;;
|
|
esac
|
|
;;
|
|
SKIP) case $data in
|
|
'</error>')
|
|
state=INIT
|
|
;;
|
|
esac
|
|
;;
|
|
WHAT) case $data in
|
|
'<text>'*'</text>')
|
|
(( errors++ ))
|
|
what=${data#'<text>'}
|
|
what=${what%'</text>'}
|
|
echo $'\n\t'"$what"
|
|
;;
|
|
'</xwhat>')
|
|
state=KEEP
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|
|
done < "$1"
|
|
(( errors )) && echo
|
|
return $errors
|
|
}
|
|
|
|
command set +o posix 2>/dev/null
|
|
unset DISPLAY FIGNORE HISTFILE POSIXLY_CORRECT _AST_FEATURES
|
|
export ENV=/./dev/null SHTESTS_COMMON=$PWD/_common
|
|
trap + $(kill -l) # unadvertised -- set all signals to SIG_DFL
|
|
|
|
integer compile=-1 posix=-1 utf8=-1
|
|
integer debug=0 keep=0 locale=0 time=1
|
|
typeset vmalloc_options=abort trace= valgrind=
|
|
vmalloc_options= #XXX# until multi-region vmalloc trace fixed #XXX#
|
|
|
|
while getopts -a $command "$USAGE" OPT
|
|
do case $OPT in
|
|
c) if (( $OPTARG ))
|
|
then compile=2
|
|
else compile=0
|
|
fi
|
|
;;
|
|
d) debug=$OPTARG
|
|
;;
|
|
k) keep=$OPTARG
|
|
;;
|
|
l) locale=$OPTARG
|
|
;;
|
|
p) posix=$OPTARG
|
|
;;
|
|
t) time=$OPTARG
|
|
;;
|
|
u) utf8=$OPTARG
|
|
;;
|
|
v) if (( OPTARG ))
|
|
then vmalloc_options=abort
|
|
else vmalloc_options=
|
|
fi
|
|
;;
|
|
V) valgrind="${VALGRIND:-valgrind} ${VALGRINDFLAGS:-$valgrindflags}"
|
|
vmalloc_options=
|
|
;;
|
|
x) trace=-x
|
|
;;
|
|
*) usage
|
|
;;
|
|
esac
|
|
done
|
|
shift $OPTIND-1
|
|
|
|
if (( debug )) || [[ $trace ]]
|
|
then export PS4='+ [${SECONDS:+${SECONDS%????}s|}${.sh.pid:+P${.sh.pid},}${.sh.subshell:+S${.sh.subshell},}${.sh.file:+${.sh.file#${.sh.file%/*/*}/},}${.sh.fun:+${.sh.fun},}${LINENO:+L$LINENO,}e$?] '
|
|
typeset -F SECONDS # show fractional seconds in xtrace output
|
|
if (( debug ))
|
|
then set -x
|
|
fi
|
|
fi
|
|
|
|
while [[ $1 == *=* ]]
|
|
do eval export "$1"
|
|
shift
|
|
done
|
|
|
|
if (( compile <= 0 && posix <= 0 && utf8 <= 0 ))
|
|
then (( compile )) && compile=1
|
|
(( posix )) && posix=1
|
|
(( utf8 )) && utf8=1
|
|
fi
|
|
(( compile < 0 )) && compile=0
|
|
(( posix < 0 )) && posix=0
|
|
(( utf8 < 0 )) && utf8=0
|
|
if (( locale ))
|
|
then utf8=0
|
|
if [[ $LC_ALL ]]
|
|
then export LANG=$LC_ALL
|
|
unset ${!LC_*}
|
|
fi
|
|
if ! let 1.0 2>/dev/null
|
|
then export LC_NUMERIC=C
|
|
fi
|
|
else unset LANG ${!LC_*}
|
|
fi
|
|
if [[ $VMALLOC_OPTIONS ]]
|
|
then vmalloc_options=$VMALLOC_OPTIONS
|
|
else VMALLOC_OPTIONS=$vmalloc_options
|
|
fi
|
|
[[ $VMALLOC_OPTIONS ]] || timesensitive=.
|
|
export PATH PWD SHCOMP SHELL VMALLOC_OPTIONS
|
|
PWD=$(pwd)
|
|
SHELL=${SHELL-ksh}
|
|
case $0 in
|
|
/*) d=$(dirname $0);;
|
|
*/*) d=$PWD/$(dirname $0);;
|
|
*) d=$PWD;;
|
|
esac
|
|
case $SHELL in
|
|
/*) ;;
|
|
*/*) SHELL=$d/$SHELL;;
|
|
*) SHELL=$(whence $SHELL);;
|
|
esac
|
|
PATH=$(
|
|
builtin getconf 2>/dev/null || PATH=/run/current-system/sw/bin:/usr/xpg7/bin:/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin:$PATH
|
|
getconf PATH 2>/dev/null
|
|
) || PATH=/bin:/usr/bin
|
|
if [[ -d /usr/ucb ]]
|
|
then PATH=$PATH:/usr/ucb
|
|
fi
|
|
PATH=$PATH:$d
|
|
if [[ $INSTALLROOT && -r $INSTALLROOT/bin/.paths ]]
|
|
then PATH=$INSTALLROOT/bin:$PATH
|
|
fi
|
|
if [[ ${SHELL%/*} != $INSTALLROOT/bin ]]
|
|
then PATH=${SHELL%/*}:$PATH
|
|
fi
|
|
if [[ ! $SHCOMP ]]
|
|
then s=${SHELL:##*sh}
|
|
s=${SHELL:%/*}/shcomp$s
|
|
if [[ -x $s ]]
|
|
then SHCOMP=$s
|
|
elif [[ -x ${s%-g} ]]
|
|
then SHCOMP=${s%-g}
|
|
else SHCOMP=shcomp
|
|
fi
|
|
fi
|
|
|
|
tmp=$(
|
|
d=${TMPDIR:-/tmp}/ksh93.shtests.$$.${RANDOM:-0}
|
|
mkdir -m700 -- "$d" && CDPATH= cd -P -- "$d" && pwd
|
|
) || {
|
|
echo 'mkdir failed' >&2
|
|
exit 1
|
|
}
|
|
if (( keep ))
|
|
then trap 'printf "\nTemporary files left in: %s\n" "$tmp"' EXIT
|
|
else trap 'cd / && rm -rf "$tmp"' EXIT
|
|
fi
|
|
|
|
# for interactive shell ('ksh -i') tests: avoid affecting ~/.sh_history
|
|
# (some tests must unset or modify $HISTFILE, so set $HOME instead)
|
|
export HOME=$tmp
|
|
|
|
# make the SHOPT_* macros available to the tests as environment variables
|
|
SHOPT()
|
|
{
|
|
[[ $1 == *=* ]] && eval "export SHOPT_${ printf %q "${1%%=*}"; }=${ printf %q "${1#*=}"; }"
|
|
}
|
|
. "${SHOPTFILE:-../SHOPT.sh}"
|
|
unset -f SHOPT
|
|
if (( !SHOPT_MULTIBYTE && utf8 && !posix && !compile ))
|
|
then echo "The -u/--utf8 option is unavailable as SHOPT_MULTIBYTE is turned off in ${SHOPTFILE:-SHOPT.sh}." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if (( compile ))
|
|
then if whence $SHCOMP > /dev/null
|
|
then :
|
|
elif (( compile > 1 ))
|
|
then echo $0: --compile: $SHCOMP not found >&2
|
|
exit 1
|
|
else compile=0
|
|
fi
|
|
fi
|
|
if [[ $valgrind ]]
|
|
then if [[ -x $SHELL-g ]]
|
|
then SHELL=$SHELL-g
|
|
fi
|
|
valxml=$tmp/valgrind.xml
|
|
valgrind+=" --xml-file=$valxml"
|
|
fi
|
|
typeset -A tests
|
|
typeset -i total_e=0
|
|
for i in ${*-*.sh}
|
|
do [[ $i == *.sh ]] || i+='.sh'
|
|
if [[ ! -r $i ]]
|
|
then echo $0: $i: not found >&2
|
|
(( ++total_e ))
|
|
continue
|
|
fi
|
|
t=$( case $i in
|
|
glob.sh) grep -c '^[[:blank:]]*test_[a-z]\{3,\}' $i ;;
|
|
leaks.sh) grep -c ^TEST $i ;;
|
|
pty.sh) grep -c 'tst ' $i ;;
|
|
*) grep -c err_exit $i ;;
|
|
esac )
|
|
tests[$i]=$t
|
|
T=test
|
|
if (( t != 1 ))
|
|
then T=${T}s
|
|
fi
|
|
u=${i##*/}
|
|
u=${u%.sh}
|
|
if [[ $i == $timesensitive ]]
|
|
then VMALLOC_OPTIONS=
|
|
fi
|
|
if (( posix || utf8 ))
|
|
then locales=
|
|
(( posix )) && locales+=" ${LANG:-C}"
|
|
[[ $utf8 == 0 || $i == $setslocale ]] || ((!SHOPT_MULTIBYTE)) || locales+=" C.UTF-8"
|
|
for lang in $locales
|
|
do o=$u
|
|
tmp_s=$tmp/$u.$lang
|
|
mkdir -m700 "$tmp_s" || exit
|
|
cd "$tmp_s" || exit
|
|
if [[ $lang == C ]]
|
|
then lang=
|
|
else o="$o($lang)"
|
|
lang=LANG=$lang
|
|
fi
|
|
echo test $o begins ${time:+"at $(date +%Y-%m-%d+%H:%M:%S)"}
|
|
(
|
|
export ${lang:+"$lang"} "tmp=$tmp_s"
|
|
$valgrind $SHELL $trace "$OLDPWD/$i"
|
|
)
|
|
e=$?
|
|
cd "$OLDPWD" || exit
|
|
if (( !keep ))
|
|
then rm -rf "$tmp_s"
|
|
fi
|
|
if [[ $valgrind ]]
|
|
then valxml $valxml
|
|
(( e += $? ))
|
|
fi
|
|
if (( e > 128 && ++total_e ))
|
|
then E="(killed by SIG$(kill -l "$e"))" # report signal if test crashes
|
|
elif (( e == 1 && ++total_e ))
|
|
then E="1 error"
|
|
else E="$e errors"
|
|
(( total_e += e ))
|
|
fi
|
|
if (( e == 0 ))
|
|
then echo test $o passed ${time:+"at $(date +%Y-%m-%d+%H:%M:%S)"} "[ $t $T $E ]"
|
|
else echo test $o failed ${time:+"at $(date +%Y-%m-%d+%H:%M:%S)"} with exit code $e "[ $t $T $E ]"
|
|
fi
|
|
case $E in
|
|
*INT\)) kill -s INT $$ ;; # if test was ^C'd, don't keep going but pass down SIGINT
|
|
esac
|
|
done
|
|
fi
|
|
# Skip pty tests for shcomp, unless -c was explicitly specified (compile==2)
|
|
if [[ compile -gt 0 && ! ($u == pty && compile -lt 2) ]]
|
|
then c=$tmp/shcomp-$u.ksh
|
|
o="$u(shcomp)"
|
|
echo test $o begins ${time:+"at $(date +%Y-%m-%d+%H:%M:%S)"}
|
|
tmp_s=$tmp/$u.shcomp
|
|
mkdir -m700 "$tmp_s" || exit
|
|
# Must define aliases from _common in script itself so they are expanded at compile time.
|
|
# Replace the first few lines with those to avoid affecting $LINENO; they are comments anyway.
|
|
num_alias=$(grep -c '^alias [[:alnum:]_]\{2,\}=' _common)
|
|
{ grep '^alias [[:alnum:]_]\{2,\}=' _common; sed "1,$num_alias d" "$i"; } >$c.orig
|
|
cd "$tmp_s" || exit
|
|
if "$SHCOMP" "$c.orig" > $c
|
|
then if tmp=$tmp_s $valgrind $SHELL $trace $c
|
|
then echo test $o passed ${time:+"at $(date +%Y-%m-%d+%H:%M:%S)"} "[ $t $T 0 errors ]"
|
|
else e=$?
|
|
if (( e > 128 && ++total_e ))
|
|
then E="(killed by SIG$(kill -l "$e"))" # report signal if test crashes
|
|
elif (( e == 1 && ++total_e ))
|
|
then E="1 error"
|
|
else E="$e errors"
|
|
(( total_e += e ))
|
|
fi
|
|
echo test $o failed ${time:+"at $(date +%Y-%m-%d+%H:%M:%S)"} with exit code $e "[ $t $T $E ]"
|
|
case $E in
|
|
*INT\)) kill -s INT $$ ;; # if test was ^C'd, don't keep going but pass down SIGINT
|
|
esac
|
|
fi
|
|
else e=$?
|
|
(( ++total_e ))
|
|
echo test $o failed to compile ${time:+"at $(date +%Y-%m-%d+%H:%M:%S)"} with exit code $e "[ 1 test 1 error ]"
|
|
fi
|
|
cd "$OLDPWD" || exit
|
|
if (( !keep ))
|
|
then rm -rf "$tmp_s" "$c"
|
|
fi
|
|
if [[ $i == $timesensitive ]]
|
|
then VMALLOC_OPTIONS=$vmalloc_options
|
|
fi
|
|
fi
|
|
done
|
|
print "Total errors: $total_e"
|
|
|
|
# This test harness should continue to work with old and buggy versions of ksh93, so check
|
|
# if we have the 'times' builtin and can use process substitution with output redirection.
|
|
# See: https://github.com/ksh93/ksh/commit/65d363fd
|
|
# https://github.com/ksh93/ksh/issues/2
|
|
if ( ulimit -t unlimited 2> /dev/null # fork to circumvent old ksh bugs
|
|
unalias times
|
|
PATH=/dev/null whence -q times || exit
|
|
cd "$tmp" || exit
|
|
echo ok > >(cat > procsubst_test)
|
|
wait
|
|
[[ $(< procsubst_test) == ok ]]
|
|
) 2>/dev/null
|
|
then # Transforming 'times' output requires a process substitution.
|
|
# A regular 'times | ...' pipeline would cause 'times' to be
|
|
# run in a subshell, which would reset all the times to zero.
|
|
times > >(
|
|
t[0]='CPU time' t[1]='user:' t[2]='system:'
|
|
t[3]='main:'; read t[4] t[5]
|
|
t[6]='tests:'; read t[7] t[8]
|
|
printf '%-8s%12s %12s\n' "${t[@]}"
|
|
)
|
|
wait "$!" # process substitutions are executed asynchronously
|
|
else # No pretty-printing.
|
|
times
|
|
fi
|
|
|
|
exit $((total_e > 125 ? 125 : total_e))
|