1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-03-09 15:50:02 +00:00

tests/leaks.sh: redesign with a more robust testing algorithm

On modern operating systems, memory management is non-deterministic
(i.e. random, unpredictable) to varying degrees. This makes testing
for memory leaks a nightmare as the OS may decide to randomly grow
a process's memory allocation at any time for no apparent reason,
causing intermittent test failures that do not represent real
memory leaks. So far, the leaks test tried to cope with this by
using a large number of iterations plus a certain amount of bytes
of tolerance per iteration. This was inefficient and on some
systems still did not fully eliminate intermittent test failures.

This commit introduces a new testing algorithm that is designed to
cope with a large degree of unpredictability. Instead of a fixed
number of test iterations, it defines a maximum (16384), dividing
them in blocks of 128 iterations. It also defines a minimum number
of sequential "good" iteration blocks, counted if memory usage did
not increase from one block to the next. That minimum number is set
to 16. The theory is that if we can get 16 "good" iteration blocks
in a row, we can safely assume it's not a real memory leak, break
the loop, and consider the test succeeded. That "good" sequence is
allowed to occur at any point in the loop, creating a high built-in
tolerance for non-deterministic shenanigans. It also speeds up the
tests, as successful tests can bow out at 16 * 128 == 2048
iterations if they're lucky. If the OS decides to randomly grow the
memory heap, it may take more tries, but almost (?) certainly not
more than the maximum 16384 (128 blocks). If the counter reaches
that, then we assume a memory leak and throw a test failure.

We're also no longer testing with byte granularity in any case; the
randomness of memory management makes that pointless. All getmem()
function versions now return kibibytes (1024 bytes).

This should eliminate the need for workarounds such as initial
iterations to "steady the state" or a tolerance of a certain number
of bytes. I've experimentally determined the exact values
(max_iter, block_iter, min_good_blocks) that seem to work reliably
on all systems I've tested. They are easy to tweak if necessary.

To make all this manageable, this commit hides all the supporting
code in a triplet of aliases (TEST, DO, DONE) that, when used
correctly, create a grammatically robust shell code block: you can
add redirections, pipe into it, etc. as expected. This makes the
actual tests a great deal easier to read as well.

src/cmd/ksh93/tests/pty.sh:
- Implement new leaks testing framework as described and convert
  all the tests to it.
- Mark known leaks with a 'known' variable. Print non-fail warnings
  for all known leaks, but skip the tests by default. Test them
  only if DEBUG is exported. This is better than commenting them
  out as we will no longer be tempted to forget about these.
- Move the test for large command substitutions to subshell.sh --
  it's not in fact a leak test; instead, it checks that command
  substitutions don't lose data.

src/cmd/ksh93/tests/_common: err_exit():
- Since we're printing more warnings, clearly mark all test
  failures with 'FAIL:' to make them stand out.

src/cmd/ksh93/tests/shtests.
src/cmd/ksh93/tests/pty.sh:
- Special-case leaks.sh for counting tests; grep ^TEST.
- Special-case pty.sh as well while we're at it by grepping tst()
  calls. Remove all the dummy '# err_exit #' comments from pty.sh
  as they are now no longer used for counting the tests.
This commit is contained in:
Martijn Dekker 2021-12-28 17:47:29 +00:00
parent a9c6f77c3e
commit db3a3d8fc0
5 changed files with 259 additions and 369 deletions

View file

@ -21,7 +21,7 @@ _message()
}
function err_exit
{
_message "$@"
_message "$1" "FAIL:" "${@:2}"
let Errors+=1
}
alias err_exit='err_exit $LINENO' # inaccurate err_exit name kept for historical integrity :)

View file

@ -16,6 +16,7 @@
# Florham Park NJ #
# #
# David Korn <dgk@research.att.com> #
# Martijn Dekker <martijn@inlv.org> #
# #
########################################################################
@ -27,31 +28,22 @@
if builtin vmstate 2>/dev/null &&
n=$(vmstate --format='%(busy_size)u') &&
let "($n) == ($n) && n > 0" # non-zero number?
then N=512 # number of iterations for each test
unit=bytes
tolerance=$((4*N)) # tolerate 4 bytes per iteration to account for vmalloc artefacts
vmalloc=enabled
function getmem
then vmalloc=enabled
getmem()
{
vmstate --format='%(busy_size)u'
print $(( $(vmstate --format='%(busy_size)u') / 1024 ))
}
# On Linux, we can use /proc to get byte granularity for vsize (field 23).
elif [[ -f /proc/$$/stat && $(uname) == Linux ]]
then N=4096 # number of iterations for each test
unit=bytes
tolerance=$((16*N)) # tolerate 16 bytes per iteration to account for malloc artefacts
function getmem
then getmem()
{
cut -f 23 -d ' ' </proc/$$/stat
print $(( $(cut -f 23 -d ' ' </proc/$$/stat ) / 1024 ))
}
# On UnixWare, read the process virtual size with ps
elif [[ $(uname) == UnixWare ]] &&
n=$(ps -o vsz= -p "$$" 2>/dev/null) &&
let "($n) == ($n) && n > 0"
then N=16384
unit=KiB
tolerance=$((4*N/1024)) # tolerate 4 bytes per iteration to account for malloc artefacts
function getmem
then getmem()
{
ps -o vsz= -p "$$"
}
@ -59,10 +51,7 @@ then N=16384
# of the 'ps' command (the standard 'vsz', virtual size, is not usable).
elif n=$(ps -o rss= -p "$$" 2>/dev/null) &&
let "($n) == ($n) && n > 0"
then N=16384
unit=KiB
tolerance=$((12*N/1024)) # tolerate 12 bytes per iteration to account for malloc/ps artefacts
function getmem
then getmem()
{
ps -o rss= -p "$$"
}
@ -70,79 +59,104 @@ else warning 'cannot find method to measure memory usage; skipping tests'
exit 0
fi
# test for variable reset leak #
# Parameters for test blocks.
# Intended to cope with all non-deterministic OS memory management artefacts.
#
# Theory: if we can get a sequence of $min_good_blocks blocks of $block_iter iterations without the memory state changing,
# then we can safely assume it's not a memory leak, break the loop, and consider the test succeeded. To allow for
# unpredictable OS memory management artefacts, that sequence is allowed to occur anywhere within $max_iter iterations.
# This speeds up the tests, as successful tests can bow out at $((min_good_blocks * block_iter)) iterations if they're
# lucky. If the OS decides to randomly grow the memory heap, it may take more tries, but hopefully not more than
# $max_iter iterations. If the loop counter reaches $max_iter, then we assume a memory leak and throw a test failure.
function test_reset
{
integer i N=$1
typeset -ir max_iter=16384 block_iter=128 min_good_blocks=16
for ((i = 0; i < N; i++))
do u=$i
done
}
# Set up test block construct.
# Known leaks can be marked known=y to turn them into non-fail warnings.
#
# Usage:
# TEST title='description' [ known=y [ url=<issue tracker URL> ] ]
# <optional preparatory commands>
# DO
# <test payload commands>
# DONE
#
# To avoid messing up $LINENO, aliases should not contain newline characters.
# To keep things readable, backslash line continuation is used instead.
# Initialise variables used below to avoid false leaks
before=0 after=0 i=0 u=0
typeset .lt # create lt (leak test) namespace for internal variables
typeset -i .lt.before=0 .lt.after=0 .lt.total=0 .lt.good=0 .lt.i=0 .lt.j=0
alias TEST=\
'for .lt.i in 1; do '\
' unset -v known url; '
# optional preparatory commands go here
alias DO=\
' if [[ -v known && ! -v DEBUG ]]; '\
' then warning "skipping test for known leak \"$title\";" '\
' "export DEBUG=y to test" ${url:+"and help us fix it at: $url"}; '\
' break; '\
' fi; '\
' .lt.before=$(getmem) .lt.good=0 .lt.total=0; '\
' for ((.lt.i = 0; .lt.i < max_iter; .lt.i += block_iter)); '\
' do for ((.lt.j = 0; .lt.j < block_iter; .lt.j++)); '\
' do '
# test payload commands go here
alias DONE=\
' done; '\
' .lt.after=$(getmem); '\
' if ((.lt.after <= .lt.before)); '\
' then ((.lt.good++ == min_good_blocks)) && break; '\
' else ((.lt.good = 0)); '\
' ((.lt.total += (.lt.after - .lt.before))); '\
' .lt.before=$(getmem); '\
' fi; '\
' done; '\
' if ((.lt.i >= max_iter)); '\
' then if [[ -v known ]]; '\
' then err_exit "known leak: $title: leaked approx ${.lt.total} KiB after ${.lt.i} iterations" '\
' ${url:+"-- help us fix it at: $url"}; '\
' else err_exit "$title: leaked approx ${.lt.total} KiB after ${.lt.i} iterations"; '\
' fi; '\
' elif [[ -v known ]]; '\
' then warning "did not detect known leak \"$title\": succeeded after ${.lt.i} iterations)" ${url:+"-- see: $url"}; '\
' elif [[ -v DEBUG ]]; '\
' then _message "$LINENO" "[DEBUG] test \"$title\" succeeded after ${.lt.i} iterations"; '\
' fi; '\
'done'
# ____ Begin memory leak tests ____
# Check results.
# The function has 'err_exit' in the name so that shtests counts each call as a test.
function err_exit_if_leak
{
if ((after > before + tolerance))
then err\_exit "$1" "$2 (leaked approx $((after - before)) $unit after $N iterations)"
fi
}
alias err_exit_if_leak='err_exit_if_leak "$LINENO"'
# one round to get to steady state -- sensitive to -x
test_reset $N
test_reset $N
before=$(getmem)
test_reset $N
after=$(getmem)
err_exit_if_leak "variable value reset memory leak"
# buffer boundary tests
for exp in 65535 65536
do got=$($SHELL -c 'x=$(printf "%.*c" '$exp' x); print ${#x}' 2>&1)
[[ $got == $exp ]] || err_exit "large command substitution failed -- expected $exp, got $got"
done
TEST title='variable value reset'
integer i=0
u=foo
DO
u=$((++i))
DONE
# data for the next two tests...
data="(v=;sid=;di=;hi=;ti='1328244300';lv='o';id='172.3.161.178';var=(k='conn_num._total';u=;fr=;l='Number of Connections';n='22';t='number';))"
read -C stat <<< "$data"
for ((i=0; i < 8; i++)) # steady state first
do print -r -- "$data" | while read -u$n -C stat; do :; done {n}<&0-
done
before=$(getmem)
for ((i=0; i < N; i++))
do print -r -- "$data"
done | while read -u$n -C stat
do :
done {n}<&0-
after=$(getmem)
err_exit_if_leak "memory leak with read -C when deleting compound variable"
# extra 'read's to get to steady state
for ((i=0; i < 10; i++))
do read -C stat <<< "$data"
done
before=$(getmem)
for ((i=0; i < N; i++))
do read -C stat <<< "$data"
done
after=$(getmem)
err_exit_if_leak "memory leak with read -C when using <<<"
while :
do print -r -- "$data"
done | \
TEST title='read -C when deleting compound variable'
DO read -u$n -C stat
DONE {n}<&0-
TEST title='read -C when using <<<'
DO
read -C stat <<< "$data"
DONE
unset data stat
# ======
# Unsetting an associative array shouldn't cause a memory leak
# See https://www.mail-archive.com/ast-users@lists.research.att.com/msg01016.html
typeset -A stuff
before=$(getmem)
for (( i=0; i < N; i++ ))
do
TEST title='unset of associative array'
typeset -A stuff
DO
unset stuff[xyz]
typeset -A stuff[xyz]
stuff[xyz][elem0]="data0"
@ -150,106 +164,82 @@ do
stuff[xyz][elem2]="data2"
stuff[xyz][elem3]="data3"
stuff[xyz][elem4]="data4"
done
unset stuff
after=$(getmem)
err_exit_if_leak 'unset of associative array causes memory leak'
DONE
# https://github.com/ksh93/ksh/issues/94
TEST title='defining associative array in subshell' known=y url=https://github.com/ksh93/ksh/issues/94
DO
(typeset -A foo=([a]=1 [b]=2 [c]=3))
DONE
# ======
# Memory leak when resetting PATH and clearing hash table
# ...steady memory state:
command -v ls >/dev/null # add something to hash table
PATH=/dev/null true # set/restore PATH & clear hash table
# ...test for leak:
before=$(getmem)
for ((i=0; i < N; i++))
do PATH=/dev/null true # set/restore PATH & clear hash table
TEST title='PATH reset before PATH search'
DO
PATH=/dev/null true # set/restore PATH & clear hash table
command -v ls # do PATH search, add to hash table
done >/dev/null
after=$(getmem)
err_exit_if_leak 'memory leak on PATH reset before PATH search'
DONE >/dev/null
# ...test for another leak that only shows up when building with nmake:
before=$(getmem)
for ((i=0; i < N; i++))
do PATH=/dev/null true # set/restore PATH & clear hash table
done >/dev/null
after=$(getmem)
err_exit_if_leak 'memory leak on PATH reset'
TEST title='PATH reset'
DO
PATH=/dev/null true # set/restore PATH & clear hash table
DONE >/dev/null
# ======
# Defining a function in a virtual subshell
# https://github.com/ksh93/ksh/issues/114
unset -f foo
before=$(getmem)
for ((i=0; i < N; i++))
do (function foo { :; }; foo)
done
after=$(getmem)
err_exit_if_leak 'ksh function defined in virtual subshell'
typeset -f foo >/dev/null && err_exit 'ksh function leaks out of subshell'
TEST title='ksh function defined in virtual subshell'
unset -f foo
DO
(function foo { :; }; foo)
DONE
unset -f foo
before=$(getmem)
for ((i=0; i < N; i++))
do (foo() { :; }; foo)
done
after=$(getmem)
err_exit_if_leak 'POSIX function defined in virtual subshell'
typeset -f foo >/dev/null && err_exit 'POSIX function leaks out of subshell'
TEST title='POSIX function defined in virtual subshell'
unset -f foo
DO
(foo() { :; }; foo)
DONE
# Unsetting a function in a virtual subshell
function foo { echo bar; }
before=$(getmem)
for ((i=0; i < N; i++))
do (unset -f foo)
done
after=$(getmem)
err_exit_if_leak 'ksh function unset in virtual subshell'
typeset -f foo >/dev/null || err_exit 'ksh function unset in subshell was unset in main shell'
TEST title='ksh function unset in virtual subshell'
function foo { echo bar; }
DO
(unset -f foo)
DONE
foo() { echo bar; }
before=$(getmem)
for ((i=0; i < N; i++))
do (unset -f foo)
done
after=$(getmem)
err_exit_if_leak 'POSIX function unset in virtual subshell'
typeset -f foo >/dev/null || err_exit 'POSIX function unset in subshell was unset in main shell'
TEST title='POSIX function unset in virtual subshell'
foo() { echo bar; }
DO
(unset -f foo)
DONE
before=$(getmem)
for ((i=0; i < N; i++))
do (function foo { echo baz; }; unset -f foo)
done
after=$(getmem)
err_exit_if_leak 'ksh function defined and unset in virtual subshell'
TEST title='ksh function defined and unset in virtual subshell'
DO
(function foo { echo baz; }; unset -f foo)
DONE
before=$(getmem)
for ((i=0; i < N; i++))
do (foo() { echo baz; }; unset -f foo)
done
after=$(getmem)
err_exit_if_leak 'POSIX function defined and unset in virtual subshell'
TEST title='POSIX function defined and unset in virtual subshell'
DO
(foo() { echo baz; }; unset -f foo)
DONE
# ======
# Sourcing a dot script in a virtual subshell
echo 'echo "$@"' > $tmp/dot.sh
before=$(getmem)
for ((i=0; i < N; i++))
do (. "$tmp/dot.sh" dot one two three >/dev/null)
done
after=$(getmem)
err_exit_if_leak 'script dotted in virtual subshell'
TEST title='script dotted in virtual subshell'
echo 'echo "$@"' > $tmp/dot.sh
DO
(. "$tmp/dot.sh" dot one two three >/dev/null)
DONE
echo 'echo "$@"' > $tmp/dot.sh
before=$(getmem)
for ((i=0; i < N; i++))
do (source "$tmp/dot.sh" source four five six >/dev/null)
done
after=$(getmem)
err_exit_if_leak 'script sourced in virtual subshell'
TEST title='script sourced in virtual subshell'
echo 'echo "$@"' > $tmp/dot.sh
DO
(source "$tmp/dot.sh" source four five six >/dev/null)
DONE
# ======
# Multiple leaks when using arrays in functions (Red Hat #921455)
@ -259,31 +249,27 @@ err_exit_if_leak 'script sourced in virtual subshell'
# after the patch) when run in a non-C locale.
[[ $vmalloc == enabled ]] && saveLANG=$LANG && LANG=C # comment out to test remaining leak (1/2)
function _hash
{
typeset w=([abc]=1 [def]=31534 [xyz]=42)
print -u2 $w 2>&-
# accessing the var will leak
}
before=$(getmem)
for ((i=0; i < N; i++))
do _hash
done
after=$(getmem)
err_exit_if_leak 'associative array in function'
TEST title='associative array in function'
function _hash
{
typeset w=([abc]=1 [def]=31534 [xyz]=42)
print -u2 $w 2>&-
# accessing the var will leak
}
DO
_hash
DONE
function _array
{
typeset w=(1 31534 42)
print -u2 $w 2>&-
# unset w will prevent leak
}
before=$(getmem)
for ((i=0; i < N; i++))
do _array
done
after=$(getmem)
err_exit_if_leak 'indexed array in function'
TEST title='indexed array in function'
function _array
{
typeset w=(1 31534 42)
print -u2 $w 2>&-
# unset w will prevent leak
}
DO
_array
DONE
[[ $vmalloc == enabled ]] && LANG=$saveLANG # comment out to test remaining leak (2/2)
@ -292,175 +278,126 @@ err_exit_if_leak 'indexed array in function'
# Fix based on: https://src.fedoraproject.org/rpms/ksh/blob/642af4d6/f/ksh-20120801-memlik3.patch
# The fix was backported from ksh 93v- beta.
function myFunction
{
typeset toPrint="something"
echo "${toPrint}"
}
state=$(myFunction)
before=$(getmem)
for ((i=0; i < N; i++))
do state=$(myFunction)
done
after=$(getmem)
err_exit_if_leak 'typeset in function called by command substitution'
TEST title='typeset in function called by command substitution'
function myFunction
{
typeset toPrint="something"
echo "${toPrint}"
}
DO
state=$(myFunction)
DONE
# ======
# Check that unsetting an alias frees both the node and its value
before=$(getmem)
for ((i=0; i < N; i++))
do alias "test$i=command$i"
TEST title='unalias'
DO
alias "test$i=command$i"
unalias "test$i"
done
after=$(getmem)
err_exit_if_leak 'unalias'
DONE
# ======
# Red Hat bug rhbz#982142: command substitution leaks
# case1: Nested command substitutions
# (reportedly already fixed in 93u+, but let's keep the test)
before=$(getmem)
for ((i=0; i < N; i++))
do a=`true 1 + \`true 1 + 1\`` # was: a=`expr 1 + \`expr 1 + 1\``
done
after=$(getmem)
err_exit_if_leak 'nested command substitutions'
TEST title='nested command substitutions'
DO
a=`true 1 + \`true 1 + 1\`` # was: a=`expr 1 + \`expr 1 + 1\``
DONE
# case2: Command alias
alias ls='true -ltr' # was: alias ls='ls -ltr'
before=$(getmem)
for ((i=0; i < N; i++))
do eval 'a=`ls`'
done
after=$(getmem)
unalias ls
err_exit_if_leak 'alias in command substitution'
TEST title='alias in command substitution'
alias ls='true -ltr' # was: alias ls='ls -ltr'
DO
eval 'a=`ls`'
DONE
# case3: Function call via autoload
cat >$tmp/func1 <<\EOF
function func1
{
echo "func1 call";
}
EOF
FPATH=$tmp
autoload func1
a=`func1` # steady memory state
before=$(getmem)
for ((i=0; i < N; i++))
do a=`func1`
done
after=$(getmem)
unset -f func1
unset -v FPATH
err_exit_if_leak 'function call via autoload in command substitution'
TEST title='function call via autoload in command substitution'
cat >$tmp/func1 <<-\EOF
function func1
{
echo "func1 call";
}
EOF
FPATH=$tmp
autoload func1
DO
a=`func1`
DONE
# ======
# add some random utilities to the hash table to detect memory leak on hash table reset when changing PATH
random_utils=(chmod cp mv awk sed diff comm cut sort uniq date env find mkdir rmdir pr sleep)
save_PATH=$PATH
hash "${random_utils[@]}"
before=$(getmem)
for ((i=0; i < N; i++))
do hash -r
hash "${random_utils[@]}"
done
after=$(getmem)
err_exit_if_leak 'clear hash table (hash -r) in main shell'
before=$(getmem)
for ((i=0; i < N; i++))
do PATH=/dev/null
TEST title='clear hash table (hash -r) in main shell'
DO
hash -r
hash "${random_utils[@]}"
DONE
TEST title='set PATH value in main shell'
DO
PATH=/dev/null
PATH=$save_PATH
hash "${random_utils[@]}"
done
after=$(getmem)
err_exit_if_leak 'set PATH value in main shell'
DONE
before=$(getmem)
for ((i=0; i < N; i++))
do PATH=/dev/null command true
done
after=$(getmem)
err_exit_if_leak 'run command with preceding PATH assignment in main shell'
TEST title='run command with preceding PATH assignment in main shell'
DO
PATH=/dev/null command true
DONE
: <<'disabled' # TODO: known leak (approx 73552 bytes after 512 iterations)
before=$(getmem)
for ((i=0; i < N; i++))
do typeset -A PATH
TEST title='set PATH attribute in main shell' known=y url=https://github.com/ksh93/ksh/issues/405
DO
typeset -A PATH
unset PATH
PATH=$save_PATH
hash "${random_utils[@]}"
done
after=$(getmem)
err_exit_if_leak 'set PATH attribute in main shell'
disabled
DONE
: <<'disabled' # TODO: known leak (approx 99568 bytes after 512 iterations)
before=$(getmem)
for ((i=0; i < N; i++))
do unset PATH
TEST title='unset PATH in main shell' known=y url=https://github.com/ksh93/ksh/issues/405
DO
unset PATH
PATH=$save_PATH
hash "${random_utils[@]}"
done
after=$(getmem)
err_exit_if_leak 'unset PATH in main shell'
disabled
DONE
hash "${random_utils[@]}"
before=$(getmem)
for ((i=0; i < N; i++))
do (hash -r)
done
after=$(getmem)
err_exit_if_leak 'clear hash table (hash -r) in subshell'
TEST title='clear hash table (hash -r) in subshell'
hash "${random_utils[@]}"
DO
(hash -r)
DONE
: <<'disabled' # TODO: known leak (approx 123520 bytes after 512 iterations)
before=$(getmem)
for ((i=0; i < N; i++))
do (PATH=/dev/null)
done
after=$(getmem)
err_exit_if_leak 'set PATH value in subshell'
disabled
TEST title='set PATH value in subshell' known=y url=https://github.com/ksh93/ksh/issues/405
DO
(PATH=/dev/null)
DONE
: <<'disabled' # TODO: known leak (approx 24544 bytes after 512 iterations)
before=$(getmem)
for ((i=0; i < N; i++))
do (PATH=/dev/null command true)
done
after=$(getmem)
err_exit_if_leak 'run command with preceding PATH assignment in subshell'
disabled
TEST title='run command with preceding PATH assignment in subshell' known=y url=https://github.com/ksh93/ksh/issues/405
DO
(PATH=/dev/null command true)
DONE
: <<'disabled' # TODO: known leak (approx 131200 bytes after 512 iterations)
before=$(getmem)
for ((i=0; i < N; i++))
do (readonly PATH)
done
after=$(getmem)
err_exit_if_leak 'set PATH attribute in subshell'
disabled
TEST title='set PATH attribute in subshell' known=y url=https://github.com/ksh93/ksh/issues/405
DO
(readonly PATH)
DONE
: <<'disabled' # TODO: known leak (approx 229440 bytes after 512 iterations)
before=$(getmem)
for ((i=0; i < N; i++))
do (unset PATH)
done
after=$(getmem)
err_exit_if_leak 'unset PATH in subshell'
disabled
TEST title='unset PATH in subshell' known=y url=https://github.com/ksh93/ksh/issues/405
DO
(unset PATH)
DONE
# ======
# Test for a memory leak after 'cd' (in relation to $PWD and $OLDPWD)
original_pwd=$PWD
before=$(getmem)
for ((i=0; i < N; i++))
do cd /tmp
TEST title='PWD and/or OLDPWD changed by cd'
DO
cd /tmp
cd - > /dev/null
PWD=/foo
OLDPWD=/bar
@ -471,23 +408,14 @@ do cd /tmp
cd - > /dev/null
unset OLDPWD PWD
cd /bin
cd /tmp
done
after=$(getmem)
err_exit_if_leak 'PWD and/or OLDPWD changed by cd'
cd $original_pwd
cd "$tmp"
DONE
# ======
# https://github.com/ksh93/ksh/issues/253#issuecomment-815308466
: <<'disabled' # TODO: known leak(s) (approx 1008 KiB after 16384 iterations)
before=$(getmem)
for ((i=0; i < N; i++))
do
TEST title='variable with discipline function in subshell' known=y url=https://github.com/ksh93/ksh/issues/404
DO
(SECONDS=1; LANG=C)
done
after=$(getmem)
err_exit_if_leak 'Variable with discipline function in subshell causes memory leak'
disabled
DONE
# ======
exit $((Errors<125?Errors:125))

View file

@ -27,8 +27,6 @@
# read the pty manual by running: arch/*/bin/pty --man
#
# Do not globally set the locale; these tests must pass for all locales.
#
# The # err_exit # comments are to enable shtests to count the tests.
# the trickiest part of the tests is avoiding typeahead
# in the pty dialogue
@ -101,7 +99,6 @@ then warning "pty command hangs on $bintrue -- tests skipped"
exit 0
fi
# err_exit #
tst $LINENO <<"!"
L POSIX sh 026(C)
@ -125,7 +122,6 @@ w wait
u (Killed|Done)
!
# err_exit #
tst $LINENO <<"!"
L POSIX sh 028(C)
@ -148,7 +144,6 @@ w wait
u (Killed|Done)
!
# err_exit #
tst $LINENO <<"!"
L POSIX sh 029(C)
@ -171,7 +166,6 @@ w wait
u (Killed|Done)
!
# err_exit #
tst $LINENO <<"!"
L POSIX sh 091(C)
@ -188,7 +182,6 @@ w o
u ^hello\r?\n$
!
# err_exit #
((SHOPT_VSH)) && tst $LINENO <<"!"
L POSIX sh 093(C)
@ -204,7 +197,6 @@ w e
u ^goodbye\r?\n$
!
# err_exit #
((SHOPT_VSH)) && tst $LINENO <<"!"
L POSIX sh 094(C)
@ -221,7 +213,6 @@ u ^hello\r?\n$
if [[ $(id -u) == 0 ]]
then warning "running as root: skipping test POSIX sh 096(C)"
else
# err_exit #
tst $LINENO <<"!"
L POSIX sh 096(C)
@ -251,7 +242,6 @@ r history
!
fi
# err_exit #
tst $LINENO <<"!"
L POSIX sh 097(C)
@ -267,7 +257,6 @@ u ^ok\r?\n$
if [[ $(id -u) == 0 ]]
then warning "running as root: skipping test POSIX sh 099(C)"
else
# err_exit #
tst $LINENO <<"!"
L POSIX sh 099(C)
@ -297,7 +286,6 @@ r history
!
fi
# err_exit #
tst $LINENO <<"!"
L POSIX sh 100(C)
@ -313,7 +301,6 @@ w echo ok
u ^ok\r?\n$
!
# err_exit #
((SHOPT_VSH || SHOPT_ESH)) && tst $LINENO <<"!"
L POSIX sh 101(C)
@ -358,7 +345,6 @@ w echo interrupt=:\cV\cC:
u ^interrupt=:\cC:\r?\n$
!
# err_exit #
tst $LINENO <<"!"
L POSIX sh 104(C)
@ -378,7 +364,6 @@ u ^done\r?\n$
if [[ $(id -u) == 0 ]]
then warning "running as root: skipping test POSIX sh 111(C)"
else
# err_exit #
((SHOPT_VSH)) && tst $LINENO <<"!"
L POSIX sh 111(C)
@ -402,7 +387,6 @@ fi
if [[ $(id -u) == 0 ]]
then warning "running as root: skipping test POSIX sh 251(C)"
else
# err_exit #
((SHOPT_VSH)) && tst $LINENO <<"!"
L POSIX sh 251(C)
@ -461,7 +445,6 @@ u yes-yes
!
disabled
# err_exit #
# Test file name completion in vi mode
if((SHOPT_VSH)); then
mkdir "/tmp/fakehome_$$" && tst $LINENO <<!
@ -478,7 +461,6 @@ u ^/tmp/fakehome_$$/testfile_$$\r?\n$
rm -r "/tmp/fakehome_$$"
fi # SHOPT_VSH
# err_exit #
VISUAL='' tst $LINENO <<"!"
L raw Bourne mode literal tab characters
@ -494,7 +476,6 @@ r ^:test-1: true (/de\tv/nu\tl\tl|/de v/nu l l)\r\n$
p :test-2:
!
# err_exit #
VISUAL='' tst $LINENO <<"!"
L raw Bourne mode backslash handling
@ -511,8 +492,6 @@ w true incorrect\\\cXtrue correct
r ^:test-3: true correct\r\n$
!
# err_exit #
# err_exit #
set --
((SHOPT_VSH)) && set -- "$@" vi
((SHOPT_ESH)) && set -- "$@" emacs gmacs
@ -532,7 +511,6 @@ r ^:test-2: true string\\r\\n$
!
done
# err_exit #
tst $LINENO <<"!"
L notify job state changes
@ -543,7 +521,6 @@ w set -b; sleep .01 &
u Done
!
# err_exit #
# Tests for 'test -t'. These were moved here from bracket.sh because they require a tty.
cat >test_t.sh <<"EOF"
integer n
@ -604,7 +581,6 @@ r ^OK11\r\n$
r ^:test-2:
!
# err_exit #
tst $LINENO <<"!"
L race condition while launching external commands
@ -623,7 +599,6 @@ r ^/dev/null\r\n$
r ^:test-2:
!
# err_exit #
((SHOPT_ESH)) && [[ -o ?backslashctrl ]] && tst $LINENO <<"!"
L nobackslashctrl in emacs
@ -637,7 +612,6 @@ w \cR\\\cH\cH
r ^:test-2: \r\n$
!
# err_exit #
((SHOPT_ESH)) && tst $LINENO <<"!"
L emacs backslash escaping
@ -655,7 +629,6 @@ w true \\\cC
r true \^C
!
# err_exit #
((SHOPT_VSH)) && touch vi_completion_A_file vi_completion_B_file && tst $LINENO <<"!"
L vi filename completion menu
@ -690,7 +663,6 @@ r ^:test-3: ls vi_completion_A_file\r\n$
r ^vi_completion_A_file\r\n$
!
# err_exit #
tst $LINENO <<"!"
L syntax error added to history file
@ -706,7 +678,6 @@ r ^:test-2: fc -lN1\r\n$
r \tdo something\r\n$
!
# err_exit #
tst $LINENO <<"!"
L value of $? after the shell uses a variable with a discipline function
@ -730,7 +701,6 @@ w echo "Exit status is: $?"
u Exit status is: 1
!
# err_exit #
((SHOPT_ESH)) && ((SHOPT_VSH)) && tst $LINENO <<"!"
L crash after switching from emacs to vi mode
@ -751,7 +721,6 @@ r ^:test-2: echo Success\r\n$
r ^Success\r\n$
!
# err_exit #
((SHOPT_VSH || SHOPT_ESH)) && tst $LINENO <<"!"
L value of $? after tilde expansion in tab completion
@ -769,7 +738,6 @@ w echo $? ~\t
u 42 /tmp
!
# err_exit #
((SHOPT_MULTIBYTE && (SHOPT_VSH || SHOPT_ESH))) &&
[[ ${LC_ALL:-${LC_CTYPE:-${LANG:-}}} =~ [Uu][Tt][Ff]-?8 ]] &&
touch $'XXX\xc3\xa1' $'XXX\xc3\xab' &&
@ -783,7 +751,6 @@ w : XX\t
r ^:test-1: : XXX\r\n$
!
# err_exit #
((SHOPT_VSH)) && tst $LINENO <<"!"
L Using b, B, w and W commands in vi mode
# https://github.com/att/ast/issues/1467
@ -797,7 +764,6 @@ r ^:test-2: echo asdf\r\n$
r ^asdf\r\n$
!
# err_exit #
((SHOPT_ESH)) && mkdir -p emacstest/123abc && VISUAL=emacs tst $LINENO <<"!"
L autocomplete stops numeric input
# https://github.com/ksh93/ksh/issues/198
@ -808,7 +774,6 @@ w cd emacste\t123abc
r ^:test-1: cd emacstest/123abc\r\n$
!
# err_exit #
echo '((' >$tmp/synerror
ENV=$tmp/synerror tst $LINENO <<"!"
L syntax error in profile causes exit on startup
@ -822,7 +787,6 @@ r ^:test-1: echo ok\r\n$
r ^ok\r\n$
!
# err_exit #
((SHOPT_VSH)) && tst $LINENO <<"!"
L split on quoted whitespace when extracting words from command history
# https://github.com/ksh93/ksh/pull/291
@ -836,7 +800,6 @@ w :\E_
r ^:test-2: : One\\ "Two Three"\$'Four Five'\.mp3\r\n$
!
# err_exit #
((SHOPT_VSH)) && tst $LINENO <<"!"
L crash when entering comment into history file (vi mode)
# https://github.com/att/ast/issues/798
@ -851,7 +814,6 @@ r \t#foo\r\n$
r \thist -lnN 1\r\n$
!
# err_exit #
((SHOPT_VSH || SHOPT_ESH)) && tst $LINENO <<"!"
L tab completion while expanding ${.sh.*} variables
# https://github.com/att/ast/issues/1461
@ -862,7 +824,6 @@ w test \$\{.sh.level\t
r ^:test-1: test \$\{.sh.level\}\r\n$
!
# err_exit #
((SHOPT_VSH || SHOPT_ESH)) && tst $LINENO <<"!"
L tab completion executes command substitutions
# https://github.com/ksh93/ksh/issues/268
@ -876,7 +837,6 @@ w `echo true`\t
r ^:test-2: `echo true`\r\n$
!
# err_exit #
((SHOPT_ESH)) && VISUAL=emacs tst $LINENO <<"!"
L emacs: keys with repeat parameters repeat extra steps
# https://github.com/ksh93/ksh/issues/292
@ -896,7 +856,6 @@ w : test_string\1\E6\E[C\4
r ^:test-4: : teststring\r\n$
!
# err_exit #
tst $LINENO <<"!"
L crash with KEYBD trap after entering multi-line command substitution
# https://www.mail-archive.com/ast-users@lists.research.att.com/msg00313.html
@ -907,7 +866,6 @@ w true); echo "Exit status is $?"
u Exit status is 0
!
# err_exit #
tst $LINENO <<"!"
L interrupted PS2 discipline function
# https://github.com/ksh93/ksh/issues/347
@ -929,7 +887,6 @@ r > \)
r one two three end
!
# err_exit #
((SHOPT_VSH || SHOPT_ESH)) && tst $LINENO <<"!"
L tab completion of '.' and '..'
# https://github.com/ksh93/ksh/issues/372
@ -948,7 +905,6 @@ w : ..\t
r : \.\./\r\n$
!
# err_exit #
tst $LINENO <<"!"
L Ctrl+C with SIGINT ignored
# https://github.com/ksh93/ksh/issues/343

View file

@ -8,7 +8,7 @@ valgrindflags='--xml=yes --log-file=/dev/null --track-origins=yes --read-var-inf
USAGE=$'
[-s8?
@(#)$Id: shtests (ksh 93u+m) 2021-12-20 $
@(#)$Id: shtests (ksh 93u+m) 2021-12-28 $
]
[-author?David Korn <dgk@research.att.com>]
[-author?Glenn Fowler <gsf@research.att.com>]
@ -344,6 +344,8 @@ do [[ $i == *.sh ]] || i+='.sh'
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

View file

@ -100,6 +100,10 @@ while whence $TEST_notfound >/dev/null 2>&1
do TEST_notfound=notfound-$RANDOM
done
for exp in 65535 65536
do got=$($SHELL -c 'x=$(printf "%.*c" '$exp' x); print ${#x}' 2>&1)
[[ $got == $exp ]] || err_exit "large command substitution failed -- expected $exp, got $got"
done
integer BS=1024 nb=64 ss=60 bs no
for bs in $BS 1