diff options
Diffstat (limited to 'Test')
48 files changed, 1932 insertions, 142 deletions
diff --git a/Test/A01grammar.ztst b/Test/A01grammar.ztst index 1e0e9a04e..0312fe94e 100644 --- a/Test/A01grammar.ztst +++ b/Test/A01grammar.ztst @@ -557,6 +557,13 @@ >Hip hip hooray >Hip hip hooray + repeat 2*2 print yeah +0:Tokens in repeat argument +>yeah +>yeah +>yeah +>yeah + case bravo { (alpha) print schmalpha ;; @@ -915,7 +922,7 @@ F:Note that the behaviour of 'exit' inside try-list inside a function is unspeci x=1 x=2 | echo $x echo $x -0:Assignment-only current shell commands in LHS of pipelin +0:Assignment-only current shell commands in LHS of pipeline >1 >1 @@ -930,5 +937,36 @@ F:Note that the behaviour of 'exit' inside try-list inside a function is unspeci >or false $ZTST_testdir/../Src/zsh -fc '{ ( ) } always { echo foo }' --f:exec last command optimization inhibited for try/always +0:exec last command optimization inhibited for try/always +>foo + + a='${' + if : ${(e)a}; then echo x; fi +1:Status on bad substitution in if without else +?(eval):2: bad substitution + + echo 'echo foo # comment + echo $( + echo bar # comment + )' >source_comments.zsh + $ZTST_testdir/../Src/zsh -f -o extendedglob -is -c '. ./source_comments.zsh' +0:Comments should be handled in command subst in interactively sourced files >foo +>bar + + function 'ls,/' () {echo success} + {ls,/} +0:workers/47599: current-shell blocks masquerading as brace expansion +>success +F:This test was written to ensure the behaviour doesn't change silently. +F:If this test fails during development, it *might* be appropriate to change +F:its expectations. + + ( + export VALUE=first + print -l 'echo Value is $VALUE' 'VALUE=second sh' 'echo Value is $VALUE' | + $ZTST_testdir/../Src/zsh -f + ) +0:Non-interactive shell command input is line buffered +>Value is first +>Value is second diff --git a/Test/A04redirect.ztst b/Test/A04redirect.ztst index d60519064..17f6dfa29 100644 --- a/Test/A04redirect.ztst +++ b/Test/A04redirect.ztst @@ -440,7 +440,7 @@ # This tests the here-string to filename optimisation; we can't # test that it's actually being optimised, but we can test that it # still works. - cat =(<<<$'This string has been replaced\nby a file containing it.\n') + cat =(<<<$'This string has been replaced\nby a file containing it.') 0:Optimised here-string to filename >This string has been replaced >by a file containing it. @@ -708,3 +708,17 @@ cat <&$testfd 0:Regression test for here document with fd declarator > This is, in some sense, a here document. + + (setopt noclobber clobberempty + rm -f foo + touch foo + print Works >foo + cat foo + print Works not >foo + # Make sure the file was not harmed + cat foo + ) +0:CLOBBER_EMPTY +>Works +>Works +?(eval):6: file exists: foo diff --git a/Test/A05execution.ztst b/Test/A05execution.ztst index edc561582..d95ee363c 100644 --- a/Test/A05execution.ztst +++ b/Test/A05execution.ztst @@ -258,7 +258,7 @@ F:side of a pipe to block on write after the right side has exited print -u $ZTST_fd "Skipping pipe leak test, requires MONITOR option" print "[0] 0 0" fi -0:Bug regression: piping to anonymous function; piping to backround function +0:Bug regression: piping to anonymous function; piping to background function *>\[<->\] <-> <-> F:This test checks for two different bugs, a parser segfault piping to an F:anonymous function, and a descriptor leak when backgrounding a pipeline @@ -326,6 +326,7 @@ F:anonymous function, and a descriptor leak when backgrounding a pipeline callfromchld() { true && { print CHLD } } TRAPCHLD() { callfromchld } sleep 2 & sleep 3; print OK + unfunction TRAPCHLD # don't affect future tests 0:Background job exit does not affect reaping foreground job >CHLD >OK @@ -394,3 +395,23 @@ F:anonymous function, and a descriptor leak when backgrounding a pipeline >127 # TBD: the 0 above is believed to be bogus and should also be turned # into 127 when the ccorresponding bug is fixed in the main shell. + +# Without the outer subshell, the test harness reports the pre-46060 behaviour +# as "skipped" rather than "failed". + (( exit 130 ) | { sleep 1; echo hello }) +0:exit code 130 isn't mistaken for a signal (unit test for workers/46060) +>hello + + (exit 3); repeat "$?" echo x + (exit 3); repeat '?' echo y +0:'repeat' loop can use lastval in the count +>x +>x +>x +>y +>y +>y + + (exit 4); repeat 0 do done +0:'repeat 0' resets lastval + diff --git a/Test/B01cd.ztst b/Test/B01cd.ztst index d903b7462..bc6757549 100644 --- a/Test/B01cd.ztst +++ b/Test/B01cd.ztst @@ -33,7 +33,7 @@ # # Tests should use subdirectories ending in `.tmp'. These will be # removed with all the contents even if the test is aborted. - mkdir cdtst.tmp cdtst.tmp/real cdtst.tmp/sub + mkdir cdtst.tmp cdtst.tmp/foo cdtst.tmp/real cdtst.tmp/sub ln -s ../real cdtst.tmp/sub/fake @@ -70,7 +70,7 @@ # the expected status returned by the code when run, or - if it is # irrelevant. An optional set of single-letter flags follows the status # or -. The following are understood: -# . d Don't diff stdout against the expected stdout. +# d Don't diff stdout against the expected stdout. # D Don't diff stderr against the expected stderr. # q All redirection lines given in the test script (not the lines # actually produced by the test) are subject to ordinary quoted shell @@ -96,8 +96,8 @@ # itself. (The example below isn't particularly useful as errors with # `cd' are unusual.) # -# A couple of features aren't used in this file, but are usefuil in cases -# where features may not be available so should not be tested. They boh +# A couple of features aren't used in this file, but are useful in cases +# where features may not be available so should not be tested. They both # take the form of variables. Note that to keep the test framework simple # there is no magic in setting the variables: the chunk of code being # executed needs to avoid executing any test code by appropriate structure @@ -149,6 +149,23 @@ F:something is broken. But you already knew that. -f:(workers/45367) cd -P squashes multiple leading slashes >/dev + chpwd_hook() { hook_pwd=$PWD; } + chpwd_functions=(chpwd_hook) + cd $mydir/cdtst.tmp/foo && + (cd $mydir && mv $mydir/cdtst.tmp/{foo,bar}) && + print $PWD && + print $hook_pwd && + cd . && + print $PWD && + print $hook_pwd + chpwd_functions=() + unfunction chpwd_hook +0q:cd . with moved PWD +>$mydir/cdtst.tmp/foo +>$mydir/cdtst.tmp/foo +>$mydir/cdtst.tmp/bar +>$mydir/cdtst.tmp/bar + %clean # This optional section cleans up after the test, if necessary, # e.g. killing processes etc. This is in addition to the removal of *.tmp diff --git a/Test/B02typeset.ztst b/Test/B02typeset.ztst index e7bf93794..8b3988151 100644 --- a/Test/B02typeset.ztst +++ b/Test/B02typeset.ztst @@ -620,7 +620,7 @@ print ${+pbro} >&2 (typeset -g pbro=3) (pbro=4) - readonly -p pbro >&2 # shows up as "readonly" although unset + readonly -p >&2 # shows up as "readonly" although unset typeset -gr pbro # idempotent (no error)... print ${+pbro} >&2 # ...so still readonly... typeset -g +r pbro # ...can't turn it off @@ -1050,23 +1050,21 @@ $ZTST_testdir/../Src/zsh --emulate sh -f -c ' PATH=/bin; export PATH; readonly PATH - export -p PATH + export -p PATH # Should be a no-op, -p ignored typeset -p PATH readonly -p' 0: readonly/export output for exported+readonly+special when started as sh ->export PATH=/bin >export -r PATH=/bin >readonly PATH=/bin function { emulate -L sh MANPATH=/bin; export MANPATH; readonly MANPATH - export -p MANPATH + export -p MANPATH # Should be a no-op, -p ignored typeset -p MANPATH readonly -p } 0: readonly/export output for exported+readonly+tied+special after switching to sh emulation ->export MANPATH=/bin >export -rT MANPATH manpath=( /bin ) >readonly MANPATH=/bin diff --git a/Test/B03print.ztst b/Test/B03print.ztst index 0ef3743ce..4d2cf9764 100644 --- a/Test/B03print.ztst +++ b/Test/B03print.ztst @@ -4,6 +4,7 @@ # Use of print -p to output to coprocess A01grammar # Prompt expansion with print -P D01prompt # -l, -r, -R and -n indirectly tested in various places +# multibyte tests in D07multibyte # Not yet tested: # echo and pushln @@ -303,15 +304,16 @@ foo=$'one\ttwo\tthree\tfour\n' foo+=$'\tone\ttwo\tthree\tfour\n' foo+=$'\t\tone\t\ttwo\t\tthree\t\tfour' - print -x4 $foo - print -X4 $foo + 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 0:Tab expansion by print >one two three four > one two three four -> one two three four +> one two three fourZ >one two three four > one two three four -> one two three four +> one two three fourZ unset foo print -v foo once more diff --git a/Test/B07emulate.ztst b/Test/B07emulate.ztst index 7b1592fa9..45c39b51d 100644 --- a/Test/B07emulate.ztst +++ b/Test/B07emulate.ztst @@ -276,3 +276,25 @@ F:Some reserved tokens are handled in alias expansion 0:--emulate followed by other options >yes >no + + emulate sh -c ' + foo () { + VAR=foo && + echo $VAR | bar && + echo "$VAR" + } + bar () { + tr f b && + VAR="$(echo bar | tr r z)" && + echo "$VAR" + } + foo + ' + emulate sh -c 'func() { echo | local def="abc"; echo $def;}; func' + emulate sh -c 'abc="def"; echo | abc="ghi"; echo $abc' +0:emulate sh uses subshell for last pipe entry +>boo +>baz +>foo +> +>def diff --git a/Test/B10getopts.ztst b/Test/B10getopts.ztst index 72c9e209e..e50d177c7 100644 --- a/Test/B10getopts.ztst +++ b/Test/B10getopts.ztst @@ -96,3 +96,32 @@ done 0:missing option-argument (quiet mode) >:,x + + # This function is written so it can be easily referenced against other shells + t() { + local o i=0 n=$1 + shift + while [ $i -lt $n ]; do + i=$(( i + 1 )) + getopts a: o "$@" 2> /dev/null + done + printf '<%d>' "$OPTIND" + } + # Try all these the native way, then the POSIX_BUILTINS way + for 1 in no_posix_builtins posix_builtins; do ( + setopt $1 + print -rn - "$1: " + t 1 + t 1 foo + t 1 -- foo + t 1 -a + t 1 -b + t 2 -a -b + t 4 -a -b -c -d -a + t 5 -a -b -c -a -b -c + t 5 -a -b -c -d -ax -a + print + ); done +0:OPTIND calculation with and without POSIX_BUILTINS (workers/42248) +>no_posix_builtins: <1><1><2><1><1><3><5><7><6> +>posix_builtins: <1><1><2><2><2><3><6><7><7> diff --git a/Test/B11kill.ztst b/Test/B11kill.ztst new file mode 100644 index 000000000..dc6bf9b89 --- /dev/null +++ b/Test/B11kill.ztst @@ -0,0 +1,86 @@ +# Tests for the kill builtin. +# +# The exit codes 11 and 19 in this file don't mean anything special; they're +# just exit codes which are specific enough that the failure of `kill` itself +# can be differentiated from exiting due to executing a trap. + +%test + +# Correct invocation + + if zmodload zsh/system &>/dev/null; then + ( + trap 'exit 19' TERM + kill $sysparams[pid] + ) + else + ZTST_skip='Cannot zmodload zsh/system, skipping kill with no sigspec' + fi +19:kill with no sigspec + + + if zmodload zsh/system &>/dev/null; then + ( + trap 'exit 11' USR1 + kill -USR1 $sysparams[pid] + ) + else + ZTST_skip='Cannot zmodload zsh/system, skipping kill with sigspec' + fi +11:kill with sigspec + +# Incorrect invocation + + ( + kill a b c + ) +3:kill with multiple wrong inputs should increment status +?(eval):kill:2: illegal pid: a +?(eval):kill:2: illegal pid: b +?(eval):kill:2: illegal pid: c + + ( + kill -INT a b c + ) +3:kill with sigspec and wrong inputs should increment status +?(eval):kill:2: illegal pid: a +?(eval):kill:2: illegal pid: b +?(eval):kill:2: illegal pid: c + + ( + kill + ) +1:kill with no arguments +?(eval):kill:2: not enough arguments + + ( + kill -INT + ) +1:kill with sigspec only +?(eval):kill:2: not enough arguments + +# Regression tests: `kill ''` should not result in `kill 0`. +# +# We use SIGURG where an explicit sigspec can be provided as: +# +# 1. By default it's non-terminal, so even if we regress, we won't kill the +# test runner and other processes in the process group since we'll stop +# running this test before we get to the plain kill (and thus SIGTERM) +# cases; +# 2. It's also unlikely to be sent for any other reason during the process +# lifetime, so the test shouldn't be flaky. + + ( + trap 'exit 11' URG + kill -URG '' + ) +1:kill with empty pid and sigspec should not send signal to current process group +?(eval):kill:3: illegal pid: + + ( + trap 'exit 19' TERM + kill '' + ) +1:Plain kill with empty pid should not send signal to current process group +?(eval):kill:3: illegal pid: + diff --git a/Test/B12limit.ztst b/Test/B12limit.ztst new file mode 100644 index 000000000..9dce59824 --- /dev/null +++ b/Test/B12limit.ztst @@ -0,0 +1,28 @@ + +%prep + + if ! zmodload zsh/rlimits 2>/dev/null + then + ZTST_unimplemented="the zsh/rlimits module was disabled by configure (see config.modules)" + return 0 + fi + zmodload zsh/rlimits + +%test + + limit | grep UNKNOWN || print OK +0:Check if there is unknown resource(s) in the system +>OK +F:A failure here does not indicate any error in zsh. It just means there +F:is a resource in your system that is unknown to zsh developers. Please +F:report this to zsh-workers mailing list. + + () { + set -- ${(f)"$(ulimit -a)"} + set -- ${@%%:*} + typeset -aU unique_options=( "$@" ) + # The value of $unique_options is, e.g., ( -t -f '-N 2' -s ... ). + (( $# == $#unique_options )) + } +0:check if limit option letters are unique + diff --git a/Test/B13whence.ztst b/Test/B13whence.ztst new file mode 100644 index 000000000..3b35835fe --- /dev/null +++ b/Test/B13whence.ztst @@ -0,0 +1,34 @@ +%prep + + mkdir whence.tmp + ln -s . whence.tmp/cwd + # cd through the symlink in order to test the case that ${prefix} and + # ${prefix:P} are different + pushd whence.tmp/cwd + ln -s real step3 + ln -s step3 step2 + ln -s step2 step1 + ln -s loop loop + ln -s flip flop + ln -s flop flip + touch real + chmod +x real + prefix=$PWD + popd + +%test + + ( + path=( ${PWD:P}/whence.tmp $path ) + whence -S step1 + whence -s step1 + ) +0q:whence symlink resolution +>${prefix:P}/step1 -> ${prefix:P}/step2 -> ${prefix:P}/step3 -> ${prefix:P}/real +>${prefix:P}/step1 -> ${prefix:P}/real + + ( + path=( $PWD/whence.tmp $path ) + whence -S flip || whence -S loop || whence -s flip || whence -s loop + ) +1:whence deals with symlink loops gracefully diff --git a/Test/C01arith.ztst b/Test/C01arith.ztst index 419f45292..d0092fefa 100644 --- a/Test/C01arith.ztst +++ b/Test/C01arith.ztst @@ -180,9 +180,10 @@ 1:bases beyond 36 don't work ?(eval):1: invalid base (must be 2 to 36 inclusive): 37 + fail=39 print $(( 3 + "fail" )) -1:parse failure in arithmetic -?(eval):1: bad math expression: operand expected at `"fail" ' +0:Double quotes are not treated specially in arithmetic +>42 alias 3=echo print $(( 3 + "OK"); echo "Worked") @@ -487,3 +488,8 @@ let noexist==0 ) 1:Arithmetic, NO_UNSET part 3 ?(eval):2: noexist: parameter not set + + print $(( "6+2" / "1+3" )) +0:Double quotes are not treated specially in arithmetic (POSIX) +# and do not do grouping! this is 6 + (2/1) + 3 +>11 diff --git a/Test/C02cond.ztst b/Test/C02cond.ztst index 4b1ec02f0..4366b4142 100644 --- a/Test/C02cond.ztst +++ b/Test/C02cond.ztst @@ -146,39 +146,27 @@ # can't be bothered with -S - 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 + 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 ZTST_skip="[[ -N file ]] not supported with NFS" - elif { (( ! $+unmodified_ls )) && - 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 >= 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" + elif ! zmodload -F zsh/stat b:zstat 2> /dev/null; then + ZTST_skip='[[ -N file ]] not tested; zsh/stat not available' + elif ! { sleep 2; touch -a $unmodified 2> /dev/null }; then + ZTST_skip='[[ -N file ]] not tested; touch failed' + elif [[ "$(zstat +atime $unmodified)" == "$(zstat +mtime $unmodified)" ]]; then + ZTST_skip='[[ -N file ]] not supported on this file system' else [[ -N $newnewnew && ! -N $unmodified ]] fi 0:-N cond -F:This test can fail on NFS-mounted filesystems as the access and -F:modification times are not updated separately. The test will fail -F:on HFS+ (Apple Mac OS X default) filesystems because access times -F:are not recorded. Also, Linux ext3 filesystems may be mounted -F:with the noatime option which does not update access times. -F:Failures in these cases do not indicate a problem in the shell. +F:This test relies on the file system supporting atime updates. It +F:should automatically detect whether this is the case, and skip +F:without failing if it isn't, but it's possible that some +F:configurations may elude this detection. Please report this +F:scenario if you encounter it. [[ $newnewnew -nt $zlnfs && ! ($unmodified -nt $zlnfs) ]] 0:-nt cond @@ -392,7 +380,7 @@ F:Failures in these cases do not indicate a problem in the shell. >0 >0 >1 ->0 +>1 >0 >0 >1 diff --git a/Test/C04funcdef.ztst b/Test/C04funcdef.ztst index 407fc471f..af469c527 100644 --- a/Test/C04funcdef.ztst +++ b/Test/C04funcdef.ztst @@ -307,7 +307,7 @@ # lsfoo should not be expanded as an anonymous function argument alias lsfoo='This is not ls.' () (echo anon func; echo "$@") lsfoo -0:Anonmous function with arguments in a form nobody sane would ever use but unfortunately we have to support anyway +0:Anonymous function with arguments in a form nobody sane would ever use but unfortunately we have to support anyway >anon func >lsfoo @@ -508,7 +508,8 @@ # keep spec from getting loaded in parent shell for simplicity ( - if whence spec; then print spec already loaded >&2; exit 1; fi + if [[ $(whence -v spec) = 'spec is a shell function from '$PWD/* ]] + then print spec already loaded >&2; exit 1; fi autoload -Uz $PWD/spec autoload -Uz $PWD/extra/spec spec @@ -517,7 +518,8 @@ >I have been loaded by explicit path. ( - if whence spec; then print spec already loaded >&2; exit 1; fi + if [[ $(whence -v spec) = 'spec is a shell function from '$PWD/* ]] + then print spec already loaded >&2; exit 1; fi autoload -Uz $PWD/extra/spec autoload spec spec diff --git a/Test/D01prompt.ztst b/Test/D01prompt.ztst index 7ff478e68..6879e6fd1 100644 --- a/Test/D01prompt.ztst +++ b/Test/D01prompt.ztst @@ -229,3 +229,37 @@ Ffoo=${(%):-'%F{foo}'} # Unrecognised [[ $f == $Fdefault && $Fdefault == $Freset && $Freset == $Ffoo ]] 0:Regression test for workers/44029 + + if + zmodload zsh/terminfo >& /dev/null && + (( terminfo[colors] >= 8 )) + then + F1=$(echoti setaf 2) + F2=${(%):-%2F} + F3=${(%):-%F{2}} + F4=${(%):-%F{green}} + [[ -n $F1 && $F1 = $F2 && $F2 = $F3 && $F3 = $F4 ]] + else + ZTST_skip='Missing terminfo module or non-colour terminal' + fi +0:Equivalence of terminal colour settings (foreground colour) + + if + zmodload zsh/terminfo >& /dev/null && + (( terminfo[colors] >= 8 )) + then + K1=$(echoti setab 2) + K2=${(%):-%2K} + K3=${(%):-%K{2}} + K4=${(%):-%K{green}} + [[ -n $K1 && $K1 = $K2 && $K2 = $K3 && $K3 = $K4 ]] + else + ZTST_skip='Missing terminfo module or non-colour terminal' + fi +0:Equivalence of terminal colour settings (background colour) + + (RPS1=foo; echo $RPS1 $RPROMPT) + (RPS2=bar; echo $RPS2 $RPROMPT2) +-fD:RPS1 and RPROMPT are aliases (regression from 5.0.6) (workers/49600) +>foo foo +>bar bar diff --git a/Test/D02glob.ztst b/Test/D02glob.ztst index b0650c8c8..72891a2a7 100644 --- a/Test/D02glob.ztst +++ b/Test/D02glob.ztst @@ -690,10 +690,9 @@ # This is a bit brittle as it depends on PATH_MAX. # We could use sysconf.. bad_pwd="/${(l:16000:: :):-}" - print ${bad_pwd:P} + print ${#${bad_pwd:P}} 0:modifier ':P' with path too long -?(eval):4: path expansion failed, using root directory ->/ +>16001 foo=a value="ac" @@ -734,7 +733,7 @@ mkdir -m 444 glob.tmp/secret-d444 for 1 in 000 111 444 ; do ln -s secret-d$1 glob.tmp/secret-s$1; done print -rC 2 -- glob.tmp/secret-*/ glob.tmp/secret-*(-/) --f:unreadable directories can be globbed (users/24619, users/24626) +0:unreadable directories can be globbed (users/24619, users/24626) >glob.tmp/secret-d000/ glob.tmp/secret-d000 >glob.tmp/secret-d111/ glob.tmp/secret-d111 >glob.tmp/secret-d444/ glob.tmp/secret-d444 @@ -742,10 +741,79 @@ >glob.tmp/secret-s111/ glob.tmp/secret-s111 >glob.tmp/secret-s444/ glob.tmp/secret-s444 + for 1 in 000 111 444 ; do + chmod 777 glob.tmp/secret-d$1 + touch glob.tmp/secret-d$1/file + mkdir -m 777 glob.tmp/secret-d$1/dir + touch glob.tmp/secret-d$1/dir/file + chmod $1 glob.tmp/secret-d$1 + done + print -raC 2 -- glob.tmp/secret-*/* glob.tmp/secret-*/file +0:names inside unreadable directories can be globbed if searchable +>glob.tmp/secret-d444/dir glob.tmp/secret-d444/file +>glob.tmp/secret-s444/dir glob.tmp/secret-s444/file +>glob.tmp/secret-d111/file glob.tmp/secret-s111/file + + print -rC 2 -- glob.tmp/secret-*/dir/* +0:glob files in readable directories inside unreadable directories +>glob.tmp/secret-d111/dir/file glob.tmp/secret-s111/dir/file + + # On macOS, stat(2) allows files to be treated as directories if the calling + # process has super-user privileges. e.g., stat() on /my/regular/file/. will + # succeed as root but (correctly) fail otherwise. This can produce strange + # results when globbing, depending on how it's implemented. This test should, + # when run with privileges, confirm that the implementation avoids this + # problem. See workers/42891 and workers/45291 + : > glob.tmp/not-a-directory + print -r - glob.tmp/not-a-dir*(N) , glob.tmp/not-a-dir*/(N) +0:non-directories not globbed as directories +>glob.tmp/not-a-directory , + () { echo $1:P } ////dev -f:(workers/45367) modifier ':P' squashes multiple slashes >/dev + ln -s loop glob.tmp/loop + ln -s loop glob.tmp/trap + { + (set -- glob.tmp/trap; echo $1:P) + (set -- glob.tmp/loop; echo $1:P) + } always { + rm -f glob.tmp/trap glob.tmp/loop + } +0:the ':P' modifier handles symlink loops in the last path component +*>*/(trap|loop) +*>*/(trap|loop) + + ln -s loop glob.tmp/loop + ln -s loop glob.tmp/trap + { + (set -- glob.tmp/loop/trailing/components; echo $1:P) + (set -- glob.tmp/trap/trailing/components; echo $1:P) + } always { + rm -f glob.tmp/trap glob.tmp/loop + } +0:the ':P' modifier handles symlink loops before the last path component +*>*/glob.tmp/loop/trailing/components +*>*/glob.tmp/(loop|trap)/trailing/components + + ln -s flip glob.tmp/flop + ln -s flop glob.tmp/flip + { + (set -- glob.tmp/flip; echo $1:P) + (set -- glob.tmp/flip/trailing/components; echo $1:P) + } always { + rm -f glob.tmp/flip glob.tmp/flop + } +0:the ':P' modifier handles symlink loops other than the trivial case +*>*/glob.tmp/(flip|flop) +*>*/glob.tmp/(flip|flop)/trailing/components + + unsetopt extendedglob + print -r -- ${(*)=${(@s.+.):-A+B}/(#b)(?)/-${(L)match[1]} ${match[1]}} +0:the '*' qualfier enables extended_glob for pattern matching +>-a A -b B + %clean # Fix unreadable-directory permissions so ztst can clean up properly diff --git a/Test/D03procsubst.ztst b/Test/D03procsubst.ztst index 8cf4e2a7f..1e5cd9f6c 100644 --- a/Test/D03procsubst.ztst +++ b/Test/D03procsubst.ztst @@ -156,3 +156,16 @@ procfunc <(echo argument) 0:With /proc/self file descriptors must not be tidied up too early >argument + + $ZTST_testdir/../Src/zsh -df -o shfileexpansion -c 'cat =(echo hi)' +0:EQUALS expansion followed by =(...) (sh ordering) should work +>hi + + () { + local TMPPREFIX=$PWD/tmp + command true =(true) =(true) | : + print -rC1 -- $TMPPREFIX*(N) + } +0f:external command with =(...) on LHS of pipeline cleans up its tempfiles +# (Expected result: no output.) + diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index 76f3e77a1..6bf55b4db 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -1398,6 +1398,13 @@ >a6 a17 a117 b6 b17 b117 >b117 b17 b6 a117 a17 a6 + foo=(a-6 a117 a-17 a-34 b6 b-117 b17 b-2) + print ${(-)foo} + print ${(O-)foo} +0:Numeric sorting of signed integers +>a-34 a-17 a-6 a117 b-117 b-2 b6 b17 +>b17 b6 b-2 b-117 a117 a-6 a-17 a-34 + x=sprodj x[-10]=scrumf print $x @@ -1615,6 +1622,8 @@ print ${foo: -1} print ${foo: -10} print ${foo:5:-2} + print ${foo::3} + print ${foo: } 0:Bash-style offsets, scalar >456789 >56789 @@ -1627,6 +1636,8 @@ >9 >123456789 >67 +>123 +>123456789 foo=(1 2 3 4 5 6 7 8 9) print ${foo:3} @@ -1682,14 +1693,48 @@ >b >c + () { + emulate -L sh + local a=( one two three ) + printf '<%s><%s>\n' ${a[*]:0:2} + printf '<%s><%s>\n' "${a[*]:0:2}" + printf '<%s><%s>\n' ${a[@]:0:2} + printf '<%s><%s>\n' "${a[@]:0:2}" + printf '<%s><%s>\n' "${a:0:2}" + printf '<%s><%s>\n' ${*:1:2} + printf '<%s><%s>\n' "${*:1:2}" + printf '<%s><%s>\n' ${@:1:2} + printf '<%s><%s>\n' "${@:1:2}" + printf '<%s><%s>\n' ${*:0:2} + printf '<%s><%s>\n' "${*:0:2}" + printf '<%s><%s>\n' ${@:0:2} + printf '<%s><%s>\n' "${@:0:2}" + } one two three +0:Bash-style offsets, quoted array +><one><two> +><one two><> +><one><two> +><one><two> +><on><> +><one><two> +><one two><> +><one><two> +><one><two> +><(anon)><one> +><(anon) one><> +><(anon)><one> +><(anon)><one> + printf "%n" '[0]' 1:Regression test for identifier test ?(eval):1: not an identifier: [0] str=rts + print ${str:0: } print ${str:0:} 1:Regression test for missing length after offset -?(eval):2: unrecognized modifier +> +?(eval):3: unrecognized modifier foo="123456789" print ${foo:5:-6} @@ -2650,3 +2695,35 @@ F:behavior, see http://austingroupbugs.net/view.php?id=888 >1: pws >3: pw >4: pw + + : "${foo:0:${\"}}" +1:broken length in ${name:offset:length} (workers/45843#1) +?(eval):1: unrecognized modifier `$' + + $ZTST_testdir/../Src/zsh -fc $'$\\\n(' +1:regression test for workers/45843#2: escaped newline in command substitution start token +?zsh:2: parse error near `$(' + +# ` + + eval $'echo $\\\n(printf "%d\\n" $(( 4 + 2 )) )' +0:Normal command substitution with escaped newline +>6 + + eval $'echo $\\\n(( 14 / 2 ))' +0:Normal math eval with escaped newline after $ +>7 + + eval $'echo $(\\\n( 15 / 3 ))' +0:Normal math eval with escaped newline after $( +>5 + + function '*' { echo What a star; } + eval 'echo $(\*)' +0:Backslash character other than newline is normal after $( +>What a star + + : ${(zZ+x+):-} +1:parameter expansion flags parsing error gives a clue +?(eval):1: error in flags near position 7 in '${(zZ+x+):-}' + diff --git a/Test/D06subscript.ztst b/Test/D06subscript.ztst index c1a8d79cf..adbd398c4 100644 --- a/Test/D06subscript.ztst +++ b/Test/D06subscript.ztst @@ -289,3 +289,8 @@ F:Regression test for workers/42297 >14 24 >b b >b?rbaz foob?r + + i=1,3 + [[ ${a[$i]} = ${a[i]} ]] +0f:Math evaluation of commas in array subscripts +F:In math, (($i)) should be the same as ((i)), see workers/47748. diff --git a/Test/D07multibyte.ztst b/Test/D07multibyte.ztst index e20315340..cbd802f23 100644 --- a/Test/D07multibyte.ztst +++ b/Test/D07multibyte.ztst @@ -178,6 +178,13 @@ >3 ?(eval):1: command not found: hähä=3 + expr='[[ é = [[:IDENT:]] ]]' + ( unsetopt posix_identifiers; eval $expr && echo ok unset ) + ( setopt posix_identifiers; eval $expr || echo ok set ) +0:Regression test for workers/47745 +>ok unset +>ok set + foo="Ølaf«Ødd«øpénëd«ån«àpple" print -l ${(s.«.)foo} ioh="Ἐν ἀρχῇ ἦν ὁ λόγος, καὶ ὁ λόγος ἦν πρὸς τὸν θεόν, καὶ θεὸς ἦν ὁ λόγος." @@ -340,6 +347,18 @@ 0:Multibyte characters in printf widths > főo +# TODO?: POSIX requires that printf should always compute width and +# precision of '%s' conversion in bytes, while zsh computes them in +# characters if multi-byte locale is in use. + ARGV0=sh $ZTST_testdir/../Src/zsh -c "printf '<%10s>\n' St$'\M-C\M-)'phane" +0f:POSIX: width in %s should be computed in bytes, not in characters +F:This is considered a bugfix in zsh +>< Stéphane> + + ARGV0=sh $ZTST_testdir/../Src/zsh -c "printf '<%7.5s>\n' St$'\M-C\M-)'phane" +0f:POSIX: precision should also be computed in bytes, not in characers +>< Stép> + # We ask for case-insensitive sorting here (and supply upper case # characters) so that we exercise the logic in the shell that lowers the # case of the string for case-insensitive sorting. @@ -570,6 +589,15 @@ 0:printf %q and quotestring and general metafy / token madness >你你 + typeset foo + print -v foo 'ÖÓŐ' + echo $foo + printf -v foo 'ÖÓŐ' + echo $foo +0:print and printf into a variable with multibyte text +>ÖÓŐ +>ÖÓŐ + # This test is kept last as it introduces an additional # dependency on the system regex library. if zmodload zsh/regex 2>/dev/null; then @@ -585,3 +613,17 @@ >OK F:A failure here may indicate the system regex library does not F:support character sets outside the portable 7-bit range. + + ( + locale=$LANG + unset -m 'LC_*|LANG' + export LC_CTYPE=$locale + echo '\u276F' # this works + () { + local LC_ALL=C + } + echo '\u276F' # this doesn't work + ) +0:locale gets restored when locale parameters go out of scope (regression test for 45772) +>❯ +>❯ diff --git a/Test/E01options.ztst b/Test/E01options.ztst index cfe2c75cc..72749e6ab 100644 --- a/Test/E01options.ztst +++ b/Test/E01options.ztst @@ -57,7 +57,6 @@ # PROMPT_CR # PUSHD_SILENT # REC_EXACT -# RM_STAR_SILENT # RM_STAR_WAIT # SHARE_HISTORY # SINGLE_LINE_ZLE @@ -92,6 +91,9 @@ catpath=$(which cat) lspath==ls + # If the module fails to load, individual test points will skip. + zmodload zsh/zpty 2>/dev/null || true + %test # setopt should move on to the next operation in the face of an error, but @@ -1109,15 +1111,22 @@ F:Regression test for workers/41811 eval 'for f (word1 word2) print $f' eval 'repeat 3 print nonsense' } - unsetopt shortloops - print option unset + unsetopt shortloops shortrepeat + print shortloops and shortrepeat unset + fn + setopt shortrepeat + print shortrepeat set fn setopt shortloops - print option set + print shortloops set fn 0:SHORT_LOOPS option ->option unset ->option set +>shortloops and shortrepeat unset +>shortrepeat set +>nonsense +>nonsense +>nonsense +>shortloops set >foo >bar >word1 @@ -1128,6 +1137,8 @@ F:Regression test for workers/41811 ?(eval):1: parse error near `print' ?(eval):1: parse error near `print' ?(eval):1: parse error near `print' +?(eval):1: parse error near `print' +?(eval):1: parse error near `print' fn() { print -l $*; } setopt shwordsplit @@ -1414,3 +1425,44 @@ F:If this test fails at the first unsetopt, refer to P01privileged.ztst. (( UID == EUID )) fi 0:PRIVILEGED sanity check: default value is correct + + if zmodload -e zsh/zpty 2>/dev/null; then + for target_dir target_pattern in \ + '.' '*' \ + '/' '/*' + do + before=`ls -a -- $target_dir` + zpty subshell $ZTST_testdir/../Src/zsh -f +Z + [[ $PWD == */options.tmp ]] || return 1 # Sanity check before calling rm(1). + zpty -w subshell 'PS1=PROMPT' + zpty -r -m subshell REPLY $'*PROMPT' + zpty -w subshell "rm $target_pattern" + zpty -w -n subshell 'n' + sleep 1 + zpty -rt subshell REPLY && print -r -- ${REPLY%%$'\r\n'} + zpty -d subshell + after=`ls -a -- $target_dir` + [[ $before == $after ]] || return 1 + done + else + ZTST_skip="the zsh/zpty module is not available" + fi + BEL=$'\a' +0q:RM_STAR_SILENT +*>zsh: sure you want to delete all 15 files in ${PWD:h}/options.tmp \[yn\]\? ${BEL}(|n) +*>zsh: sure you want to delete (all <->|more than <->) files in / \[yn\]\? ${BEL}(|n) + + () { + local var + print ${(t)var} + } +0:(t) returns correct type +>scalar-local + + () { + readonly var + typeset -p var + } +0:readonly with typeset -p +F:compare E03posix.ztst +>typeset -r var='' diff --git a/Test/E02xtrace.ztst b/Test/E02xtrace.ztst index da6191cd0..56bc20f1a 100644 --- a/Test/E02xtrace.ztst +++ b/Test/E02xtrace.ztst @@ -146,3 +146,118 @@ ?+(anon):0> '(anon)' ?+(anon):0> true ?+fn:0> gn + + test_cases=( + f # baseline + foo-bar # Dash + ヌ # Meta (the UTF-8 representation of this character has an 0x83 byte) + \$\'ba\\0z\' # Nul, escaped as though by ${(qqqq)} + ) + for 1 in "$test_cases[@]"; do + eval " + ${1}() { + ${1}() { echo inner } + } + functions -T ${1} + ${1} + LC_ALL=C which ${1} + " + done +0:a function that redefines itself preserves tracing +>f () { +> # traced +> echo inner +>} +>foo-bar () { +> # traced +> echo inner +>} +>$'\M-c\M-\C-C\M-\C-L' () { +> # traced +> echo inner +>} +>$'ba\C-@z' () { +> # traced +> echo inner +>} + + function -T { echo traced anonymous function } + functions -- -T # no output +1:define traced function: anonymous function +?+(anon):0> echo traced anonymous function +>traced anonymous function + + function -T f { echo traced named function } + functions -- -T # no output + functions f + f +0:define traced function: named function +>f () { +> # traced +> echo traced named function +>} +?+f:0> echo traced named function +>traced named function + + function -T -- -T { echo trace function literally named "-T" } + -T + function -T -- { echo trace anonymous function } + functions -- -- # no output +1:define traced function: parse test +?+-T:0> echo trace function literally named -T +>trace function literally named -T +?+(anon):0> echo trace anonymous function +>trace anonymous function + + function -- g { echo g } + g + function -- { echo anonymous } + functions -- -- # no output +1:function end-of-"options" syntax, #1 +>g +>anonymous + + function -- -T { echo runs } + functions -- -- # no output + echo the definition didn\'t execute it + -T +0:function end-of-"options" syntax, #2 +>the definition didn't execute it +>runs + + f() g + g() : + functions -t f + f +0:functions -t smoke test #1 +?+f:4> g +?+g:4> : +F:The `4' on the second line is incorrect; see workers/48594. + + f() g + g() { () : } + functions -t f + f +0:functions -t smoke test #2 +?+f:4> g +?+g:0> '(anon)' +?+(anon):0> : + + f() g + g() : + ( + functions -T f + functions -t f + f + ) + ( + functions -t f + functions -T f + f + ) +0:ensure the behaviour of 'functions -Tt f' doesn't change surreptitiously +?+f:6> g +?+f:11> g +F:If this test fails, the new behaviour may be +F:workers/48591. + diff --git a/Test/E03posix.ztst b/Test/E03posix.ztst new file mode 100644 index 000000000..caab97ab6 --- /dev/null +++ b/Test/E03posix.ztst @@ -0,0 +1,161 @@ +# Test POSIX-specific behavior +# Currently this covers only POSIXBUILTINS, other behaviors are in their +# more directly related sections +# + +%prep + setopt POSIX_BUILTINS TYPESET_TO_UNSET + +%test + + local parentenv=preserved + fn() { + typeset -h +g -m \* + unset -m \* + integer i=9 + float -H f=9 + declare -t scalar + declare -H -a array + typeset + typeset + + } + fn + echo $parentenv +0:Parameter hiding and tagging, printing types and values +>array local array +>float local f +>integer local i=9 +>local tagged scalar +>array local array +>float local f +>integer local i +>local tagged scalar +>preserved + + readonly foo=bar novalue + readonly -p +0:readonly -p output (no readonly specials) +>readonly foo=bar +>readonly novalue + + local -a myarray + typeset -p1 myarray + myarray=("&" sand '""' "" plugh) + typeset -p1 myarray +0:typeset -p1 output for array +>typeset -a myarray +>typeset -a myarray=( +> '&' +> sand +> '""' +> '' +> plugh +>) + + local -A myhash + typeset -p1 myhash + myhash=([one]=two [three]= [four]="[]") + typeset -p1 myhash +0:typeset -p1 output for associative array +>typeset -A myhash +>typeset -A myhash=( +> [four]='[]' +> [one]=two +> [three]='' +>) + + str=s + arr=(a) + typeset -A ass + ass=(a a) + integer i=0 + float f=0 + print ${(t)str} ${(t)arr} ${(t)ass} ${(t)i} ${(t)f} +0:${(t)...} +>scalar array association-local integer-local float-local + + print $empty[(i)] $empty[(I)] +0:(i) and (I) return nothing for empty array +> + + ( + # reserved words are handled during parsing, + # hence eval... + disable -r typeset + eval ' + setopt kshtypeset + ktvars=(ktv1 ktv2) + typeset ktfoo=`echo arg1 arg2` $ktvars + () { + local ktfoo + print $+ktv1 $+ktv2 $+ktv3 $+ktfoo + } + print $ktfoo + unsetopt kshtypeset + typeset noktfoo=`echo noktarg1 noktarg2` + print $noktfoo + print $+noktarg1 $+noktarg2 + unset ktfoo ktv1 ktv2 noktfoo noktarg2 + ' + ) +0:KSH_TYPESET option +>0 0 0 0 +>arg1 arg2 +>noktarg1 +>0 0 + + () { + local var + print ${(t)var} + } +0:(t) returns correct type +>scalar-local + + () { + readonly var + typeset -p var + } +0:readonly with typeset -p +>typeset -g -r var + +# Tests expected to fail + + echo - +0f:A single "-" for echo does not end the arguments +F:POSIX requires a solitary "-" to be a plain argument +>- + + ARGV0=sh $ZTST_testdir/../Src/zsh -c 'foreach() { true; }' +-f:"foreach" is not a reserved word + + ARGV0=sh $ZTST_testdir/../Src/zsh -c 'end() { true; } +-f:"end" is not a reserved word + + a='a:b:' ARGV0=sh $ZTST_testdir/../Src/zsh -c 'IFS=:; printf "<%s>\n" $a' +0f:IFS is a separator, not a delimiter +><a> +><b> + + a=$'\ra\r\rb' ARGV0=sh $ZTST_testdir/../Src/zsh -c 'IFS=:; printf "<%s>\n" $a' +0f:All whitespace characters are "IFS whitespace" +F:isspace('\r') is true so \r should behave like space, \t, \n +F:This may also need to apply to multibyte whitespace +><a> +><b> + + ARGV0=sh $ZTST_testdir/../Src/zsh -c 'IFS=2; printf "<%s>\n" $((11*11))' +0f:IFS applies to math results (numbers treated as strings) +><1> +><1> + + ARGV0=sh $ZTST_testdir/../Src/zsh -c 'inf=42; echo $((inf))' +0:All identifiers are variable references in POSIX arithmetic +F:POSIX has neither math functions nor floating point +>42 + + ARGV0=sh $ZTST_testdir/../Src/zsh -c 'EUID=10; echo "$EUID"' +-f:EUID is not a special variable +>10 + + PPID=foo +-f:PPID is not a readonly variable diff --git a/Test/P01privileged.ztst b/Test/P01privileged.ztst index c54112bb6..7c4a1be35 100644 --- a/Test/P01privileged.ztst +++ b/Test/P01privileged.ztst @@ -13,8 +13,13 @@ # same requirements here.) # # If either of the aforementioned environment variables is not set, the test -# script will try to pick the first two >0 IDs from the passwd/group databases -# on the current system. +# script will try to use the UID/GID of the test directory, if not 0, for the +# two effective IDs. (This is intended to work around issues that might occur +# when e.g. the test directory lives under a home directory with mode 0700. +# Unfortunately, if this is the case, it will not be possible to use anything +# besides the directory owner or root as the test shell's EUID -- maintainers +# take note.) Otherwise, the script will pick the first >0 ID(s) from the +# passwd/group databases on the current system. # # If either variable is set, the tests will run, but they will likely fail # without super-user privileges. @@ -45,10 +50,12 @@ euid=${ZSH_TEST_UNPRIVILEGED_UID##*:} else print -ru$ZTST_fd 'Selecting unprivileged UID:EUID pair automatically' + # See above for why we do this + zmodload -sF zsh/stat b:zstat && euid=${"$( zstat +uid -- $ZTST_testdir )":#0} local tmp=$( getent passwd 2> /dev/null || < /etc/passwd ) # Note: Some awks require -v and its argument to be separate - ruid=$( awk -F: '$3 > 0 { print $3; exit; }' <<< $tmp ) - euid=$( awk -F: -v u=$ruid '$3 > u { print $3; exit; }' <<< $tmp ) + ruid=$( awk -F: -v u=${euid:-0} '$3 > 0 && $3 != u { print $3; exit; }' <<< $tmp ) + euid=${euid:-"$( awk -F: -v u=$ruid '$3 > u { print $3; exit; }' <<< $tmp )"} fi # if [[ -n $ZSH_TEST_UNPRIVILEGED_GID ]]; then @@ -56,10 +63,12 @@ egid=${ZSH_TEST_UNPRIVILEGED_GID##*:} else print -ru$ZTST_fd 'Selecting unprivileged GID:EGID pair automatically' + # See above again -- this shouldn't have the same impact as the UID, though + zmodload -sF zsh/stat b:zstat && egid=${"$( zstat +gid -- $ZTST_testdir )":#0} local tmp=$( getent group 2> /dev/null || < /etc/group ) # Note: Some awks require -v and its argument to be separate - rgid=$( awk -F: '$3 > 0 { print $3; exit; }' <<< $tmp ) - egid=$( awk -F: -v g=$rgid '$3 > g { print $3; exit; }' <<< $tmp ) + rgid=$( awk -F: -v g=${egid:-0} '$3 > 0 && $3 != g { print $3; exit; }' <<< $tmp ) + egid=${egid:="$( awk -F: -v g=$rgid '$3 > g { print $3; exit; }' <<< $tmp )"} fi # [[ $ruid/$euid == <1->/<1-> && $ruid != $euid ]] || ruid= euid= @@ -134,11 +143,9 @@ %test - re_zsh $ruid $ruid -1 -1 'echo $UID/$EUID $options[privileged]' re_zsh $euid $euid -1 -1 'echo $UID/$EUID $options[privileged]' re_zsh $ruid $euid -1 -1 'echo $UID/$EUID $options[privileged]' 0q:PRIVILEGED automatically enabled when RUID != EUID ->$ruid/$ruid off >$euid/$euid off >$ruid/$euid on diff --git a/Test/V01zmodload.ztst b/Test/V01zmodload.ztst index 55c3c314b..daf49cd72 100644 --- a/Test/V01zmodload.ztst +++ b/Test/V01zmodload.ztst @@ -64,7 +64,7 @@ for m in $mods do - zmodload $m || mods[(r)$m]=() + zmodload $m || return $? done 0d:Test loading of all compiled modules @@ -349,6 +349,52 @@ ?(eval):9: file descriptor out of range $ZTST_testdir/../Src/zsh -fc " + MODULE_PATH=${(q)MODULE_PATH} + if zmodload -e zsh/parameter; then zmodload -u zsh/parameter; fi + unset options + zmodload zsh/parameter + echo \$+options + " +-f:can unset a non-readonly autoloadable parameter before loading the module +>0 +# Currently prints '1'. + + $ZTST_testdir/../Src/zsh -fc " + MODULE_PATH=${(q)MODULE_PATH} + zmodload zsh/parameter + unset options + echo \$+options + " +0:can unset a non-readonly autoloadable parameter after loading the module +>0 + + $ZTST_testdir/../Src/zsh -fc " + MODULE_PATH=${(q)MODULE_PATH} + if zmodload -e zsh/parameter; then zmodload -u zsh/parameter; fi + unset builtins + " +-f:can't unset a readonly autoloadable parameter before loading the module +*?zsh:?: read-only variable: builtins +# Currently, the 'unset' succeeds. + + $ZTST_testdir/../Src/zsh -fc " + MODULE_PATH=${(q)MODULE_PATH} + zmodload zsh/parameter + unset builtins + " +1:can't unset a readonly autoloadable parameter after loading the module +*?zsh:?: read-only variable: builtins + + $ZTST_testdir/../Src/zsh -fc " + MODULE_PATH=${(q)MODULE_PATH} + zmodload zsh/parameter + zmodload -u zsh/parameter + echo \$options + " +0:unloading a module doesn't implicitly unset autoloadable parameters +*>(on|off) * + + $ZTST_testdir/../Src/zsh -fc " MODULE_PATH=${(q)MODULE_PATH} # zmodload zsh/zutil diff --git a/Test/V05styles.ztst b/Test/V05styles.ztst index c221d9db8..61d2cdb0a 100644 --- a/Test/V05styles.ztst +++ b/Test/V05styles.ztst @@ -151,3 +151,25 @@ >one >two + ( + zstyle ':weather:*:Sunday:*' preferred-precipitation snow + zstyle ':weather:europe:*' preferred-precipitation rain + zstyle -s ':weather:europe:Sunday:foo' preferred-precipitation REPLY && print $REPLY + ) + ( + zstyle ':weather:europe:*' preferred-precipitation rain + zstyle ':weather:*:Sunday:*' preferred-precipitation snow + zstyle -s ':weather:europe:Sunday:foo' preferred-precipitation REPLY && print $REPLY + ) +0:the example in the documentation remains correct +>snow +>snow + + ( + zstyle $'con\x00text' $'ke\x00y' $'val\x00u' $'e' + a=( ${(f)"$(zstyle -L)"} ) + a=( ${(M)a:#*con*text*ke*y*val*u*e} ) + print -r -- "$a" + ) +0:zstyle -L escapes the key (regression: workers/48424) +>zstyle $'con\C-@text' $'ke\C-@y' $'val\C-@u' e diff --git a/Test/V07pcre.ztst b/Test/V07pcre.ztst index ab67f3d80..c9c844d2a 100644 --- a/Test/V07pcre.ztst +++ b/Test/V07pcre.ztst @@ -1,11 +1,10 @@ %prep - if grep '^name=zsh/pcre .* link=no ' $ZTST_testdir/../config.modules >/dev/null + if ! zmodload zsh/pcre 2>/dev/null then ZTST_unimplemented="the zsh/pcre module was disabled by configure (see config.modules)" return 0 fi - zmodload zsh/pcre setopt rematch_pcre # Find a UTF-8 locale. setopt multibyte diff --git a/Test/V08zpty.ztst b/Test/V08zpty.ztst index b0cbfa050..057db2e18 100644 --- a/Test/V08zpty.ztst +++ b/Test/V08zpty.ztst @@ -6,8 +6,6 @@ if ! zmodload zsh/zpty 2>/dev/null then ZTST_unimplemented="the zsh/zpty module is not available" - elif [[ $OSTYPE = cygwin ]]; then - ZTST_unimplemented="the zsh/zpty module does not work on Cygwin" fi %test diff --git a/Test/V09datetime.ztst b/Test/V09datetime.ztst index 9f67ecec3..622bdf6ed 100644 --- a/Test/V09datetime.ztst +++ b/Test/V09datetime.ztst @@ -130,3 +130,7 @@ >%6. 2002-02-02 02:02:02.999999 >%9. 2002-02-02 02:02:02.999999999 >%12. 2002-02-02 02:02:02.999999999 + + strftime -n 'one line%n' 2> /dev/null +0:-n option +>one line diff --git a/Test/V10private.ztst b/Test/V10private.ztst index a3a63867b..56ffbc5b4 100644 --- a/Test/V10private.ztst +++ b/Test/V10private.ztst @@ -19,14 +19,14 @@ () { print $scalar_test private scalar_test - print $+scalar_test + typeset +m scalar_test unset scalar_test print $+scalar_test } print $scalar_test 0:basic scope hiding >toplevel ->1 +>local scalar_test >0 >toplevel @@ -45,14 +45,14 @@ print $+unset_test () { private unset_test - print $+unset_test + typeset +m unset_test unset_test=setme print $unset_test } print $+unset_test 0:variable defined only in scope >0 ->1 +>local unset_test >setme >0 @@ -62,13 +62,13 @@ local -Pa array_test=(in function) () { private array_test - print $+array_test + typeset +m array_test } print $array_test } print $array_test 0:nested scope with different type, correctly restored ->1 +>local array_test >in function >top level @@ -116,14 +116,14 @@ } outer () { local -PA hash_test=(in function) - typeset -p hash_test + private + hash_test inner } outer print ${(kv)hash_test} 0:private hides value from surrounding scope in nested scope >typeset -a hash_test=( top level ) ->typeset -A hash_test=( [in]=function ) +>hash_test=( [in]=function ) >typeset -g -a hash_test=( top level ) >array-local top level >top level @@ -246,7 +246,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: attempt to assign private in nested scope +?(anon):4: array_test: can't change parameter attribute F:future revision will create a global with this assignment typeset -a array_test diff --git a/Test/V12zparseopts.ztst b/Test/V12zparseopts.ztst index d7fc33f72..816e1d041 100644 --- a/Test/V12zparseopts.ztst +++ b/Test/V12zparseopts.ztst @@ -65,11 +65,11 @@ } $=1 done 0:zparseopts -F -?(anon):zparseopts:2: bad option: x +?(anon):zparseopts:2: bad option: -x >ret: 1, optv: , argv: -a -x -z -?(anon):zparseopts:2: bad option: x +?(anon):zparseopts:2: bad option: -x >ret: 1, optv: , argv: -ax -z -?(anon):zparseopts:2: bad option: - +?(anon):zparseopts:2: bad option: --x >ret: 1, optv: , argv: -a --x -z for 1 in '-a 1 2 3' '1 2 3'; do @@ -168,5 +168,5 @@ print -r - ret: $?, optv: $optv, argv: $argv } -ab1 -c 0:missing optarg -?(anon):zparseopts:2: missing argument for option: c +?(anon):zparseopts:2: missing argument for option: -c >ret: 1, optv: , argv: -ab1 -c diff --git a/Test/V13zformat.ztst b/Test/V13zformat.ztst new file mode 100644 index 000000000..035a0a495 --- /dev/null +++ b/Test/V13zformat.ztst @@ -0,0 +1,91 @@ +# Test the use of zformat, if the zsh/zutil module is available. + +%prep + + if ! zmodload zsh/zutil 2>/dev/null; then + ZTST_unimplemented="can't load the zsh/zutil module for testing" + fi + + # Helper function. Expects a single format using %s and a value for it. + zformat_and_print_s() { + zformat -f REPLY "$1" "s:$2" + print -r - ${(qq)REPLY} + } + +%test + + zformat_and_print_s '%s' foo + zformat_and_print_s '%5s' min + zformat_and_print_s '%-5s' neg + zformat_and_print_s '%5.s' empty + zformat_and_print_s '%.5s' max + zformat_and_print_s '%.5s' truncated +0:basic zformat test +>'foo' +>'min ' +>' neg' +>'empty' +>'max' +>'trunc' + + # There may be a set of digits either before or after the opening parenthesis. + zformat_and_print_s 'The answer is "%3(s.yes.no)".' 3 + zformat_and_print_s 'The answer is "%(3s.yes.no)".' 3 + # The test number defaults to zero. + zformat_and_print_s '%(s.equal.unequal)' -1 + zformat_and_print_s '%(s.equal.unequal)' 0 + zformat_and_print_s '%(s.equal.unequal)' 1 + # Negative numbers are allowed + # The delimiter is arbitrary + zformat_and_print_s '%-4(s.minus four.)' -4 + zformat_and_print_s '%(-4s//minus four)' -4 + # The argument is evaluated as a math expression + zformat_and_print_s '%18(s.math.)' '6*3' +0:basic conditionals test +>'The answer is "yes".' +>'The answer is "yes".' +>'unequal' +>'equal' +>'unequal' +>'minus four' +>'' +>'math' + + () { + zformat -f 1 '%(8n.%(5j.yes.no).no)' 'n:8' 'j:5' + echo $1 + } +0:nested conditionals test +>yes + + () { + zformat -f 1 '%(w.zero.fail) %(x.fail.present) %(y.empty.fail) %(z.missing.fail)' w:0 x:1 y: + zformat -F 2 '%(w.zero.fail) %(x.present.fail) %(y.fail.empty) %(z.fail.missing)' w:0 x:1 y: + echo $1 + echo $2 + } +0:conditionals with empty and missing values +>zero present empty missing +>zero present empty missing + + () { + local l + for l in 0 1 2 3; do + zformat -F 1 "%$l(a.a.A)%$l(b.b.B)%$l(c.c.C)%$l(d.d.D)" a: b:1 c:12 d:123 + zformat -F 2 "%-$l(a.a.A)%-$l(b.b.B)%-$l(c.c.C)%-$l(d.d.D)" a: b:1 c:12 d:123 + print - $1 $2 + done + } +0:minimum and maximum widths +>Abcd aBCD +>ABcd abCD +>ABCd abcD +>ABCD abcd + + zformat -a argv . foo:lorem ipsum:bar bazbaz '\\esc\:ape' + print -rl -- "$@" +0:basic -a test +>foo .lorem +>ipsum.bar +>bazbaz +>\esc:ape diff --git a/Test/V14system.ztst b/Test/V14system.ztst new file mode 100644 index 000000000..100daab08 --- /dev/null +++ b/Test/V14system.ztst @@ -0,0 +1,149 @@ +# Test zsh/system module + +%prep + + if zmodload -s zsh/system && zmodload -s zsh/zselect; then + tst_dir=V14.tmp + mkdir -p -- $tst_dir + else + ZTST_unimplemented='the zsh/system and zsh/zselect modules are not available' + fi + : > $tst_dir/file # File on which to acquire flock. + +%test + + ( + zsystem flock -t 0 -i 0.000001 $tst_dir/file && + zsystem flock -t 0.1 -i 0.000001 $tst_dir/file && + zsystem flock -t 0.1 -i 0.0000001 $tst_dir/file && + zsystem flock -t 1 -i 0.000001 $tst_dir/file + ) +0:zsystem flock valid time arguments + + ( + zsystem flock -t 1073741824 $tst_dir/file || + zsystem flock -t 1e100 $tst_dir/file || + zsystem flock -i -1 $tst_dir/file || + zsystem flock -i 0 $tst_dir/file || + zsystem flock -i 1e100 $tst_dir/file + ) +1:zsystem flock invalid time arguments +?(eval):zsystem:2: flock: invalid timeout value: '1073741824' +?(eval):zsystem:3: flock: invalid timeout value: '1e100' +?(eval):zsystem:4: flock: invalid interval value: '-1' +?(eval):zsystem:5: flock: invalid interval value: '0' +?(eval):zsystem:6: flock: invalid interval value: '1e100' + + ( + # Lock file for 1 second in the background. + lock_flag=$tst_dir/locked1 + (zsystem flock $tst_dir/file \ + && touch $lock_flag \ + && zselect -t 100 + mv $lock_flag $lock_flag.done) & + # Wait until sub-shell above has started. + while ! [[ -f $lock_flag || -f $lock_flag.done ]]; do + zselect -t 1 + done + if [[ -f $lock_flag.done ]]; then + echo "Background shell should not have completed already." 1>&2 + else + # Attempt to lock file with 0.5 second timeout: must fail. + zsystem flock -t 0.5 $tst_dir/file + fi + ) +2:zsystem flock unsuccessful wait test +F:This timing test might fail due to process scheduling issues unrelated to zsh. + + ( + # Lock file for 0.5 second in the background. + lock_flag=$tst_dir/locked2 + (zsystem flock $tst_dir/file \ + && touch $lock_flag \ + && zselect -t 50 + mv $lock_flag $lock_flag.done) & + # Wait until sub-shell above has started. + while ! [[ -f $lock_flag || -f $lock_flag.done ]]; do + zselect -t 1 + done + if [[ -f $lock_flag.done ]]; then + echo "Background shell should not have completed already." 1>&2 + fi + typeset -F SECONDS + start=$SECONDS + # Attempt to lock file without a timeout: + # must succeed after sub-shell above releases it (0.5 second). + if zsystem flock $tst_dir/file; then + elapsed=$[ $SECONDS - $start ] + if [[ $elapsed -ge 0.3 && $elapsed -le 0.7 ]]; then + echo "elapsed time seems OK" 1>&2 + else + echo "elapsed time $elapsed should be ~ 0.5 second" 1>&2 + fi + fi + ) +0:zsystem flock successful wait test, no timeout +?elapsed time seems OK +F:This timing test might fail due to process scheduling issues unrelated to zsh. + + ( + # Lock file for 0.5 second in the background. + lock_flag=$tst_dir/locked3 + (zsystem flock $tst_dir/file \ + && touch $lock_flag \ + && zselect -t 50 + mv $lock_flag $lock_flag.done) & + # Wait until sub-shell above has started. + while ! [[ -f $lock_flag || -f $lock_flag.done ]]; do + zselect -t 1 + done + if [[ -f $lock_flag.done ]]; then + echo "Background shell should not have completed already." 1>&2 + fi + typeset -F SECONDS + start=$SECONDS + # Attempt to lock file with 1-second timeout: + # must succeed 1 second after start because we retry every 1 second. + if zsystem flock -t 1 $tst_dir/file; then + elapsed=$[ $SECONDS - $start ] + if [[ $elapsed -ge 0.8 && $elapsed -le 1.2 ]]; then + echo "elapsed time seems OK" 1>&2 + else + echo "elapsed time $elapsed should be ~ 1 second" 1>&2 + fi + fi + ) +0:zsystem flock successful wait test, integral seconds +?elapsed time seems OK +F:This timing test might fail due to process scheduling issues unrelated to zsh. + + ( + # Lock file for 0.25 second in the background. + lock_flag=$tst_dir/locked4 + (zsystem flock $tst_dir/file \ + && touch $lock_flag \ + && zselect -t 25 + mv $lock_flag $lock_flag.done) & + # Wait until sub-shell above has started. + while ! [[ -f $lock_flag || -f $lock_flag.done ]]; do + zselect -t 1 + done + if [[ -f $lock_flag.done ]]; then + echo "Background shell should not have completed already." 1>&2 + fi + typeset -F SECONDS + start=$SECONDS + # Attempt to lock file with 0.4-second timeout, retrying every 0.1 second: + # must succeed 0.3 second after start. + if zsystem flock -t 0.4 -i 0.1 $tst_dir/file; then + elapsed=$[ $SECONDS - $start ] + if [[ $elapsed -ge 0.2 && $elapsed -le 0.5 ]]; then + echo "elapsed time seems OK" 1>&2 + else + echo "elapsed time $elapsed should be ~ 0.3 second" 1>&2 + fi + fi + ) +0:zsystem flock successful wait test, fractional seconds +?elapsed time seems OK +F:This timing test might fail due to process scheduling issues unrelated to zsh. diff --git a/Test/W01history.ztst b/Test/W01history.ztst index 0b2f60d1e..1d3f3cf6f 100644 --- a/Test/W01history.ztst +++ b/Test/W01history.ztst @@ -88,3 +88,25 @@ F:Check that a history bug introduced by workers/34160 is working again. 0:Modifier :P >/my/path/for/testing >/my/path/for/testing + + $ZTST_testdir/../Src/zsh -fgis <<<' + SAVEHIST=7 + print -rs "one\\" + print -rs "two\\\\" + print -rs "three\\\\\\" + print -rs "four\\\\\\\\" + print -rs "five\\\\\\\\\\" + print -s "while false\ndo\ntrue\\\\\n && break\ndone" + print -s "echo one\\\\\ntwo" + fc -W hist + fc -p -R hist + fc -l + rm hist' 2>/dev/null +0:Lines ending in backslash saved and restored to history +> 1 one\ +> 2 two\\ +> 3 three\\\ +> 4 four\\\\ +> 5 five\\\\\ +> 6 while false\ndo\ntrue\\n && break\ndone +> 7 echo one\\ntwo diff --git a/Test/W02jobs.ztst b/Test/W02jobs.ztst index fe12f979d..d52888dd9 100644 --- a/Test/W02jobs.ztst +++ b/Test/W02jobs.ztst @@ -2,9 +2,7 @@ %prep - if [[ $OSTYPE == cygwin ]]; then - ZTST_unimplemented='the zsh/zpty module does not work on Cygwin' - elif zmodload zsh/zpty 2> /dev/null; then + if zmodload zsh/zpty 2> /dev/null; then zpty_start() { export PS1= PS2= zpty -d @@ -146,12 +144,14 @@ zpty_start zpty_input 'sleep 3 &' zpty_input 'jobs -r' + zpty_input '(jobs -r)' zpty_input 'print -- -' zpty_input 'jobs -s' zpty_stop 0:`jobs -r` and `jobs -s` with running job *>\[1] [0-9]## *>\[1] + running*sleep* +*>\[1] + running*sleep* *>- *>zsh:*SIGHUPed* diff --git a/Test/W03jobparameters.ztst b/Test/W03jobparameters.ztst new file mode 100644 index 000000000..a6f7a09b1 --- /dev/null +++ b/Test/W03jobparameters.ztst @@ -0,0 +1,78 @@ +# Tests for interactive job control with parameter state + +%prep + + if zmodload zsh/zpty 2> /dev/null; then + zpty_start() { + export PS1= PS2= + zpty -d + zpty zsh "${(q)ZTST_testdir}/../Src/zsh -fiV +Z" + } + zpty_input() { + zpty -w zsh "${(F)@}" $'\n' + } + zpty_line() { + local REPLY + integer i + for (( i = 0; i < ${1:-1}; ++i )); do + zpty -r zsh REPLY + print -r -- ${REPLY%%($'\r\n'|$'\n')} + done + } + zpty_stop() { + # exit twice in case of check_jobs + zpty -w zsh $'exit\nexit\n' + # zpty gives no output when piped without these braces (?) + { zpty -r zsh } | sed $'/[^[:space:]]/!d; s/\r$//;' + zpty -d + : + } + if ! zmodload zsh/parameter 2> /dev/null; then + ZTST_unimplemented='the zsh/parameter module is not available' + fi + else + ZTST_unimplemented='the zsh/zpty module is not available' + fi + +%test + + zpty_start + zpty_input "MODULE_PATH=${(q)MODULE_PATH}" + zpty_input 'sleep 3 &' + zpty_input 'print $jobstates' + zpty_input '(print $jobstates)' + zpty_input 'jobs -s' + zpty_stop +0:$jobstate for running job in main shell and subshell +*>\[1] [0-9]## +*>running:+:*=running +*>running:+:*=running +*>zsh:*SIGHUPed* + +# $jobstates refers to a job started in the main shell unless +# one has been started in the subshell. In the latter case, +# the subshell has no job control so the job is not marked as current. + zpty_start + zpty_input "MODULE_PATH=${(q)MODULE_PATH}" + zpty_input 'sleep 3 &' + zpty_input '(print main; print $jobstates; sleep 2& print sub; print $jobstates)' + zpty_input 'jobs -s' + zpty_stop +0:$jobstate shows one job started in main shell or one started in subshell +*>\[1] [0-9]## +>main +*>running:+:*=running +>sub +*>running::*=running +*>zsh:*SIGHUPed* + +# output from zpty removes empty lines + zpty_start + zpty_input "MODULE_PATH=${(q)MODULE_PATH}" + zpty_input '(print main; print $jobstates; sleep 2& print sub; print $jobstates)' + zpty_input 'jobs -s' + zpty_stop +0:$jobstate shows no job started in main shell but one started in subshell +>main +>sub +*>running::*=running diff --git a/Test/X02zlevi.ztst b/Test/X02zlevi.ztst index 4e7966e12..8146d6752 100644 --- a/Test/X02zlevi.ztst +++ b/Test/X02zlevi.ztst @@ -11,9 +11,7 @@ break; fi done - if [[ $OSTYPE = cygwin ]]; then - ZTST_unimplemented="the zsh/zpty module does not work on Cygwin" - elif ( zmodload zsh/zpty 2>/dev/null ); then + if ( zmodload zsh/zpty 2>/dev/null ); then . $ZTST_srcdir/comptest comptestinit -v -z $ZTST_testdir/../Src/zsh else diff --git a/Test/X03zlebindkey.ztst b/Test/X03zlebindkey.ztst index e5aac7379..3e299a337 100644 --- a/Test/X03zlebindkey.ztst +++ b/Test/X03zlebindkey.ztst @@ -12,9 +12,7 @@ break; fi done - if [[ $OSTYPE = cygwin ]]; then - ZTST_unimplemented="the zsh/zpty module does not work on Cygwin" - elif ( zmodload zsh/zpty 2>/dev/null ); then + if ( zmodload zsh/zpty 2>/dev/null ); then . $ZTST_srcdir/comptest comptestinit -z $ZTST_testdir/../Src/zsh else diff --git a/Test/X04zlehighlight.ztst b/Test/X04zlehighlight.ztst index 475a2e309..f84c02505 100644 --- a/Test/X04zlehighlight.ztst +++ b/Test/X04zlehighlight.ztst @@ -5,8 +5,6 @@ export TERM=xterm-256color if [[ ${+termcap} != 1 || ${termcap[Co]} != <-> || ${termcap[Co]} -lt 256 ]]; then ZTST_unimplemented="no termcap module OR termcap doesn't support 256 or more colors" - elif [[ $OSTYPE == cygwin ]]; then - ZTST_unimplemented='the zsh/zpty module does not work on Cygwin' elif zmodload zsh/zpty 2> /dev/null; then zpty_start() { export PS1= PS2= @@ -95,6 +93,50 @@ >0m27m24mCDE|32|trueCDE|39| zpty_start + zpty_input 'rh_widget() { region_highlight+=( "0 4 fg=green memo=someplugin" ); typeset -p region_highlight }' + zpty_input 'zle -N rh_widget' + zpty_input 'bindkey "\C-a" rh_widget' + zpty_enable_zle + zpty_input $'\C-a' + zpty_line + zpty_stop +0:region_highlight memo information round trips +>typeset -a region_highlight=( '0 4 fg=green memo=someplugin' ) + + zpty_start + zpty_input 'rh_widget() { region_highlight+=( "0 4 fg=green memo=someplugin,futureattribute=futurevalue" ); typeset -p region_highlight }' + zpty_input 'zle -N rh_widget' + zpty_input 'bindkey "\C-a" rh_widget' + zpty_enable_zle + zpty_input $'\C-a' + zpty_line + zpty_stop +0:region_highlight memo information forward compatibility, #1 +>typeset -a region_highlight=( '0 4 fg=green memo=someplugin' ) + + zpty_start + zpty_input 'rh_widget() { region_highlight+=( "0 4 fg=green memo=someplugin futurefifthfield" ); typeset -p region_highlight }' + zpty_input 'zle -N rh_widget' + zpty_input 'bindkey "\C-a" rh_widget' + zpty_enable_zle + zpty_input $'\C-a' + zpty_line + zpty_stop +0:region_highlight memo information forward compatibility, #2 +>typeset -a region_highlight=( '0 4 fg=green memo=someplugin' ) + + zpty_start + zpty_input 'rh_widget() { region_highlight+=( "0 4 fg=green memo=some'$'\0''plugin" ); typeset -p region_highlight }' + zpty_input 'zle -N rh_widget' + zpty_input 'bindkey "\C-a" rh_widget' + zpty_enable_zle + zpty_input $'\C-a' + zpty_line + zpty_stop +0:region_highlight memo information forward compatibility, #3: NULs +>typeset -a region_highlight=( '0 4 fg=green memo=some' ) + + zpty_start zpty_input 'rh_widget() { BUFFER="true"; region_highlight+=( "0 4 fg=#040810" ); }' zpty_input 'zle -N rh_widget' zpty_input 'bindkey "\C-a" rh_widget' @@ -154,6 +196,17 @@ 0:overlapping region_highlight with near-color (hex-triplets at input) >0m27m24mCDE|340|tCDE|3160|rCDE|39|CDE|340|ueCDE|39| + zpty_start + zpty_input 'f () { zle clear-screen; zle g -f nolast; BUFFER=": ${(q)LASTWIDGET}" }; zle -N f' + zpty_input 'g () { }; zle -N g' + zpty_input 'bindkey "\C-a" f' + zpty_enable_zle + zpty_input $'\C-a' + zpty_line 1 p + zpty_stop +0:zle $widgetname -f nolast +>0m27m24m0m27m24m: clear-screen + %clean zmodload -ui zsh/zpty diff --git a/Test/Y01completion.ztst b/Test/Y01completion.ztst index 51f604bcf..6af0efc6d 100644 --- a/Test/Y01completion.ztst +++ b/Test/Y01completion.ztst @@ -11,9 +11,7 @@ break; fi done - if [[ $OSTYPE = cygwin ]]; then - ZTST_unimplemented="the zsh/zpty module does not work on Cygwin" - elif ( zmodload zsh/zpty 2>/dev/null ); then + if ( zmodload zsh/zpty 2>/dev/null ); then . $ZTST_srcdir/comptest mkdir comp.tmp cd comp.tmp @@ -46,6 +44,35 @@ >line: {: dir1/}{} >line: {: dir2/}{} + comptest $': *\t\t\t\t\t\t' +0:_expand shows file types +>line: {: dir1/}{} +>DESCRIPTION:{expansions} +>DI:{dir1} +>DI:{dir2} +>FI:{file1} +>FI:{file2} +>DESCRIPTION:{all expansions} +>NO:{dir1 dir2 file1 file2} +>DESCRIPTION:{original} +>NO:{*} +>line: {: dir2/}{} +>line: {: file1 }{} +>line: {: file2 }{} +>line: {: dir1 dir2 file1 file2 }{} +>line: {: *}{} + + comptesteval $'zstyle \'*\' glob no' + comptesteval $'typeset -g tst=(*)' + comptest $': $tst\C-D' +0:_expand preserves array form +>DESCRIPTION:{expansions} +>NO:{dir1} +>NO:{dir2} +>NO:{file1} +>NO:{file2} + + comptesteval $'zstyle -d \'*\' glob' comptesteval '_users () { compadd user1 user2 }' comptest $': ~\t\t\t\t\t' 0:tilde @@ -156,6 +183,9 @@ F:regression test workers/31611 >line: {tst word:/}{} # Test for regression introduced by workers/41242, raised in workers/43842 + if [[ $OSTYPE = cygwin ]]; then + ZTST_skip="\\ is equivalent to / in pathname on cygwin" + else { mkdir sortnobslash touch sortnobslash/{'!foo','#foo','\foo','|foo','~foo',Afoo,bfoo} @@ -165,6 +195,7 @@ F:regression test workers/31611 comptesteval 'LC_ALL=$old_LC_ALL' rm -rf sortnobslash } + fi 0:ignore backslashes when sorting completion matches >line: {: sortnobslash/}{} >DESCRIPTION:{file} @@ -216,6 +247,16 @@ F:regression test workers/31611 >NO:{3pm} >NO:{10pm} + comptesteval "_tst() { _arguments ':desc:_sequence compadd - 1 2 3' }" + comptesteval "zstyle ':completion:*:tst:*' ignored-patterns 2" + comptest $'tst 1,\t' + comptesteval "zstyle -d ':completion:*:tst:*' ignored-patterns" +0:-F does not break _sequence +>line: {tst 1,}{} +>DESCRIPTION:{desc} +>NO:{2} +>NO:{3} + comptest $'a=() b=(\t' 0:multiple envarrays >line: {a=() b=(}{} @@ -225,6 +266,74 @@ F:regression test workers/31611 >FI:{file1} >FI:{file2} + 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:*" fake-parameters bar bat bay' + comptest $': $ba\t' +0:extra-verbose shows parameter values +>line: {: $ba}{} +>DESCRIPTION:{parameter} +>NO:{bar -- ( '^@' '^A' '^B' '^C' '^D' '^E' '^F' '^G' '^H' '\t' '\n' '^K' '^L' '} +>NO:{bat -- ( [' ']='!' ['"']='#' ['$']=% ['&']=\' ['(']=')' ['*']=+ [,]=- [.]=/} +>NO:{bay -- '^@ ^A ^B ^C ^D ^E ^F ^G ^H \t \n ^K ^L ^M ^N ^O ^P ^Q ^R ^S ^T ^U ^} + + comptesteval "path=( $ZTST_srcdir:A )" + comptesteval 'typeset -H paths=HIDDEN' + comptest $': $path\t' +0:extra-verbose doesn't show special or hidden parameter values +>line: {: $path}{} +>DESCRIPTION:{parameter} +>NO:{path} +>NO:{paths} + + comptesteval 'zstyle -d ":completion:*:parameters" extra-verbose' + comptest $': $ba\t' +0:parameter values not shown without extra-verbose +>line: {: $ba}{} +>DESCRIPTION:{parameter} +>NO:{bar} +>NO:{bat} +>NO:{bay} + + comptesteval '_tst() { local disp=( {a..z} ); compadd -ld disp $disp[@]; comppostfuncs=( _pst ) }' + comptesteval '_pst() { local disp=( "<INSERT>$compstate[insert]</INSERT>" ); compadd -Qld disp $disp }' + comptesteval "zstyle ':completion:*' menu select=long-list" + # This test is sensitive to sorting differences across platforms + comptesteval 'export LC_ALL=C' + comptest $'tst \C-d' + comptesteval "export LC_ALL=${(q)ZSH_TEST_LANG}" +0: menu select=long-list starts menu selection for list widgets +>NO:{<INSERT>menu</INSERT>} +>NO:{a} +>NO:{b} +>NO:{c} +>NO:{d} +>NO:{e} +>NO:{f} +>NO:{g} +>NO:{h} +>NO:{i} +>NO:{j} +>NO:{k} +>NO:{l} +>NO:{m} +>NO:{n} +>NO:{o} +>NO:{p} +>NO:{q} +>NO:{r} +>NO:{s} +>NO:{t} +>NO:{u} +>NO:{v} +>NO:{w} +>NO:{x} +>NO:{y} +>NO:{z} + + %clean zmodload -ui zsh/zpty diff --git a/Test/Y02compmatch.ztst b/Test/Y02compmatch.ztst index e2f8e1a61..f28913867 100644 --- a/Test/Y02compmatch.ztst +++ b/Test/Y02compmatch.ztst @@ -11,9 +11,7 @@ # contains the compadd output. %prep - if [[ $OSTYPE = cygwin ]]; then - ZTST_unimplemented="the zsh/zpty module does not work on Cygwin" - elif ( zmodload zsh/zpty 2>/dev/null ); then + if ( zmodload zsh/zpty 2>/dev/null ); then . $ZTST_srcdir/comptest mkdir match.tmp cd match.tmp @@ -380,15 +378,26 @@ comp.graphics.rendering.misc comp.graphics.rendering.raytracing comp.graphics.rendering.renderman) test_code $example4_matcher example4_list - comptest $'tst c.s.u\t' -0:Documentation example using input c.s.u + comptest $'tst .s.u\t' +0:r:|.=* should complete .s.u +>line: {tst comp.sources.unix }{} +>COMPADD:{} +>INSERT_POSITIONS:{21} + + example4b_matcher='r:[^.]||.=* r:|=*' + test_code $example4b_matcher example4_list + comptest $'tst .s.u\t^[bc\t' +0f:r:[^.]||.=* should not complete .s.u, but should complete c.s.u +>line: {tst .s.u}{} +>COMPADD:{} +>INSERT_POSITIONS:{} >line: {tst comp.sources.unix }{} >COMPADD:{} >INSERT_POSITIONS:{21} test_code $example4_matcher example4_list - comptest $'tst c.g.\ta\t.\tp\ta\tg\t' -0:Documentation example using input c.g.\ta\t.\tp\ta\tg\t + comptest $'tst .g.\ta\t.\tp\ta\tg\t' +0:r:|.=* should complete .g. >line: {tst comp.graphics.}{} >COMPADD:{} >INSERT_POSITIONS:{18} @@ -426,9 +435,32 @@ >COMPADD:{} >INSERT_POSITIONS:{32} + test_code $example4b_matcher example4_list + comptest $'tst .g.\t^[bc\t' +0f:r:[^.]||.=* should not complete .g., but should complete c.g. +>line: {tst .g.}{} +>COMPADD:{} +>INSERT_POSITIONS:{} +>line: {tst comp.graphics.}{} +>COMPADD:{} +>INSERT_POSITIONS:{18} + test_code $example4_matcher example4_list - comptest $'tst c...pag\t' -0:Documentation example using input c...pag\t + comptest $'tst ...pag\t' +0:r:|.=* should complete ...pag +>line: {tst comp.graphics.apps.pagemaker }{} +>COMPADD:{} +>INSERT_POSITIONS:{32} + + test_code $example4b_matcher example4_list + comptest $'tst ...pag\t^[bc\t^Fg^F^Fa\t' +0f:r:[^.]||.=* should not complete ...pag or c...pag, but should complete c.g.a.p +>line: {tst ...pag}{} +>COMPADD:{} +>INSERT_POSITIONS:{} +>line: {tst c...pag}{} +>COMPADD:{} +>INSERT_POSITIONS:{} >line: {tst comp.graphics.apps.pagemaker }{} >COMPADD:{} >INSERT_POSITIONS:{32} @@ -446,8 +478,8 @@ example5_matcher='r:|[.,_-]=* r:|=*' example5_list=(veryverylongfile.c veryverylongheader.h) test_code $example5_matcher example5_list - comptest $'tst v.c\tv.h\t' -0:Documentation example using input v.c\t + comptest $'tst .c\t.h\t' +0:r:|[.,_-]=* should complete .c and .h >line: {tst veryverylongfile.c }{} >COMPADD:{} >INSERT_POSITIONS:{23} @@ -455,6 +487,23 @@ >COMPADD:{} >INSERT_POSITIONS:{44} + example5b_matcher='r:[^.,_-]||[.,_-]=* r:|=*' + test_code $example5b_matcher example5_list + comptest $'tst .c\t^[bv\t.h\t^[bv\t' +0f:r:[^.,_-]||[.,_-]=* should not complete .c or .h, but should complete v.c and v.h +>line: {tst .c}{} +>COMPADD:{} +>INSERT_POSITIONS:{} +>line: {tst veryverylongfile.c }{} +>COMPADD:{} +>INSERT_POSITIONS:{23} +>line: {tst veryverylongfile.c .h}{} +>COMPADD:{} +>INSERT_POSITIONS:{} +>line: {tst veryverylongfile.c veryverylongheader.h }{} +>COMPADD:{} +>INSERT_POSITIONS:{44} + example6_list=(LikeTHIS FooHoo 5foo123 5bar234) test_code 'r:|[A-Z0-9]=* r:|=*' example6_list @@ -495,16 +544,57 @@ example7_matcher="r:[^A-Z0-9]||[A-Z0-9]=** r:|=*" example7_list=($example6_list) test_code $example7_matcher example7_list - comptest $'tst H\t2\t' -0:Documentation example using "r:[^A-Z0-9]||[A-Z0-9]=** r:|=*" + comptest $'tst H\t^BF\to\t2\t^B5\tb\t' +0f:r:[^A-Z0-9]||[A-Z0-9]=** should not complete H, FH, 2 or 52, but should complete FoH and 5b2. +>line: {tst H}{} +>COMPADD:{} +>INSERT_POSITIONS:{} +>line: {tst F}{H} +>COMPADD:{} +>INSERT_POSITIONS:{} >line: {tst FooHoo }{} >COMPADD:{} >INSERT_POSITIONS:{10} +>line: {tst FooHoo 2}{} +>COMPADD:{} +>INSERT_POSITIONS:{} +>line: {tst FooHoo 5}{2} +>COMPADD:{} +>INSERT_POSITIONS:{} +>line: {tst FooHoo 5bar234 }{} +>COMPADD:{} +>INSERT_POSITIONS:{18} + + example7b_matcher="r:?||[A-Z0-9]=* r:|=*" + test_code $example7b_matcher example7_list + comptest $'tst H\t^BF2\t^B5\t' +0f:r:?||[A-Z0-9]=* r:|=* should not complete H or 2, but should complete FH and 52. +>line: {tst H}{} +>COMPADD:{} +>INSERT_POSITIONS:{} +>line: {tst FooHoo }{} +>COMPADD:{} +>INSERT_POSITIONS:{10} +>line: {tst FooHoo 2}{} +>COMPADD:{} +>INSERT_POSITIONS:{} >line: {tst FooHoo 5bar234 }{} >COMPADD:{} >INSERT_POSITIONS:{18} + example8_list=(passwd.byname) + test_code 'r:[^.]||.=* l:.||[^.]=*' + comptest $'tst .^B\tpass^Fname\t' +0f:r:[^.]||.=* and l:.||[^.]=* should work symmetrically. +>line: {tst }{.} +>COMPADD:{} +>INSERT_POSITIONS:{} +>line: {tst passwd.byname }{} +>COMPADD:{} +>INSERT_POSITIONS:{17} + + workers_7311_matcher="m:{a-z}={A-Z} r:|[.,_-]=* r:|=*" workers_7311_list=(Abc-Def-Ghij.txt Abc-def.ghi.jkl_mno.pqr.txt Abc_def_ghi_jkl_mno_pqr.txt) test_code $workers_7311_matcher workers_7311_list @@ -539,11 +629,11 @@ >COMPADD:{} >INSERT_POSITIONS:{5} - workers_11081_matcher='m:{a-zA-Z}={A-Za-z} r:|[.,_-]=* r:[^A-Z0-9]||[A-Z0-9]=* r:[A-Z0-9]||[^A-Z0-9]=* r:[^0-9]||[0-9]=* r:|=*' + workers_11081_matcher='m:{a-zA-Z}={A-Za-z} r:|[.,_-]=* r:|=*' workers_11081_list=(build.out build.out1 build.out2) test_code $workers_11081_matcher workers_11081_list comptest $'tst bui\t\t\t' -0:Bug from workers 11081 +0:Erratic completion bug from workers 11081: bui > build.out[] > build[.]out > build.out[] > build.out1[] > build.out2[] >line: {tst build.out}{} >COMPADD:{} >INSERT_POSITIONS:{13} @@ -580,7 +670,7 @@ workers_11586_list=(c00.abc c01.abc.def.00.0) test_code $workers_11586_matcher workers_11586_list comptest $'tst c00\t.\ta\t' -0:Bug from workers 11586 +0:Disappearing characters bug from workers 11586: c00\t -> c0[], c00\t -> c0.abc[], c00.\t -> c0.abc[] >line: {tst c00}{} >COMPADD:{} >INSERT_POSITIONS:{6} @@ -613,12 +703,12 @@ >COMPADD:{} >INSERT_POSITIONS:{22} - workers_13320_matcher='r:|[.,_-]=** r:[^0-9]||[0-9]=**' + workers_13320_matcher='r:|[.,_-]=**' workers_13320_list=(glibc-2.1.94-3.i386.rpm glibc-devel-2.1.94-3.i386.rpm) workers_13320_list=($workers_13320_list glibc-profile-2.1.94-3.i386.rpm) test_code $workers_13320_matcher workers_13320_list comptest $'tst glibc-2.1\t' -0:Test from workers 13320 +0:Incorrect cursor position bug from workers 13320: glibc-2.1\t -> glibc-2[.]1.94-3.i386.rpm >line: {tst glibc}{-2.1.94-3.i386.rpm} >COMPADD:{} >INSERT_POSITIONS:{9:27} @@ -643,11 +733,11 @@ >NO:{A.C} - workers_13345b_matcher='r:|[.,_-]=** r:[^0-9]||[0-9]=**' + workers_13345b_matcher='r:|[.,_-]=** r:|[0-9]=**' workers_13345b_list=(a-b_1_2_2 a-b_2_0.gz a-b_2_0.zip) test_code $workers_13345b_matcher workers_13345b_list comptest $'tst a-b_2\t' -0:Second test from workers 13345 +0:Disappearing character bug from workers 13345: a-b_2\t -> a-b__ >line: {tst a-b_2_}{} >COMPADD:{} >INSERT_POSITIONS:{8:10} diff --git a/Test/Y03arguments.ztst b/Test/Y03arguments.ztst index fa4589374..200c83e8c 100644 --- a/Test/Y03arguments.ztst +++ b/Test/Y03arguments.ztst @@ -1,9 +1,7 @@ # Tests for _arguments. %prep - if [[ $OSTYPE = cygwin ]]; then - ZTST_unimplemented="the zsh/zpty module does not work on Cygwin" - elif ( zmodload zsh/zpty 2>/dev/null ); then + if ( zmodload zsh/zpty 2>/dev/null ); then . $ZTST_srcdir/comptest mkdir comp.tmp cd comp.tmp @@ -104,6 +102,28 @@ >NO:{+o} >NO:{-o} + tst_arguments -s -{a,b,c} \!-{d,e,f} \!+{d,e,f} + comptest $'tst -ad\t\024\t\bef\t' +0:mix of + and - and exclusion of stacked options +>line: {tst -ad}{} +>DESCRIPTION:{option} +>NO:{-b} +>NO:{-c} +>line: {tst -da}{} +>DESCRIPTION:{option} +>NO:{-b} +>NO:{-c} +>line: {tst -def}{} +>DESCRIPTION:{option} +>NO:{-a} +>NO:{-b} +>NO:{-c} + + tst_arguments -s -{a,b,c} +{a,b,c} + comptest $'tst -a +b +c\t' +0:mix of + and - and exclusion of stacked options +>line: {tst -a +b +ca}{} + tst_arguments '-o:1:(a):2:(b)' comptest $'tst \t\t\t' 0:two option arguments @@ -231,9 +251,15 @@ 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 +0:opt_args with multiple arguments and quoting of colons and backslashes, part #1: default behaviour >line: {tst -a 1:x \2 1\:x:\\2 }{} + # Same as previous test, except with -0 and (qqqq) added + tst_arguments -0 : '-a:one: :two' ':descr:{compadd -Q - ${(qqqq)opt_args[-a]}}' + comptest $'tst -a 1:x \\2 \t' +0:opt_args with multiple arguments and quoting of colons and backslashes, part #2: NUL escaping +>line: {tst -a 1:x \2 $'1:x\0\\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 @@ -355,6 +381,12 @@ 0:allowed option before -- >line: {tst -x }{ --} + tst_arguments -S '1:one' '2:two' + comptest $'tst -- -- \t' +0:only first of duplicate -- is ignored +>line: {tst -- -- }{} +>DESCRIPTION:{two} + tst_arguments -x :word comptest $'tst word -\t' 0:option after a word @@ -386,6 +418,25 @@ 0:continue completion after rest argument that looks like an option >line: {tst -a -x more }{} + tst_arguments -A '-*' -a -b '*: :(words)' + comptest $'tst -x -\t' +0:word matching -A pattern doesn't exclude options +>line: {tst -x -}{} +>DESCRIPTION:{option} +>NO:{-a} +>NO:{-b} + + tst_arguments -A '-*' -a -b '1:word:(word)' + comptest $'tst -x \t' +0:unrecognised word matching -A pattern not treated as a rest argument +>line: {tst -x word }{} + + tst_arguments -A "-*" '(3)-a' '1:one' '2:two' '3:three' '4:four' '*:extra' + comptest $'tst x -a \t' +0:exclusion from option following word matching -A pattern should not apply +>line: {tst x -a }{} +>DESCRIPTION:{three} + tst_arguments '*-v' comptest $'tst -v -\t' 0:repeatable options @@ -474,6 +525,16 @@ >NO:{-b} >NO:{-v} + tst_arguments -a -b -c '(-a)1:one' '(-b)2:two' '(-c)*:extra' + comptest $'tst x y z\e6\C-b-\t' +0:exclude option from normal argument to the right of the cursor +>line: {tst -}{ x y z} +>DESCRIPTION:{one} +>DESCRIPTION:{option} +>NO:{-a} +>NO:{-b} +>NO:{-c} + 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 diff --git a/Test/Z01is-at-least.ztst b/Test/Z01is-at-least.ztst new file mode 100644 index 000000000..039b3e737 --- /dev/null +++ b/Test/Z01is-at-least.ztst @@ -0,0 +1,27 @@ +%prep + autoload -Uz is-at-least + +%test + + versions=( + 1 1.1 1.1.1 1.2 1.2.1 + 2 2.1 2.1.1 2.2 2.2.1 + 3 3.1 3.1.1 3.2 3.2.1 + ) + for (( i = 1; i <= $#versions; ++i)); do + for (( j = i+1; j <= $#versions; ++j)); do + is-at-least $versions[i] $versions[j] || echo "$versions[i] ≰ $versions[j]" + { ! is-at-least $versions[j] $versions[i] } || echo "$versions[j] ≤ $versions[i]" + done + done +0:is-at-least smoke test + + is-at-least 5.8.0.2 5.8 +1f:regression test: Two trailing zeroes are filled in +# TODO: When fixing this, extend the smoke test to cover this. + + is-at-least 5.8.0.2 5.8.0.0 +1:regression test: Two trailing zeroes are filled in +# TODO: Extend the smoke test to cover this. + +%clean diff --git a/Test/Z02zmathfunc.ztst b/Test/Z02zmathfunc.ztst new file mode 100644 index 000000000..2be770a13 --- /dev/null +++ b/Test/Z02zmathfunc.ztst @@ -0,0 +1,58 @@ +%prep + autoload -Uz zmathfunc && zmathfunc + +%test + + echo $(( min(42, 43) )) $(( max(42, 43) )) $(( sum(42, 43) )) + echo $(( min(42) )) $(( max(42) )) $(( sum(42) )) + echo $(( sum() )) +0:basic functionality test +>42 43 85 +>42 42 42 +>0 + + + (set -e; echo $(( min(0, 42) ))) + (set -e; echo $(( max(0, -42) ))) + (set -e; echo $(( sum(42, -42) ))) +0:regression test for ERR_EXIT +>0 +>0 +>0 + + echo $(( min(42, 43, 44) )) + echo $(( min(44, 42, 43) )) + echo $(( min(43, 44, 42) )) + # + echo $(( max(42, 43, 44) )) + echo $(( max(44, 42, 43) )) + echo $(( max(43, 44, 42) )) +0:min() and max() with three arguments +>42 +>42 +>42 +>44 +>44 +>44 + + echo $(( min() )) +1:error test for min() +?(eval):1: wrong number of arguments: min() + + echo $(( max() )) +1:error test for max() +?(eval):1: wrong number of arguments: max() + + zsh_math_func_min "foo bar" x y z +2d:check errors from an unsupported use-case (workers/48156) +# We expect one non-empty line of stderr, but don't care about the specific +# error message; thus, the expectation is a pattern (*), for stderr (?), which +# matches any non-empty string (?*). +# +# Sorry, Perl, but I had to give you a run for your money. +*??* +F:Calling zsh_math_func_min directly isn't a supported use-case, but if it +F:returns zero, something's probably wrong. + + +%clean diff --git a/Test/Z03run-help.ztst b/Test/Z03run-help.ztst new file mode 100644 index 000000000..ca8ba4d04 --- /dev/null +++ b/Test/Z03run-help.ztst @@ -0,0 +1,106 @@ +%prep + PAGER=cat + unalias run-help + autoload +X -Uz ${^fpath}/run-help*(N) + builtin() { + case "$1 $2" in + ( 'getln cmd_args' ) + cmd_args="$BUFFER_STACK" + ;; + ( 'print -z' ) + ;; + ( 'whence -va' ) + print -l "$3 is WHENCE:{$3}" + ;; + ( * ) + eval $@ + ;; + esac + } + man() { + [[ $1 == -w && -n $NO_SUBCMD_MANUALS ]] && + return 1 + print "MAN:{${(qq)@}}" + } + git svn () { + print "${(U)0}:{${(qq)@}}" + } + + +%test + + BUFFER_STACK='btrfs --help' + run-help btrfs +0:btrfs with option flag, no subcmd +>btrfs is WHENCE:{btrfs} +>MAN:{'btrfs'} + + BUFFER_STACK='btrfs subvolume snapshot –r /btrfs/SV1 /btrfs/SV1-rosnap' + run-help btrfs +0:btrfs with subcmd +>btrfs is WHENCE:{btrfs} +>MAN:{'btrfs-subvolume'} + + BUFFER_STACK="sudo $BUFFER_STACK" + run-help btrfs +0:sudo btrfs with subcmd +>btrfs is WHENCE:{btrfs} +>MAN:{'btrfs-subvolume'} + + BUFFER_STACK='ip addr add 192.168.50.5 dev eth1' + run-help ip +0:ip with subcmd +>ip is WHENCE:{ip} +>MAN:{'ip-address'} + + NO_SUBCMD_MANUALS=1 + run-help ip + unset NO_SUBCMD_MANUALS +0:ip with subcmd, but no subcmd manuals +>ip is WHENCE:{ip} +>MAN:{'ip'} + + BUFFER_STACK='ip -s -s link ls up' + run-help ip +0:ip with options and subcmd +>ip is WHENCE:{ip} +>MAN:{'ip-link'} + + BUFFER_STACK="sudo $BUFFER_STACK" + run-help ip +0:sudo ip with options and subcmd +>ip is WHENCE:{ip} +>MAN:{'ip-link'} + + BUFFER_STACK='svn -vq' + run-help svn +0:svn with options +>svn is WHENCE:{svn} +>SVN:{'help'} + + BUFFER_STACK+=' commit -m "log messages"' + run-help svn +0:svn with options and subcmd +>svn is WHENCE:{svn} +>SVN:{'help' 'commit'} + + BUFFER_STACK='git --exec-path' + run-help git +0:git with option +>git is WHENCE:{git} +>GIT:{'help' 'git'} + + BUFFER_STACK='git -C $PWD/.. difftool --no-prompt --tool opendiff --dir-diff' + run-help git +0:git with option, file & subcmd +>git is WHENCE:{git} +>GIT:{'help' 'difftool'} + + BUFFER_STACK='git -c http.proxy=someproxy clone https://github.com/user/repo.git' + run-help git +0:git with option, assignment & subcmd +>git is WHENCE:{git} +>GIT:{'help' 'clone'} + + +%clean diff --git a/Test/comptest b/Test/comptest index 166d0b404..79c69979a 100644 --- a/Test/comptest +++ b/Test/comptest @@ -40,6 +40,7 @@ KEYTIMEOUT=1 setopt zle autoload -U compinit compinit -u +zstyle ":completion:*" completer _expand _complete _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> @@ -50,9 +51,9 @@ zstyle ":completion:*:options" verbose yes zstyle ":completion:*:values" verbose yes setopt noalwayslastprompt listrowsfirst completeinword zmodload zsh/complist -expand-or-complete-with-report () { - print -lr "<WIDGET><expand-or-complete>" - zle expand-or-complete +complete-word-with-report () { + print -lr "<WIDGET><complete-word>" + zle complete-word print -lr - "<LBUFFER>$LBUFFER</LBUFFER>" "<RBUFFER>$RBUFFER</RBUFFER>" zle clear-screen zle -R @@ -80,11 +81,11 @@ zle-finish () { (( $+mark )) && print -lr "MARK: $mark" zle accept-line } -zle -N expand-or-complete-with-report +zle -N complete-word-with-report zle -N list-choices-with-report zle -N comp-finish zle -N zle-finish -bindkey "^I" expand-or-complete-with-report +bindkey "^I" complete-word-with-report bindkey "^D" list-choices-with-report bindkey "^Z" comp-finish bindkey "^X" zle-finish @@ -112,17 +113,26 @@ zpty_run() { } comptesteval () { - local tmp=/tmp/comptest.$$ + { + # Avoid symlink attacks on the predictable filename + # TODO: either use =(:) or create this file in the tests' workdir + local tmp=/tmp/comptest.$$ + () { + setopt localoptions NO_CLOBBER ERR_EXIT + print -lr - "$@" > $tmp + } "$@" - print -lr - "$@" > $tmp - # zpty_flush Before comptesteval - zpty -w zsh ". $tmp" - zpty -r -m zsh log_eval "*<PROMPT>*" || { - print "prompt hasn't appeared." - return 1 - } - zpty_flush After comptesteval - rm $tmp + # zpty_flush Before comptesteval + zpty -w zsh ". ${(q)tmp}" + zpty -r -m zsh log_eval "*<PROMPT>*" || { + print "prompt hasn't appeared." + return 1 + } + zpty_flush After comptesteval + : Suppress error from zpty_flush + } always { + rm $tmp + } } comptest () { diff --git a/Test/runtests.zsh b/Test/runtests.zsh index 562234d91..b66d579b6 100644 --- a/Test/runtests.zsh +++ b/Test/runtests.zsh @@ -7,7 +7,7 @@ emulate zsh # protect from catastrophic failure of an individual test. # We could probably do that with subshells instead. -integer success failure skipped retval +integer success=0 failure=0 skipped=0 retval for file in "${(f)ZTST_testlist}"; do $ZTST_exe +Z -f $ZTST_srcdir/ztst.zsh $file retval=$? diff --git a/Test/ztst.zsh b/Test/ztst.zsh index 375efd16c..89fe69b5b 100755 --- a/Test/ztst.zsh +++ b/Test/ztst.zsh @@ -25,6 +25,7 @@ emulate -R zsh # Ensure the locale does not screw up sorting. Don't supply a locale # unless there's one set, to minimise problems. [[ -n $LC_ALL ]] && LC_ALL=C +[[ -n $LC_CTYPE ]] && LC_CTYPE=C [[ -n $LC_COLLATE ]] && LC_COLLATE=C [[ -n $LC_NUMERIC ]] && LC_NUMERIC=C [[ -n $LC_MESSAGES ]] && LC_MESSAGES=C @@ -36,8 +37,6 @@ typeset +x WORDCHARS # Set the module load path to correspond to this build of zsh. # This Modules directory should have been created by "make check". [[ -d Modules/zsh ]] && module_path=( $PWD/Modules ) -# Allow this to be passed down. -export MODULE_PATH # We need to be able to save and restore the options used in the test. # We use the $options variable of the parameter module for this. @@ -60,7 +59,7 @@ ZTST_mainopts=(${(kv)options}) ZTST_testdir=$PWD ZTST_testname=$1 -integer ZTST_testfailed +integer ZTST_testfailed=0 # This is POSIX nonsense. Because of the vague feeling someone, somewhere # may one day need to examine the arguments of "tail" using a standard @@ -146,6 +145,19 @@ $ZTST_failmsg" ZTST_testfailed=1 return 1 } +ZTST_testxpassed() { + print -r "Test $ZTST_testname was expected to fail, but passed." + if [[ -n $ZTST_message ]]; then + print -r "Was testing: $ZTST_message" + fi + print -r "$ZTST_testname: test XPassed." + if [[ -n $ZTST_failmsg ]]; then + print -r "The following may (or may not) help identifying the cause: +$ZTST_failmsg" + fi + ZTST_testfailed=1 + return 1 +} # Print messages if $ZTST_verbose is non-empty ZTST_verbose() { @@ -520,7 +532,7 @@ $ZTST_code" return 1 fi if (( expected_to_fail )); then - ZTST_testfailed "test was expected to fail, but passed." + ZTST_testxpassed return 1 fi fi |