diff options
Diffstat (limited to 'Test')
-rw-r--r-- | Test/A01grammar.ztst | 88 | ||||
-rw-r--r-- | Test/A02alias.ztst | 33 | ||||
-rw-r--r-- | Test/A04redirect.ztst | 36 | ||||
-rw-r--r-- | Test/A05execution.ztst | 13 | ||||
-rw-r--r-- | Test/A06assign.ztst | 206 | ||||
-rw-r--r-- | Test/B02typeset.ztst | 14 | ||||
-rw-r--r-- | Test/B03print.ztst | 28 | ||||
-rw-r--r-- | Test/B09hash.ztst | 8 | ||||
-rw-r--r-- | Test/C02cond.ztst | 39 | ||||
-rw-r--r-- | Test/C03traps.ztst | 266 | ||||
-rw-r--r-- | Test/C04funcdef.ztst | 200 | ||||
-rw-r--r-- | Test/D01prompt.ztst | 18 | ||||
-rw-r--r-- | Test/D02glob.ztst | 47 | ||||
-rw-r--r-- | Test/D04parameter.ztst | 260 | ||||
-rw-r--r-- | Test/D06subscript.ztst | 7 | ||||
-rw-r--r-- | Test/D07multibyte.ztst | 56 | ||||
-rw-r--r-- | Test/D08cmdsubst.ztst | 24 | ||||
-rw-r--r-- | Test/E01options.ztst | 118 | ||||
-rw-r--r-- | Test/E02xtrace.ztst | 23 | ||||
-rw-r--r-- | Test/V01zmodload.ztst | 74 | ||||
-rw-r--r-- | Test/V06parameter.ztst | 42 | ||||
-rw-r--r-- | Test/V09datetime.ztst | 14 | ||||
-rw-r--r-- | Test/V10private.ztst | 16 | ||||
-rw-r--r-- | Test/V11db_gdbm.ztst | 327 | ||||
-rw-r--r-- | Test/X02zlevi.ztst | 27 | ||||
-rw-r--r-- | Test/Y01completion.ztst | 24 | ||||
-rw-r--r-- | Test/Y03arguments.ztst | 536 | ||||
-rwxr-xr-x | Test/ztst.zsh | 26 |
28 files changed, 2504 insertions, 66 deletions
diff --git a/Test/A01grammar.ztst b/Test/A01grammar.ztst index 7eedfa6e0..9625a15bc 100644 --- a/Test/A01grammar.ztst +++ b/Test/A01grammar.ztst @@ -103,16 +103,64 @@ 0:`exec' with -a option, no space >/bin/SPLOOSH + ( + opts=(-a /bin/WHOOOSH) + exec $opts /bin/sh -c 'echo $0' + ) +0:`exec' with -a option from expansion +>/bin/WHOOOSH + (export FOO=bar; exec -c /bin/sh -c 'echo x${FOO}x') 0:`exec' with -c option >xx + (\exec /bin/sh -c 'echo Test one'; print Not reached) + ('exec' /bin/sh -c 'echo Test two'; print Not reached) + (\exec -c /bin/sh -c 'echo Test three'; print Not reached) +0:precommand modifiers with quotes +>Test one +>Test two +>Test three + cat() { echo Function cat executed; } command cat && unfunction cat 0:`command' precommand modifier <External command cat executed >External command cat executed + (command -p echo this is output) + (\command -p echo this is more output) + ('command' -p echo this is yet more output) +0: command -p without -v or -V +>this is output +>this is more output +>this is yet more output + + command -pv cat + command -pv echo + command -p -V cat + command -p -V -- echo +0:command -p in combination +*>*/cat +>echo +>cat is /*/cat +>echo is a shell builtin + + args=( + 'command -pv cat' + 'command -pv echo' + 'command -p -V cat' + 'command -p -V -- echo' + ) + for arg in $args; do + ${=arg} + done +0:command -p in combination, using expansion +*>*/cat +>echo +>cat is /*/cat +>echo is a shell builtin + cd() { echo Not cd at all; } builtin cd . && unfunction cd 0:`builtin' precommand modifier @@ -283,6 +331,14 @@ >2 >3 + case whatever in + (*) print yeah, right ;& + esac + print but well +0:'case', redundant final ";&" +>yeah, right +>but well + ## Select now reads from stdin if the shell is not interactive. ## Its own output goes to stderr. (COLUMNS=80 LINES=3 @@ -738,3 +794,35 @@ > print Stuff here >} >Stuff here + + (exit 37) + case $? in + (37) echo $? + ;; + esac +0:case retains exit status for execution of cases +>37 + + false + case stuff in + (nomatch) foo + ;; + esac + echo $? +0:case sets exit status to zero if no patterns are matched +>0 + + case match in + (match) true; false; (exit 37) + ;; + esac + echo $? +0:case keeps exit status of last command executed in compound-list +>37 + + x=1 + x=2 | echo $x + echo $x +0:Assignment-only current shell commands in LHS of pipelin +>1 +>1 diff --git a/Test/A02alias.ztst b/Test/A02alias.ztst index 49e47567c..e68e93e0d 100644 --- a/Test/A02alias.ztst +++ b/Test/A02alias.ztst @@ -82,6 +82,7 @@ 0:Global aliasing quotes > a string S *>*5*echo S a string S " +# " # Note there is a trailing space on the "> a string S " line ( @@ -104,3 +105,35 @@ >0 ?(eval):2: invalid alias 'x=y' encountered while printing aliases # Currently, 'alias -L' returns 0 in this case. Perhaps it should return 1. + + alias -s mysuff='print -r "You said it.";' + eval 'thingummy.mysuff' +127:No endless loop with suffix alias in command position +>You said it. +?(eval):1: command not found: thingummy.mysuff + + alias +x; alias -z +1:error message has the correct sign +?(eval):alias:1: bad option: +x +?(eval):alias:1: bad option: -z + + # Usual issue that aliases aren't expanded until we + # trigger a new parse... + (alias badalias=notacommand + eval 'badalias() { print does not work; }') +1:ALIAS_FUNC_DEF off by default. +?(eval):1: defining function based on alias `badalias' +?(eval):1: parse error near `()' + + (alias goodalias=isafunc + setopt ALIAS_FUNC_DEF + eval 'goodalias() { print does now work; }' + isafunc) +0:ALIAS_FUNC_DEF causes the icky behaviour to be avaliable +>does now work + + (alias thisisokthough='thisworks() { print That worked; }' + eval thisisokthough + thisworks) +0:NO_ALIAS_FUNC_DEF works if the alias is a complete definition +>That worked diff --git a/Test/A04redirect.ztst b/Test/A04redirect.ztst index d7fe22fb0..cb82751ce 100644 --- a/Test/A04redirect.ztst +++ b/Test/A04redirect.ztst @@ -165,6 +165,15 @@ ?About to close a second time *?\(eval\):*: failed to close file descriptor * + eval $'fn-varid() { print {\x18}<<0 }' + { which -x2 fn-varid; fn-varid } | tr $'\x18' '?' +0:Regression test for off-by-one in varid check +>fn-varid () { +> print {?} <<0 +>0 +>} +>{?} + print foo >&- 0:'>&-' redirection @@ -475,6 +484,18 @@ >Nothing output yet >I just read any old rubbish + print you cannot be serious >input1 + () { + local var + read var + print $1 $var $2 + } <input1 Shirley >output1 dude + print Nothing output yet + cat output1 +0:anonymous function redirections mixed with argument +>Nothing output yet +>Shirley you cannot be serious dude + redirfn() { local var read var @@ -586,3 +607,18 @@ >x >bar >y + + fn-here-pipe() { + cat <<-HERE |& cat + FOO + HERE + } + fn-here-pipe + which fn-here-pipe +0:Combination of HERE-document and |& +>FOO +>fn-here-pipe () { +> cat <<HERE 2>&1 | cat +>FOO +>HERE +>} diff --git a/Test/A05execution.ztst b/Test/A05execution.ztst index 52738181d..0804691a7 100644 --- a/Test/A05execution.ztst +++ b/Test/A05execution.ztst @@ -284,6 +284,7 @@ F:anonymous function, and a descriptor leak when backgrounding a pipeline select x; do :; done; echo $? select x in; do :; done; echo $? select x in _*_; do :; done; echo $? + unsetopt ERR_EXIT NULL_GLOB 0:The status of "select" is zero when the loop body does not execute >0 >0 @@ -297,3 +298,15 @@ F:anonymous function, and a descriptor leak when backgrounding a pipeline 0:Background job exit does not affect reaping foreground job >CHLD >OK + +# Regression test for workers/39839 and workers/39844 + () { if return 11; then :; fi }; echo $? + () { while return 13; do :; done }; echo $? + () { until return 17; do :; done }; echo $? + () { until false; do return 19; done }; echo $? +0:"return" in "if" or "while" conditional +>11 +>13 +>17 +>19 + diff --git a/Test/A06assign.ztst b/Test/A06assign.ztst index da4e3b0fe..fd2b4177c 100644 --- a/Test/A06assign.ztst +++ b/Test/A06assign.ztst @@ -133,6 +133,72 @@ >1 2 42 43 44 5 >1 2 42 100 99 5 +# (subsection: append to array) + + array=( ) + array[5,6]=( 1 2 3 ) + print $array + print "${(q@)array}" +0:Append to empty array by range +>1 2 3 +>'' '' '' '' 1 2 3 + + array=( a ) + array[5,6]=( 1 2 3 ) + print $array + print "${(q@)array}" +0:Append to 1-element array by range +>a 1 2 3 +>a '' '' '' 1 2 3 + + array=( a b ) + array[5,6]=( 1 2 3 ) + print $array + print "${(q@)array}" +0:Append to 2-element array by range +>a b 1 2 3 +>a b '' '' 1 2 3 + + array=( a b ) + array[5,5]=( 1 2 3 ) + print $array + print "${(q@)array}" +0:Append to 2-element array by [a,a] range +>a b 1 2 3 +>a b '' '' 1 2 3 + + array=( a b c d ) + array[5,6]=( 1 2 3 ) + print $array + print "${(q@)array}" +0:Append array by range, continuously +>a b c d 1 2 3 +>a b c d 1 2 3 + + array=( a b c d ) + array[5,5]=( 1 2 3 ) + print $array + print "${(q@)array}" +0:Append array by [a,a] range, continuously +>a b c d 1 2 3 +>a b c d 1 2 3 + + array=( ) + array+=( 1 2 3 ) + print $array + print "${(q@)array}" +0:Append empty array via += +>1 2 3 +>1 2 3 + + array=( a ) + array+=( 1 2 3 ) + print $array + print "${(q@)array}" +0:Append 1-element array via += +>a 1 2 3 +>a 1 2 3 + # tests of var+=scalar s+=foo @@ -489,3 +555,143 @@ print $array 0:slice beyond length of array >FIRST + +# tests of string assignments + + a="abc" + a[1]=x + print $a +0:overwrite first character in string +>xbc + + a="abc" + a[2]="x" + print $a +0:overwrite middle character in string +>axc + + a="abc" + a[3]="x" + print $a +0:overwrite last character in string +>abx + + a="abc" + a[-1]="x" + print $a +0:overwrite -1 character in string +>abx + + a="abc" + a[-2]="x" + print $a +0:overwrite -2 character (middle) in string +>axc + + a="ab" + a[-2]="x" + print $a +0:overwrite -2 character (first) in string +>xb + + a="abc" + a[-3]="x" + print $a +0:overwrite -3 character (first) in string +>xbc + + a="abc" + a[-4]="x" + print $a +0:overwrite -4 character (before first) in string +>xabc + + a="abc" + a[-5]="x" + print $a +0:overwrite -5 character (before-before first) in string +>xabc + + a="abc" + a[-4,0]="x" + print $a +0:overwrite [-4,0] characters (before first) in string +>xabc + + a="abc" + a[-4,-4]="x" + print $a +0:overwrite [-4,-4] character (before first) in string +>xabc + + a="abc" + a[-40,-30]="x" + print $a +0:overwrite [-40,-30] characters (far before first) in string +>xabc + + a="abc" + a[-40,1]="x" + print $a +0:overwrite [-40,1] characters in short string +>xbc + + a="abc" + a[-40,40]="x" + print $a +0:overwrite [-40,40] characters in short string +>x + + a="abc" + a[2,40]="x" + print $a +0:overwrite [2,40] characters in short string +>ax + + a="abc" + a[2,-1]="x" + print $a +0:overwrite [2,-1] characters in short string +>ax + + a="abc" + a[-2,-1]="x" + print $a +0:overwrite [-2,-1] characters in short string +>ax + + a="a" + a[-1]="xx" + print $a +0:overwrite [-1] character with "xx" +>xx + + a="a" + a[-2]="xx" + print $a +0:overwrite [-2] character (before first) with "xx" +>xxa + + a="a" + a[2]="xx" + print $a +0:overwrite [2] character (after last) with "xx" +>axx + + a="" + a[1]="xx" + print $a +0:overwrite [1] character (string: "") with "xx" +>xx + + a="" + a[-1]="xx" + print $a +0:overwrite [-1] character (string: "") with "xx" +>xx + + a="" + a[2]="xx" + print $a +0:overwrite [2] character (string: "") with "xx" +>xx diff --git a/Test/B02typeset.ztst b/Test/B02typeset.ztst index d6d24210b..b27bb4f6b 100644 --- a/Test/B02typeset.ztst +++ b/Test/B02typeset.ztst @@ -454,7 +454,7 @@ fn() { typeset -p array nonexistent; } fn 1:declare -p shouldn't create scoped values ->typeset -a array=( foo bar ) +>typeset -g -a array=( foo bar ) ?fn:typeset: no such variable: nonexistent unsetopt typesetsilent @@ -490,7 +490,7 @@ ?0 ?(eval):5: read-only variable: pbro ?(eval):6: read-only variable: pbro -?typeset -r pbro +?typeset -g -r pbro ?0 ?(eval):10: read-only variable: pbro @@ -711,3 +711,13 @@ typeset isreadonly=still 1:typeset returns status 1 if setting readonly variable ?(eval):2: read-only variable: isreadonly + + if (( UID )); then + UID=$((UID+1)) date; echo "Status is printed, $?" + else + ZTST_skip="cannot test setuid error when tests run as superuser" + fi +0:when cannot change UID, the command isn't run +# 'date' did not run. +>Status is printed, 1 +*?*: failed to change user ID: * diff --git a/Test/B03print.ztst b/Test/B03print.ztst index befe2f2dd..c65568ad9 100644 --- a/Test/B03print.ztst +++ b/Test/B03print.ztst @@ -308,5 +308,29 @@ printf -v foo "%s\0%s-" into the breach typeset -p foo 0:print and printf into a variable ->typeset foo='once more' ->typeset foo=$'into\C-@the-breach\C-@-' +>typeset -g foo='once more' +>typeset -g foo=$'into\C-@the-breach\C-@-' + + typeset -a foo + print -f '%2$d %4s' -v foo one 1 two 2 three 3 + typeset -p foo +0:printf into an array variable +>typeset -a foo=( '1 one' '2 two' '3 three' ) + + typeset -a foo + print -f '%s' -v foo string + typeset -p foo +0:printf to an array variable without format string reuse +>typeset foo=string + + printf - + printf - - + printf -- + printf -- - + printf -- -- + printf -x -v foo + # Final print for newline on stdout + print +0:regression test of printf with assorted ambiguous options or formats +>------x +?(eval):printf:3: not enough arguments diff --git a/Test/B09hash.ztst b/Test/B09hash.ztst index 49f304838..7b5dfb43e 100644 --- a/Test/B09hash.ztst +++ b/Test/B09hash.ztst @@ -69,3 +69,11 @@ >one=/first/directory >two=/directory/the/second >three=/noch/ein/verzeichnis + + hash -d t-t=/foo + i="~t-t" + print ~t-t/bar + print ${~i}/rab +0:Dashes are untokenized in directory hash names +>/foo/bar +>/foo/rab diff --git a/Test/C02cond.ztst b/Test/C02cond.ztst index b0e84ddeb..38525016c 100644 --- a/Test/C02cond.ztst +++ b/Test/C02cond.ztst @@ -11,9 +11,9 @@ typeset -gi isnfs [[ "$(find . -prune -fstype nfs 2>/dev/null)" == "." ]] && isnfs=1 if (( isnfs )) && - (cd -q ${TMPPREFIX:h} >/dev/null 2>&1 && + (cd -q ${ZTST_tmp} >/dev/null 2>&1 && [[ "$(find . -prune -fstype nfs 2>/dev/null)" != "." ]]); then - filetmpprefix=${TMPPREFIX}-$$- + filetmpprefix=${ZTST_tmp}/condtest-$$- isnfs=0 else filetmpprefix= @@ -146,29 +146,30 @@ # can't be bothered with -S - if [[ ${mtab::="$({mount || /sbin/mount})"} = *[(]?*[)] ]]; then + if [[ ${mtab::="$({mount || /sbin/mount || /usr/sbin/mount} 2>/dev/null)"} = *[(]?*[)] ]]; then print -u $ZTST_fd 'This test takes two seconds...' else unmodified_ls="$(ls -lu $unmodified)" print -u $ZTST_fd 'This test takes up to 60 seconds...' fi + sleep 2 + touch $newnewnew if [[ $OSTYPE == "cygwin" ]]; then ZTST_skip="[[ -N file ]] not supported on Cygwin" elif (( isnfs )); then ZTST_skip="[[ -N file ]] not supported with NFS" elif { (( ! $+unmodified_ls )) && - { sleep 2; cat $unmodified } && + cat $unmodified && { df -k -- ${$(print -r -- "$mtab" | awk '/noatime/ {print $1,$3}'):-""} | tr -s ' ' | fgrep -- "$(df -k . | tail -1 | tr -s ' ')" } >&/dev/null } || { (( $+unmodified_ls )) && SECONDS=0 && - ! until (( SECONDS >= 60 )); do + ! until (( SECONDS >= 58 )); do ZTST_hashmark; sleep 2; cat $unmodified [[ $unmodified_ls != "$(ls -lu $unmodified)" ]] && break done }; then ZTST_skip="[[ -N file ]] not supported with noatime file system" else - touch $newnewnew [[ -N $newnewnew && ! -N $unmodified ]] fi 0:-N cond @@ -268,6 +269,25 @@ F:Failures in these cases do not indicate a problem in the shell. 0:-nt shouldn't abort on non-existent files >status = 1 + str='string' empty='' + [[ -v IFS && -v str && -v empty && ! -v str[3] && ! -v not_a_variable ]] +0:-v cond + + arr=( 1 2 3 4 ) empty=() + [[ -v arr && -v arr[1,4] && -v arr[1] && -v arr[4] && -v arr[-4] && + -v arr[(i)3] && ! -v arr[(i)x] && + ! -v arr[0] && ! -v arr[5] && ! -v arr[-5] && ! -v arr[2][1] && + ! -v arr[3]extra && -v empty && ! -v empty[1] ]] +0:-v cond with array + + typeset -A assoc=( key val num 4 ) + [[ -v assoc && -v assoc[key] && -v assoc[(i)*] && -v assoc[(I)*] && + ! -v assoc[x] && ! -v assoc[key][1] ]] +0:-v cond with association + + () { [[ -v 0 && -v 1 && -v 2 && ! -v 3 ]] } arg '' +0:-v cond with positional parameters + # core dumps on failure if zmodload zsh/regex 2>/dev/null; then echo >regex_test.sh 'if [[ $# = 1 ]]; then @@ -413,6 +433,13 @@ F:Failures in these cases do not indicate a problem in the shell. >OK 4 >OK 5 + fn() { [[ 'a' == 'b' || 'b' = 'c' || 'c' != 'd' ]] } + which -x2 fn +0: = and == appear as input +>fn () { +> [[ 'a' == 'b' || 'b' = 'c' || 'c' != 'd' ]] +>} + %clean # This works around a bug in rm -f in some versions of Cygwin chmod 644 unmodish diff --git a/Test/C03traps.ztst b/Test/C03traps.ztst index 83c05aa08..759401225 100644 --- a/Test/C03traps.ztst +++ b/Test/C03traps.ztst @@ -476,7 +476,7 @@ fi } fn -0:ERRRETURN not triggered in if condition +0:ERR_RETURN not triggered in if condition >Oh, yes fn() { @@ -490,7 +490,7 @@ fi } fn -1:ERRRETURN in "if" +1:ERR_RETURN in "if" fn() { emulate -L zsh @@ -503,7 +503,7 @@ fi } fn -1:ERRRETURN in "else" branch (regression test) +1:ERR_RETURN in "else" branch (regression test) $ZTST_testdir/../Src/zsh -f =(<<<" if false; then @@ -515,10 +515,268 @@ print Yes fi ") -0:ERRRETURN when false "if" is the first statement in an "else" (regression) +0:ERR_RETURN when false "if" is the first statement in an "else" (regression) >Yes F:Must be tested with a top-level script rather than source or function + fn() { + emulate -L zsh + setopt errreturn + print before + false + print after + } + fn +1:ERR_RETURN, basic case +>before + + fn() { + emulate -L zsh + setopt errreturn + print before + ! true + ! false + print after + } + fn +0:ERR_RETURN with "!" +>before +>after + + fn() { + emulate -L zsh + setopt errreturn + print before + ! true + ! false + false + print after + } + fn +1:ERR_RETURN with "!" and a following false +>before + + fn() { + emulate -L zsh + setopt errreturn + print before + ! if true; then + false + fi + print after + } + fn +0:ERR_RETURN with "!" suppressed inside complex structure +>before +>after + + fn() { + emulate -L zsh + setopt errreturn + print before + if true; then + false + fi + print after + } + fn +1:ERR_RETURN with no "!" suppression (control case) +>before + + (setopt err_return + fn() { + print before-in + false && false + } + print before-out + fn + print after-out + ) +1:ERR_RETURN with "&&" in function (regression test) +>before-out +>before-in + + (setopt err_return + fn() { + print before-in + false && false + print after-in + } + print before-out + fn + print after-out + ) +0:ERR_RETURN not triggered on LHS of "&&" in function +>before-out +>before-in +>after-in +>after-out + + (setopt err_return + fn() { + print before-in + true && false + print after-in + } + print before-out + fn + print after-out + ) +1:ERR_RETURN triggered on RHS of "&&" in function +>before-out +>before-in + + (setopt err_exit + for x in y; do + false && true + done + print OK + ) +0:ERR_EXIT not triggered by status 1 at end of for +>OK + + (setopt err_exit + integer x=0 + while (( ! x++ )); do + false && true + done + print OK + ) +0:ERR_EXIT not triggered by status 1 at end of while +>OK + + (setopt err_exit + repeat 1; do + false && true + done + print OK + ) +0:ERR_EXIT not triggered by status 1 at end of repeat +>OK + + (setopt err_exit + if true; then + false && true + fi + print OK + ) +0:ERR_EXIT not triggered by status 1 at end of if +>OK + + (setopt err_exit + { + false && true + } + print OK + ) +0:ERR_EXIT not triggered by status 1 at end of { } +>OK + + (setopt err_exit + for x in y; do + false + done + print OK + ) +1:ERR_EXIT triggered by status 1 within for + + (setopt err_exit + integer x=0 + while (( ! x++ )); do + false + done + print OK + ) +1:ERR_EXIT triggered by status 1 within while + + (setopt err_exit + repeat 1; do + false + done + print OK + ) +1:ERR_EXIT triggered by status 1 within repeat + + (setopt err_exit + if true; then + false + fi + print OK + ) +1:ERR_EXIT triggered by status 1 within if + + (setopt err_exit + { + false + } + print OK + ) +1:ERR_EXIT triggered by status 1 within { } + + (setopt err_exit + () { + false && true + print Still functioning + false && true + } + print OK + ) +1:ERR_EXIT triggered by status 1 at end of anon func +>Still functioning + + if zmodload zsh/system 2>/dev/null; then + ( + trap 'echo TERM; exit 2' TERM + trap 'echo EXIT' EXIT + kill -s TERM "$sysparams[pid]" + echo 'FATAL: we should never get here!' 1>&2 + exit 1 + ) + else + ZTST_skip="zsh/system library not found." + fi +2:EXIT trap from TERM trap +>TERM +>EXIT + + # Should not get "hello" in the single quotes. + ( + trap "echo hello" EXIT; + { :; } | { read line; print "'$line'"; } + ) +0:EXIT trap not called in LHS of pipeline: Shell construct on LHS +>'' +>hello + + ( + trap "echo hello" EXIT; + cat </dev/null | { read line; print "'$line'"; } + ) +0:EXIT trap not called in LHS of pipeline: External command on LHS +>'' +>hello + + $ZTST_testdir/../Src/zsh -f =(<<<" + trap handler EXIT + handler() { + echoa + echo b + } + echoa() { + echo a + } + exit0() { + exit + } + main() { + exit0 + } + main + ") +0:No early exit from nested function in EXIT trap. +>a +>b + %clean rm -f TRAPEXIT diff --git a/Test/C04funcdef.ztst b/Test/C04funcdef.ztst index 496577f6c..6a675e0b4 100644 --- a/Test/C04funcdef.ztst +++ b/Test/C04funcdef.ztst @@ -2,6 +2,10 @@ mkdir funcdef.tmp cd funcdef.tmp + setopt chaselinks + cd . + unsetopt chaselinks + mydir=$PWD %test @@ -98,6 +102,24 @@ >4 >5 + strmathfunc() { + if [[ $0 = stralpha ]]; then + set -- ${1//[^[:alpha:]]} + fi + (( $#1 )) + } + functions -Ms strlen 1 1 strmathfunc + functions -Ms stralpha 1 1 strmathfunc + print $(( strlen(this, is, a, raw, string) )) + print $(( strlen() )) + print $(( stralpha(this, is, a, raw, string) )) + print $(( stralpha() )) +0:User-defined math functions, string arguments +>24 +>0 +>16 +>0 + command_not_found_handler() { print "Great News! I've handled the command:" print "$1" @@ -120,14 +142,13 @@ print "Your command:" >&2 print "$1" >&2 print "has gone down the tubes. Sorry." >&2 - return 1 + return 42 } ThisCommandDoesNotExistEither -127:Command not found handler, failure +42:Command not found handler, failure ?Your command: ?ThisCommandDoesNotExistEither ?has gone down the tubes. Sorry. -?(eval):7: command not found: ThisCommandDoesNotExistEither local variable=outside print "I am $variable" @@ -321,6 +342,179 @@ > print oops was successfully autoloaded >} + ( + fpath=(.) + printf '%s\n' 'oops(){}' 'ninjas-earring(){}' 'oops "$@"' >oops + autoload oops + oops + whence -v oops + ) +0q:whence -v of zsh-style autoload +>oops is a shell function from $mydir/oops + + ( + fpath=(.) + mkdir extra + print 'print "I have been loaded by explicit path."' >extra/spec + autoload -Uz $PWD/extra/spec + spec + ) +0:autoload with explicit path +>I have been loaded by explicit path. + + ( + fpath=(.) + print 'print "I have been loaded by default path."' >def + autoload -Uz $PWD/extra/def + def + ) +1:autoload with explicit path with function in normal path, no -d +?(eval):5: def: function definition file not found + + ( + fpath=(.) + autoload -dUz $PWD/extra/def + def + ) +0:autoload with explicit path with function in normal path, with -d +>I have been loaded by default path. + + ( + cd extra + fpath=(.) + autoload -r spec + cd .. + spec + ) +0:autoload -r +>I have been loaded by explicit path. + + ( + cd extra + fpath=(.) + autoload -r def + cd .. + def + ) +0:autoload -r is permissive +>I have been loaded by default path. + + ( + cd extra + fpath=(.) + autoload -R def + ) +1:autoload -R is not permissive +?(eval):4: def: function definition file not found + + ( + spec() { autoload -XUz $PWD/extra; } + spec + ) +0:autoload -X with path +>I have been loaded by explicit path. + +# The line number 1 here and in the next test seems suspect, +# but this example proves it's not down to the new features +# being tested here. + ( + fpath=(.) + cod() { autoload -XUz; } + cod + ) +1:autoload -X with no path, failure +?(eval):1: cod: function definition file not found + + ( + fpath=(.) + def() { autoload -XUz $PWD/extra; } + def + ) +1:autoload -X with wrong path and no -d +?(eval):1: def: function definition file not found + + ( + fpath=(.) + def() { autoload -dXUz $PWD/extra; } + def + ) +0:autoload -dX with path +>I have been loaded by default path. + + ( + fpath=(.) + print 'loadthisfunc() { autoload -X }' >loadthisfunc_sourceme + print 'print Function was loaded correctly.' >loadthisfunc + source $PWD/loadthisfunc_sourceme + loadthisfunc + ) +0: autoload -X interaction with absolute filename used for source location +>Function was loaded correctly. + + ( + fpath=() + mkdir extra2 + for f in fun2a fun2b; do + print "print $f" >extra2/$f + done + repeat 3; do + autoload $PWD/extra2/fun2{a,b} $PWD/extra/spec + fun2a + fun2b + spec + unfunction fun2a fun2b spec + autoload $PWD/extra2/fun2{a,b} $PWD/extra/spec + spec + fun2b + fun2a + unfunction fun2a fun2b spec + done + ) +0: Exercise the directory name cache for autoloads +>fun2a +>fun2b +>I have been loaded by explicit path. +>I have been loaded by explicit path. +>fun2b +>fun2a +>fun2a +>fun2b +>I have been loaded by explicit path. +>I have been loaded by explicit path. +>fun2b +>fun2a +>fun2a +>fun2b +>I have been loaded by explicit path. +>I have been loaded by explicit path. +>fun2b +>fun2a + + not_trashed() { print This function was not trashed; } + autoload -Uz /foo/bar/not_trashed + not_trashed +0:autoload with absolute path doesn't trash loaded function +>This function was not trashed + + # keep spec from getting loaded in parent shell for simplicity + ( + if whence spec; then print spec already loaded >&2; exit 1; fi + autoload -Uz $PWD/spec + autoload -Uz $PWD/extra/spec + spec + ) +0:autoload with absolute path can be overridden if not yet loaded +>I have been loaded by explicit path. + + ( + if whence spec; then print spec already loaded >&2; exit 1; fi + autoload -Uz $PWD/extra/spec + autoload spec + spec + ) +0:autoload with absolute path not cancelled by bare autoload +>I have been loaded by explicit path. + %clean rm -f file.in file.out diff --git a/Test/D01prompt.ztst b/Test/D01prompt.ztst index 2638e2438..11f18dc71 100644 --- a/Test/D01prompt.ztst +++ b/Test/D01prompt.ztst @@ -82,9 +82,12 @@ # We could test for that, but we can't be bothered. # I hope LC_ALL is enough to make the format what's expected. +# The $date2 assignment tests that %s is interpreted as a printf format +# string, rather than as a prompt escape (end standout). + LC_ALL=C date1=$(print -P %w) - date2=$(print -P %W) + date2=$(print -P -f %s %W) date3=$(print -P %D) if [[ $date1 != [A-Z][a-z][a-z][[:blank:]]##[0-9]## ]]; then print "Date \`$date1' is not in the form \`Day DD' (e.g. \`Mon 1'" @@ -196,8 +199,17 @@ ?+zsh_directory_name:1> emulate -L zsh ?+zsh_directory_name:2> setopt extendedglob ?+zsh_directory_name:3> local -a match mbegin mend -?+zsh_directory_name:4> [[ d == n ]] -?+zsh_directory_name:12> [[ <parent>/very_long_directory_name == (#b)(*)/very_long_directory_name ]] +?+zsh_directory_name:4> [[ d = n ]] +?+zsh_directory_name:12> [[ <parent>/very_long_directory_name = (#b)(*)/very_long_directory_name ]] ?+zsh_directory_name:14> return 0 ?+fn:7> local d='~[<parent>:l]' ?+fn:8> print '~[<parent>:l]' + +# Test that format strings are not subject to prompt expansion + print -P -f '%%Sfoo%%s\n' bar +0:print -P -f +>%Sfoo%s + + print ${(%U)Y-%(v} +0:Regression test for test on empty psvar +> diff --git a/Test/D02glob.ztst b/Test/D02glob.ztst index a6b704a8e..08b71dc8e 100644 --- a/Test/D02glob.ztst +++ b/Test/D02glob.ztst @@ -654,4 +654,49 @@ [[ "z" != [$~cset] ]] || print Fail 4 [[ "1" = [$~cset] ]] || print Fail 5 [[ "b" != [$~cset] ]] || print Fail 6 -0:character set specified as active variabe +0:character set specified as active variable + + () { print -l -- $@:a } / /..{,/} /1 /nonexistent/..{,/} /deeper/nonexistent/..{,/} +0:modifier ':a' doesn't require existence +>/ +>/ +>/ +>/1 +>/ +>/ +>/deeper +>/deeper + + () { set -- ${PWD}/$^@; print -l -- $@:A } glob.tmp/nonexistent/foo/bar/baz +0:modifier ':A' doesn't require existence +*>*/glob.tmp/nonexistent/foo/bar/baz + + ln -s dir3/subdir glob.tmp/link + () { + print ${1:A} | grep glob.tmp + } glob.tmp/link/../../hello + rm glob.tmp/link +0:modifier ':A' resolves '..' components before symlinks +# There should be no output + + ln -s dir3/subdir glob.tmp/link + () { + print ${1:P} + } glob.tmp/link/../../hello/world + rm glob.tmp/link +0:modifier ':P' resolves symlinks before '..' components +*>*glob.tmp/hello/world + + # This is a bit brittle as it depends on PATH_MAX. + # We could use sysconf.. + bad_pwd="/${(l:16000:: :):-}" + print ${bad_pwd:P} +0:modifier ':P' with path too long +?(eval):4: path expansion failed, using root directory +>/ + + foo=a + value="ac" + print ${value//[${foo}b-z]/x} +0:handling of - range in complicated pattern context +>xx diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index 7cb297e0c..d5798b5b9 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -82,6 +82,11 @@ >wasnull2d >wasnull2d + unset array + print ${#${(A)=array=word}} +0:${#${(A)=array=word}} counts array elements +>1 + (print ${set1:?okhere}; print ${unset1:?exiting1}; print not reached;) (print ${null1?okhere}; print ${null1:?exiting2}; print not reached;) 1:${...:?...}, ${...?...} @@ -90,6 +95,21 @@ ?(eval):1: unset1: exiting1 ?(eval):2: null1: exiting2 + PROMPT="" $ZTST_testdir/../Src/zsh -fis <<<' + unsetopt PROMPT_SP + PS1="" PS2="" PS3="" PS4="" RPS1="" RPS2="" + exec 2>&1 + foo() { + print ${1:?no arguments given} + print not reached + } + foo + print reached + ' 2>/dev/null +0:interactive shell returns to top level on ${...?...} error +*>*foo:1: 1: no arguments given +>reached + print ${set1:+word1} ${set1+word2} ${null1:+word3} ${null1+word4} print ${unset1:+word5} ${unset1+word6} 0:${...:+...}, ${...+...} @@ -612,6 +632,19 @@ >; >(( echo 42 + # From parse error on it's not possible to split. + # Just check we get the complete string. + foo='echo $(|||) bar' + print -rl ${(z)foo} +0:$($(z)} with parse error in command substitution. +>echo +>$(|||) bar + + foo=$'\x06ZUI\x1f text-field example: \x1azuitfieldtfield1_1\x1a\'\'\x1a\'\'\x1a1\x1aZUI\\[my_tfield1_width\\]\x1aZUI\\[my_tfield1_start\\]\x1aZUI\\[my_tfield1_data\\]\x1c' + print "${#${(z@)foo}}" +0:Test real-world data that once seemed to fail +>4 + psvar=(dog) setopt promptsubst foo='It shouldn'\''t $(happen) to a %1v.' @@ -825,6 +858,7 @@ foo='b* e*' print ${(e)~foo} print ${(e)~=foo} + setopt nomatch 0:Rule 10: Re-Evaluation >b* e* >boringfile evenmoreboringfile @@ -1713,6 +1747,26 @@ >2 >2 + # SHLVL is incremented twice and decremented once in between. + SHLVL=1 + $ZTST_testdir/../Src/zsh -fc $ZTST_testdir/../Src/zsh' -fc "echo \$SHLVL"' + $ZTST_testdir/../Src/zsh -fc '('$ZTST_testdir/../Src/zsh' -fc "echo \$SHLVL")' + $ZTST_testdir/../Src/zsh -fc '( ('$ZTST_testdir/../Src/zsh' -fc "echo \$SHLVL"))' +0:SHLVL decremented upon implicit exec optimisation +>2 +>2 +>2 + + # SHLVL is incremented twice with no decrement in between. + SHLVL=1 + $ZTST_testdir/../Src/zsh -fc '('$ZTST_testdir/../Src/zsh' -fc "echo \$SHLVL"); exit' + $ZTST_testdir/../Src/zsh -fc '(exec '$ZTST_testdir/../Src/zsh' -fc "echo \$SHLVL"); exit' + $ZTST_testdir/../Src/zsh -fc '( ('$ZTST_testdir/../Src/zsh' -fc "echo \$SHLVL"); exit)' +0:SHLVL not decremented upon exec in subshells +>3 +>3 +>3 + # The following tests the return behaviour of parsestr/parsestrnoerr alias param-test-alias='print $'\''\x45xpanded in substitution'\' param='$(param-test-alias)' @@ -1920,3 +1974,209 @@ print $array 0:"-" works after "[" in same expression (Dash problem) >foo two three + + ( + setopt shwordsplit + set -- whim:wham:whom + IFS=: + print -l $@ + ) +0:Splitting of $@ on IFS: single element +>whim +>wham +>whom + + ( + setopt shwordsplit + set -- one:two bucklemy:shoe + IFS=: + print -l $@ + ) +0:Splitting of $@ on IFS: multiple elements +# No forced joining in this case +>one +>two +>bucklemy +>shoe + + ( + set -- one:two bucklemy:shoe + print -l ${(s.:.)@} + ) +0:Splitting of $@ on (s): multiple elements +# Forced joining in this case +>one +>two bucklemy +>shoe + + ( + set -- one:two bucklemy:shoe + print -l ${(@s.:.)@} + ) +0:Splitting of $@ on (@s): multiple elements +# Forced non-joining in this case +>one +>two +>bucklemy +>shoe + + ( + set -- one:two bucklemy:shoe + IFS= + setopt shwordsplit + print -l ${@} ${(s.:.)*} ${(s.:.j.-.)*} + ) +0:Joining of $@ does not happen when IFS is empty, but splitting $* does +>one:two +>bucklemy:shoe +>one +>twobucklemy +>shoe +>one +>two-bucklemy +>shoe + + ( + set -- "one two" "bucklemy shoe" + IFS= + setopt shwordsplit rcexpandparam + print -l "X${(@j.-.)*}" + ) +0:Use of @ does not prevent forced join with j +>Xone two-bucklemy shoe + + () { print -r -- "${(q)1}" "${(b)1}" "${(qq)1}" } '=foo' +0:(q) and (b) quoting deal with the EQUALS option +>\=foo =foo '=foo' + + args() { print $#; } + a=(foo) + args "${a[3,-1]}" + args "${(@)a[3,-1]}" +0:Out-of-range multiple array subscripts with quoting, with and without (@) +>1 +>0 + + a='~-/'; echo $~a +0:Regression: "-" became Dash in workers/37689, breaking ~- expansion +*>* +F:We do not care what $OLDPWD is, as long as it does not cause an error + + ( + set -- one 'two three' four + for ifs in default null unset; do + for wordsplit in native sh; do + print -r -- "--- $ifs IFS, $wordsplit splitting ---" + case $ifs in + default) IFS=$' \t\n\00' ;; + null) IFS= ;; + unset) unset -v IFS ;; + esac + case $wordsplit in + native) unsetopt shwordsplit ;; + sh) setopt shwordsplit ;; + esac + for testcmd in 'var=$@' 'var=$*' 'var="$@"' 'var="$*"'; do + print -r -- "> $testcmd" + eval "$testcmd" + printf '[%s]\n' "${var[@]}" + done + done + done + ) +0:Assigning $@, $*, "$@", "$*" to var with various shwordsplit/IFS settings +>--- default IFS, native splitting --- +>> var=$@ +>[one two three four] +>> var=$* +>[one two three four] +>> var="$@" +>[one two three four] +>> var="$*" +>[one two three four] +>--- default IFS, sh splitting --- +>> var=$@ +>[one two three four] +>> var=$* +>[one two three four] +>> var="$@" +>[one two three four] +>> var="$*" +>[one two three four] +>--- null IFS, native splitting --- +>> var=$@ +>[onetwo threefour] +>> var=$* +>[onetwo threefour] +>> var="$@" +>[onetwo threefour] +>> var="$*" +>[onetwo threefour] +>--- null IFS, sh splitting --- +>> var=$@ +>[onetwo threefour] +>> var=$* +>[onetwo threefour] +>> var="$@" +>[onetwo threefour] +>> var="$*" +>[onetwo threefour] +>--- unset IFS, native splitting --- +>> var=$@ +>[one two three four] +>> var=$* +>[one two three four] +>> var="$@" +>[one two three four] +>> var="$*" +>[one two three four] +>--- unset IFS, sh splitting --- +>> var=$@ +>[one two three four] +>> var=$* +>[one two three four] +>> var="$@" +>[one two three four] +>> var="$*" +>[one two three four] +F:As of this writing, var=$@ and var="$@" with null IFS have unspecified +F:behavior, see http://austingroupbugs.net/view.php?id=888 + + () { + setopt localoptions extendedglob + [[ $- = [[:alnum:]]## ]] || print Failed 1 + [[ ${-} = [[:alnum:]]## ]] || print Failed 2 + } +0:$- expansion correctly handles Dash token + + a=(1 "" 3) + print -rl -- "${(@)a//*/x}" + a="" + print -rl -- "${(@)a//*/y}" +0:Zero-length string match in parameter substitution +>x +>x +>x +>y + + a=(1 "" 3) + print -rl -- "${(@)a//#%*/x}" + a="" + print -rl -- "${(@)a//#%*/y}" +0:Zero-length string match at end +>x +>x +>x +>y + + my_width=6 + my_index=1 + my_options=Option1 + hyperlink=$'\034'"MYID"$'\034'"DATA1"$'\034'"DATA2"$'\034'"DATA3"$'\034'"my_width"$'\034'"my_index"$'\034'"my_options"$'\02' + array=( $hyperlink "Regular text" $hyperlink ) + array=( "${array[@]//(#b)$'\034'[^$'\034']#$'\034'[^$'\034']#$'\034'[^$'\034']#$'\034'[^$'\034']#$'\034'([^$'\034']#)$'\034'([^$'\034']#)$'\034'([^$'\02']#)$'\02'/${(mr:${(P)${(Q)match[1]}}:: :)${(As:;:)${(P)${(Q)match[3]}}}[${(P)${(Q)match[2]}}]}}" ) + print -rl -- "${array[@]}" +0:Test substitution that uses P,Q,A,s,r,m flags +>Option +>Regular text +>Option diff --git a/Test/D06subscript.ztst b/Test/D06subscript.ztst index 144923667..f0a858b1c 100644 --- a/Test/D06subscript.ztst +++ b/Test/D06subscript.ztst @@ -266,3 +266,10 @@ >of the gang >of the gang >of the gang + + string='abcde' + twoarg() { return $(( $2 - $1 )) } + functions -M twoarg + print ${string[1,twoarg(1,4)]} +0:Commas inside parentheses do not confuse subscripts +>abc diff --git a/Test/D07multibyte.ztst b/Test/D07multibyte.ztst index 39ba5ef8b..e20315340 100644 --- a/Test/D07multibyte.ztst +++ b/Test/D07multibyte.ztst @@ -500,18 +500,6 @@ # aren't quite double width, but the arithmetic is correct. # It appears just to be an effect of the font. - if zmodload zsh/regex 2>/dev/null; then - [[ $'\ua0' =~ '^.$' ]] && print OK - [[ $'\ua0' =~ $'^\ua0$' ]] && print OK - [[ $'\ua0'X =~ '^X$' ]] || print OK - else - ZTST_skip="regexp library not found." - fi -0:Ensure no confusion on metafied input to regex module ->OK ->OK ->OK - () { emulate -L zsh setopt errreturn @@ -553,3 +541,47 @@ 0:${(q+)...} with printable multibyte characters >ホ >'She said "ホ". I said "You can'\''t '\''ホ'\'' me!' + +# This will silently succeed if zsh/parameter isn't available + (zmodload zsh/parameter >/dev/null 2>&1 + f() { + : $(:) + "↓" + } + : $functions) +0:Multibyte handling of functions parameter + +# c1=U+0104 (Ą) and c2=U+0120 (Ġ) are chosen so that +# u1 = utf8(c1) = c4 84 < u2 = utf8(c2) = c4 a0 +# metafy(u1) = c4 83 a4 > metafy(u2) = c4 83 80 +# in both UTF-8 and ASCII collations (the latter is used in macOS +# and some versions of BSDs). + local -a names=( $'\u0104' $'\u0120' ) + print -o $names + mkdir -p colltest + cd colltest + touch $names + print ? +0:Sorting of metafied characters +>Ą Ġ +>Ą Ġ + + printf '%q%q\n' 你你 +0:printf %q and quotestring and general metafy / token madness +>你你 + +# This test is kept last as it introduces an additional +# dependency on the system regex library. + if zmodload zsh/regex 2>/dev/null; then + [[ $'\ua0' =~ '^.$' ]] && print OK + [[ $'\ua0' =~ $'^\ua0$' ]] && print OK + [[ $'\ua0'X =~ '^X$' ]] || print OK + else + ZTST_skip="regexp library not found." + fi +0:Ensure no confusion on metafied input to regex module +>OK +>OK +>OK +F:A failure here may indicate the system regex library does not +F:support character sets outside the portable 7-bit range. diff --git a/Test/D08cmdsubst.ztst b/Test/D08cmdsubst.ztst index 89e725966..4e0759e35 100644 --- a/Test/D08cmdsubst.ztst +++ b/Test/D08cmdsubst.ztst @@ -153,3 +153,27 @@ eval 'foo echo this just works, OK\?)' 0:backtracking within command string parsing with alias still pending >this just works, OK? + + ( + set errexit + show_nargs() { print $#; } + print a $() b + print c "$()" d + ) +0:Empty $() is a valid empty substitution. +>a b +>c d + + empty=$() && print "'$empty'" +0:Empty $() is a valid assignment +>'' + + ( + setopt ignoreclosebraces + alias OPEN='{' CLOSE='};' + eval '{ OPEN print hi; CLOSE } + var=$({ OPEN print bye; CLOSE}) && print $var' + ) +0:Alias expansion needed in parsing substituions +>hi +>bye diff --git a/Test/E01options.ztst b/Test/E01options.ztst index 40e96afc9..dac9430cc 100644 --- a/Test/E01options.ztst +++ b/Test/E01options.ztst @@ -784,6 +784,10 @@ >unsetting option... ?(eval):14: no such file or directory: pathtestdir/findme + (setopt pathdirs; path+=( /usr/bin ); type ./env) +1:whence honours PATH_DIRS option +>./env not found + setopt posixbuiltins PATH= command -v print PATH= command -V print @@ -800,6 +804,20 @@ >print is a shell builtin ?(eval):8: command not found: print + ( + setopt posixbuiltins + opts=() + command $opts print foo + opts=(-v) + command $opts print + opts=(-V) + command $opts print + ) +0:command with options from expansion +>foo +>print +>print is a shell builtin + # With non-special command: original value restored # With special builtin: new value kept # With special builtin preceeded by "command": original value restored. @@ -1114,7 +1132,8 @@ integer foo6=9 (( foo6=10 )) } - fn + # don't pollute the test environment with the variables... + (fn) 0:WARN_CREATE_GLOBAL option ?fn:3: scalar parameter foo1 created globally in function fn ?fn:5: scalar parameter foo1 created globally in function fn @@ -1129,6 +1148,103 @@ fn 0:WARN_CREATE_GLOBAL negative cases + ( + foo1=global1 foo2=global2 foo3=global3 foo4=global4 + integer foo5=5 + # skip foo6, defined in fn_wnv + foo7=(one two) + fn_wnv() { + # warns + foo1=bar1 + # doesn't warn + local foo2=bar3 + unset foo2 + # still doesn't warn + foo2=bar4 + # doesn't warn + typeset -g foo3=bar5 + # warns + foo3=bar6 + fn2() { + # warns if global option, not attribute + foo3=bar6 + } + fn2 + # doesn't warn + foo4=bar7 =true + # warns + (( foo5=8 )) + integer foo6=9 + # doesn't warn + (( foo6=10 )) + foo7[3]=three + foo7[4]=(four) + } + print option off >&2 + fn_wnv + print option on >&2 + setopt warnnestedvar + fn_wnv + unsetopt warnnestedvar + print function attribute on >&2 + functions -W fn_wnv + fn_wnv + print all off again >&2 + functions +W fn_wnv + fn_wnv + ) +0:WARN_NESTED_VAR option +?option off +?option on +?fn_wnv:2: scalar parameter foo1 set in enclosing scope in function fn_wnv +?fn_wnv:11: scalar parameter foo3 set in enclosing scope in function fn_wnv +?fn2:2: scalar parameter foo3 set in enclosing scope in function fn2 +?fn_wnv:20: numeric parameter foo5 set in enclosing scope in function fn_wnv +?function attribute on +?fn_wnv:2: scalar parameter foo1 set in enclosing scope in function fn_wnv +?fn_wnv:11: scalar parameter foo3 set in enclosing scope in function fn_wnv +?fn_wnv:20: numeric parameter foo5 set in enclosing scope in function fn_wnv +?all off again + + + ( + setopt warnnestedvar + () { + typeset -A a + : ${a[hello world]::=foo} + print ${(t)a} + key="hello world" + print $a[$key] + } + ) +0:No false positive on parameter used with subscripted assignment +>association-local +>foo + + ( + setopt warnnestedvar + () { + local var=(one two) + () { var=three; } + print $var + } + ) +0:Warn when changing type of nested variable: array to scalar. +?(anon): scalar parameter var set in enclosing scope in function (anon) +>three + + ( + setopt warnnestedvar + () { + local var=three + () { var=(one two); } + print $var + } + ) +0:Warn when changing type of nested variable: scalar to array. +?(anon): array parameter var set in enclosing scope in function (anon) +>one two + # This really just tests if XTRACE is egregiously broken. # To test it properly would need a full set of its own. fn() { print message; } diff --git a/Test/E02xtrace.ztst b/Test/E02xtrace.ztst index 093a587bd..da6191cd0 100644 --- a/Test/E02xtrace.ztst +++ b/Test/E02xtrace.ztst @@ -120,10 +120,29 @@ ?+./fnfile:3> print This is fn. set -x + [[ 'f o' == 'f x'* || 'b r' != 'z o' && 'squashy sound' < 'squishy sound' ]] [[ 'f o' = 'f x'* || 'b r' != 'z o' && 'squashy sound' < 'squishy sound' ]] [[ -e nonexistentfile || ( -z '' && -t 3 ) ]] set +x 0:Trace for conditions ?+(eval):2> [[ 'f o' == f\ x* || 'b r' != z\ o && 'squashy sound' < 'squishy sound' ]] -?+(eval):3> [[ -e nonexistentfile || -z '' && -t 3 ]] -?+(eval):4> set +x +?+(eval):3> [[ 'f o' = f\ x* || 'b r' != z\ o && 'squashy sound' < 'squishy sound' ]] +?+(eval):4> [[ -e nonexistentfile || -z '' && -t 3 ]] +?+(eval):5> set +x + + # Part 1: Recurses into nested anonymous functions + fn() { + () { () { true } } + } + functions -T fn + fn + # Part 2: Doesn't recurse into named functions + gn() { true } + fn() { gn } + functions -T fn + fn +0:tracing recurses into anonymous functions +?+fn:1> '(anon)' +?+(anon):0> '(anon)' +?+(anon):0> true +?+fn:0> gn diff --git a/Test/V01zmodload.ztst b/Test/V01zmodload.ztst index 349ae9c89..092f9d1c7 100644 --- a/Test/V01zmodload.ztst +++ b/Test/V01zmodload.ztst @@ -271,6 +271,80 @@ 0:Listing feature autoloads includes unloaded modules >zmodload -Fa zsh/zftp b:zftp + if ! zmodload zsh/system >/dev/null 2>&1; then + ZTST_skip="zsh/system module not available" + else + zmodload -lF zsh/system + zmodload -F zsh/system -p:errnos + print ${+errnos} + zmodload -lF zsh/system + zmodload -F zsh/system +p:errnos + print ${+errnos} + zmodload -lF zsh/system + fi +0:Regression tests for index bug with math functions. +>+b:syserror +>+b:sysread +>+b:syswrite +>+b:sysopen +>+b:sysseek +>+b:zsystem +>+f:systell +>+p:errnos +>+p:sysparams +>0 +>+b:syserror +>+b:sysread +>+b:syswrite +>+b:sysopen +>+b:sysseek +>+b:zsystem +>+f:systell +>-p:errnos +>+p:sysparams +>1 +>+b:syserror +>+b:sysread +>+b:syswrite +>+b:sysopen +>+b:sysseek +>+b:zsystem +>+f:systell +>+p:errnos +>+p:sysparams + + if ! zmodload zsh/system >/dev/null 2>&1; then + ZTST_skip="zsh/system module not available" + else + zmodload -F zsh/system -f:systell + zmodload -lF zsh/system + (print $(( systell(-1) ))) + zmodload -F zsh/system +f:systell + zmodload -lF zsh/system + (print $(( systell(-1) ))) + fi +1:Module Features for math functions +>+b:syserror +>+b:sysread +>+b:syswrite +>+b:sysopen +>+b:sysseek +>+b:zsystem +>-f:systell +>+p:errnos +>+p:sysparams +>+b:syserror +>+b:sysread +>+b:syswrite +>+b:sysopen +>+b:sysseek +>+b:zsystem +>+f:systell +>+p:errnos +>+p:sysparams +?(eval):6: unknown function: systell +?(eval):9: file descriptor out of range + %clean eval "$deps" diff --git a/Test/V06parameter.ztst b/Test/V06parameter.ztst index c7df35dce..27d587852 100644 --- a/Test/V06parameter.ztst +++ b/Test/V06parameter.ztst @@ -1,15 +1,22 @@ +%prep + + setopt chaselinks + cd . + unsetopt chaselinks + mydir=$PWD + %test print 'print In sourced file - print $LINENO + $functrace + $funcsourcetrace + print $LINENO + $functrace + ${funcsourcetrace} ' >sourcedfile print -r -- 'print Started functrace.zsh module_path=(./Modules) - print $LINENO + $functrace + $funcsourcetrace + print $LINENO + $functrace + ${funcsourcetrace} : fn() { print Inside function $0 - print $LINENO + $functrace + $funcsourcetrace + print $LINENO + $functrace + ${funcsourcetrace} } : fn @@ -17,7 +24,7 @@ fpath=(. $fpath) : echo '\''print Inside $0 - print $LINENO + $functrace + $funcsourcetrace + print $LINENO + $functrace + ${funcsourcetrace} '\'' >autofn : autoload autofn @@ -26,15 +33,15 @@ autofn . ./sourcedfile' >functrace.zsh $ZTST_testdir/../Src/zsh +Z -f ./functrace.zsh -0:Function tracing +0q:Function tracing >Started functrace.zsh >3 + + >Inside function fn >2 + ./functrace.zsh:10 + ./functrace.zsh:5 >Inside autofn ->2 + ./functrace.zsh:20 + ./autofn:0 +>2 + ./functrace.zsh:20 + $mydir/autofn:0 >Inside autofn ->2 + ./functrace.zsh:21 + ./autofn:0 +>2 + ./functrace.zsh:21 + $mydir/autofn:0 >In sourced file >2 + ./functrace.zsh:22 + ./sourcedfile:0 @@ -66,6 +73,25 @@ >./rocky3.zsh:13 (eval):2 >./rocky3.zsh:14 ./rocky3.zsh:14 + ( + fpath=($PWD) + print "print I have been autoloaded" >myfunc + autoload $PWD/myfunc + print ${functions_source[myfunc]} + myfunc + print ${functions_source[myfunc]} + ) +0q: $functions_source +>$mydir/myfunc +>I have been autoloaded +>$mydir/myfunc + + functions+=(a 'echo foo'); a + functions+=(a 'echo bar'); a +0:$functions can be appended to twice +>foo +>bar + %clean - rm -f autofn functrace.zsh rocky3.zsh sourcedfile + rm -f autofn functrace.zsh rocky3.zsh sourcedfile myfunc diff --git a/Test/V09datetime.ztst b/Test/V09datetime.ztst index 7905155d8..ffad96c04 100644 --- a/Test/V09datetime.ztst +++ b/Test/V09datetime.ztst @@ -8,19 +8,21 @@ # It's not clear this skip_extensions is correct, but the # format in question is causing problems on Solaris. # We'll revist this after the release. - [[ "$(strftime %^_10B 0)" = " JANUARY" ]] || skip_extensions=1 - [[ "$(LC_TIME=ja_JP.UTF-8 strftime %OS 1)" = 一 ]] || skip_japanese=1 + [[ "$(strftime %^_10B 0 2>/dev/null)" = " JANUARY" ]] || skip_extensions=1 + [[ "$(LC_TIME=ja_JP.UTF-8 strftime %OS 1 2>/dev/null)" = 一 ]] || skip_japanese=1 else ZTST_unimplemented="can't load the zsh/datetime module for testing" fi %test + strftime '' 0 strftime %y 0 strftime %Y 1000000000 strftime %x 1200000000 strftime %X 1200000001 0:basic format specifiers +> >70 >2001 >01/10/08 @@ -61,6 +63,8 @@ strftime '%^_10B' 0 strftime %03Ey 650000000 strftime %-Oe 0 + # width=400 is too wide and should cause an error + strftime %400d 0 2> /dev/null || echo OK ) fi 0:various extensions @@ -68,7 +72,13 @@ > JANUARY >090 >1 +>OK print -r -- ${(V)"$(strftime $'%Y\0%m\0%d' 100000000)"} 0:Embedded nulls >1973^@03^@03 + +# We assume '%@' is not a valid format on any OSs. +# The result can be '%@' (Linux), '@' (BSDs) or an error (Cygwin). + [[ $(strftime '%@' 0 2> /dev/null) == (%|)@ || $? != 0 ]] +0:bad format specifier diff --git a/Test/V10private.ztst b/Test/V10private.ztst index 320e35764..78ecd48ea 100644 --- a/Test/V10private.ztst +++ b/Test/V10private.ztst @@ -15,11 +15,6 @@ (zmodload -u zsh/param/private && zmodload zsh/param/private) 0:unload and reload the module without crashing - ZTST_verbose=0 $ZTST_exe +Z -f $ZTST_srcdir/ztst.zsh private.TMP/B02 -0:typeset still works with zsh/param/private module loaded -*>* -*>* - typeset scalar_test=toplevel () { print $scalar_test @@ -129,7 +124,7 @@ 0:private hides value from surrounding scope in nested scope >typeset -a hash_test=( top level ) >typeset -A hash_test=( in function ) ->typeset -a hash_test=( top level ) +>typeset -g -a hash_test=( top level ) >array-local top level >top level F:note "typeset" rather than "private" in output from outer @@ -295,6 +290,15 @@ F:future revision will create a global with this assignment () { private -h SECONDS } 0:private parameter may hide a special parameter + if (( UID )); then + ZTST_verbose=0 $ZTST_exe +Z -f $ZTST_srcdir/ztst.zsh private.TMP/B02 + else + ZTST_skip="cannot re-run typeset tests when tests run as superuser" + fi +0:typeset still works with zsh/param/private module loaded +*>* +*>* + %clean rm -r private.TMP diff --git a/Test/V11db_gdbm.ztst b/Test/V11db_gdbm.ztst new file mode 100644 index 000000000..6d74cef2c --- /dev/null +++ b/Test/V11db_gdbm.ztst @@ -0,0 +1,327 @@ +# Tests for the zsh/db/gdbm module. +# This contains literal UTF-8 characters; if editing, use +# UTF-8 mode. + +%prep + + modname="zsh/db/gdbm" + dbfile=db.gdbm + if ! zmodload $modname 2>/dev/null; then + ZTST_unimplemented="can't load $modname module for testing" + fi + rm -f db.gdbm + +%test + + (zmodload -u $modname && zmodload $modname) +0:unload and reload the module without crashing + + ztie -d db/gdbm -f $dbfile dbase + zuntie dbase +0:create the database + + ztie -r -d db/gdbm -f $dbfile dbase + zuntie -u dbase +0:open the database read-only + + ztie -d db/gdbm -f $dbfile dbase + dbase[testkey]=testdata + zuntie dbase + ztie -r -d db/gdbm -f $dbfile dbase + echo $dbase[testkey] + zuntie -u dbase +0:store key in database +>testdata + + ztie -d db/gdbm -f $dbfile dbase2 + unset 'dbase2[testkey]' + zuntie dbase2 + ztie -d db/gdbm -f $dbfile dbase + echo $dbase[testkey] + zuntie dbase +0:remove key from database (different variables) +> + + ztie -d db/gdbm -f $dbfile dbase + dbase[testkey]=testdata + zuntie dbase + ztie -r -d db/gdbm -f $dbfile dbase + echo $dbase[testkey] + zuntie -u dbase + ztie -d db/gdbm -f $dbfile dbase + unset 'dbase[testkey]' + zuntie dbase + ztie -r -d db/gdbm -f $dbfile dbase + echo $dbase[testkey] + zuntie -u dbase +0:store & remove key from database (the same variables) +>testdata +> + + ztie -d db/gdbm -f $dbfile dbase + dbase[testkey]=testdata + dbase[testkey2]=$dbase[testkey] + dbase[testkey3]=$dbase[testkey]x$dbase[testkey2] + zuntie dbase + ztie -d db/gdbm -f $dbfile dbase + echo $dbase[testkey] + echo $dbase[testkey2] + echo $dbase[testkey3] + zuntie dbase +0:store 2 keys fetching 1st +>testdata +>testdata +>testdataxtestdata + + ztie -d db/gdbm -f $dbfile dbase + val=$dbase[testkey2] + unset 'dbase[testkey2]' + echo $val + zuntie dbase +0:unset key that was fetched +>testdata + + ztie -r -d db/gdbm -f $dbfile dbase + local -a result=( "${(kv)dbase[@]}" ) + print -rl -- "${(o)result[@]}" + zuntie -u dbase +0:scan read-only tied hash, directly assign local -a +>testdata +>testdataxtestdata +>testkey +>testkey3 + + ztie -d db/gdbm -f $dbfile dbase + dbase=( a a ) + print -rl -- "${(kv)dbase[@]}" + zuntie dbase +0:Use scan directly, read-write mode +>a +>a + + ztie -d db/gdbm -f $dbfile dbase + dbase=( a b c d ) + zuntie dbase + ztie -d db/gdbm -f $dbfile dbase + result=( "${(kv)dbase[@]}" ) + print -rl -- "${(o)result[@]}" + zuntie dbase +0:replace hash / database, scan +>a +>b +>c +>d + + ztie -d db/gdbm -f $dbfile dbase + local -a arr + arr=( "${dbase[@]}" ) + print -rl -- "${(o)arr[@]}" + zuntie dbase +0:scan with no (kv) +>b +>d + + ztie -d db/gdbm -f $dbfile dbase + result=( "${(k)dbase[@]}" ) + print -rl -- "${(o)result[@]}" + zuntie dbase +0:scan with keys only (k) +>a +>c + + ztie -d db/gdbm -f $dbfile dbase + result=( "${(v)dbase[@]}" ) + print -rl -- "${(o)result[@]}" + zuntie dbase +0:scan with keys only explicit (v) +>b +>d + + rm -f $dbfile + ztie -r -d db/gdbm -f $dbfile dbase 2>/dev/null +1:read-only open non-existent database + + ztie -d db/gdbm -f $dbfile dbase + dbase+=( a b ) + echo $dbase[a] + zuntie dbase + ztie -r -d db/gdbm -f $dbfile dbase + echo $dbase[a] + result=( "${(kv)dbase[@]}" ) + print -rl -- "${(o)result[@]}" + zuntie -u dbase + ztie -d db/gdbm -f $dbfile dbase + dbase+=( c d ) + echo $dbase[a] + echo $dbase[c] + result=( "${(kv)dbase[@]}" ) + print -rl -- "${(o)result[@]}" + zuntie dbase + ztie -r -d db/gdbm -f $dbfile dbase + echo $dbase[a] + echo $dbase[c] + result=( "${(kv)dbase[@]}" ) + print -rl -- "${(o)result[@]}" + zuntie -u dbase +0:Append with +=( ), also with existing data, also (kv) scan +>b +>b +>a +>b +>b +>d +>a +>b +>c +>d +>b +>d +>a +>b +>c +>d + + ztie -d db/gdbm -f $dbfile dbase + echo ${(t)dbase} + zuntie dbase +0:Type of tied parameter +>association-special + + typeset -ga dbase + ztie -d db/gdbm -f $dbfile dbase + echo ${(t)dbase} + zuntie dbase +0:Type of tied parameter, with preceding unset +>association-special + + local -a dbase + ztie -d db/gdbm -f $dbfile dbase + echo ${(t)dbase} + zuntie dbase +0:Type of tied parameter, with local parameter already existing +>association-local-special + + local -a dbase + dbase=( fromarray ) + () { + local -a dbase + ztie -d db/gdbm -f $dbfile dbase + echo ${(t)dbase} + zuntie dbase + } + echo $dbase[1] + ztie -d db/gdbm -f $dbfile dbase2 + echo "Can connect, so untie happened:" $dbase2[a] + zuntie dbase2 +0:Test of automatic untie (use of local scope) and of scoping +>association-local-special +>fromarray +>Can connect, so untie happened: b + + echo $zgdbm_tied ${#zgdbm_tied} + ztie -r -d db/gdbm -f $dbfile dbase + echo $zgdbm_tied ${#zgdbm_tied} + ztie -d db/gdbm -f ${dbfile}2 dbase2 + echo $zgdbm_tied ${#zgdbm_tied} + zuntie -u dbase + echo $zgdbm_tied ${#zgdbm_tied} + zuntie dbase2 + echo $zgdbm_tied ${#zgdbm_tied} +0:zgdbm_tied parameter +>0 +>dbase 1 +>dbase dbase2 2 +>dbase2 1 +>0 + + unset zgdbm_tied 2>/dev/null +1:unset of read-only zgdbm_tied parameter + + ztie -d db/gdbm -f $dbfile dbase + dbase[漢字]=漢字 + echo $dbase[漢字] + zuntie dbase + ztie -r -d db/gdbm -f $dbfile dbase + echo $dbase[漢字] + zuntie -u dbase +0:Unicode test +>漢字 +>漢字 + + key="ab"$'\0'"ef" + ztie -d db/gdbm -f $dbfile dbase + dbase[$key]=value + echo $dbase[$key] + zuntie dbase + ztie -r -d db/gdbm -f $dbfile dbase + echo $dbase[$key] + zuntie -u dbase + ztie -d db/gdbm -f $dbfile dbase + dbase[$key]=$key + zuntie dbase + ztie -d db/gdbm -f $dbfile dbase + [[ "$dbase[$key]" = "$key" ]] && echo correct + zuntie dbase +0:Metafication of $'\0' +>value +>value +>correct + + ztie -d db/gdbm -f $dbfile dbase + dbase=( 漢字 漢字 ) + echo $dbase[漢字] + zuntie dbase + ztie -d db/gdbm -f $dbfile dbase + echo $dbase[漢字] + zuntie dbase + key="ab"$'\0'"ef" + ztie -d db/gdbm -f $dbfile dbase + dbase+=( $key $key ) + zuntie dbase + ztie -r -d db/gdbm -f $dbfile dbase + [[ "$dbase[$key]" = "$key" ]] && echo correct + zuntie -u dbase +0:Unicode & metafication test, different hash access +>漢字 +>漢字 +>correct + + ztie -d db/gdbm -f $dbfile dbase + dbase=( 漢字 漢字 ) + zuntie dbase + ztie -d db/gdbm -f $dbfile dbase + noglob print -rl ${(kv)dbase[@]} + zuntie dbase +0:Hash scanning and metafication +>漢字 +>漢字 + + ztie -d db/gdbm -f $dbfile dbase + noglob print -rl ${(okv)dbase[@]} + zuntie dbase +0:Sorted hash scanning and metafication +>漢字 +>漢字 + + ztie -d db/gdbm -f $dbfile dbase + zgdbmpath dbase + [[ $REPLY = */Test/db.gdbm ]] && echo correct + zuntie dbase + ztie -r -d db/gdbm -f $dbfile dbase + zgdbmpath dbase + [[ $REPLY = */Test/db.gdbm ]] && echo correct + zuntie -u dbase +0:zgdbmpath builtin +>correct +>correct + + ztie -d db/gdbm -f $dbfile dbase + fun() { while read line; do echo $line; done } + eval "dbase[testkey]=value1" | fun + echo $dbase[testkey] +0:Test store in forked Zsh +>value1 + +%clean + + rm -f ${dbfile}* diff --git a/Test/X02zlevi.ztst b/Test/X02zlevi.ztst index ced70300f..d3b533490 100644 --- a/Test/X02zlevi.ztst +++ b/Test/X02zlevi.ztst @@ -130,6 +130,22 @@ >long >CURSOR: 0 + zletest $'one two\e03rX$.' +0:repeat replace chars at the end of the line consumes the replace char +>BUFFER: XXX two +>CURSOR: 6 + + zletest $'one two three\e02rxw3.w.' +0:numeric argument to repeat replaces change count +>BUFFER: xxe xxx xxxee +>CURSOR: 10 + + zletest $'one two three four five six seven eight\e.03d2wk.1.' +0:numeric args to both action and movement are multiplied (and saved for any repeat) +>BUFFER: eight +>seven eight +>CURSOR: 0 + zletest $'yankee doodle\ebhDyy0"1P' 0:paste register 1 to get last deletion >BUFFER: doodleyankee @@ -178,6 +194,11 @@ > >CURSOR: 0 + zletest $'123456789\exxxxxxxxx"1P.........' +0:repeat advances to next killring register +>BUFFER: 9987654321 +>CURSOR: 0 + zletest $'Z\exayankee doodle\e"_db0"_yeP' 0:yank and delete to black hole register >BUFFER: Zyankee e @@ -244,6 +265,12 @@ >BUFFER: binging >CURSOR: 3 + print -u $ZTST_fd 'This test may hang the shell when it fails...' + zletest $'worm\erdhd..' +0:use of vi-repeat as the motion and repeat after a failed change +>BUFFER: wodd +>CURSOR: 2 + zpty_run 'bindkey "^_" undo' zletest $'undoc\037e' 0:use of undo in vi insert mode diff --git a/Test/Y01completion.ztst b/Test/Y01completion.ztst index 1568369c8..113a45076 100644 --- a/Test/Y01completion.ztst +++ b/Test/Y01completion.ztst @@ -9,6 +9,7 @@ cd comp.tmp comptestinit -z $ZTST_testdir/../Src/zsh && { + comptesteval 'compdef _tst tst' mkdir dir1 && mkdir dir2 && touch file1 && @@ -77,6 +78,29 @@ F:regression test workers/32182 >FI:{file2} F:regression test workers/31611 + { + mkdir 'A(B)' 'A(B)/C' + comptest $'cd "A(B)\t\t' + comptesteval 'cd "A(B)/C"' + comptest $'cd ../\t' + } always { + rmdir 'A(B)/C' 'A(B)' + } +0:directory name is not a glob qualifier +>line: {cd "A(B)/}{} +>line: {cd "A(B)/C/}{} +>line: {cd ../C/}{} + + comptesteval "_tst() { compadd -U -s : -S / -I . word; compstate[to_end]= }" + comptest $'tst .\C-b\t' +0:allow for suffixes when moving cursor to end of match (with ignored suffix) +>line: {tst word:/}{.} + + comptesteval "_tst() { compadd -s : -S / word; compstate[to_end]= }" + comptest $'tst \t' +0:allow for suffixes when moving cursor to end of match (without ignored suffix) +>line: {tst word:/}{} + %clean zmodload -ui zsh/zpty diff --git a/Test/Y03arguments.ztst b/Test/Y03arguments.ztst index 0147c7d14..25bb96b84 100644 --- a/Test/Y03arguments.ztst +++ b/Test/Y03arguments.ztst @@ -40,6 +40,14 @@ >NO:{a} >NO:{b} + tst_arguments ':desc2:((a\:a\ value b\:other\\\\value))' + comptest $'tst \t' +0:a and b with descriptions +>line: {tst }{} +>DESCRIPTION:{desc2} +>NO:{a -- a value} +>NO:{b -- other\value} + tst_arguments ':desc1:(arg1)' ':desc2:(arg2)' ':desc3:(arg3)' comptest $'tst \t\t\t\C-w\C-w\C-w\C-d' 0:three arguments @@ -56,6 +64,20 @@ >line: {tst arg1 }{} >MESSAGE:{no more arguments} + tst_arguments -a '2:desc2:(arg2)' + comptest $'tst a1\t \t' +0:second argument but no first argument +>line: {tst a1}{} +>MESSAGE:{no more arguments} +>line: {tst a1 arg2 }{} + + tst_arguments '2:desc2:(arg2)' '*:rest:(rest)' + comptest $'tst \t\t\t' +0:second and rest arguments but no first argument +>line: {tst rest }{} +>line: {tst rest arg2 }{} +>line: {tst rest arg2 rest }{} + tst_arguments '-\+[opt]' comptest $'tst -\C-d' 0:-+ @@ -74,6 +96,14 @@ >line: {tst +o -o }{} >MESSAGE:{no arguments} + tst_arguments +-o + comptest $'tst \t' +0:option beginning with + and -, specified the other way around +>line: {tst }{} +>DESCRIPTION:{option} +>NO:{+o} +>NO:{-o} + tst_arguments '-o:1:(a):2:(b)' comptest $'tst \t\t\t' 0:two option arguments @@ -81,6 +111,74 @@ >line: {tst -o a }{} >line: {tst -o a b }{} + tst_arguments '!-x:arg:(ok)' + comptest $'tst -x \t' +0:option argument to ignored option +>line: {tst -x ok }{} + + tst_arguments '!-a' -b + comptest $'tst -\t' +0:ignored option not completed +>line: {tst -b }{} + + tst_arguments +x +y '!+z' ':arg:(x)' + comptest $'tst +z \t' +0:ignored option is not taken to be the normal argument +>line: {tst +z x }{} + + tst_arguments --known --other + comptest $'tst --unknown -\t' +0:unrecognised option has no effect on proceedings with no normal arguments +>line: {tst --unknown --}{} + + tst_arguments +x +y ':arg:(x)' + comptest $'tst +z \t' +0:unrecognised option is taken to be the normal argument +>line: {tst +z +}{} + + tst_arguments '*-a:value:(1)' + comptest $'tst -a\t\t -a=\t' +0:option argument follows in next argument +>line: {tst -a }{} +>line: {tst -a 1 }{} +>line: {tst -a 1 -a=}{} +>MESSAGE:{no arguments} + + tst_arguments '*-a+:value:(1)' + comptest $'tst -a\t -a \t -a=\t' +0:option argument either direct or in following argument +>line: {tst -a1 }{} +>line: {tst -a1 -a 1 }{} +>line: {tst -a1 -a 1 -a=}{} + + tst_arguments '*-a-:value:(1)' + comptest $'tst -a\t -a \t=\t' +0:option argument follows directly +>line: {tst -a1 }{} +>line: {tst -a1 -a -a}{} +>line: {tst -a1 -a -a=}{} + + tst_arguments '*-a=:value:(1)' + comptest $'tst -a\t\t -a \t' +0:option argument follows optional equals +>line: {tst -a=}{} +>line: {tst -a=1 }{} +>line: {tst -a=1 -a 1 }{} + + tst_arguments -s '*-a=:value:(1)' + comptest $'tst -a\t-a=\t -a \t' +0:option argument follows optional equals, with -s +>line: {tst -a1 }{} +>line: {tst -a1 -a=1 }{} +>line: {tst -a1 -a=1 -a 1 }{} + + tst_arguments '*-a=-:value:(1)' + comptest $'tst -a\t\t-a \t' +0:option argument follows mandatory equals +>line: {tst -a=}{} +>line: {tst -a=1 }{} +>line: {tst -a=1 -a -a=}{} + tst_arguments '-x:arg:' comptest $'tst -x\t' 0:sticky option argument @@ -99,6 +197,11 @@ >DESCRIPTION:{option} >NO:{-x} + tst_arguments '-x' ": :_guard '[0-9]#' number" + comptest $'tst -\t' +0:argument beginning with minus, guard on rest argument +>line: {tst -x }{} + tst_arguments '-o::optarg:(oa)' ':arg1:(a1)' comptest $'tst -o\t\t' 0:optional option argument @@ -110,20 +213,69 @@ >NO:{a1} tst_arguments '-o:*a:a:(a)' ':A:(A)' ':B:(B)' - comptest $'tst A -o a \t' + comptest $'tst A -o a \t\C-W\C-w-a -b -c a \t' 0:variable length option arguments >line: {tst A -o a B }{} +>line: {tst A -o -a -b -c a B }{} tst_arguments -s '-a' '-b' ':descr:{compadd - $+opt_args[-a]}' comptest $'tst -ab \t' 0:opt_args >line: {tst -ab 1 }{} + tst_arguments '-a:one: :two' ':descr:{compadd -Q - $opt_args[-a]}' + comptest $'tst -a 1:x \\2 \t' +0:opt_args with multiple arguments and quoting of colons and backslashes +>line: {tst -a 1:x \2 1\:x:\\2 }{} + + tst_arguments -a -b + comptest $'tst rest -\t\C-w\eb\C-b-\t' +0:option completion with rest arguments on the line but not in the specs +>line: {tst rest -}{} +>line: {tst -}{ rest } +>DESCRIPTION:{option} +>NO:{-a} +>NO:{-b} + tst_arguments '-a' '*::rest:{compadd - -b}' comptest $'tst arg -\t' 0:rest arguments >line: {tst arg -b }{} + tst_arguments -a :more '*:rest:{ compadd - $words }' + comptest $'tst x -a rest \t' +0:rest arguments with single colon +>line: {tst x -a rest }{} +>NO:{-a} +>NO:{rest} +>NO:{tst} +>NO:{x} + + tst_arguments -a :more '*::rest:{ compadd - $words }' + comptest $'tst x -a rest \t\eb\eb\eb\et\C-E \t' +0:rest arguments with two colons +>line: {tst x -a rest rest }{} +>line: {tst -a x rest rest }{} +>NO:{rest} +>NO:{x} + + tst_arguments -a -b :more '*:::rest:{ compadd - $words }' + comptest $'tst -b x -a -x rest \t' +0:rest arguments with three colons +>line: {tst -b x -a -x rest }{} +>NO:{-x} +>NO:{rest} + + tst_arguments -a ::more '*:::rest:{ compadd - $words }' + comptest $'tst -a opt rest \t' +0:rest arguments with three colons following optional argument +>line: {tst -a opt rest rest }{} + + tst_arguments -a::arg '*:::rest:{ compadd - $words }' + comptest $'tst -a opt rest \t' +0:rest arguments with three colons following optional argument to an option +>line: {tst -a opt rest rest }{} + tst_arguments '-e:*last:::b:{compadd "${(j:,:)words}"}' ':arg1:(arg1)' comptest $'tst -\t\tla\t\C-hst\t\t\eb\eb\C-b\t\t' 0:words array in rest arguments @@ -135,14 +287,14 @@ >line: {tst -e ,last }{ last arg1} >line: {tst -e ,last ,last,,last }{ last arg1} - tst_arguments -s '-d+:msg1:' '*::msg2:{compadd $CURRENT}' + tst_arguments -s '-d+:msg1:' '*::msg2:{compadd $CURRENT}' comptest $'tst add \t\t\t' 0:opt_args >line: {tst add 2 }{} >line: {tst add 2 3 }{} >line: {tst add 2 3 4 }{} - tst_arguments -s '-a' '-b' '-c' ':words:compadd - abyyy abzzz' + tst_arguments -s '-a' '-b' '-c' ':words:compadd - abyyy abzzz' comptest $'tst ab\t' 0:options and words (zsh-workers:12257) >line: {tst ab}{} @@ -150,6 +302,384 @@ >NO:{abyyy} >NO:{abzzz} + tst_arguments -M 'm:{j}={y}' -y -n ':yes/no:(y n)' + comptest $'tst j\t\eb-\C-e\t' +0:matcher applies to options but not rest arguments +>line: {tst j}{} +>line: {tst -y }{} + + tst_arguments -M 'm:{j}={y}' -y -n - set1 -i - set2 -k + comptest $'tst -k -j\t' +0:matcher in combination with sets (implies separate cadef structure) +>line: {tst -k -y }{} + + tst_arguments -x :word + comptest $'tst -- -\t' +0:option after -- +>line: {tst -- -x }{} + + tst_arguments -S -x ':word:()' + comptest $'tst -- -\t' +0:disallowed option after -- +>line: {tst -- -}{} + + tst_arguments -S -x ':word:()' + comptest $'tst - --\eB\C-b\t' +0:allowed option before -- +>line: {tst -x }{ --} + + tst_arguments -x :word + comptest $'tst word -\t' +0:option after a word +>line: {tst word -x }{} + + tst_arguments -A'-*' -x :word + comptest $'tst word -\t' +0:option after word that doesn't match -A pattern, no space before pattern +>line: {tst word -}{} +>MESSAGE:{no more arguments} + + tst_arguments -A '-*' -x ':word:(-word)' + comptest $'tst word\eB\C-b-\t' +0:option before a word that doesn't match -A pattern, separate -A from pattern +>line: {tst -}{ word} +>DESCRIPTION:{word} +>NO:{-word} +>DESCRIPTION:{option} +>NO:{-x} + + tst_arguments -A '-*' -h -V -a '*: :(-x more)' + comptest $'tst -a -x m\t' +0:continue completion after rest argument that looks like an option +>line: {tst -a -x more }{} + + tst_arguments '*-v' + comptest $'tst -v -\t' +0:repeatable options +>line: {tst -v -v }{} + + tst_arguments -A '-*' - help -h -V - other -a '*: :(-x more)' + comptest $'tst -a -x m\t' +0:continue completion after rest argument that looks like an option (with sets) +>line: {tst -a -x more }{} + + tst_arguments - '(help)' -h -V - other -a '*:rest:(1 2 3)' + comptest $'tst -h \t-a \t' +0:foreign option disables whole set (without -A) +>line: {tst -h }{} +>MESSAGE:{no arguments} +>line: {tst -h -a }{} + + tst_arguments -A "-*" - '(help)' -h -V - other -a '*:rest:(1 2 3)' + comptest $'tst -h \t-a \t' +0:foreign option disables whole set (with -A) +>line: {tst -h }{} +>MESSAGE:{no arguments} +>line: {tst -h -a }{} + + tst_arguments '(-C)-a' - set1 -C -v - set2 '(-a)-C' -w + comptest $'tst -a -\t' $'\C-w\C-w-C -\t' +0:exclude option common to two sets and from one common option +>line: {tst -a -}{} +>DESCRIPTION:{option} +>NO:{-v} +>NO:{-w} +>line: {tst -C -}{} +>DESCRIPTION:{option} +>NO:{-a} +>NO:{-v} +>NO:{-w} + +# _arguments doesn't know what rest arguments might be so any non-option +# might apply: for the one set, it accepts "a" + tst_arguments -e - one -o '*:number:(1 2)' - two '(-e)*:letter:(a b)' + comptest $'tst \t' $'a -\t' +0:rest argument rule in two sets +>line: {tst }{} +>DESCRIPTION:{letter} +>NO:{a} +>NO:{b} +>DESCRIPTION:{number} +>NO:{1} +>NO:{2} +>line: {tst a -}{} +>DESCRIPTION:{option} +>NO:{-e} +>NO:{-o} + + tst_arguments '(set1-: set2-* set3-1)-a' - set1 '1: :(1)' '2: :(2)' - set2 '*:rest:(rest)' - set3 '1:num:(num)' '*: :(allowable)' - set4 ': :(allowed)' + comptest $'tst -a \t' +0:exclude various forms of rest argument in set specific form +>line: {tst -a allow}{} + + tst_arguments '(-v)-a' '(set1--m -a)-b' - '(set1)' '( -a )-m' '( )-n' - set2 '(-w)*-v' -w + comptest $'tst -a -\t' $'\C-w\C-w-'{b,m,v}$' -\t' +0:exclusion lists +>line: {tst -a -}{} +>DESCRIPTION:{option} +>NO:{-b} +>NO:{-m} +>NO:{-n} +>NO:{-w} +>line: {tst -b -}{} +>DESCRIPTION:{option} +>NO:{-n} +>NO:{-v} +>NO:{-w} +>line: {tst -m -b }{} +>line: {tst -v -}{} +>DESCRIPTION:{option} +>NO:{-a} +>NO:{-b} +>NO:{-v} + + tst_arguments -a - set1 -d - set2 '(set2)-m' -n -o ':arg:(x)' - set2 -x + comptest $'tst -m \t' +0:exclude own set from an option +>line: {tst -m -a }{} + + tst_arguments - set1 '(set2)-a' -m -n - set2 -a -t -u + comptest $'tst -a -\t' +0:exclude later set from an option common to both +>line: {tst -a -}{} +>DESCRIPTION:{option} +>NO:{-m} +>NO:{-n} +>NO:{-t} +>NO:{-u} + + tst_arguments - set2 -a -t -u - set1 '(set2)-a' -m -n + comptest $'tst -a -\t' +0:exclude earlier set from an option common to both +>line: {tst -a -}{} +>DESCRIPTION:{option} +>NO:{-m} +>NO:{-n} +>NO:{-t} +>NO:{-u} + + tst_arguments -x - '(set1)' -a -b - '(set2)' -m -n + comptest $'tst -m -\t' +0:single option sets are still mutually exclusive +>line: {tst -m -x }{} + + tst_arguments '(set-c set-g)-a' '(set)-b' -c + grp -g - set -s + comptest $'tst -a -b -\t' +0:excluding a set doesn't exclude common options as part of the set +>line: {tst -a -b -}{} +>DESCRIPTION:{option} +>NO:{-c} +>NO:{-g} + + tst_arguments '(-)-h' -a -b -c + comptest $'tst -h -\t' +0:exclude all other options +>line: {tst -h -}{} +>MESSAGE:{no arguments} + + tst_arguments -a '(-a)-b' + comptest $'tst - -b\C-b\C-b\C-b\t' +0:exclusion only applies to later words +>line: {tst -}{ -b} +>DESCRIPTION:{option} +>NO:{-a} +>NO:{-b} +F:shouldn't offer -b as it is already on the command-line + + tst_arguments -s : '(-d)-a' -b -c -d + comptest $'tst -ab\t\C-h -\t\eb\eb \C-b-\t' +0:exclusion with clumped options, in, after and before +>line: {tst -abc}{} +>line: {tst -ab -c }{} +>line: {tst -}{ -ab -c} +>DESCRIPTION:{option} +>NO:{-a} +>NO:{-b} +>NO:{-c} +>NO:{-d} + + tst_arguments -s '(-conf)-c' '-conf' '-f' '(-)--long' --longer + comptest $'tst -c\t\C-h-long\t' +0:don't prematurely exclude option that current word is a prefix of +>line: {tst -c}{} +>DESCRIPTION:{option} +>NO:{-conf} +>NO:{-f} +>line: {tst --long}{} +>DESCRIPTION:{option} +>NO:{--long} +>NO:{--longer} + + tst_arguments -s '(set)-c' - set '-conf' '-f' + comptest $'tst -c\t' +0:don't prematurely exclude option that current word is a prefix of (with sets) +>line: {tst -conf }{} + + tst_arguments -s : -ad '(-d)-a' -b -ca -d + comptest $'tst -ad\t-b\t' +0:option clumping mixed with real option that looks like clump +>line: {tst -ad }{} +>line: {tst -ad -b}{} +>DESCRIPTION:{option} +>NO:{-a} +>NO:{-d} + + tst_arguments -s : '(-d)-a+:arg:(b)' '(-c)-b' -c -d + comptest $'tst -ab\t-\t' +0:option clumping mixed with direct argument +>line: {tst -ab }{} +>line: {tst -ab -}{} +>DESCRIPTION:{option} +>NO:{-b} +>NO:{-c} + + tst_arguments '-a:arg' -b '(-b)-c' + comptest $'tst -a -c -\t' +0:exclusion with option argument that looks like an option +>line: {tst -a -c -}{} +>MESSAGE:{no arguments} +F:The current behaviour is wrong; the correct expected output is: +F:>line: {tst -a -c -}{} +F:>DESCRIPTION:{option} +F:>NO:{-b} +F:>NO:{-c} + + tst_arguments + grp1 -a -b - onlyset '(-a grp3--y grp2 grp4--)-m' -n + grp2 -u -v + grp3 -x -y + grp4 -0 ':rest' + comptest $'tst -m -\t' +0:exclude group options +>line: {tst -m -}{} +>DESCRIPTION:{rest} +>DESCRIPTION:{option} +>NO:{-b} +>NO:{-n} +>NO:{-x} + + tst_arguments -x + '(grp1)' -a -b + '(grp2)' -m -n + comptest $'tst -m -\t' +0:single option groups are not mutually exclusive +>line: {tst -m -}{} +>DESCRIPTION:{option} +>NO:{-a} +>NO:{-b} +>NO:{-x} + + tst_arguments '(grp1 grp2-2)-a' '(grp1-*)-b' + grp1 ':first' '*:rest' + grp2 ':second' + comptest $'tst -a \t\eb\C-w\C-e x y \t' +0:exclude rest args listed within a group +>line: {tst -a -b }{} +>line: {tst -b x y -a }{} + + tst_arguments '(grp--m)-a' + grp '-m:value:(a b c)' + agrp '-m:other:(1 2 3)' + comptest $'tst -m \t\C-w-a -m \t' +0:two forms of same option in different groups +>line: {tst -m }{} +>DESCRIPTION:{value} +>NO:{a} +>NO:{b} +>NO:{c} +>line: {tst -a -m }{} +>DESCRIPTION:{other} +>NO:{1} +>NO:{2} +>NO:{3} +F:should offer both sets of arguments in first case + + tst_arguments '(grp--m)-x' + '(grp)' -a -b -c ':val:(1 2 3)' + comptest $'tst 1 -\t' +0:normal argument excludes options in internally mutually exclusive group +>line: {tst 1 -x }{} + + tst_arguments -s -a - set1 -c -s - set2 -c -t + grp -d + comptest $'tst -s\t -\t' +0:mix sets, groups and option stacking +>line: {tst -s}{} +>DESCRIPTION:{option} +>NO:{-a} +>NO:{-c} +>NO:{-d} +>NO:{-t} +>line: {tst -s -}{} +>DESCRIPTION:{option} +>NO:{-a} +>NO:{-c} +>NO:{-d} +F:shouldn't offer -t in the first case (with stacked options) + + tst_arguments -s '(set-a set--b grp-a grp--b grp-)-a' - set-a -s - set--b -t + grp-a -g + grp--b -h + grp- -i + comptest $'tst -a\t' +0:sets and groups with - in their name +>line: {tst -a}{} +>DESCRIPTION:{option} +>NO:{-h} +>NO:{-t} + + tst_arguments --abc --aah :arg: + comptesteval 'setopt bashautolist automenu' + comptest $'tst --a\t\t\t' +0:with message and bashautolist, a third tab will get menu completion +>line: {tst --a}{} +>line: {tst --a}{} +>DESCRIPTION:{arg} +>DESCRIPTION:{option} +>NO:{--aah} +>NO:{--abc} +>line: {tst --aah}{} + + tst_arguments --abc --aah :arg: + comptesteval 'setopt bashautolist noautomenu' + comptest $'tst --a\t\t\t' +0:with message and bashautolist, a third tab is needed to display the list +>line: {tst --a}{} +>line: {tst --a}{} +>DESCRIPTION:{arg} +>DESCRIPTION:{option} +>NO:{--aah} +>NO:{--abc} +>line: {tst --a}{} + + tst_arguments --abc --aah :arg: + comptesteval 'setopt nobashautolist noautomenu' + comptest $'tst --\t\t' +0:with message and noautomenu second tab redisplays the list +>line: {tst --}{} +>DESCRIPTION:{arg} +>DESCRIPTION:{option} +>NO:{--aah} +>NO:{--abc} +>line: {tst --}{} +>DESCRIPTION:{arg} +>DESCRIPTION:{option} +>NO:{--aah} +>NO:{--abc} + + tst_arguments --abc --aah :arg: + comptesteval 'setopt nobashautolist automenu' + comptest $'tst --\t\t' +0:with message two tabs will start menu completion +>line: {tst --}{} +>DESCRIPTION:{arg} +>DESCRIPTION:{option} +>NO:{--aah} +>NO:{--abc} +>line: {tst --aah}{} + + tst_arguments --abc --aah :arg: + comptesteval 'zstyle ":completion:*::::" completer _oldlist _complete' + comptest $'tst --\t\t' +0:with message and _oldlist, two tabs will start menu completion +>line: {tst --}{} +>DESCRIPTION:{arg} +>DESCRIPTION:{option} +>NO:{--aah} +>NO:{--abc} +>line: {tst --aah}{} + + tst_arguments '--prefix=: :(one two)' '--prop=: :(three four)' --pre + comptest $'tst --pre=o\t --=\t' +0:partial completion of option with an argument +>line: {tst --prefix=one }{} +>line: {tst --prefix=one --prop=}{} + %clean zmodload -ui zsh/zpty diff --git a/Test/ztst.zsh b/Test/ztst.zsh index cdd84b55d..0b2679927 100755 --- a/Test/ztst.zsh +++ b/Test/ztst.zsh @@ -104,19 +104,23 @@ fpath=( $ZTST_srcdir/../Functions/*~*/CVS(/) $ZTST_srcdir/../Completion/*/*~*/CVS(/) ) : ${TMPPREFIX:=/tmp/zsh} +ZTST_tmp=${TMPPREFIX}.ztst.$$ +if ! rm -f $ZTST_tmp || ! mkdir -p $ZTST_tmp || ! chmod go-w $ZTST_tmp; then + print "Can't create $ZTST_tmp for exclusive use." >&2 + exit 1 +fi # Temporary files for redirection inside tests. -ZTST_in=${TMPPREFIX}.ztst.in.$$ +ZTST_in=${ZTST_tmp}/ztst.in # hold the expected output -ZTST_out=${TMPPREFIX}.ztst.out.$$ -ZTST_err=${TMPPREFIX}.ztst.err.$$ +ZTST_out=${ZTST_tmp}/ztst.out +ZTST_err=${ZTST_tmp}/ztst.err # hold the actual output from the test -ZTST_tout=${TMPPREFIX}.ztst.tout.$$ -ZTST_terr=${TMPPREFIX}.ztst.terr.$$ +ZTST_tout=${ZTST_tmp}/ztst.tout +ZTST_terr=${ZTST_tmp}/ztst.terr ZTST_cleanup() { cd $ZTST_testdir - rm -rf $ZTST_testdir/dummy.tmp $ZTST_testdir/*.tmp(N) \ - ${TMPPREFIX}.ztst*$$(N) + rm -rf $ZTST_testdir/dummy.tmp $ZTST_testdir/*.tmp(N) ${ZTST_tmp} } # This cleanup always gets performed, even if we abort. Later, @@ -330,10 +334,10 @@ ZTST_diff() { diff_ret=1 fi else - diff_out=$(diff "$@") + diff_out=$(diff -a "$@") diff_ret="$?" if [[ "$diff_ret" != "0" ]]; then - print -r "$diff_out" + print -r -- "$diff_out" fi fi @@ -458,7 +462,7 @@ $(<$ZTST_terr)" rm -rf $ZTST_out print -r -- "${(e)substlines}" >$ZTST_out fi - if [[ $ZTST_flags != *d* ]] && ! ZTST_diff $diff_out -c $ZTST_out $ZTST_tout; then + if [[ $ZTST_flags != *d* ]] && ! ZTST_diff $diff_out -u $ZTST_out $ZTST_tout; then ZTST_testfailed "output differs from expected as shown above for: $ZTST_code${$(<$ZTST_terr):+ Error output: @@ -470,7 +474,7 @@ $(<$ZTST_terr)}" rm -rf $ZTST_err print -r -- "${(e)substlines}" >$ZTST_err fi - if [[ $ZTST_flags != *D* ]] && ! ZTST_diff $diff_err -c $ZTST_err $ZTST_terr; then + if [[ $ZTST_flags != *D* ]] && ! ZTST_diff $diff_err -u $ZTST_err $ZTST_terr; then ZTST_testfailed "error output differs from expected as shown above for: $ZTST_code" return 1 |