diff options
Diffstat (limited to 'Test')
-rw-r--r-- | Test/A01grammar.ztst | 36 | ||||
-rw-r--r-- | Test/A02alias.ztst | 7 | ||||
-rw-r--r-- | Test/A04redirect.ztst | 5 | ||||
-rw-r--r-- | Test/A05execution.ztst | 16 | ||||
-rw-r--r-- | Test/A06assign.ztst | 21 | ||||
-rw-r--r-- | Test/B02typeset.ztst | 28 | ||||
-rw-r--r-- | Test/B03print.ztst | 5 | ||||
-rw-r--r-- | Test/C02cond.ztst | 8 | ||||
-rw-r--r-- | Test/C03traps.ztst | 11 | ||||
-rw-r--r-- | Test/D01prompt.ztst | 2 | ||||
-rw-r--r-- | Test/D02glob.ztst | 26 | ||||
-rw-r--r-- | Test/D04parameter.ztst | 107 | ||||
-rw-r--r-- | Test/D10nofork.ztst | 515 | ||||
-rw-r--r-- | Test/E01options.ztst | 63 | ||||
-rw-r--r-- | Test/K01nameref.ztst | 893 | ||||
-rw-r--r-- | Test/K02parameter.ztst | 154 | ||||
-rw-r--r-- | Test/README | 1 | ||||
-rw-r--r-- | Test/V07pcre.ztst | 37 | ||||
-rw-r--r-- | Test/V10private.ztst | 262 | ||||
-rw-r--r-- | Test/X04zlehighlight.ztst | 2 | ||||
-rw-r--r-- | Test/X05zleincarg.ztst | 562 | ||||
-rw-r--r-- | Test/Y01completion.ztst | 79 | ||||
-rw-r--r-- | Test/comptest | 8 | ||||
-rw-r--r-- | Test/runtests.zsh | 1 | ||||
-rwxr-xr-x | Test/ztst.zsh | 4 |
25 files changed, 2811 insertions, 42 deletions
diff --git a/Test/A01grammar.ztst b/Test/A01grammar.ztst index b3aea1055..d57085798 100644 --- a/Test/A01grammar.ztst +++ b/Test/A01grammar.ztst @@ -982,3 +982,39 @@ F:its expectations. } fn 1:! does not affect return status of explicit return + + msg=unset + for x in 1 2 3 4 5; do + continue && msg=set && print Not executed + print Not executed, neither. + done + print $msg +0:continue causes immediate continuation +>unset + + msg=unset + () { + return && msg=set && print Not executed + print Not executed, not nor neither. + } + print $msg +0:return causes immediate return +>unset + + msg=unset + for x in 1 2 3 4 5; do + ! continue || msg=set && print Not executed + print Not executed, neither. + done + print $msg +0:! continue causes immediate continuation +>unset + + msg=unset + () { + ! return || msg=set && print Not executed + print Not executed, not nor neither. + } + print $msg +0:! return causes immediate return +>unset diff --git a/Test/A02alias.ztst b/Test/A02alias.ztst index ca415fa39..1c6969e74 100644 --- a/Test/A02alias.ztst +++ b/Test/A02alias.ztst @@ -123,7 +123,12 @@ 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 firstalias=notacommand + alias secondalias=firstalias + eval 'secondalias() { print does not work either; }') +1:ALIAS_FUNC_DEF reports original alias if multiple +?(eval):1: defining function based on alias `secondalias' (alias goodalias=isafunc setopt ALIAS_FUNC_DEF diff --git a/Test/A04redirect.ztst b/Test/A04redirect.ztst index 17f6dfa29..dc62efab3 100644 --- a/Test/A04redirect.ztst +++ b/Test/A04redirect.ztst @@ -3,9 +3,8 @@ %prep mkdir redir.tmp && cd redir.tmp - myfd=99 - (echo >&$myfd) 2>msg - bad_fd_msg="${$(<msg)##*:}" + bad_fd_msg="${$( { exec 9>&-; echo >&9 } 2>&1)##*:}" + [[ -n "$bad_fd_msg" ]] %test diff --git a/Test/A05execution.ztst b/Test/A05execution.ztst index bcadc6d56..07a24f9c8 100644 --- a/Test/A05execution.ztst +++ b/Test/A05execution.ztst @@ -2,7 +2,7 @@ storepath=($path) - mkdir command.tmp command.tmp/dir1 command.tmp/dir2 + mkdir command.tmp command.tmp/dir{1,2} command.tmp/{+,-}dir cd command.tmp @@ -21,7 +21,10 @@ print '#!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxnyyy' >tstcmd-interp-too-long print "#!${sh}\necho should not execute; exit 1" >xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxn - chmod 755 tstcmd dir1/tstcmd dir2/tstcmd + print 'echo no shebang in -dir' > -dir/tstcmd + print 'echo no shebang in +dir' > +dir/tstcmd + + chmod 755 tstcmd dir{1,2}/tstcmd ./{-,+}dir/tstcmd chmod 755 tstcmd-slashless tstcmd-arg tstcmd-interp-too-long chmod 755 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxn @@ -422,3 +425,12 @@ F:anonymous function, and a descriptor leak when backgrounding a pipeline (exit 4); repeat 0 do done 0:'repeat 0' resets lastval + -dir/tstcmd + +dir/tstcmd + PATH=-dir tstcmd + PATH=+dir tstcmd +0:shebang-less scripts are to be run by sh even when their file paths start with - or + (workers/52515) +>no shebang in -dir +>no shebang in +dir +>no shebang in -dir +>no shebang in +dir diff --git a/Test/A06assign.ztst b/Test/A06assign.ztst index f89edb888..3eff5331a 100644 --- a/Test/A06assign.ztst +++ b/Test/A06assign.ztst @@ -296,13 +296,26 @@ # tests of var+=(array) + a= + a+=(1 2 3) + print "${(q@)a}" +0:add array to empty parameter +>'' 1 2 3 + unset a a+=(1 2 3) - print -l $a + print "${(q@)a}" 0:add array to unset parameter ->1 ->2 ->3 +>1 2 3 + + () { + setopt localoptions typeset_to_unset + typeset a + a+=(1 2 3) + print "${(q@)a}" + } +0:add array to declared unset parameter +>1 2 3 a=(a) a+=(b) diff --git a/Test/B02typeset.ztst b/Test/B02typeset.ztst index 8b3988151..914eea92b 100644 --- a/Test/B02typeset.ztst +++ b/Test/B02typeset.ztst @@ -311,7 +311,7 @@ print $OUTER 0:Export of tied parameters >i:n:n:e:r ->typeset -xT OUTER outer=( i n n e r ) +>local -xT OUTER outer=( i n n e r ) >typeset -aT OUTER outer=( i n n e r ) >OUTER=i:n:n:e:r >outer=( i n n e r ) @@ -959,6 +959,20 @@ > [three]='' >) + () { + local -h status + typeset -p status + } +0:parameter hiding preserved by "typeset -p" +>typeset -h status='' + + () { + local status + typeset -p status + } +0:read-only special params are output when localized +>typeset -i10 -r status=0 + (export PATH MANPATH path=(/bin) MANPATH=/ @@ -1085,12 +1099,12 @@ } 0: no array/hash in POSIX export/readonly -p >zsh: ->typeset -arx zsh_exported_readonly_array=( 2 ) ->typeset -Arx zsh_exported_readonly_hash=( [3]=3 ) ->typeset -rx zsh_exported_readonly_scalar=1 ->typeset -arx zsh_exported_readonly_array=( 2 ) ->typeset -Arx zsh_exported_readonly_hash=( [3]=3 ) ->typeset -rx zsh_exported_readonly_scalar=1 +>local -arx zsh_exported_readonly_array=( 2 ) +>local -Arx zsh_exported_readonly_hash=( [3]=3 ) +>local -rx zsh_exported_readonly_scalar=1 +>local -arx zsh_exported_readonly_array=( 2 ) +>local -Arx zsh_exported_readonly_hash=( [3]=3 ) +>local -rx zsh_exported_readonly_scalar=1 >sh: >export zsh_exported_readonly_scalar=1 >readonly zsh_exported_readonly_scalar=1 diff --git a/Test/B03print.ztst b/Test/B03print.ztst index 4d2cf9764..93a9669b0 100644 --- a/Test/B03print.ztst +++ b/Test/B03print.ztst @@ -305,8 +305,9 @@ foo+=$'\tone\ttwo\tthree\tfour\n' foo+=$'\t\tone\t\ttwo\t\tthree\t\tfour' foo+='\0' # regression test for multibyte tab expand - print -x4 $foo | tr '\0' Z # avoid raw nul byte in expected output below - print -X4 $foo | tr '\0' Z + # avoid raw nul byte in expected output below + print ${"$(print -x4 $foo)"/$'\0'/Z} + print ${"$(print -X4 $foo)"/$'\0'/Z} 0:Tab expansion by print >one two three four > one two three four diff --git a/Test/C02cond.ztst b/Test/C02cond.ztst index 4366b4142..daea5b4f8 100644 --- a/Test/C02cond.ztst +++ b/Test/C02cond.ztst @@ -111,10 +111,6 @@ if (( EUID == 0 )); then print -u$ZTST_fd 'Warning: Not testing [[ ! -r file ]] (root reads anything)' [[ -r zerolength && -r unmodish ]] - elif [[ $OSTYPE = cygwin ]]; then - print -u$ZTST_fd 'Warning: Not testing [[ ! -r file ]] - (all files created by user may be readable)' - [[ -r zerolength ]] else [[ -r zerolength && ! -r unmodish ]] fi @@ -148,9 +144,7 @@ print -ru $ZTST_fd 'This test may take two seconds...' touch $newnewnew - if [[ $OSTYPE == "cygwin" ]]; then - ZTST_skip="[[ -N file ]] not supported on Cygwin" - elif (( isnfs )); then + if (( isnfs )); then ZTST_skip="[[ -N file ]] not supported with NFS" elif ! zmodload -F zsh/stat b:zstat 2> /dev/null; then ZTST_skip='[[ -N file ]] not tested; zsh/stat not available' diff --git a/Test/C03traps.ztst b/Test/C03traps.ztst index e0b6afb5f..de57765a0 100644 --- a/Test/C03traps.ztst +++ b/Test/C03traps.ztst @@ -1083,6 +1083,17 @@ F:Must be tested with a top-level script rather than source or function >trap1 # As of 5.7.1-test-2, the output was "out1 fn1 trap1 fn2" (on separate lines). + TRAPEXIT() { echo This is TRAPEXIT; } + TRAPEXIT + TRAPEXIT + TRAPEXIT +0:No memory problems with explicit call to TRAPEXIT. +>This is TRAPEXIT +>This is TRAPEXIT +>This is TRAPEXIT +>This is TRAPEXIT +# Three explicit calls, one implicit call at function exit. + %clean rm -f TRAPEXIT diff --git a/Test/D01prompt.ztst b/Test/D01prompt.ztst index a0abb7e1d..55861cca1 100644 --- a/Test/D01prompt.ztst +++ b/Test/D01prompt.ztst @@ -261,7 +261,7 @@ A1=${(%):-%s} A2=${(%):-%u} A3=${(%):-%s%u%s} - [[ $A3 = $A1$A2 && -n $A1 && -n $A2 ]] + [[ $A3 = $A1$A2 ]] 0:Attribute optimisation - preserve initial disabling of attribute but drop useless later one : ${(%):-%K{blue}} diff --git a/Test/D02glob.ztst b/Test/D02glob.ztst index 850a535e5..4d88e5c27 100644 --- a/Test/D02glob.ztst +++ b/Test/D02glob.ztst @@ -817,6 +817,32 @@ *>*/glob.tmp/(flip|flop) *>*/glob.tmp/(flip|flop)/trailing/components +# The following set test an obscure problem with branches followed by +# exclusions that shows up when the exclusion matches against +# something other than the complete test string, hence the complicated +# double negative. + [[ ab = (|a*)~^(*b) ]] +0:Regression test for exclusion after branches: empty first alternative + + [[ ab = (b|a*)~^(*b) ]] +0:Regression test for exclusion after branches: non-empty first alternative + + [[ ab = (b*|a*)~^(*b) ]] +0:Regression test for exclusion after branches: full length first alternative + +# Corresponding tests where the exclusion should succeed, so the +# match fails. It's hard to know how to provoke bugs here... + [[ abc = (|a*)~^(*b) ]] +1:Regression test for exclusion after branches: failure case 1 + + [[ abc = (b|a*)~^(*b) ]] +1:Regression test for exclusion after branches: failure case 2 + + [[ abc = (b*|a*)~^(*b) ]] +1:Regression test for exclusion after branches: failure case 3 + +# Careful: extendedglob off from this point. + unsetopt extendedglob print -r -- ${(*)=${(@s.+.):-A+B}/(#b)(?)/-${(L)match[1]} ${match[1]}} 0:the '*' qualfier enables extended_glob for pattern matching diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index 6bf55b4db..0e2a04eb5 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -110,6 +110,11 @@ *>*foo:1: 1: no arguments given >reached + message="expand me and remove quotes" + (: ${UNSET_PARAM?$message}) +1:${...?....} performs expansion on the message +?(eval):2: UNSET_PARAM: expand me and remove quotes + print ${set1:+word1} ${set1+word2} ${null1:+word3} ${null1+word4} print ${unset1:+word5} ${unset1+word6} 0:${...:+...}, ${...+...} @@ -1217,6 +1222,7 @@ typeset -T STRING string print $STRING $string unset string + typeset -p string STRING=x:y:z print $STRING $string STRING=a:b @@ -2275,6 +2281,27 @@ F:We do not care what $OLDPWD is, as long as it does not cause an error F:As of this writing, var=$@ and var="$@" with null IFS have unspecified F:behavior, see http://austingroupbugs.net/view.php?id=888 + ( + IFS=$'\x80' + if [[ $IFS = $' \t\n\0' ]]; then + echo OK # if $'\x80' is illegal (e.g. Linux) + else # otherwise (e.g. macOS), it should work as a separator + s=$'foo\x80\bar' + [[ ${${=s}[1]} = foo ]] && echo OK + fi + ) +0D:reset IFS to default if it contains illegal character +>OK + + ( + unsetopt multibyte + IFS=$'\xc3\xa9' + s=$'foo\xc3bar\xa9boo' + echo ${${=s}[2]} + ) +0:eight bit chars in IFS should work if multibute option is off +>bar + () { setopt localoptions extendedglob [[ $- = [[:alnum:]]## ]] || print Failed 1 @@ -2302,6 +2329,13 @@ F:behavior, see http://austingroupbugs.net/view.php?id=888 >x >y + a="string" + print ${(S)a//#%((#b)(*))/different} + print $match[1] +0:Fully anchored string must be fully searched +>different +>string + my_width=6 my_index=1 my_options=Option1 @@ -2727,3 +2761,76 @@ F:behavior, see http://austingroupbugs.net/view.php?id=888 1:parameter expansion flags parsing error gives a clue ?(eval):1: error in flags near position 7 in '${(zZ+x+):-}' + slash='/' + print -r -- x${slash/'/'}y +0:(users/28784) substituting a single-quoted backslash, part #1: slash +>xy + + single_quote="'" + print -r -- x${single_quote/$'/'}y +0:(users/28784) substituting a single-quoted backslash, part #2: single quote +>x'y + + control="foobar" + print -r -- x${control/'bar'}y +0:(users/28784 inspired this) substituting a single-quoted backslash, part #3: control +>xfooy + + spacestring="string with spaces" + print ${spacestring:gs/[[:space:]]/ /} + print ${spacestring:g&} + print ${spacestring:gS/[[:space:]]//} + print ${spacestring:g&} +0:Different behaviour of :s and :S modifiers +>string with spaces +>string with spaces +>stringwithspaces +>stringwithspaces + + : ${(#X):-@} +1:${(#X)...}: bad math expression +?(eval):1: bad math expression: illegal character: @ + + echo a${(#):-@}z +0:${(#)...}: bad math expression +>az + + printf "a%sz\n" ${(#):-@} +0:${(#)...}: bad math expression, printf +>az + + a=( '1 +' '@' ) + : ${(#X)a} +1:${(#X)...}: array of bad math expressions +?(eval):2: bad math expression: operand expected at end of string + + printf "a%sz\n" ${(#)a} +0:${(#)...}: array of bad math expressions, printf +>az + + if [[ ! -o multibyte ]]; then + ZTST_skip='(#X) accepts any byte if multibyte is off' + else + : ${(#X):-0x80} + fi +1:${(#X)...}: out-of-range character +?(eval):4: character not in range + + [[ ${(#):-0x80} = $'\x80' ]] && echo OK +0:${(#)...}: out-of-range character +>OK + + a=( 0x80 0x81 ) + printf "%s\n" ${(#)a} | + while read x; do echo $(( #x )); done +0:${(#)...}: array of out-of-range characters +>128 +>129 + + if [[ ! -o multibyte ]]; then + ZTST_skip='(#X) accepts any byte if multibyte is off' + else + : ${(#X)a} + fi +1:${(#X)...}: array of out-of-range characters +?(eval):4: character not in range diff --git a/Test/D10nofork.ztst b/Test/D10nofork.ztst new file mode 100644 index 000000000..5bb10266f --- /dev/null +++ b/Test/D10nofork.ztst @@ -0,0 +1,515 @@ +# Tests for "nofork" command substitution. + +%prep + mkdir nofork.tmp + touch nofork.tmp/file{1,2}.txt + + purr() { print -r -- "$@" } + purl() { print -rl -- "$@" } + +%test + + REPLY=OUTER + purr ${| REPLY=INNER } $REPLY +0:Basic substitution and REPLY scoping +>INNER OUTER + + reply=(x OUTER x) + purl ${{reply} reply=(\{ INNER \})} $reply +0:Basic substitution, brace quoting, and array result +>{ +>INNER +>} +>{ +>INNER +>} + + () { + setopt localoptions ignorebraces + purl ${{reply} reply=({ INNER })} $reply + } +0:Basic substitution, ignorebraces, and array result +>{ +>INNER +>} +>{ +>INNER +>} + + purr ${| REPLY=first}:${| REPLY=second}:$REPLY +0:re-scoping of REPLY in one statement +>first:second:OUTER + + purr BEGIN${| printf -v REPLY '%s\n' one two three ; }END +0:Adjacent words +>BEGINone +>two +>three +>END + + purr "BEGIN${| printf -v REPLY '%s\n' one two three }END" +0:Adjacent words and quoting, part 1 +>BEGINone +>two +>three +>END + + purr BEGIN"${| printf -v REPLY '%s\n' one two three }"END +0:Adjacent words and quoting, part 2 +>BEGINone +>two +>three +>END + + purr BEGIN"${| + printf -v REPLY '%s\n'\ + one two three + }"END +0:Embedded newlines +>BEGINone +>two +>three +>END + + purr BEGIN"${| + printf -v REPLY $'%s\n' one two three + }"END +0:Embedded newlines and $'...' +>BEGINone +>two +>three +>END + + purl ${| print -v REPLY one word here; setopt shwordsplit } + purl ${| print -v REPLY three words here } + purl "and ${| print -v REPLY one word here }" + unsetopt shwordsplit +0:test word splitting on result +F:setting option inside is too late for that substitution +>one word here +>three +>words +>here +>and one word here + + ( + cd nofork.tmp + setopt globsubst + purr ${| REPLY=f* } + purr ${| REPLY=f? }* + unsetopt globsubst + purr ${| REPLY=f* } + purr ${| REPLY=f? }* + ) +1:globsubst on result +>file1.txt file2.txt +>file1.txt file2.txt +>f* +?(eval):8: no matches found: f?* + + purr ${| REPLY=$'trailing newlines remain\n\n' } +0:newline removal should not occur, part 1 +>trailing newlines remain +> +> + + purr ${ echo $'one trailing newline\nremoved\n\n\n' } +0:newline removal in ${ ... }, zsh mode +>one trailing newline +>removed +> +> +> + + () { + emulate -L ksh + purl ${ echo $'all trailing newlines\nremoved\n\n\n' } + purr "${ echo $'all trailing newlines\nremoved\n\n\n' }" + } +0:newline removal in ${ ... }, emulation mode, shwordsplit +>all +>trailing +>newlines +>removed +>all trailing newlines +>removed + + purr "${ echo $'no trailing newlines\nremoved\n\n\n' }" +0:newline removal should not occur, part 2 +>no trailing newlines +>removed +> +> +> +> + + () { + purr ${| REPLY=$* ; shift 2 } + purr $* + } these are arguments +0:access to context $argv +>these are arguments +>arguments + + purr ${:-${| REPLY=${REPLY:-buried}}} + purr ${:-"${| REPLY=${REPLY:-more buried}}"} +0:nofork inside parameter scope +>buried +>more buried + + : ${(e):-'${| REPLY=oops'} +1:unclosed braces are sometimes a bad substitution +F:This seems silly, but see A01grammar ${(e):-'${'} test +?(eval):1: bad substitution + + purr ${| REPLY=oops +1:other times lack of closing brace is merely unexpected +F:Why not use this error in the previous case as well? +?(eval):1: closing brace expected + +# Next tests check that the PS2 stack is properly managed on error + + purr ${| REPLY=${REPLY:-buried}}} +1:unbalanced braces, part 0 +?(eval):1: parse error near `}' + + purr ${:-${| REPLY=${REPLY:-buried}} +1:unbalanced braces, part 1 +?(eval):1: closing brace expected + + purr ${:-"${| REPLY=${REPLY:-more buried}"} +1:unbalanced braces, part 2 +?(eval):1: unmatched " + + purr ${:-"${| REPLY=${REPLY:-more buried"}}} +1:unbalanced braces, part 3 +?(eval):1: unmatched " + + purr ${:-"${| REPLY=${REPLY:-more buried}}}" +1:unbalanced braces, part 4 +?(eval):1: closing brace expected + +# Same tests with leading space (future-proofing) + + purr ${ purr ${REPLY:-buried}}} +1:unbalanced braces, part 0+ +?(eval):1: parse error near `}' + + purr ${:-${ purr ${REPLY:-buried}} +1:unbalanced braces, part 1+ +?(eval):1: closing brace expected + + purr ${:-"${ purr ${REPLY:-more buried}"} +1:unbalanced braces, part 2+ +?(eval):1: unmatched " + + purr ${:-"${ purr ${REPLY:-more buried"}}} +1:unbalanced braces, part 3+ +?(eval):1: unmatched " + + purr ${:-"${ purr ${REPLY:-more buried}}}" +1:unbalanced braces, part 4+ +?(eval):1: closing brace expected + + purr "${ purr STDOUT }" +0:capture stdout +>STDOUT +> + +# end PS2 stack tests + + purr $(purr outside ${| REPLY=inside }) + purr BEGIN$(purr outside ${| REPLY=inside })END + purr "BEGIN$(purr outside ${| REPLY=inside })END" + purr outside ${| REPLY=$(purr inside)} + purr "outside ${| REPLY=$(purr inside)}" +0:mixing with forking cmdsubst +>outside inside +>BEGINoutside insideEND +>BEGINoutside insideEND +>outside inside +>outside inside + + purr `purr outside ${| REPLY=inside }` + purr "outside `purr ${| REPLY=inside }`" + purr outside ${| REPLY=`purr inside`} + purr "outside ${| REPLY=`purr inside`}" + purr outside "`purr ${| REPLY="${:-inside}"}`" + purr "outside ${| REPLY=`purr ${:-inside}`}" +0:mixing with backticks +>outside inside +>outside inside +>outside inside +>outside inside +>outside inside +>outside inside + + purr ${| REPLY=$(( 9 + 17 )) } + purr $(( 9 + ${| REPLY=17 } )) +0:mixing with arithemetic +>26 +>26 + + unset reply + purl ${{reply} reply=(1 2 ${| REPLY=3 } 4) } + typeset -p reply +0:array behavior with global assignment +>1 +>2 +>3 +>4 +>typeset -g -a reply=( 1 2 3 4 ) + + unset outer + purr "${| + outer=OUTER + REPLY=INNER + return 7 + OUTER=NOTREACHED + } $outer $?" +0:return statement inside, part 1 +F:status of "print" should hide return +>INNER OUTER 7 + + unset outer + outer=${| REPLY=${| return 7}} +7:return status propages in assignment like $(...) + + unset outer + purr "${| + outer=OUTER + REPLY=INNER + return 7 + OUTER=NOTREACHED + } $outer $?" + print REACHED $OUTER +0:return statement inside, part 2 +>INNER OUTER 7 +>REACHED + + unset outer + purr "${| + # Localoptions needed to avoid breaking test harness? + # The setopt command affects surrounding context + setopt localoptions errreturn + outer=OUTER + REPLY=INNER + false + OUTER=NOTREACHED + } $outer $?" + print REACHED $OUTER ${options[errreturn]} +0:errreturn works inside and remains outside +>INNER OUTER 1 +>REACHED on + + ( + unset outer + purr "${| + outer=OUTER + REPLY=INNER + exit 7 + OUTER=NOTREACHED + } $outer $OUTER $?" + print NOT REACHED + ) +7:exit statement inside + + ( + unset outer + purr "${| + setopt errexit + outer=OUTER + REPLY=INNER + false + OUTER=NOTREACHED + } $outer $?" + print NOT REACHED + ) +1:errexit inside + + outer=GLOBAL + purr "${| + local outer=LOCAL + REPLY=INNER + } $outer $?" +0:local declaration inside +>INNER GLOBAL 0 + + unset zz + outer=GLOBAL + purr "${{zz} + local outer=LOCAL + zz=NONLOCAL + } $outer $?" + print $zz +0:local declaration, global assignment, part 1 +>NONLOCAL GLOBAL 0 +>NONLOCAL + + unset zz + outer=GLOBAL + purr "${${| + local outer=LOCAL + zz=NONLOCAL + }:-$zz} $outer $?" +0:local declaration, global assignment, part 2 (evaluation order) +>NONLOCAL GLOBAL 0 + + : ${| fn1() { () { print -v REPLY $'Defined Function' ;} ;} } + print "IN${| fn2() { () { print "${:-Second }${|fn1}" ;} ;} }OUT" + fn2 +0:function definition, brace nesting, quote nesting +>INOUT +>Second Defined Function + + <<-EOF + ${| REPLY=$'in a here document\n' } + EOF +0:here-document behavior +F:Fiddly here to get EOF past the test syntax +>in a here document +> + + <<<${| REPLY="in a here string" } +0:here-string behavior +>in a here string + + <<<"${ purr $'stdout as a here string' }" +0:another capture stdout +>stdout as a here string +> + + wrap=${| REPLY="REPLY in environment assignment" } typeset -p wrap + wrap=${ purr "capture in environment assignment" } typeset -p wrap +0:assignment context +>typeset -g wrap='REPLY in environment assignment' +>typeset -g wrap='capture in environment assignment' + +# Repeat return and exit tests with stdout capture + + purr "${ + print INNER + return 7 + } $?" +0:return statement inside, part 1+ +F:status of "print" should hide return +>INNER +> 7 + + unset outer + outer=${ return 7 } +7:return status propages in stdout capture + + unset outer + purr "${ + outer=OUTER + print INNER + return 7 + OUTER=NOTREACHED + } $outer $?" + print REACHED $OUTER +0:return statement inside, part 2+ +>INNER +> OUTER 7 +>REACHED + + unset outer + purr "${ + # Localoptions needed to avoid breaking test harness? + # The setopt command affects surrounding context + setopt localoptions errreturn + outer=OUTER + print INNER + false + OUTER=NOTREACHED + } $outer $?" + print REACHED $OUTER ${options[errreturn]} +0:errreturn works inside stdout capture +>INNER +> OUTER 1 +>REACHED on + + ( + unset outer + purr "${ + outer=OUTER + print INNER + exit 7 + OUTER=NOTREACHED + } $outer $OUTER $?" + print NOT REACHED + ) +7:exit statement inside stdout capture + + ( + unset outer + purr "${ + setopt errexit + outer=OUTER + print INNER + false + OUTER=NOTREACHED + } $outer $?" + print NOT REACHED + ) +1:errexit inside stdout capture + + setopt ignorebraces +0:dummy test to set option soon enough +F:must do this before evaluating the next test block + + purr ${| REPLY=${REPLY:-buried}}} +0:ignored braces, part 1 +>buried} + + # Global $REPLY still set from earlier test + purr "${ purr ${REPLY:+buried}}}" +0:ignored braces, part 2 +>buried +>} + + purr ${ { echo nested ;} } +0:ignored braces, part 3 +>nested + + purr ${ { echo nested } } DONE +1:ignored braces, part 4 +?(eval):3: parse error near `}' + + unsetopt ignorebraces + # "break" blocks function calls in outer loop + # Could use print, but that might get fixed + repeat 3 do purr ${ + for x in 1 2 3 4 + do (( x == 3 )) && break 2 + # use error output to confirm loop count + print -u 2 $x + done + } XX + done +0:break N propagates +?1 +?2 + + # Cannot "purr": break skips pending function calls + # Use "repeat" to avoid infinite loop on failure + repeat 3 do; echo ${|REPLY=x; break }; done + repeat 3 do; echo ${{x} x=y; break }; done + repeat 3 do; echo ${ echo z; break }; done +0:break after assignment completes the assignment +>x +>y +>z + + # Subshell because error exits + ( purr ${ echo ${unset?oops} } ) +1:error handling (without crashing) +*?*unset: oops + + purr ${ .zsh.cmdsubst=error } +1:reserved parameter name (without crashing) +*?*.zsh.cmdsubst: can't modify read-only parameter + +%clean + + unfunction purr purl diff --git a/Test/E01options.ztst b/Test/E01options.ztst index d38fbed74..363846f5c 100644 --- a/Test/E01options.ztst +++ b/Test/E01options.ztst @@ -416,6 +416,9 @@ 1:NO_EXEC does recognize bad substitution syntax *?* bad substitution + (setopt noexec; : $(<nonexistentfile)) +0:NO_EXEC does not attempt to read files in $(<....) + setopt NO_eval_lineno eval 'print $LINENO' setopt eval_lineno @@ -558,12 +561,14 @@ foo=(one.c two.c three.c) print ${foo:s/#%(#b)t(*).c/T${match[1]}.X/} print *(#q:s/#(#b)tmp(*e)/'scrunchy${match[1]}'/) + print ${${:-"left[({})]over"}:fs/(\\{\\}|\\(\\)|\\[\\])//} unsetopt histsubstpattern 0:HIST_SUBST_PATTERN option >TINGcd TINGfile1 TINGfile2 homedir >THUMPcd THUMPfile1 THUMPfile2 >one.c Two.X Three.X >homedir scrunchyfile1 scrunchyfile2 tmpcd +>leftover setopt ignorebraces echo X{a,b}Y @@ -1376,6 +1381,64 @@ F:Regression test for workers/41811 >1 >2 + pipefailfn1() { + emulate -L zsh + setopt errreturn pipefail + false | { true; } + print "Shouldn't get here, status $?" + } + pipefailfn1 +1:PIPE_FAIL causes ERR_RETURN with complex end of pipeline: braces + + pipefailfn2() { + emulate -L zsh + setopt errreturn pipefail + false | if true; then true; fi + print "Shouldn't get here, status $?" + } + pipefailfn2 || print Function failed, as expected +0:PIPE_FAIL causes ERR_RETURN with complex end of pipeline: if +>Function failed, as expected + + pipefailfn3() { + emulate -L zsh + setopt errreturn pipefail + false | while true; do break; done + print "Shouldn't get here, status $?" + } + pipefailfn3 || print Function failed, as expected +0:PIPE_FAIL causes ERR_RETURN with complex end of pipeline: while +>Function failed, as expected + + pipefailfn4() { + emulate -L zsh + setopt errreturn pipefail + false | true + print "Shouldn't get here, status $?" + } + pipefailfn4 +1:PIPE_FAIL causes ERR_RETURN in simple case + + pipefailfn5() { + emulate -L zsh + setopt errreturn pipefail + false | { true | true; } + print "Shouldn't get here, status $?" + } + pipefailfn5 || print Function failed as expected +0:PIPE_FAIL causes ERR_RETURN with nested successful pipe +>Function failed as expected + + pipefailfn6() { + emulate -L zsh + setopt errreturn pipefail + false | { false | true; } + print "Shouldn't get here, status $?" + } + pipefailfn6 || print Function failed as expected +0:PIPE_FAIL causes ERR_RETURN with nested failed pipe +>Function failed as expected + for (( i = 0; i < 10; i++ )); do () { print $i diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst new file mode 100644 index 000000000..bb0d11821 --- /dev/null +++ b/Test/K01nameref.ztst @@ -0,0 +1,893 @@ +# Tests for named references + +%prep + + # Required in order to declare an unset hash for substitution test + setopt TYPESET_TO_UNSET + + : ${ZTST_continue::=1} + +%test + + typeset -n ptr + typeset -n +0:minimal declaration +>ptr + + typeset -n ptr= + typeset -n +0:nameref placeholder +>ptr='' + + typeset -n ptr + ptr=var + typeset -n +0:assign nameref placeholder +>ptr=var + + unset ptr + typeset -n ptr + typeset -n ptr=var + typeset -n +0:assign placeholder with new typeset +>ptr=var + + typeset -n ptr1 + typeset -n ptr2=ptr1 + typeset -n +0:chain ending in placeholder +>ptr1 +>ptr2=ptr1 + + typeset ptr=var + typeset -n ptr + typeset -n +0:convert scalar to nameref +>ptr=var + + typeset -n ptr=var + typeset +n ptr + typeset -p ptr +0:remove nameref attribute +>typeset ptr=var + + typeset -n ptr=gvar + () { + local ptr + typeset -p ptr + } + typeset -p ptr +0:Local non-reference hides outside reference +>typeset ptr +>typeset -n ptr=gvar + + typeset -n ptr + typeset -t ptr + typeset -p ptr +0:change type of a placeholder +F:Other type changes are fatal errors, should this also be? +>typeset -n ptr='' +*?*ptr: can't change type of a named reference + + typeset -n ptr=var + typeset -t ptr + typeset -p ptr var +0:change type of referenced var +>typeset -n ptr=var +>typeset -t var + + typeset var + unset var + typeset -n ptr=var + typeset -t ptr + typeset -p ptr var +0:change type of unset referenced var +F:regression - at one time this incorrectly applied the tag to "ptr" +F:note this causes "var" to become set +>typeset -n ptr=var +>typeset -t var + + typeset -n ptr=var[2] + typeset -t ptr +1:change type of referenced array element +*?*var\[2\]: can't change type via subscript reference + + typeset -n ptr[1]=var +1:illegal nameref name +*?*reference variable cannot be an array + + typeset var=value + typeset -n ptr=var + print $ptr +0:basic nameref expansion, no braces +>value + + typeset var=value + typeset -n ptr=var + print ${ptr} +0:basic nameref expansion, braces +>value + + typeset var=(val1 val2) + typeset -n ptr=var + print $ptr +0:nameref array expansion +>val1 val2 + + typeset -A var=(val1 val2) + typeset -n ptr=var + print ${(kv)ptr} +0:nameref hash expansion +>val1 val2 + + typeset -n ptr=var + typeset var=value + typeset -p ptr var + ptr=newvalue + typeset -p ptr var +0:assign existing scalar via nameref +>typeset -n ptr=var +>typeset var=value +>typeset -n ptr=var +>typeset var=newvalue + + typeset -n ptr=var + typeset var=value + unset ptr + typeset -p var +0:unset via nameref + + typeset -n ptr=var + typeset var=value + unset -n ptr + typeset -p var ptr +0:unset of the nameref itself +F:If earlier tests change, might get "no such variable" here +>typeset var=value + + typeset -n ptr=var + typeset var=value + typeset -p ptr var + typeset ptr=newvalue + typeset -p ptr var +0:typeset existing scalar via nameref +>typeset -n ptr=var +>typeset var=value +>typeset -n ptr=var +>typeset var=newvalue + + typeset -n ptr=var + ptr=value + typeset -p var ptr +0:assign new scalar via nameref +>typeset -g var=value +>typeset -n ptr=var + + unset var + typeset -n ptr=var + typeset var=(val1 val2) + typeset -p ptr var + ptr=(new1 new2) + typeset -p ptr var +0:assign existing array via nameref +>typeset -n ptr=var +>typeset -a var=( val1 val2 ) +>typeset -n ptr=var +>typeset -a var=( new1 new2 ) + + typeset -p ptr ptr1 ptr2 var +1:check state of paramtab ONE +F:unexpected side-effects of previous tests +*?*no such variable: ptr +*?*no such variable: ptr1 +*?*no such variable: ptr2 +*?*no such variable: var + + typeset -n ptr=var + ptr=(val1 val2) + typeset -p var ptr +0:assign new array via nameref +>typeset -g -a var=( val1 val2 ) +>typeset -n ptr=var + + unset var + typeset -n ptr2=var + typeset -n ptr1=ptr2 + typeset var=value + typeset -p ptr1 ptr2 var + print $ptr1 +0:indirect nameref expansion +>typeset -n ptr1=ptr2 +>typeset -n ptr2=var +>typeset var=value +>value + + typeset -p ptr1 ptr2 var +1:check state of paramtab TWO +F:unexpected side-effects of previous tests +*?*no such variable: ptr1 +*?*no such variable: ptr2 +*?*no such variable: var + + typeset var + typeset -n ptr2=var + typeset -n ptr1=ptr2 + typeset ptr1=newvalue + typeset -p ptr1 ptr2 var +0:typeset existing parameter indirectly +>typeset -n ptr1=ptr2 +>typeset -n ptr2=var +>typeset var=newvalue + + typeset var=value + typeset -n ptr2=var + typeset -n ptr1=ptr2 + unset ptr1 + typeset -p ptr1 ptr2 var +0:unset parameter indirectly +>typeset -n ptr1=ptr2 +>typeset -n ptr2=var + + typeset -n ptr2=var + typeset -n ptr1=ptr2 + typeset ptr1=newvalue + typeset -p ptr1 ptr2 var +0:typeset new parameter indirectly +>typeset -n ptr1=ptr2 +>typeset -n ptr2=var +>typeset var=newvalue + + unset var + typeset -n ptr2=var + typeset -n ptr1=ptr2 + typeset var=value + typeset -p ptr1 ptr2 var + ptr1=newvalue + typeset -p ptr1 ptr2 var +0:assign new parameter indirectly +>typeset -n ptr1=ptr2 +>typeset -n ptr2=var +>typeset var=value +>typeset -n ptr1=ptr2 +>typeset -n ptr2=var +>typeset var=newvalue + + typeset -p ptr1 ptr2 var +1:check state of paramtab THREE +F:unexpected side-effects of previous tests +*?*no such variable: ptr1 +*?*no such variable: ptr2 +*?*no such variable: var + + typeset -a var + typeset -n ptr2=var + typeset -n ptr1=ptr2 + typeset ptr1=(val1 val2) + typeset -p ptr1 ptr2 var +0:typeset existing array indirectly +>typeset -n ptr1=ptr2 +>typeset -n ptr2=var +>typeset -a var=( val1 val2 ) + + typeset -n ptr2=var + typeset -n ptr1=ptr2 + typeset ptr1=(val1 val2) + typeset -p ptr1 ptr2 var +0:typeset new array indirectly +>typeset -n ptr1=ptr2 +>typeset -n ptr2=var +>typeset -a var=( val1 val2 ) + + typeset -p ptr1 ptr2 +1:check state of paramtab FOUR +F:unexpected side-effects of previous tests +*?*no such variable: ptr1 +*?*no such variable: ptr2 + + unset var + typeset -n ptr2=var + typeset -n ptr1=ptr2 + ptr1=(val1 val2) + typeset -p ptr1 ptr2 var +0:assign new array indirectly +>typeset -n ptr1=ptr2 +>typeset -n ptr2=var +>typeset -g -a var=( val1 val2 ) + + typeset -n ptr1=ptr2 + typeset -n ptr2=ptr1 +1:direct nameref loop not allowed +*?*invalid self reference + + unset var + typeset -gn ptr1=var + typeset -p ptr1 +0:global reference to unset var +>typeset -g -n ptr1=var + + unset -n ptr1 + typeset -gn ptr1 + typeset -p ptr1 + ptr1=ptr1 +1:global direct reference +>typeset -g -n ptr1 +*?*invalid self reference + + typeset -n ptr1=ptr2 + typeset -n ptr2=ptr3 + typeset -n ptr3=ptr1 +1:indirect nameref loop not allowed +*?*invalid self reference + + typeset -n ptr1 ptr2 + ptr1=ptr2 + ptr2=ptr1 +1:looping assignment not allowed +*?*invalid self reference + + unset -n ptr2 + typeset -n ptr2='path[2]' + print -r -- $ptr2 +0q:nameref to array element, no braces +>${path[2]} + + unset -n ptr2 + typeset -n ptr2='path[2]' + print -r -- ${ptr2} +0q:nameref to array element, with braces +>${path[2]} + + unset -n ptr1 + typeset -A hash=(x MISS y HIT) + typeset -n ptr1='hash[y]' + print -r -- $ptr1 +0:nameref to hash element, no braces +>HIT + + unset -n ptr1 + typeset -A hash=(x MISS y HIT) + typeset -n ptr1='hash[y]' + print -r -- ${ptr1} +0:nameref to hash element, with braces +>HIT + + unset -n ptr2 + typeset -a ary=(1 2) + typeset -n ptr2='ary[2]' + ptr2=TWO + typeset -p ary +0:assign array element by nameref +>typeset -a ary=( 1 TWO ) + + unset -n ptr2 + typeset -n ptr2='ary[2]' + ptr2=TWO + typeset -p ary +0f:create array element by nameref +F:ksh93 does not implement this either +>typeset -a ary=( '' TWO ) + + unset -n ptr1 + typeset -A hash=(x MISS y MISS) + typeset -n ptr1='hash[y]' + ptr1=HIT + typeset -p hash +0:assign to hash element by nameref +>typeset -A hash=( [x]=MISS [y]=HIT ) + + unset -n ptr1 + typeset -A hash + typeset -n ptr1='hash[y]' + ptr1=HIT + typeset -p hash +0f:create hash by element nameref +F:ksh93 does not implement this either +>typeset -A hash=( [y]=HIT ) + + unset -n ptr1 + typeset -n ptr1='not[2]good' +1:invalid nameref +*?*invalid variable name: not\[2\]good + + unset -n ptr1 + unset hash + typeset -A hash + typeset -n ptr1='hash[y]' + print ${ptr1::=HIT} + typeset -p ptr1 hash +0f:create hash by element substitution +>HIT +>typeset -n ptr1='hash[y]' +>typeset -A hash=( [y]=HIT ) + + unset -n ptr + unset gval + typeset -n ptr=gval + gval=global + () { local gval=local; print $ptr; typeset -p ptr gval } +0:up-reference part 1 +>global +>typeset -g -n ptr=gval +>typeset gval=local + + typeset -p ptr ptr1 ptr2 val +1:check state of paramtab FIVE +F:unexpected side-effects of previous tests +*?*no such variable: ptr +*?*no such variable: ptr1 +*?*no such variable: ptr2 +*?*no such variable: val + + unset gval + typeset -n ptr1=gval + typeset gval + () { typeset gval=local; ptr1=global } + typeset -p ptr1 gval +0:up-reference assignment part 1 +F:All tests run inside a function, so "typeset gval" creates a local; +F:if that were omitted, ptr1= assignment would create a true global +F:and the output below would change to "typeset -g gval=global" +>typeset -n ptr1=gval +>typeset gval=global + + typeset -p ptr ptr1 ptr2 val gval +1:check state of paramtab SIX +F:unexpected side-effects of previous tests +*?*no such variable: ptr +*?*no such variable: ptr1 +*?*no such variable: ptr2 +*?*no such variable: val +*?*no such variable: gval + + typeset gval=global + () { + typeset -n ptr=gval + local gval=local + print $ptr + } + typeset -p ptr gval +1:up-reference part 2 +>global +*?*no such variable: ptr +>typeset gval=global + + typeset -n ptr=gval + () { + local lval=local + typeset -n ptr=lval + ptr=LOCAL + typeset -p lval gval ptr + } + typeset -p ptr +0:localized namerefs hide global namerefs +*?*no such variable: gval +>typeset lval=LOCAL +>typeset -n ptr=lval +>typeset -n ptr=gval + + typeset -A var=(myself outside) + () { + typeset -n myself=var[myself] + local -h var + print -r -- $myself + typeset -p var + } +0:up-reference part 3, hidden global +>outside +>typeset -h var + + () { + typeset notdef + unset notdef + () { + typeset -n ptr=notdef + ptr=(DEFINED) + } + typeset -p notdef + } +0:up-reference part 4, unset local and type change +>typeset -a notdef=( DEFINED ) + + () { + typeset -n ptr1=ptr2 + typeset -n ptr2 + typeset -p ptr1 ptr2 + typeset val=LOCAL + () { + ptr1=val + typeset -n + printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2" + } + typeset -p ptr1 ptr2 + } + typeset -p ptr2 +1:up-reference part 5, stacked namerefs, end not in scope +>typeset -n ptr1=ptr2 +>typeset -n ptr2 +>ptr1=ptr2 +>ptr2=val +>ptr1=LOCAL +>ptr2=LOCAL +>typeset -n ptr1=ptr2 +>typeset -n ptr2=val +*?*no such variable: ptr2 + + typeset ptr2 + () { + typeset -n ptr1=ptr2 + typeset -n ptr2 + typeset -p ptr1 ptr2 + typeset val=LOCAL + () { + ptr1=val + typeset -n + printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2" + } + typeset -p ptr1 ptr2 + } + typeset -p ptr2 +0:up-reference part 6, stacked namerefs, end is in scope +F:Same test, should part 5 output look like this? +>typeset -n ptr1=ptr2 +>typeset -n ptr2 +>ptr1=ptr2 +>ptr2 +>ptr1=val +>ptr2= +>typeset -n ptr1=ptr2 +>typeset -n ptr2 +>typeset ptr2=val + + () { + () { + local var + typeset -nu ptr1=var + ptr1=outer && print -u2 assignment expected to fail + typeset -n ptr2=var + ptr2=inner + typeset -n + printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2" + } + typeset -p var + } + typeset -p var +1:up-reference part 7, upscope namerefs, end not in scope +>ptr1=var +>ptr2=var +>ptr1= +>ptr2=inner +*?*typeset*: no such variable: var +*?*typeset*: no such variable: var + + typeset var + () { + () { + local var + typeset -nu ptr1=var + ptr1=outer || print -u2 assignment expected to succeed + typeset -n ptr2=var + ptr2=inner + typeset -n + printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2" + } + typeset -p var + } + typeset -p var +0:up-reference part 8, upscope namerefs, end in scope +>ptr1=var +>ptr2=var +>ptr1=outer +>ptr2=inner +>typeset -g var=outer +>typeset var=outer + + if zmodload zsh/parameter; then + () { + zmodload -u zsh/parameter + typeset -n myself=parameters[myself] + local -h parameters + print -r -- $myself + typeset -p parameters + } + else ZTST_skip='Cannot zmodload zsh/parameter, skipping autoload test' + fi +0:up-reference part 9, autoloading with hidden special +>nameref-local-nameref-local +>typeset -h parameters + + if [[ $options[typesettounset] != on ]]; then + ZTST_skip='Ignoring zmodload bug that resets TYPESET_TO_UNSET' + setopt typesettounset + fi +0:options reloaded +F:Checking for a bug in zmodload that affects later tests + + typeset ptr2=var2 + typeset var2=GLOBAL + () { + typeset -n ptr1=ptr2 + typeset ptr2=var1 + typeset var1=VAR1 + typeset var2=VAR2 + print -r -- ${(P)ptr1} + } +0:Order of evaluation with ${(P)...} +>VAR2 + + ary=(one two three four) + typeset -n ptr=ary + print -r ${(j.:.)ptr//o/0} +0:expansion flags and string replacement +>0ne:tw0:three:f0ur + + var=value + typeset -n ptr=var + myscalar=ptr + echo ${(P)myscalar} +0:named references with (P), as ${(P)name_of_nameref} +>value + + var=value + myscalar=var + typeset -n ptr=myscalar + echo ${(P)ptr} +0:named references with (P), as ${(P)nameref} +>value + + ary=( 'bry[1]' 'bry[2]' ) + bry=( lorem ipsum ) + typeset -n ptr='ary[2]' + print -r -- ${ptr} + print -r -- ${(P)ptr} +0:named references with (P), array element to array element +>bry[2] +>ipsum + + unset -n ref + unset var + typeset -n ref=var + typeset var=GLOBAL + () { + typeset -n ref=$1 + print -r $ref + ref=RESET + typeset -p ref var + } ref + typeset -p ref var +0:local reference points to same-name global reference, part 1 +>GLOBAL +>typeset -n ref=ref +>typeset -g var=RESET +>typeset -n ref=var +>typeset var=RESET + + unset -n ref + unset var + typeset -n ref=var + () { + typeset -n ref=$1 + print -r $ref + ref=RESET + typeset -p ref var + } ref + typeset -p ref var +0:local reference points to same-name global reference, part 2 +> +>typeset -n ref=ref +>typeset -g var=RESET +>typeset -n ref=var +>typeset -g var=RESET + + unset -n ref + unset one + typeset -n ref + typeset one=ONE + for ref in one ref two; do print -r $ref; done +1:for-loop variable is a reference, part 1 +>ONE +*?*ref: invalid self reference + + unset -n ref + unset one + typeset -n ref + () { + typeset one=ONE + for ref in one ref two; do print -r ${(t)ref}; done + } +1:for-loop variable is a reference, part 2 +>scalar-local +*?*ref: invalid self reference + + unset -n ref + unset one var + typeset -n ref=var + () { + typeset one=ONE + typeset -n ref=ref + for ref in one ref two; do + typeset -p ref + print -r $ref + done + typeset -p ref + } + typeset -p ref +0:for-loop variable is a reference, part 3 +>typeset -n ref=one +>ONE +>typeset -n ref=ref +> +>typeset -n ref=two +> +>typeset -n ref=two +>typeset -n ref=var + + typeset -g .K01.scalar='RW' + typeset -gA .K01.assoc=(x y) + typeset -ga .K01.array=(z) + typeset -gi .K01.integer=0 + typeset -gE .K01.double=0.0 + typeset -gF .K01.float=0.0 + typeset -gr .K01.readonly='RO' + typeset -n gref + for gref in ARGC .K01.{scalar,assoc,array,integer,double,float,readonly} + do + { unset gref } always { TRY_BLOCK_ERROR=0 } + done + typeset -p .K01.{scalar,assoc,array,integer,double,float,readonly} + unset .K01.{scalar,assoc,array,integer,double,float} +0:unset various types via nameref, including a readonly special +>typeset -g .K01.scalar +>typeset -g -A .K01.assoc +>typeset -g -a .K01.array +>typeset -g -i .K01.integer +>typeset -g -E .K01.double +>typeset -g -F .K01.float +>typeset -g -r .K01.readonly=RO +*?*read-only variable: ARGC +*?*read-only variable: .K01.readonly + + unset -n ref + unset one + typeset -n ref + () { + setopt localoptions warn_nested_var + typeset one=ONE + for ref in one two; do print -r ${(t)ref}; done + typeset -n ref + for ref in one two; do print -r ${(t)ref}; done + } +0:for-loop variable is a reference, part 4, warnings +>scalar-local +> +>scalar-local +> +*?*reference ref*to local variable one + + unset -n ref + typeset -n ref + () { + setopt localoptions warn_nested_var + typeset inner + ref=inner + } + typeset -p ref +0:Global variable is a reference, warning +>typeset -n ref=inner +*?*reference ref*to local variable inner + + typeset -n ptr='ary[$(echo 2)]' + typeset -a ary=(one two three) + print $ptr +1:attempt deferred command substitution in subscript +F:runs in `setopt noexec` so $(...) returns nothing +*?*bad math expression: empty string + + unset -n ref + typeset -n ref=GLOBAL + () { + typeset -gn ref=RESET + } + typeset -p ref +0:reset global reference within function +>typeset -n ref=RESET + + unset -n ref + typeset -rn ref=RO + typeset -p ref + (typeset -n ref=RW) + print status: $? expected: 1 + typeset +r -n ref + typeset -p ref + typeset -r +n ref + typeset -p ref + (typeset -rn ref) + print status: $? expected: 1 + typeset +r -n ref=RW # Assignment occurs after type change, + typeset -p ref RO # so RO=RW here. Potentially confusing. + typeset -r -n ref=RX # No type change, so referent changes ... + typeset -p ref RO # ... and previous refererent does not. + typeset +rn ref=RW # Here ref=RW, again type changed first. + typeset -p ref +0:add and remove readonly attribute with references +>typeset -rn ref=RO +*?*: ref: read-only reference +>status: 1 expected: 1 +>typeset -n ref=RO +>typeset -r ref=RO +*?*: ref: read-only variable +>status: 1 expected: 1 +>typeset -n ref=RO +>typeset -g RO=RW +>typeset -rn ref=RX +>typeset -g RO=RW +>typeset ref=RW + + () { + typeset -n r1 r2= + typeset -p r1 r2 + print -- ${(!)r1-unset} + print -- ${+r1} + typeset -p r1 + } +0:unset nameref remains unset when resolved +F:relies on global TYPESET_TO_UNSET in %prep +>typeset -n r1 +>typeset -n r2='' +>unset +>0 +>typeset -n r1 + + bar=xx + typeset -n foo=bar + () { + typeset -n foo; foo=zz + foo=zz || print -u2 foo: assignment failed + print $bar $zz + } + () { typeset -n foo; foo=zz; local zz; foo=zz; print $bar $zz } +0:regression: local nameref may not in-scope a global parameter +F:previously this could create an infinite recursion and crash +>xx +>xx zz +*?*foo: assignment failed + + typeset -nm foo=bar +1:create nameref by pattern match not allowed +*?*typeset:1: invalid reference + +# +# The following tests are run in interactive mode, using PS1 as an +# assignable special with side-effects. This crashed at one time. +# + + # Note bypassing TYPESET_TO_UNSET here + $ZTST_testdir/../Src/zsh -fis <<<$' + typeset -n p=PS1 + () { + typeset -p p + local p + typeset -p p + p=xx + typeset -p p + } + ' +0:regression: assign to local that shadows global named reference +>typeset -g -n p=PS1 +>typeset p='' +>typeset p=xx +*?* + + # Note bypassing TYPESET_TO_UNSET here + $ZTST_testdir/../Src/zsh -fis <<<$' + () { + typeset p=PS1 + typeset -n p + p=zz + } + typeset -p PS1 + ' +0:regression - converting a string into a named reference +>typeset PS1=zz +*?* + +%clean diff --git a/Test/K02parameter.ztst b/Test/K02parameter.ztst new file mode 100644 index 000000000..0b1a8dd4a --- /dev/null +++ b/Test/K02parameter.ztst @@ -0,0 +1,154 @@ +# Test parameter expansion with namespace syntax +# (heavily borrowed from D04parameter.ztst) + +%prep + +%test + + .k02.foo='the first parameter' + .k02.bar='the second parameter' + print -l $.k02.foo ${.k02.bar} +0:Basic scalars with namespace +F:Braces are required +>$.k02.foo +>the second parameter + + typeset .k02.bar='the second parameter' + print -l ${.k02.bar} +0:Scalar but with typeset +>the second parameter + + .k02.array1=(the first array) + .k02.array2=(the second array) + print -l $.k02.array1 ${.k02.array2} +0:Basic arrays with namespace +>$.k02.array1 +>the +>second +>array + + typeset -a .k02.array2=(the second array) + print -l ${.k02.array2} +0:Array but with typeset +>the +>second +>array + + setopt ksharrays + print -l ${.k02.array2} + unsetopt ksharrays +0:Basic ksharray with namespace +>the + + setopt shwordsplit + print -l ${.k02.foo} ${==.k02.bar} + unsetopt shwordsplit +0:Basic shwordsplit with namespace +>the +>first +>parameter +>the second parameter + + print ${+.k02.foo} ${+.k02.notappearinginthistest} +0:$+... and namespace +>1 0 + + .k02.x=() + print ${+.k02.x} ${+.k02.x[1]} ${+.k02.x[(r)foo]} ${+.k02.x[(r)bar]} + .k02.x=(foo) + print ${+.k02.x} ${+.k02.x[1]} ${+.k02.x[(r)foo]} ${+.k02.x[(r)bar]} +0:$+... with arrays and namespace +>1 0 0 0 +>1 1 1 0 + + # See D04 for complete explanation. + # For K02 we're just testing that flag syntax works. + .k02.foo='<five> {six} (seven) >eight< }nine{ |forty-two| $many$ )ten( more' + .k02.array=(${(z).k02.foo}) + print -l ${(Q).k02.array} +0:${(z)...} and ${(Q)...} for some hard to parse cases +>< +>five +>> +>{six} +>( +>seven +>) +>> +>eight +>< +>}nine{ +>| +>forty-two +>| +>$many$ +>) +>ten( more + + .k02.array=(characters in an array) + print ${(c)#.k02.array} +0:${(c)#...} +>22 + + () { + typeset -n .k02.ref=.k02.array + emulate -L ksh + print -l ${!.k02.ref} ${(!).k02.ref} ${.k02.ref} + } +0:namerefs with namespaces +>.k02.array +>.k02.array +>characters + + k.=empty + k.2=test + print ${k.} ${k.2} +0:Parse without leading dot (future proofing) +>empty test + + .k=OK + print ${.k} +0:Bare namespace is usable (ksh compatibility) +>OK + + .k.=empty +1:Namespace must precede an identifier, assignment +?(eval):1: not an identifier: .k. + + typeset .k.=empty +1:Namespace must precede an identifier, typeset +?(eval):typeset:1: not valid in this context: .k. + + print ${.k.} +1:Namespace must precede an identifier, reference +?(eval):1: bad substitution + + .2.b=not +1:Namespace identifier must not begin with a digit, assignment +?(eval):1: not an identifier: .2.b + + typeset .2.b=not +1:Namespace identifier must not begin with a digit, typeset +?(eval):typeset:1: not valid in this context: .2.b + + print ${.2.b} +1:Namespace identifier must not begin with a digit, reference +?(eval):1: bad substitution + + .not.2b=question +1:Identifier starting with a digit must be all digits, assignment +?(eval):1: not an identifier: .not.2b + + typeset .not.2b=question +1:Identifier starting with a digit must be all digits, typeset +?(eval):typeset:1: not valid in this context: .not.2b + + print ${.not.2b} +1:Identifier starting with a digit must be all digits, reference +?(eval):1: bad substitution + + integer .var.d=0 + float .var.f=.2 + print $((.var.x = ++.var.d - -.var.f)) +0:Namespaces in math context +>1.2 diff --git a/Test/README b/Test/README index 670434ac3..b9d393d7c 100644 --- a/Test/README +++ b/Test/README @@ -6,6 +6,7 @@ scripts names: C: shell commands with special syntax D: substititution E: options + K: features adopted from ksh P: privileged (needs super-user privileges) V: modules W: builtin interactive commands and constructs diff --git a/Test/V07pcre.ztst b/Test/V07pcre.ztst index 22a0b64c7..b8cd31c96 100644 --- a/Test/V07pcre.ztst +++ b/Test/V07pcre.ztst @@ -108,6 +108,11 @@ >0 xo→t →t >0 Xo→t →t + [[ foo =~ (pre)?f(o*)(opt(i)onal)?(y)* ]] + typeset -p match +0:Empty string for optional captures that don't match +>typeset -g -a match=( '' oo '' '' '' ) + string="The following zip codes: 78884 90210 99513" pcre_compile -m "\d{5}" pcre_match -b -- $string && print "$MATCH; ZPCRE_OP: $ZPCRE_OP" @@ -117,12 +122,17 @@ >78884; ZPCRE_OP: 25 30 >90210; ZPCRE_OP: 31 36 -# Embedded NULs allowed in plaintext, but not in RE (although \0 as two-chars allowed) +# Embedded NULs allowed in plaintext, in RE, pcre supports \0 as two-chars [[ $'a\0bc\0d' =~ '^(a\0.)(.+)$' ]] print "${#MATCH}; ${#match[1]}; ${#match[2]}" 0:ensure ASCII NUL passes in and out of matched plaintext >6; 3; 3 +# PCRE2 supports NULs also in the RE + [[ $'a\0b\0c' =~ $'^(.\0)+' ]] && print "${#MATCH}; ${#match[1]}" +0:ensure ASCII NUL works also in the regex +>4; 2 + # Ensure the long-form infix operator works [[ foo -pcre-match ^f..$ ]] print $? @@ -169,7 +179,30 @@ [[ é =~ '^..\z' ]]; echo $? LANG=$LANG_SAVE [[ é =~ '^.\z' ]]; echo $? -0:swich between C/UTF-8 locales +0:switch between C/UTF-8 locales >0 >0 >0 + + [[ abc =~ 'a(d*)bc' ]] && print "$#MATCH; $#match; ${#match[1]}" +0:empty capture +>3; 1; 0 + + [[ category/name-12345 =~ '(?x)^ + (?<category> [^/]* ) / + (?<package> + (?<name> \w+ ) - + (?<version> \d+ ))$' ]] + typeset -p1 .pcre.match +0:named captures +>typeset -g -A .pcre.match=( +> [category]=category +> [name]=name +> [package]=name-12345 +> [version]=12345 +>) + + pcre_compile 'cat(er(pillar)?)?' + pcre_match -d 'the caterpillar catchment' && print $match +0:pcre_match -d +>caterpillar cater cat diff --git a/Test/V10private.ztst b/Test/V10private.ztst index 56ffbc5b4..26004a2dc 100644 --- a/Test/V10private.ztst +++ b/Test/V10private.ztst @@ -10,6 +10,8 @@ sed -e 's,# test_zsh_param_private,zmodload zsh/param/private,' < $ZTST_srcdir/B02typeset.ztst > private.TMP/B02 fi + setopt TYPESET_TO_UNSET + %test (zmodload -u zsh/param/private && zmodload zsh/param/private) @@ -26,7 +28,7 @@ print $scalar_test 0:basic scope hiding >toplevel ->local scalar_test +>local hide scalar_test >0 >toplevel @@ -52,7 +54,7 @@ print $+unset_test 0:variable defined only in scope >0 ->local unset_test +>local hide unset_test >setme >0 @@ -68,7 +70,7 @@ } print $array_test 0:nested scope with different type, correctly restored ->local array_test +>local hide array_test >in function >top level @@ -111,7 +113,7 @@ typeset -a hash_test=(top level) typeset -p hash_test inner () { - private -p hash_test + typeset -p hash_test print ${(t)hash_test} ${(kv)hash_test} } outer () { @@ -246,7 +248,7 @@ F:note "typeset" rather than "private" in output from outer 1:privates are not visible in anonymous functions, part 3 >X top level >array_test not set -?(anon):4: array_test: can't change parameter attribute +?(anon):4: array_test: can't modify read-only parameter F:future revision will create a global with this assignment typeset -a array_test @@ -299,6 +301,256 @@ F:future revision will create a global with this assignment *>* *>* + typeset top=TOP + () { + local -P -n test=top + print $top + () { print UP: $test } + } +0:nameref can be declared private +>TOP +>UP: + + () { + typeset -a ary + local -P -n ref=ary + { + (){ + ref=XX # Should be an error + typeset -p ary ref + } + } always { + TRY_BLOCK_ERROR=0 + typeset -p ary ref + } + } + typeset -p ary +1:assignment to private nameref in wrong scope, part 1 +>typeset -a ary +>typeset -hn ref=ary +*?*ref: can't modify read-only parameter +*?*no such variable: ary + + () { + typeset -a ary + local -P -n ref=ary + { + (){ + typeset ref=XX # Should create a local + typeset -p ary ref + } + } always { + TRY_BLOCK_ERROR=0 + typeset -p ary ref + } + } + typeset -p ary +1:assignment to private nameref in wrong scope, part 2 +>typeset -g -a ary +>typeset ref=XX +>typeset -a ary +>typeset -hn ref=ary +*?*no such variable: ary + + () { + typeset -n ptr1=ptr2 + private -n ptr2 # TYPESET_TO_UNSET makes this not a "placeholder" + typeset -p ptr1 ptr2 + typeset val=LOCAL + () { + ptr1=val # Test dies here as ptr2 is private and unset + typeset -n + printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2" + } + typeset -p ptr1 ptr2 + } + typeset -p ptr2 +1:up-reference for private namerefs, end unset and not in scope +F:See K01nameref.ztst up-reference part 5 +F:Here ptr1 finds private ptr2 by scope mismatch +>typeset -n ptr1=ptr2 +>typeset -hn ptr2 +*?*read-only variable: ptr2 + + () { + typeset -n ptr1=ptr2 + private -n ptr2= # Assignment makes this a placeholder, not unset + typeset -p ptr1 ptr2 + typeset val=LOCAL + () { + ptr1=val || print -u2 ptr1: assignment failed + typeset -n + printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2" + } + typeset -p ptr1 ptr2 + } + typeset -p ptr2 +1:up-reference for private namerefs, end not in scope +F:See K01nameref.ztst up-reference part 5 +F:Here ptr1 finds private ptr2 by scope mismatch +>typeset -n ptr1=ptr2 +>typeset -hn ptr2='' +>ptr1=ptr2 +>ptr1= +>ptr2= +>typeset -n ptr1=ptr2 +>typeset -hn ptr2='' +*?*ptr1: assignment failed +*?*no such variable: ptr2 + + typeset ptr2 + () { + typeset -n ptr1=ptr2 + private -n ptr2 # Set/unset is irrelevant, not referenced + typeset -p ptr1 ptr2 + typeset val=LOCAL + () { + ptr1=val + typeset -n + printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2" + } + typeset -p ptr1 ptr2 + } + typeset -p ptr2 +0:up-reference for private namerefs, end is in scope +F:See K01typeset.ztst up-reference part 5 +F:Here ptr1 points to global ptr2 so assignment succeeds +>typeset -n ptr1=ptr2 +>typeset -hn ptr2 +>ptr1=ptr2 +>ptr2=val +>ptr1=val +>ptr2=val +>typeset -n ptr1=ptr2 +>typeset -hn ptr2 +>typeset ptr2=val + + () { + setopt localoptions errreturn + private -n ptr2 + typeset -n ptr1=ptr2 + typeset -p ptr1 ptr2 + typeset val=LOCAL + () { + ptr1=val + typeset -n + printf "v %s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2" + } + typeset -p ptr1 ptr2 + } + typeset -p ptr1 ptr2 +1:up-reference for private namerefs, end is in scope but private +F:Should we allow "public" namerefs to private parameters? +*?*ptr2: invalid reference +*?*no such variable: ptr1 +*?*no such variable: ptr2 + + () { + private x=1 + unset x + x=2 + } +0:regression test for unset private + + () { + private x=1 + unset x + private x=2 + print $x + } +0:private may be called twice +>2 + + () { + private x=1 + private -a x + print $x + } +1:private may not change parameter type +?(anon):private:2: can't change type of private param: x + + () { + private fd1 fd2 + exec {fd1}>&1 + print OK + () { exec {fd2}>&2 } + print BAD $fd2 + } +1:redirection cannot assign private in wrong scope +F:Better if caught in checkclobberparam() but exec.c doesn't know scope +>OK +?(anon): fd2: can't modify read-only parameter + + () { + private z=outer + print ${(t)z} $z + print ${| + print ${(t)z} $z + REPLY=$z + } + } +0:nofork may read private in calling function +>scalar-local-hide-special outer +>scalar-local-hide-special outer +>outer + + () { + private z=outer + print ${(t)z} $z + print ${| REPLY=${{z} z=nofork} } + print ${(t)z} $z + } +0:nofork may write to private in calling function +>scalar-local-hide-special outer +>nofork +>scalar-local-hide-special nofork + + () { + local q=outer + print ${| + private q=nofork + REPLY=${| REPLY=$q} + } + } +0:nofork cannot see private in surrounding nofork +>outer + + () { + private z=outer + print ${(t)z} $z + print ${{z} + private q + z=${{q} q=nofork} + } + print ${(t)z} $z + } +1:nofork may not change private in surrounding nofork +>scalar-local-hide-special outer +*?*: q: can't modify read-only parameter + + () { + private q=outer + print ${| + () { REPLY="{$q}" } + } + print ${{q} + () { q=nofork } + } + } +1:function may not access private from inside nofork +>{} +*?*: q: can't modify read-only parameter + + () { + print ${| + private q + () { q=nofork } + } + } +1:function may not access private declared in nofork +*?*: q: can't modify read-only parameter + %clean + unsetopt TYPESET_TO_UNSET rm -r private.TMP diff --git a/Test/X04zlehighlight.ztst b/Test/X04zlehighlight.ztst index 296635bf5..87a59fde5 100644 --- a/Test/X04zlehighlight.ztst +++ b/Test/X04zlehighlight.ztst @@ -40,7 +40,7 @@ # Fix e^Mexit - match ((?)\r(?)), if \2 == \3, then replace with \2 # otherwise replace with \1 stripped out of leading/trailing [[:space:]] REPLY=${REPLY//(#b)((?(#c0,1))$cm(?(#c0,1)))/${${${(M)match[2]:#${match[3]}}:+${match[2]}}:-${${match[1]##[[:space:]]##}%%[[:space:]]##}}} - [[ -n "$REPLY" ]] && print -r -- ${${REPLY%%[[:space:]]##}##[[:space:]]##} + [[ -n "$REPLY" ]] && print -r -- ${${REPLY%%${~cm}*}##[[:space:]]##} done } zpty_stop() { diff --git a/Test/X05zleincarg.ztst b/Test/X05zleincarg.ztst new file mode 100644 index 000000000..cd9817c82 --- /dev/null +++ b/Test/X05zleincarg.ztst @@ -0,0 +1,562 @@ +# Tests the incarg ZLE widget + +%prep + ZSH_TEST_LANG=$(ZTST_find_UTF8) + if ( zmodload zsh/zpty 2>/dev/null ); then + . $ZTST_srcdir/comptest + comptestinit -v -z $ZTST_testdir/../Src/zsh + else + ZTST_unimplemented="the zsh/zpty module is not available" + fi + zpty_run ' + autoload -Uz incarg + for name in {,vim-,vim-backward-}{,sync-}{inc,dec}arg; do + zle -N "$name" incarg + done + bindkey -v "^N" incarg + bindkey -v "^P" decarg + bindkey -v "^F" sync-incarg + bindkey -v "^B" sync-decarg + bindkey -a "^N" vim-incarg + bindkey -a "^P" vim-decarg + bindkey -a "^F" vim-sync-incarg + bindkey -a "^B" vim-sync-decarg + bindkey -a "^E" vim-backward-incarg + bindkey -a "^Y" vim-backward-decarg + unset TMUX_PANE ITERM_SESSION_ID + tmux() { + echo "$TMUX_PANE" + } + ' + +%test + +# Basic increment & decrement + + zletest $'0\C-n' +0:incarg increments an integer +>BUFFER: 1 +>CURSOR: 1 + + zletest $'0\C-p' +0:decarg decrements an integer +>BUFFER: -1 +>CURSOR: 2 + + zletest $'echo 0\e0\C-n' +0:vim-incarg increments an integer +>BUFFER: echo 1 +>CURSOR: 5 + + zletest $'echo 0\e0\C-p' +0:vim-decarg decrements an integer +>BUFFER: echo -1 +>CURSOR: 6 + + zletest $'echo 0 foo\e\C-e' +0:vim-backward-incarg increments an integer +>BUFFER: echo 1 foo +>CURSOR: 5 + + zletest $'echo 0 foo\e\C-y' +0:vim-backward-decarg decrements an integer +>BUFFER: echo -1 foo +>CURSOR: 6 + +# sync- variants + + zletest $'0\C-f' +0:sync-incarg does nothing on unsupported terminals +>BUFFER: 0 +>CURSOR: 1 + + zpty_run 'TMUX_PANE=0' + zletest $'0\C-f' + zpty_run 'unset TMUX_PANE' +0:sync-incarg does nothing on tmux in pane 0 +>BUFFER: 0 +>CURSOR: 1 + + zpty_run 'TMUX_PANE=1' + zletest $'0\C-f' + zpty_run 'unset TMUX_PANE' +0:sync-incarg increments by 1 on tmux in pane 1 +>BUFFER: 1 +>CURSOR: 1 + + zpty_run 'TMUX_PANE=2' + zletest $'0\C-f' + zpty_run 'unset TMUX_PANE' +0:sync-incarg increments by 2 on tmux in pane 2 +>BUFFER: 2 +>CURSOR: 1 + + zpty_run 'ITERM_SESSION_ID=w0t0p0:00000000-0000-0000-0000-000000000000' + zletest $'0\C-f' + zpty_run 'unset ITERM_SESSION_ID' +0:sync-incarg does nothing on tmux in pane 0 +>BUFFER: 0 +>CURSOR: 1 + + zpty_run 'ITERM_SESSION_ID=w0t0p1:00000000-0000-0000-0000-000000000000' + zletest $'0\C-f' + zpty_run 'unset ITERM_SESSION_ID' +0:sync-incarg increments by 1 on tmux in pane 1 +>BUFFER: 1 +>CURSOR: 1 + + zpty_run 'ITERM_SESSION_ID=w0t0p2:00000000-0000-0000-0000-000000000000' + zletest $'0\C-f' + zpty_run 'unset ITERM_SESSION_ID' +0:sync-incarg increments by 2 on tmux in pane 2 +>BUFFER: 2 +>CURSOR: 1 + + zpty_run 'TMUX_PANE=1' + zpty_run 'ITERM_SESSION_ID=w0t0p2:00000000-0000-0000-0000-000000000000' + zletest $'0\C-f' + zpty_run 'unset TMUX_PANE ITERM_SESSION_ID' +0:sync-incarg prioritizes tmux pane number over iTerm2's +>BUFFER: 1 +>CURSOR: 1 + + zletest $'0\e2\C-n' +0:incarg changes the incremented amount based on the numeric argument +>BUFFER: 2 +>CURSOR: 0 + + zpty_run 'incarg=3' + zletest $'0\e\C-n' + zpty_run 'unset incarg' +0:incarg changes the default incremented amount based on the incarg variable +>BUFFER: 3 +>CURSOR: 0 + + zpty_run 'incarg=3' + zletest $'0\e2\C-n' + zpty_run 'unset incarg' +0:incarg prioritizes the numeric argument over the incarg variable +>BUFFER: 2 +>CURSOR: 0 + + zpty_run 'TMUX_PANE=2' + zletest $'0\e2\C-f' + zpty_run 'unset TMUX_PANE' +0:The sync- variants of incarg takes the numeric argument into account +>BUFFER: 4 +>CURSOR: 0 + +# Leading zeros + + zletest $'000\C-n' +0:incarg preserves leading zeros of decimal integers +>BUFFER: 001 +>CURSOR: 3 + + zletest $'-001\C-n\C-n' +0:incarg preserves leading zeros when the digit turns from negative to positive +>BUFFER: 001 +>CURSOR: 3 + + zletest $'001\C-p\C-p' +0:incarg preserves leading zeros when the digit turns from positive to negative +>BUFFER: -001 +>CURSOR: 4 + + zletest $'001\e1000\C-n' +0:incarg works when the result has more number of digits than the original +>BUFFER: 1001 +>CURSOR: 3 + + zletest $'001\e2000\C-p' +0:decargs works on integers with leading zeros when the result has more digits than the original +>BUFFER: -1999 +>CURSOR: 4 + + zletest $'-000\C-n' +0:incarg produces the correct number of zeros when incrementing integers starting with -0 +>BUFFER: 001 +>CURSOR: 3 + + zletest $'-000\C-p' +0:decarg produces the correct number of zeros when incrementing integers starting with -0 +>BUFFER: -001 +>CURSOR: 4 + + zpty_run 'incarg=0' + zletest $'-000\C-n' + zpty_run 'unset incarg' +0:incarg removes the sign when the target integer starts with -0 and the increment amount is 0 +>BUFFER: 000 +>CURSOR: 3 + + zletest $'-0\C-n' +0:incarg turns -0 into 1 +>BUFFER: 1 +>CURSOR: 1 + + zletest $'-0\C-p' +0:decarg turns -0 into -1 +>BUFFER: -1 +>CURSOR: 2 + + zpty_run 'incarg=0' + zletest $'-0\C-n' + zpty_run 'unset incarg' +0:incarg turns -0 into 0 when the increment amount is 0 +>BUFFER: 0 +>CURSOR: 1 + +# Binaries + + zletest $'0b11\C-n' +0:incarg can increment a binary integer +>BUFFER: 0b100 +>CURSOR: 5 + + zletest $'0B11\C-n' +0:incarg can increment a binary integer with an upper case prefix +>BUFFER: 0B100 +>CURSOR: 5 + + zletest $'0b100\C-p' +0:decarg can decrement a binary integer +>BUFFER: 0b11 +>CURSOR: 4 + + zletest $'0b0011\C-n' +0:incarg can preserve leading zeros of binaries +>BUFFER: 0b0100 +>CURSOR: 6 + + zletest $'0b001\e8\C-n' +0:incarg works on binaries when the result has more zeros than the original +>BUFFER: 0b1001 +>CURSOR: 5 + + zletest $'0b0\C-p' +0:decarg fails to produce a negative binary value +>BUFFER: 0b0 +>CURSOR: 3 + +# Octals + + zletest $'0o7\C-n' +0:incarg can increment an octal integer +>BUFFER: 0o10 +>CURSOR: 4 + + zletest $'0O7\C-n' +0:incarg can increment an octal integer with an upper case prefix +>BUFFER: 0O10 +>CURSOR: 4 + + zletest $'0o10\C-p' +0:decarg can decrement an octal integer +>BUFFER: 0o7 +>CURSOR: 3 + + zletest $'0o0\C-p' +0:decarg fails to produce a negative octal value +>BUFFER: 0o0 +>CURSOR: 3 + +# Hexadecimals + + zletest $'0x9\C-n' +0:incarg can increment a hexadecimal integer +>BUFFER: 0xa +>CURSOR: 3 + + zletest $'0X9\C-n' +0:incarg can increment a hexadecimal integer with an upper case prefix +>BUFFER: 0XA +>CURSOR: 3 + + zletest $'0xf\C-n' +0:incarg can increment a hexadecimal integer with no numeric digit +>BUFFER: 0x10 +>CURSOR: 4 + + zletest $'0x10\C-p' +0:decarg can decrement a hexadecimal integer +>BUFFER: 0xf +>CURSOR: 3 + + zletest $'0x0\C-p' +0:decarg fails to produce a negative hexadecimal value +>BUFFER: 0x0 +>CURSOR: 3 + + zletest $'0x0b1\C-n' +0:incarg interprets integers starting with 0x0b as a hexadecimal +>BUFFER: 0x0b2 +>CURSOR: 5 + + zletest $'0x0b1\e\C-e' +0:vim-backward-incarg interprets integers starting with 0x0b as a hexadecimal +>BUFFER: 0x0b2 +>CURSOR: 4 + +# Cursor position - incarg + + zletest $'echo 012ab\eF i\C-n' +0:incarg does nothing when the cursor is placed just to the left of an integer +>BUFFER: echo 012ab +>CURSOR: 4 + + zletest $'echo 012ab\eF0i\C-n' +0:incarg works when the cursor is placed at the leftmost digit of an integer +>BUFFER: echo 013ab +>CURSOR: 8 + + zletest $'echo 012ab\eF1i\C-n' +0:incarg works when the cursor is placed at the inner digit of an integer +>BUFFER: echo 013ab +>CURSOR: 8 + + zletest $'echo 012ab\eF2i\C-n' +0:incarg works when the cursor is placed at the rightmost digit of an integer +>BUFFER: echo 013ab +>CURSOR: 8 + + zletest $'echo 012ab\eFai\C-n' +0:incarg works when the cursor is placed just to the right of an integer +>BUFFER: echo 013ab +>CURSOR: 8 + + zletest $'echo 012ab\ei\C-n' +0:incarg does nothing when the cursor is placed more than a single letter away to the right +>BUFFER: echo 012ab +>CURSOR: 9 + + zletest $'10x9\e0\C-n' +0:incarg turns [0-9]0x[0-9a-f] into [0-9]1x[0-9a-f] when the cursor is at the left of x +>BUFFER: 11x9 +>CURSOR: 1 + + zletest $'10x9\eFx\C-n' +0:incarg takes [0-9]0x[0-9a-f] and increments the hexadecimal part when the cursor is on x +>BUFFER: 10xa +>CURSOR: 3 + + zletest $'10x9\e\C-n' +0:incarg takes [0-9]0x[0-9a-f] and increments the hexadecimal part when the cursor is at the right of x +>BUFFER: 10xa +>CURSOR: 3 + + zletest $'10b1\e0\C-n' +0:incarg turns [0-9]0b[01] into [0-9]1b[01] when the cursor is at the left of b +>BUFFER: 11b1 +>CURSOR: 1 + + zletest $'10b1\eFb\C-n' +0:incarg takes [0-9]0b[01] and increments the binary part when the cursor is on b +>BUFFER: 10b10 +>CURSOR: 4 + + zletest $'10b1\e\C-n' +0:incarg takes [0-9]0b[01] and increments binary part when the cursor is at the right of b +>BUFFER: 10b10 +>CURSOR: 4 + + zletest $'10o7\e0\C-n' +0:incarg turns [0-9]0o[0-7] into [0-9]1o[0-7] when the cursor is at the left of o +>BUFFER: 11o7 +>CURSOR: 1 + + zletest $'10o7\eFo\C-n' +0:incarg takes [0-9]0o[0-7] and increments the octal part when the cursor is on o +>BUFFER: 10o10 +>CURSOR: 4 + + zletest $'10o7\e\C-n' +0:incarg takes [0-9]0o[0-7] and increments the octal part when the cursor is at the right of o +>BUFFER: 10o10 +>CURSOR: 4 + + zletest $'0b0x9\eF0\C-n' +0:incarg takes 0b0x[0-9a-f] and increments the binary part when the cursor is at the left of x +>BUFFER: 0b1x9 +>CURSOR: 2 + + zletest $'0b0x9\eFx\C-n' +0:incarg takes 0b0x[0-9a-f] and increments the hexadecimal part when the cursor is on x +>BUFFER: 0b0xa +>CURSOR: 4 + + zletest $'0b0x9\e\C-n' +0:incarg takes 0b0x[0-9a-f] and increments the hexadecimal part when the cursor is at the right of x +>BUFFER: 0b0xa +>CURSOR: 4 + +# Cursor position - vim-incarg + + zletest $'echo 012ab\eF \C-n' +0:vim-incarg works when the cursor is placed to the left of an integer +>BUFFER: echo 013ab +>CURSOR: 7 + + zletest $'echo 012ab\eF0\C-n' +0:vim-incarg works when the cursor is placed at the leftmost digit of an integer +>BUFFER: echo 013ab +>CURSOR: 7 + + zletest $'echo 012ab\eF1\C-n' +0:vim-incarg works when the cursor is placed at the inner digit of an integer +>BUFFER: echo 013ab +>CURSOR: 7 + + zletest $'echo 012ab\eF2\C-n' +0:incarg works when the cursor is placed at the rightmost digit of an integer +>BUFFER: echo 013ab +>CURSOR: 7 + + zletest $'echo 012ab\eFa\C-n' +0:vim-incarg does nothing when the cursor is placed to the right of an integer +>BUFFER: echo 012ab +>CURSOR: 8 + + zletest $'echo 012ab\ei\C-n' +0:vim-incarg does nothing when the cursor is placed more than a single letter away to the right +>BUFFER: echo 012ab +>CURSOR: 9 + +# Cursor position - vim-backward-incarg + + zletest $'echo 012ab\eF \C-e' +0:vim-backward-incarg does nothing when the cursor is placed just to the left of an integer +>BUFFER: echo 012ab +>CURSOR: 4 + + zletest $'echo 012ab\eF0\C-e' +0:vim-backward-incarg works when the cursor is placed at the leftmost digit of an integer +>BUFFER: echo 013ab +>CURSOR: 7 + + zletest $'echo 012ab\eF1\C-e' +0:vim-backward-incarg works when the cursor is placed at the inner digit of an integer +>BUFFER: echo 013ab +>CURSOR: 7 + + zletest $'echo 012ab\eF2\C-e' +0:vim-backward-incarg works when the cursor is placed at the rightmost digit of an integer +>BUFFER: echo 013ab +>CURSOR: 7 + + zletest $'echo 012ab\eFa\C-e' +0:vim-backward-incarg works when the cursor is placed just to the right of an integer +>BUFFER: echo 013ab +>CURSOR: 7 + + zletest $'echo 012ab\e\C-e' +0:vim-backward-incarg works when the cursor is placed more than a single letter away to the right +>BUFFER: echo 013ab +>CURSOR: 7 + + zletest $'10x9\eFx\C-e' +0:vim-backward-incarg will take [0-9]0x[0-9a-f] and increment the hexadecimal part when the cursor is on x +>BUFFER: 10xa +>CURSOR: 3 + + zletest $'10x9\e\C-e' +0:vim-backward-incarg will take [0-9]0x[0-9a-f] and increment the hexadecimal part when the cursor is on the right of x +>BUFFER: 10xa +>CURSOR: 3 + + zletest $'10b1\e0\C-e' +0:vim-backward-incarg will turn [0-9]0b[01] into [0-9]1b[01] when the cursor is at the left of b +>BUFFER: 11b1 +>CURSOR: 1 + + zletest $'10b1\eFb\C-e' +0:vim-backward-incarg will take [0-9]0b[01] and increment the binary part when the cursor is on b +>BUFFER: 10b10 +>CURSOR: 4 + + zletest $'10b1\e\C-e' +0:vim-backward-incarg will take [0-9]0b[01] and increment the binary part when the cursor is on the right of b +>BUFFER: 10b10 +>CURSOR: 4 + + zletest $'10o7\e0\C-e' +0:vim-backward-incarg will turn [0-9]0o[0-7] into [0-9]1o[0-7] when the cursor is at the left of o +>BUFFER: 11o7 +>CURSOR: 1 + + zletest $'10o7\eFo\C-e' +0:vim-backward-incarg will take [0-9]0o[0-7] and increment the octal part when the cursor is on o +>BUFFER: 10o10 +>CURSOR: 4 + + zletest $'10o7\e\C-e' +0:vim-backward-incarg will take [0-9]0o[0-7] and increment the octal part when the cursor is at the right of o +>BUFFER: 10o10 +>CURSOR: 4 + + zletest $'0b0x9\eF0\C-e' +0:vim-backward-incarg will take 0b0x[0-9a-f] and increment the binary 0b0 when the cursor is on the left of x +>BUFFER: 0b1x9 +>CURSOR: 2 + + zletest $'0b0x9\eFx\C-e' +0:vim-backward-incarg will take 0b0x[0-9a-f] and increment the hexadecimal part when the cursor is on x +>BUFFER: 0b0xa +>CURSOR: 4 + + zletest $'0b0x9\e\C-e' +0:vim-backward-incarg will take 0b0x[0-9a-f] and increment the hexadecimal part when the cursor is at the right of x +>BUFFER: 0b0xa +>CURSOR: 4 + +# Repeats + + zletest $'echo 0\e0\C-n.' +0:vim-incarg is compatible with the repeat command +>BUFFER: echo 2 +>CURSOR: 5 + + zletest $'echo 0\e0\C-p.' +0:vim-decarg is compatible with the repeat command +>BUFFER: echo -2 +>CURSOR: 6 + + zletest $'echo 0 foo\e\C-e.' +0:vim-backward-incarg is compatible with the repeat command +>BUFFER: echo 2 foo +>CURSOR: 5 + + zletest $'echo 0\e010\C-n.' +0:Repeats of vim-incarg takes the numeric argument into account +>BUFFER: echo 20 +>CURSOR: 6 + + zletest $'echo 0 foo\e10\C-e.' +0:Repeats of vim-backward-incarg takes the numeric argument into account +>BUFFER: echo 20 foo +>CURSOR: 6 + + zpty_run 'TMUX_PANE=0' + zletest $'echo 0\e0\C-f.' + zpty_run 'unset TMUX_PANE' +0:Repeats of vim-sync-incarg work in pane 0 +>BUFFER: echo 0 +>CURSOR: 5 + + zpty_run 'TMUX_PANE=1' + zletest $'echo 0\e0\C-f.' + zpty_run 'unset TMUX_PANE' +0:Repeats of vim-sync-incarg work in pane 1 +>BUFFER: echo 2 +>CURSOR: 5 + + zpty_run 'TMUX_PANE=2' + zletest $'echo 0\e0\C-f.' + zpty_run 'unset TMUX_PANE' +0:Repeats of vim-sync-incarg work in pane 2 +>BUFFER: echo 4 +>CURSOR: 5 + +%clean + + zmodload -ui zsh/zpty diff --git a/Test/Y01completion.ztst b/Test/Y01completion.ztst index f976f9f91..fc18b19a4 100644 --- a/Test/Y01completion.ztst +++ b/Test/Y01completion.ztst @@ -35,6 +35,54 @@ >line: {: dir1/}{} >line: {: dir2/}{} + comptest $': d\t\t\t\t\t \t' +0:unambiguous prefix and autoremovable suffix +>line: {: dir}{} +>line: {: dir}{} +>DESCRIPTION:{file} +>DI:{dir1} +>DI:{dir2} +>line: {: dir1/}{} +>line: {: dir2/}{} +>line: {: dir1/}{} +>line: {: dir1 }{} +>DESCRIPTION:{file} +>DI:{dir1} +>DI:{dir2} +>FI:{file1} +>FI:{file2} + + comptest $': suf\ebd\t\t\t\t\t \t' +0:unambiguous prefix and autoremovable suffix with _prefix completer +>line: {: dir}{suf} +>line: {: dir}{suf} +>DESCRIPTION:{file} +>DI:{dir1} +>DI:{dir2} +>line: {: dir1/}{suf} +>line: {: dir2/}{suf} +>line: {: dir1/}{suf} +>line: {: dir1 }{suf} +>DESCRIPTION:{file} +>DI:{dir1} +>DI:{dir2} +>FI:{file1} +>FI:{file2} +F:regression test workers/51641 + + comptesteval 'comptest-postfunc() { compstate[insert]=1 compstate[list]= }' + comptest $': \t \t' +0:compstate[insert]=1 compstate[list]= +>line: {: dir1/}{} +>line: {: dir1 dir1/}{} + + comptest $': suf\eb\t \t' +0:compstate[insert]=1 compstate[list]= with _prefix completer +>line: {: dir1/}{suf} +>line: {: dir1 dir1/}{suf} +F:regression test workers/51641 + + comptesteval 'comptest-postfunc() {}' comptest $': *\t\t\t\t\t\t' 0:_expand shows file types >line: {: dir1/}{} @@ -75,6 +123,27 @@ >line: {: ~user2}{} >line: {: ~user1}{} + comptesteval 'zsh_directory_name() { compadd "$expl[@]" -- name/1 name2 }' + comptest $': ~[\t\t\t\t' +0:dynamic directory names after ~[ +>line: {: ~[name}{} +>line: {: ~[name}{} +>DESCRIPTION:{dynamically named directory} +>NO:{name/1} +>NO:{name2} +>line: {: ~[name/1]}{} +>line: {: ~[name2]}{} + + comptest $': ~[]\C-b\t\t\t\t' +0:dynamic directory names inside ~[...] +>line: {: ~[name}{]} +>line: {: ~[name}{]} +>DESCRIPTION:{dynamically named directory} +>NO:{name/1} +>NO:{name2} +>line: {: ~[name/1}{]} +>line: {: ~[name2}{]} + comptest $'echo ;:\C-b\C-b\t' 0:directories and files before separator >line: {echo }{;:} @@ -260,10 +329,10 @@ F:regression test workers/31611 comptesteval "typeset -a bar=({$'\\0'..$'\\C-?'})" comptesteval 'typeset -A bat=( "$bar[@]" )' comptesteval 'typeset bay="$bar"' - comptesteval 'zstyle ":completion:*:parameters" extra-verbose yes' + comptesteval 'zstyle ":completion:*:parameters" verbose yes' comptesteval 'zstyle ":completion:*" fake-parameters bar bat bay' comptest $': $ba\t' -0:extra-verbose shows parameter values +0:verbose shows parameter values >line: {: $ba}{} >DESCRIPTION:{parameter} >NO:{bar -- ( '^@' '^A' '^B' '^C' '^D' '^E' '^F' '^G' '^H' '\t' '\n' '^K' '^L' '} @@ -273,15 +342,15 @@ F:regression test workers/31611 comptesteval "path=( $ZTST_srcdir:A )" comptesteval 'typeset -H paths=HIDDEN' comptest $': $path\t' -0:extra-verbose doesn't show special or hidden parameter values +0:verbose doesn't show special or hidden parameter values >line: {: $path}{} >DESCRIPTION:{parameter} >NO:{path} >NO:{paths} - comptesteval 'zstyle -d ":completion:*:parameters" extra-verbose' + comptesteval 'zstyle -d ":completion:*:parameters" verbose' comptest $': $ba\t' -0:parameter values not shown without extra-verbose +0:parameter values not shown without verbose >line: {: $ba}{} >DESCRIPTION:{parameter} >NO:{bar} diff --git a/Test/comptest b/Test/comptest index 79c69979a..39ad14768 100644 --- a/Test/comptest +++ b/Test/comptest @@ -40,7 +40,7 @@ KEYTIMEOUT=1 setopt zle autoload -U compinit compinit -u -zstyle ":completion:*" completer _expand _complete _ignored +zstyle ":completion:*" completer _expand _complete _prefix _ignored zstyle ":completion:*:default" list-colors "no=<NO>" "fi=<FI>" "di=<DI>" "ln=<LN>" "pi=<PI>" "so=<SO>" "bd=<BD>" "cd=<CD>" "ex=<EX>" "mi=<MI>" "tc=<TC>" "sp=<SP>" "lc=<LC>" "ec=<EC>\n" "rc=<RC>" zstyle ":completion:*" group-name "" zstyle ":completion:*:messages" format "<MESSAGE>%d</MESSAGE> @@ -51,6 +51,12 @@ zstyle ":completion:*:options" verbose yes zstyle ":completion:*:values" verbose yes setopt noalwayslastprompt listrowsfirst completeinword zmodload zsh/complist +zle -C complete-word complete-word complete-word-with-postfunc +complete-word-with-postfunc() { + local +h -a comppostfuncs=( comptest-postfunc ) + _main_complete "$@" +} +comptest-postfunc() {} complete-word-with-report () { print -lr "<WIDGET><complete-word>" zle complete-word diff --git a/Test/runtests.zsh b/Test/runtests.zsh index b66d579b6..538663f50 100644 --- a/Test/runtests.zsh +++ b/Test/runtests.zsh @@ -15,6 +15,7 @@ for file in "${(f)ZTST_testlist}"; do (( skipped++ )) elif (( $retval )); then (( failure++ )) + (( $retval > 128 )) && print "$file: failed: SIG$signals[$retval - 127]." else (( success++ )) fi diff --git a/Test/ztst.zsh b/Test/ztst.zsh index ea1b016d5..1d05baddf 100755 --- a/Test/ztst.zsh +++ b/Test/ztst.zsh @@ -326,6 +326,7 @@ ZTST_diff() { emulate -L zsh setopt extendedglob + local -a diff_arg local diff_out integer diff_pat diff_ret @@ -342,6 +343,7 @@ ZTST_diff() { ;; esac shift + [[ $OSTYPE != solaris* ]] && diff_arg=( -a ) if (( diff_pat )); then local -a diff_lines1 diff_lines2 @@ -382,7 +384,7 @@ ZTST_diff() { diff_ret=1 fi else - diff_out=$(diff -a "$@") + diff_out=$(diff $diff_arg "$@") diff_ret="$?" if [[ "$diff_ret" != "0" ]]; then print -r -- "$diff_out" |