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 +}