1265 lines
40 KiB
Bash
1265 lines
40 KiB
Bash
# $NetBSD: t_syntax.sh,v 1.12 2021/11/22 05:07:15 kre Exp $
|
||
#
|
||
# Copyright (c) 2017 The NetBSD Foundation, Inc.
|
||
# All rights reserved.
|
||
#
|
||
# Redistribution and use in source and binary forms, with or without
|
||
# modification, are permitted provided that the following conditions
|
||
# are met:
|
||
# 1. Redistributions of source code must retain the above copyright
|
||
# notice, this list of conditions and the following disclaimer.
|
||
# 2. Redistributions in binary form must reproduce the above copyright
|
||
# notice, this list of conditions and the following disclaimer in the
|
||
# documentation and/or other materials provided with the distribution.
|
||
#
|
||
# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||
# POSSIBILITY OF SUCH DAMAGE.
|
||
#
|
||
: ${TEST_SH:=/bin/sh}
|
||
|
||
# This set of tests verifies various requirementgs relating to correct
|
||
# (and incorrect) syntax of shell input
|
||
#
|
||
# There is no intent in these tests to verify correct operation
|
||
# (though that sometimes cannot be separated from correct parsing.)
|
||
# That is (or should be) verified elsewhere.
|
||
#
|
||
# Also, some very basic syntax is tested in almost every test
|
||
# (they cannot work without basic parsing of elementary commands)
|
||
# so that is also not explicitly tested here.
|
||
#
|
||
# Similarly word expansion, field splitting, redirection, all have
|
||
# tests of their own (though we do test parsing of redirect ops).
|
||
#
|
||
# Note that in order to test the basic facilities, other shell operations
|
||
# are used - a test failure here does not necessarily mean that the
|
||
# operation intended to be tested is faulty, just that something is.
|
||
|
||
atf_test_case a_basic_tokenisation
|
||
a_basic_tokenisation_head() {
|
||
atf_set "descr" "Test the shell correctly finds various tokens"
|
||
}
|
||
a_basic_tokenisation_body() {
|
||
atf_check -s exit:0 -o 'inline:3\n' -e empty ${TEST_SH} -c \
|
||
'set -- a b c; echo $#'
|
||
atf_check -s exit:0 -o 'inline:2\n' -e empty ${TEST_SH} -c \
|
||
'set -- a""b c; echo $#'
|
||
atf_check -s exit:0 -o 'inline:3\n' -e empty ${TEST_SH} -c \
|
||
'set -- a"" b c; echo $#'
|
||
atf_check -s exit:0 -o 'inline:3\n' -e empty ${TEST_SH} -c \
|
||
'set -- ""a b c\;; echo $#'
|
||
|
||
atf_check -s exit:0 -o 'inline:3\n' -e empty ${TEST_SH} -c \
|
||
'set -- set -- c; echo $#'
|
||
atf_check -s exit:0 -o 'inline:1\n' -e empty ${TEST_SH} -c \
|
||
'set --;set -- c; echo $#'
|
||
atf_check -s exit:0 -o 'inline:1\n' -e empty ${TEST_SH} -c \
|
||
'set --&set -- c; echo $#'
|
||
atf_check -s exit:0 -o 'inline:1\n' -e empty ${TEST_SH} -c \
|
||
'set -- a b&&set -- c; echo $#'
|
||
atf_check -s exit:0 -o 'inline:2\n' -e empty ${TEST_SH} -c \
|
||
'set -- a b||set -- c; echo $#'
|
||
}
|
||
|
||
atf_test_case b_comments
|
||
b_comments_head() {
|
||
atf_set "descr" "Test the shell correctly handles comments"
|
||
}
|
||
b_comments_body() {
|
||
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c '#'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c '# exit 1'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c 'true # exit 1'
|
||
atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c 'false # exit 0'
|
||
|
||
atf_check -s exit:0 -o 'inline:foo\n' -e empty ${TEST_SH} -c \
|
||
'echo foo # bar'
|
||
atf_check -s exit:0 -o 'inline:foo # bar\n' -e empty ${TEST_SH} -c \
|
||
'echo foo \# bar'
|
||
atf_check -s exit:0 -o 'inline:foo\n' -e empty ${TEST_SH} -c \
|
||
'echo foo; # echo bar'
|
||
atf_check -s exit:0 -o 'inline:foo#bar\n' -e empty ${TEST_SH} -c \
|
||
'echo foo#bar'
|
||
atf_check -s exit:0 -o 'inline:foo# bar\n' -e empty ${TEST_SH} -c \
|
||
'echo foo# bar'
|
||
atf_check -s exit:0 -o 'inline:foo\n' -e empty ${TEST_SH} -c \
|
||
'x=foo; echo ${x#bar}'
|
||
|
||
atf_check -s exit:0 -o 'inline:#\n' -e empty ${TEST_SH} -c \
|
||
'echo "#"'
|
||
atf_check -s exit:0 -o 'inline:#\n' -e empty ${TEST_SH} -c \
|
||
"echo '#'"
|
||
atf_check -s exit:0 -o 'inline:#\n' -e empty ${TEST_SH} -c \
|
||
'echo \#'
|
||
atf_check -s exit:0 -o 'inline:##\n' -e empty ${TEST_SH} -c \
|
||
'echo "#"#'
|
||
atf_check -s exit:0 -o 'inline:##\n' -e empty ${TEST_SH} -c \
|
||
"echo '#'#"
|
||
atf_check -s exit:0 -o 'inline:##\n' -e empty ${TEST_SH} -c \
|
||
'echo \##'
|
||
atf_check -s exit:0 -o 'inline:##\n' -e empty ${TEST_SH} -c \
|
||
'echo "#"# #"#"'
|
||
atf_check -s exit:0 -o 'inline:##\n' -e empty ${TEST_SH} -c \
|
||
"echo '#'# #'#'"
|
||
atf_check -s exit:0 -o 'inline:##\n' -e empty ${TEST_SH} -c \
|
||
'echo \## #\#'
|
||
|
||
cat <<-'DONE' |
|
||
# test comments do not provoke synax errors !\
|
||
echo foo # ( { " hello
|
||
while : # that's forever
|
||
do # the following command list
|
||
# starting with nothing ${unset?error}
|
||
break # done loop terminate $( echo bar; exit 1 )
|
||
done #####################################################
|
||
# "hello
|
||
exit 0
|
||
DONE
|
||
atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} ||
|
||
atf_fail "ignoring comments"
|
||
}
|
||
|
||
atf_test_case c_line_wrapping
|
||
c_line_wrapping_head() {
|
||
atf_set "descr" "check aspects of command line wrapping"
|
||
}
|
||
c_line_wrapping_body() {
|
||
atf_require_prog ls
|
||
atf_require_prog printf
|
||
|
||
cat <<- 'DONE' | atf_check -s exit:0 -o ignore -e empty ${TEST_SH} -e ||
|
||
l\
|
||
s
|
||
DONE
|
||
atf_fail "#1: ls wrapped fails"
|
||
|
||
cat <<- 'DONE' | atf_check -s exit:7 -o empty -e empty ${TEST_SH} ||
|
||
e\
|
||
x\
|
||
it \
|
||
7
|
||
DONE
|
||
atf_fail "#2: exit7 wrapped fails"
|
||
|
||
# Have to do this twice as cannot say "any exit code but 0 or 7" ...
|
||
cat <<- 'DONE' | atf_check -s not-exit:0 -o empty -e not-empty \
|
||
${TEST_SH} ||
|
||
e\
|
||
x\
|
||
it\
|
||
7
|
||
DONE
|
||
atf_fail "#3a: !exit(0||7) badly wrapped fails (0)"
|
||
cat <<- 'DONE' | atf_check -s not-exit:7 -o empty -e not-empty \
|
||
${TEST_SH} ||
|
||
e\
|
||
x\
|
||
it\
|
||
7
|
||
DONE
|
||
atf_fail "#3b: !exit(0||7) badly wrapped fails (7)"
|
||
|
||
cat <<- 'DONE' | atf_check -s exit:0 -o empty -e empty ${TEST_SH} ||
|
||
wh\
|
||
il\
|
||
e \
|
||
f\a\
|
||
\l\s\e
|
||
do
|
||
:\
|
||
;
|
||
done
|
||
DONE
|
||
atf_fail "#4: wrapped while fails"
|
||
|
||
cat <<- 'DONE' | atf_check -s exit:0 -o inline:'hellohellohellohello' \
|
||
-e empty ${TEST_SH} ||
|
||
V\
|
||
AR=hel\
|
||
lo
|
||
unset U V1
|
||
pri\
|
||
ntf '%s' ${\
|
||
VAR\
|
||
}
|
||
p\
|
||
r\
|
||
i\
|
||
n\
|
||
t\
|
||
f\
|
||
\
|
||
'%s' \
|
||
$\
|
||
{\
|
||
V\
|
||
A\
|
||
R}
|
||
printf '%s' ${U\
|
||
-\
|
||
"$\
|
||
{V\
|
||
1:\
|
||
=$\
|
||
{V\
|
||
AR+\
|
||
${V\
|
||
AR}\
|
||
}\
|
||
}"}
|
||
printf '%s' ${V\
|
||
1?V1\
|
||
\
|
||
FAIL}
|
||
DONE
|
||
atf_fail "#5: wrapped var expansions fails"
|
||
|
||
cat <<- 'DONE' | atf_check -s exit:0 -o inline:'2\n' ${TEST_SH} ||
|
||
l\
|
||
s=7 bi\
|
||
n\
|
||
=\
|
||
3
|
||
echo $(\
|
||
( ls /bin )\
|
||
)
|
||
DONE
|
||
atf_fail "#6: wrapped command substitution fails"
|
||
|
||
# Inspired by src/etc/MAKEDEV.tmpl failure with (broken)
|
||
# sh LINENO code... avoid it happening again...
|
||
for VARS in 1:0:0:0 0:1:0:0 0:0:1:0 0:0:0:1 \
|
||
1:0:0:1 1:0:1:0 1:1:0:0 0:1:1:0 \
|
||
0:0:0:0 1:1:0:1 0:1:1:1 1:1:1:1
|
||
do
|
||
eval $(
|
||
IFS=:
|
||
set -- $VARS
|
||
test $(( $1 + $2 + $3 + $4 )) -eq 1 &&
|
||
R=OK || R=BAD
|
||
printf "R=%s;" $R
|
||
for v in a b c d
|
||
do
|
||
case $1 in
|
||
(0) printf "export %s=false;" $v;;
|
||
(1) printf "export %s=true;" $v;;
|
||
esac
|
||
shift
|
||
done
|
||
)
|
||
|
||
cat <<- 'DONE' |
|
||
case $(( $($a && echo 1 || echo 0) + \
|
||
$($b && echo 1 || echo 0) + \
|
||
$($c && echo 1 || echo 0) + \
|
||
$($d && echo 1 || echo 0) ))
|
||
in
|
||
(1) printf OK ;;
|
||
(*) printf BAD ;;
|
||
esac
|
||
DONE
|
||
atf_check -s exit:0 -o inline:"${R}" ${TEST_SH} ||
|
||
atf_fail "#7 (${VARS}): wrapped arith fails"
|
||
done
|
||
|
||
# inspired by pkgsrc/pkgtools/cwrappers :: libnbcompat/configure
|
||
# failure with (broken) sh LINENO code .. avoid recurrence
|
||
# This test would have failed.
|
||
cat <<- 'DONE' | atf_check -s exit:0 -o inline:'/tmp\n' ${TEST_SH} ||
|
||
dn=/tmp/foo
|
||
|
||
D=`dirname -- "${dn}" ||
|
||
expr X"${dn}" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
|
||
X"${dn}" : 'X\(//\)[^/]' \| \
|
||
X"${dn}" : 'X\(//\)$' \| \
|
||
X"${dn}" : 'X\(/\)' \| . 2>/dev/null ||
|
||
echo X"${dn}" |
|
||
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
|
||
s//\1/
|
||
q
|
||
}
|
||
/^X\(\/\/\)[^/].*/{
|
||
s//\1/
|
||
q
|
||
}
|
||
/^X\(\/\/\)$/{
|
||
s//\1/
|
||
q
|
||
}
|
||
/^X\(\/\).*/{
|
||
s//\1/
|
||
q
|
||
}
|
||
s/.*/./; q'`
|
||
|
||
echo "${D}"
|
||
DONE
|
||
atf_fail "#8: cwrappers/LINENO bug test failed"
|
||
|
||
return 0
|
||
}
|
||
|
||
atf_test_case d_cstrings
|
||
d_cstrings_head() {
|
||
atf_set "descr" "Check processing of $' ' quoting (C style strings)"
|
||
}
|
||
d_cstrings_body() {
|
||
unset ENV
|
||
|
||
if ! ${TEST_SH} -c ": \$'abc'" ||
|
||
test $( ${TEST_SH} -c "printf %s \$'abc'" ) != abc
|
||
then
|
||
atf_skip "\$'...' (C style quoted strings) not supported"
|
||
fi
|
||
|
||
# simple stuff
|
||
atf_check -s exit:0 -e empty -o inline:'abc\tdef\n' ${TEST_SH} -c \
|
||
"printf '%s\\n' \$'abc\tdef'"
|
||
atf_check -s exit:0 -e empty -o inline:'abc\tdef\n' ${TEST_SH} -c \
|
||
"printf '%s\\n' \$'abc\011def'"
|
||
atf_check -s exit:0 -e empty -o inline:'abc\tdef\n' ${TEST_SH} -c \
|
||
"printf '%s\\n' \$'abc\x09'def"
|
||
atf_check -s exit:0 -e empty -o inline:'abc$def\n' ${TEST_SH} -c \
|
||
"def=xyz; printf '%s\\n' \$'abc\$def'"
|
||
|
||
# control chars (\c) and unicode \u
|
||
atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
|
||
"test \$'\\1-\\2-\\3' = \$'\\ca-\\cb-\\cc'"
|
||
atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
|
||
"test \$'\\r-\\n-\\f' = \$'\\cm-\\cj-\\cl'"
|
||
atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
|
||
"unset LC_ALL; export LC_CTYPE=en_AU.UTF-8;
|
||
test \$'\\u0123' = \$'\\304\\243'"
|
||
atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
|
||
"test \$'\\u0123' = \$'\\xC4\\xA3'"
|
||
atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
|
||
"test \$'\\c\\\\' = \$'\\x1C'"
|
||
atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
|
||
"test \$'\\c[\\c]\\c^\\c_\\c?' = \$'\\x1B\\x1D\\x1E\\x1F\\x7F'"
|
||
|
||
# all the \X sequences for a single char X (ie: not hex/octal/unicode)
|
||
atf_check -s exit:0 -e empty -o inline:'\n\r\t\n' \
|
||
${TEST_SH} -c "printf '%s\\n' \$'\\a\\b\\e\\f\\n\\r\\t\\v'"
|
||
atf_check -s exit:0 -e empty -o inline:'\n\r\t\n' \
|
||
${TEST_SH} -c "printf '%s\\n' \$'\\cG\\cH\\x1b\\cl\\cJ\\cm\\cI\\ck'"
|
||
atf_check -s exit:0 -e empty -o inline:"'"'"\\\n' \
|
||
${TEST_SH} -c "printf '%s\\n' \$'\\'\\\"\\\\'"
|
||
|
||
# various invalid $'...' sequences
|
||
atf_check -s not-exit:0 -e not-empty -o ignore ${TEST_SH} -c \
|
||
": \$'\\q'"
|
||
atf_check -s not-exit:0 -e not-empty -o ignore ${TEST_SH} -c \
|
||
": \$'\\c\\q'"
|
||
atf_check -s not-exit:0 -e not-empty -o ignore ${TEST_SH} -c \
|
||
": \$'\\uDEFF'"
|
||
atf_check -s not-exit:0 -e not-empty -o ignore ${TEST_SH} -c \
|
||
": \$'abcd"
|
||
atf_check -s not-exit:0 -e not-empty -o ignore ${TEST_SH} -c \
|
||
": \$'abcd\\"
|
||
|
||
# anything that generates \0 ends the $'...' immediately
|
||
atf_check -s exit:0 -e empty -o inline:'aAaA' ${TEST_SH} -c \
|
||
"printf '%s' \$'a\\0x'\$'A\\x00X'\$'a\\c@x'\$'A\\u0000X'"
|
||
|
||
# \newline in a $'...' is dropped (just like in "" strings)
|
||
atf_check -s exit:0 -e empty -o inline:'abcdef' ${TEST_SH} -c \
|
||
"printf '%s' \$'abc\\
|
||
def'"
|
||
# but a normal newline in a $'...' is just a newline
|
||
atf_check -s exit:0 -e empty -o inline:'abc\ndef' ${TEST_SH} -c \
|
||
"printf '%s' \$'abc
|
||
def'"
|
||
# and should work when elided line wrap occurs between $ and '
|
||
atf_check -s exit:0 -e empty -o inline:'abc\ndef' ${TEST_SH} -c \
|
||
"printf '%s' \$\\
|
||
'abc\\ndef'"
|
||
|
||
# $'...' only works when the $ is unquoted.
|
||
atf_check -s exit:0 -e empty -o inline:"abc\$'def'g" ${TEST_SH} -c \
|
||
"printf '%s' \"abc\$'def'g\""
|
||
atf_check -s exit:0 -e empty -o inline:'abc$defg' ${TEST_SH} -c \
|
||
"printf '%s' abc\\\$'def'g"
|
||
atf_check -s exit:0 -e empty -o inline:'abc$def' ${TEST_SH} -c \
|
||
"printf '%s' abc'\$'def"
|
||
}
|
||
|
||
atf_test_case f_redirects
|
||
f_redirects_head() {
|
||
atf_set "descr" "Check parsing of redirect operators"
|
||
}
|
||
f_redirects_body() {
|
||
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'>/dev/null'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'</dev/null'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'>>/dev/null'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'<>/dev/null'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'</dev/null>/dev/null'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'>|/dev/null'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'>/dev/null>/dev/null>/dev/null'
|
||
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'echo hello >/dev/null'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'echo >/dev/null hello'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'>/dev/null echo hello'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'echo hello >/dev/null world'
|
||
atf_check -s exit:0 -o 'inline:hello world\n' -e empty ${TEST_SH} -c \
|
||
'echo hello </dev/null world'
|
||
|
||
atf_check -s exit:0 -o 'inline:hello\n' -e empty ${TEST_SH} -c \
|
||
'echo hello </dev/null'
|
||
atf_check -s exit:0 -o 'inline:hello\n' -e empty ${TEST_SH} -c \
|
||
'echo hello 3</dev/null'
|
||
atf_check -s exit:0 -o 'inline:hello 3\n' -e empty ${TEST_SH} -c \
|
||
'echo hello 3 </dev/null'
|
||
atf_check -s exit:0 -o 'inline:hello 3\n' -e empty ${TEST_SH} -c \
|
||
'echo hello \3</dev/null'
|
||
atf_check -s exit:0 -o 'inline:hello\n' -e empty ${TEST_SH} -c \
|
||
'echo hello</dev/null'
|
||
atf_check -s exit:0 -o 'inline:3\n' -e empty ${TEST_SH} -c \
|
||
'hello=3; echo ${hello}</dev/null'
|
||
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'2>&1'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'2>& 1'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'FD=1; 2>&"${FD}"'
|
||
atf_check -s exit:0 -o 'inline:hello\n' -e empty ${TEST_SH} -c \
|
||
'FD=1; echo hello 2>&"${FD}" >&2'
|
||
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'2>&- 3<&- 4>&-'
|
||
|
||
return 0
|
||
}
|
||
|
||
atf_test_case g_variable_syntax
|
||
g_variable_syntax_head() {
|
||
atf_set "descr" "Check that var names of all legal forms work"
|
||
}
|
||
g_variable_syntax_body() {
|
||
# don't test _ as a variable, it can be "unusual"
|
||
for vname in a ab _a _9 a123 a_1_2_3 __ ___ ____ __1__ _0 \
|
||
A AA AAA AaBb _A_a A_a_ a1_ abc_123 ab_12_cd_ef_34_99 \
|
||
abcdefghijklmnopqrstuvwzyz ABCDEFGHIJKLMNOPQRSTUVWXYZ_ \
|
||
A_VERY_LONG_VARIABLE_NAME_that_is_probably_longer_than_most_used_in_the_average_shell_script_already_about_100_chars_in_this_one_but_there_is_not_supposed_to_be_any_limit_on_the_length_at_all \
|
||
Then_Make_it_Even_Longer_by_Multiplying_it___A_VERY_LONG_VARIABLE_NAME_that_is_probably_longer_than_most_used_in_the_average_shell_script_already_about_100_chars_in_this_one_but_there_is_not_supposed_to_be_any_limit_on_the_length_at_all__A_VERY_LONG_VARIABLE_NAME_that_is_probably_longer_than_most_used_in_the_average_shell_script_already_about_100_chars_in_this_one_but_there_is_not_supposed_to_be_any_limit_on_the_length_at_all__A_VERY_LONG_VARIABLE_NAME_that_is_probably_longer_than_most_used_in_the_average_shell_script_already_about_100_chars_in_this_one_but_there_is_not_supposed_to_be_any_limit_on_the_length_at_all__A_VERY_LONG_VARIABLE_NAME_that_is_probably_longer_than_most_used_in_the_average_shell_script_already_about_100_chars_in_this_one_but_there_is_not_supposed_to_be_any_limit_on_the_length_at_all \
|
||
xyzzy __0123454321__ _0_1_2_3_4_5_6_7_8_9_ ABBA X_ Y__ Z___ \
|
||
_____________________________________________________________
|
||
do
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
"unset ${vname}"
|
||
atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -c \
|
||
"unset ${vname}; printf %s \${${vname}-OK}"
|
||
atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
|
||
"${vname}=GOOD; printf %s \${${vname}-OK}"
|
||
atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
|
||
"${vname}=GOOD; printf %s \$${vname}"
|
||
atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
|
||
"unset ${vname};${vname}=GOOD;printf %s \${${vname}-OK}"
|
||
atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -c \
|
||
"${vname}=GOOD;unset ${vname};printf %s \${${vname}-OK}"
|
||
atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
|
||
"${vname}=GOOD; unset ${vname}x; printf %s \$${vname}"
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
"unset ${vname}x; ${vname}=GOOD; printf %s \$${vname}x"
|
||
atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
|
||
"${vname}=GOOD; ${vname}_=BAD; printf %s \$${vname}"
|
||
|
||
case "${vname}" in
|
||
?) continue;;
|
||
esac
|
||
|
||
# The following tests do not work for 1 char var names.
|
||
# hence the check and "continue" above to skip the remaining
|
||
# tests for that case
|
||
|
||
atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
|
||
"${vname}=GOOD; unset ${vname%?}; printf %s \$${vname}"
|
||
|
||
# (this next would work, but becomes just a duplicate of
|
||
# an earlier test, so is pointless for 1 ch names)
|
||
atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
|
||
"${vname}=GOOD; unset ${vname}x ${vname%?}; printf %s \$${vname}"
|
||
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
"unset ${vname%?};${vname}=GOOD; printf %s \$${vname%?}"
|
||
|
||
atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
|
||
"${vname}=GOOD; ${vname%?}=BAD; printf %s \$${vname}"
|
||
|
||
# all the remaining tests require the 2nd char of the
|
||
# variable name to be a legal first character. That
|
||
# is, not a digit, so skip the rest if we have a digit
|
||
# second...
|
||
case "${vname}" in
|
||
?[0-9]*) continue;;
|
||
esac
|
||
|
||
atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
|
||
"${vname}=GOOD; unset ${vname#?}; printf %s \$${vname}"
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
"unset ${vname#?};${vname}=GOOD; printf %s \$${vname#?}"
|
||
|
||
atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
|
||
"${vname}=GOOD; ${vname#?}=BAD; printf %s \$${vname}"
|
||
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
"unset ${vname%?} ${vname#?} ${vname}x; ${vname}=GOOD;
|
||
printf %s \$${vname%?}\$${vname#?}\$${vname}x"
|
||
|
||
atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
|
||
"${vname}=GOOD; ${vname%?}=BAD; ${vname}_=BAD;
|
||
${vname#?}=BAD; printf %s \$${vname}"
|
||
done
|
||
|
||
# don't test '.' in var names, some shells permit that (in ${} anyway)
|
||
# this test also cannot check for embedded - + ? = : % or #
|
||
for vname in ,x -p +h :def 0_1 'x*x' '()' '"' "'" 'a b c' '?!' ';'
|
||
do
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
"echo \${${vname}}"
|
||
done
|
||
|
||
for vname in ,x -p +h :def 0_1 'x*x' '()' '"' "'" 'a b c' x,y,z '?!' \
|
||
';' a-b a+b 'a?b' 'a:b' 'a%b' 'a#b' 0 1 99 @ '*' '!' '?'
|
||
do
|
||
# failure modes will differ, but they should all fail somehow
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
"${vname}="
|
||
done
|
||
|
||
}
|
||
|
||
atf_test_case h_var_assign
|
||
h_var_assign_head() {
|
||
atf_set "descr" "Check var assignments "
|
||
}
|
||
h_var_assign_body() {
|
||
atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
|
||
'a=b'
|
||
atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
|
||
'\a=b'
|
||
atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
|
||
'a=b c=d'
|
||
atf_check -s exit:0 -e empty -o 'inline:e=f\n' ${TEST_SH} -c \
|
||
'a=b c=d echo e=f'
|
||
atf_check -s exit:0 -e empty -o 'inline:e=f\n' ${TEST_SH} -c \
|
||
'a=b 2>/dev/null c=d </dev/null echo e=f'
|
||
|
||
# We need to make sure that we do not accidentally
|
||
# find a command called 'a=b' ...
|
||
|
||
for d in /foo /foo/bar /not-dir /no/such/directory '/!!!' \
|
||
'/-/--/#' '/"/""/"""' -
|
||
do
|
||
test -d "${d}" || break
|
||
done
|
||
test "${#d}" -gt 1 || atf-skip 'Wacky directories seem to exist!'
|
||
|
||
atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
|
||
"PATH='${d}';"'a=\b'
|
||
atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
|
||
"PATH='${d}';"'a\=b'
|
||
atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
|
||
"PATH='${d}';"'\a=b'
|
||
atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
|
||
"PATH='${d}';"'X=; c=d ${X} a=b'
|
||
}
|
||
|
||
atf_test_case i_pipelines
|
||
i_pipelines_head() {
|
||
atf_set "descr" "Check pipelines"
|
||
}
|
||
i_pipelines_body() {
|
||
|
||
cmd='printf "%s\n" foo'
|
||
for n in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
||
do
|
||
atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
|
||
"${cmd}"
|
||
cmd="${cmd} | cat"
|
||
done
|
||
|
||
cmd='printf "%s\n" foo'
|
||
for n in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
||
do
|
||
atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
|
||
"${cmd}"
|
||
cmd="${cmd} |
|
||
cat"
|
||
done
|
||
|
||
cmd='printf "%s\n" foo'
|
||
for n in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
||
do
|
||
atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
|
||
"${cmd}"
|
||
cmd="${cmd} |
|
||
|
||
|
||
|
||
|
||
cat"
|
||
done
|
||
|
||
cmd='! printf "%s\n" foo'
|
||
for n in 1 2 3 4 5 6 7 8 9 10
|
||
do
|
||
atf_check -s exit:1 -o inline:'foo\n' -e empty ${TEST_SH} -c \
|
||
"${cmd}"
|
||
cmd="${cmd} | cat"
|
||
done
|
||
|
||
cmd='exec 4>&2 3<&0; printf "%s\n" foo'
|
||
for n in 1 2 3
|
||
do
|
||
pfx=
|
||
for xtra in 'x=y' 'a=b' '6>&1' '5<&3'
|
||
do
|
||
atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
|
||
"${cmd} | ${xtra} cat"
|
||
|
||
atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
|
||
"${cmd} | ${pfx} cat"
|
||
|
||
pfx="${pfx} ${xtra}"
|
||
done
|
||
cmd="${cmd} | ${pfx} cat"
|
||
done
|
||
|
||
# pipelines are not required to contain commands ...
|
||
# they don't do anything useful (at all) but the syntax is legal
|
||
base='4>&2'; cmd="${base}"
|
||
for pipe in 'a=b' '3<&0' '>>/dev/null' 'a= b= c=' '${x}' 'cat'
|
||
do
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
"${base} | ${pipe}"
|
||
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
"${cmd} | ${pipe}"
|
||
|
||
cmd="${cmd} | ${pipe}"
|
||
done
|
||
|
||
# but the command cannot be empty, or a reserved word used improperly
|
||
base='printf "%s\n" foo'; cmd="${base}"
|
||
for pipe in '' do done then else fi esac
|
||
do
|
||
atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
|
||
"${base} | ${pipe}"
|
||
|
||
atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
|
||
"${pipe} | ${base}"
|
||
|
||
atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
|
||
"${cmd} | ${pipe}"
|
||
|
||
cmd="${cmd} | ${pipe}"
|
||
done
|
||
}
|
||
|
||
atf_test_case j_and_or_lists
|
||
j_and_or_lists_head() {
|
||
atf_set "descr" "Check && and || command lists"
|
||
}
|
||
j_and_or_lists_body() {
|
||
and=true
|
||
or=false
|
||
and_or=false
|
||
for i in 1 2 3 4 5 6 7 8 9 10
|
||
do
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
"${and}"
|
||
|
||
atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
|
||
"${or}"
|
||
|
||
atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
|
||
"${and_or}"
|
||
|
||
and="${and} && true"
|
||
or="${or} || false"
|
||
and_or="${and_or} || true && false"
|
||
done
|
||
|
||
atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
|
||
'true &&'
|
||
atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
|
||
'&& true'
|
||
atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
|
||
'|| true'
|
||
atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
|
||
'true ||'
|
||
atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
|
||
'true || && false'
|
||
atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
|
||
'false || && true'
|
||
|
||
cmd='printf "%s" foo | cat | cat>/dev/null'
|
||
line="${cmd}"
|
||
for i in 1 2 3 4
|
||
do
|
||
line="${line} && ! ${cmd} || ${cmd}"
|
||
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
"${line}"
|
||
done
|
||
|
||
}
|
||
|
||
atf_test_case k_lists
|
||
k_lists_head() {
|
||
atf_set "descr" "Check ; command lists"
|
||
}
|
||
k_lists_body() {
|
||
line=
|
||
for N in 1 2 3 4
|
||
do
|
||
for cmd in \
|
||
true false : 'cat</dev/null>/dev/null' x=3 'exec 4>&-'
|
||
do
|
||
line="${line}${line:+;}${cmd}"
|
||
atf_check -s exit:0 -o 'inline:hello\nworld\n' \
|
||
-e empty ${TEST_SH} -c \
|
||
"echo hello; ${line}; echo world"
|
||
atf_check -s exit:0 -o 'inline:hello\nworld\n' \
|
||
-e empty ${TEST_SH} -c \
|
||
"echo hello; ${line}; echo world;"
|
||
done
|
||
done
|
||
|
||
for cmd in ';' ';;' 'false ;; true' 'false; ;true' '; true'
|
||
do
|
||
atf_check -s not-exit:0 -o ignore -e not-empty \
|
||
${TEST_SH} -c "${cmd}"
|
||
done
|
||
}
|
||
|
||
atf_test_case l_async_lists
|
||
l_async_lists_head() {
|
||
atf_set "descr" "Check & command lists"
|
||
}
|
||
l_async_lists_body() {
|
||
line=
|
||
for N in 1 2 3 4
|
||
do
|
||
for cmd in \
|
||
true false : 'cat</dev/null>/dev/null' x=3 'exec 4>&-'
|
||
do
|
||
line="${line:+${line}&}${cmd}"
|
||
atf_check -s exit:0 -o 'inline:hello\nworld\n' \
|
||
-e empty ${TEST_SH} -c \
|
||
"echo hello; ${line}& echo world"
|
||
atf_check -s exit:0 -o 'inline:hello\nworld\n' \
|
||
-e empty ${TEST_SH} -c \
|
||
"echo hello; ${line}& echo world"
|
||
done
|
||
done
|
||
|
||
for cmd in '&' ';&' '&;' '& true' 'false& &true'
|
||
do
|
||
atf_check -s not-exit:0 -o ignore -e not-empty \
|
||
${TEST_SH} -c "${cmd}"
|
||
done
|
||
}
|
||
|
||
atf_test_case m_compound_lists
|
||
m_compound_lists_head() {
|
||
atf_set "descr" "Check subshells () and { ;} command grouping"
|
||
}
|
||
m_compound_lists_body() {
|
||
# Note: (( is an unspecified (reserved) operator, don't use it...
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'( true )'
|
||
atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
|
||
'( false )'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'( (:) )'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'( ( true ))'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'( ( ( ( ( true )))))'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'( ( ( ( (true);:));true))'
|
||
|
||
atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
|
||
'()'
|
||
atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
|
||
'( )'
|
||
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'{ true; }'
|
||
atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
|
||
'{ false; }'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'{ { :; };}'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'{ { { { { :;};};};};}'
|
||
|
||
atf_check -s exit:0 -o 'inline:}\n' -e empty ${TEST_SH} -c \
|
||
'{ echo } ; }'
|
||
atf_check -s exit:0 -o 'inline:{\n' -e empty ${TEST_SH} -c \
|
||
'{ echo { ; }'
|
||
}
|
||
|
||
atf_test_case q_for_loop
|
||
q_for_loop_head() {
|
||
atf_set "descr" "Check for loop parsing"
|
||
}
|
||
q_for_loop_body() {
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'for x; do : ; done'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'for x in ; do : ; done'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'for x in a b c ; do : ; done'
|
||
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'for x in in;do : ; done'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'for x in for;do : ; done'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'for x in do;do : ; done'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'for for in in;do :;done'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'for for in for;do :; done'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'for for in do;do : ;done'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'for in in in;do : ; done'
|
||
atf_check -s exit:0 -o 'inline:do\nin\ndo\n' -e empty ${TEST_SH} -c \
|
||
'for in in in do in;do case $in in in)echo do;;do)echo in;;esac; done'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'for in in for;do : ; done'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'for in in do;do : ; done'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'for do in in;do : ; done'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'for do in for;do : ; done'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'for do in do;do : ; done'
|
||
atf_check -s exit:0 -o 'inline:dodo\n' -e empty ${TEST_SH} -c \
|
||
'for do in do;do echo ${do}do ; done'
|
||
}
|
||
|
||
atf_test_case r_case
|
||
r_case_head() {
|
||
atf_set "descr" "Check case statement parsing"
|
||
}
|
||
r_case_body() {
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in x) esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in (x) esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in x) ;; esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in (x) ;; esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in x|y) ;; esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in (x|y) ;; esac'
|
||
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in x|esac) ;; esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in x|esac|y) ;; esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in (x|esac) ;; esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in (x|esac|y) ;; esac'
|
||
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in in) esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in in) ;; esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in x|in) ;; esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in x|in|y) ;; esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in (x|in) ;; esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in (in|x) ;; esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in (x|in|y) ;; esac'
|
||
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case case in case) esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case in in in) esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case esac in (in) esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case in in esac|cat'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case esac in esac|cat'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case in in esac|case x in u) echo foo;; esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case esac in esac|case x in u) echo foo;; esac'
|
||
atf_check -s exit:0 -o 'inline:foo\n' -e empty ${TEST_SH} -c \
|
||
'case in in esac|case x in x) echo foo;; esac'
|
||
|
||
atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
|
||
'case in in (esac|cat'
|
||
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in x );;esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in ( x );;esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in x | y );;esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in ( x | y );;esac'
|
||
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x
|
||
in
|
||
( x | y )
|
||
|
||
;;
|
||
|
||
|
||
esac
|
||
'
|
||
}
|
||
|
||
atf_test_case s_if
|
||
s_if_head() {
|
||
atf_set "descr" "Check if statement parsing"
|
||
}
|
||
s_if_body() {
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'if :; then :; fi'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'if :; then :; else :; fi'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'if :; then :; elif :; then :; else :; fi'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'if :; then :; elif :; then :; elif :; then :; else :; fi'
|
||
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'if :; then : else :; fi'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'if : then :; then :; fi'
|
||
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'if if :;then :;fi then :;fi'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'if if :;then if :;then :;fi fi;then :;fi'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'if if :;then :;fi;then :;else if :;then :;fi fi'
|
||
|
||
for a in true false; do
|
||
for b in true false; do
|
||
for c in true false; do
|
||
|
||
$a && out=a || {
|
||
$b && out=b || {
|
||
$c && out=c || {
|
||
out=d; };};}
|
||
|
||
atf_check -s exit:0 -e empty \
|
||
-o "inline:${out}"'\n' \
|
||
${TEST_SH} -c \
|
||
"if $a; then echo a
|
||
elif $b; then echo b
|
||
elif $c; then echo c
|
||
else echo d
|
||
fi"
|
||
done
|
||
done
|
||
done
|
||
}
|
||
|
||
atf_test_case t_loops
|
||
t_loops_head() {
|
||
atf_set "descr" "Check while/until loop parsing"
|
||
}
|
||
t_loops_body() {
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'while false; do :; done'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'while false; do \done; done'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'until :; do :; done'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'until :; do \done; done'
|
||
|
||
atf_check -s exit:0 -o 'inline:x\n1\n' -e empty ${TEST_SH} -c \
|
||
':; while (exit) do echo x; false; done; echo $?'
|
||
atf_check -s exit:0 -o 'inline:x\n0\n' -e empty ${TEST_SH} -c \
|
||
'false; until (exit) do echo x; done; echo $?'
|
||
}
|
||
|
||
atf_test_case u_case_cont
|
||
u_case_cont_head() {
|
||
atf_set "descr" "Check case stmt parsing using ;& [optional]"
|
||
}
|
||
u_case_cont_body() {
|
||
|
||
${TEST_SH} -c 'case x in (x) false ;& (y) : ;; esac' 2>/dev/null ||
|
||
atf_skip ";& case list terminator unsupported in ${TEST_SH}"
|
||
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in x) ;& esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in (x) ;& esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in x|y) ;& esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in (x|y) ;& esac'
|
||
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in x );&esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in ( x );&esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in x | y );&esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in ( x | y );&esac'
|
||
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in x) ;& (y) esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in (x) ;& esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in x|y) ;& esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case x in (x|y) ;& esac'
|
||
}
|
||
|
||
atf_test_case x_functions
|
||
x_functions_head() {
|
||
atf_set "descr" "Check function definition parsing"
|
||
}
|
||
x_functions_body() {
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'func() { :; }'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'func() { :; }; func'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'func()(:)'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'func()if :; then false; else true; fi'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'func()while false; do :;done'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'func () for a in b c; do :; done'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'func() case x in (y) esac'
|
||
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'f1() { f2() { :; }; }; f1; f2'
|
||
|
||
# f2 should be not found, but f1 clears $?
|
||
atf_check -s exit:0 -o empty -e not-empty ${TEST_SH} -c \
|
||
'f1() { f2() { :; }; }; f2; f1'
|
||
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'f1() { eval "$1() { :; }"; }; f1 f2; f2'
|
||
}
|
||
|
||
atf_test_case z_PR_48498
|
||
z_PR_48498_head() {
|
||
atf_set "descr" "Check for detecting the syntax error from PR bin/48498"
|
||
}
|
||
z_PR_48498_body() {
|
||
|
||
# reserved words/operators that end things,
|
||
# were completely ignored after a ';' or '&'
|
||
# many of these tests lifted directly from the PR
|
||
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'true; fi'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'false; fi'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'false; then echo wut'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'true; then echo wut'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'true; do echo wut'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'true; then'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'true; else'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'true; do'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'true; done'
|
||
# {
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
': ; }'
|
||
# (
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
':;)'
|
||
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'true& fi'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'false& fi'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'false& then echo wut'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'true& then echo wut'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'true& do echo wut'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'true& then'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'true& else'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'true& do'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'true&done'
|
||
# {
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
':&}'
|
||
# (
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
':&)'
|
||
}
|
||
|
||
atf_test_case z_PR_52426
|
||
z_PR_52426_head() {
|
||
atf_set "descr" "Check for detecting the syntax error from PR bin/52426"
|
||
}
|
||
z_PR_52426_body() {
|
||
# Absoluely anything was permitted as a pattern of a case
|
||
# statement, any token (except 'esac') would serve
|
||
# What follows are a few "pretty" examples that were accepted.
|
||
# The first is the example from the PR
|
||
|
||
# Note we use only ;; type case lists, ;& should do the same, but
|
||
# only for shells that support it, we do not want the shell to
|
||
# object to any of these just because it does not support ;&
|
||
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'case x in <|() ;; esac'
|
||
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'case x in ((|)) ;; esac'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'case x in _|() ;; esac'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'case x in ()|() ;; esac'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'case x in -|;) ;; esac'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'case x in (;|-) ;; esac'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'case x in ;;|;) ;; esac'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'case x in (|| | ||) ;; esac'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'case x in (<<|>>) ;; esac'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'case x in (&&|&) ;; (|||>&) ;; &) esac'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'case x in (>||<) ;; esac'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'case x in( || | || | || | || | || );; esac'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'case x in (||| ||| ||| ||| ||) ;; esac'
|
||
atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
|
||
'case x in <> |
|
||
) ;; esac'
|
||
|
||
# now check some similar looking cases that are supposed to work
|
||
# That is, make sure the fix for the PR does not break valid cases.
|
||
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case fi in ({|}) ;; (!) ;; esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case esac in ([|]);; (][);; !!!|!!!|!!!|!!!);; esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case then in ({[]]}) ;; (^^);; (^|^);; ([!]);; (-);; esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case while in while );;(if|then|elif|fi);;(do|done);; esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case until in($);;($$);;($4.50);;(1/2);;0.3333);;esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case return in !);; !$);; $!);; !#);; (@);; esac'
|
||
atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
|
||
'case break in (/);; (\/);; (/\|/\));; (\\//);; esac'
|
||
}
|
||
|
||
atf_test_case z_PR_53712
|
||
z_PR_53712_head() {
|
||
atf_set "descr" "Check for avoiding the core dump from PR bin/53712"
|
||
}
|
||
z_PR_53712_body() {
|
||
atf_require_prog sysctl
|
||
atf_require_prog rm
|
||
|
||
# Don't want to have to deal with all the possible ways
|
||
# that the systcm might be configured to drop core files...
|
||
sysctl -w proc.$$.corename=core ||
|
||
atf_skip "Unable to set file name for core dump file"
|
||
rm -f core
|
||
|
||
${TEST_SH} -c '{ } > out'; S=$?
|
||
test -f core &&
|
||
atf_fail "PR bin/53712: ${TEST_SH} dumps core: status=$S"
|
||
test "$S" -lt 128 ||
|
||
atf_fail "PR bin/53712: ${TEST_SH} reported status $S (core?)"
|
||
|
||
# It doesn't matter here whether or not there was an error
|
||
# from the empty compound, or whether "out" was created
|
||
# just that no core dump appeared, and the shell did not
|
||
# exit because of a signal.
|
||
|
||
return 0
|
||
}
|
||
|
||
atf_init_test_cases() {
|
||
atf_add_test_case a_basic_tokenisation
|
||
atf_add_test_case b_comments
|
||
atf_add_test_case c_line_wrapping
|
||
atf_add_test_case d_cstrings
|
||
atf_add_test_case f_redirects
|
||
atf_add_test_case g_variable_syntax
|
||
atf_add_test_case h_var_assign
|
||
atf_add_test_case i_pipelines
|
||
atf_add_test_case j_and_or_lists
|
||
atf_add_test_case k_lists
|
||
atf_add_test_case l_async_lists
|
||
atf_add_test_case m_compound_lists
|
||
atf_add_test_case q_for_loop
|
||
atf_add_test_case r_case
|
||
atf_add_test_case s_if
|
||
atf_add_test_case t_loops
|
||
atf_add_test_case u_case_cont
|
||
atf_add_test_case x_functions
|
||
|
||
atf_add_test_case z_PR_48498
|
||
atf_add_test_case z_PR_52426
|
||
atf_add_test_case z_PR_53712
|
||
}
|