Module Name: src
Committed By: kre
Date: Sat May 20 16:35:55 UTC 2017
Modified Files:
src/distrib/sets/lists/tests: mi
src/tests/bin/sh: Makefile
Added Files:
src/tests/bin/sh: t_syntax.sh
Log Message:
Add a test of sh syntax & parsing (first attempt anyway.)
To generate a diff of this commit:
cvs rdiff -u -r1.743 -r1.744 src/distrib/sets/lists/tests/mi
cvs rdiff -u -r1.11 -r1.12 src/tests/bin/sh/Makefile
cvs rdiff -u -r0 -r1.1 src/tests/bin/sh/t_syntax.sh
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/distrib/sets/lists/tests/mi
diff -u src/distrib/sets/lists/tests/mi:1.743 src/distrib/sets/lists/tests/mi:1.744
--- src/distrib/sets/lists/tests/mi:1.743 Mon May 15 09:58:22 2017
+++ src/distrib/sets/lists/tests/mi Sat May 20 16:35:55 2017
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.743 2017/05/15 09:58:22 ozaki-r Exp $
+# $NetBSD: mi,v 1.744 2017/05/20 16:35:55 kre Exp $
#
# Note: don't delete entries from here - mark them as "obsolete" instead.
#
@@ -1278,6 +1278,7 @@
./usr/tests/bin/sh/t_redircloexec tests-bin-tests compattestfile,atf
./usr/tests/bin/sh/t_set_e tests-bin-tests compattestfile,atf
./usr/tests/bin/sh/t_shift tests-bin-tests compattestfile,atf
+./usr/tests/bin/sh/t_syntax tests-bin-tests compattestfile,atf
./usr/tests/bin/sh/t_ulimit tests-bin-tests compattestfile,atf
./usr/tests/bin/sh/t_varquote tests-bin-tests compattestfile,atf
./usr/tests/bin/sh/t_varval tests-bin-tests compattestfile,atf
Index: src/tests/bin/sh/Makefile
diff -u src/tests/bin/sh/Makefile:1.11 src/tests/bin/sh/Makefile:1.12
--- src/tests/bin/sh/Makefile:1.11 Sun Mar 20 22:57:04 2016
+++ src/tests/bin/sh/Makefile Sat May 20 16:35:55 2017
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.11 2016/03/20 22:57:04 christos Exp $
+# $NetBSD: Makefile,v 1.12 2017/05/20 16:35:55 kre Exp $
#
.include <bsd.own.mk>
@@ -19,6 +19,7 @@ TESTS_SH+= t_redir
TESTS_SH+= t_redircloexec
TESTS_SH+= t_set_e
TESTS_SH+= t_shift
+TESTS_SH+= t_syntax
TESTS_SH+= t_ulimit
TESTS_SH+= t_varquote
TESTS_SH+= t_varval
Added files:
Index: src/tests/bin/sh/t_syntax.sh
diff -u /dev/null src/tests/bin/sh/t_syntax.sh:1.1
--- /dev/null Sat May 20 16:35:55 2017
+++ src/tests/bin/sh/t_syntax.sh Sat May 20 16:35:55 2017
@@ -0,0 +1,888 @@
+# $NetBSD: t_syntax.sh,v 1.1 2017/05/20 16:35:55 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'|atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH}
+ # 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_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
+
+ cat <<- 'DONE' | atf_check -s exit:7 -o empty -e empty ${TEST_SH}
+ e\
+ x\
+ it \
+ 7
+ DONE
+
+ # 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
+ cat <<- 'DONE' | atf_check -s not-exit:7 -o empty -e not-empty \
+ ${TEST_SH}
+ e\
+ x\
+ it\
+ 7
+ DONE
+
+ cat <<- 'DONE' | atf_check -s exit:0 -o empty -e empty ${TEST_SH}
+ wh\
+ il\
+ e \
+ f\a\
+ \l\s\e
+ do
+ :\
+ ;
+ done
+ DONE
+
+ 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
+ cat <<- 'DONE' | atf_check -s exit:0 -o inline:'2\n' ${TEST_SH}
+ l\
+ s=7 bi\
+ n\
+ =\
+ 3
+ echo $(\
+ ( ls /bin )\
+ )
+ DONE
+}
+
+atf_test_case d_redirects
+d_redirects_head() {
+ atf_set "descr" "Check parsing of redirect operators"
+}
+d_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 f_variable_syntax
+f_variable_syntax_head() {
+ atf_set "descr" "Check that var names of all legal forms work"
+}
+f_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 xyzzy \
+ _____________________________________________________________
+ 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"
+ 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 g_var_assign
+g_var_assign_head() {
+ atf_set "descr" "Check var assignments "
+}
+g_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 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_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_redirects
+ atf_add_test_case f_variable_syntax
+ atf_add_test_case g_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
+}