Skip to content

Commit

Permalink
fix possible false negatives in whence -a/-v regress tests
Browse files Browse the repository at this point in the history
src/cmd/ksh93/tests/builtins.sh:
- Remove redundant extra bincat=$(whence -p cat).
- Move whence -v/-a tests to path.sh.
- Fix 'whence -q' test so errors are counted outside of a subshell.

src/cmd/ksh93/tests/path.sh:
- Add all_paths function that is basically a reimplementation of
  'whence -a -p' in shell. Useful for testing 'whence'.
- Move whence -v/-a tests to here, changing them to use all_paths
  where needed. Also fix the 'whence -a' function autoloading
  regression test to do the same. This fixes the tests for systems
  (such as Slackware) where commands such as 'ls' or 'chmod' have
  more than one path in even the default $PATH.
  • Loading branch information
McDutchie committed Oct 22, 2020
1 parent 5ea8114 commit 02e4f2d
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 93 deletions.
97 changes: 9 additions & 88 deletions src/cmd/ksh93/tests/builtins.sh
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,6 @@ t=$(ulimit -t)
$SHELL 2> /dev/null -c 'cd ""' && err_exit 'cd "" not producing an error'
[[ $($SHELL 2> /dev/null -c 'cd "";print hi') != hi ]] && err_exit 'cd "" should not terminate script'
bincat=$(whence -p cat)
builtin cat
out=$tmp/seq.out
for ((i=1; i<=11; i++)); do print "$i"; done >$out
Expand Down Expand Up @@ -763,84 +762,6 @@ foo=BUG command eval ':'
[[ $foo == BUG ]] && err_exit '`command` fails to disable the special properties of special builtins'
# ======
# whence -a/-v tests
# wrong path to tracked aliases after loading builtin: https://github.com/ksh93/ksh/pull/25
actual=$("$SHELL" -c '
whence chmod >/dev/null # add to hash table (create tracked alias)
builtin chmod
whence -a chmod
')
expect="chmod is a shell builtin
chmod is a tracked alias for $(whence -p chmod)"
[[ $actual == "$expect" ]] || err_exit "'whence -a' does not work correctly with tracked aliases" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
# spurious 'undefined function' message: https://github.com/ksh93/ksh/issues/26
actual=$("$SHELL" -c 'whence -a printf')
expect="printf is a shell builtin
$(whence -a -p printf | sed 's/^/printf is /')"
[[ $actual == "$expect" ]] || err_exit "'whence -a': incorrect output" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
# 'whence -a'/'type -a' failed to list builtin if function exists: https://github.com/ksh93/ksh/issues/83
actual=$(printf() { :; }; whence -a printf)
expect="printf is a function
printf is a shell builtin
$(whence -a -p printf | sed 's/^/printf is /')"
[[ $actual == "$expect" ]] || err_exit "'whence -a': incorrect output for function+builtin" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
actual=$(autoload printf; whence -a printf)
expect="printf is an undefined function
printf is a shell builtin
$(whence -a -p printf | sed 's/^/printf is /')"
[[ $actual == "$expect" ]] || err_exit "'whence -a': incorrect output for autoload+builtin" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
# 'whence -v' canonicalized paths improperly: https://github.com/ksh93/ksh/issues/84
cmdpath=${ whence -p printf; }
actual=$(cd /; whence -v "${cmdpath#/}")
expect="${cmdpath#/} is $cmdpath"
[[ $actual == "$expect" ]] || err_exit "'whence -v': incorrect canonicalization of initial /" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
dotdot=
num=$(set -f; IFS=/; set -- $PWD; echo $#)
for ((i=1; i<num; i++))
do dotdot+='../'
done
actual=$(cd /; whence -v "$dotdot${cmdpath#/}")
expect="$dotdot${cmdpath#/} is $cmdpath"
[[ $actual == "$expect" ]] || err_exit "'whence -v': incorrect canonicalization of pathname containing '..'" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
# even absolute paths should be canonicalized
if [[ -x /usr/bin/env && -d /usr/lib ]] # even NixOS has this...
then expect='/usr/lib/../bin/./env is /usr/bin/env'
actual=$(whence -v /usr/lib/../bin/./env)
[[ $actual == "$expect" ]] || err_exit "'whence -v': incorrect canonicalization of absolute path" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
fi
# whence -v/-a should not autoload functions itself
echo 'ls() { echo "Oops, I'\''m a function!"; }' >$tmp/ls
expect=$'/dev/null\n/dev/null'
actual=$(FPATH=$tmp; ls /dev/null; whence -a ls >/dev/null; ls /dev/null)
[[ $actual == "$expect" ]] || err_exit "'whence -a': mistaken \$FPATH function autoload (non-executable file)" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
chmod +x "$tmp/ls"
actual=$(FPATH=$tmp; ls /dev/null; whence -a ls >/dev/null; ls /dev/null)
[[ $actual == "$expect" ]] || err_exit "'whence -a': mistaken \$FPATH function autoload (executable file)" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
# "tracked aliases" (known on other shells as hash table entries) are really just cached PATH search
# results; they should be reported independently from real aliases, as they're actually completely
# different things, and "tracked aliases" are actually used when bypassing an alias (with e.g. \ls).
expect="ls is an alias for 'echo ALL UR F1LEZ R G0N3'
ls is a tracked alias for ${ whence -p ls; }"
actual=$(hash -r; alias ls='echo ALL UR F1LEZ R G0N3'; hash ls; whence -a ls)
[[ $actual == "$expect" || $actual == "$expect"$'\n'* ]] || err_exit "'whence -a' does not report tracked alias if alias exists" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
# 'whence -f' should ignore functions
foo_bar() { true; }
actual="$(whence -f foo_bar)"
Expand All @@ -852,15 +773,15 @@ type -f foo_bar >/dev/null 2>&1 && err_exit "'type -f' doesn't ignore functions
type -qf foo_bar && err_exit "'type -qf' doesn't ignore functions"
# Test the exit status of 'whence -q'
(
mkdir "$tmp/fakepath"
ln -s "${ whence -p cat ;}" "$tmp/fakepath"
ln -s "${ whence -p ls ;}" "$tmp/fakepath"
PATH="$tmp/fakepath"
whence -q cat nonexist ls && err_exit "'whence -q' has the wrong exit status"
whence -q cat nonexist && err_exit "'whence -q' has the wrong exit status"
whence -q nonexist && err_exit "'whence -q' has the wrong exit status"
)
mkdir "$tmp/fakepath"
ln -s "${ whence -p cat ;}" "$tmp/fakepath/"
ln -s "${ whence -p ls ;}" "$tmp/fakepath/"
save_PATH=$PATH
PATH=$tmp/fakepath
whence -q cat nonexist ls && err_exit "'whence -q' has the wrong exit status"
whence -q cat nonexist && err_exit "'whence -q' has the wrong exit status"
whence -q nonexist && err_exit "'whence -q' has the wrong exit status"
PATH=$save_PATH
# ======
# 'cd ../.foo' should not exclude the '.' in '.foo'
Expand Down
110 changes: 105 additions & 5 deletions src/cmd/ksh93/tests/path.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,32 @@ integer Errors=0
[[ -d $tmp && -w $tmp && $tmp == "$PWD" ]] || { err\_exit "$LINENO" '$tmp not set; run this from shtests. Aborting.'; exit 1; }
PATH_orig=$PATH

# output all paths to a command, skipping duplicates in $PATH
# (reimplementation of 'whence -a -p', useful for testing 'whence')
function all_paths
{
typeset IFS=':' CDPATH='' p seen
set -o noglob
p=$PATH: # IFS field splitting discards a final empty field; add one to avoid that
for p in $p
do if [[ -z $p ]]
then # empty $PATH element == current dir
p='.'
fi
if [[ $p != /* ]]
then # get absolute directory
p=$(cd -L -- "$p" 2>/dev/null && print -r -- "${PWD}X") && p=${p%X} || continue
fi
if [[ :$seen: == *:"$p":* ]]
then continue
fi
if [[ -f $p/$1 && -x $p/$1 ]]
then print -r "$p/$1"
fi
seen=${seen:+$seen:}$p
done
}

type /xxxxxx > out1 2> out2
[[ -s out1 ]] && err_exit 'type should not write on stdout for not found case'
[[ -s out2 ]] || err_exit 'type should write on stderr for not found case'
Expand Down Expand Up @@ -452,6 +478,81 @@ actual=$(PATH=$tmp; redirect 2>&1; hash ls; command -p -v ls)
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"

# ======
# whence -a/-v tests

# wrong path to tracked aliases after loading builtin: https://github.com/ksh93/ksh/pull/25
actual=$("$SHELL" -c '
hash chmod
builtin chmod
whence -a chmod
')
expect=$'chmod is a shell builtin\n'$(all_paths chmod | sed '1 s/^/chmod is a tracked alias for /; 2,$ s/^/chmod is /')
[[ $actual == "$expect" ]] || err_exit "'whence -a' does not work correctly with tracked aliases" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"

# spurious 'undefined function' message: https://github.com/ksh93/ksh/issues/26
actual=$("$SHELL" -c 'whence -a printf')
expect=$'printf is a shell builtin\n'$(all_paths printf | sed 's/^/printf is /')
[[ $actual == "$expect" ]] || err_exit "'whence -a': incorrect output" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"

# 'whence -a'/'type -a' failed to list builtin if function exists: https://github.com/ksh93/ksh/issues/83
actual=$(printf() { :; }; whence -a printf)
expect="printf is a function
printf is a shell builtin
$(all_paths printf | sed 's/^/printf is /')"
[[ $actual == "$expect" ]] || err_exit "'whence -a': incorrect output for function+builtin" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
actual=$(autoload printf; whence -a printf)
expect="printf is an undefined function
printf is a shell builtin
$(all_paths printf | sed 's/^/printf is /')"
[[ $actual == "$expect" ]] || err_exit "'whence -a': incorrect output for autoload+builtin" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"

# 'whence -v' canonicalized paths improperly: https://github.com/ksh93/ksh/issues/84
cmdpath=${ whence -p printf; }
actual=$(cd /; whence -v "${cmdpath#/}")
expect="${cmdpath#/} is $cmdpath"
[[ $actual == "$expect" ]] || err_exit "'whence -v': incorrect canonicalization of initial /" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
dotdot=
num=$(set -f; IFS=/; set -- $PWD; echo $#)
for ((i=1; i<num; i++))
do dotdot+='../'
done
actual=$(cd /; whence -v "$dotdot${cmdpath#/}")
expect="$dotdot${cmdpath#/} is $cmdpath"
[[ $actual == "$expect" ]] || err_exit "'whence -v': incorrect canonicalization of pathname containing '..'" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"

# even absolute paths should be canonicalized
if [[ -x /usr/bin/env && -d /usr/lib ]] # even NixOS has this...
then expect='/usr/lib/../bin/./env is /usr/bin/env'
actual=$(whence -v /usr/lib/../bin/./env)
[[ $actual == "$expect" ]] || err_exit "'whence -v': incorrect canonicalization of absolute path" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
fi

# whence -v/-a should not autoload functions itself
echo 'ls() { echo "Oops, I'\''m a function!"; }' >$tmp/ls
expect=$'/dev/null\n/dev/null'
actual=$(FPATH=$tmp; ls /dev/null; whence -a ls >/dev/null; ls /dev/null)
[[ $actual == "$expect" ]] || err_exit "'whence -a': mistaken \$FPATH function autoload (non-executable file)" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
chmod +x "$tmp/ls"
actual=$(FPATH=$tmp; ls /dev/null; whence -a ls >/dev/null; ls /dev/null)
[[ $actual == "$expect" ]] || err_exit "'whence -a': mistaken \$FPATH function autoload (executable file)" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"

# "tracked aliases" (known on other shells as hash table entries) are really just cached PATH search
# results; they should be reported independently from real aliases, as they're actually completely
# different things, and "tracked aliases" are actually used when bypassing an alias (with e.g. \ls).
expect=$'ls is an alias for \'echo ALL UR F1LEZ R G0N3\'\n'$(all_paths ls|sed '1 s/^/ls is a tracked alias for /;2,$ s/^/ls is /')
actual=$(hash -r; alias ls='echo ALL UR F1LEZ R G0N3'; hash ls; whence -a ls)
[[ $actual == "$expect" ]] || err_exit "'whence -a' does not report tracked alias if alias exists" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"

# Unlike pdksh, ksh93 didn't report the path to autoloadable functions, which was an annoying omission.
if ((.sh.version >= 20200925))
then fundir=$tmp/whencefun
Expand All @@ -460,7 +561,6 @@ then fundir=$tmp/whencefun
echo "whence_autoload_test() { echo I was explicitly autoloaded; }" >$fundir/whence_autoload_test
echo "function chmod { echo Hi, I\'m your new chmod!; }" >$fundir/chmod
echo "function ls { echo Hi, I\'m your new ls!; }" >$fundir/ls
hash -r
actual=$("$SHELL" -c 'FPATH=$1
autoload chmod whence_autoload_test
whence -a chmod whence_FPATH_test whence_autoload_test ls cp
Expand All @@ -471,17 +571,17 @@ then fundir=$tmp/whencefun
chmod --totally-invalid-option' \
whence_autoload_test "$fundir" 2>&1)
expect="chmod is an undefined function (autoload from $fundir/chmod)"
expect+=$'\n'"chmod is ${ whence -p chmod; }"
expect+=$'\n'$(all_paths chmod | sed 's/^/chmod is /')
expect+=$'\n'"whence_FPATH_test is an undefined function (autoload from $fundir/whence_FPATH_test)"
expect+=$'\n'"whence_autoload_test is an undefined function (autoload from $fundir/whence_autoload_test)"
expect+=$'\n'"ls is a tracked alias for ${ whence -p ls; }"
expect+=$'\n'$(all_paths ls | sed '1 s/^/ls is a tracked alias for /; 2,$ s/^/ls is /')
expect+=$'\n'"ls is an undefined function (autoload from $fundir/ls)"
expect+=$'\n'"cp is a tracked alias for ${ whence -p cp; }"
expect+=$'\n'$(all_paths cp | sed '1 s/^/cp is a tracked alias for /; 2,$ s/^/cp is /')
expect+=$'\n'"I'm just on FPATH"
expect+=$'\n'"I was explicitly autoloaded"
expect+=$'\n'"Hi, I'm your new chmod!"
[[ $actual == "$expect" ]] || err_exit "failure in reporting or running autoloadable functions" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
$'-- diff follows:\n'"$(diff -u <(print -r -- "$expect") <(print -r -- "$actual") | sed $'s/^/\t| /')"
fi

# ======
Expand Down

0 comments on commit 02e4f2d

Please sign in to comment.