diff options
Diffstat (limited to 'dotfiles/system/.zsh/modules/Test')
48 files changed, 14852 insertions, 0 deletions
diff --git a/dotfiles/system/.zsh/modules/Test/.cvsignore b/dotfiles/system/.zsh/modules/Test/.cvsignore new file mode 100644 index 0000000..855d729 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/.cvsignore @@ -0,0 +1,3 @@ +Makefile +*.tmp +*.swp diff --git a/dotfiles/system/.zsh/modules/Test/.distfiles b/dotfiles/system/.zsh/modules/Test/.distfiles new file mode 100644 index 0000000..f03668b --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/.distfiles @@ -0,0 +1,2 @@ +DISTFILES_SRC=' +' diff --git a/dotfiles/system/.zsh/modules/Test/A01grammar.ztst b/dotfiles/system/.zsh/modules/Test/A01grammar.ztst new file mode 100644 index 0000000..e4b6870 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/A01grammar.ztst @@ -0,0 +1,790 @@ +# +# This file contains tests corresponding to the `Shell Grammar' texinfo node. +# + +%prep + + mkdir basic.tmp && cd basic.tmp + + touch foo bar + echo "'" >unmatched_quote.txt + +%test +# +# Tests for `Simple Commands and Pipelines' +# + + # Test skipping early to ensure we run the remainder... + if [[ -n $ZTST_test_skip ]]; then + ZTST_skip="Test system verification for skipping" + else + print "This is standard output" + print "This is standard error" >&2 + false + fi +1:Test skipping if ZTST_test_skip is set +>This is standard output +?This is standard error + + echo foo | cat | sed 's/foo/bar/' +0:Basic pipeline handling +>bar + + false | true +0:Exit status of pipeline with builtins (true) + + true | false +1:Exit status of pipeline with builtins (false) + + false + $nonexistent_variable +0:Executing command that evaluates to empty resets status + + false + sleep 1 & + print $? + # a tidy test is a happy test + wait $! +0:Starting background command resets status +>0 + + false + . /dev/null +0:Sourcing empty file resets status + + fn() { local foo; read foo; print $foo; } + coproc fn + print -p coproc test output + read -p bar + print $bar +0:Basic coprocess handling +>coproc test output + + true | false && print true || print false +0:Basic sublist (i) +>false + + false | true && print true || print false +0:Basic sublist (ii) +>true + + (cd /NonExistentDirectory >&/dev/null) || print false +0:Basic subshell list with error +>false + + { cd /NonExistentDirectory >&/dev/null } || print false +0:Basic current shell list with error +>false + +# +# Tests for `Precommand Modifiers' +# + - $ZTST_testdir/../Src/zsh -fc "[[ \$0 = \"-$ZTST_testdir/../Src/zsh\" ]]" +0:`-' precommand modifier + + echo f* + noglob echo f* +0:`noglob' precommand modifier +>foo +>f* + + (exec /bin/sh; echo bar) +0:`exec' precommand modifier + + (exec -l $ZTST_testdir/../Src/zsh -fc 'echo $0' | sed 's%/.*/%%' ) +0:`exec' with -l option +>-zsh + + (exec -a /bin/SPLATTER /bin/sh -c 'echo $0') +0:`exec' with -a option +>/bin/SPLATTER + + (exec -a/bin/SPLOOSH /bin/sh -c 'echo $0') +0:`exec' with -a option, no space +>/bin/SPLOOSH + + (export FOO=bar; exec -c /bin/sh -c 'echo x${FOO}x') +0:`exec' with -c option +>xx + + cat() { echo Function cat executed; } + command cat && unfunction cat +0:`command' precommand modifier +<External command cat executed +>External command cat executed + + command -pv cat + command -pv echo + command -p -V cat + command -p -V -- echo +0:command -p in combination +*>*/cat +>echo +>cat is /*/cat +>echo is a shell builtin + + cd() { echo Not cd at all; } + builtin cd . && unfunction cd +0:`builtin' precommand modifier + +# +# Tests for `Complex Commands' +# + + if true; then + print true-1 + elif true; then + print true-2 + else + print false + fi +0:`if ...' (i) +>true-1 + + if false; then + print true-1 + elif true; then + print true-2 + else + print false + fi +0:`if ...' (ii) +>true-2 + + if false; then + print true-1 + elif false; then + print true-2 + else + print false + fi +0:`if ...' (iii) +>false + + if true; + : + fi +1d:`if ...' (iv) +?(eval):3: parse error near `fi' + + for name in word to term; do + print $name + done +0:`for' loop +>word +>to +>term + + for name + in word to term; do + print $name + done +0:`for' loop with newline before in keyword +>word +>to +>term + + for (( name = 0; name < 3; name++ )); do + print $name + done +0:arithmetic `for' loop +>0 +>1 +>2 + + for (( $(true); ; )); do break; done + for (( ; $(true); )); do break; done + for (( ; ; $(true) )); do break; done + for (( ; $((1)); )); do break; done +0:regression test, nested cmdsubst in arithmetic `for' loop + + for keyvar valvar in key1 val1 key2 val2; do + print key=$keyvar val=$valvar + done +0:enhanced `for' syntax with two loop variables +>key=key1 val=val1 +>key=key2 val=val2 + + for keyvar valvar stuffvar in keyA valA stuffA keyB valB stuffB; do + print key=$keyvar val=$valvar stuff=$stuffvar + done +0:enhanced `for' syntax with three loop variables +>key=keyA val=valA stuff=stuffA +>key=keyB val=valB stuff=stuffB + + for in in in in in stop; do + print in=$in + done +0:compatibility of enhanced `for' syntax with standard syntax +>in=in +>in=in +>in=in +>in=stop + + name=0 + while (( name < 3 )); do + print $name + (( name++ )) + done +0:`while' loop +>0 +>1 +>2 + + name=0 + until (( name == 3 )); do + print $name + (( name++ )) + done +0:`until' loop +>0 +>1 +>2 + + repeat 3 do + echo over and over + done +0:`repeat' loop +>over and over +>over and over +>over and over + + word=Trinity + case $word in + Michaelmas) print 0 + ;; + Hilary) print 1 + ;; + Trinity) print 2 + ;; + *) print 3 + ;; + esac +0:`case', old syntax +>2 + + word=Trinity + case $word in + (Michaelmas) print 0 + ;; + (Hilary) print 1 + ;; + (Trinity) print 2 + ;; + (*) print 3 + ;; + esac +0:`case', new syntax +>2 + + word=Hilary + case $word in + (Michaelmas) print 0 + ;; + (Hilary) print 1 + ;& + (Trinity) print 2 + ;& + (*) print 3 + ;; + esac +0:`case', new syntax, cascaded +>1 +>2 +>3 + + case whatever in + (*) print yeah, right ;& + esac + print but well +0:'case', redundant final ";&" +>yeah, right +>but well + +## Select now reads from stdin if the shell is not interactive. +## Its own output goes to stderr. + (COLUMNS=80 LINES=3 + PS3="input> " + select name in one two three; do + print $name + done) +0:`select' loop +<2 +?1) one 2) two 3) three +?input> input> +>two + + function name1 name2 () { print This is $0; } + name2 + name1 name2() { print This is still $0; } + name2 +0:`function' keyword +>This is name2 +>This is still name2 + + (time cat) >&/dev/null +0:`time' keyword (status only) + + if [[ -f foo && -d . && -n $ZTST_testdir ]]; then + true + else + false + fi +0:basic [[ ... ]] test + +# +# Current shell execution with try/always form. +# We put those with errors in subshells so that any unhandled error doesn't +# propagate. +# + + { + print The try block. + } always { + print The always block. + } + print After the always block. +0:Basic `always' syntax +>The try block. +>The always block. +>After the always block. + + ({ + print Position one. + print ${*this is an error*} + print Position two. + } always { + if (( TRY_BLOCK_ERROR )); then + print An error occurred. + else + print No error occurred. + fi + } + print Position three) +1:Always block with error not reset +>Position one. +>An error occurred. +?(eval):3: bad substitution + + ({ + print Stelle eins. + print ${*voici une erreur} + print Posizione due. + } always { + if (( TRY_BLOCK_ERROR )); then + print Erratum factum est. Retro ponetur. + (( TRY_BLOCK_ERROR = 0 )) + else + print unray touay foay anguageslay + fi + } + print Status after always block is $?.) +0:Always block with error reset +>Stelle eins. +>Erratum factum est. Retro ponetur. +>Status after always block is 1. +?(eval):3: bad substitution + + fn() { { return } always { echo always 1 }; echo not executed } + fn + fn() { { echo try 2 } always { return }; echo not executed } + fn +0:Always block interaction with return +>always 1 +>try 2 + +# Outputting of structures from the wordcode is distinctly non-trivial, +# we probably ought to have more like the following... + fn1() { { echo foo; } } + fn2() { { echo foo; } always { echo bar; } } + fn3() { ( echo foo; ) } + functions fn1 fn2 fn3 +0:Output of syntactic structures with and without always blocks +>fn1 () { +> { +> echo foo +> } +>} +>fn2 () { +> { +> echo foo +> } always { +> echo bar +> } +>} +>fn3 () { +> ( +> echo foo +> ) +>} + + +# +# Tests for `Alternate Forms For Complex Commands' +# + + if (true) { print true-1 } elif (true) { print true-2 } else { print false } + if (false) { print true-1 } elif (true) { print true-2 } else { print false } + if (false) { print true-1 } elif (false) { print true-2 } else { print false } +0:Alternate `if' with braces +>true-1 +>true-2 +>false + + if { true } print true + if { false } print false +0:Short form of `if' +>true + + eval "if" +1:Short form of `if' can't be too short +?(eval):1: parse error near `if' + + for name ( word1 word2 word3 ) print $name +0:Form of `for' with parentheses. +>word1 +>word2 +>word3 + + for name in alpha beta gamma; print $name +0:Short form of `for' +>alpha +>beta +>gamma + + for (( val = 2; val < 10; val *= val )) print $val +0:Short arithmetic `for' +>2 +>4 + + foreach name ( verbiage words periphrasis ) + print $name + end +0:Csh-like `for' +>verbiage +>words +>periphrasis + +# see comment with braces used in if loops + val=0; + while (( val < 2 )) { print $((val++)); } +0:Alternative `while' +>0 +>1 + + val=2; + until (( val == 0 )) { print $((val--)); } +0:Alternative `until' +>2 +>1 + + repeat 3 print Hip hip hooray +0:Short `repeat' +>Hip hip hooray +>Hip hip hooray +>Hip hip hooray + + case bravo { + (alpha) print schmalpha + ;; + (bravo) print schmavo + ;; + (charlie) print schmarlie + ;; + } +0:`case' with braces +>schmavo + + for word in artichoke bladderwort chrysanthemum Zanzibar + case $word in + (*der*) print $word contains the forbidden incantation der + ;; + (a*) print $word begins with a + ;& + ([[:upper:]]*) print $word either begins with a or an upper case letter + ;| + ([[:lower:]]*) print $word begins with a lower case letter + ;| + (*e*) print $word contains an e + ;; + esac +0:`case' with mixed ;& and ;| +>artichoke begins with a +>artichoke either begins with a or an upper case letter +>artichoke begins with a lower case letter +>artichoke contains an e +>bladderwort contains the forbidden incantation der +>chrysanthemum begins with a lower case letter +>chrysanthemum contains an e +>Zanzibar either begins with a or an upper case letter + + print -u $ZTST_fd 'This test hangs the shell when it fails...' + name=0 +# The number 4375 here is chosen to produce more than 16384 bytes of output + while (( name < 4375 )); do + print -n $name + (( name++ )) + done < /dev/null | { read name; print done } +0:Bug regression: `while' loop with redirection and pipeline +>done + +# This used to be buggy and print X at the end of each iteration. + for f in 1 2 3 4; do + print $f || break + done && print X +0:Handling of ||'s and &&'s with a for loop in between +>1 +>2 +>3 +>4 +>X + +# Same bug for &&, used to print `no' at the end of each iteration + for f in 1 2 3 4; do + false && print strange + done || print no +0:Handling of &&'s and ||'s with a for loop in between +>no + + $ZTST_testdir/../Src/zsh -f unmatched_quote.txt +1:Parse error with file causes non-zero exit status +?unmatched_quote.txt:2: unmatched ' + + $ZTST_testdir/../Src/zsh -f <unmatched_quote.txt +1:Parse error on standard input causes non-zero exit status +?zsh: unmatched ' + + $ZTST_testdir/../Src/zsh -f -c "'" +1:Parse error on inline command causes non-zero exit status +?zsh:1: unmatched ' + + $ZTST_testdir/../Src/zsh -f NonExistentScript +127q:Non-existent script causes exit status 127 +?$ZTST_testdir/../Src/zsh: can't open input file: NonExistentScript + + (setopt nonomatch + # use this to get stuff at start of line + contents=$'# comment \'\necho value #with " stuff\n# comment\n#comment + echo not#comment\n' + eval 'VAR=$('"$contents"')' + print -l $VAR) +0:comments within $(...) +>value +>not#comment + + . ./nonexistent +127: Attempt to "." non-existent file. +?(eval):.:1: no such file or directory: ./nonexistent + + echo '[[' >bad_syntax + . ./bad_syntax +126: Attempt to "." file with bad syntax. +?./bad_syntax:2: parse error near `\n' +# ` + + echo 'false' >dot_false + . ./dot_false + print $? + echo 'true' >dot_true + . ./dot_true + print $? +0:Last status of successfully executed "." file is retained +>1 +>0 + + echo 'echo $?' >dot_status + false + . ./dot_status +0:"." file sees status from previous command +>1 + + mkdir test_path_script + print "#!/bin/sh\necho Found the script." >test_path_script/myscript + chmod u+x test_path_script/myscript + path=($PWD/test_path_script $path) + export PATH + $ZTST_testdir/../Src/zsh -f -o pathscript myscript +0:PATHSCRIPT option +>Found the script. + + $ZTST_testdir/../Src/zsh -f myscript +127q:PATHSCRIPT option not used. +?$ZTST_testdir/../Src/zsh: can't open input file: myscript +# ' + + $ZTST_testdir/../Src/zsh -fc 'echo $0; echo $1' myargzero myargone +0:$0 is traditionally if bizarrely set to the first argument with -c +>myargzero +>myargone + + (setopt shglob + eval ' + if ! (echo success1); then echo failure1; fi + if !(echo success2); then echo failure2; fi + print -l one two | while(read foo)do(print read it)done + ') +0:Parentheses in shglob +>success1 +>success2 +>read it +>read it + + ( + mywrap() { echo BEGIN; true; echo END } + mytest() { { exit 3 } always { mywrap }; print Exited before this } + mytest + print Exited before this, too + ) +3:Exit and always block with functions: simple +>BEGIN +>END + + ( + mytrue() { echo mytrue; return 0 } + mywrap() { echo BEGIN; mytrue; echo END } + mytest() { { exit 4 } always { mywrap }; print Exited before this } + mytest + print Exited before this, too + ) +4:Exit and always block with functions: nested +>BEGIN +>mytrue +>END + + (emulate sh -c ' + fn() { + case $1 in + ( one | two | three ) + print Matched $1 + ;; + ( fo* | fi* | si* ) + print Pattern matched $1 + ;; + ( []x | a[b]* ) + print Character class matched $1 + ;; + esac + } + ' + which fn + fn one + fn two + fn three + fn four + fn five + fn six + fn abecedinarian + fn xylophone) +0: case word handling in sh emulation (SH_GLOB parentheses) +>fn () { +> case $1 in +> (one | two | three) print Matched $1 ;; +> (fo* | fi* | si*) print Pattern matched $1 ;; +> ([]x | a[b]*) print Character class matched $1 ;; +> esac +>} +>Matched one +>Matched two +>Matched three +>Pattern matched four +>Pattern matched five +>Pattern matched six +>Character class matched abecedinarian + + case grumph in + ( no | (grumph) ) + print 1 OK + ;; + esac + case snruf in + ( fleer | (|snr(|[au]f)) ) + print 2 OK + ;; + esac +0: case patterns within words +>1 OK +>2 OK + + case horrible in + ([a-m])(|[n-z])rr(|ib(um|le|ah))) + print It worked + ;; + esac + case "a string with separate words" in + (*with separate*)) + print That worked, too + ;; + esac +0:Unbalanced parentheses and spaces with zsh pattern +>It worked +>That worked, too + + case horrible in + (([a-m])(|[n-z])rr(|ib(um|le|ah))) + print It worked + ;; + esac + case "a string with separate words" in + (*with separate*) + print That worked, too + ;; + esac +0:Balanced parentheses and spaces with zsh pattern +>It worked +>That worked, too + + fn() { + typeset ac_file="the else branch" + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) break;; + *) + ;; + esac + print Stuff here + } + which fn + fn +0:Long case with parsed alternatives turned back into text +>fn () { +> typeset ac_file="the else branch" +> case $ac_file in +> (*.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj) ;; +> (*.*) break ;; +> (*) ;; +> esac +> print Stuff here +>} +>Stuff here + + (exit 37) + case $? in + (37) echo $? + ;; + esac +0:case retains exit status for execution of cases +>37 + + false + case stuff in + (nomatch) foo + ;; + esac + echo $? +0:case sets exit status to zero if no patterns are matched +>0 + + case match in + (match) true; false; (exit 37) + ;; + esac + echo $? +0:case keeps exit status of last command executed in compound-list +>37 + + x=1 + x=2 | echo $x + echo $x +0:Assignment-only current shell commands in LHS of pipelin +>1 +>1 diff --git a/dotfiles/system/.zsh/modules/Test/A02alias.ztst b/dotfiles/system/.zsh/modules/Test/A02alias.ztst new file mode 100644 index 0000000..e68e93e --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/A02alias.ztst @@ -0,0 +1,139 @@ +# To get the "command not found" message when aliasing is suppressed +# we need, er, a command that isn't found. +# The other aliases are only ever used as aliases. + +%prep + alias ThisCommandDefinitelyDoesNotExist=echo + + alias -g bar=echo + + alias '\bar=echo' + +%test + ThisCommandDefinitelyDoesNotExist ThisCommandDefinitelyDoesNotExist +0:Basic aliasing +>ThisCommandDefinitelyDoesNotExist + + bar bar +0:Global aliasing +>echo + + \ThisCommandDefinitelyDoesNotExist ThisCommandDefinitelyDoesNotExist +127:Not aliasing +?(eval):1: command not found: ThisCommandDefinitelyDoesNotExist + + \bar \bar +0:Aliasing with a backslash +>bar + + (alias '!=echo This command has the argument' + eval 'print Without + ! true' + setopt posixaliases + eval 'print With + ! true') +1:POSIX_ALIASES option +>Without +>This command has the argument true +>With + + print -u $ZTST_fd 'This test hangs the shell when it fails...' + alias cat='LC_ALL=C cat' + cat <(echo foo | cat) +0:Alias expansion works at the end of parsed strings +>foo + + alias -g '&&=(){ return $?; } && ' + alias not_the_print_command=print + eval 'print This is output + && print And so is this + && { print And this too; false; } + && print But not this + && print Nor this + true + && not_the_print_command And aliases are expanded' +0:We can now alias special tokens. Woo hoo. +>This is output +>And so is this +>And this too +>And aliases are expanded + + $ZTST_testdir/../Src/zsh -fis <<<' + unsetopt PROMPT_SP + PROMPT="" PS2="" PS3="" PS4="" RPS1="" RPS2="" + exec 2>&1 + alias \{=echo + { begin + {end + fc -l -2' 2>/dev/null +0:Aliasing reserved tokens +>begin +>end +*>*5*{ begin +*>*6*{end + + $ZTST_testdir/../Src/zsh -fis <<<' + unsetopt PROMPT_SP + PROMPT="" PS2="" PS3="" PS4="" RPS1="" RPS2="" + exec 2>&1 + alias -g S=\" + echo S a string S " + fc -l -1' 2>/dev/null +0:Global aliasing quotes +> a string S +*>*5*echo S a string S " +# " +# Note there is a trailing space on the "> a string S " line + + ( + unalias -a + alias + ) +0:unalias -a + + alias -s foo=print + type bar.foo; type -w bar.foo + unalias -as +0:unalias -as +>foo is a suffix alias for print +>foo: suffix alias + + aliases[x=y]=z + alias -L | grep x=y + echo $pipestatus[1] +0:printing invalid aliases warns +>0 +?(eval):2: invalid alias 'x=y' encountered while printing aliases +# Currently, 'alias -L' returns 0 in this case. Perhaps it should return 1. + + alias -s mysuff='print -r "You said it.";' + eval 'thingummy.mysuff' +127:No endless loop with suffix alias in command position +>You said it. +?(eval):1: command not found: thingummy.mysuff + + alias +x; alias -z +1:error message has the correct sign +?(eval):alias:1: bad option: +x +?(eval):alias:1: bad option: -z + + # Usual issue that aliases aren't expanded until we + # trigger a new parse... + (alias badalias=notacommand + eval 'badalias() { print does not work; }') +1:ALIAS_FUNC_DEF off by default. +?(eval):1: defining function based on alias `badalias' +?(eval):1: parse error near `()' + + (alias goodalias=isafunc + setopt ALIAS_FUNC_DEF + eval 'goodalias() { print does now work; }' + isafunc) +0:ALIAS_FUNC_DEF causes the icky behaviour to be avaliable +>does now work + + (alias thisisokthough='thisworks() { print That worked; }' + eval thisisokthough + thisworks) +0:NO_ALIAS_FUNC_DEF works if the alias is a complete definition +>That worked diff --git a/dotfiles/system/.zsh/modules/Test/A03quoting.ztst b/dotfiles/system/.zsh/modules/Test/A03quoting.ztst new file mode 100644 index 0000000..da3ce35 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/A03quoting.ztst @@ -0,0 +1,80 @@ +%test + print 'single quotes' "double quotes" `echo backquotes` +0:Simple use of quotes +>single quotes double quotes backquotes + + foo=text + print -r '$foo\\\' "$foo\$foo\\\"\``echo bar`\`\"" `print -r $foo\\\`` +0:Quoting inside quotes +>$foo\\\ text$foo\"`bar`" text` + + print -r $'\'ut queant laxis\'\n"resonare fibris"' +0:$'-style quotes +>'ut queant laxis' +>"resonare fibris" + + print -r $'\'a \\\' is \'a backslash\' is \'a \\\'' +0:$'-style quotes with backslashed backslashes +>'a \' is 'a backslash' is 'a \' + + chars=$(print -r $'BS\\MBS\M-\\') + for (( i = 1; i <= $#chars; i++ )); do + char=$chars[$i] + print $(( [#16] #char )) + done +0:$'-style quote with metafied backslash +>16#42 +>16#53 +>16#5C +>16#4D +>16#42 +>16#53 +>16#DC + + print -r '''' + setopt rcquotes +# We need to set rcquotes here for the next example since it is +# needed while parsing. +0:No RC_QUOTES with single quotes +> + + print -r '''' + unsetopt rcquotes +0:Yes RC_QUOTES with single quotes +>' +# ' Deconfuse Emacs quoting rules + + print '<\u0041>' + printf '%s\n' $'<\u0042>' + print '<\u0043>' + printf '%s\n' $'<\u0044>' +0:\u in both print and printf +><A> +><B> +><C> +><D> + + null1="$(print -r a$'b\0c'd)" + null2="$(setopt posixstrings; print -r a$'b\0c'd)" + for string in $null1 $null2; do + print ":" + for (( i = 1; i <= $#string; i++ )); do + char=$string[$i] + print $(( [#16] #char )) + done + done +0:Embedded null characters in $'...' strings. +>: +>16#61 +>16#62 +>16#0 +>16#63 +>16#64 +>: +>16#61 +>16#62 +>16#64 + + () { print $# } '' "" $'' +0:$'' should not be elided, in common with other empty quotes +>3 diff --git a/dotfiles/system/.zsh/modules/Test/A04redirect.ztst b/dotfiles/system/.zsh/modules/Test/A04redirect.ztst new file mode 100644 index 0000000..d7fe22f --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/A04redirect.ztst @@ -0,0 +1,588 @@ +# Tests corresponding to the `Redirection' texinfo node. + +%prep + mkdir redir.tmp && cd redir.tmp + + myfd=99 + (echo >&$myfd) 2>msg + bad_fd_msg="${$(<msg)##*:}" + +%test + + print 'This is file redir' >redir && cat redir +0:'>' and '<' redirection +>This is file redir + + rm -f redir + print 'This is still file redir' <>redir >&0 && cat <>redir +0:'<>' redirection +>This is still file redir + + rm -f redir + print 'With a bar' >|redir && cat redir +0:'>|' redirection +>With a bar + + rm -f redir + print 'With a bang' >!redir && cat redir +0:'>!' redirection +>With a bang + + rm -f redir + print 'Line 1' >>redir && print 'Line 2' >>redir && cat redir +0:'>>' redirection +>Line 1 +>Line 2 + + rm -f redir + print 'Line a' >>|redir && print 'Line b' >>!redir +0:'>>|' and '>>!' redirection + + foo=bar + cat <<' HERE' + $foo + HERE + eval "$(print 'cat <<HERE\n$foo\nHERE')" +0:Here-documents +> $foo +>bar + + cat <<-HERE +# note tabs at the start of the following lines + $foo$foo + HERE +0:Here-documents stripping tabs +>barbar + + cat <<-$'$HERE '`$(THERE) `'$((AND)) '"\EVERYWHERE" # +# tabs again. sorry about the max miller. + Here's a funny thing. Here is a funny thing. + I went home last night. There's a funny thing. + Man walks into a $foo. Ouch, it's an iron $foo. + $HERE `$(THERE) `$((AND)) \EVERYWHERE +0:Here-documents don't perform shell expansion on the initial word +>Here's a funny thing. Here is a funny thing. +>I went home last night. There's a funny thing. +>Man walks into a $foo. Ouch, it's an iron $foo. + + cat <<-$'\x45\x4e\x44\t\x44\x4f\x43' +# tabs again + This message is unfathomable. + END DOC +0:Here-documents do perform $'...' expansion on the initial word +>This message is unfathomable. + + cat <<<"This is a line with a $foo in it" +0:'<<<' redirection +>This is a line with a bar in it + + cat <<<$'a\nb\nc' +0:here-strings with $'...' quoting +>a +>b +>c + +# The following tests check that output of parsed here-documents works. +# This isn't completely trivial because we convert the here-documents +# internally to here-strings. So we check again that we can output +# the reevaluated here-strings correctly. Hence there are three slightly +# different stages. We don't care how the output actually looks, so +# we don't test that. + heretest() { + print First line + cat <<-HERE + $foo$foo met celeste 'but with extra' "stuff to test quoting" + HERE + print Last line + } + heretest + eval "$(functions heretest)" + heretest + eval "$(functions heretest)" + heretest +0:Re-evaluation of function output with here document, unquoted +>First line +>barbar met celeste 'but with extra' "stuff to test quoting" +>Last line +>First line +>barbar met celeste 'but with extra' "stuff to test quoting" +>Last line +>First line +>barbar met celeste 'but with extra' "stuff to test quoting" +>Last line + + heretest() { + print First line + cat <<' HERE' + $foo$foo met celeste 'but with extra' "stuff to test quoting" + HERE + print Last line + } + heretest + eval "$(functions heretest)" + heretest + eval "$(functions heretest)" + heretest +0:Re-evaluation of function output with here document, quoted +>First line +> $foo$foo met celeste 'but with extra' "stuff to test quoting" +>Last line +>First line +> $foo$foo met celeste 'but with extra' "stuff to test quoting" +>Last line +>First line +> $foo$foo met celeste 'but with extra' "stuff to test quoting" +>Last line + + read -r line <<' HERE' + HERE +1:No input, not even newline, from empty here document. + + # + # exec tests: perform these in subshells so if they fail the + # shell won't exit. + # + (exec 3>redir && print hello >&3 && print goodbye >&3 && cat redir) +0:'>&' redirection +>hello +>goodbye + + (exec 3<redir && read foo <&3 && print $foo && read foo <&3 && print $foo) +0:'<&' redirection +>hello +>goodbye + + ({exec 3<&- } 2>/dev/null + exec 3<&- + read foo <&-) +1:'<&-' redirection with numeric fd (no error message on failure) + + (exec {varid}<&0 + exec {varid}<&- + print About to close a second time >&2 + read {varid}<&-) +1:'<&-' redirection with fd in variable (error message on failure) +?About to close a second time +*?\(eval\):*: failed to close file descriptor * + + print foo >&- +0:'>&-' redirection + + (exec >&- + print foo) +0:'>&-' with attempt to use closed fd +*?\(eval\):2: write error:* + + fn() { local foo; read foo; print $foo; } + coproc fn + print test output >&p + read bar <&p + print $bar +0:'>&p' and '<&p' redirection +>test output + + ( print Output; print Error >& 2 ) >&errout && cat errout +0:'>&FILE' handling +>Output +>Error + + rm -f errout + ( print Output2; print Error2 >& 2 ) &>errout && cat errout +0:'&>FILE' handling +>Output2 +>Error2 + + rm -f errout + ( print Output3; print Error3 >& 2 ) >&|errout && cat errout + ( print Output4; print Error4 >& 2 ) >&!errout && cat errout + ( print Output5; print Error5 >& 2 ) &>|errout && cat errout + ( print Output6; print Error6 >& 2 ) &>!errout && + ( print Output7; print Error7 >& 2 ) >>&errout && + ( print Output8; print Error8 >& 2 ) &>>errout && + ( print Output9; print Error9 >& 2 ) >>&|errout && + ( print Output10; print Error10 >& 2 ) &>>|errout && + ( print Output11; print Error11 >& 2 ) >>&!errout && + ( print Output12; print Error12 >& 2 ) &>>!errout && cat errout +0:'>&|', '>&!', '&>|', '&>!' redirection +>Output3 +>Error3 +>Output4 +>Error4 +>Output5 +>Error5 +>Output6 +>Error6 +>Output7 +>Error7 +>Output8 +>Error8 +>Output9 +>Error9 +>Output10 +>Error10 +>Output11 +>Error11 +>Output12 +>Error12 + + rm -f errout + ( print Output; print Error 1>&2 ) 1>errout 2>&1 && cat errout +0:'Combining > with >& (1)' +>Output +>Error + + rm -f errout + ( print Output; print Error 1>&2 ) 2>&1 1>errout && print errout: && + cat errout +0:'Combining > with >& (2)' +>Error +>errout: +>Output + + rm -f errout + print doo be doo be doo >foo >bar + print "foo: $(<foo)\nbar: $(<bar)" +0:2-file multio +>foo: doo be doo be doo +>bar: doo be doo be doo + + rm -f foo bar + print dont be dont be dont >foo | sed 's/dont/wont/g' >bar +0:setup file+pipe multio + + print "foo: $(<foo)\nbar: $(<bar)" +0:read file+pipe multio +>foo: dont be dont be dont +>bar: wont be wont be wont + + rm -f * + touch out1 out2 + print All files >* + print * + print "out1: $(<out1)\nout2: $(<out2)" +0:multio with globbing +>out1 out2 +>out1: All files +>out2: All files + + print This is out1 >out1 + print This is out2 >out2 +0:setup multio for input + +# Currently, <out{1,2} doesn't work: this is a bug. + cat <out* +0:read multio input +>This is out1 +>This is out2 + + cat out1 | sed s/out/bout/ <out2 +0:read multio input with pipe +>This is bout1 +>This is bout2 + + unset NULLCMD + >out1 +1:null redir with NULLCMD unset +?(eval):2: redirection with no command + + echo this should still work >out1 + print "$(<out1)" +0:null redir in $(...) with NULLCMD unset +>this should still work + + READNULLCMD=cat + print cat input >out1 + <out1 +1:READNULLCMD with NULLCMD unset +?(eval):3: redirection with no command + + NULLCMD=: + >out1 + [[ ! -s out1 ]] || print out1 is not empty +0:null redir with NULLCMD=: +<input + + print cat input >out1 + <out1 +0:READNULLCMD +>cat input + + NULLCMD=cat + >out1 + cat out1 +0:null redir with NULLCMD=cat +<input +>input + + (myfd= + exec {myfd}>logfile + if [[ -z $myfd ]]; then + print "Ooops, failed to set myfd to a file descriptor." >&2 + else + print This is my logfile. >&$myfd + print Examining contents of logfile... + cat logfile + fi) +0:Using {fdvar}> syntax to open a new file descriptor +>Examining contents of logfile... +>This is my logfile. + + (setopt noclobber + exec {myfd}>logfile2 + echo $myfd + exec {myfd}>logfile3) | read myfd + (( ! ${pipestatus[1]} )) +1q:NO_CLOBBER prevents overwriting parameter with allocated fd +?(eval):4: can't clobber parameter myfd containing file descriptor $myfd + + (setopt noclobber + exec {myfd}>logfile2b + print First open >&$myfd + rm -f logfile2b # prevent normal file no_clobberation + myotherfd="${myfd}+0" + exec {myotherfd}>logfile2b + print Overwritten >&$myotherfd) + cat logfile2b +0:NO_CLOBBER doesn't complain about any other expression +>Overwritten + + (exec {myfd}>logfile4 + echo $myfd + exec {myfd}>&- + print This message should disappear >&$myfd) | read myfd + (( ! ${pipestatus[1]} )) +1q:Closing file descriptor using brace syntax +?(eval):4: $myfd:$bad_fd_msg + + typeset -r myfd + echo This should not appear {myfd}>nologfile +1:Error opening file descriptor using readonly variable +?(eval):2: can't allocate file descriptor to readonly parameter myfd + + (typeset +r myfd + exec {myfd}>newlogfile + typeset -r myfd + exec {myfd}>&-) +1:Error closing file descriptor using readonly variable +?(eval):4: can't close file descriptor from readonly parameter myfd + +# 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') +0:Optimised here-string to filename +>This string has been replaced +>by a file containing it. + + print This f$'\x69'le contains d$'\x61'ta. >redirfile + print redirection: + cat<redirfile>outfile + print output: + cat outfile + print append: + cat>>outfile<redirfile + print more output: + cat outfile +0:Parsing of redirection operators (no space required before operators) +>redirection: +>output: +>This file contains data. +>append: +>more output: +>This file contains data. +>This file contains data. + + $ZTST_testdir/../Src/zsh -fc 'exec >/nonexistent/nonexistent + echo output' +0:failed exec redir, no POSIX_BUILTINS +>output +?zsh:1: no such file or directory: /nonexistent/nonexistent + + $ZTST_testdir/../Src/zsh -f -o POSIX_BUILTINS -c ' + exec >/nonexistent/nonexistent + echo output' +1:failed exec redir, POSIX_BUILTINS +?zsh:2: no such file or directory: /nonexistent/nonexistent + + $ZTST_testdir/../Src/zsh -f -o POSIX_BUILTINS -c ' + set >/nonexistent/nonexistent + echo output' +1:failed special builtin redir, POSIX_BUILTINS +?zsh:2: no such file or directory: /nonexistent/nonexistent + + $ZTST_testdir/../Src/zsh -f -o POSIX_BUILTINS -c ' + command set >/nonexistent/nonexistent + echo output' +0:failed special builtin redir with command prefix, POSIX_BUILTINS +>output +?zsh:2: no such file or directory: /nonexistent/nonexistent + + $ZTST_testdir/../Src/zsh -f -o POSIX_BUILTINS -c ' + echo >/nonexistent/nonexistent + echo output' +0:failed unspecial builtin redir, POSIX_BUILTINS +>output +?zsh:2: no such file or directory: /nonexistent/nonexistent + + $ZTST_testdir/../Src/zsh -f -o POSIX_BUILTINS -c ' + . /nonexistent/nonexistent + echo output' +1:failed dot, POSIX_BUILTINS +?zsh:.:2: no such file or directory: /nonexistent/nonexistent + + $ZTST_testdir/../Src/zsh -f -c ' + . /nonexistent/nonexistent + echo output' +0:failed dot, NO_POSIX_BUILTINS +>output +?zsh:.:2: no such file or directory: /nonexistent/nonexistent + + $ZTST_testdir/../Src/zsh -f -o CONTINUE_ON_ERROR <<<' + readonly foo + foo=bar set output + echo output' +0:failed assignment on posix special, CONTINUE_ON_ERROR +>output +?zsh: read-only variable: foo + + $ZTST_testdir/../Src/zsh -f <<<' + readonly foo + foo=bar set output + echo output' +1:failed assignment on posix special, NO_CONTINUE_ON_ERROR +?zsh: read-only variable: foo + + $ZTST_testdir/../Src/zsh -f -o CONTINUE_ON_ERROR <<<' + readonly foo + foo=bar echo output + echo output' +0:failed assignment on non-posix-special, CONTINUE_ON_ERROR +>output +?zsh: read-only variable: foo + + [</dev/null ] +1:check behaviour with square brackets + + print any old rubbish >input1 + () { + local var + read var + print I just read $var + } <input1 >output1 + print Nothing output yet + cat output1 +0:anonymous function redirections are applied immediately +>Nothing output yet +>I just read any old rubbish + + redirfn() { + local var + read var + print I want to tell you about $var + print Also, this might be an error >&2 + } <input2 >output2 2>&1 + print something I heard on the radio >input2 + redirfn + print No output until after this + cat output2 +0:redirections with normal function definition +>No output until after this +>I want to tell you about something I heard on the radio +>Also, this might be an error + + which redirfn +0:text output of function with redirections +>redirfn () { +> local var +> read var +> print I want to tell you about $var +> print Also, this might be an error >&2 +>} < input2 > output2 2>&1 + + 1func 2func 3func() { print Ich heisse $0 } >output3 + for i in 1 2 3; do + f=${i}func + print Running $f + $f + cat output3 + unfunction $f + done +0:multiply named functions with redirection +>Running 1func +>Ich heisse 1func +>Running 2func +>Ich heisse 2func +>Running 3func +>Ich heisse 3func + + redirfn2() { print The latest output; } >&3 + redirfn2 3>output4 + print No output yet + cat output4 +0:Redirections in both function definition and command line +>No output yet +>The latest output + +# This relies on the fact that the test harness always loads +# the zsh/parameter module. + print $functions[redirfn] +0:Output from $functions[] for definition with redirection +>{ +> local var +> read var +> print I want to tell you about $var +> print Also, this might be an error >&2 +>} < input2 > output2 2>&1 + + noredirfn() { print This rather boring function has no redirection.; } + print $functions[noredirfn] +0:Output from $functions[] for definition with no redirection +> print This rather boring function has no redirection. + + (x=43 + x=$(print This should appear, really >&2; print Not used) exec >test.log + print x=$x) + cat test.log +0:Assignment with exec used for redirection: no POSIX_BUILTINS +>x=43 +?This should appear, really + + (setopt POSIX_BUILTINS + x=45 + x=$(print This should appear, too >&2; print And this) exec >test.log + print x=$x) + cat test.log +0:Assignment with exec used for redirection: POSIX_BUILTINS +>x=And this +?This should appear, too + + fn-two-heres() { +# tabs below + cat <<-x <<-y + foo + x + bar + y + } + which -x2 fn-two-heres + fn-two-heres + eval "$(which -x2 fn-two-heres)" + fn-two-heres + print $functions[fn-two-heres] +0:Two here-documents in a line are shown correctly. +>fn-two-heres () { +> cat <<x <<y +>foo +>x +>bar +>y +>} +>foo +>bar +>foo +>bar +> cat <<x <<y +>foo +>x +>bar +>y diff --git a/dotfiles/system/.zsh/modules/Test/A05execution.ztst b/dotfiles/system/.zsh/modules/Test/A05execution.ztst new file mode 100644 index 0000000..0804691 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/A05execution.ztst @@ -0,0 +1,312 @@ +%prep + + storepath=($path) + + mkdir command.tmp command.tmp/dir1 command.tmp/dir2 + + cd command.tmp + + print '#!/bin/sh\necho This is top' >tstcmd + + print '#!/bin/sh\necho This is dir1' >dir1/tstcmd + + print '#!/bin/sh\necho This is dir2' >dir2/tstcmd + + chmod 755 tstcmd dir1/tstcmd dir2/tstcmd + +%test + ./tstcmd +0:./prog execution +>This is top + + path=($ZTST_testdir/command.tmp/dir1 + $ZTST_testdir/command.tmp/dir2 + .) + tstcmd + path=($storepath) +0:path (1) +>This is dir1 + + path=(. command.tmp/dir{1,2}) + tstcmd + path=($storepath) +0:path (2) +>This is top + + functst() { print $# arguments:; print -l $*; } + functst "Eines Morgens" "als Gregor Samsa" + functst "" + functst "aus unrhigen Trumen erwachte" + foo="fand er sich in seinem Bett" + bar= + rod="zu einem ungeheuren Ungeziefer verwandelt." + functst $foo $bar $rod +# set up alias for next test + alias foo='print This is alias one' +0:function argument passing +>2 arguments: +>Eines Morgens +>als Gregor Samsa +>1 arguments: +> +>1 arguments: +>aus unrhigen Trumen erwachte +>2 arguments: +>fand er sich in seinem Bett +>zu einem ungeheuren Ungeziefer verwandelt. + + alias foo='print This is alias two' + fn() { foo; } + fn +0:Aliases in functions +>This is alias one + + foo='Global foo' + traptst() { local foo="Local foo"; trap 'print $foo' EXIT; } + traptst +0:EXIT trap environment +>Global foo + + functst() { return 0; print Ha ha; return 1; } + functst +0:return (1) + + functst() { return 1; print Ho ho; return 0; } + functst +1:return (2) + + unfunction functst + fpath=(.) + print "print This is functst." >functst + autoload functst + functst +0:autoloading (1) +>This is functst. + + unfunction functst + print "functst() { print This, too, is functst; }; print Hello." >functst + typeset -fu functst + functst + functst +0:autoloading with initialization +>Hello. +>This, too, is functst + + unfunction functst + print "print Yet another version" >functst + functst() { autoload -X; } + functst +0:autoloading via -X +>Yet another version + + chpwd() { print Changed to $PWD; } + cd . + unfunction chpwd +0q:chpwd +>Changed to $ZTST_testdir/command.tmp + + chpwd() { print chpwd: changed to $PWD; } + chpwdfn1() { print chpwdfn1: changed to $PWD; } + chpwdfn2() { print chpwdfn2: changed to $PWD; } + chpwd_functions=(chpwdfn1 '' chpwdnonexistentfn chpwdfn2) + cd . + unfunction chpwd + unset chpwd_functions +0q:chpwd_functions +>chpwd: changed to $ZTST_testdir/command.tmp +>chpwdfn1: changed to $ZTST_testdir/command.tmp +>chpwdfn2: changed to $ZTST_testdir/command.tmp + +# Hard to test periodic, precmd and preexec non-interactively. + + fn() { TRAPEXIT() { print Exit; }; } + fn +0:TRAPEXIT +>Exit + + unsetopt DEBUG_BEFORE_CMD + unfunction fn + print 'TRAPDEBUG() { + print Line $LINENO + } + : + unfunction TRAPDEBUG + ' > fn + autoload fn + fn + rm fn +0:TRAPDEBUG +>Line 1 +>Line 1 + + unsetopt DEBUG_BEFORE_CMD + unfunction fn + print 'trap '\''print Line $LINENO'\'' DEBUG + : + trap - DEBUG + ' > fn + autoload fn + fn + rm fn +0:trap DEBUG +>Line 1 +>Line 2 + + TRAPZERR() { print Command failed; } + true + false + true + false + unfunction TRAPZERR +0:TRAPZERR +>Command failed +>Command failed + + trap 'print Command failed again.' ZERR + true + false + true + false + trap - ZERR +0:trap ZERR +>Command failed again. +>Command failed again. + + false + sleep 1000 & + print $? + kill $! +0:Status reset by starting a backgrounded command +>0 + + { setopt MONITOR } 2>/dev/null + [[ -o MONITOR ]] || print -u $ZTST_fd 'Unable to change MONITOR option' + repeat 2048; do (return 2 | + return 1 | + while true; do + false + break + done; + print "${pipestatus[@]}") + ZTST_hashmark + done | sort | uniq -c | sed 's/^ *//' +0:Check whether '$pipestatus[]' behaves. +>2048 2 1 0 +F:This test checks for a bug in '$pipestatus[]' handling. If it breaks then +F:the bug is still there or it reappeared. See workers-29973 for details. + + { setopt MONITOR } 2>/dev/null + externFunc() { awk >/dev/null 2>&1; true; } + false | true | false | true | externFunc + echo $pipestatus +0:Check $pipestatus with a known difficult case +>1 0 1 0 0 +F:This similar test was triggering a reproducible failure with pipestatus. + + { unsetopt MONITOR } 2>/dev/null + coproc { read -et 5 || { print -u $ZTST_fd KILLED; kill -HUP -$$ } } + print -u $ZTST_fd 'This test takes 5 seconds to fail...' + { printf "%d\n" {1..20000} } 2>/dev/null | ( read -e ) + hang(){ printf "%d\n" {2..20000} | cat }; hang 2>/dev/null | ( read -e ) + print -p done + read -et 6 -p +0:Bug regression: piping a shell construct to an external process may hang +>1 +>2 +>done +F:This test checks for a file descriptor leak that could cause the left +F:side of a pipe to block on write after the right side has exited + + { setopt MONITOR } 2>/dev/null + if [[ -o MONITOR ]] + then + ( while :; do print "This is a line"; done ) | () : & + sleep 1 + jobs -l + else + 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 +*>\[<->\] <-> <-> +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 + + print "autoload_redir() { print Autoloaded ksh style; } >autoload.log" >autoload_redir + autoload -Uk autoload_redir + autoload_redir + print No output yet + cat autoload.log + functions autoload_redir +0: +>No output yet +>Autoloaded ksh style +>autoload_redir () { +> print Autoloaded ksh style +>} > autoload.log + +# This tests that we record the status of processes that have already exited +# for when we wait for them. +# +# Actually, we don't guarantee here that the jobs have already exited, but +# the order of the waits means it's highly likely we do need to recall a +# previous status, barring accidents which shouldn't happen very often. In +# other words, we rely on the test working repeatedly rather than just +# once. The monitor option is irrelevant to the logic, so we'll make +# our job easier by turning it off. + { unsetopt MONITOR } 2>/dev/null + (exit 1) & + one=$! + (exit 2) & + two=$! + (exit 3) & + three=$! + wait $three + print $? + wait $two + print $? + wait $one + print $? +0:The status of recently exited background jobs is recorded +>3 +>2 +>1 + +# Regression test for workers/34060 (patch in 34065) + setopt ERR_EXIT NULL_GLOB + if false; then :; else echo if:$?; fi + if false; then :; else for x in _*_; do :; done; echo for:$?; fi +0:False "if" condition handled correctly by "for" loops with ERR_EXIT +>if:1 +>for:0 + +# Regression test for workers/34065 (uses setopt from preceding test) + select x; do :; done; echo $? + select x in; do :; done; echo $? + select x in _*_; do :; done; echo $? + unsetopt ERR_EXIT NULL_GLOB +0:The status of "select" is zero when the loop body does not execute +>0 +>0 +>0 + +# Regression test for workers/36392 + print -u $ZTST_fd 'This test takes 3 seconds and hangs the shell when it fails...' + callfromchld() { true && { print CHLD } } + TRAPCHLD() { callfromchld } + sleep 2 & sleep 3; print OK +0:Background job exit does not affect reaping foreground job +>CHLD +>OK + +# Regression test for workers/39839 and workers/39844 + () { if return 11; then :; fi }; echo $? + () { while return 13; do :; done }; echo $? + () { until return 17; do :; done }; echo $? + () { until false; do return 19; done }; echo $? +0:"return" in "if" or "while" conditional +>11 +>13 +>17 +>19 + diff --git a/dotfiles/system/.zsh/modules/Test/A06assign.ztst b/dotfiles/system/.zsh/modules/Test/A06assign.ztst new file mode 100644 index 0000000..bf39aee --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/A06assign.ztst @@ -0,0 +1,631 @@ +# Tests of parameter assignments + +%prep + mkdir assign.tmp && cd assign.tmp + + touch tmpfile1 tmpfile2 + +%test + + typeset -A assoc + assoc=(one 1 two 2 odd) +1:assign to association with odd no. of values +?(eval):2: bad set of key/value pairs for associative array + +# tests of array element assignment + + array=(1 2 3 4 5) + array[1]=42 + print $array +0:Replacement of array element +>42 2 3 4 5 + + array=(1 2 3 4 5) + array[1]=(42 43) + print $array +0:Replacement of array element with array +>42 43 2 3 4 5 + + array=(1 2 3 4 5) + array[1,2]=(42 43) + print $array +0:Replacement of start of array +>42 43 3 4 5 + + array=(1 2 3 4 5) + array[1,4]=(42 43) + print $array +0:Replacement of start of array with shorter slice +>42 43 5 + + array=(1 2 3 4 5) + array[1,6]=(42 43) + print $array +0:Replacement of array by extending slice +>42 43 + + array=(1 2 3 4 5) + array[3]=(42 43) + print $array +0:Replacement of middle element with array +>1 2 42 43 4 5 + + array=(1 2 3 4 5) + array[3,4]=(42 43 44) + print $array +0:Replacement of slice in middle +>1 2 42 43 44 5 + + array=(1 2 3 4 5) + array[7,8]=(42 43) + print $array + # check that [6] was left empty... + array[6]=41 + print $array +0:Appending by replacing elements off the end +>1 2 3 4 5 42 43 +>1 2 3 4 5 41 42 43 + + array=(1 2 3 4 5) + array[-1]=42 + print $array +0:Replacement of last element of array, negative indices +>1 2 3 4 42 + + array=(1 2 3 4 5) + array[-1]=(42 43) + print $array +0:Replacement of last element of array with array, negative indices +>1 2 3 4 42 43 + + array=(1 2 3 4 5) + array[-3,-2]=(42 43 44) + print $array +0:Replacement of middle of array, negative indices +>1 2 42 43 44 5 + + array=(1 2 3 4 5) + array[-5,-1]=(42 43) + print $array +0:Replacement of entire array, negative indices +>42 43 + + array=(1 2 3 4 5) + array[-7,-1]=(42 43) + print $array +0:Replacement of more than entire array, negative indices +>42 43 + + array=(1 2 3 4 5) + array[-7]=42 + print $array +0:Replacement of element off start of array. +>42 1 2 3 4 5 + + array=(1 2 3 4 5) + array[-7]=42 + array[-6]=43 + print $array +0:Replacement off start doesn't leave gaps. Hope this is right. +>43 1 2 3 4 5 + + array=(1 2 3 4 5) + array[1,-1]=(42 43) + print $array + array[-3,3]=(1 2 3 4 5) + print $array +0:Replacement of entire array, mixed indices +>42 43 +>1 2 3 4 5 + + array=(1 2 3 4 5) + array[-7,7]=(42 43) + print $array +0:Replacement of more than entire array, mixed indices +>42 43 + + array=(1 2 3 4 5) + array[3,-2]=(42 43 44) + print $array + array[-3,5]=(100 99) + print $array +0:Replacement of slice in middle, mixed indices +>1 2 42 43 44 5 +>1 2 42 100 99 5 + +# tests of var+=scalar + + s+=foo + echo $s +0:append scalar to unset scalar +>foo + + s=foo + s+=bar + echo $s +0:append to scalar +>foobar + + set -- a b c + 2+=end + echo $2 +0:append to positional parameter +>bend + + a=(first second) + a+=last + print -l $a +0:add scalar to array +>first +>second +>last + + setopt ksharrays + a=(first second) + a+=last + print -l $a + unsetopt ksharrays +0:add scalar to array with ksharrays set +>firstlast + + a=(1 2) + a[@]+=3 + print -l $a +0:add scalar to array with alternate syntax +>1 +>2 +>3 + + integer i=10 + i+=20 + (( i == 30 )) +0:add to integer + + float f=3.4 + f+=2.3 + printf "%g\n" f +0:add to float +>5.7 + + typeset -A hash + hash=(one 1) + hash+=string + [[ $hash[@] == string ]] +0:add scalar to association + +# tests of var+=(array) + + unset a + a+=(1 2 3) + print -l $a +0:add array to unset parameter +>1 +>2 +>3 + + a=(a) + a+=(b) + print -l $a +0:add array to existing array +>a +>b + + s=foo + s+=(bar) + print -l $s +0:add array to scalar +>foo +>bar + + integer i=1 + i+=(2 3) + print -l $i +0:add array to integer +>1 +>2 +>3 + + float f=2.5 + f+=(3.5 4.5) + printf '%g\n' $f +0:add array to float +>2.5 +>3.5 +>4.5 + + typeset -A h + h+=(a 1 b 2) + print -l $h +0:add to empty association +>1 +>2 + + typeset -A h + h=(a 1) + h+=(b 2 c 3) + print -l $h +0:add to association +>1 +>2 +>3 + + typeset -A h + h=(a 1 b 2) + h+=() + print -l $h +0:add empty array to association +>1 +>2 + +# tests of var[range]+=scalar + + s=sting + s[2]+=art + echo $s +0:insert scalar inside another +>starting + + s=inert + s[-4]+=s + echo $s +0:insert scalar inside another with negative index +>insert + + s=append + s[2,6]+=age + echo $s +0:append scalar to scalar using a range +>appendage + + s=123456789 + s[3,-5]+=X + echo $s +0:insert scalar inside another, specifying a slice +>12345X6789 + + a=(a b c) + a[2]+=oo + echo $a +0:append to array element +>a boo c + + a=(a b c d) + a[-2]+=ool + echo $a +0:append to array element with negative index +>a b cool d + + a=(a b c d) + a[2,-1]+=oom + echo $a +0:append to array element, specifying a slice +>a b c doom + + setopt ksharrays + a=(a b c d) + a[0]+=0 + echo $a + unsetopt ksharrays +0:append to array element with ksharrays set +>a0 + + typeset -A h + h=(one foo) + h[one]+=bar + echo $h +0:append to association element +>foobar + + typeset -A h + h[foo]+=bar + echo ${(kv)h} +0:append to non-existent association element +>foo bar + + typeset -A h + h=(one a two b three c four d) + h[(I)*o*]+=append +1:attempt to append to slice of association +?(eval):3: h: attempt to set slice of associative array + + integer i=123 + i[2]+=6 +1:attempt to add to indexed integer variable +?(eval):2: attempt to add to slice of a numeric variable + + float f=1234.5 + f[2,4]+=3 +1:attempt to add to slice of float variable +?(eval):2: attempt to add to slice of a numeric variable + + unset u + u[3]+=third + echo $u[1]:$u[3] +0:append to unset variable with index +>:third + +# tests of var[range]+=(array) + + a=(1 2 3) + a[2]+=(a b) + echo $a +0:insert array inside another +>1 2 a b 3 + + a=(a b c) + a[-1]+=(d) + echo $a +0:append to array using negative index +>a b c d + + a=(1 2 3 4) + a[-1,-3]+=(x) + echo $a +0:insert array using negative range +>1 2 x 3 4 + + s=string + s[2]+=(a b) +1:attempt to insert array into string +?(eval):2: s: attempt to assign array value to non-array + + integer i=365 + i[2]+=(1 2) +1:attempt to insert array into string +?(eval):2: i: attempt to assign array value to non-array + + typeset -A h + h=(a 1) + h[a]+=(b 2) +1:attempt to append array to hash element +?(eval):3: h: attempt to set slice of associative array + + unset u + u[-34,-2]+=(a z) + echo $u +0:add array to indexed unset variable +>a z + + repeat 10 PATH=. echo hello +0:saving and restoring of exported special parameters +>hello +>hello +>hello +>hello +>hello +>hello +>hello +>hello +>hello +>hello + + repeat 10 FOO=BAR BAR=FOO echo $FOO $BAR +0:save and restore multiple variables around builtin +> +> +> +> +> +> +> +> +> +> + + call() { print $HELLO; } + export HELLO=world + call + HELLO=universe call + call + HELLO=${HELLO}liness call + call + unset HELLO +0:save and restore when using original value in temporary +>world +>universe +>world +>worldliness +>world + + (integer i n x + float f + setopt globassign + i=tmpfile1 + n=tmpf* + x=*2 + f=2+2 + typeset -p i n x f) +0:GLOB_ASSIGN with numeric types +>typeset -i i=0 +>typeset -a n=( tmpfile1 tmpfile2 ) +>typeset x=tmpfile2 +>typeset -E f=4.000000000e+00 + + setopt globassign + foo=tmpf* + print $foo + unsetopt globassign + foo=tmpf* + print $foo +0:GLOB_ASSIGN option +>tmpfile1 tmpfile2 +>tmpf* + + (setopt globassign + typeset -A foo + touch gatest1 gatest2 + foo=(gatest*) + print ${(t)foo} + rm -rf gatest*) +0:GLOB_ASSIGN doesn't monkey with type if not scalar assignment. +>association-local + + A=(first second) + A="${A[*]}" /bin/sh -c 'echo $A' + print -l "${A[@]}" +0:command execution with assignments shadowing array parameter +>first second +>first +>second + + setopt ksharrays + A=(first second) + A="${A[*]}" /bin/sh -c 'echo $A' + print -l "${A[@]}" + unsetopt ksharrays +0:command execution with assignments shadowing array parameter with ksharrays +>first second +>first +>second + + typeset -aU unique_array=(first second) + unique_array[1]=second + print $unique_array +0:assignment to unique array +>second + + typeset -a array=(first) + array[1,3]=(FIRST) + print $array +0:slice beyond length of array +>FIRST + +# tests of string assignments + + a="abc" + a[1]=x + print $a +0:overwrite first character in string +>xbc + + a="abc" + a[2]="x" + print $a +0:overwrite middle character in string +>axc + + a="abc" + a[3]="x" + print $a +0:overwrite last character in string +>abx + + a="abc" + a[-1]="x" + print $a +0:overwrite -1 character in string +>abx + + a="abc" + a[-2]="x" + print $a +0:overwrite -2 character (middle) in string +>axc + + a="ab" + a[-2]="x" + print $a +0:overwrite -2 character (first) in string +>xb + + a="abc" + a[-3]="x" + print $a +0:overwrite -3 character (first) in string +>xbc + + a="abc" + a[-4]="x" + print $a +0:overwrite -4 character (before first) in string +>xabc + + a="abc" + a[-5]="x" + print $a +0:overwrite -5 character (before-before first) in string +>xabc + + a="abc" + a[-4,0]="x" + print $a +0:overwrite [-4,0] characters (before first) in string +>xabc + + a="abc" + a[-4,-4]="x" + print $a +0:overwrite [-4,-4] character (before first) in string +>xabc + + a="abc" + a[-40,-30]="x" + print $a +0:overwrite [-40,-30] characters (far before first) in string +>xabc + + a="abc" + a[-40,1]="x" + print $a +0:overwrite [-40,1] characters in short string +>xbc + + a="abc" + a[-40,40]="x" + print $a +0:overwrite [-40,40] characters in short string +>x + + a="abc" + a[2,40]="x" + print $a +0:overwrite [2,40] characters in short string +>ax + + a="abc" + a[2,-1]="x" + print $a +0:overwrite [2,-1] characters in short string +>ax + + a="abc" + a[-2,-1]="x" + print $a +0:overwrite [-2,-1] characters in short string +>ax + + a="a" + a[-1]="xx" + print $a +0:overwrite [-1] character with "xx" +>xx + + a="a" + a[-2]="xx" + print $a +0:overwrite [-2] character (before first) with "xx" +>xxa + + a="a" + a[2]="xx" + print $a +0:overwrite [2] character (after last) with "xx" +>axx + + a="" + a[1]="xx" + print $a +0:overwrite [1] character (string: "") with "xx" +>xx + + a="" + a[-1]="xx" + print $a +0:overwrite [-1] character (string: "") with "xx" +>xx + + a="" + a[2]="xx" + print $a +0:overwrite [2] character (string: "") with "xx" +>xx diff --git a/dotfiles/system/.zsh/modules/Test/A07control.ztst b/dotfiles/system/.zsh/modules/Test/A07control.ztst new file mode 100644 index 0000000..b1a2487 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/A07control.ztst @@ -0,0 +1,165 @@ +# Test control commands for loops and functions. + +%test + + fn3() { return $1; print Error } + fn2() { fn3 $1 } + fn() { + print start $1 + fn2 $1 + return + print Error + } + for val in -1 0 1 255; do + fn $val; print $? + done +0:Passing of return values back through functions +>start -1 +>-1 +>start 0 +>0 +>start 1 +>1 +>start 255 +>255 + + $ZTST_testdir/../Src/zsh -fc 'fn() { + continue + } + fn' +1:continue outside loop +?fn:continue:1: not in while, until, select, or repeat loop + + for outer in 0 1 2 3; do + print outer $outer + for inner in 0 1 2 3; do + print inner $inner + continue $(( (outer & 1) ? 2 : 1 )) + print error + done + print outer end + done +0:continue with valid argument +>outer 0 +>inner 0 +>inner 1 +>inner 2 +>inner 3 +>outer end +>outer 1 +>inner 0 +>outer 2 +>inner 0 +>inner 1 +>inner 2 +>inner 3 +>outer end +>outer 3 +>inner 0 + + for outer in 0 1; do + continue 0 + print -- $outer got here, status $? + done +1:continue error case 0 +?(eval):continue:2: argument is not positive: 0 + + for outer in 0 1; do + continue -1 + print -- $outer got here, status $? + done +1:continue error case -1 +?(eval):continue:2: argument is not positive: -1 + + fn() { + break + } + for outer in 0 1; do + print $outer + fn + done +0:break from within function (this is a feature, I disovered) +>0 + + for outer in 0 1 2 3; do + print outer $outer + for inner in 0 1 2 3; do + print inner $inner + break $(( (outer & 1) ? 2 : 1 )) + print error + done + print outer end + done +0:break with valid argument +>outer 0 +>inner 0 +>outer end +>outer 1 +>inner 0 + + for outer in 0 1; do + break 0 + print -- $outer got here, status $? + done +1:break error case 0 +?(eval):break:2: argument is not positive: 0 + + for outer in 0 1; do + break -1 + print -- $outer got here, status $? + done +1:break error case -1 +?(eval):break:2: argument is not positive: -1 + + false + for x in; do + print nothing executed + done +0:Status 0 from for with explicit empty list + + set -- + false + for x; do + print nothing executed + done +0:Status 0 from for with implicit empty list + + (exit 2) + for x in 1 2; do + print $? + done +0:Status from previous command propagated into for loop +>2 +>0 + + false + for x in $(echo 1 2; (exit 3)); do + print $? + done +0:Status from expansion propagated into for loop +>3 +>0 + + false + for x in $(exit 4); do + print not executed + done +0:Status from expansion not propagated after unexecuted for loop + + false + for x in NonExistentFilePrefix*(N); do + print not executed, either + done +0:Status from before for loop not propagated if empty after expansion + + for x in $(echo 1; false); do + done +0:Status reset by empty list in for loop + + false + for x in $(echo 1; false); do + echo $? + (exit 4) + done +4:Last status from loop body is kept even with other funny business going on +>1 diff --git a/dotfiles/system/.zsh/modules/Test/B01cd.ztst b/dotfiles/system/.zsh/modules/Test/B01cd.ztst new file mode 100644 index 0000000..94447e7 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/B01cd.ztst @@ -0,0 +1,144 @@ +# This file serves as a model for how to write tests, so is more heavily +# commented than the others. All tests are run in the Test subdirectory +# of the distribution, which must be writable. They should end with +# the suffix `.ztst': this is not required by the test harness itself, +# but it is needed by the Makefile to run all the tests. + +# Blank lines with no other special meaning (e.g. separating chunks of +# code) and all those with a `#' in the first column are ignored. + +# All section names start with a % in the first column. The names +# must be in the expected order, though not all sections are required. +# The sections are %prep (preparatory setup: code executed should return +# status 0, but no other tests are performed), %test (the main tests), and +# %clean (to cleanup: the code is simply unconditionally executed). +# +# Literal shell code to be evaluated must be indented with any number +# of spaces and/or tabs, to differentiate it from tags with a special +# meaning to the test harness. Note that this is true even in sections +# where there are no such tags. Also note that file descriptor 9 +# is reserved for input from the test script, and file descriptor 8 +# preserves the original stdout. Option settings are preserved between the +# execution of different code chunks; initially, all standard zsh options +# (the effect of `emulate -R zsh') are set. + +%prep +# This optional section prepares the test, creating directories and files +# and so on. Chunks of code are separated by blank lines (which is not +# necessary before the end of the section); each chunk of code is evaluated +# in one go and must return status 0, or the preparation is deemed to have +# failed and the test ends with an appropriate error message. Standard +# output from this section is redirected to /dev/null, but standard error +# is not redirected. +# +# 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 + + ln -s ../real cdtst.tmp/sub/fake + + setopt chaselinks + cd . + unsetopt chaselinks + mydir=$PWD + +%test +# This is where the tests are run. It consists of blocks separated +# by blank lines. Each block has the same format and there may be any +# number of them. It consists of indented code, plus optional sets of lines +# beginning '<', '>' and '?' which may appear in any order. These correspond +# to stdin (fed to the code), stdout (compared with code output) and +# stderr (compared with code error output) respectively. These subblocks +# may occur in any order, but the natural one is: code, stdin, stdout, +# stderr. +# +# The rules for '<', '>' and '?' lines are the same: only the first +# character is stripped (with the excpetion for '*' noted below), with +# subsequent whitespace being significant; lines are not subject to any +# substitution unless the `q' flag (see below) is set. +# +# Each line of a '>' and '?' chunk may be preceded by a '*', so the line +# starts '*>' or '*?'. This signifies that for any line with '*' in front +# the actual output should be pattern matched against the corresponding +# lines in the test output. Each line following '>' or '?' must be a +# valid pattern, so characters special to patterns such as parentheses +# must be quoted with a backslash. The EXTENDED_GLOB option is used for +# all such patterns. +# +# Each chunk of indented code is to be evaluated in one go and is to +# be followed by a line starting (in the first column) with +# 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 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 +# expansion (i.e. not globbing). +# This can be followed by a `:' and a message describing the +# test, which will be printed if the test fails, along with a +# description of the failure that occurred. The `:' and message are +# optional, but highly recommended. +# Hence a complete status line looks something like: +# 0dDq:Checking whether the world will end with a bang or a whimper +# +# If either or both of the '>' and '?' sets of lines is absent, it is +# assumed the corresponding output should be empty and it is an error if it +# is not. If '<' is empty, stdin is an empty (but opened) file. +# +# It is also possible to add lines in the redirection section beginning +# with `F:'. The remaining text on all such lines will be concatenated +# (with newlines in between) and displayed in the event of an error. +# This text is useful for explaining certain frequent errors, for example +# ones which may arise from the environment rather than from the shell +# 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 +# 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 +# (typically "if"). In both cases, the value of the variable is output +# as a warning that the test was skipped. +# ZTST_unimplemented: Set this in the %prep phase if the entire test file +# is to be skipped. +# ZTST_skip: Set this in any test case if that single test case is to be +# skipped. Testing resumes at the next test case in the same file. + cd cdtst.tmp/sub/fake && + pwd && + print $PWD +0q:Preserving symbolic links in the current directory string +>$mydir/cdtst.tmp/sub/fake +>$mydir/cdtst.tmp/sub/fake +F:This test shouldn't really fail. The fact that it has indicates +F:something is broken. But you already knew that. + + cd ../../.. && + pwd && + print $PWD +0q:Changing directory up through symbolic links without following them +>$mydir +>$mydir + + setopt chaselinks + cd cdtst.tmp/sub/fake && + pwd && + print $PWD +0q:Resolving symbolic links with chaselinks set +>$mydir/cdtst.tmp/real +>$mydir/cdtst.tmp/real + + ln -s nonexistent link_to_nonexistent + pwd1=$(pwd -P) + cd -s link_to_nonexistent + pwd2=$(pwd -P) + [[ $pwd1 = $pwd2 ]] || print "Ooops, changed to directory '$pwd2'" +0: +?(eval):cd:3: not a directory: link_to_nonexistent + +%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 +# subdirectories. This is essentially like %prep, except that status +# return values are ignored. diff --git a/dotfiles/system/.zsh/modules/Test/B02typeset.ztst b/dotfiles/system/.zsh/modules/Test/B02typeset.ztst new file mode 100644 index 0000000..b27bb4f --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/B02typeset.ztst @@ -0,0 +1,723 @@ +# There are certain usages of typeset and its synonyms that it is not +# possible to test here, because they must appear at the top level and +# everything that follows is processed by an "eval" within a function. + +# Equivalences: +# declare typeset +# export typeset -xg +# float typeset -E +# functions typeset -f +# integer typeset -i +# local typeset +g -m approximately +# readonly typeset -r + +# Tested elsewhere: +# Equivalence of autoload and typeset -fu A05execution +# Associative array creation & assignment D04parameter, D06subscript +# Effects of GLOBAL_EXPORT E01options +# Function tracing (typeset -ft) E02xtrace + +# Not yet tested: +# Assorted illegal flag combinations + +%prep + ## Do not remove the next line, it's used by V10private.ztst + # test_zsh_param_private + + mkdir typeset.tmp && cd typeset.tmp + + setopt noglob + + scalar=scalar + array=(a r r a y) + + scope00() { + typeset scalar + scalar=local + typeset -a array + array=(l o c a l) + print $scalar $array + } + scope01() { + local scalar + scalar=local + local -a array + array=(l o c a l) + print $scalar $array + } + scope02() { + declare scalar + scalar=local + declare -a array + array=(l o c a l) + print $scalar $array + } + scope10() { + export outer=outer + /bin/sh -fc 'echo $outer' + } + scope11() { + typeset -x outer=outer + /bin/sh -fc 'echo $outer' + } + scope12() { + local -x outer=inner + /bin/sh -fc 'echo $outer' + } + scope13() { + local -xT OUTER outer + outer=(i n n e r) + /bin/sh -fc 'echo $OUTER' + } + + # Bug? `typeset -h' complains that ! # $ * - ? @ are not identifiers. + stress00() { + typeset -h +g -m [[:alpha:]_]* + unset -m [[:alpha:]_]* + typeset +m [[:alpha:]_]* + } + +%test + + typeset +m scalar array +0:Report types of parameters with typeset +m +>scalar +>array array + + scope00 + print $scalar $array +0:Simple local declarations +>local l o c a l +>scalar a r r a y + + scope01 + print $scalar $array +0:Equivalence of local and typeset in functions +>local l o c a l +>scalar a r r a y + + scope02 + print $scalar $array +0:Basic equivalence of declare and typeset +>local l o c a l +>scalar a r r a y + + declare +m scalar +0:declare previously lacked -m/+m options +>scalar + + scope10 + print $outer +0:Global export +>outer +>outer + + scope11 + print $outer +0:Equivalence of export and typeset -x +>outer +>outer + + scope12 + print $outer +0:Local export +>inner +>outer + + float f=3.14159 + typeset +m f + float -E3 f + print $f + float -F f + print $f +0:Floating point, adding a precision, and fixed point +>float local f +>3.14e+00 +>3.142 + + integer i=3.141 + typeset +m i + integer -i2 i + print $i +0:Integer and changing the base +>integer local i +>2#11 + + float -E3 f=3.141 + typeset +m f + integer -i2 f + typeset +m f + print $f +0:Conversion of floating point to integer +>float local f +>integer 2 local f +>2#11 + + typeset -f +0q:Equivalence of functions and typeset -f +>$(functions) + + readonly r=success + print $r + r=failure +1:Readonly declaration +>success +?(eval):3: read-only variable: r + + typeset r=success + readonly r + print $r + r=failure +1:Convert to readonly +>success +?(eval):4: read-only variable: r + + typeset -gU array + print $array +0:Uniquified arrays and non-local scope +>a r y + + typeset -T SCALAR=l:o:c:a:l array + print $array + typeset -U SCALAR + print $SCALAR $array +0:Tied parameters and uniquified colon-arrays +>l o c a l +>l:o:c:a l o c a + + (setopt NO_multibyte cbases + LC_ALL=C 2>/dev/null + typeset -T SCALAR=$'l\x83o\x83c\x83a\x83l' array $'\x83' + print $array + typeset -U SCALAR + for (( i = 1; i <= ${#SCALAR}; i++ )); do + char=$SCALAR[i] + print $(( [#16] #char )) + done + print $array) +0:Tied parameters and uniquified arrays with meta-character as separator +>l o c a l +>0x6C +>0x83 +>0x6F +>0x83 +>0x63 +>0x83 +>0x61 +>l o c a + + typeset -T SCALAR=$'l\000o\000c\000a\000l' array $'\000' + typeset -U SCALAR + print $array + [[ $SCALAR == $'l\000o\000c\000a' ]] +0:Tied parameters and uniquified arrays with NUL-character as separator +>l o c a + + typeset -T SCALAR array + typeset +T SCALAR +1:Untying is prohibited +?(eval):typeset:2: use unset to remove tied variables + + OUTER=outer + scope13 + print $OUTER +0:Export of tied parameters +>i:n:n:e:r +>outer + + typeset -TU MORESTUFF=here-we-go-go-again morestuff '-' + print -l $morestuff +0:Tied arrays with separator specified +>here +>we +>go +>again + + typeset -T THIS will not work +1:Tied array syntax +?(eval):typeset:1: too many arguments for -T + + local array[2]=x +1:Illegal local array element assignment +?(eval):local:1: array[2]: can't create local array elements + + local -a array + typeset array[1]=a array[2]=b array[3]=c + print $array +0:Legal local array element assignment +>a b c + + local -A assoc + local b=1 ;: to stomp assoc[1] if assoc[b] is broken + typeset assoc[1]=a assoc[b]=2 assoc[3]=c + print $assoc[1] $assoc[b] $assoc[3] +0:Legal local associative array element assignment +>a 2 c + + local scalar scalar[1]=a scalar[2]=b scalar[3]=c + print $scalar +0:Local scalar subscript assignment +>abc + + typeset -L 10 fools + for fools in " once" "twice" " thrice" " oops too long here"; do + print "'$fools'" + done +0:Left justification of scalars +>'once ' +>'twice ' +>'thrice ' +>'oops too l' + + typeset -L 10 -F 3 foolf + for foolf in 1.3 4.6 -2.987 -4.91031; do + print "'$foolf'" + done +0:Left justification of floating point +>'1.300 ' +>'4.600 ' +>'-2.987 ' +>'-4.910 ' + + typeset -L 10 -Z foolzs + for foolzs in 001.3 04.6 -2.987 -04.91231; do + print "'$foolzs'" + done +0:Left justification of scalars with zero suppression +>'1.3 ' +>'4.6 ' +>'-2.987 ' +>'-04.91231 ' + + typeset -R 10 foors + for foors in short longer even-longer; do + print "'$foors'" + done +0:Right justification of scalars +>' short' +>' longer' +>'ven-longer' + + typeset -Z 10 foozs + for foozs in 42 -42 " 43" " -43"; do + print "'$foozs'" + done +0:Right justification of scalars with zeroes +>'0000000042' +>' -42' +>' 000000043' +>' -43' + + integer -Z 10 foozi + for foozi in 42 -42 " 43" " -43"; do + print "'$foozi'" + done +0:Right justification of integers with zero, no initial base +>'0000000042' +>'-000000042' +>'0000000043' +>'-000000043' +# In case you hadn't twigged, the spaces are absorbed in the initial +# math evaluation, so don't get through. + + unsetopt cbases + integer -Z 10 -i 16 foozi16 + for foozi16 in 42 -42 " 43" " -43"; do + print "'$foozi16'" + done +0:Right justification of integers with zero, base 16, C_BASES off +>'16#000002A' +>'-16#00002A' +>'16#000002B' +>'-16#00002B' + + setopt cbases + integer -Z 10 -i 16 foozi16c + for foozi16c in 42 -42 " 43" " -43"; do + print "'$foozi16c'" + done +0:Right justification of integers with zero, base 16, C_BASES on +>'0x0000002A' +>'-0x000002A' +>'0x0000002B' +>'-0x000002B' + + setopt cbases + integer -Z 10 -i 16 foozi16c + for foozi16c in 0x1234 -0x1234; do + for (( i = 1; i <= 5; i++ )); do + print "'${foozi16c[i,11-i]}'" + done + print "'${foozi16c[-2]}'" + done +0:Extracting substrings from padded integers +>'0x00001234' +>'x0000123' +>'000012' +>'0001' +>'00' +>'3' +>'-0x0001234' +>'0x000123' +>'x00012' +>'0001' +>'00' +>'3' + + typeset -F 3 -Z 10 foozf + for foozf in 3.14159 -3.14159 4 -4; do + print "'$foozf'" + done +0:Right justification of fixed point numbers with zero +>'000003.142' +>'-00003.142' +>'000004.000' +>'-00004.000' + + stress00 + print $scalar $array +0q:Stress test: all parameters are local and unset, using -m +>scalar a r y + + 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 + + export ENVFOO=bar + print ENVFOO in environment + env | grep '^ENVFOO' + print Changing ENVFOO + ENVFOO="not bar any more" + env | grep '^ENVFOO' + unset ENVFOO + print ENVFOO no longer in environment + env | grep '^ENVFOO' +1:Adding and removing values to and from the environment +>ENVFOO in environment +>ENVFOO=bar +>Changing ENVFOO +>ENVFOO=not bar any more +>ENVFOO no longer in environment + + (export FOOENV=BAR + env | grep '^FOOENV' + print Exec + exec $ZTST_testdir/../Src/zsh -fc ' + print Unset + unset FOOENV + env | grep "^FOOENV"') +1:Can unset environment variables after exec +>FOOENV=BAR +>Exec +>Unset + + local case1=upper + typeset -u case1 + print $case1 + upper="VALUE OF \$UPPER" + print ${(P)case1} +0:Upper case conversion, does not apply to values used internally +>UPPER +>VALUE OF $UPPER + + local case2=LOWER + typeset -l case2 + print $case2 + LOWER="value of \$lower" + print ${(P)case2} +0:Lower case conversion, does not apply to values used internally +>lower +>value of $lower + + typeset -a array + array=(foo bar) + fn() { typeset -p array nonexistent; } + fn +1:declare -p shouldn't create scoped values +>typeset -g -a array=( foo bar ) +?fn:typeset: no such variable: nonexistent + + unsetopt typesetsilent + silent1(){ typeset -g silence; } + silent2(){ local silence; silent1; } + silent2 +0:typeset -g should be silent even without TYPESET_SILENT + + typeset -T TIED_SCALAR tied_array + TIED_SCALAR=foo:bar + print $tied_array + typeset -T TIED_SCALAR=goo:car tied_array + print $tied_array + typeset -T TIED_SCALAR tied_array=(poo par) + print $TIED_SCALAR +0:retying arrays to same array works +>foo bar +>goo car +>poo:par + + ( + setopt POSIXBUILTINS + readonly pbro + print ${+pbro} >&2 + (typeset -g pbro=3) + (pbro=4) + readonly -p pbro >&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 + ) +1:readonly with POSIX_BUILTINS +?0 +?(eval):5: read-only variable: pbro +?(eval):6: read-only variable: pbro +?typeset -g -r pbro +?0 +?(eval):10: read-only variable: pbro + + readonly foo=bar novalue + readonly -p +0:readonly -p output (no readonly specials) +>typeset -r foo=bar +>typeset -r novalue='' + + local -a a1 a2 + local -r r1=yes r2=no + a1=(one two) a2=(three four) + readonly a1 + typeset -pm 'a[12]' + typeset -pm 'r[12]' +0:readonly -p output +>typeset -ar a1=( one two ) +>typeset -a a2=( three four ) +>typeset -r r1=yes +>typeset -r r2=no + + one=hidden two=hidden three=hidden four=hidden five=hidden + fn() { + local bleugh="four=vier" + typeset -R10 one=eins two=(zwei dio) three $bleugh five=(cinq cinque) + three=drei + print -l $one $two $three $four $five + } + fn + print -l $one $two $three $four $five +0:typeset reserved word interface: basic +> eins +>zwei +>dio +> drei +> vier +>cinq +>cinque +>hidden +>hidden +>hidden +>hidden +>hidden + + ( + setopt glob + mkdir -p arrayglob + touch arrayglob/{one,two,three,four,five,six,seven} + fn() { + typeset array=(arrayglob/[tf]*) + print -l ${array:t} + # + typeset {first,second,third}=the_same_value array=( + extends + over + multiple + lines + ) + print -l $first $second $third "$array" + # + integer i=$(echo 1 + 2 + 3 + 4) + print $i + # + # only noted by accident this was broken.. + # we need to turn off special recognition + # of assignments within assignments... + typeset careful=( i=1 j=2 k=3 ) + print -l $careful + } + fn + ) +0:typeset reserved word, more complicated cases +>five +>four +>three +>two +>the_same_value +>the_same_value +>the_same_value +>extends over multiple lines +>10 +>i=1 +>j=2 +>k=3 + + ( + # reserved word is recognised at parsing. + # yes, this is documented. + # anyway, that means we need to + # re-eval the function... + fn=' + fn() { + typeset foo=`echo one word=two` + print $foo + print $word + } + ' + print reserved + eval $fn; fn + print builtin + disable -r typeset + eval $fn; fn + enable -r typeset + disable typeset + print reserved + eval $fn; fn + ) +0:reserved word and builtin interfaces +>reserved +>one word=two +> +>builtin +>one +>two +>reserved +>one word=two +> + + fn() { + emulate -L zsh + setopt typeset_silent + local k + typeset -A hash=(k1 v1 k2 v2) + typeset foo=word array=(more than one word) + for k in ${(ko)hash}; do + print $k $hash[$k] + done + print -l $foo $array + typeset -A hash + typeset foo array + for k in ${(ko)hash}; do + print $k $hash[$k] + done + print -l $foo $array + typeset hash=(k3 v3 k4 v4) array=(odd number here) + for k in ${(ko)hash}; do + print $k $hash[$k] + done + print -l $array + } + fn +0:typeset preserves existing variable types +>k1 v1 +>k2 v2 +>word +>more +>than +>one +>word +>k1 v1 +>k2 v2 +>word +>more +>than +>one +>word +>k3 v3 +>k4 v4 +>odd +>number +>here + + fn() { typeset foo bar thing=this stuff=(that other) more=woevva; } + which -x2 fn + fn2() { typeset assignfirst=(why not); } + which -x2 fn2 +0:text output from typeset +>fn () { +> typeset foo bar thing=this stuff=(that other) more=woevva +>} +>fn2 () { +> typeset assignfirst=(why not) +>} + + fn() { + typeset array=() + print ${(t)array} ${#array} + typeset gnothergarray=() gnothergarray[1]=yes gnothergarray[2]=no + print -l ${(t)gnothergarray} $gnothergarray + } + fn +0:can set empty array +>array-local 0 +>array-local +>yes +>no + + array=(nothing to see here) + fn() { + typeset array=(one two three four five) + typeset array[2,4]=(umm er) + print ${#array} $array + typeset array[2,3]=() + print ${#array} $array + } + fn + print ${#array} $array +0:can update array slices in typeset +>4 one umm er five +>2 one five +>4 nothing to see here + + array=(no really nothing here) + fn() { + typeset array=() array[2]=two array[4]=four + typeset -p array + typeset array=() array[3]=three array[1]=one + typeset -p array + } + fn + print $array +0:setting empty array in typeset +>typeset -a array=( '' two '' four ) +>typeset -a array=( one '' three ) +>no really nothing here + + readonly isreadonly=yes + typeset isreadonly=still +1:typeset returns status 1 if setting readonly variable +?(eval):2: read-only variable: isreadonly + + if (( UID )); then + UID=$((UID+1)) date; echo "Status is printed, $?" + else + ZTST_skip="cannot test setuid error when tests run as superuser" + fi +0:when cannot change UID, the command isn't run +# 'date' did not run. +>Status is printed, 1 +*?*: failed to change user ID: * diff --git a/dotfiles/system/.zsh/modules/Test/B03print.ztst b/dotfiles/system/.zsh/modules/Test/B03print.ztst new file mode 100644 index 0000000..c65568a --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/B03print.ztst @@ -0,0 +1,336 @@ +# Tests for the echo, print, printf and pushln builtins + +# Tested elsewhere: +# 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 + +# Not yet tested: +# echo and pushln +# print's -b -c -s -z -N options + + +%test + + print -D "${HOME:-~}" +0:replace directory name +>~ + + print -u2 'error message' +0:output to file-descriptor +?error message + + print -o foo bar Baz +0:argument sorting +>Baz bar foo + + print -f +1:print -f needs a format specified +?(eval):print:1: argument expected: -f + + print -Of '%s\n' foo bar baz +0:reverse argument sorting +>foo +>baz +>bar + +# some locales force case-insensitive sorting + (LC_ALL=C; print -o a B c) +0:case-sensitive argument sorting +>B a c + + (LC_ALL=C; print -io a B c) +0:case-insensitive argument sorting +>a B c + + print -m '[0-9]' one 2 three 4 five 6 +0:removal of non-matching arguments +>2 4 6 + + printf '%s\n' string +0:test s format specifier +>string + + printf '%b' '\t\\\n' +0:test b format specifier +> \ + + printf '%q\n' '=a=b \ c!' +0: test q format specifier +>\=a=b\ \\\ c! + + printf '%c\n' char +0:test c format specifier +>c + + printf '%.10e%n\n' 1 count >/dev/null + printf '%d\n' $count +0:test n format specifier +>16 + + printf '%5b%n\n' abc count >/dev/null; echo $count +0:check count of width-specified %b +>5 + + printf '%s!%5b!\n' abc +0:ensure width is applied to empty param +>abc! ! + + printf '%d %d\n' 123.45 678 90.1 +0:test d format specifier +>123 678 +>90 0 + + printf '%g %g\n' 123.45 678 90.1 +0:test g format specifier +>123.45 678 +>90.1 0 + + print -f 'arg: %b\n' -C2 '\x41' '\x42' '\x43' +0:override -C when -f was given +>arg: A +>arg: B +>arg: C + +# Is anyone not using ASCII + printf '%d\n' \'A +0:initial quote to get numeric value of character with int +>65 + + printf '%.1E\n' \'B +0:initial quote to get numeric value of character with double +>6.6E+01 + + printf '%x\n' $(printf '"\xf0') +0:numeric value of high numbered character +>f0 + + printf '\x25s\n' arg +0:using \x25 to print a literal % in format +>%s + + printf '%3c\n' c +0:width specified in format specifier +> c + + printf '%.4s\n' chopped +0:precision specified in format specifier +>chop + + printf '%*.*f\n' 6 2 10.2 +0:width/precision specified in arguments +> 10.20 + + printf '%z' +1:use of invalid directive +?(eval):printf:1: %z: invalid directive + + printf '%d\n' 3a +1:bad arithmetic expression +?(eval):1: bad math expression: operator expected at `a' +>0 + + printf '%12$s' 1 2 3 +1:out of range argument specifier +?(eval):printf:1: 12: argument specifier out of range + + printf '%2$s\n' 1 2 3 +1:out of range argument specifier on format reuse +?(eval):printf:1: 2: argument specifier out of range +>2 + + printf '%*0$d' +1:out of range argument specifier on width +?(eval):printf:1: 0: argument specifier out of range + + print -m -f 'format - %s.\n' 'z' a b c +0:format not printed if no arguments left after -m removal + + print -f 'format - %s%b.\n' +0:format printed despite lack of arguments +>format - . + + printf 'x%4sx\n' +0:with no arguments empty string where string needed +>x x + + printf '%d\n' +0:with no arguments, zero used where number needed +>0 + + printf '%s\t%c:%#x%%\n' one a 1 two b 2 three c 3 +0:multiple arguments with format reused +>one a:0x1% +>two b:0x2% +>three c:0x3% + + printf '%d%n' 123 val val val > /dev/null + printf '%d\n' val +0:%n count zeroed on format reuse +>1 + +# this may fill spec string with '%0'+- #*.*lld\0' - 14 characters + printf '%1$0'"'+- #-08.5dx\n" 123 +0:maximal length format specification +>+00123 x + + printf "x:%-20s:y\n" fubar +0:left-justification of string +>x:fubar :y + + printf '%*smorning\n' -5 good +0:negative width specified +>good morning + + printf '%.*g\n' -1 .1 +0:negative precision specified +>0.1 + + printf '%2$s %1$d\n' 1 2 +0:specify argument to output explicitly +>2 1 + + printf '%3$.*1$d\n' 4 0 3 +0:specify output and precision arguments explicitly +>0003 + + printf '%2$d%1$d\n' 1 2 3 4 +0:reuse format where arguments are explicitly specified +>21 +>43 + + printf '%1$*2$d' 1 2 3 4 5 6 7 8 9 10; echo .EoL. +0:reuse of specified arguments +> 1 3 5 7 9.EoL. + + echo -n 'Now is the time'; echo .EoL. +0:testing -n with echo +>Now is the time.EoL. + + printf '%1$0+.3d\n' 3 +0:flags mixed with specified argument +>+003 + +# Test the parsing of the \c escape. + + echo '1 2!\c3 4' a b c d; echo .EoL. +0:Truncating first echo arg using backslash-c +>1 2!.EoL. + + echo a b '1 2?\c5 6' c d; echo .EoL. +0:Truncating third echo arg using backslash-c +>a b 1 2?.EoL. + + printf '1 2!\c3 4'; echo .EoL. +0:Truncating printf literal using backslash-c +>1 2!.EoL. + + printf '%s %b!\c%s %s' 1 2 3 4 5 6 7 8 9; echo .EoL. +0:Truncating printf format using backslash-c +>1 2!.EoL. + + printf '%s %b!\c%s %s' '1\c' '2\n\c' 3 4 5 6 7 8 9 +0:Truncating printf early %b arg using backslash-c +>1\c 2 + + printf '%b %b\n' 1 2 3 4 '5\c' 6 7 8 9; echo .EoL. +0:Truncating printf late %b arg using backslash-c +>1 2 +>3 4 +>5.EoL. + +# The following usage, as stated in the manual, is not recommended and the +# results are undefined. Tests are here anyway to ensure some form of +# half-sane behaviour. + + printf '%2$s %s %3$s\n' Morning Good World +0:mixed style of argument selection +>Good Morning World + + printf '%*1$.*d\n' 1 2 +0:argument specified for width only +>00 + + print -f '%*.*1$d\n' 1 2 3 +0:argument specified for precision only +>2 +>000 + + printf -- '%s\n' str +0:initial `--' ignored to satisfy POSIX +>str + + printf '%' +1:nothing after % in format specifier +?(eval):printf:1: %: invalid directive + + printf $'%\0' +1:explicit null after % in format specifier +?(eval):printf:1: %: invalid directive + + printf '%b\n' '\0123' +0:printf handles \0... octal escapes in replacement text +>S + + print -lO $'a' $'a\0' $'a\0b' $'a\0b\0' $'a\0b\0a' $'a\0b\0b' $'a\0c' | + while read -r line; do + for (( i = 1; i <= ${#line}; i++ )); do + foo=$line[i] + printf "%02x" $(( #foo )) + done + print + done +0:sorting with embedded nulls +>610063 +>6100620062 +>6100620061 +>61006200 +>610062 +>6100 +>61 + + 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 +0:Tab expansion by print +>one two three four +> one two three four +> one two three four +>one two three four +> one two three four +> one two three four + + unset foo + print -v foo once more + typeset -p foo + printf -v foo "%s\0%s-" into the breach + typeset -p foo +0:print and printf into a variable +>typeset -g foo='once more' +>typeset -g foo=$'into\C-@the-breach\C-@-' + + typeset -a foo + print -f '%2$d %4s' -v foo one 1 two 2 three 3 + typeset -p foo +0:printf into an array variable +>typeset -a foo=( '1 one' '2 two' '3 three' ) + + typeset -a foo + print -f '%s' -v foo string + typeset -p foo +0:printf to an array variable without format string reuse +>typeset foo=string + + printf - + printf - - + printf -- + printf -- - + printf -- -- + printf -x -v foo + # Final print for newline on stdout + print +0:regression test of printf with assorted ambiguous options or formats +>------x +?(eval):printf:3: not enough arguments diff --git a/dotfiles/system/.zsh/modules/Test/B04read.ztst b/dotfiles/system/.zsh/modules/Test/B04read.ztst new file mode 100644 index 0000000..25c3d41 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/B04read.ztst @@ -0,0 +1,112 @@ +# Tests for the read builtin + +# Tested elsewhere: +# reading from a coprocess A01grammar, A04redirect + +# Not tested: +# -c/-l/-n (options for compctl functions) +# -q/-s (needs a tty) + +%test + + read <<<'hello world' + print $REPLY +0:basic read command +>hello world + + read -A <<<'hello world' + print $reply[2] +0:array read +>world + + read -k3 -u0 <<<foo:bar + print $REPLY +0:read specified number of chars +>foo + + for char in y Y n N X $'\n'; do + read -q -u0 <<<$char + print $? + done +0:read yes or no, default no +>0 +>0 +>1 +>1 +>1 +>1 + + read -d: <<<foo:bar + print $REPLY +0:read up to delimiter +>foo + + print foo:bar|IFS=: read -A + print $reply +0:use different, IFS separator to array +>foo bar + + print -z hello world; read -z + print $REPLY +0:read from editor buffer stack +>hello world + + unset REPLY + read -E <<<hello + print $REPLY +0:read with echoing and assigning +>hello +>hello + + unset REPLY + read -e <<<hello + print $REPLY +0:read with echoing but assigning disabled +>hello +> + + read -e -t <<<hello +0:read with test first +>hello + + SECONDS=0 + read -e -t 5 <<<hello + print $SECONDS +0:read with timeout (no waiting should occur) +>hello +>0 + + print -n 'Testing the\0null hypothesis\0' | + while read -d $'\0' line; do print $line; done +0:read with null delimiter +>Testing the +>null hypothesis + +# Note that trailing NULLs are not stripped even if they are in +# $IFS; only whitespace characters contained in $IFS are stripped. + print -n $'Aaargh, I hate nulls.\0\0\0' | read line + print ${#line} +0:read with trailing metafied characters +>24 + + (typeset -r foo + read foo) <<<bar +1:return status on failing to set parameter +?(eval):2: read-only variable: foo + + read -AE array <<<'one two three' + print ${(j.:.)array} +0:Behaviour of -A and -E combination +>one +>two +>three +>one:two:three + + array=() + read -Ae array <<<'four five six' + print ${(j.:.)array} +0:Behaviour of -A and -e combination +>four +>five +>six +> diff --git a/dotfiles/system/.zsh/modules/Test/B05eval.ztst b/dotfiles/system/.zsh/modules/Test/B05eval.ztst new file mode 100644 index 0000000..6427d6f --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/B05eval.ztst @@ -0,0 +1,34 @@ +# Tests for the eval builtin. +# This is quite short; eval is widely tested throughout the test suite +# and its basic behaviour is fairly straightforward. + +%prep + + cmd='print $?' + +%test + + false + eval $cmd +0:eval retains value of $? +>1 + + # no point getting worked up over what the error message is... + ./command_not_found 2>/dev/null + eval $cmd +0:eval after command not found +>127 + + # trick the test system + sp= + false + eval " + $sp + $sp + $sp + " +0:eval with empty command resets the status + + false + eval +0:eval with empty command resets the status diff --git a/dotfiles/system/.zsh/modules/Test/B06fc.ztst b/dotfiles/system/.zsh/modules/Test/B06fc.ztst new file mode 100644 index 0000000..922b001 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/B06fc.ztst @@ -0,0 +1,25 @@ +# Tests of fc command +%prep + + mkdir fc.tmp + cd fc.tmp + print 'fc -l foo' >fcl + +%test + $ZTST_testdir/../Src/zsh -f ./fcl +1:Checking that fc -l foo doesn't core dump when history is empty +?./fcl:fc:1: event not found: foo + + PS1='%% ' $ZTST_testdir/../Src/zsh +Z -fsi <<< $'fc -p /dev/null 0 0\n:' +0:Checking that fc -p doesn't core dump when history size is zero +*?*%* + + PS1='%% ' $ZTST_testdir/../Src/zsh +Z -fsi <<< 'fc -p /dev/null a 0' +1:Checking that fc -p rejects non-integer history size +*?*% fc: HISTSIZE must be an integer +*?*%* + + PS1='%% ' $ZTST_testdir/../Src/zsh +Z -fsi <<< 'fc -p /dev/null 0 a' +1:Checking that fc -p rejects non-integer history save size +*?*% fc: SAVEHIST must be an integer +*?*%* diff --git a/dotfiles/system/.zsh/modules/Test/B07emulate.ztst b/dotfiles/system/.zsh/modules/Test/B07emulate.ztst new file mode 100644 index 0000000..2de097e --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/B07emulate.ztst @@ -0,0 +1,253 @@ +# Test the "emulate" builtin and related functions. + +%prep + + isset() { + print -n "${1}: " + if [[ -o $1 ]]; then print yes; else print no; fi + } + showopts() { + # Set for Bourne shell emulation + isset shwordsplit + # Set in native mode and unless "emulate -R" is in use + isset banghist + } + cshowopts() { + showopts + # Show a csh option, too + isset cshnullglob + } + +%test + + (print Before + showopts + fn() { + emulate sh + } + fn + print After + showopts) +0:Basic use of emulate +>Before +>shwordsplit: no +>banghist: yes +>After +>shwordsplit: yes +>banghist: yes + + fn() { + emulate -L sh + print During + showopts + } + print Before + showopts + fn + print After + showopts +0:Use of emulate -L +>Before +>shwordsplit: no +>banghist: yes +>During +>shwordsplit: yes +>banghist: yes +>After +>shwordsplit: no +>banghist: yes + + (print Before + showopts + emulate -R sh + print After + showopts) +0:Use of emulate -R +>Before +>shwordsplit: no +>banghist: yes +>After +>shwordsplit: yes +>banghist: no + + print Before + showopts + emulate sh -c 'print During; showopts' + print After + showopts +0:Use of emulate -c +>Before +>shwordsplit: no +>banghist: yes +>During +>shwordsplit: yes +>banghist: yes +>After +>shwordsplit: no +>banghist: yes + + print Before + showopts + emulate -R sh -c 'print During; showopts' + print After + showopts +0:Use of emulate -R -c +>Before +>shwordsplit: no +>banghist: yes +>During +>shwordsplit: yes +>banghist: no +>After +>shwordsplit: no +>banghist: yes + + print Before + showopts + emulate -R sh -c 'shshowopts() { showopts; }' + print After definition + showopts + print In sticky emulation + shshowopts + print After sticky emulation + showopts +0:Basic sticky function emulation +>Before +>shwordsplit: no +>banghist: yes +>After definition +>shwordsplit: no +>banghist: yes +>In sticky emulation +>shwordsplit: yes +>banghist: no +>After sticky emulation +>shwordsplit: no +>banghist: yes + + print Before + cshowopts + emulate -R sh -c 'shshowopts() { cshowopts; }' + emulate csh -c 'cshshowopts() { + cshowopts + print In nested sh emulation + shshowopts + }' + print After definition + cshowopts + print In sticky csh emulation + cshshowopts + print After sticky emulation + cshowopts +0:Basic sticky function emulation +>Before +>shwordsplit: no +>banghist: yes +>cshnullglob: no +>After definition +>shwordsplit: no +>banghist: yes +>cshnullglob: no +>In sticky csh emulation +>shwordsplit: no +>banghist: yes +>cshnullglob: yes +>In nested sh emulation +>shwordsplit: yes +>banghist: no +>cshnullglob: no +>After sticky emulation +>shwordsplit: no +>banghist: yes +>cshnullglob: no + + isalp() { if [[ -o alwayslastprompt ]]; then print on; else print off; fi; } + emulate sh -c 'shfunc_inner() { setopt alwayslastprompt; }' + emulate csh -c 'cshfunc_inner() { setopt alwayslastprompt; }' + emulate sh -c 'shfunc_outer() { + unsetopt alwayslastprompt; + shfunc_inner; + isalp + unsetopt alwayslastprompt + cshfunc_inner + isalp + }' + shfunc_outer +0:Sticky emulation not triggered if sticky emulation unchanged +>on +>off + + ( + setopt ignorebraces + emulate zsh -o extendedglob -c ' + [[ -o ignorebraces ]] || print "Yay, ignorebraces was reset" + [[ -o extendedglob ]] && print "Yay, extendedglob is set" + ' + ) +0:emulate -c with options +>Yay, ignorebraces was reset +>Yay, extendedglob is set + + ( + setopt ignorebraces + emulate zsh -o extendedglob + [[ -o ignorebraces ]] || print "Yay, ignorebraces is no longer set" + [[ -o extendedglob ]] && print "Yay, extendedglob is set" + ) +0:emulate with options but no -c +>Yay, ignorebraces is no longer set +>Yay, extendedglob is set + + emulate zsh -o fixallmybugs 'print This was executed, bad' +1:emulate -c with incorrect options +?(eval):emulate:1: no such option: fixallmybugs + + emulate zsh -c ' + func() { [[ -o extendedglob ]] || print extendedglob is off } + ' + func + emulate zsh -o extendedglob -c ' + func() { [[ -o extendedglob ]] && print extendedglob is on } + ' + func +0:options specified alongside emulation are also sticky +>extendedglob is off +>extendedglob is on + + emulate zsh -o extendedglob -c ' + func_inner() { setopt nobareglobqual } + ' + emulate zsh -o extendedglob -c ' + func_outer() { + func_inner + [[ -o bareglobqual ]] || print bareglobqual was turned off + [[ -o extendedglob ]] && print extendedglob is on, though + } + ' + [[ -o extendedglob ]] || print extendedglob is initially off + func_outer +0:options propagate between identical emulations +>extendedglob is initially off +>bareglobqual was turned off +>extendedglob is on, though + + emulate zsh -o extendedglob -c ' + func_inner() { setopt nobareglobqual } + ' + emulate zsh -o extendedglob -o cbases -c ' + func_outer() { + func_inner + [[ -o bareglobqual ]] && print bareglobqual is still on + [[ -o extendedglob ]] && print extendedglob is on, too + } + ' + [[ -o extendedglob ]] || print extendedglob is initially off + func_outer +0:options do not propagate between different emulations +>extendedglob is initially off +>bareglobqual is still on +>extendedglob is on, too + + emulate sh -c '[[ a == a ]]' +0:regression test for POSIX_ALIASES reserved words +F:Some reserved tokens are handled in alias expansion diff --git a/dotfiles/system/.zsh/modules/Test/B08shift.ztst b/dotfiles/system/.zsh/modules/Test/B08shift.ztst new file mode 100644 index 0000000..0aa9226 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/B08shift.ztst @@ -0,0 +1,33 @@ +# Test the shift builtin. + +%test + + set -- one two three four five six seven eight nine ten + shift + print $* + shift 2 + print $* + shift -p 3 + print $* + shift -p + print $* +0:shifting positional parameters +>two three four five six seven eight nine ten +>four five six seven eight nine ten +>four five six seven +>four five six + + array=(yan tan tether mether pip azer sezar akker conter dick) + shift 2 array + print $array + shift array + print $array + shift -p 3 array + print $array + shift -p array + print $array +0:shifting array +>tether mether pip azer sezar akker conter dick +>mether pip azer sezar akker conter dick +>mether pip azer sezar +>mether pip azer diff --git a/dotfiles/system/.zsh/modules/Test/B09hash.ztst b/dotfiles/system/.zsh/modules/Test/B09hash.ztst new file mode 100644 index 0000000..7b5dfb4 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/B09hash.ztst @@ -0,0 +1,79 @@ +# The hash builtin is most used for the command hash table, which is +# populated automatically. This is therefore highly system specific, +# so mostly we'll test with the directory hash table: the logic is +# virtually identical but with the different table, and furthermore +# the shell doesn't care whether the directory exists unless you refer +# to it in a context that needs one. + +%prep + populate_hash() { + hash -d one=/first/directory + hash -d two=/directory/the/second + hash -d three=/noch/ein/verzeichnis + hash -d four=/bored/with/this/now + } + +%test + + hash -d +0:Directory hash initially empty + + populate_hash + hash -d +0:Populating directory hash and output with sort +>four=/bored/with/this/now +>one=/first/directory +>three=/noch/ein/verzeichnis +>two=/directory/the/second + + hash -rd + hash -d +0:Empty hash + + populate_hash + hash -d +0:Refill hash +>four=/bored/with/this/now +>one=/first/directory +>three=/noch/ein/verzeichnis +>two=/directory/the/second + + hash -dL +0:hash -L option +>hash -d four=/bored/with/this/now +>hash -d one=/first/directory +>hash -d three=/noch/ein/verzeichnis +>hash -d two=/directory/the/second + + hash -dm 't*' +0:hash -m option +>three=/noch/ein/verzeichnis +>two=/directory/the/second + + hash -d five=/yet/more six=/here/we/go seven=/not/yet/eight + hash -d +0:Multiple assignments +>five=/yet/more +>four=/bored/with/this/now +>one=/first/directory +>seven=/not/yet/eight +>six=/here/we/go +>three=/noch/ein/verzeichnis +>two=/directory/the/second + + hash -d one two three +0:Multiple arguments with no assignment not in verbose mode + + hash -vd one two three +0:Multiple arguments with no assignment in verbose mode +>one=/first/directory +>two=/directory/the/second +>three=/noch/ein/verzeichnis + + hash -d t-t=/foo + i="~t-t" + print ~t-t/bar + print ${~i}/rab +0:Dashes are untokenized in directory hash names +>/foo/bar +>/foo/rab diff --git a/dotfiles/system/.zsh/modules/Test/C01arith.ztst b/dotfiles/system/.zsh/modules/Test/C01arith.ztst new file mode 100644 index 0000000..61da763 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/C01arith.ztst @@ -0,0 +1,422 @@ +# Tests corresponding to the texinfo node `Arithmetic Evaluation' + +%test + + integer light there + (( light = 42 )) && + let 'there = light' && + print $(( there )) +0:basic integer arithmetic +>42 + + float light there + integer rnd + (( light = 3.1415 )) && + let 'there = light' && + print -- $(( rnd = there * 10000 )) +# save rounding problems by converting to integer +0:basic floating point arithmetic +>31415 + + integer rnd + (( rnd = ((29.1 % 13.0 * 10) + 0.5) )) + print $rnd +0:Test floating point modulo function +>31 + + print $(( 0x10 + 0X01 + 2#1010 )) +0:base input +>27 + + float light + (( light = 4 )) + print $light + typeset -F light + print $light +0:conversion to float +>4.000000000e+00 +>4.0000000000 + + integer i + (( i = 32.5 )) + print $i +0:conversion to int +>32 + + integer i + (( i = 4 - - 3 * 7 << 1 & 7 ^ 1 | 16 ** 2 )) + print $i +0:precedence (arithmetic) +>1591 + + fn() { + setopt localoptions c_precedences + integer i + (( i = 4 - - 3 * 7 << 1 & 7 ^ 1 | 16 ** 2 )) + print $i + } + fn +0:precedence (arithmetic, with C_PRECEDENCES) +>259 + + print $(( 1 < 2 || 2 < 2 && 3 > 4 )) +0:precedence (logical) +>1 + + print $(( 1 + 4 ? 3 + 2 ? 4 + 3 ? 5 + 6 ? 4 * 8 : 0 : 0 : 0 : 0 )) +0:precedence (ternary) +>32 + + print $(( 3 ? 2 )) +1:parsing ternary (1) +?(eval):1: bad math expression: ':' expected + + print $(( 3 ? 2 : 1 : 4 )) +1:parsing ternary (2) +?(eval):1: bad math expression: ':' without '?' + + print $(( 0, 4 ? 3 : 1, 5 )) +0:comma operator +>5 + + foo=000 + print $(( ##A + ##\C-a + #foo + $#foo )) +0:#, ## and $# +>117 + + print $((##)) +1:## without following character +?(eval):1: bad math expression: character missing after ## + + print $((## )) +0:## followed by a space +>32 + + integer i + (( i = 3 + 5 * 1.75 )) + print $i +0:promotion to float +>11 + + typeset x && + (( x = 3.5 )) && + print $x && + (( x = 4 )) && + print $x +0:use of scalars to store integers and floats +>3.5 +>4 + + (( newarray[unsetvar] = 1 )) +2:error using unset variable as index +?(eval):1: newarray: assignment to invalid subscript range + + integer setvar=1 + (( newarray[setvar]++ )) + (( newarray[setvar]++ )) + print ${(t)newarray} ${#newarray} ${newarray[1]} +0:setting array elements in math context +>array 1 2 + + xarr=() + (( xarr = 3 )) + print ${(t)xarr} $xarr +0:converting type from array +>integer 3 + + print $(( 13 = 42 )) +1:bad lvalue +?(eval):1: bad math expression: lvalue required + + x=/bar + (( x = 32 )) + print $x +0:assigning to scalar which contains non-math string +>32 + + print $(( )) +0:empty math parse e.g. $(( )) acts like a zero +>0 + + print $(( a = )) +1:empty assignment +?(eval):1: bad math expression: operand expected at end of string + + print $(( 3, )) +1:empty right hand of comma +?(eval):1: bad math expression: operand expected at end of string + + print $(( 3,,4 )) +1:empty middle of comma +?(eval):1: bad math expression: operand expected at `,4 ' + + print $(( (3 + 7, 4), 5 )) +0:commas and parentheses, part 1 +>5 + + print $(( 5, (3 + 7, 4) )) +0:commas and parentheses, part 1 +>4 + + print $(( 07.5 )) + (setopt octalzeroes; print $(( 09.5 ))) +0:leading zero doesn't affect floating point +>7.5 +>9.5 + + (setopt octalzeroes; print $(( 09 ))) +1:octalzeroes rejects invalid constants +?(eval):1: bad math expression: operator expected at `9 ' + + (setopt octalzeroes; print $(( 08#77 ))) +0:octalzeroes doesn't affect bases +>63 + + print $(( 36#z )) +0:bases up to 36 work +>35 + + print $(( 37#z )) +1:bases beyond 36 don't work +?(eval):1: invalid base (must be 2 to 36 inclusive): 37 + + print $(( 3 + "fail" )) +1:parse failure in arithmetic +?(eval):1: bad math expression: operand expected at `"fail" ' + + alias 3=echo + print $(( 3 + "OK"); echo "Worked") +0:not a parse failure because not arithmetic +>+ OK Worked + + fn() { + emulate -L zsh + print $(( [#16] 255 )) + print $(( [##16] 255 )) + setopt cbases + print $(( [#16] 255 )) + print $(( [##16] 255 )) + } + fn +0:doubled # in base removes radix +>16#FF +>FF +>0xFF +>FF + + array=(1) + x=0 + (( array[++x]++ )) + print $x + print $#array + print $array +0:no double increment for subscript +>1 +>1 +>2 + + # This is a bit naughty... the value of array + # isn't well defined since there's no sequence point + # between the increments of x, however we just want + # to be sure that in this case, unlike the above, + # x does get incremented twice. + x=0 + array=(1 2) + (( array[++x] = array[++x] + 1 )) + print $x +0:double increment for repeated expression +>2 + + # Floating point. Default precision should take care of rounding errors. + print $(( 1_0.000_000e0_1 )) + # Integer. + print $(( 0x_ff_ff_ )) + # _ are parts of variable names that don't start with a digit + __myvar__=42 + print $(( __myvar__ + $__myvar__ )) + # _ is not part of variable name that does start with a digit + # (which are substituted before math eval) + set -- 6 + print $(( $1_000_000 )) + # Underscores in expressions with no whitespace + print $(( 3_000_+4_000_/2 )) + # Underscores may appear in the base descriptor, for what it's worth... + print $(( 1_6_#f_f_ )) +0:underscores in math constants +>100. +>65535 +>84 +>6000000 +>5000 +>255 + + # Force floating point. + for expr in "3/4" "0x100/0x200" "0x30/0x10"; do + print $(( $expr )) + setopt force_float + print $(( $expr )) + unsetopt force_float + done +0:Forcing floating point constant evaluation, or not. +>0 +>0.75 +>0 +>0.5 +>3 +>3. + + print $(( 0x30 + 0.5 )) + print $(( 077 + 0.5 )) + (setopt octalzeroes; print $(( 077 + 0.5 )) ) +0:Mixed float and non-decimal integer constants +>48.5 +>77.5 +>63.5 + + underscore_integer() { + setopt cbases localoptions + print $(( [#_] 1000000 )) + print $(( [#16_] 65536 )) + print $(( [#16_4] 65536 * 32768 )) + } + underscore_integer +0:Grouping output with underscores: integers +>1_000_000 +>0x10_000 +>0x8000_0000 + + print $(( [#_] (5. ** 10) / 16. )) +0:Grouping output with underscores: floating point +>610_351.562_5 + + env SHLVL=1+RANDOM $ZTST_testdir/../Src/zsh -f -c 'print $SHLVL' +0:Imported integer functions are not evaluated +>2 + + print $(( 0b0 + 0b1 + 0b11 + 0b110 )) +0:Binary input +>10 + + print $(( 0b2 )) +1:Binary numbers don't tend to have 2's in +?(eval):1: bad math expression: operator expected at `2 ' +# ` for emacs shell mode + + integer varassi + print $(( varassi = 5.5 / 2.0 )) + print $varassi +0:Integer variable assignment converts result to integer +>2 +>2 +# It's hard to test for integer to float. + + integer ff1=3 ff2=4 + print $(( ff1/ff2 )) + setopt force_float + print $(( ff1/ff2 )) + unsetopt force_float +0:Variables are forced to floating point where necessary +# 0.75 is exactly representable, don't expect rounding error. +>0 +>0.75 + + # The following tests for a bug that only happens when + # backing up over input read a line at a time, so we'll + # read the input from stdin. + $ZTST_testdir/../Src/zsh -f <<<' + print $((echo first command + ); echo second command) + print third command + ' +0:Backing up a line of input when finding out it's not arithmetic +>first command second command +>third command + + $ZTST_testdir/../Src/zsh -f <<<' + print $((3 + + 4)) + print next line + ' +0:Not needing to back up a line when reading multiline arithmetic +>7 +>next line + + $ZTST_testdir/../Src/zsh -f <<<' + print $((case foo in + bar) + echo not this no, no + ;; + foo) + echo yes, this one + ;; + esac) + print after case in subshell) + ' +0:Non-arithmetic subst with command subsitution parse from hell +>yes, this one after case in subshell + + print "a$((echo one subst) + (echo two subst))b" +0:Another tricky case that is actually a command substitution +>aone subst +>two substb + + print "x$((echo one frob); (echo two frob))y" +0:Same on a single line +>xone frob +>two froby + + # This case actually only works by accident: if it wasn't for the + # unbalanced parenthesis this would be a valid math substitution. + # Hence it's definitely not recommended code. However, it does give + # the algorithm an extra check. + print $((case foo in + foo) + print Worked OK + ;; + esac)) +0:Would-be math expansion with extra parenthesis making it a cmd subst +>Worked OK + + (setopt extendedglob + set -- 32.463 + print ${$(( $1 * 100 ))%%.[0-9]#}) +0:Arithmetic substitution nested in parameter substitution +>3246 + + print $((`:`)) +0:Null string in arithmetic evaluation after command substitution +>0 + + print $(( 1 + $(( 2 + 3 )) )) + print $(($((3+4)))) + print $((1*$((2*$((3))*4))*5)) +0:Nested math substitutions. Yes, I know, very useful. +>6 +>7 +>120 + + foo="(1)" + print $((foo)) + print $(($foo)) + print $(((2))) + foo="3)" + (print $((foo))) 2>&1 + (print $(($foo))) 2>&1 +1: Good and bad trailing parentheses +>1 +>1 +>2 +>(eval):6: bad math expression: unexpected ')' +>(eval):7: bad math expression: unexpected ')' + + unset number + (( number = 3 )) + print ${(t)number} + unset number + (setopt posix_identifiers + (( number = 3 )) + print ${(t)number}) +0:type of variable when created in arithmetic context +>integer +>scalar diff --git a/dotfiles/system/.zsh/modules/Test/C02cond.ztst b/dotfiles/system/.zsh/modules/Test/C02cond.ztst new file mode 100644 index 0000000..3852501 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/C02cond.ztst @@ -0,0 +1,448 @@ +# Tests corresponding to the texinfo node `Conditional Expressions' + +%prep + + umask 077 + + mkdir cond.tmp + + cd cond.tmp + + typeset -gi isnfs + [[ "$(find . -prune -fstype nfs 2>/dev/null)" == "." ]] && isnfs=1 + if (( isnfs )) && + (cd -q ${ZTST_tmp} >/dev/null 2>&1 && + [[ "$(find . -prune -fstype nfs 2>/dev/null)" != "." ]]); then + filetmpprefix=${ZTST_tmp}/condtest-$$- + isnfs=0 + else + filetmpprefix= + fi + newnewnew=${filetmpprefix}newnewnew + unmodified=${filetmpprefix}unmodified + zlnfs=${filetmpprefix}zlnfs + + touch $unmodified + + touch zerolength + chgrp $EGID zerolength + + touch $zlnfs + chgrp $EGID $zlnfs + + print 'Garbuglio' >nonzerolength + + mkdir modish + chgrp $EGID modish + + chmod 7710 modish # g+xs,u+s,+t + chmod g+s modish # two lines combined work around chmod bugs + + touch unmodish + chmod 000 unmodish + + print 'MZ' > cmd.exe + chmod +x cmd.exe +%test + + [[ -a zerolength && ! -a nonexistent ]] +0:-a cond + + # Find a block special file system. This is a little tricky. + block=$(find /dev(|ices)/ -type b -print) + if [[ -n $block ]]; then + [[ -b $block[(f)1] && ! -b zerolength ]] + else + print -u$ZTST_fd 'Warning: Not testing [[ -b blockdevice ]] (no devices found)' + [[ ! -b zerolength ]] + fi +0D:-b cond + + # Use hardcoded /dev/tty because globbing inside /dev fails on Cygwin + char=/dev/tty + [[ -c $char && ! -c $zerolength ]] +0:-c cond + + [[ -d . && ! -d zerolength ]] +0:-d cond + + [[ -e zerolength && ! -e nonexistent ]] +0:-e cond + + if [[ -n $block ]]; then + [[ -f zerolength && ! -f cond && ! -f $char && ! -f $block[(f)1] && ! -f . ]] + else + print -u$ZTST_fd 'Warning: Not testing [[ -f blockdevice ]] (no devices found)' + [[ -f zerolength && ! -f cond && ! -f $char && ! -f . ]] + fi +0:-f cond + + [[ -g modish && ! -g zerolength ]] +0:-g cond + + ln -s zerolength link + [[ -h link && ! -h zerolength ]] +0:-h cond + + [[ -k modish && ! -k zerolength ]] +0:-k cond + + foo=foo + bar= + [[ -n $foo && ! -n $bar && ! -n '' ]] +0:-n cond + + [[ -o rcs && ! -o norcs && -o noerrexit && ! -o errexit ]] +0:-o cond + + if ! grep '#define HAVE_FIFOS' $ZTST_testdir/../config.h; then + print -u$ZTST_fd 'Warning: Not testing [[ -p pipe ]] (FIFOs not supported)' + [[ ! -p zerolength ]] + else + if whence mkfifo && mkfifo pipe || mknod pipe p; then + [[ -p pipe && ! -p zerolength ]] + else + print -u$ZTST_fd 'Warning: Not testing [[ -p pipe ]] (cannot create FIFO)' + [[ ! -p zerolength ]] + fi + fi +0dD:-p cond + + 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 +0:-r cond + + [[ -s nonzerolength && ! -s zerolength ]] +0:-s cond + +# no simple way of guaranteeing test for -t + + [[ -u modish && ! -u zerolength ]] +0:-u cond + + [[ -x cmd.exe && ! -x zerolength ]] +0:-x cond + + [[ -z $bar && -z '' && ! -z $foo ]] +0:-z cond + + [[ -L link && ! -L zerolength ]] +0:-L cond + +# hard to guarantee a file not owned by current uid + [[ -O zerolength ]] +0:-O cond + + [[ -G zerolength ]] +0:-G cond + +# 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 + 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" + 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. + + [[ $newnewnew -nt $zlnfs && ! ($unmodified -nt $zlnfs) ]] +0:-nt cond + + [[ $zlnfs -ot $newnewnew && ! ($zlnfs -ot $unmodified) ]] +0:-ot cond + + [[ link -ef zerolength && ! (link -ef nonzerolength) ]] +0:-ef cond + + [[ foo = foo && foo != bar && foo == foo && foo != '' ]] +0:=, == and != conds + + [[ bar < foo && foo > bar ]] +0:< and > conds + + [[ $(( 3 + 4 )) -eq 0x07 && $(( 5 * 2 )) -ne 0x10 ]] +0:-eq and -ne conds + + [[ 3 -lt 04 && 05 -gt 2 ]] +0:-lt and -gt conds + + [[ 3 -le 3 && ! (4 -le 3) ]] +0:-le cond + + [[ 3 -ge 3 && ! (3 -ge 4) ]] +0:-ge cond + + [[ 1 -lt 2 || 2 -lt 2 && 3 -gt 4 ]] +0:|| and && in conds + + if ! grep '#define PATH_DEV_FD' $ZTST_testdir/../config.h; then + print -u$ZTST_fd "Warning: not testing [[ -e /dev/fd/0 ]] (/dev/fd not supported)" + true + else + [[ -e /dev/fd/0 ]] + fi +0dD:/dev/fd support in conds handled by access + + if ! grep '#define PATH_DEV_FD' $ZTST_testdir/../config.h; then + print -u$ZTST_fd "Warning: not testing [[ -O /dev/fd/0 ]] (/dev/fd not supported)" + true + else + [[ -O /dev/fd/0 ]] + fi +0dD:/dev/fd support in conds handled by stat + + [[ ( -z foo && -z foo ) || -z foo ]] +1:complex conds with skipping + + [ '' != bar -a '' = '' ] +0:strings with `[' builtin + + [ `echo 0` -lt `echo 1` ] +0:substitution in `[' builtin + + [ -n foo scrimble ] +2:argument checking for [ builtin +?(eval):[:1: too many arguments + + test -n foo scramble +2:argument checking for test builtin +?(eval):test:1: too many arguments + + [ -n foo scrimble scromble ] +2:argument checking for [ builtin +?(eval):[:1: too many arguments + + test -n foo scramble scrumble +2:argument checking for test builtin +?(eval):test:1: too many arguments + + [ -n foo -a -n bar scrimble ] +2:argument checking for [ builtin +?(eval):[:1: too many arguments + + test -n foo -a -z "" scramble +2:argument checking for test builtin +?(eval):test:1: too many arguments + + fn() { + # careful: first file must exist to trigger bug + [[ -e $unmodified ]] || print Where\'s my file\? + [[ $unmodified -nt NonExistentFile ]] + print status = $? + } + fn +0:-nt shouldn't abort on non-existent files +>status = 1 + + str='string' empty='' + [[ -v IFS && -v str && -v empty && ! -v str[3] && ! -v not_a_variable ]] +0:-v cond + + arr=( 1 2 3 4 ) empty=() + [[ -v arr && -v arr[1,4] && -v arr[1] && -v arr[4] && -v arr[-4] && + -v arr[(i)3] && ! -v arr[(i)x] && + ! -v arr[0] && ! -v arr[5] && ! -v arr[-5] && ! -v arr[2][1] && + ! -v arr[3]extra && -v empty && ! -v empty[1] ]] +0:-v cond with array + + typeset -A assoc=( key val num 4 ) + [[ -v assoc && -v assoc[key] && -v assoc[(i)*] && -v assoc[(I)*] && + ! -v assoc[x] && ! -v assoc[key][1] ]] +0:-v cond with association + + () { [[ -v 0 && -v 1 && -v 2 && ! -v 3 ]] } arg '' +0:-v cond with positional parameters + +# core dumps on failure + if zmodload zsh/regex 2>/dev/null; then + echo >regex_test.sh 'if [[ $# = 1 ]]; then + if [[ $1 =~ /?[^/]+:[0-9]+:$ ]]; then + : + fi + fi + exit 0' + $ZTST_testdir/../Src/zsh -f ./regex_test.sh + fi +0:regex tests shouldn't crash + + if zmodload zsh/regex 2>/dev/null; then + ( # subshell in case coredump test failed + string="this has stuff in it" + bad_regex=0 + if [[ $string =~ "h([a-z]*) s([a-z]*) " ]]; then + if [[ "$MATCH $MBEGIN $MEND" != "has stuff 6 15" ]]; then + print -r "regex variables MATCH MBEGIN MEND: + '$MATCH $MBEGIN $MEND' + should be: + 'has stuff 6 15'" + bad_regex=1 + else + results=("as 7 8" "tuff 11 14") + for i in 1 2; do + if [[ "$match[$i] $mbegin[$i] $mend[$i]" != $results[i] ]]; then + print -r "regex variables match[$i] mbegin[$i] mend[$i]: + '$match[$i] $mbegin[$i] $mend[$i]' + should be + '$results[$i]'" + bad_regex=1 + break + fi + done + fi + (( bad_regex )) || print OK + else + print -r "regex failed to match '$string'" + fi + ) + else + # if it didn't load, tough, but not a test error + ZTST_skip="regexp library not found." + fi +0:MATCH, MBEGIN, MEND, match, mbegin, mend +>OK + + if zmodload zsh/regex 2>/dev/null; then + ( # subshell because regex module may dump core, see above + if [[ a =~ a && b == b ]]; then + print OK + else + print "regex =~ inverted following test" + fi + ) + else + # not a test error + ZTST_skip="regexp library not found." + fi +0:regex infix operator should not invert following conditions +>OK + + [[ -fail badly ]] +2:Error message for unknown prefix condition +?(eval):1: unknown condition: -fail + + [[ really -fail badly ]] +2:Error message for unknown infix condition +?(eval):1: unknown condition: -fail + + crashme() { + if [[ $1 =~ ^http:* ]] + then + url=${1#*=} + fi + } + which crashme +0:Regression test for examining code with regular expression match +>crashme () { +> if [[ $1 =~ ^http:* ]] +> then +> url=${1#*=} +> fi +>} + + weirdies=( + '! -a !' + '! -o !' + '! -a' + '! -o' + '! -a ! -a !' + '! = !' + '! !' + '= -a o' + '! = -a o') + for w in $weirdies; do + eval test $w + print $? + done +0:test compatability weirdness: treat ! as a string sometimes +>0 +>0 +>1 +>0 +>0 +>0 +>1 +>0 +>1 + + foo='' + [[ $foo ]] || print foo is empty + foo=full + [[ $foo ]] && print foo is full +0:bash compatibility with single [[ ... ]] argument +>foo is empty +>foo is full + + test -z \( || print Not zero 1 + test -z \< || print Not zero 2 + test -n \( && print Not zero 3 + test -n \) && print Not zero 4 + [ -n \> ] && print Not zero 5 + [ -n \! ] && print Not zero 6 +0:test with two arguments and a token +>Not zero 1 +>Not zero 2 +>Not zero 3 +>Not zero 4 +>Not zero 5 +>Not zero 6 + + [ '(' = ')' ] || print OK 1 + [ '((' = '))' ] || print OK 2 + [ '(' = '(' ] && print OK 3 + [ '(' non-empty-string ')' ] && echo OK 4 + [ '(' '' ')' ] || echo OK 5 +0:yet more old-fashioned test fix ups: prefer comparison to parentheses +>OK 1 +>OK 2 +>OK 3 +>OK 4 +>OK 5 + + fn() { [[ 'a' == 'b' || 'b' = 'c' || 'c' != 'd' ]] } + which -x2 fn +0: = and == appear as input +>fn () { +> [[ 'a' == 'b' || 'b' = 'c' || 'c' != 'd' ]] +>} + +%clean + # This works around a bug in rm -f in some versions of Cygwin + chmod 644 unmodish + for tmpfile in $newnewnew $unmodified $zlnfs; do + [[ -f $tmpfile ]] && rm -f $tmpfile + done diff --git a/dotfiles/system/.zsh/modules/Test/C03traps.ztst b/dotfiles/system/.zsh/modules/Test/C03traps.ztst new file mode 100644 index 0000000..7bc0b48 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/C03traps.ztst @@ -0,0 +1,761 @@ +# Tests for both trap builtin and TRAP* functions. + +%prep + + setopt localtraps + mkdir traps.tmp && cd traps.tmp + +%test + + fn1() { + trap 'print EXIT1' EXIT + fn2() { trap 'print EXIT2' EXIT; } + fn2 + } + fn1 +0:Nested `trap ... EXIT' +>EXIT2 +>EXIT1 + + fn1() { + TRAPEXIT() { print EXIT1; } + fn2() { TRAPEXIT() { print EXIT2; }; } + fn2 + } + fn1 +0: Nested TRAPEXIT +>EXIT2 +>EXIT1 + + fn1() { + trap 'print EXIT1' EXIT + fn2() { trap - EXIT; } + fn2 + } + fn1 +0:Nested `trap - EXIT' on `trap ... EXIT' +>EXIT1 + + fn1() { + TRAPEXIT() { print EXIT1; } + fn2() { trap - EXIT; } + fn2 + } + fn1 +0:Nested `trap - EXIT' on `TRAPEXIT' +>EXIT1 + +# We can't test an EXIT trap for the shell as a whole, because +# we're inside a function scope which we don't leave when the +# subshell exits. Not sure if that's the correct behaviour, but +# it's sort of consistent. + ( fn1() { trap 'print Function 1 going' EXIT; exit; print Not reached; } + fn2() { trap 'print Function 2 going' EXIT; fn1; print Not reached; } + fn2 + ) +0:EXIT traps on functions when exiting from function +>Function 1 going +>Function 2 going + +# $ZTST_exe is relative to the parent directory. +# We ought to fix this in ztst.zsh... + (cd .. + $ZTST_exe -fc 'TRAPEXIT() { print Exited.; }') +0:EXIT traps on a script +>Exited. + + trap - + trap + trap int INT + trap sigterm SIGTERM + trap quit 3 + trap +0: Outputting traps correctly +>trap -- int INT +>trap -- quit QUIT +>trap -- sigterm TERM + + fn1() { + trap - + trap + trap 'print INT1' INT + fn2() { trap 'print INT2' INT; trap; } + trap + fn2 + trap + } + fn1 +0: Nested `trap ... INT', not triggered +>trap -- 'print INT1' INT +>trap -- 'print INT2' INT +>trap -- 'print INT1' INT + + fn1() { + trap - + trap + TRAPINT() { print INT1; } + fn2() { TRAPINT() { print INT2; }; trap; } + trap + fn2 + trap + } + fn1 +0: Nested TRAPINT, not triggered +>TRAPINT () { +> print INT1 +>} +>TRAPINT () { +> print INT2 +>} +>TRAPINT () { +> print INT1 +>} + + fn1() { + trap - + trap 'print INT1' INT + fn2() { trap - INT; trap; } + trap + fn2 + trap + } + fn1 +0: Nested `trap - INT' on untriggered `trap ... INT' +>trap -- 'print INT1' INT +>trap -- 'print INT1' INT + +# Testing the triggering of traps here is very unpleasant. +# The delays are attempts to avoid race conditions, though there is +# no guarantee that they will work. Note the subtlety that the +# `sleep' in the function which receives the trap does *not* get the +# signal, only the parent shell, which is waiting for a SIGCHILD. +# (At least, that's what I think is happening.) Thus we have to wait at +# least the full two seconds to make sure we have got the output from the +# execution of the trap. + + print -u $ZTST_fd 'This test takes at least three seconds...' + fn1() { + trap 'print TERM1' TERM + fn2() { trap 'print TERM2; return 1' TERM; sleep 2; } + fn2 & + sleep 1 + kill -TERM $! + sleep 2 + } + fn1 +0: Nested `trap ... TERM', triggered on inner loop +>TERM2 + + print -u $ZTST_fd 'This test, too, takes at least three seconds...' + fn1() { + trap 'print TERM1; return 1' TERM + fn2() { trap 'print TERM2; return 1' TERM; } + fn2 + sleep 2 + } + fn1 & + sleep 1 + kill -TERM $! + sleep 2 +0: Nested `trap ... TERM', triggered on outer loop +>TERM1 + + TRAPZERR() { print error activated; } + fn() { print start of fn; false; print end of fn; } + fn + fn() { + setopt localoptions localtraps + unfunction TRAPZERR + print start of fn + false + print end of fn + } + fn + unfunction TRAPZERR + print finish +0: basic localtraps handling +>start of fn +>error activated +>end of fn +>start of fn +>end of fn +>finish + + TRAPZERR() { print 'ERR-or!'; } + f() { print f; false; } + t() { print t; } + f + f && t + t && f && true + t && f + testunset() { + setopt localtraps + unset -f TRAPZERR + print testunset + false + true + } + testunset + f + print status $? + unfunction TRAPZERR +0: more sophisticated error trapping +>f +>ERR-or! +>f +>t +>f +>t +>f +>ERR-or! +>testunset +>f +>ERR-or! +>status 1 + + f() { + setopt localtraps + TRAPWINCH() { print "Window changed. That wrecked the test."; } + } + f + f + functions TRAPWINCH +1:Unsetting ordinary traps with localtraps. + +# +# Returns from within traps are a perennial problem. +# The following two apply to returns in and around standard +# ksh-style traps. The intention is that a return value from +# within the function is preserved (i.e. statuses set by the trap +# are ignored) unless the trap explicitly executes `return', which makes +# it return from the enclosing function. +# + fn() { trap 'true' EXIT; return 1; } + fn +1: ksh-style EXIT traps preserve return value + + inner() { trap 'return 3' EXIT; return 2; } + outer() { inner; return 1; } + outer +3: ksh-style EXIT traps can force return status of enclosing function + +# Autoloaded traps are horrid, but unfortunately people expect +# them to work if we support them. + echo "print Running exit trap" >TRAPEXIT + ${${ZTST_exe##[^/]*}:-$ZTST_testdir/$ZTST_exe} -fc ' + fpath=(. $fpath) + autoload TRAPEXIT + print "Exiting, attempt 1" + exit + print "What?" + ' + ${${ZTST_exe##[^/]*}:-$ZTST_testdir/$ZTST_exe} -fc ' + fpath=(. $fpath) + autoload TRAPEXIT; + fn() { print Some function } + fn + print "Exiting, attempt 2" + exit + ' +0: autoloaded TRAPEXIT (exit status > 128 indicates an old bug is back) +>Exiting, attempt 1 +>Running exit trap +>Some function +>Exiting, attempt 2 +>Running exit trap + + print -u $ZTST_fd Another test that takes three seconds + gotsig=0 + signal_handler() { + echo "parent received signal" + gotsig=1 + } + child() { + sleep 1 + echo "child sending signal" + kill -15 $parentpid + sleep 2 + echo "child exiting" + exit 33 + } + parentpid=$$ + child & + childpid=$! + trap signal_handler 15 + echo "parent waiting" + wait $childpid + cstatus=$? + echo "wait #1 finished, gotsig=$gotsig, status=$cstatus" + gotsig=0 + wait $childpid + cstatus=$? + echo "wait #2 finished, gotsig=$gotsig, status=$cstatus" +0:waiting for trapped signal +>parent waiting +>child sending signal +>parent received signal +>wait #1 finished, gotsig=1, status=143 +>child exiting +>wait #2 finished, gotsig=0, status=33 + + fn1() { + setopt errexit + trap 'echo error1' ZERR + false + print Shouldn\'t get here 1a + } + fn2() { + setopt errexit + trap 'echo error2' ZERR + return 1 + print Shouldn\'t get here 2a + } + fn3() { + setopt errexit + TRAPZERR() { echo error3; } + false + print Shouldn\'t get here 3a + } + fn4() { + setopt errexit + TRAPZERR() { echo error4; } + return 1 + print Shouldn\'t get here 4a + } + (fn1; print Shouldn\'t get here 1b) + (fn2; print Shouldn\'t get here 2b) + (fn3; print Shouldn\'t get here 3b) + (fn4; print Shouldn\'t get here 4b) +1: Combination of ERR_EXIT and ZERR trap +>error1 +>error2 +>error3 +>error4 + + fn1() { TRAPZERR() { print trap; return 42; }; false; print Broken; } + (fn1) + print Working $? +0: Force return of containing function from TRAPZERR. +>trap +>Working 42 + + fn2() { trap 'print trap; return 42' ZERR; false; print Broken } + (fn2) + print Working $? +0: Return with non-zero status triggered from within trap '...' ZERR. +>trap +>Working 42 + + fn3() { TRAPZERR() { print trap; return 0; }; false; print OK this time; } + (fn3) + print Working $? +0: Normal return from TRAPZERR. +>trap +>OK this time +>Working 0 + + fn4() { trap 'print trap; return 0' ZERR; false; print Broken; } + (fn4) + print Working $? +0: Return with zero status triggered from within trap '...' ZERR. +>trap +>Working 0 + + { trap 'echo This subshell is exiting' EXIT; } | cat +0: EXIT trap set in current shell at left of pipeline +>This subshell is exiting + + ( trap 'echo This subshell is also exiting' EXIT; ) | cat +0: EXIT trap set in subshell at left of pipeline +>This subshell is also exiting + + ( trap 'echo Should only appear once at the end' EXIT + ( : trap reset here ) | cat + : trap not reset but not part of shell command list | cat + echo nothing after this should appear $( : trap reset here too) + ) +0: EXIT trap set in subshell reset in subsubshell +>nothing after this should appear +>Should only appear once at the end + + echo $( trap 'echo command substitution exited' EXIT ) +0: EXIT trap set in command substitution +>command substitution exited + + (cd ..; $ZTST_exe -fc 'setopt posixtraps; + TRAPEXIT() { print Exited; } + fn1() { trap; } + setopt localtraps # should be ignored by EXIT + fn2() { TRAPEXIT() { print No, really exited; } } + fn1 + fn2 + fn1') +0:POSIX_TRAPS option +>TRAPEXIT () { +> print Exited +>} +>TRAPEXIT () { +> print No, really exited +>} +>No, really exited + + (cd ..; $ZTST_exe -fc 'unsetopt posixtraps; + echo start program + emulate sh -c '\''testfn() { + echo start function + set -o | grep posixtraps + trap "echo EXIT TRAP TRIGGERED" EXIT + echo end function + }'\'' + testfn + echo program continuing + echo end of program') +0:POSIX_TRAPS effect on EXIT trap is sticky +>start program +>start function +>noposixtraps off +>end function +>program continuing +>end of program +>EXIT TRAP TRIGGERED + + (cd ..; $ZTST_exe -fc ' + echo entering program + emulate sh -c '\''trap "echo POSIX exit trap triggered" EXIT'\'' + fn() { + trap "echo native zsh function-local exit trap triggered" EXIT + echo entering native zsh function + } + fn + echo exiting program + ') +0:POSIX EXIT trap can have nested native mode EXIT trap +>entering program +>entering native zsh function +>native zsh function-local exit trap triggered +>exiting program +>POSIX exit trap triggered + + (cd ..; $ZTST_exe -fc ' + echo entering program + emulate sh -c '\''spt() { trap "echo POSIX exit trap triggered" EXIT; }'\'' + fn() { + trap "echo native zsh function-local exit trap triggered" EXIT + echo entering native zsh function + } + spt + fn + echo exiting program + ') +0:POSIX EXIT trap not replaced if defined within function +>entering program +>entering native zsh function +>native zsh function-local exit trap triggered +>exiting program +>POSIX exit trap triggered + + (set -e + printf "a\nb\n" | while read line + do + [[ $line = a* ]] || continue + ((ctr++)) + [[ $line = foo ]] + done + echo "ctr = $ctr" + ) +1:ERREXIT in loop with simple commands + + fn() { + emulate -L zsh + setopt errreturn + if false; then + false + print No. + else + print Oh, yes + fi + } + fn +0:ERR_RETURN not triggered in if condition +>Oh, yes + + fn() { + emulate -L zsh + setopt errreturn + if true; then + false + print No. + else + print No, no. + fi + } + fn +1:ERR_RETURN in "if" + + fn() { + emulate -L zsh + setopt errreturn + if false; then + print No. + else + false + print No, no. + fi + } + fn +1:ERR_RETURN in "else" branch (regression test) + + $ZTST_testdir/../Src/zsh -f =(<<<" + if false; then + : + else + if [[ -n '' ]]; then + a=2 + fi + print Yes + fi + ") +0:ERR_RETURN when false "if" is the first statement in an "else" (regression) +>Yes +F:Must be tested with a top-level script rather than source or function + + fn() { + emulate -L zsh + setopt errreturn + print before + false + print after + } + fn +1:ERR_RETURN, basic case +>before + + fn() { + emulate -L zsh + setopt errreturn + print before + ! true + ! false + print after + } + fn +0:ERR_RETURN with "!" +>before +>after + + fn() { + emulate -L zsh + setopt errreturn + print before + ! true + ! false + false + print after + } + fn +1:ERR_RETURN with "!" and a following false +>before + + fn() { + emulate -L zsh + setopt errreturn + print before + ! if true; then + false + fi + print after + } + fn +0:ERR_RETURN with "!" suppressed inside complex structure +>before +>after + + fn() { + emulate -L zsh + setopt errreturn + print before + if true; then + false + fi + print after + } + fn +1:ERR_RETURN with no "!" suppression (control case) +>before + + (setopt err_return + fn() { + print before-in + false && false + } + print before-out + fn + print after-out + ) +1:ERR_RETURN with "&&" in function (regression test) +>before-out +>before-in + + (setopt err_return + fn() { + print before-in + false && false + print after-in + } + print before-out + fn + print after-out + ) +0:ERR_RETURN not triggered on LHS of "&&" in function +>before-out +>before-in +>after-in +>after-out + + (setopt err_return + fn() { + print before-in + true && false + print after-in + } + print before-out + fn + print after-out + ) +1:ERR_RETURN triggered on RHS of "&&" in function +>before-out +>before-in + + (setopt err_exit + for x in y; do + false && true + done + print OK + ) +0:ERR_EXIT not triggered by status 1 at end of for +>OK + + (setopt err_exit + integer x=0 + while (( ! x++ )); do + false && true + done + print OK + ) +0:ERR_EXIT not triggered by status 1 at end of while +>OK + + (setopt err_exit + repeat 1; do + false && true + done + print OK + ) +0:ERR_EXIT not triggered by status 1 at end of repeat +>OK + + (setopt err_exit + if true; then + false && true + fi + print OK + ) +0:ERR_EXIT not triggered by status 1 at end of if +>OK + + (setopt err_exit + { + false && true + } + print OK + ) +0:ERR_EXIT not triggered by status 1 at end of { } +>OK + + (setopt err_exit + for x in y; do + false + done + print OK + ) +1:ERR_EXIT triggered by status 1 within for + + (setopt err_exit + integer x=0 + while (( ! x++ )); do + false + done + print OK + ) +1:ERR_EXIT triggered by status 1 within while + + (setopt err_exit + repeat 1; do + false + done + print OK + ) +1:ERR_EXIT triggered by status 1 within repeat + + (setopt err_exit + if true; then + false + fi + print OK + ) +1:ERR_EXIT triggered by status 1 within if + + (setopt err_exit + { + false + } + print OK + ) +1:ERR_EXIT triggered by status 1 within { } + + (setopt err_exit + () { + false && true + print Still functioning + false && true + } + print OK + ) +1:ERR_EXIT triggered by status 1 at end of anon func +>Still functioning + + if zmodload zsh/system 2>/dev/null; then + ( + trap 'echo TERM; exit 2' TERM + trap 'echo EXIT' EXIT + kill -s TERM "$sysparams[pid]" + echo 'FATAL: we should never get here!' 1>&2 + exit 1 + ) + else + ZTST_skip="zsh/system library not found." + fi +2:EXIT trap from TERM trap +>TERM +>EXIT + + # Should not get "hello" in the single quotes. + ( + trap "echo hello" EXIT; + { :; } | { read line; print "'$line'"; } + ) +0:EXIT trap not called in LHS of pipeline: Shell construct on LHS +>'' +>hello + + ( + trap "echo hello" EXIT; + cat </dev/null | { read line; print "'$line'"; } + ) +0:EXIT trap not called in LHS of pipeline: External command on LHS +>'' +>hello + +%clean + + rm -f TRAPEXIT diff --git a/dotfiles/system/.zsh/modules/Test/C04funcdef.ztst b/dotfiles/system/.zsh/modules/Test/C04funcdef.ztst new file mode 100644 index 0000000..0cf2b58 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/C04funcdef.ztst @@ -0,0 +1,502 @@ +%prep + + mkdir funcdef.tmp + cd funcdef.tmp + setopt chaselinks + cd . + unsetopt chaselinks + mydir=$PWD + +%test + + fn1() { return 1; } + fn2() { + fn1 + print $? + return 2 + } + fn2 +2:Basic status returns from functions +>1 + + fnz() { } + false + fnz +0:Empty function body resets status + + fn3() { return 3; } + fnstat() { print $?; } + fn3 + fnstat +0:Status is not reset on non-empty function body +>3 + + function f$$ () { + print regress expansion of function names + } + f$$ +0:Regression test: 'function f$$ () { ... }' +>regress expansion of function names + + function foo () print bar + foo +0:Function definition without braces +>bar + + functions -M m1 + m1() { (( $# )) } + print $(( m1() )) + print $(( m1(1) )) + print $(( m1(1,2) )) +0:User-defined math functions, argument handling +>0 +>1 +>2 + + functions -M m2 + m2() { + integer sum + local val + for val in $*; do + (( sum += $val )) + done + } + print $(( m2(1) )) + print $(( m2(1,3+3,4**2) )) +0:User-defined math functions, complex argument handling +>1 +>23 + + functions -M m3 1 2 + m3() { (( 1 )) } + print zero + (print $(( m3() ))) + print one + print $(( m3(1) )) + print two + print $(( m3(1,2) )) + print three + (print $(( m3(1,2,3) ))) +1:User-defined math functions, argument checking +>zero +>one +>1 +>two +>1 +>three +?(eval):4: wrong number of arguments: m3() +?(eval):10: wrong number of arguments: m3(1,2,3) + + functions -M m4 0 0 testmathfunc + functions -M m5 0 0 testmathfunc + testmathfunc() { + if [[ $0 = m4 ]]; then + (( 4 )) + else + (( 5 )) + fi + } + print $(( m4() )) + print $(( m5() )) +0:User-defined math functions, multiple interfaces +>4 +>5 + + command_not_found_handler() { + print "Great News! I've handled the command:" + print "$1" + print "with arguments:" + print -l ${argv[2,-1]} + } + ACommandWhichHadBetterNotExistOnTheSystem and some "really useful" args +0:Command not found handler, success +>Great News! I've handled the command: +>ACommandWhichHadBetterNotExistOnTheSystem +>with arguments: +>and +>some +>really useful +>args + +# ' deconfuse emacs + + command_not_found_handler() { + print "Your command:" >&2 + print "$1" >&2 + print "has gone down the tubes. Sorry." >&2 + return 42 + } + ThisCommandDoesNotExistEither +42:Command not found handler, failure +?Your command: +?ThisCommandDoesNotExistEither +?has gone down the tubes. Sorry. + + local variable=outside + print "I am $variable" + function { + local variable=inside + print "I am $variable" + } + print "I am $variable" + () { + local variable="inside again" + print "I am $variable" + } + print "I am $variable" +0:Anonymous function scope +>I am outside +>I am inside +>I am outside +>I am inside again +>I am outside + + integer i + for (( i = 0; i < 10; i++ )); do function { + case $i in + ([13579]) + print $i is odd + ;| + ([2468]) + print $i is even + ;| + ([2357]) + print $i is prime + ;; + esac + }; done +0:Anonymous function with patterns in loop +>1 is odd +>2 is even +>2 is prime +>3 is odd +>3 is prime +>4 is even +>5 is odd +>5 is prime +>6 is even +>7 is odd +>7 is prime +>8 is even +>9 is odd + + echo stuff in file >file.in + function { + sed 's/stuff/rubbish/' + } <file.in >file.out + cat file.out +0:Anonymous function redirection +>rubbish in file + + variable="Do be do" + print $variable + function { + print $variable + local variable="Da de da" + print $variable + function { + print $variable + local variable="Dum da dum" + print $variable + } + print $variable + } + print $variable +0:Nested anonymous functions +>Do be do +>Do be do +>Da de da +>Da de da +>Dum da dum +>Da de da +>Do be do + + () (cat $1 $2) <(print process expanded) =(print expanded to file) +0:Process substitution with anonymous functions +>process expanded +>expanded to file + + () { print This has arguments $*; } of all sorts; print After the function + function { print More stuff $*; } and why not; print Yet more +0:Anonymous function with arguments +>This has arguments of all sorts +>After the function +>More stuff and why not +>Yet more + + fn() { + (){ print Anonymous function 1 $*; } with args + function { print Anonymous function 2 $*; } with more args + print Following bit + } + functions fn +0:Text representation of anonymous function with arguments +>fn () { +> () { +> print Anonymous function 1 $* +> } with args +> () { +> print Anonymous function 2 $* +> } with more args +> print Following bit +>} + + touch yes no + () { echo $1 } (y|z)* + (echo here) + () { echo $* } some (y|z)* + () { echo empty };(echo here) +0:Anonymous function arguments and command arguments +>yes +>here +>some yes +>empty +>here + + if true; then f() { echo foo1; } else f() { echo bar1; } fi; f + if false; then f() { echo foo2; } else f() { echo bar2; } fi; f +0:Compatibility with other shells when not anonymous functions +>foo1 +>bar2 + + ( + setopt ignorebraces + fpath=(.) + print "{ echo OK }\n[[ -o ignorebraces ]] || print 'ignorebraces is off'" \ + >emufunctest + (autoload -z emufunctest; emufunctest) 2>&1 + emulate zsh -c 'autoload -Uz emufunctest' + emufunctest + [[ -o ignorebraces ]] && print 'ignorebraces is still on here' + ) +0:sticky emulation applies to autoloads and autoloaded function execution +>emufunctest:3: parse error near `\n' +>OK +>ignorebraces is off +>ignorebraces is still on here +#` (matching error message for editors parsing the file) + +# 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 +>anon func +>lsfoo + + print foo | () cat +0:Simple anonymous function should not simplify enclosing pipeline +>foo + + alias fooalias=barexpansion + funcwithalias() { echo $(fooalias); } + functions funcwithalias + barexpansion() { print This is the correct output.; } + funcwithalias +0:Alias expanded in command substitution does not appear expanded in text +>funcwithalias () { +> echo $(fooalias) +>} +>This is the correct output. + + unfunction command_not_found_handler # amusing but unhelpful + alias first='firstfn1 firstfn2' second='secondfn1 secondfn2' + function first second { print This is function $0; } + first + second + firstfn1 + secondfn1 +127:No alias expansion after "function" keyword +>This is function first +>This is function second +?(eval):6: command not found: firstfn1 +?(eval):7: command not found: secondfn1 + + ( + fpath=(.) + print "print oops was successfully autoloaded" >oops + oops() { eval autoload -X } + oops + which -x2 oops + ) +0:autoload containing eval +>oops was successfully autoloaded +>oops () { +> print oops was successfully autoloaded +>} + + ( + fpath=(.) + printf '%s\n' 'oops(){}' 'ninjas-earring(){}' 'oops "$@"' >oops + autoload oops + oops + whence -v oops + ) +0q:whence -v of zsh-style autoload +>oops is a shell function from $mydir/oops + + ( + fpath=(.) + mkdir extra + print 'print "I have been loaded by explicit path."' >extra/spec + autoload -Uz $PWD/extra/spec + spec + ) +0:autoload with explicit path +>I have been loaded by explicit path. + + ( + fpath=(.) + print 'print "I have been loaded by default path."' >def + autoload -Uz $PWD/extra/def + def + ) +1:autoload with explicit path with function in normal path, no -d +?(eval):5: def: function definition file not found + + ( + fpath=(.) + autoload -dUz $PWD/extra/def + def + ) +0:autoload with explicit path with function in normal path, with -d +>I have been loaded by default path. + + ( + cd extra + fpath=(.) + autoload -r spec + cd .. + spec + ) +0:autoload -r +>I have been loaded by explicit path. + + ( + cd extra + fpath=(.) + autoload -r def + cd .. + def + ) +0:autoload -r is permissive +>I have been loaded by default path. + + ( + cd extra + fpath=(.) + autoload -R def + ) +1:autoload -R is not permissive +?(eval):4: def: function definition file not found + + ( + spec() { autoload -XUz $PWD/extra; } + spec + ) +0:autoload -X with path +>I have been loaded by explicit path. + +# The line number 1 here and in the next test seems suspect, +# but this example proves it's not down to the new features +# being tested here. + ( + fpath=(.) + cod() { autoload -XUz; } + cod + ) +1:autoload -X with no path, failure +?(eval):1: cod: function definition file not found + + ( + fpath=(.) + def() { autoload -XUz $PWD/extra; } + def + ) +1:autoload -X with wrong path and no -d +?(eval):1: def: function definition file not found + + ( + fpath=(.) + def() { autoload -dXUz $PWD/extra; } + def + ) +0:autoload -dX with path +>I have been loaded by default path. + + ( + fpath=(.) + print 'loadthisfunc() { autoload -X }' >loadthisfunc_sourceme + print 'print Function was loaded correctly.' >loadthisfunc + source $PWD/loadthisfunc_sourceme + loadthisfunc + ) +0: autoload -X interaction with absolute filename used for source location +>Function was loaded correctly. + + ( + fpath=() + mkdir extra2 + for f in fun2a fun2b; do + print "print $f" >extra2/$f + done + repeat 3; do + autoload $PWD/extra2/fun2{a,b} $PWD/extra/spec + fun2a + fun2b + spec + unfunction fun2a fun2b spec + autoload $PWD/extra2/fun2{a,b} $PWD/extra/spec + spec + fun2b + fun2a + unfunction fun2a fun2b spec + done + ) +0: Exercise the directory name cache for autoloads +>fun2a +>fun2b +>I have been loaded by explicit path. +>I have been loaded by explicit path. +>fun2b +>fun2a +>fun2a +>fun2b +>I have been loaded by explicit path. +>I have been loaded by explicit path. +>fun2b +>fun2a +>fun2a +>fun2b +>I have been loaded by explicit path. +>I have been loaded by explicit path. +>fun2b +>fun2a + + not_trashed() { print This function was not trashed; } + autoload -Uz /foo/bar/not_trashed + not_trashed +0:autoload with absolute path doesn't trash loaded function +>This function was not trashed + + # keep spec from getting loaded in parent shell for simplicity + ( + if whence spec; then print spec already loaded >&2; exit 1; fi + autoload -Uz $PWD/spec + autoload -Uz $PWD/extra/spec + spec + ) +0:autoload with absolute path can be overridden if not yet loaded +>I have been loaded by explicit path. + + ( + if whence spec; then print spec already loaded >&2; exit 1; fi + autoload -Uz $PWD/extra/spec + autoload spec + spec + ) +0:autoload with absolute path not cancelled by bare autoload +>I have been loaded by explicit path. + +%clean + + rm -f file.in file.out diff --git a/dotfiles/system/.zsh/modules/Test/C05debug.ztst b/dotfiles/system/.zsh/modules/Test/C05debug.ztst new file mode 100644 index 0000000..9a8df1d --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/C05debug.ztst @@ -0,0 +1,159 @@ +%prep + + setopt localtraps + +%test + + unsetopt DEBUG_BEFORE_CMD + debug-trap-bug1() { + setopt localtraps + print "print bug file here" >bug-file + print "print this is line one + print this is line two + print this is line three + print and this is line fifty-nine." >bug-file2 + function debug_trap_handler { + print $functrace[1] + do_bug + } + function do_bug { + . ./bug-file + } + trap 'echo EXIT hit' EXIT + trap 'debug_trap_handler' DEBUG + . ./bug-file2 + } + debug-trap-bug1 +0: Relationship between traps and sources +>debug-trap-bug1:15 +>bug file here +>this is line one +>./bug-file2:1 +>bug file here +>this is line two +>./bug-file2:2 +>bug file here +>this is line three +>./bug-file2:3 +>bug file here +>and this is line fifty-nine. +>./bug-file2:4 +>bug file here +>debug-trap-bug1:16 +>bug file here +>EXIT hit + + cat >zsh-trapreturn-bug2 <<-'HERE' + cmd='./fdasfsdafd' + [[ -x $cmd ]] && rm $cmd + set -o DEBUG_BEFORE_CMD + trap '[[ $? -ne 0 ]] && exit 0' DEBUG + $cmd # invalid command + # Failure + exit 10 + HERE + $ZTST_testdir/../Src/zsh -f ./zsh-trapreturn-bug2 2>erroutput.dif + mystat=$? + ( + setopt extendedglob + print ${"$(< erroutput.dif)"%%:[^:]#: ./fdasfsdafd} + ) + (( mystat == 0 )) +0: trapreturn handling bug is properly fixed +>./zsh-trapreturn-bug2:5 + + fn() { + setopt localtraps localoptions debugbeforecmd + trap '(( LINENO == 4 )) && setopt errexit' DEBUG + print $LINENO three + print $LINENO four + print $LINENO five + [[ -o errexit ]] && print "Hey, ERREXIT is set!" + } + fn +1:Skip line from DEBUG trap +>3 three +>5 five + + # Assignments are a special case, since they use a simpler + # wordcode type, so we need to test skipping them separately. + fn() { + setopt localtraps localoptions debugbeforecmd + trap '(( LINENO == 4 )) && setopt errexit' DEBUG + x=three + x=four + print $LINENO $x + [[ -o errexit ]] && print "Hey, ERREXIT is set!" + } + fn +1:Skip assignment from DEBUG trap +>5 three + + fn() { + setopt localtraps localoptions debugbeforecmd + trap 'print $LINENO' DEBUG + [[ a = a ]] && print a is ok + } + fn +0:line numbers of complex sublists +>3 +>a is ok + + fn() { + setopt localtraps localoptions debugbeforecmd + trap 'print $LINENO' DEBUG + print before + x=' first + second + third' + print $x + } + fn +0:line numbers of multiline assignments +>3 +>before +>4 +>7 +> first +> second +> third + + fn() { + emulate -L zsh; setopt debugbeforecmd + trap 'print "$LINENO: '\''$ZSH_DEBUG_CMD'\''"' DEBUG + print foo && + print bar || + print rod + x=y + print $x + fn2() { echo wow } + fn2 + } + fn +0:ZSH_DEBUG_CMD in debug traps +>3: 'print foo && print bar || print rod' +>foo +>bar +>6: 'x=y ' +>7: 'print $x' +>y +>8: 'fn2 () { +> echo wow +>}' +>9: 'fn2' +>0: 'echo wow' +>wow + + foo() { + emulate -L zsh; setopt debugbeforecmd + trap '[[ $ZSH_DEBUG_CMD == *bar* ]] && return 2' DEBUG + echo foo + echo bar + } + foo +2:Status of forced return from eval-style DEBUG trap +>foo + +%clean + + rm -f bug-file bug-file2 erroutput.dif zsh-trapreturn-bug2 diff --git a/dotfiles/system/.zsh/modules/Test/D01prompt.ztst b/dotfiles/system/.zsh/modules/Test/D01prompt.ztst new file mode 100644 index 0000000..607ffb6 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/D01prompt.ztst @@ -0,0 +1,203 @@ +%prep + + mkdir prompt.tmp + cd prompt.tmp + mydir=$PWD + SHLVL=2 + setopt extendedglob + +%test + + hash -d mydir=$mydir + print -P ' %%%): %) + %%~: %~ + %%d: %d + %%1/: %1/ + %%h: %h + %%L: %L + %%M: %M + %%m: %m + %%n: %n + %%N: %N + %%i: %i + a%%{...%%}b: a%{%}b + ' +0q:Basic prompt escapes as shown. +> %): ) +> %~: ~mydir +> %d: $mydir +> %1/: ${mydir:t} +> %h: 0 +> %L: 2 +> %M: $HOST +> %m: ${HOST%%.*} +> %n: $USERNAME +> %N: (eval) +> %i: 2 +> a%{...%}b: ab +> + + true + print -P '%?' + false + print -P '%?' +0:`%?' prompt escape +>0 +>1 + + PS4="%_> " + setopt xtrace + if true; then true; else false; fi + unsetopt xtrace +0:`%_' prompt escape +?if> true +?then> true +?> unsetopt xtrace + + diff =(print -P '%#') =(print -P '%(!.#.%%)') +0:`%#' prompt escape and its equivalent + + psvar=(caesar adsum jam forte) + print -P '%v' '%4v' +0:`%v' prompt escape +>caesar forte + + true + print -P '%(?.true.false)' + false + print -P '%(?.true.false)' +0:ternary prompt escapes +>true +>false + + print -P 'start %10<...<truncated at 10%<< Not truncated%3< ...<Not shown' + print -P 'start %10>...>truncated at 10%>> Not truncated%3> ...>Not shown' +0:prompt truncation +>start ...d at 10 Not truncated ... +>start truncat... Not truncated ... + +# It's hard to check the time and date as they are moving targets. +# We therefore just check that various forms of the date are consistent. +# In fact, if you perform this at midnight it can still fail. +# We could test for that, but we can't be bothered. +# I hope LC_ALL is enough to make the format what's expected. + + LC_ALL=C + date1=$(print -P %w) + date2=$(print -P %W) + date3=$(print -P %D) + if [[ $date1 != [A-Z][a-z][a-z][[:blank:]]##[0-9]## ]]; then + print "Date \`$date1' is not in the form \`Day DD' (e.g. \`Mon 1'" + fi + if [[ $date2 != [0-9][0-9]/[0-9][0-9]/[0-9][0-9] ]]; then + print "Date \`$date2' is not in the form \`DD/MM/YYYY'" + fi + if [[ $date3 != [0-9][0-9]-[0-9][0-9]-[0-9][0-9] ]]; then + print "Date \`$date3' is not in the form \`YY-MM-DD'" + fi + if (( $date1[5,-1] != $date2[4,5] )) || (( $date2[4,5] != $date3[7,8] )) + then + print "Days of month do not agree in $date1, $date2, $date3" + fi + if (( $date2[1,2] != $date3[4,5] )); then + print "Months do not agree in $date2, $date3" + fi + if (( $date2[7,8] != $date3[1,2] )); then + print "Years do not agree in $date2, $date3" + fi +0:Dates produced by prompt escapes + + mkdir foo + mkdir foo/bar + mkdir foo/bar/rod + (zsh_directory_name() { + emulate -L zsh + setopt extendedglob + local -a match mbegin mend + if [[ $1 = d ]]; then + if [[ $2 = (#b)(*bar)/rod ]]; then + reply=(barmy ${#match[1]}) + else + return 1 + fi + else + if [[ $2 = barmy ]]; then + reply=($mydir/foo/bar) + else + return 1 + fi + fi + } + # success + print ~[barmy]/anything + cd foo/bar/rod + print -P %~ + # failure + setopt nonomatch + print ~[scuzzy]/rubbish + cd ../.. + print -P %~ + # catastrophic failure + unsetopt nonomatch + print ~[scuzzy]/rubbish + ) +1q:Dynamic named directories +>$mydir/foo/bar/anything +>~[barmy]/rod +>~[scuzzy]/rubbish +>~mydir/foo +?(eval):33: no directory expansion: ~[scuzzy] + + ( + zsh_directory_name() { + emulate -L zsh + setopt extendedglob + local -a match mbegin mend + if [[ $1 = n ]]; then + if [[ $2 = *:l ]]; then + reply=(${2%%:l}/very_long_directory_name) + return 0 + else + return 1 + fi + else + if [[ $2 = (#b)(*)/very_long_directory_name ]]; then + reply=(${match[1]}:l ${#2}) + return 0 + else + return 1 + fi + fi + } + parent=$PWD + dir=$parent/very_long_directory_name + mkdir $dir + cd $dir + fn() { + PS4='+%N:%i> ' + setopt localoptions xtrace + # The following is the key to the test. + # It invokes zsh_directory_name which does PS4 output stuff + # while we're doing prompt handling for the parameter + # substitution. This checks recursion works OK. + local d=${(%):-%~} + print ${d//$parent/\<parent\>} + } + fn 2>stderr + # post process error to remove variable contents + while read line; do + # tricky: reply is set to include directory length which is variable + [[ $line = *reply* ]] && continue + print ${line//$parent/\<parent\>} + done <stderr >&2 + ) +0:Recursive use of prompts +>~[<parent>:l] +?+zsh_directory_name:1> emulate -L zsh +?+zsh_directory_name:2> setopt extendedglob +?+zsh_directory_name:3> local -a match mbegin mend +?+zsh_directory_name:4> [[ d = n ]] +?+zsh_directory_name:12> [[ <parent>/very_long_directory_name = (#b)(*)/very_long_directory_name ]] +?+zsh_directory_name:14> return 0 +?+fn:7> local d='~[<parent>:l]' +?+fn:8> print '~[<parent>:l]' diff --git a/dotfiles/system/.zsh/modules/Test/D02glob.ztst b/dotfiles/system/.zsh/modules/Test/D02glob.ztst new file mode 100644 index 0000000..1385d57 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/D02glob.ztst @@ -0,0 +1,688 @@ +# Tests for globbing + +%prep + mkdir glob.tmp + mkdir glob.tmp/dir{1,2,3,4} + mkdir glob.tmp/dir3/subdir + : >glob.tmp/{,{dir1,dir2}/}{a,b,c} + + globtest () { + $ZTST_testdir/../Src/zsh -f $ZTST_srcdir/../Misc/$1 + } + + regress_absolute_path_and_core_dump() { + local absolute_dir=$(cd glob.tmp && pwd -P) + [[ -n $absolute_dir ]] || return 1 + setopt localoptions extendedglob nullglob + print $absolute_dir/**/*~/* + setopt nonullglob nomatch + print glob.tmp/**/*~(.)# + } + +%test + + globtest globtests +0:zsh globbing +>0: [[ foo~ = foo~ ]] +>0: [[ foo~ = (foo~) ]] +>0: [[ foo~ = (foo~|) ]] +>0: [[ foo.c = *.c~boo* ]] +>1: [[ foo.c = *.c~boo*~foo* ]] +>0: [[ fofo = (fo#)# ]] +>0: [[ ffo = (fo#)# ]] +>0: [[ foooofo = (fo#)# ]] +>0: [[ foooofof = (fo#)# ]] +>0: [[ fooofoofofooo = (fo#)# ]] +>1: [[ foooofof = (fo##)# ]] +>1: [[ xfoooofof = (fo#)# ]] +>1: [[ foooofofx = (fo#)# ]] +>0: [[ ofxoofxo = ((ofo#x)#o)# ]] +>1: [[ ofooofoofofooo = (fo#)# ]] +>0: [[ foooxfooxfoxfooox = (fo#x)# ]] +>1: [[ foooxfooxofoxfooox = (fo#x)# ]] +>0: [[ foooxfooxfxfooox = (fo#x)# ]] +>0: [[ ofxoofxo = ((ofo#x)#o)# ]] +>0: [[ ofoooxoofxo = ((ofo#x)#o)# ]] +>0: [[ ofoooxoofxoofoooxoofxo = ((ofo#x)#o)# ]] +>0: [[ ofoooxoofxoofoooxoofxoo = ((ofo#x)#o)# ]] +>1: [[ ofoooxoofxoofoooxoofxofo = ((ofo#x)#o)# ]] +>0: [[ ofoooxoofxoofoooxoofxooofxofxo = ((ofo#x)#o)# ]] +>0: [[ aac = ((a))#a(c) ]] +>0: [[ ac = ((a))#a(c) ]] +>1: [[ c = ((a))#a(c) ]] +>0: [[ aaac = ((a))#a(c) ]] +>1: [[ baaac = ((a))#a(c) ]] +>0: [[ abcd = ?(a|b)c#d ]] +>0: [[ abcd = (ab|ab#)c#d ]] +>0: [[ acd = (ab|ab#)c#d ]] +>0: [[ abbcd = (ab|ab#)c#d ]] +>0: [[ effgz = (bc##d|ef#g?|(h|)i(j|k)) ]] +>0: [[ efgz = (bc##d|ef#g?|(h|)i(j|k)) ]] +>0: [[ egz = (bc##d|ef#g?|(h|)i(j|k)) ]] +>0: [[ egzefffgzbcdij = (bc##d|ef#g?|(h|)i(j|k))# ]] +>1: [[ egz = (bc##d|ef##g?|(h|)i(j|k)) ]] +>0: [[ ofoofo = (ofo##)# ]] +>0: [[ oxfoxoxfox = (oxf(ox)##)# ]] +>1: [[ oxfoxfox = (oxf(ox)##)# ]] +>0: [[ ofoofo = (ofo##|f)# ]] +>0: [[ foofoofo = (foo|f|fo)(f|ofo##)# ]] +>0: [[ oofooofo = (of|oofo##)# ]] +>0: [[ fffooofoooooffoofffooofff = (f#o#)# ]] +>1: [[ fffooofoooooffoofffooofffx = (f#o#)# ]] +>0: [[ fofoofoofofoo = (fo|foo)# ]] +>0: [[ foo = ((^x)) ]] +>0: [[ foo = ((^x)*) ]] +>1: [[ foo = ((^foo)) ]] +>0: [[ foo = ((^foo)*) ]] +>0: [[ foobar = ((^foo)) ]] +>0: [[ foobar = ((^foo)*) ]] +>1: [[ foot = z*~*x ]] +>0: [[ zoot = z*~*x ]] +>1: [[ foox = z*~*x ]] +>1: [[ zoox = z*~*x ]] +>0: [[ moo.cow = (*~*.*).(*~*.*) ]] +>1: [[ mad.moo.cow = (*~*.*).(*~*.*) ]] +>0: [[ moo.cow = (^*.*).(^*.*) ]] +>1: [[ sane.moo.cow = (^*.*).(^*.*) ]] +>1: [[ mucca.pazza = mu(^c#)?.pa(^z#)? ]] +>1: [[ _foo~ = _(|*[^~]) ]] +>0: [[ fff = ((^f)) ]] +>0: [[ fff = ((^f)#) ]] +>0: [[ fff = ((^f)##) ]] +>0: [[ ooo = ((^f)) ]] +>0: [[ ooo = ((^f)#) ]] +>0: [[ ooo = ((^f)##) ]] +>0: [[ foo = ((^f)) ]] +>0: [[ foo = ((^f)#) ]] +>0: [[ foo = ((^f)##) ]] +>1: [[ f = ((^f)) ]] +>1: [[ f = ((^f)#) ]] +>1: [[ f = ((^f)##) ]] +>0: [[ foot = (^z*|*x) ]] +>1: [[ zoot = (^z*|*x) ]] +>0: [[ foox = (^z*|*x) ]] +>0: [[ zoox = (^z*|*x) ]] +>0: [[ foo = (^foo)# ]] +>1: [[ foob = (^foo)b* ]] +>0: [[ foobb = (^foo)b* ]] +>1: [[ foob = (*~foo)b* ]] +>0: [[ foobb = (*~foo)b* ]] +>1: [[ zsh = ^z* ]] +>0: [[ a%1X = [[:alpha:][:punct:]]#[[:digit:]][^[:lower:]] ]] +>1: [[ a%1 = [[:alpha:][:punct:]]#[[:digit:]][^[:lower:]] ]] +>0: [[ [: = [[:]# ]] +>0: [[ :] = []:]# ]] +>0: [[ :] = [:]]# ]] +>0: [[ [ = [[] ]] +>0: [[ ] = []] ]] +>0: [[ [] = [^]]] ]] +>0: [[ fooxx = (#i)FOOXX ]] +>1: [[ fooxx = (#l)FOOXX ]] +>0: [[ FOOXX = (#l)fooxx ]] +>1: [[ fooxx = (#i)FOO(#I)X(#i)X ]] +>0: [[ fooXx = (#i)FOO(#I)X(#i)X ]] +>0: [[ fooxx = ((#i)FOOX)x ]] +>1: [[ fooxx = ((#i)FOOX)X ]] +>1: [[ BAR = (bar|(#i)foo) ]] +>0: [[ FOO = (bar|(#i)foo) ]] +>0: [[ Modules = (#i)*m* ]] +>0: [[ fooGRUD = (#i)(bar|(#I)foo|(#i)rod)grud ]] +>1: [[ FOOGRUD = (#i)(bar|(#I)foo|(#i)rod)grud ]] +>0: [[ readme = (#i)readme~README|readme ]] +>0: [[ readme = (#i)readme~README|readme~README ]] +>0: [[ 633 = <1-1000>33 ]] +>0: [[ 633 = <-1000>33 ]] +>0: [[ 633 = <1->33 ]] +>0: [[ 633 = <->33 ]] +>0: [[ 12345678901234567890123456789012345678901234567890123456789012345678901234567890foo = <42->foo ]] +>0: [[ READ.ME = (#ia1)readme ]] +>1: [[ READ..ME = (#ia1)readme ]] +>0: [[ README = (#ia1)readm ]] +>0: [[ READM = (#ia1)readme ]] +>0: [[ README = (#ia1)eadme ]] +>0: [[ EADME = (#ia1)readme ]] +>0: [[ READEM = (#ia1)readme ]] +>1: [[ ADME = (#ia1)readme ]] +>1: [[ README = (#ia1)read ]] +>0: [[ bob = (#a1)[b][b] ]] +>1: [[ bob = (#a1)[b][b]a ]] +>0: [[ bob = (#a1)[b]o[b]a ]] +>1: [[ bob = (#a1)[c]o[b] ]] +>0: [[ abcd = (#a2)XbcX ]] +>0: [[ abcd = (#a2)ad ]] +>0: [[ ad = (#a2)abcd ]] +>0: [[ abcd = (#a2)bd ]] +>0: [[ bd = (#a2)abcd ]] +>0: [[ badc = (#a2)abcd ]] +>0: [[ adbc = (#a2)abcd ]] +>1: [[ dcba = (#a2)abcd ]] +>0: [[ dcba = (#a3)abcd ]] +>0: [[ aabaXaaabY = (#a1)(a#b)#Y ]] +>0: [[ aabaXaaabY = (#a1)(a#b)(a#b)Y ]] +>0: [[ aaXaaaaabY = (#a1)(a#b)(a#b)Y ]] +>0: [[ aaaXaaabY = (#a1)(a##b)##Y ]] +>0: [[ aaaXbaabY = (#a1)(a##b)##Y ]] +>1: [[ read.me = (#ia1)README~READ.ME ]] +>0: [[ read.me = (#ia1)README~READ_ME ]] +>1: [[ read.me = (#ia1)README~(#a1)READ_ME ]] +>0: [[ test = *((#s)|/)test((#e)|/)* ]] +>0: [[ test/path = *((#s)|/)test((#e)|/)* ]] +>0: [[ path/test = *((#s)|/)test((#e)|/)* ]] +>0: [[ path/test/ohyes = *((#s)|/)test((#e)|/)* ]] +>1: [[ atest = *((#s)|/)test((#e)|/)* ]] +>1: [[ testy = *((#s)|/)test((#e)|/)* ]] +>1: [[ testy/path = *((#s)|/)test((#e)|/)* ]] +>1: [[ path/atest = *((#s)|/)test((#e)|/)* ]] +>1: [[ atest/path = *((#s)|/)test((#e)|/)* ]] +>1: [[ path/testy = *((#s)|/)test((#e)|/)* ]] +>1: [[ path/testy/ohyes = *((#s)|/)test((#e)|/)* ]] +>1: [[ path/atest/ohyes = *((#s)|/)test((#e)|/)* ]] +>0: [[ XabcdabcY = X(ab|c|d)(#c5)Y ]] +>0: [[ XabcdabcY = X(ab|c|d)(#c1,5)Y ]] +>0: [[ XabcdabcY = X(ab|c|d)(#c5,8)Y ]] +>0: [[ XabcdabcY = X(ab|c|d)(#c4,)Y ]] +>1: [[ XabcdabcY = X(ab|c|d)(#c6,)Y ]] +>1: [[ XabcdabcY = X(ab|c|d)(#c1,4)Y ]] +>0: [[ ZX = Z(|)(#c1)X ]] +>0: [[ froofroo = (fro(#c2))(#c2) ]] +>1: [[ froofroofroo = (fro(#c2))(#c2) ]] +>1: [[ froofro = (fro(#c2))(#c2) ]] +>0: [[ ax = ?(#c1,2)x ]] +>0: [[ ax = ?(#c1,)x ]] +>0: [[ ax = ?(#c0,1)x ]] +>1: [[ ax = ?(#c0,0)x ]] +>1: [[ ax = ?(#c2,)x ]] +>0: [[ aa = a(#c1,2)a ]] +>0: [[ aa = a(#c1,)a ]] +>0: [[ aa = a(#c0,1)a ]] +>1: [[ aa = a(#c0,0)a ]] +>1: [[ aa = a(#c2,)a ]] +>0: [[ test.zsh = *.?(#c1)sh ]] +>0: [[ test.bash = *.?(#c2)sh ]] +>0: [[ test.bash = *.?(#c1,2)sh ]] +>0: [[ test.bash = *.?(#c1,)sh ]] +>0: [[ test.zsh = *.?(#c1,)sh ]] +>0 tests failed. + + globtest globtests.ksh +0:ksh compatibility +>0: [[ fofo = *(f*(o)) ]] +>0: [[ ffo = *(f*(o)) ]] +>0: [[ foooofo = *(f*(o)) ]] +>0: [[ foooofof = *(f*(o)) ]] +>0: [[ fooofoofofooo = *(f*(o)) ]] +>1: [[ foooofof = *(f+(o)) ]] +>1: [[ xfoooofof = *(f*(o)) ]] +>1: [[ foooofofx = *(f*(o)) ]] +>0: [[ ofxoofxo = *(*(of*(o)x)o) ]] +>1: [[ ofooofoofofooo = *(f*(o)) ]] +>0: [[ foooxfooxfoxfooox = *(f*(o)x) ]] +>1: [[ foooxfooxofoxfooox = *(f*(o)x) ]] +>0: [[ foooxfooxfxfooox = *(f*(o)x) ]] +>0: [[ ofxoofxo = *(*(of*(o)x)o) ]] +>0: [[ ofoooxoofxo = *(*(of*(o)x)o) ]] +>0: [[ ofoooxoofxoofoooxoofxo = *(*(of*(o)x)o) ]] +>0: [[ ofoooxoofxoofoooxoofxoo = *(*(of*(o)x)o) ]] +>1: [[ ofoooxoofxoofoooxoofxofo = *(*(of*(o)x)o) ]] +>0: [[ ofoooxoofxoofoooxoofxooofxofxo = *(*(of*(o)x)o) ]] +>0: [[ aac = *(@(a))a@(c) ]] +>0: [[ ac = *(@(a))a@(c) ]] +>1: [[ c = *(@(a))a@(c) ]] +>0: [[ aaac = *(@(a))a@(c) ]] +>1: [[ baaac = *(@(a))a@(c) ]] +>0: [[ abcd = ?@(a|b)*@(c)d ]] +>0: [[ abcd = @(ab|a*@(b))*(c)d ]] +>0: [[ acd = @(ab|a*(b))*(c)d ]] +>0: [[ abbcd = @(ab|a*(b))*(c)d ]] +>0: [[ effgz = @(b+(c)d|e*(f)g?|?(h)i@(j|k)) ]] +>0: [[ efgz = @(b+(c)d|e*(f)g?|?(h)i@(j|k)) ]] +>0: [[ egz = @(b+(c)d|e*(f)g?|?(h)i@(j|k)) ]] +>0: [[ egzefffgzbcdij = *(b+(c)d|e*(f)g?|?(h)i@(j|k)) ]] +>1: [[ egz = @(b+(c)d|e+(f)g?|?(h)i@(j|k)) ]] +>0: [[ ofoofo = *(of+(o)) ]] +>0: [[ oxfoxoxfox = *(oxf+(ox)) ]] +>1: [[ oxfoxfox = *(oxf+(ox)) ]] +>0: [[ ofoofo = *(of+(o)|f) ]] +>0: [[ foofoofo = @(foo|f|fo)*(f|of+(o)) ]] +>0: [[ oofooofo = *(of|oof+(o)) ]] +>0: [[ fffooofoooooffoofffooofff = *(*(f)*(o)) ]] +>1: [[ fffooofoooooffoofffooofffx = *(*(f)*(o)) ]] +>0: [[ fofoofoofofoo = *(fo|foo) ]] +>0: [[ foo = !(x) ]] +>0: [[ foo = !(x)* ]] +>1: [[ foo = !(foo) ]] +>0: [[ foo = !(foo)* ]] +>0: [[ foobar = !(foo) ]] +>0: [[ foobar = !(foo)* ]] +>0: [[ moo.cow = !(*.*).!(*.*) ]] +>1: [[ mad.moo.cow = !(*.*).!(*.*) ]] +>1: [[ mucca.pazza = mu!(*(c))?.pa!(*(z))? ]] +>1: [[ _foo~ = _?(*[^~]) ]] +>0: [[ fff = !(f) ]] +>0: [[ fff = *(!(f)) ]] +>0: [[ fff = +(!(f)) ]] +>0: [[ ooo = !(f) ]] +>0: [[ ooo = *(!(f)) ]] +>0: [[ ooo = +(!(f)) ]] +>0: [[ foo = !(f) ]] +>0: [[ foo = *(!(f)) ]] +>0: [[ foo = +(!(f)) ]] +>1: [[ f = !(f) ]] +>1: [[ f = *(!(f)) ]] +>1: [[ f = +(!(f)) ]] +>0: [[ foot = @(!(z*)|*x) ]] +>1: [[ zoot = @(!(z*)|*x) ]] +>0: [[ foox = @(!(z*)|*x) ]] +>0: [[ zoox = @(!(z*)|*x) ]] +>0: [[ foo = *(!(foo)) ]] +>1: [[ foob = !(foo)b* ]] +>0: [[ foobb = !(foo)b* ]] +>0: [[ fooxx = (#i)FOOXX ]] +>1: [[ fooxx = (#l)FOOXX ]] +>0: [[ FOOXX = (#l)fooxx ]] +>1: [[ fooxx = (#i)FOO@(#I)X@(#i)X ]] +>0: [[ fooXx = (#i)FOO@(#I)X@(#i)X ]] +>0: [[ fooxx = @((#i)FOOX)x ]] +>1: [[ fooxx = @((#i)FOOX)X ]] +>1: [[ BAR = @(bar|(#i)foo) ]] +>0: [[ FOO = @(bar|(#i)foo) ]] +>0: [[ Modules = (#i)*m* ]] +>0 tests failed. + + (unsetopt multibyte + [[ bjrn = *[]* ]]) +0:single byte match with top bit set + + ( regress_absolute_path_and_core_dump ) +0:exclusions regression test +> +>glob.tmp/a glob.tmp/b glob.tmp/c glob.tmp/dir1 glob.tmp/dir1/a glob.tmp/dir1/b glob.tmp/dir1/c glob.tmp/dir2 glob.tmp/dir2/a glob.tmp/dir2/b glob.tmp/dir2/c glob.tmp/dir3 glob.tmp/dir3/subdir glob.tmp/dir4 + + print glob.tmp/*(/) +0:Just directories +>glob.tmp/dir1 glob.tmp/dir2 glob.tmp/dir3 glob.tmp/dir4 + + print glob.tmp/*(.) +0:Just files +>glob.tmp/a glob.tmp/b glob.tmp/c + + print glob.tmp/*(.e^'reply=( glob.tmp/*/${REPLY:t} )'^:t) +0:Globbing used recursively (inside e glob qualifier) +>a a b b c c + + print glob.tmp/*/*(e:'reply=( glob.tmp/**/*([1]) )'::t) +0:Recursive globbing used recursively (inside e glob qualifier) +>a a a a a a a + + print glob.tmp/**/(:h) +0:Head modifier +>. glob.tmp glob.tmp glob.tmp glob.tmp glob.tmp/dir3 + + print glob.tmp(:r) +0:Remove extension modifier +>glob + + print glob.tmp/*(:s/./_/) +0:Substitute modifier +>glob_tmp/a glob_tmp/b glob_tmp/c glob_tmp/dir1 glob_tmp/dir2 glob_tmp/dir3 glob_tmp/dir4 + + print glob.tmp/*(F) +0:Just full dirs +>glob.tmp/dir1 glob.tmp/dir2 glob.tmp/dir3 + + print glob.tmp/*(^F) +0:Omit full dirs +>glob.tmp/a glob.tmp/b glob.tmp/c glob.tmp/dir4 + + print glob.tmp/*(/^F) +0:Just empty dirs +>glob.tmp/dir4 + + setopt extendedglob + print glob.tmp/**/*~*/dir3(/*|(#e))(/) +0:Exclusions with complicated path specifications +>glob.tmp/dir1 glob.tmp/dir2 glob.tmp/dir4 + + print -l -- glob.tmp/*(P:-f:) +0:Prepending words to each argument +>-f +>glob.tmp/a +>-f +>glob.tmp/b +>-f +>glob.tmp/c +>-f +>glob.tmp/dir1 +>-f +>glob.tmp/dir2 +>-f +>glob.tmp/dir3 +>-f +>glob.tmp/dir4 + + print -l -- glob.tmp/*(P:one word:P:another word:) +0:Prepending two words to each argument +>one word +>another word +>glob.tmp/a +>one word +>another word +>glob.tmp/b +>one word +>another word +>glob.tmp/c +>one word +>another word +>glob.tmp/dir1 +>one word +>another word +>glob.tmp/dir2 +>one word +>another word +>glob.tmp/dir3 +>one word +>another word +>glob.tmp/dir4 + + [[ "" = "" ]] && echo OK +0:Empty strings +>OK + + foo="this string has a : colon in it" + print ${foo%% #:*} +0:Must-match arguments in complex patterns +>this string has a + + mkdir glob.tmp/ra=1.0_et=3.5 + touch glob.tmp/ra=1.0_et=3.5/foo + print glob.tmp/ra=1.0_et=3.5/??? +0:Bug with intermediate paths with plain strings but tokenized characters +>glob.tmp/ra=1.0_et=3.5/foo + + doesmatch() { + setopt localoptions extendedglob + print -n $1 $2\ + if [[ $1 = $~2 ]]; then print yes; else print no; fi; + } + doesmatch MY_IDENTIFIER '[[:IDENT:]]##' + doesmatch YOUR:IDENTIFIER '[[:IDENT:]]##' + IFS=$'\n' doesmatch $'\n' '[[:IFS:]]' + IFS=' ' doesmatch $'\n' '[[:IFS:]]' + IFS=':' doesmatch : '[[:IFSSPACE:]]' + IFS=' ' doesmatch ' ' '[[:IFSSPACE:]]' + WORDCHARS="" doesmatch / '[[:WORD:]]' + WORDCHARS="/" doesmatch / '[[:WORD:]]' +0:Named character sets handled internally +>MY_IDENTIFIER [[:IDENT:]]## yes +>YOUR:IDENTIFIER [[:IDENT:]]## no +> +> [[:IFS:]] yes +> +> [[:IFS:]] no +>: [[:IFSSPACE:]] no +> [[:IFSSPACE:]] yes +>/ [[:WORD:]] no +>/ [[:WORD:]] yes + + [[ foo = (#c0)foo ]] +2:Misplaced (#c...) flag +?(eval):1: bad pattern: (#c0)foo + + mkdir glob.tmp/dir5 + touch glob.tmp/dir5/N123 + print glob.tmp/dir5/N<->(N) + rm -rf glob.tmp/dir5 +0:Numeric glob is not usurped by process substitution. +>glob.tmp/dir5/N123 + + tpd() { + [[ $1 = $~2 ]] + print -r "$1, $2: $?" + } + test_pattern_disables() { + emulate -L zsh + tpd 'forthcoming' 'f*g' + disable -p '*' + tpd 'forthcoming' 'f*g' + tpd 'f*g' 'f*g' + tpd '[frog]' '[frog]' + tpd '[frog]' '\[[f]rog\]' + disable -p '[' + tpd '[frog]' '[frog]' + tpd '[frog]' '\[[f]rog\]' + setopt extendedglob + tpd 'foo' '^bar' + disable -p '^' + tpd 'foo' '^bar' + tpd '^bar' '^bar' + tpd 'rumble' '(rumble|bluster)' + tpd '(thunder)' '(thunder)' + disable -p '(' + tpd 'rumble' '(rumble|bluster)' + tpd '(thunder)' '(thunder)' + setopt kshglob + tpd 'scramble' '@(panic|frenzy|scramble)' + tpd '@(scrimf)' '@(scrimf)' + disable -p '@(' + tpd 'scramble' '@(panic|frenzy|scramble)' + tpd '@(scrimf)' '@(scrimf)' + disable -p + } + test_pattern_disables + print Nothing should be disabled. + disable -p +0:disable -p +>forthcoming, f*g: 0 +>forthcoming, f*g: 1 +>f*g, f*g: 0 +>[frog], [frog]: 1 +>[frog], \[[f]rog\]: 0 +>[frog], [frog]: 0 +>[frog], \[[f]rog\]: 1 +>foo, ^bar: 0 +>foo, ^bar: 1 +>^bar, ^bar: 0 +>rumble, (rumble|bluster): 0 +>(thunder), (thunder): 1 +>rumble, (rumble|bluster): 1 +>(thunder), (thunder): 0 +>scramble, @(panic|frenzy|scramble): 0 +>@(scrimf), @(scrimf): 1 +>scramble, @(panic|frenzy|scramble): 1 +>@(scrimf), @(scrimf): 0 +>'(' '*' '[' '^' '@(' +>Nothing should be disabled. + + ( + setopt nomatch + x=( '' ) + print $^x(N) + ) +0:No error with empty null glob with (N). +> + + (setopt kshglob + test_array=( + '+fours' '+*' + '@titude' '@*' + '!bang' '!*' + # and check they work in the real kshglob cases too... + '+bus+bus' '+(+bus|-car)' + '@sinhats' '@(@sinhats|wrensinfens)' + '!kerror' '!(!somethingelse)' + # and these don't match, to be sure + '+more' '+(+less)' + '@all@all' '@(@all)' + '!goesitall' '!(!goesitall)' + ) + for str pat in $test_array; do + eval "[[ $str = $pat ]]" && print "$str matches $pat" + done + true + ) +0:kshglob option does not break +, @, ! without following open parenthesis +>+fours matches +* +>@titude matches @* +>!bang matches !* +>+bus+bus matches +(+bus|-car) +>@sinhats matches @(@sinhats|wrensinfens) +>!kerror matches !(!somethingelse) + + ( + setopt extendedglob + cd glob.tmp + [[ -n a*(#qN) ]] && print File beginning with a + [[ -z z*(#qN) ]] && print No file beginning with z + setopt nonomatch + [[ -n z*(#q) ]] && print Normal string if nullglob not set + ) +0:Force glob expansion in conditions using (#q) +>File beginning with a +>No file beginning with z +>Normal string if nullglob not set + + (){ print $#@ } glob.tmp/dir*(Y1) + (){ print $#@ } glob.tmp/file*(NY1) + (){ [[ "$*" == */dir?\ */dir? ]] && print Returns matching filenames } glob.tmp/dir*(Y2) + (){ print "Limit is upper bound:" ${(o)@:t} } glob.tmp/dir*(Y5) + (){ print "Negated:" $@:t } glob.tmp/dir*(Y1^Y) + (){ print "Sorting:" $@:t } glob.tmp/dir*(Y4On) + (){ [[ $#@ -eq 1 ]] && print Globs before last path component } glob.tmp/dir?/subdir(NY1) + (){ [[ $1 == glob.tmp/a ]] } glob.tmp/**/a(Y1) && print Breadth first + (){ [[ $#@ -eq 0 ]] && print Respects qualifiers } glob.tmp/dir*(NY1.) + (print -- *(Y)) 2>/dev/null || print "Argument required" +0:short-circuit modifier +>1 +>0 +>Returns matching filenames +>Limit is upper bound: dir1 dir2 dir3 dir4 +>Negated: dir1 dir2 dir3 dir4 +>Sorting: dir4 dir3 dir2 dir1 +>Globs before last path component +>Breadth first +>Respects qualifiers +>Argument required + + [[ "ce fichier n'existe pas" = (#b)ce\ (f[^ ]#)\ *s(#q./) ]] + print $match[1] +0:(#q) is ignored completely in conditional pattern matching +>fichier + +# The following should not cause excessive slowdown. + print glob.tmp/*.* + print glob.tmp/**************************.************************* +0:Optimisation to squeeze multiple *'s used as ordinary glob wildcards. +>glob.tmp/ra=1.0_et=3.5 +>glob.tmp/ra=1.0_et=3.5 + + [[ 1_2_ = (*_)(#c1) ]] && print 1 OK # because * matches 1_2 + [[ 1_2_ = (*_)(#c2) ]] && print 2 OK + [[ 1_2_ = (*_)(#c3) ]] || print 3 OK +0:Some more complicated backtracking with match counts. +>1 OK +>2 OK +>3 OK + + [[ foo = 'f'\o"o" ]] +0:Stripping of quotes from patterns (1) + + [[ foo = 'f'('o'|'a')('o'|'b') ]] +0:Stripping of quotes from patterns (2) + + [[ fob = 'f'('o'|'a')('o'|'b') ]] +0:Stripping of quotes from patterns (3) + + [[ fab = 'f'('o'|'a')('o'|'b') ]] +0:Stripping of quotes from patterns (4) + + [[ fib != 'f'('o'|'a')('o'|'b') ]] +0:Stripping of quotes from patterns (4) + + [[ - != [a-z] ]] +0:- is a special character in ranges + + [[ - = ['a-z'] ]] +0:- is not a special character in ranges if quoted + + [[ b-1 = [a-z]-[0-9] ]] +0:- untokenized following a bracketed subexpression + + [[ b-1 = []a-z]-[]0-9] ]] +0:- "]" after "[" is normal range character and - still works + + headremove="bcdef" + print ${headremove#[a-z]} +0:active - works in pattern in parameter +>cdef + + headremove="bcdef" + print ${headremove#['a-z']} + headremove="-cdef" + print ${headremove#['a-z']} +0:quoted - works in pattern in parameter +>bcdef +>cdef + + [[ a != [^a] ]] +0:^ active in character class if not quoted + + [[ a = ['^a'] ]] +0:^ not active in character class if quoted + + [[ a != [!a] ]] +0:! active in character class if not quoted + + [[ a = ['!a'] ]] +0:! not active in character class if quoted + + # Actually, we don't need the quoting here, + # c.f. the next test. This just makes it look + # more standard. + cset="^a-z" + [[ "^" = ["$cset"] ]] || print Fail 1 + [[ "a" = ["$cset"] ]] || print Fail 2 + [[ "-" = ["$cset"] ]] || print Fail 3 + [[ "z" = ["$cset"] ]] || print Fail 4 + [[ "1" != ["$cset"] ]] || print Fail 5 + [[ "b" != ["$cset"] ]] || print Fail 6 +0:character set specified as quoted variable + + cset="^a-z" + [[ "^" = [$~cset] ]] || print Fail 1 + [[ "a" != [$~cset] ]] || print Fail 2 + [[ "-" = [$~cset] ]] || print Fail 3 + [[ "z" != [$~cset] ]] || print Fail 4 + [[ "1" = [$~cset] ]] || print Fail 5 + [[ "b" != [$~cset] ]] || print Fail 6 +0:character set specified as active variable + + () { print -l -- $@:a } / /..{,/} /1 /nonexistent/..{,/} /deeper/nonexistent/..{,/} +0:modifier ':a' doesn't require existence +>/ +>/ +>/ +>/1 +>/ +>/ +>/deeper +>/deeper + + () { set -- ${PWD}/$^@; print -l -- $@:A } glob.tmp/nonexistent/foo/bar/baz +0:modifier ':A' doesn't require existence +*>*/glob.tmp/nonexistent/foo/bar/baz + + ln -s dir3/subdir glob.tmp/link + () { + print ${1:A} | grep glob.tmp + } glob.tmp/link/../../hello + rm glob.tmp/link +0:modifier ':A' resolves '..' components before symlinks +# There should be no output + + ln -s dir3/subdir glob.tmp/link + () { + print ${1:P} + } glob.tmp/link/../../hello/world + rm glob.tmp/link +0:modifier ':P' resolves symlinks before '..' components +*>*glob.tmp/hello/world diff --git a/dotfiles/system/.zsh/modules/Test/D03procsubst.ztst b/dotfiles/system/.zsh/modules/Test/D03procsubst.ztst new file mode 100644 index 0000000..ca8d56f --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/D03procsubst.ztst @@ -0,0 +1,151 @@ +# Tests for process substitution: <(...), >(...) and =(...). + +%prep + if grep '#define PATH_DEV_FD' $ZTST_testdir/../config.h > /dev/null 2>&1 || + grep '#define HAVE_FIFOS' $ZTST_testdir/../config.h > /dev/null 2>&1; then + mkdir procsubst.tmp + cd procsubst.tmp + print 'First\tSecond\tThird\tFourth' >FILE1 + print 'Erste\tZweite\tDritte\tVierte' >FILE2 + else + ZTST_unimplemented="process substitution is not supported" + true + fi + + function copycat { cat "$@" } + +%test + paste <(cut -f1 FILE1) <(cut -f3 FILE2) +0:<(...) substitution +>First Dritte + +# slightly desperate hack to force >(...) to be synchronous + { paste <(cut -f2 FILE1) <(cut -f4 FILE2) } > >(sed 's/e/E/g' >OUTFILE) + cat OUTFILE +0:>(...) substitution +>SEcond ViErtE + + diff =(cat FILE1) =(cat FILE2) +1:=(...) substituion +>1c1 +>< First Second Third Fourth +>--- +>> Erste Zweite Dritte Vierte + + copycat <(print First) <(print Zweite) +0:FDs remain open for external commands called from functions +>First +>Zweite + + catfield2() { + local -a args + args=(${(s.,.)1}) + print $args[1] + cat $args[2] + print $args[3] + } + catfield2 up,<(print $'\x64'own),sideways +0:<(...) when embedded within an argument +>up +>down +>sideways + + outputfield2() { + local -a args + args=(${(s.,.)1}) + print $args[1] + echo 'How sweet the moonlight sits upon the bank' >$args[2] + print $args[3] + } + outputfield2 muddy,>(sed -e s/s/th/g >outputfield2.txt),vesture + # yuk + while [[ ! -e outputfield2.txt || ! -s outputfield2.txt ]]; do :; done + cat outputfield2.txt +0:>(...) when embedded within an argument +>muddy +>vesture +>How thweet the moonlight thitth upon the bank + + catfield1() { + local -a args + args=(${(s.,.)1}) + cat $args[1] + print $args[2] + } + catfield1 =(echo s$'\x69't),jessica +0:=(...) followed by something else without a break +>sit +>jessica + + ( + setopt nonomatch + # er... why is this treated as a glob? + print everything,=(here is left),alone + ) +0:=(...) preceded by other stuff has no special effect +>everything,=(here is left),alone + + print something=${:-=(echo 'C,D),(F,G)'} +1: Graceful handling of bad substitution in enclosed context +?(eval):1: unterminated `=(...)' +# '` + + () { + print -n "first: " + cat $1 + print -n "second: " + cat $2 + } =(echo This becomes argument one) =(echo and this argument two) + function { + print -n "third: " + cat $1 + print -n "fourth: " + cat $2 + } =(echo This becomes argument three) =(echo and this argument four) +0:Process environment of anonymous functions +>first: This becomes argument one +>second: and this argument two +>third: This becomes argument three +>fourth: and this argument four + + () { + # Make sure we don't close the file descriptor too early + eval 'print "Execute a complicated command first" | sed s/command/order/' + cat $1 + } <(echo This line was brought to you by the letters F and D) +0:Process substitution as anonymous function argument +>Execute a complicated order first +>This line was brought to you by the letters F and D + + alias foo='cat <(' + eval 'foo echo this is bound to work)' +0:backtacking within command string parsing with alias still pending +>this is bound to work + + alias foo='cat <( print' + eval 'foo here is some output)' +0:full alias expanded when substitution starts in alias +>here is some output + + if ! (mkfifo test_pipe >/dev/null 2>&1); then + ZTST_skip="mkfifo not available" + else + echo 1 | tee >(cat > test_pipe) | (){ + local pipein + read pipein <test_pipe + print $pipein + read pipein + print $pipein + } + fi +0:proc subst fd in forked subshell closed in parent +>1 +>1 + + if [[ ! -e test_pipe ]]; then + ZTST_skip="mkfifo not available" + else + echo 1 | tee >(cat > test_pipe) | paste - test_pipe + fi +0:proc subst fd in forked subshell closed in parent (external command) +>1 1 diff --git a/dotfiles/system/.zsh/modules/Test/D04parameter.ztst b/dotfiles/system/.zsh/modules/Test/D04parameter.ztst new file mode 100644 index 0000000..9128c3c --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/D04parameter.ztst @@ -0,0 +1,2058 @@ +# Test parameter expansion. Phew. +# (By the way, did I say "phew"?) + +%prep + + mkdir parameter.tmp + + cd parameter.tmp + + touch boringfile evenmoreboringfile + +%test + + foo='the first parameter' + bar='the second parameter' + print -l $foo ${bar} +0:Basic scalar parameter substitution +>the first parameter +>the second parameter + + array1=(the first array) + array2=(the second array) + print -l $array1 ${array2} +0:Basic array parameter substitution +>the +>first +>array +>the +>second +>array + + setopt ksharrays + print -l $array1 ${array2} + unsetopt ksharrays +0:Basic ksharray substitution +>the +>the + + setopt shwordsplit + print -l $foo ${bar} + print -l ${==bar} + unsetopt shwordsplit +0:Basic shwordsplit option handling +>the +>first +>parameter +>the +>second +>parameter +>the second parameter + + print $+foo ${+foo} $+notappearinginthistest ${+notappearinginthistest} +0:$+... +>1 1 0 0 + + x=() + print ${+x} ${+x[1]} ${+x[(r)foo]} ${+x[(r)bar]} + x=(foo) + print ${+x} ${+x[1]} ${+x[(r)foo]} ${+x[(r)bar]} +0:$+... with arrays +>1 0 0 0 +>1 1 1 0 + + set1=set1v + null1= + print ${set1:-set1d} ${set1-set2d} ${null1:-null1d} ${null1-null2d} x + print ${unset1:-unset1d} ${unset1-unset2d} x +0:${...:-...} and ${...-...} +>set1v set1v null1d x +>unset1d unset2d x + + set2=irrelevant + print ${set1:=set1d} ${set2::=set2d} + print $set2 + wasnull1= + wasnull2= + print ${wasnull1=wasnull1d} ${wasnull2:=wasnull2d} + print $wasnull1 $wasnull2 +0:${...:=...}, ${...::=...}, ${...=...} +>set1v set2d +>set2d +>wasnull2d +>wasnull2d + + unset array + print ${#${(A)=array=word}} +0:${#${(A)=array=word}} counts array elements +>1 + + (print ${set1:?okhere}; print ${unset1:?exiting1}; print not reached;) + (print ${null1?okhere}; print ${null1:?exiting2}; print not reached;) +1:${...:?...}, ${...?...} +>set1v +> +?(eval):1: unset1: exiting1 +?(eval):2: null1: exiting2 + + PROMPT="" $ZTST_testdir/../Src/zsh -fis <<<' + unsetopt PROMPT_SP + PS1="" PS2="" PS3="" PS4="" RPS1="" RPS2="" + exec 2>&1 + foo() { + print ${1:?no arguments given} + print not reached + } + foo + print reached + ' 2>/dev/null +0:interactive shell returns to top level on ${...?...} error +*>*foo:1: 1: no arguments given +>reached + + print ${set1:+word1} ${set1+word2} ${null1:+word3} ${null1+word4} + print ${unset1:+word5} ${unset1+word6} +0:${...:+...}, ${...+...} +>word1 word2 word4 +> + + str1='This is very boring indeed.' + print ${str1#*s} + print ${str1##*s} + print $str1##s +0:${...#...}, ${...##...} +> is very boring indeed. +> very boring indeed. +>This is very boring indeed.##s + + str2='If you'\''re reading this you should go and fix some bugs instead.' + print ${str2%d*} + print ${str2%%d*} +0:${...%...}, ${...%%...} +>If you're reading this you should go and fix some bugs instea +>If you're rea + + str1='does match' + str2='does not match' + print ${str1:#does * match} + print ${str2:#does * match} +0:${...:#...} +>does match +> + + array1=(arthur boldly claws dogs every fight) + print ${array1:#[aeiou]*} + print ${(M)array1:#[aeiou]*} +0:${...:#...}, ${(M)...:#...} with array +>boldly claws dogs fight +>arthur every + + str1="$array1" + print ${str1/[aeiou]*g/a braw bricht moonlicht nicht the nic} + print ${(S)str1/[aeiou]*g/relishe} +0:scalar ${.../.../...}, ${(S).../.../...} +>a braw bricht moonlicht nicht the nicht +>relishes every fight + + print ${array1/[aeiou]*/Y} + print ${(S)array1/[aeiou]*/Y} +0:array ${.../.../...}, ${(S).../.../...} +>Y bY clY dY Y fY +>Yrthur bYldly clYws dYgs Yvery fYght + + str1='o this is so, so so very dull' + print ${str1//o*/Please no} + print ${(S)str1//o*/Please no} +0:scalar ${...//.../...}, ${(S)...//.../...} +>Please no +>Please no this is sPlease no, sPlease no sPlease no very dull + + print ${array1//[aeiou]*/Y} + print ${(S)array1//[aeiou]*/Y} +0:array ${...//.../...}, ${(S)...//.../...} +>Y bY clY dY Y fY +>YrthYr bYldly clYws dYgs YvYry fYght + + print ${array1:/[aeiou]*/expletive deleted} +0:array ${...:/...} +>expletive deleted boldly claws dogs expletive deleted fight + + str1='a\string\with\backslashes' + str2='a/string/with/slashes' + print "${str1//\\/-}" + print ${str1//\\/-} + print "${str2//\//-}" + print ${str2//\//-} +0:use of backslashes in //-substitutions +>a-string-with-backslashes +>a-string-with-backslashes +>a-string-with-slashes +>a-string-with-slashes + + args=('one' '#foo' '(bar' "'three'" two) + mod=('#foo' '(bar' "'three'" sir_not_appearing_in_this_film) + print ${args:|mod} + print ${args:*mod} + print "${(@)args:|mod}" + print "${(@)args:*mod}" + args=(two words) + mod=('one word' 'two words') + print "${args:|mod}" + print "${args:*mod}" + scalar='two words' + print ${scalar:|mod} + print ${scalar:*mod} + print ${args:*nonexistent} + empty= + print ${args:*empty} +0:"|" array exclusion and "*" array intersection +>one two +>#foo (bar 'three' +>one two +>#foo (bar 'three' +> +>two words +> +>two words +> +> + + str1='twocubed' + array=(the number of protons in an oxygen nucleus) + print $#str1 ${#str1} "$#str1 ${#str1}" $#array ${#array} "$#array ${#array}" +0:${#...}, $#... +>8 8 8 8 8 8 8 8 + + set 1 2 3 4 5 6 7 8 9 + print ${##} + set 1 2 3 4 5 6 7 8 9 10 + print ${##} + print ${##""} + print ${##1} + print ${##2} + print ${###<->} # oh, for pete's sake... +0:${##} is length of $#, and other tales of hash horror +>1 +>2 +>10 +>0 +>10 +> + + array=(once bitten twice shy) + print IF${array}THEN + print IF${^array}THEN +0:basic ${^...} +>IFonce bitten twice shyTHEN +>IFonceTHEN IFbittenTHEN IFtwiceTHEN IFshyTHEN + + # Quote ${array} here because {...,...} doesn't like unquoted spaces. + print IF{"${array}",THEN}ELSE + print IF{${^array},THEN}ELSE +0:combined ${^...} and {...,...} +>IFonce bitten twice shyELSE IFTHENELSE +>IFonceELSE IFTHENELSE IFbittenELSE IFTHENELSE IFtwiceELSE IFTHENELSE IFshyELSE IFTHENELSE + + str1='one word' + print -l $str1 ${=str1} "split ${=str1}wise" +0:${=...} +>one word +>one +>word +>split one +>wordwise + + str1='*' + print $str1 ${~str1} $~str1 + setopt globsubst + print $str1 + unsetopt globsubst +0:${~...} and globsubst +>* boringfile evenmoreboringfile boringfile evenmoreboringfile +>boringfile evenmoreboringfile + +# The following tests a bug where globsubst didn't preserve +# backslashes when printing out the original string. + str1='\\*\\' + ( + setopt globsubst nonomatch + [[ \\\\ = $str1 ]] && print -r '\\ matched by' $str1 + [[ \\foo\\ = $str1 ]] && print -r '\\foo matched by' $str1 + [[ a\\b\\ = $str1 ]] || print -r 'a\\b not matched by' $str1 + ) +0:globsubst with backslashes +>\\ matched by \\*\\ +>\\foo matched by \\*\\ +>a\\b not matched by \\*\\ + + ( + setopt globsubst + foo="boring*" + print ${foo+$foo} + print ${foo+"$foo"} + print ${~foo+"$foo"} + ) +0:globsubst together with nested quoted expansion +>boringfile +>boring* +>boringfile + + print -l "${$(print one word)}" "${=$(print two words)}" +0:splitting of $(...) inside ${...} +>one word +>two +>words + + (setopt shwordsplit # ensure this doesn't get set in main shell... + test_splitting () + { + array="one two three" + for e in $array; do + echo "'$e'" + done + } + test_split_var= + echo _${test_split_var:=$(test_splitting)}_ + echo "_${test_split_var}_") +0:SH_WORD_SPLIT inside $(...) inside ${...} +>_'one' 'two' 'three'_ +>_'one' +>'two' +>'three'_ + + print -l "${(f)$(print first line\\nsecond line\\nthird line)}" +0:${(f)$(...)} +>first line +>second line +>third line + + array1=( uno ) + print -l ${(A)newarray=splitting by numbers} + print -l ${(t)newarray} + print -l ${(A)=newarray::=splitting by spaces, actually} + print -l ${(t)newarray} + print -l ${(A)newarray::=$array1} + print -l ${(t)newarray} + print -l ${newarray::=$array1} + print -l ${(t)newarray} + print -l ${newarray::=$array2} + print -l ${(t)newarray} +0:${(A)...=...}, ${(A)...::=...}, ${scalar=$array} +>splitting by numbers +>array +>splitting +>by +>spaces, +>actually +>array +>uno +>array +>uno +>scalar +>the second array +>scalar + + newarray=("split me" "split me" "I\'m yours") + print -l "${(@)newarray}" +0:"${(@)...}" +>split me +>split me +>I'm yours + + foo='$(print Howzat usay)' + print -l ${(e)foo} +0:${(e)...} +>Howzat +>usay + + foo='`print Howzat usay`' + print -l ${(e)foo} +0:Regress ${(e)...} with backticks (see zsh-workers/15871) +>Howzat +>usay + + foo='\u65\123' + print -r ${(g:o:)foo} + foo='\u65\0123^X\C-x' + print -r ${(g::)foo} + foo='^X' + bar='\C-\130' + [[ ${(g:c:)foo} == ${(g:oe:)bar} ]] + echo $? +0:${(g)...} +>eS +>eS^X\C-x +>0 + + foo='I'\''m nearly out of my mind with tedium' + bar=foo + print ${(P)bar} +0:${(P)...} +>I'm nearly out of my mind with tedium +#' deconfuse emacs + + foo=(I could be watching that programme I recorded) + print ${(o)foo} + print ${(oi)foo} + print ${(O)foo} + print ${(Oi)foo} +0:${(o)...}, ${(O)...} +>I I be could programme recorded that watching +>be could I I programme recorded that watching +>watching that recorded programme could be I I +>watching that recorded programme I I could be + + foo=(yOU KNOW, THE ONE WITH wILLIAM dALRYMPLE) + bar=(doing that tour of India.) + print ${(L)foo} + print ${(U)bar} +0:${(L)...}, ${(U)...} +>you know, the one with william dalrymple +>DOING THAT TOUR OF INDIA. + + foo='instead here I am stuck by the computer' + print ${(C)foo} +0:${(C)...} +>Instead Here I Am Stuck By The Computer + + foo=$'\x7f\x00' + print -r -- ${(V)foo} +0:${(V)...} +>^?^@ + + foo='playing '\''stupid'\'' "games" \w\i\t\h $quoting.' + print -r ${(q)foo} + print -r ${(qq)foo} + print -r ${(qqq)foo} + print -r ${(qqqq)foo} + print -r ${(q-)foo} +0:${(q...)...} +>playing\ \'stupid\'\ \"games\"\ \\w\\i\\t\\h\ \$quoting. +>'playing '\''stupid'\'' "games" \w\i\t\h $quoting.' +>"playing 'stupid' \"games\" \\w\\i\\t\\h \$quoting." +>$'playing \'stupid\' "games" \\w\\i\\t\\h $quoting.' +>'playing '\'stupid\'' "games" \w\i\t\h $quoting.' + + print -r ${(qqqq):-""} +0:workers/36551: literal empty string in ${(qqqq)...} +>$'' + + x=( a '' '\b' 'c d' '$e' ) + print -r ${(q)x} + print -r ${(q-)x} +0:Another ${(q...)...} test +>a '' \\b c\ d \$e +>a '' '\b' 'c d' '$e' + + print -r -- ${(q-):-foo} + print -r -- ${(q-):-foo bar} + print -r -- ${(q-):-"*(.)"} + print -r -- ${(q-):-"wow 'this is cool' or is it?"} + print -r -- ${(q-):-"no-it's-not"} +0:${(q-)...} minimal single quoting +>foo +>'foo bar' +>'*(.)' +>'wow '\''this is cool'\'' or is it?' +>no-it\'s-not + + foo="'and now' \"even the pubs\" \\a\\r\\e shut." + print -r ${(Q)foo} +0:${(Q)...} +>and now even the pubs are shut. + + foo="X$'\x41'$'\x42'Y" + print -r ${(Q)foo} +0:${(Q)...} with handling of $'...' +>XABY + + # The following may look a bit random. + # For the split we are checking that anything that + # would normally be followed by a different word has + # an argument break after it and anything that wouldn't doesn't. + # For the (Q) we are simply checking that nothing disappears + # in the parsing. + foo='<five> {six} (seven) >eight< }nine{ |forty-two| $many$ )ten( more' + array=(${(z)foo}) + print -l ${(Q)array} +0:${(z)...} and ${(Q)...} for some hard to parse cases +>< +>five +>> +>{six} +>( +>seven +>) +>> +>eight +>< +>}nine{ +>| +>forty-two +>| +>$many$ +>) +>ten( more + + strings=( + 'foo=(1 2 3)' + '(( 3 + 1 == 8 / 2 ))' + 'for (( i = 1 ; i < 10 ; i++ ))' + '((0.25542 * 60) - 15)*60' + 'repeat 3 (x)' + 'repeat 3 (echo foo; echo bar)' + 'repeat $(( 2 + 4 )) (x)' + 'repeat $( : foo bar; echo 4) (x)' + 'repeat "1"'\''2'\''$(( 3 + 0 ))$((echo 4);)\ 5 (x)' + ) + for string in $strings; do + array=(${(z)string}) + for (( i = 1; i <= ${#array}; i++ )); do + print -r -- "${i}:${array[i]}:" + done + done +0:Some syntactical expressions that are hard to split into words with (z). +>1:foo=(: +>2:1: +>3:2: +>4:3: +>5:): +>1:(( 3 + 1 == 8 / 2 )): +>1:for: +>2:((: +# Leading whitespace is removed, because the word proper hasn't started; +# trailing whitespace is left because the word is terminated by the +# semicolon or double parentheses. Bit confusing but sort of consistent. +>3:i = 1 ;: +>4:i < 10 ;: +>5:i++ : +>6:)): +# This one needs resolving between a math expression and +# a command, which causes interesting effects internally. +>1:(: +>2:(: +>3:0.25542: +>4:*: +>5:60: +>6:): +>7:-: +>8:15: +>9:): +>10:*60: +>1:repeat: +>2:3: +>3:(: +>4:x: +>5:): +>1:repeat: +>2:3: +>3:(: +>4:echo: +>5:foo: +>6:;: +>7:echo: +>8:bar: +>9:): +>1:repeat: +>2:$(( 2 + 4 )): +>3:(: +>4:x: +>5:): +>1:repeat: +>2:$( : foo bar; echo 4): +>3:(: +>4:x: +>5:): +>1:repeat: +>2:"1"'2'$(( 3 + 0 ))$((echo 4);)\ 5: +>3:(: +>4:x: +>5:): + + + line=$'A line with # someone\'s comment\nanother line # (1 more\nanother one' + print "*** Normal ***" + print -l ${(z)line} + print "*** Kept ***" + print -l ${(Z+c+)line} + print "*** Removed ***" + print -l ${(Z+C+)line} +0:Comments with (z) +>*** Normal *** +>A +>line +>with +># +>someone's comment +>another line # (1 more +>another one +>*** Kept *** +>A +>line +>with +># someone's comment +>; +>another +>line +># (1 more +>; +>another +>one +>*** Removed *** +>A +>line +>with +>; +>another +>line +>; +>another +>one + + line='with comment # at the end' + print -l ${(Z+C+)line} +0:Test we don't get an additional newline token +>with +>comment + + line=$'echo one\necho two # with a comment\necho three' + print -l ${(Z+nc+)line} +0:Treating zplit newlines as ordinary whitespace +>echo +>one +>echo +>two +># with a comment +>echo +>three + + print -rl - ${(z):-":;(( echo 42 "} +0:${(z)} with incomplete math expressions +>: +>; +>(( echo 42 + + # From parse error on it's not possible to split. + # Just check we get the complete string. + foo='echo $(|||) bar' + print -rl ${(z)foo} +0:$($(z)} with parse error in command substitution. +>echo +>$(|||) bar + + psvar=(dog) + setopt promptsubst + foo='It shouldn'\''t $(happen) to a %1v.' + bar='But `echo what can you do\?`' + print -r ${(%)foo} + print -r ${(%%)bar} +0:${(%)...} +>It shouldn't $(happen) to a dog. +>But what can you do? + + foo='unmatched "' + print ${(QX)foo} +1:${(QX)...} +?(eval):2: unmatched " +# " deconfuse emacs + + array=(characters in an array) + print ${(c)#array} +0:${(c)#...} +>22 + + print ${(w)#array} + str='colon::bolon::solon' + print ${(ws.:.)#str} + print ${(Ws.:.)#str} +0:${(w)...}, ${(W)...} +>4 +>3 +>5 + + typeset -A assoc + assoc=(key1 val1 key2 val2) + print ${(o)assoc} + print ${(ok)assoc} + print ${(ov)assoc} + print ${(okv)assoc} +0:${(k)...}, ${(v)...} +>val1 val2 +>key1 key2 +>val1 val2 +>key1 key2 val1 val2 + + word="obfuscatory" + print !${(l.16.)word}! +${(r.16.)word}+ +0:simple padding +>! obfuscatory! +obfuscatory + + + foo=(resulting words uproariously padded) + print ${(pl.10..\x22..X.)foo} +0:${(pl...)...} +>Xresulting """"Xwords roariously """Xpadded +#" deconfuse emacs + + print ${(l.5..X.r.5..Y.)foo} + print ${(l.6..X.r.4..Y.)foo} + print ${(l.7..X.r.3..Y.)foo} + print ${(l.6..X..A.r.6..Y..B.)foo} + print ${(l.6..X..AROOGA.r.6..Y..BARSOOM.)foo} +0:simultaneous left and right padding +>Xresulting XXXwordsYY proariousl XXpaddedYY +>XXresultin XXXXwordsY uproarious XXXpaddedY +>XXXresulti XXXXXwords Xuproariou XXXXpadded +>XAresultingB XXXAwordsBYY uproariously XXApaddedBYY +>GAresultingB OOGAwordsBAR uproariously OGApaddedBAR + + foo=(why in goodness name am I doing this) + print ${(r.5..!..?.)foo} +0:${(r...)...} +>why?! in?!! goodn name? am?!! I?!!! doing this? + + array=(I\'m simply putting a brave face on) + print ${(j:--:)array} +0:${(j)...} +>I'm--simply--putting--a--brave--face--on + + print ${(F)array} +0:${(F)...} +>I'm +>simply +>putting +>a +>brave +>face +>on + + string='zometimez zis getz zplit on a z' + print -l ${(s?z?)string} +0:${(s...)...} +>ometime +> +>is get +> +>plit on a + + str=s + arr=(a) + typeset -A ass + ass=(a a) + integer i + float f + print ${(t)str} ${(t)arr} ${(t)ass} ${(t)i} ${(t)f} +0:${(t)...} +>scalar array association-local integer-local float-local + + # it's not quite clear that these are actually right unless you know + # the algorithm: search along the string for the point at which the + # first (last) match occurs, for ## (%%), then take the shortest possible + # version of that for # (%). it's as good a definition as anything. + string='where is the white windmill, whispered walter wisely' + print ${(S)string#h*e} + print ${(S)string##h*e} + print ${(S)string%h*e} + print ${(S)string%%h*e} +0:${(S)...#...} etc. +>wre is the white windmill, whispered walter wisely +>wly +>where is the white windmill, wred walter wisely +>where is the white windmill, wly + + setopt extendedglob + print ${(SI:1:)string##w[^[:space:]]# } + print ${(SI:1+1:)string##w[^[:space:]]# } + print ${(SI:1+1+1:)string##w[^[:space:]]# } + print ${(SI:1+1+1+1:)string##w[^[:space:]]# } +0:${(I:...:)...} +>is the white windmill, whispered walter wisely +>where is the windmill, whispered walter wisely +>where is the white whispered walter wisely +>where is the white windmill, walter wisely + + print ${(MSI:1:)string##w[^[:space:]]# } +0:${(M...)...} +>where + + print ${(R)string//w[a-z]# #} +0:${(R)...} +>is the , + + # This (1) doesn't work with // or / + # (2) perhaps ought to be 18, to be consistent with normal zsh + # substring indexing and with backreferences. + print ${(BES)string##white} +0:${(BE...)...} +>14 19 + + print ${(NS)string##white} +0:${(N)...} +>5 + + string='abcdefghijklmnopqrstuvwxyz' + print ${${string%[aeiou]*}/(#m)?(#e)/${(U)MATCH}} +0:Rule 1: Nested substitutions +>abcdefghijklmnopqrsT + + array=(et Swann avec cette muflerie intermittente) + string="qui reparaissait chez lui" + print ${array[4,5]} + print ${array[4,5][1]} + print ${array[4,5][1][2,3]} + print ${string[4,5]} + print ${string[4,5][1]} +0:Rule 2: Parameter subscripting +>cette muflerie +>cette +>et +> r +> + + foo=stringalongamax + print ${${(P)foo[1,6]}[1,3]} +0:Rule 3: Parameter Name Replacement +>qui + + print "${array[5,6]}" + print "${(j.:.)array[1,2]}" +0:Rule 4: Double-Quoted Joining +>muflerie intermittente +>et:Swann + + print "${${array}[5,7]}" + print "${${(@)array}[1,2]}" +0:Rule 5: Nested Subscripting +>wan +>et Swann + + print "${${(@)array}[1,2]#?}" + print "${(@)${(@)array}[1,2]#?}" +0:Rule 6: Modifiers +>t Swann +>t wann + + array=(she sells z shells by the z shore) + (IFS='+'; print ${(s.s.)array}) +0:Rule 7: Forced Joining, and 8: Forced splitting +>he+ ell +z+ hell +by+the+z+ hore + + setopt shwordsplit + string='another poxy boring string' + print -l ${${string}/o/ } + unsetopt shwordsplit +0:Rule 9: Shell Word Splitting +>an +>ther +>p +>xy +>b +>ring +>string + + setopt nonomatch + foo='b* e*' + print ${(e)~foo} + print ${(e)~=foo} + setopt nomatch +0:Rule 10: Re-Evaluation +>b* e* +>boringfile evenmoreboringfile + + # ${bar} -> $bar here would yield "bad substitution". + bar=confinement + print ${(el.20..X.)${bar}} +0:Rule 11: Padding +>XXXXXXXXXconfinement + + foo=(bar baz) + bar=(ax1 bx1) + print "${(@)${foo}[1]}" + print "${${(@)foo}[1]}" + print -l ${(s/x/)bar} + print -l ${(j/x/s/x/)bar} + print -l ${(s/x/)bar%%1*} +0:Examples in manual on parameter expansion +>b +>bar +>a +>1 b +>1 +>a +>1 +>b +>1 +>a +> b + + set If "this test fails" "we have broken" the shell again + print -l ${1+"$@"} +0:Regression test of ${1+"$@"} bug +>If +>this test fails +>we have broken +>the +>shell +>again + + set If "this test fails" "we have broken" the shell again + print -l "${(A)foo::=$@}" + print -l ${(t)foo} + print -l $foo +0:Regression test of "${(A)foo=$@}" bug +>If this test fails we have broken the shell again +>array +>If +>this test fails +>we have broken +>the +>shell +>again + + local sure_that='sure that' varieties_of='varieties of' one=1 two=2 + extra=(5 4 3) + unset foo + set Make $sure_that "this test keeps" on 'preserving all' "$varieties_of" quoted whitespace + print -l ${=1+"$@"} + print -l ${(A)=foo=Make $sure_that "this test keeps" on 'preserving all' "$varieties_of" quoted whitespace} + print ${(t)foo} + print -l ${=1+$one $two} + print -l ${1+$extra$two$one} +0:Regression test of ${=1+"$@"} bug and some related expansions +>Make +>sure that +>this test keeps +>on +>preserving all +>varieties of +>quoted +>whitespace +>Make +>sure +>that +>this test keeps +>on +>preserving all +>varieties of +>quoted +>whitespace +>array +>1 +>2 +>5 +>4 +>321 + + splitfn() { + emulate -L sh + local HOME="/differs from/bash" foo='1 2' bar='3 4' + print -l ${1:-~} + touch has\ space + print -l ${1:-*[ ]*} + print -l ${1:-*[\ ]*} + print -l ${1:-*} + print -l ${1:-"$foo" $bar} + print -l ${==1:-$foo $bar} + rm has\ space + } + splitfn +0:More bourne-shell-compatible nested word-splitting with wildcards and ~ +>/differs from/bash +>*[ +>]* +>has space +>boringfile +>evenmoreboringfile +>has space +>1 2 +>3 +>4 +>1 2 3 4 + + splitfn() { + local IFS=.- + local foo=1-2.3-4 + # + print "Called with argument '$1'" + print "No quotes" + print -l ${=1:-1-2.3-4} ${=1:-$foo} + print "With quotes on default argument only" + print -l ${=1:-"1-2.3-4"} ${=1:-"$foo"} + } + print 'Using "="' + splitfn + splitfn 5.6-7.8 + # + splitfn() { + emulate -L zsh + setopt shwordsplit + local IFS=.- + local foo=1-2.3-4 + # + print "Called with argument '$1'" + print "No quotes" + print -l ${1:-1-2.3-4} ${1:-$foo} + print "With quotes on default argument only" + print -l ${1:-"1-2.3-4"} ${1:-"$foo"} + } + print Using shwordsplit + splitfn + splitfn 5.6-7.8 +0:Test of nested word splitting with and without quotes +>Using "=" +>Called with argument '' +>No quotes +>1 +>2 +>3 +>4 +>1 +>2 +>3 +>4 +>With quotes on default argument only +>1-2.3-4 +>1-2.3-4 +>Called with argument '5.6-7.8' +>No quotes +>5 +>6 +>7 +>8 +>5 +>6 +>7 +>8 +>With quotes on default argument only +>5 +>6 +>7 +>8 +>5 +>6 +>7 +>8 +>Using shwordsplit +>Called with argument '' +>No quotes +>1 +>2 +>3 +>4 +>1 +>2 +>3 +>4 +>With quotes on default argument only +>1-2.3-4 +>1-2.3-4 +>Called with argument '5.6-7.8' +>No quotes +>5 +>6 +>7 +>8 +>5 +>6 +>7 +>8 +>With quotes on default argument only +>5 +>6 +>7 +>8 +>5 +>6 +>7 +>8 + +# Tests a long-standing bug with joining on metafied characters in IFS + (array=(one two three) + IFS=$'\0' + foo="$array" + for (( i = 1; i <= ${#foo}; i++ )); do + char=${foo[i]} + print $(( #char )) + done) +0:Joining with NULL character from IFS +>111 +>110 +>101 +>0 +>116 +>119 +>111 +>0 +>116 +>104 +>114 +>101 +>101 + + unset SHLVL + (( SHLVL++ )) + print $SHLVL +0:Unsetting and recreation of numerical special parameters +>1 + + unset manpath + print $+MANPATH + manpath=(/here /there) + print $MANPATH + unset MANPATH + print $+manpath + MANPATH=/elsewhere:/somewhere + print $manpath +0:Unsetting and recreation of tied special parameters +>0 +>/here:/there +>0 +>/elsewhere /somewhere + + local STRING=a:b + typeset -T STRING string + print $STRING $string + unset STRING + set -A string x y z + print $STRING $string + STRING=a:b + typeset -T STRING string + print $STRING $string + unset STRING + set -A string x y z + print $STRING $string + STRING=a:b + typeset -T STRING string + print $STRING $string + unset string + STRING=x:y:z + print $STRING $string + STRING=a:b + typeset -T STRING string + print $STRING $string + unset string + STRING=x:y:z + print $STRING $string +0:Unsetting and recreation of tied normal parameters +>a:b a b +>x y z +>a:b a b +>x y z +>a:b a b +>x:y:z +>a:b a b +>x:y:z + + typeset -T tied1 tied2 + + typeset -T tied2 tied1 + +1:Attempts to swap tied variables are safe but futile +?(eval):typeset:2: already tied as non-scalar: tied2 + + string='look for a match in here' + if [[ ${string%%(#b)(match)*} = "look for a " ]]; then + print $match[1] $mbegin[1] $mend[1] $string[$mbegin[1],$mend[1]] + print $#match $#mbegin $#mend + else + print That didn\'t work. + fi +0:Parameters associated with backreferences +>match 12 16 match +>1 1 1 +#' deconfuse emacs + + string='and look for a MATCH in here' + if [[ ${(S)string%%(#m)M*H} = "and look for a in here" ]]; then + print $MATCH $MBEGIN $MEND $string[$MBEGIN,$MEND] + print $#MATCH + else + print Oh, dear. Back to the drawing board. + fi +0:Parameters associated with (#m) flag +>MATCH 16 20 MATCH +>5 + + string='this is a string' + print ${string//(#m)s/$MATCH $MBEGIN $MEND} +0:(#m) flag with pure string +>this 4 4 is 7 7 a s 11 11tring + + print ${${~:-*}//(#m)*/$MATCH=$MATCH} +0:(#m) flag with tokenized input +>*=* + + print -l JAMES${(u)${=:-$(echo yes yes)}}JOYCE + print -l JAMES${(u)${=:-$(echo yes yes she said yes i will yes)}}JOYCE +0:Bug with (u) flag reducing arrays to one element +>JAMESyesJOYCE +>JAMESyes +>she +>said +>i +>willJOYCE + + print -l JAMES${(u)${=:-$(echo yes yes she said yes i will yes she said she will and yes she did yes)}}JOYCE +0:New hash seive unique algorithm for arrays of more than 10 elements +>JAMESyes +>she +>said +>i +>will +>and +>didJOYCE + + foo= + print "${${foo}/?*/replacement}" +0:Quoted zero-length strings are handled properly +> + + file=aleftkept + print ${file//(#b)(*)left/${match/a/andsome}} + print ${file//(#b)(*)left/${match//a/andsome}} +0:Substitutions where $match is itself substituted in the replacement +>andsomekept +>andsomekept + + file=/one/two/three/four + print ${file:fh} + print ${file:F.1.h} + print ${file:F+2+h} + print ${file:F(3)h} + print ${file:F<4>h} + print ${file:F{5}h} +0:Modifiers with repetition +>/ +>/one/two/three +>/one/two +>/one +>/ +>/ + + baz=foo/bar + zab=oof+rab + print ${baz:s/\//+/} + print "${baz:s/\//+/}" + print ${zab:s/+/\//} + print "${zab:s/+/\//}" +0:Quoting of separator in substitution modifier +>foo+bar +>foo+bar +>oof/rab +>oof/rab + + bsbs='X\\\\Y' + print -r -- ${bsbs:s/\\/\\/} + print -r -- "${bsbs:s/\\/\\/}" + print -r -- ${bsbs:s/\\\\/\\\\/} + print -r -- "${bsbs:s/\\\\/\\\\/}" + print -r -- ${bsbs:gs/\\/\\/} + print -r -- "${bsbs:gs/\\/\\/}" + print -r -- ${bsbs:gs/\\\\/\\\\/} + print -r -- "${bsbs:gs/\\\\/\\\\/}" +0:Handling of backslashed backslashes in substitution modifier +>X\\\\Y +>X\\\\Y +>X\\\\Y +>X\\\\Y +>X\\\\Y +>X\\\\Y +>X\\\\Y +>X\\\\Y + + print -r ${${:-one/two}:s,/,X&Y,} + print -r ${${:-one/two}:s,/,X\&Y,} + print -r ${${:-one/two}:s,/,X\\&Y,} + print -r "${${:-one/two}:s,/,X&Y,}" + print -r "${${:-one/two}:s,/,X\&Y,}" + print -r "${${:-one/two}:s,/,X\\&Y,}" +0:Quoting of ampersand in substitution modifier RHS +>oneX/Ytwo +>oneX&Ytwo +>oneX\/Ytwo +>oneX/Ytwo +>oneX&Ytwo +>oneX\/Ytwo + + nully=($'a\0c' $'a\0b\0b' $'a\0b\0a' $'a\0b\0' $'a\0b' $'a\0' $'a') + for string in ${(o)nully}; do + for (( i = 1; i <= ${#string}; i++ )); do + foo=$string[i] + printf "%02x" $(( #foo )) + done + print + done +0:Sorting arrays with embedded nulls +>61 +>6100 +>610062 +>61006200 +>6100620061 +>6100620062 +>610063 + + array=(X) + patterns=("*X*" "spong" "a[b") + for pat in $patterns; do + print A${array[(r)$pat]}B C${array[(I)$pat]}D + done +0:Bad patterns should never match array elements +>AXB C1D +>AB C0D +>AB C0D + + foo=(a6 a117 a17 b6 b117 b17) + print ${(n)foo} + print ${(On)foo} +0:Numeric sorting +>a6 a17 a117 b6 b17 b117 +>b117 b17 b6 a117 a17 a6 + + x=sprodj + x[-10]=scrumf + print $x +0:Out of range negative scalar subscripts +>scrumfsprodj + + a=(some sunny day) + a[-10]=(we\'ll meet again) + print -l $a +0:Out of range negative array subscripts +>we'll +>meet +>again +>some +>sunny +>day + +# ' emacs likes this close quote + + a=(sping spang spong bumble) + print ${a[(i)spong]} + print ${a[(i)spung]} + print ${a[(ib.1.)spong]} + print ${a[(ib.4.)spong]} + print ${a[(ib.10.)spong]} +0:In and out of range reverse matched indices without and with b: arrays +>3 +>5 +>3 +>5 +>5 + + a="thrimblewuddlefrong" + print ${a[(i)w]} + print ${a[(i)x]} + print ${a[(ib.3.)w]} + print ${a[(ib.10.)w]} + print ${a[(ib.30.)w]} +0:In and out of range reverse matched indices without and with b: strings +>9 +>20 +>9 +>20 +>20 + + foo="line:with::missing::fields:in:it" + print -l ${(s.:.)foo} +0:Removal of empty fields in unquoted splitting +>line +>with +>missing +>fields +>in +>it + + foo="line:with::missing::fields:in:it" + print -l "${(s.:.)foo}" +0:Hacky removal of empty fields in quoted splitting with no "@" +>line +>with +>missing +>fields +>in +>it + + foo="line:with::missing::fields:in:it:" + print -l "${(@s.:.)foo}" +0:Retention of empty fields in quoted splitting with "@" +>line +>with +> +>missing +> +>fields +>in +>it +> + + str=abcd + print -l ${(s..)str} + print -l "${(s..)str}" +0:splitting of strings into characters +>a +>b +>c +>d +>a +>b +>c +>d + + array=('%' '$' 'j' '*' '$foo') + print ${array[(i)*]} "${array[(i)*]}" + print ${array[(ie)*]} "${array[(ie)*]}" + key='$foo' + print ${array[(ie)$key]} "${array[(ie)$key]}" + key='*' + print ${array[(ie)$key]} "${array[(ie)$key]}" +0:Matching array indices with and without quoting +>1 1 +>4 4 +>5 5 +>4 4 + +# Ordering of associative arrays is arbitrary, so we need to use +# patterns that only match one element. + typeset -A assoc_r + assoc_r=(star '*' of '*this*' and '!that!' or '(the|other)') + print ${(kv)assoc_r[(re)*]} + print ${(kv)assoc_r[(re)*this*]} + print ${(kv)assoc_r[(re)!that!]} + print ${(kv)assoc_r[(re)(the|other)]} + print ${(kv)assoc_r[(r)*at*]} + print ${(kv)assoc_r[(r)*(ywis|bliss|kiss|miss|this)*]} + print ${(kv)assoc_r[(r)(this|that|\(the\|other\))]} +0:Reverse subscripting associative arrays with literal matching +>star * +>of *this* +>and !that! +>or (the|other) +>and !that! +>of *this* +>or (the|other) + + print $ZSH_SUBSHELL + (print $ZSH_SUBSHELL) + ( (print $ZSH_SUBSHELL) ) + ( (print $ZSH_SUBSHELL); print $ZSH_SUBSHELL ) + print $(print $ZSH_SUBSHELL) + cat =(print $ZSH_SUBSHELL) +0:ZSH_SUBSHELL +>0 +>1 +>2 +>2 +>1 +>1 +>1 + + foo=("|" "?") + [[ "|" = ${(j.|.)foo} ]] && print yes || print no + [[ "|" = ${(j.|.)~foo} ]] && print yes || print no + [[ "|" = ${(~j.|.)foo} ]] && print yes || print no + [[ "|" = ${(~~j.|.)foo} ]] && print yes || print no + [[ "|" = ${(j.|.~)foo} ]] && print yes || print no + [[ "x" = ${(j.|.)foo} ]] && print yes || print no + [[ "x" = ${(j.|.)~foo} ]] && print yes || print no + [[ "x" = ${(~j.|.)foo} ]] && print yes || print no + [[ "x" = ${(~~j.|.)foo} ]] && print yes || print no + [[ "x" = ${(j.|.~)foo} ]] && print yes || print no +0:GLOBSUBST only on parameter substitution arguments +>no +>yes +>yes +>no +>no +>no +>yes +>no +>no +>no + + rcexbug() { + emulate -L zsh + setopt rcexpandparam + local -A hash + local -a full empty + full=(X x) + hash=(X x) + print ORDINARY ARRAYS + : The following behaves as documented in zshoptions + print FULL expand=$full + : Empty arrays remove the adjacent argument + print EMPTY expand=$empty + print ASSOCIATIVE ARRAY + print Subscript flags returning many values + print FOUND key=$hash[(I)X] val=$hash[(R)x] + : This should behave like $empty, and does + print LOST key=$hash[(I)y] val=$hash[(R)Y] + print Subscript flags returning single values + : Doc says "substitutes ... empty string" + : so must not behave like an empty array + print STRING key=$hash[(i)y] val=$hash[(r)Y] + } + rcexbug +0:Lookup failures on elements of arrays with RC_EXPAND_PARAM +>ORDINARY ARRAYS +>FULL expand=X expand=x +>EMPTY +>ASSOCIATIVE ARRAY +>Subscript flags returning many values +>FOUND key=X val=x +>LOST +>Subscript flags returning single values +>STRING key= val= + + print $zsh_eval_context[1] + [[ $ZSH_EVAL_CONTEXT = ${(j.:.)zsh_eval_context} ]] || print Not equal! + (( icontext = ${#zsh_eval_context} + 1 )) + contextfn() { print $(print $zsh_eval_context[icontext,-1]); } + contextfn +0:$ZSH_EVAL_CONTEXT and $zsh_eval_context +>toplevel +>shfunc cmdsubst + + foo="123456789" + print ${foo:3} + print ${foo: 1 + 3} + print ${foo:$(( 2 + 3))} + print ${foo:$(echo 3 + 3)} + print ${foo:3:1} + print ${foo: 1 + 3:(4-2)/2} + print ${foo:$(( 2 + 3)):$(( 7 - 6 ))} + print ${foo:$(echo 3 + 3):`echo 4 - 3`} + print ${foo: -1} + print ${foo: -10} + print ${foo:5:-2} +0:Bash-style offsets, scalar +>456789 +>56789 +>6789 +>789 +>4 +>5 +>6 +>7 +>9 +>123456789 +>67 + + foo=(1 2 3 4 5 6 7 8 9) + print ${foo:3} + print ${foo: 1 + 3} + print ${foo:$(( 2 + 3))} + print ${foo:$(echo 3 + 3)} + print ${foo:3:1} + print ${foo: 1 + 3:(4-2)/2} + print ${foo:$(( 2 + 3)):$(( 7 - 6 ))} + print ${foo:$(echo 3 + 3):`echo 4 - 3`} + print ${foo: -1} + print ${foo: -10} + print ${foo:5:-2} +0:Bash-style offsets, array +>4 5 6 7 8 9 +>5 6 7 8 9 +>6 7 8 9 +>7 8 9 +>4 +>5 +>6 +>7 +>9 +>1 2 3 4 5 6 7 8 9 +>6 7 + + testfn() { + emulate -L sh + set -A foo 1 2 3 + set -- 1 2 3 + str=abc + echo ${foo[*]:0:1} + echo ${foo[*]:1:1} + echo ${foo[*]: -1:1} + : + echo ${*:0:1} + echo ${*:1:1} + echo ${*: -1:1} + : + echo ${str:0:1} + echo ${str:1:1} + echo ${str: -1:1} + } + testfn +0:Bash-style offsets, Bourne-style indexing +>1 +>2 +>3 +>testfn +>1 +>3 +>a +>b +>c + + printf "%n" '[0]' +1:Regression test for identifier test +?(eval):1: not an identifier: [0] + + str=rts + print ${str:0:} +1:Regression test for missing length after offset +?(eval):2: unrecognized modifier + + foo="123456789" + print ${foo:5:-6} +1:Regression test for total length < 0 in string +?(eval):2: substring expression: 3 < 5 + + foo=(1 2 3 4 5 6 7 8 9) + print ${foo:5:-6} +1:Regression test for total length < 0 in array +?(eval):2: substring expression: 3 < 5 + + foo=(${(0)"$(print -n)"}) + print ${#foo} +0:Nularg removed from split empty string +>0 + + (set -- a b c + setopt shwordsplit + IFS= + print -rl "$*" + unset IFS + print -rl "$*") +0:Regression test for shwordsplit with null or unset IFS and quoted array +>abc +>a b c + + foo= + print ${foo:wq} + print ${:wq} +0:Empty parameter should not cause modifiers to crash the shell +> +> + +# This used to cause uncontrolled behaviour, but at best +# you got the wrong output so the check is worth it. + args() { print $#; } + args ${:*} + args ${:|} +0:Intersection and disjunction with empty parameters +>0 +>0 + + foo=(a b c) + bar=(1 2 3) + print ${foo:^bar} + print ${foo:^^bar} + foo=(a b c d) + bar=(1 2) + print ${foo:^bar} + print ${foo:^^bar} + foo=('a a' b) + bar=(1 '2 2') + print -l "${foo:^bar}" + print -l "${(@)foo:^bar}" +0:Zipping arrays, correct output +>a 1 b 2 c 3 +>a 1 b 2 c 3 +>a 1 b 2 +>a 1 b 2 c 1 d 2 +# maybe this should be changed to output "a a b 1" +>a a b +>1 +>a a +>1 +>b +>2 2 + + foo=(a b c) + bar=() + print ${foo:^bar} + print ${foo:^^bar} + print ${bar:^foo} + print ${bar:^^foo} + print ${bar:^bar} + print ${bar:^^bar} +0:Zipping arrays, one or both inputs empty +> +>a b c +> +>a b c +> +> + + foo=text + bar=() + print ${foo:^bar} + print ${bar:^^foo} + bar=other + print ${foo:^bar} + bar=(array elements) + print ${foo:^bar} + print ${foo:^^bar} + print ${bar:^foo} + print ${bar:^^foo} +0:Zipping arrays, scalar input +> +>text +>text other +>text array +>text array text elements +>array text +>array text elements text + + foo=(a b c) + print ${foo:^^^bar} +1:Zipping arrays, parsing +?(eval):2: not an identifier: ^bar + + (setopt nounset + print ${foo:^noexist}) +1:Zipping arrays, NO_UNSET part 1 +?(eval):2: noexist: parameter not set + + (setopt nounset + print ${noexist:^foo}) +1:Zipping arrays, NO_UNSET part 2 +?(eval):2: noexist: parameter not set + + expr="a@b,c@d:e@f,g@h:i@j,k@l" + for sep in : , @; do + print -l ${(ps.$sep.)expr} + done +0:Use of variable to get separator when splitting parameter +>a@b,c@d +>e@f,g@h +>i@j,k@l +>a@b +>c@d:e@f +>g@h:i@j +>k@l +>a +>b,c +>d:e +>f,g +>h:i +>j,k +>l + + SHLVL=1 + $ZTST_testdir/../Src/zsh -fc 'echo $SHLVL' + $ZTST_testdir/../Src/zsh -fc '(echo $SHLVL)' +0:SHLVL appears sensible when about to exit shell +>2 +>2 + + # SHLVL is incremented twice and decremented once in between. + SHLVL=1 + $ZTST_testdir/../Src/zsh -fc $ZTST_testdir/../Src/zsh' -fc "echo \$SHLVL"' + $ZTST_testdir/../Src/zsh -fc '('$ZTST_testdir/../Src/zsh' -fc "echo \$SHLVL")' + $ZTST_testdir/../Src/zsh -fc '( ('$ZTST_testdir/../Src/zsh' -fc "echo \$SHLVL"))' +0:SHLVL decremented upon implicit exec optimisation +>2 +>2 +>2 + + # SHLVL is incremented twice with no decrement in between. + SHLVL=1 + $ZTST_testdir/../Src/zsh -fc '('$ZTST_testdir/../Src/zsh' -fc "echo \$SHLVL"); exit' + $ZTST_testdir/../Src/zsh -fc '(exec '$ZTST_testdir/../Src/zsh' -fc "echo \$SHLVL"); exit' + $ZTST_testdir/../Src/zsh -fc '( ('$ZTST_testdir/../Src/zsh' -fc "echo \$SHLVL"); exit)' +0:SHLVL not decremented upon exec in subshells +>3 +>3 +>3 + +# The following tests the return behaviour of parsestr/parsestrnoerr + alias param-test-alias='print $'\''\x45xpanded in substitution'\' + param='$(param-test-alias)' + print ${(e)param} +0:Alias expansion in command substitution in parameter evaluation +>Expanded in substitution + + a=1 b=2 c=3 + : One; + function { + : Two + echo $_ + print -l $argv + } $_ Three + print -l $_ Four; +0:$_ with anonymous function +>Two +>One +>Three +>Three +>Four + + a=1 b=2 c=3 + : One + function { + : Two + echo $_ + print -l $argv + } + print -l "$_" Four +0:$_ with anonymous function without arguments +>Two +> +> +>Four + + funnychars='The qu*nk br!wan f@x j/mps o[]r \(e la~# ^"&;' + [[ $funnychars = ${~${(b)funnychars}} ]] +0:${(b)...} quoting protects from GLOB_SUBST + + set -- foo + echo $(( $#*3 )) + emulate sh -c 'nolenwithoutbrace() { echo $#-1; }' + nolenwithoutbrace +0:Avoid confusion after overloaded characters in braceless substitution in sh +>13 +>0-1 + + a="aaa bab cac" + b=d + echo $a:gs/a/${b}/ + a=(aaa bab cac) + echo $a:gs/a/${b}/ +0:History modifier works the same for scalar and array substitution +>ddd bdb cdc +>ddd bdb cdc + + a=1_2_3_4_5_6 + print ${a#(*_)(#c2)} + print ${a#(*_)(#c5)} + print ${a#(*_)(#c7)} +0:Complicated backtracking with match counts +>3_4_5_6 +>6 +>1_2_3_4_5_6 + + (setopt shwordsplit + do_test() { + print $#: "$@" + } + foo=bar + foo2="bar bar" + do_test ${:- foo } + do_test ${:- foo bar } + do_test ${:- $foo } + do_test ${:- $foo2 } + do_test x${:- foo }y + do_test x${:- foo bar }y + do_test x${:- $foo }y + do_test x${:- $foo2 }y + do_test x${foo:+ $foo }y + ) +0:We Love SH_WORD_SPLIT Day celebrated with space at start of internal subst +>1: foo +>2: foo bar +>1: bar +>2: bar bar +>3: x foo y +>4: x foo bar y +>3: x bar y +>4: x bar bar y +>3: x bar y + + (unsetopt shwordsplit # default, for clarity + do_test() { + print $#: "$@" + } + foo=bar + foo2="bar bar" + do_test ${:- foo } + do_test ${:- foo bar } + do_test ${:- $foo } + do_test ${:- $foo2 } + do_test x${:- foo }y + do_test x${:- foo bar }y + do_test x${:- $foo }y + do_test x${:- $foo2 }y + do_test x${foo:+ $foo }y + ) +0:We Love NO_SH_WORD_SPLIT Even More Day celebrated as sanity check +>1: foo +>1: foo bar +>1: bar +>1: bar bar +>1: x foo y +>1: x foo bar y +>1: x bar y +>1: x bar bar y +>1: x bar y + + testfn() { + local scalar=obfuscation + local -a array=(alpha bravo charlie delta echo foxtrot) + local -A assoc=(one eins two zwei three drei four vier) + local name subscript + for name subscript in scalar 3 array 5 assoc three; do + print ${${(P)name}[$subscript]} + done + } + testfn +0:${(P)...} with normal subscripting +>f +>echo +>drei + + testfn() { + local s1=foo s2=bar + local -a val=(s1) + print ${${(P)val}[1,3]} + val=(s1 s2) + print ${${(P)val}[1,3]} + } + testfn +1:${(P)...} with array as name +>foo +?testfn:5: parameter name reference used with array + + testfn() { + local -A assoc=(one buckle two show three knock four door) + local name='assoc[two]' + print ${${(P)name}[2,3]} + } + testfn +0:${(P)...} with internal subscripting +>ho + + testfn() { + local one=two + local two=three + local three=four + local -a four=(all these worlds belong to foo) + print ${(P)${(P)${(P)one}}} + print ${${(P)${(P)${(P)one}}}[3]} + } + testfn +0:nested parameter name references +>all these worlds belong to foo +>worlds + + ( + path=(/random /value) + testfn1() { + local path= + print $#path + } + testfn1 + testfn2() { + local path=/somewhere + print $#path $path + } + testfn2 + print $#path $path + ) +0:Local special variables with loose typing +>0 +>1 /somewhere +>2 /random /value + + print -r -- ${(q+):-} + print -r -- ${(q+)IFS} + print -r -- ${(q+):-oneword} + print -r -- ${(q+):-two words} + print -r -- ${(q+):-three so-called \'words\'} + (setopt rcquotes; print -r -- ${(q+):-three so-called \'words\'}) +0:${(q+)...} +>'' +>$' \t\n\C-@' +>oneword +>'two words' +>'three so-called '\''words'\' +>'three so-called ''words''' + + array=(one two three) + array[1]=${nonexistent:-foo} + print $array +0:"-" works after "[" in same expression (Dash problem) +>foo two three + + ( + setopt shwordsplit + set -- whim:wham:whom + IFS=: + print -l $@ + ) +0:Splitting of $@ on IFS: single element +>whim +>wham +>whom + + ( + setopt shwordsplit + set -- one:two bucklemy:shoe + IFS=: + print -l $@ + ) +0:Splitting of $@ on IFS: multiple elements +# No forced joining in this case +>one +>two +>bucklemy +>shoe + + ( + set -- one:two bucklemy:shoe + print -l ${(s.:.)@} + ) +0:Splitting of $@ on (s): multiple elements +# Forced joining in this case +>one +>two bucklemy +>shoe + + ( + set -- one:two bucklemy:shoe + print -l ${(@s.:.)@} + ) +0:Splitting of $@ on (@s): multiple elements +# Forced non-joining in this case +>one +>two +>bucklemy +>shoe + + ( + set -- one:two bucklemy:shoe + IFS= + setopt shwordsplit + print -l ${@} ${(s.:.)*} ${(s.:.j.-.)*} + ) +0:Joining of $@ does not happen when IFS is empty, but splitting $* does +>one:two +>bucklemy:shoe +>one +>twobucklemy +>shoe +>one +>two-bucklemy +>shoe + + ( + set -- "one two" "bucklemy shoe" + IFS= + setopt shwordsplit rcexpandparam + print -l "X${(@j.-.)*}" + ) +0:Use of @ does not prevent forced join with j +>Xone two-bucklemy shoe + + () { print -r -- "${(q)1}" "${(b)1}" "${(qq)1}" } '=foo' +0:(q) and (b) quoting deal with the EQUALS option +>\=foo =foo '=foo' + + args() { print $#; } + a=(foo) + args "${a[3,-1]}" + args "${(@)a[3,-1]}" +0:Out-of-range multiple array subscripts with quoting, with and without (@) +>1 +>0 + + a='~-/'; echo $~a +0:Regression: "-" became Dash in workers/37689, breaking ~- expansion +*>* +F:We do not care what $OLDPWD is, as long as it doesn't cause an error diff --git a/dotfiles/system/.zsh/modules/Test/D05array.ztst b/dotfiles/system/.zsh/modules/Test/D05array.ztst new file mode 100644 index 0000000..1fa607d --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/D05array.ztst @@ -0,0 +1,112 @@ +# Tests for array indexing + +%prep + + foo=(a b c d e f g) + arr=(foo bar baz) + mkdir array.tmp + touch array.tmp/{1..9} + +%test + + echo .$foo[1]. +0:The first element +>.a. + + echo .$foo[1,4]. +0:Normal multi-item indexing +>.a b c d. + + echo .$foo[1,0]. +0:This should be empty +>.. + + echo .$foo[4,1]. +0:Another empty slice +>.. + + echo .$foo[1,-8]. +0:An empty slice with a negative end +>.. + + echo .$foo[0]. +0:Treat 0 as empty +>.. + + echo .$foo[0,0]. +0:Treat 0,0 as empty +>.. + + echo .$foo[0,1]. +0:Another weird way to access the first element +>.a. + + echo .$foo[3]. +0:An inner element +>.c. + + echo .$foo[2,2]. +0:Another inner element +>.b. + + echo .$foo[2,-4]. +0:A slice with a negative end +>.b c d. + + echo .$foo[-4,5]. +0:A slice with a negative start +>.d e. + + echo .$foo[-6,-2]. +0:A slice with a negative start and end +>.b c d e f. + + echo .${${arr[2]}[1]}. + echo .${${arr[-2]}[1]}. + echo .${${arr[2,2]}[1]}. + echo .${${arr[-2,-2]}[1]}. + echo .${${arr[2,-2]}[1]}. + echo .${${arr[-2,2]}[1]}. +0:Slices should return an array, elements a scalar +>.b. +>.b. +>.bar. +>.bar. +>.bar. +>.bar. + + setopt ksh_arrays + echo .${foo[1,2]}. + unsetopt ksh_arrays +0:Ksh array indexing +>.b c. + + setopt ksh_arrays + echo .${foo[0,1]}. + unsetopt ksh_arrays +0:Ksh array indexing (ii) +>.a b. + + setopt ksh_arrays + echo .${foo[1,-1]}. + unsetopt ksh_arrays +0:Ksh array indexing (iii) +>.b c d e f g. + + cd array.tmp + echo . ?([3,5]) . + cd .. +0:Glob array indexing +>. 3 4 5 . + + cd array.tmp + echo . ?([2,-2]) . + cd .. +0:Glob array indexing (ii) +>. 2 3 4 5 6 7 8 . + + cd array.tmp + echo . ?([-6,-4]) . + cd .. +0:Glob array indexing (iii) +>. 4 5 6 . diff --git a/dotfiles/system/.zsh/modules/Test/D06subscript.ztst b/dotfiles/system/.zsh/modules/Test/D06subscript.ztst new file mode 100644 index 0000000..1449236 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/D06subscript.ztst @@ -0,0 +1,268 @@ +# Test parameter subscripting. + +%prep + + s='Twinkle, twinkle, little *, [how] I [wonder] what? You are!' + a=('1' ']' '?' '\2' '\]' '\?' '\\3' '\\]' '\\?' '\\\4' '\\\]' '\\\?') + typeset -g -A A + A=($a) + +%test + + x=',' + print $s[(i)winkle] $s[(I)winkle] + print ${s[(i)You are]} $#s + print ${s[(r)$x,(R)$x]} +0:Scalar pattern subscripts without wildcards +>2 11 +>53 60 +>, twinkle, little *, + + x='*' + print $s[(i)*] $s[(i)\*] $s[(i)$x*] $s[(i)${(q)x}*] $s[(I)$x\*] + print $s[(r)?,(R)\?] $s[(r)\?,(R)?] + print $s[(r)\*,(R)*] + print $s[(r)\],(R)\[] +0:Scalar pattern subscripts with wildcards +>1 26 1 26 26 +>Twinkle, twinkle, little *, [how] I [wonder] what? ? You are! +>*, [how] I [wonder] what? You are! +>] I [ + + print $s[(i)x] : $s[(I)x] + print $s[(r)x] : $s[(R)x] +0:Scalar pattern subscripts that do not match +>61 : 0 +>: + + print -R $s[$s[(i)\[]] $s[(i)$s[(r)\*]] $s[(i)${(q)s[(r)\]]}] +0:Scalar subscripting using a pattern subscript to get the index +>[ 1 33 + + print -R $a[(r)?] $a[(R)?] + print $a[(n:2:i)?] $a[(n:2:I)?] + print $a[(i)\?] $a[(I)\?] + print $a[(i)*] $a[(i)\*] +0:Array pattern subscripts +>1 ? +>2 2 +>3 3 +>1 13 + + # It'd be nice to do some of the following with (r), but we run into + # limitations of the ztst script parsing of backslashes in the output. + print -R $a[(i)\\\\?] $a[(i)\\\\\?] + print -R $a[(i)\\\\\\\\?] $a[(i)\\\\\\\\\?] + print -R ${a[(i)\\\\\\\\?]} ${a[(i)\\\\\\\\\?]} + print -R "$a[(i)\\\\\\\\?] $a[(i)\\\\\\\\\?]" + print -R $a[(i)\]] $a[(i)\\\\\]] $a[(i)\\\\\\\\\]] $a[(i)\\\\\\\\\\\\\]] + print -R $a[(i)${(q)a[5]}] $a[(i)${(q)a[8]}] $a[(i)${(q)a[11]}] + print -R $a[(i)${a[3]}] $a[(i)${a[6]}] $a[(i)${a[9]}] $a[(i)${a[12]}] +0:Array pattern subscripts with multiple backslashes +>4 6 +>7 9 +>7 9 +>7 9 +>2 5 8 11 +>5 8 11 +>1 3 4 6 + + print -R $A[1] $A[?] $A[\\\\3] $A[\\\]] + print -R $A[$a[11]] + print -R $A[${(q)a[5]}] +0:Associative array lookup (direct subscripting) +>] \2 \\] \? +>\\\? +>\\\? + + # The (o) is necessary here for predictable output ordering + print -R $A[(I)\?] ${(o)A[(I)?]} + print -R $A[(i)\\\\\\\\3] + print -R $A[(I)\\\\\\\\\?] ${(o)A[(I)\\\\\\\\?]} +0:Associative array lookup (pattern subscripting) +>? 1 ? +>\\3 +>\\? \\3 \\? + + print -R $A[(R)\?] : ${(o)A[(R)?]} + print -R $A[(R)\\\\\?] ${(o)A[(R)\\\\?]} ${(o)A[(R)\\\\\?]} + print -R ${(o)A[(R)\\\\\\\\\]]} +0:Associative array lookup (reverse subscripting) +>: ] +>\? \2 \? \? +>\\] + + eval 'A[*]=star' +1:Illegal associative array assignment +?(eval):1: A: attempt to set slice of associative array + + x='*' + A[$x]=xstar + A[${(q)x}]=qxstar + print -R ${(k)A[(r)xstar]} $A[$x] + print -R ${(k)A[(r)qxstar]} $A[${(q)x}] + A[(e)*]=star + A[\*]=backstar + print -R ${(k)A[(r)star]} $A[(e)*] + print -R ${(k)A[(r)backstar]} $A[\*] +0:Associative array assignment +>* xstar +>\* qxstar +>* star +>\* backstar + + o='[' + c=']' + A[\]]=cbrack + A[\[]=obrack + A[\\\[]=backobrack + A[\\\]]=backcbrack + print -R $A[$o] $A[$c] $A[\[] $A[\]] $A[\\\[] $A[\\\]] + print -R $A[(i)\[] $A[(i)\]] $A[(i)\\\\\[] $A[(i)\\\\\]] +0:Associative array keys with open and close brackets +>obrack cbrack obrack cbrack backobrack backcbrack +>[ ] \[ \] + + print -R $A[$o] $A[$s[(r)\[]] + print -R $A[(r)$c] $A[(r)$s[(r)\]]] + print -R $A[$A[(i)\\\\\]]] +0:Associative array lookup using a pattern subscript to get the key +>obrack obrack +>] ] +>backcbrack + + print -R ${A[${A[(r)\\\\\\\\\]]}]::=zounds} + print -R ${A[${A[(r)\\\\\\\\\]]}]} + print -R $A[\\\\\]] +0:Associative array substitution-assignment with reverse pattern subscript key +>zounds +>zounds +>zounds + + print -R ${(o)A[(K)\]]} + print -R ${(o)A[(K)\\\]]} +0:Associative array keys interpreted as patterns +>\2 backcbrack cbrack star +>\\\4 \\\? star zounds + +# It doesn't matter which element we get, since we never guarantee +# ordering of an associative array. So just test the number of matches. + array=(${(o)A[(k)\]]}) + print ${#array} + array=(${(o)A[(k)\\\]]}) + print ${#array} +0:Associative array keys interpreted as patterns, single match +>1 +>1 + + typeset -g "A[one\"two\"three\"quotes]"=QQQ + typeset -g 'A[one\"two\"three\"quotes]'=qqq + print -R "$A[one\"two\"three\"quotes]" + print -R $A[one\"two\"three\"quotes] + A[one"two"three"four"quotes]=QqQq + print -R $A[one"two"three"four"quotes] + print -R $A[$A[(i)one\"two\"three\"quotes]] + print -R "$A[$A[(i)one\"two\"three\"quotes]]" +0:Associative array keys with double quotes +>QQQ +>qqq +>QqQq +>qqq +>QQQ + + print ${x::=$A[$A[(i)one\"two\"three\"quotes]]} + print $x + print ${x::="$A[$A[(i)one\"two\"three\"quotes]]"} + print $x +0:More keys with double quotes, used in assignment-expansion +>qqq +>qqq +>QQQ +>QQQ + + qqq=lower + QQQ=upper + print ${(P)A[one\"two\"three\"quotes]} + print "${(P)A[$A[(i)one\"two\"three\"quotes]]}" +0:Keys with double quotes and the (P) expansion flag +>lower +>upper + + typeset -ga empty + echo X${${empty##*}[-1]}X +0:Negative index applied to substition result from empty array +>XX + + print $empty[(i)] $empty[(I)] +0:(i) returns 1 for empty array, (I) returns 0. +>1 0 + + array=(one two three four) + print X$array[0]X +0:Element zero is empty if KSH_ZERO_SUBSCRIPT is off. +>XX + + array[0]=fumble +1:Can't set element zero if KSH_ZERO_SUBSCRIPT is off. +?(eval):1: array: assignment to invalid subscript range + + print X$array[(R)notfound]X +0:(R) returns empty if not found if KSH_ZERO_SUBSCRIPT is off. +>XX + + setopt KSH_ZERO_SUBSCRIPT + print X$array[0]X +0:Element zero is element one if KSH_ZERO_SUBSCRIPT is on. +>XoneX + + array[0]=fimble + print $array +0:Can set element zero if KSH_ZERO_SUBSCRIPT is on. +>fimble two three four + + print X$array[(R)notfound]X +0:(R) yuckily returns the first element on failure withe KSH_ZERO_SUBSCRIPT +>XfimbleX + + unsetopt KSH_ZERO_SUBSCRIPT + array[(R)notfound,(r)notfound]=(help help here come the seventies retreads) + print $array +0:[(R)notfound,(r)notfound] replaces the whole array +>help help here come the seventies retreads + + string="Why, if it isn't Officer Dibble" + print "[${string[0]}][${string[1]}][${string[0,3]}]" +0:String subscripts with KSH_ZERO_SUBSCRIPT unset +>[][W][Why] + + setopt KSH_ZERO_SUBSCRIPT + print "[${string[0]}][${string[1]}][${string[0,3]}]" +0:String subscripts with KSH_ZERO_SUBSCRIPT set +>[W][W][Why] + + unsetopt KSH_ZERO_SUBSCRIPT + string[0,3]="Goodness" + print $string +0:Assignment to chunk of string ignores element 0 +>Goodness, if it isn't Officer Dibble + + string[0]=! +1:Can't set only element zero of string +?(eval):1: string: assignment to invalid subscript range + + typeset -A assoc=(leader topcat officer dibble sidekick choochoo) + alias myind='echo leader' myletter='echo 1' myletter2='echo 4' + print ${assoc[$(myind)]} + print $assoc[$(myind)] + print ${assoc[$(myind)][$(myletter)]}${assoc[$(myind)][$(myletter2)]} + assoc[$(myind)]='of the gang' + print ${assoc[$(myind)]} + print $assoc[$(myind)] + print $assoc[leader] +0: Parsing subscript with non-trivial tokenisation +>topcat +>topcat +>tc +>of the gang +>of the gang +>of the gang diff --git a/dotfiles/system/.zsh/modules/Test/D07multibyte.ztst b/dotfiles/system/.zsh/modules/Test/D07multibyte.ztst new file mode 100644 index 0000000..e203153 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/D07multibyte.ztst @@ -0,0 +1,587 @@ +%prep + +# Find a UTF-8 locale. + setopt multibyte +# Don't let LC_* override our choice of locale. + unset -m LC_\* + mb_ok= + langs=(en_{US,GB}.{UTF-,utf}8 en.UTF-8 + $(locale -a 2>/dev/null | egrep 'utf8|UTF-8')) + for LANG in $langs; do + if [[ é = ? ]]; then + mb_ok=1 + break; + fi + done + if [[ -z $mb_ok ]]; then + ZTST_unimplemented="no UTF-8 locale or multibyte mode is not implemented" + else + print -u $ZTST_fd Testing multibyte with locale $LANG + mkdir multibyte.tmp && cd multibyte.tmp + fi + +%test + + a=ténébreux + for i in {1..9}; do + print ${a[i]} + for j in {$i..9}; do + print $i $j ${a[i,j]} ${a[-j,-i]} + done + done +0:Basic indexing with multibyte characters +>t +>1 1 t x +>1 2 té ux +>1 3 tén eux +>1 4 téné reux +>1 5 ténéb breux +>1 6 ténébr ébreux +>1 7 ténébre nébreux +>1 8 ténébreu énébreux +>1 9 ténébreux ténébreux +>é +>2 2 é u +>2 3 én eu +>2 4 éné reu +>2 5 énéb breu +>2 6 énébr ébreu +>2 7 énébre nébreu +>2 8 énébreu énébreu +>2 9 énébreux ténébreu +>n +>3 3 n e +>3 4 né re +>3 5 néb bre +>3 6 nébr ébre +>3 7 nébre nébre +>3 8 nébreu énébre +>3 9 nébreux ténébre +>é +>4 4 é r +>4 5 éb br +>4 6 ébr ébr +>4 7 ébre nébr +>4 8 ébreu énébr +>4 9 ébreux ténébr +>b +>5 5 b b +>5 6 br éb +>5 7 bre néb +>5 8 breu énéb +>5 9 breux ténéb +>r +>6 6 r é +>6 7 re né +>6 8 reu éné +>6 9 reux téné +>e +>7 7 e n +>7 8 eu én +>7 9 eux tén +>u +>8 8 u é +>8 9 ux té +>x +>9 9 x t + + s=é + print A${s[-2]}A B${s[-1]}B C${s[0]}C D${s[1]}D E${s[2]}E +0:Out of range subscripts with multibyte characters +>AA BéB CC DéD EE + + print ${a[(i)é]} ${a[(I)é]} ${a[${a[(i)é]},${a[(I)é]}]} +0:Reverse indexing with multibyte characters +>2 4 éné + + print ${a[(r)én,(r)éb]} +0:Subscript searching with multibyte characters +>énéb + + print ${a[(rb:1:)é,-1]} + print ${a[(rb:2:)é,-1]} + print ${a[(rb:3:)é,-1]} + print ${a[(rb:4:)é,-1]} + print ${a[(rb:5:)é,-1]} +0:Subscript searching with initial offset +>énébreux +>énébreux +>ébreux +>ébreux +> + + print ${a[(rn:1:)é,-1]} + print ${a[(rn:2:)é,-1]} + print ${a[(rn:3:)é,-1]} +0:Subscript searching with count +>énébreux +>ébreux +> + + print ${a[(R)én,(R)éb]} +0:Backward subscript searching with multibyte characters +>énéb + +# Starting offsets with (R) seem to be so strange as to be hardly +# worth testing. + + setopt extendedglob + [[ $a = (#b)t(én)(éb)reux ]] || print "Failed to match." >&2 + for i in {1..${#match}}; do + print $match[i] $mbegin[i] $mend[i] ${a[$mbegin[i],$mend[i]]} + done +0:Multibyte offsets in pattern tests +>én 2 3 én +>éb 4 5 éb + + b=${(U)a} + print $b + print ${(L)b} + desdichado="Je suis le $a, le veuf, l'inconsolé" + print ${(C)desdichado} + lxiv="l'état c'est moi" + print ${(C)lxiv} +0:Case modification of multibyte strings +>TÉNÉBREUX +>ténébreux +>Je Suis Le Ténébreux, Le Veuf, L'Inconsolé +>L'État C'Est Moi + + array=(ølaf ødd øpened án encyclopædia) + barray=(${(U)array}) + print $barray + print ${(L)barray} + print ${(C)array} + print ${(C)barray} +0:Case modification of arrays with multibyte strings +>ØLAF ØDD ØPENED ÁN ENCYCLOPÆDIA +>ølaf ødd øpened án encyclopædia +>Ølaf Ødd Øpened Án Encyclopædia +>Ølaf Ødd Øpened Án Encyclopædia + + print $(( ##¥ )) + pound=£ + print $(( #pound )) + alpha=α + print $(( ##α )) $(( #alpha )) +0:Conversion to Unicode in mathematical expressions +>165 +>163 +>945 945 + + unsetopt posix_identifiers + expr='hähä=3 || exit 1; print $hähä' + eval $expr + setopt posix_identifiers + (eval $expr) +1:POSIX_IDENTIFIERS option +>3 +?(eval):1: command not found: hähä=3 + + foo="Ølaf«Ødd«øpénëd«ån«àpple" + print -l ${(s.«.)foo} + ioh="Ἐν ἀρχῇ ἦν ὁ λόγος, καὶ ὁ λόγος ἦν πρὸς τὸν θεόν, καὶ θεὸς ἦν ὁ λόγος." + print -l ${=ioh} + print ${(w)#ioh} +0:Splitting with multibyte characters +>Ølaf +>Ødd +>øpénëd +>ån +>àpple +>Ἐν +>ἀρχῇ +>ἦν +>ὁ +>λόγος, +>καὶ +>ὁ +>λόγος +>ἦν +>πρὸς +>τὸν +>θεόν, +>καὶ +>θεὸς +>ἦν +>ὁ +>λόγος. +>17 + + read -d £ one + read -d £ two + print $one + print $two +0:read with multibyte delimiter +<first£second£ +>first +>second + + (IFS=« + read -d » -A array + print -l $array) +0:read -A with multibyte IFS +<dominus«illuminatio«mea»ignored +>dominus +>illuminatio +>mea + + read -k2 -u0 twochars + print $twochars +0:read multibyte characters +<«»ignored +>«» + + read -q -u0 mb + print $? +0:multibyte character makes read -q return false +<« +>1 + + # See if the system grokks first-century Greek... + ioh="Ἐν ἀρχῇ ἦν ὁ λόγος, καὶ ὁ λόγος ἦν πρὸς τὸν θεόν, καὶ θεὸς ἦν ὁ λόγος." + for (( i = 1; i <= ${#ioh}; i++ )); do + # FC3 doesn't recognise ῇ (U+1FC7: Greek small letter eta with + # perispomeni and ypogegrammeni, of course) as a lower case character. + if [[ $ioh[i] != [[:lower:]] && $i != 7 ]]; then + for tp in upper space punct invalid; do + if [[ $tp = invalid || $ioh[i] = [[:${tp}:]] ]]; then + print "$i: $tp" + break + fi + done + fi + done +0:isw* functions on non-ASCII wide characters +>1: upper +>3: space +>8: space +>11: space +>13: space +>19: punct +>20: space +>24: space +>26: space +>32: space +>35: space +>40: space +>44: space +>49: punct +>50: space +>54: space +>59: space +>62: space +>64: space +>70: punct + + ioh="Ἐν ἀρχῇ ἦν ὁ λόγος, καὶ ὁ λόγος ἦν πρὸς τὸν θεόν, καὶ θεὸς ἦν ὁ λόγος" + print ${ioh#[[:alpha:]]##} + print ${ioh##[[:alpha:]]##} + print ${ioh%[[:alpha:]]##} + print ${ioh%%[[:alpha:]]##} + print ${(S)ioh#λ*ς} + print ${(S)ioh##λ*ς} + print ${(S)ioh%θ*ς} + print ${(S)ioh%%θ*ς} +0:Parameter #, ##, %, %% with multibyte characters +>ν ἀρχῇ ἦν ὁ λόγος, καὶ ὁ λόγος ἦν πρὸς τὸν θεόν, καὶ θεὸς ἦν ὁ λόγος +> ἀρχῇ ἦν ὁ λόγος, καὶ ὁ λόγος ἦν πρὸς τὸν θεόν, καὶ θεὸς ἦν ὁ λόγος +>Ἐν ἀρχῇ ἦν ὁ λόγος, καὶ ὁ λόγος ἦν πρὸς τὸν θεόν, καὶ θεὸς ἦν ὁ λόγο +>Ἐν ἀρχῇ ἦν ὁ λόγος, καὶ ὁ λόγος ἦν πρὸς τὸν θεόν, καὶ θεὸς ἦν ὁ +>Ἐν ἀρχῇ ἦν ὁ , καὶ ὁ λόγος ἦν πρὸς τὸν θεόν, καὶ θεὸς ἦν ὁ λόγος +>Ἐν ἀρχῇ ἦν ὁ +>Ἐν ἀρχῇ ἦν ὁ λόγος, καὶ ὁ λόγος ἦν πρὸς τὸν θεόν, καὶ ἦν ὁ λόγος +>Ἐν ἀρχῇ ἦν ὁ λόγος, καὶ ὁ λόγος ἦν πρὸς τὸν θεόν, καὶ + + a="1ë34ë6" + print ${(BEN)a#*4} + print ${(BEN)a##*ë} + print ${(BEN)a%4*} + print ${(BEN)a%%ë*} + print ${(SBEN)a#ë3} + print ${(SBEN)a%4ë} +0:Flags B, E, N and S in ${...#...} and ${...%...} +>1 5 4 +>1 6 5 +>4 7 3 +>2 7 5 +>2 4 2 +>4 6 2 + + foo=(κατέβην χθὲς εἰς Πειραιᾶ) + print ${(l.3..¥.r.3..£.)foo} + print ${(l.4..¥.r.2..£.)foo} + print ${(l.5..¥.r.1..£.)foo} + print ${(l.4..¥..«.r.4..£..».)foo} + print ${(l.4..¥..Σωκράτης.r.4..£..Γλαύκωνος.)foo} +0:simultaneous left and right padding +>κατέβη ¥χθὲς£ ¥¥εἰς£ Πειραι +>¥κατέβ ¥¥χθὲς ¥¥¥εἰς ¥Πειρα +>¥¥κατέ ¥¥¥χθὲ ¥¥¥¥εἰ ¥¥Πειρ +>«κατέβην ¥«χθὲς»£ ¥¥«εἰς»£ «Πειραιᾶ +>ςκατέβην ηςχθὲςΓλ τηςεἰςΓλ ςΠειραιᾶ +# er... yeah, that looks right... + + foo=picobarn + print ${foo:s£bar£rod£:s¥rod¥stick¥} +0:Delimiters in modifiers +>picostickn + +# TODO: if we get paired multibyte bracket delimiters to work +# (as Emacs does, the smug so-and-so), the following should change. + foo=bar + print ${(r£5££X£)foo} + print ${(l«10««Y««HI«)foo} +0:Delimiters in parameter flags +>barXX +>YYYYYHIbar + + printf "%4.3s\n" főobar +0:Multibyte characters in printf widths +> főo + +# 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. + print -oi HÛH HÔH HÎH HÊH HÂH + (LC_ALL=C; print -oi HAH HUH HEH HÉH HÈH) +0:Multibyte characters in print sorting +>HÂH HÊH HÎH HÔH HÛH +>HAH HEH HUH HÈH HÉH + +# These are control characters in Unicode, so don't show up. +# We just want to check they're not being treated as tokens. + for x in {128..150}; do + print ${(#)x} + done | while read line; do + print ${#line} $(( #line )) + done +0:evaluated character number with multibyte characters +>1 128 +>1 129 +>1 130 +>1 131 +>1 132 +>1 133 +>1 134 +>1 135 +>1 136 +>1 137 +>1 138 +>1 139 +>1 140 +>1 141 +>1 142 +>1 143 +>1 144 +>1 145 +>1 146 +>1 147 +>1 148 +>1 149 +>1 150 + + touch ngs1txt ngs2txt ngs10txt ngs20txt ngs100txt ngs200txt + setopt numericglobsort + print -l ngs* +0:NUMERIC_GLOB_SORT option in UTF-8 locale +>ngs1txt +>ngs2txt +>ngs10txt +>ngs20txt +>ngs100txt +>ngs200txt + +# Not strictly multibyte, but gives us a well-defined locale for testing. + foo=$'X\xc0Y\x07Z\x7fT' + print -r ${(q)foo} +0:Backslash-quoting of unprintable/invalid characters uses $'...' +>X$'\300'Y$'\a'Z$'\177'T + +# This also isn't strictly multibyte and is here to reduce the +# likelihood of a "cannot do character set conversion" error. + (print $'\u00e9') 2>&1 | read + if [[ $REPLY != é ]]; then + print "warning: your system can't do simple Unicode conversion." >&$ZTST_fd + print "Check you have a correctly installed iconv library." >&$ZTST_fd + # cheat + repeat 4 print OK + else + testfn() { (LC_ALL=C; print $'\u00e9') } + repeat 4 testfn 2>&1 | while read line; do + if [[ $line = *"character not in range"* ]]; then + print OK + elif [[ $line = "?" ]]; then + print OK + else + print Failed: no error message and no question mark + fi + done + fi + true +0:error handling in Unicode quoting +>OK +>OK +>OK +>OK + + tmp1='glob/\(\)Ą/*' + [[ glob/'()Ą'/foo == $~tmp1 ]] && print "Matched against $tmp1" + tmp1='glob/\(\)Ā/*' + [[ glob/'()Ā'/bar == $~tmp1 ]] && print "Matched against $tmp1" +0:Backslashes and metafied characters in patterns +>Matched against glob/()Ą/* +>Matched against glob/()Ā/* + + mkdir 梶浦由記 'Пётр Ильич Чайковский' + (cd 梶浦由記; print ${${(%):-%~}:t}) + (cd 'Пётр Ильич Чайковский'; print ${${(%):-%~}:t}) +0:Metafied characters in prompt expansion +>梶浦由記 +>Пётр Ильич Чайковский + + ( + setopt nonomatch + tmp1=Ą + tmpA=(Ą 'Пётр Ильич Чайковский' 梶浦由記) + print ${tmp1} ${(%)tmp1} ${(%%)tmp1} + print ${#tmp1} ${#${(%)tmp1}} ${#${(%%)tmp1}} + print ${tmpA} + print ${(%)tmpA} + print ${(%%)tmpA} + ) +0:More metafied characters in prompt expansion +>Ą Ą Ą +>1 1 1 +>Ą Пётр Ильич Чайковский 梶浦由記 +>Ą Пётр Ильич Чайковский 梶浦由記 +>Ą Пётр Ильич Чайковский 梶浦由記 + + setopt cbases + print $'\xc5' | read + print $(( [#16] #REPLY )) +0:read passes through invalid multibyte characters +>0xC5 + + word=abcま + word[-1]= + print $word + word=abcま + word[-2]= + print $word + word=abcま + word[4]=d + print $word + word=abcま + word[3]=not_c + print $word +0:assignment with negative indices +>abc +>abま +>abcd +>abnot_cま + + # The following doesn't necessarily need UTF-8, but this gives + # us the full effect --- if we parse this wrongly the \xe9 + # in combination with the tokenized input afterwards looks like a + # valid UTF-8 character. But it isn't. + print $'$\xe9#``' >test_bad_param + (setopt nonomatch + . ./test_bad_param) +127:Invalid parameter name with following tokenized input +?./test_bad_param:1: command not found: $\M-i# + + lines=$'one\tZSH\tthree\nfour\tfive\tsix' + print -X8 -r -- $lines +0:Tab expansion with extra-wide characters +>one ZSH three +>four five six +# This doesn't look aligned in my editor because actually the characters +# aren't quite double width, but the arithmetic is correct. +# It appears just to be an effect of the font. + + () { + emulate -L zsh + setopt errreturn + local cdpath=(.) + mkdir ホ + cd ホ + cd .. + cd ./ホ + cd .. + } +0:cd with special characters + + test_array=( + '[[ \xcc = \xcc ]]' + '[[ \xcc != \xcd ]]' + '[[ \xcc != \ucc ]]' + '[[ \ucc = \ucc ]]' + '[[ \ucc = [\ucc] ]]' + '[[ \xcc != [\ucc] ]]' + # Not clear how useful the following is... + '[[ \xcc = [\xcc] ]]' + ) + for test in $test_array; do + if ! eval ${(g::)test} ; then + print -rl "Test $test failed" >&2 + fi + done +0:Invalid characters in pattern matching + + [[ $'\xe3' == [[:INCOMPLETE:]] ]] || print fail 1 + [[ $'\xe3\x83' == [[:INCOMPLETE:]][[:INVALID:]] ]] || print fail 2 + [[ $'\xe3\x83\x9b' != [[:INCOMPLETE:][:INVALID:]] ]] || print fail 3 + [[ $'\xe3\x83\x9b' = ? ]] || print fail 4 +0:Testing incomplete and invalid multibyte character components + + print -r -- ${(q+):-ホ} + foo='She said "ホ". I said "You can'\''t '\''ホ'\'' me!' + print -r -- ${(q+)foo} +0:${(q+)...} with printable multibyte characters +>ホ +>'She said "ホ". I said "You can'\''t '\''ホ'\'' me!' + +# This will silently succeed if zsh/parameter isn't available + (zmodload zsh/parameter >/dev/null 2>&1 + f() { + : $(:) + "↓" + } + : $functions) +0:Multibyte handling of functions parameter + +# c1=U+0104 (Ą) and c2=U+0120 (Ġ) are chosen so that +# u1 = utf8(c1) = c4 84 < u2 = utf8(c2) = c4 a0 +# metafy(u1) = c4 83 a4 > metafy(u2) = c4 83 80 +# in both UTF-8 and ASCII collations (the latter is used in macOS +# and some versions of BSDs). + local -a names=( $'\u0104' $'\u0120' ) + print -o $names + mkdir -p colltest + cd colltest + touch $names + print ? +0:Sorting of metafied characters +>Ą Ġ +>Ą Ġ + + printf '%q%q\n' 你你 +0:printf %q and quotestring and general metafy / token madness +>你你 + +# This test is kept last as it introduces an additional +# dependency on the system regex library. + if zmodload zsh/regex 2>/dev/null; then + [[ $'\ua0' =~ '^.$' ]] && print OK + [[ $'\ua0' =~ $'^\ua0$' ]] && print OK + [[ $'\ua0'X =~ '^X$' ]] || print OK + else + ZTST_skip="regexp library not found." + fi +0:Ensure no confusion on metafied input to regex module +>OK +>OK +>OK +F:A failure here may indicate the system regex library does not +F:support character sets outside the portable 7-bit range. diff --git a/dotfiles/system/.zsh/modules/Test/D08cmdsubst.ztst b/dotfiles/system/.zsh/modules/Test/D08cmdsubst.ztst new file mode 100644 index 0000000..3625373 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/D08cmdsubst.ztst @@ -0,0 +1,169 @@ +# Tests for command substitution. + +%prep + mkdir cmdsubst.tmp + touch cmdsubst.tmp/file{1,2}.txt + +%test + foo="two words" + print -l `echo $foo bar` +0:Basic `...` substitution +>two +>words +>bar + + foo="two words" + print -l $(echo $foo bar) +0:Basic $(...) substitution +>two +>words +>bar + + foo='intricate buffoonery' + print -l "`echo $foo and licentiousness`" +0:Quoted `...` substitution +>intricate buffoonery and licentiousness + + foo="more words" + print -l "$(echo $foo here)" +0:Quoted $(...) substitution +>more words here + +# we used never to get this one right, but I think it is now... + print -r "`print -r \\\\\\\\`" +0:Stripping of backslasshes in quoted `...` +>\\ + + print -r "$(print -r \\\\\\\\)" +0:Stripping of backslashes in quoted $(...) +>\\\\ + + fnify() { print \"$*\"; } + print `fnify \`fnify understatement\`` +0:Nested `...` +>""understatement"" + + print $(fnify $(fnify overboard)) +0:Nested $(...) +>""overboard"" + + fructify() { print \'$*\'; } + print "`fructify \`fructify indolence\``" +0:Nested quoted `...` +>''indolence'' + + print "$(fructify $(fructify obtuseness))" +0:Nested quoted $(...) +>''obtuseness'' + + gesticulate() { print \!$*\!; } + print $((gesticulate wildly); gesticulate calmly) +0:$(( ... ) ... ) is not arithmetic +>!wildly! !calmly! + + commencify() { print +$*+; } + print "$((commencify output); commencify input)" +0:quoted $(( ... ) .. ) is not arithmetic +>+output+ +>+input+ + + ( + cd cmdsubst.tmp + print first: ${$(print \*)} + print second: ${~$(print \*)} + print third: ${$(print *)} + print fourth: "${~$(print \*)}" + print fifth: ${~"$(print \*)"} + ) +0:mixing $(...) with parameter substitution and globbing +>first: * +>second: file1.txt file2.txt +>third: file1.txt file2.txt +>fourth: * +>fifth: file1.txt file2.txt + + $(exit 0) $(exit 3) || print $? +0:empty command uses exit value of last substitution +>3 + + X=$(exit 2) $(exit 0) || print $? +0:variable assignments processed after other substitutions +>2 + + false + `` +0:Empty command substitution resets status + + false + echo `echo $?` +0:Non-empty command substitution inherits status +>1 + + echo $(( ##\" )) + echo $(echo \") + echo $((echo \"); echo OK) +0:Handling of backslash double quote in parenthesised substitutions +>34 +>" +>" OK + + echo $(case foo in + foo) + echo This test worked. + ;; + bar) + echo This test failed in a rather bizarre way. + ;; + *) + echo This test failed. + ;; + esac) +0:Parsing of command substitution with unmatched parentheses: case, basic +>This test worked. + + echo "$(case bar in + foo) + echo This test spoobed. + ;; + bar) + echo This test plurbled. + ;; + *) + echo This test bzonked. + ;; + esac)" +0:Parsing of command substitution with unmatched parentheses: case with quotes +>This test plurbled. + + echo before $( + echo start; echo unpretentious | + while read line; do + case $line in + u*) + print Word began with u + print and ended with a crunch + ;; + esac + done | sed -e 's/Word/Universe/'; echo end + ) after +0:Parsing of command substitution with ummatched parentheses: with frills +>before start Universe began with u and ended with a crunch end after + + alias foo='echo $(' + eval 'foo echo this just works, OK\?)' +0:backtracking within command string parsing with alias still pending +>this just works, OK? + + ( + set errexit + show_nargs() { print $#; } + print a $() b + print c "$()" d + ) +0:Empty $() is a valid empty substitution. +>a b +>c d + + empty=$() && print "'$empty'" +0:Empty $() is a valid assignment +>'' diff --git a/dotfiles/system/.zsh/modules/Test/D09brace.ztst b/dotfiles/system/.zsh/modules/Test/D09brace.ztst new file mode 100644 index 0000000..3e667a8 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/D09brace.ztst @@ -0,0 +1,114 @@ +# Tests for brace expansion + +%prep + + foo=(a b c) + arr=(foo bar baz) + +%test + + print X{1,2,{3..6},7,8}Y +0:Basic brace expansion +>X1Y X2Y X3Y X4Y X5Y X6Y X7Y X8Y + + print ${foo}{one,two,three}$arr +0:Brace expansion with arrays, no RC_EXPAND_PARAM +>a b conefoo ctwofoo cthreefoo bar baz + + print ${^foo}{one,two,three}$arr +0:Brace expansion with arrays, with RC_EXPAND_PARAM (1) +>aonefoo atwofoo athreefoo bonefoo btwofoo bthreefoo conefoo ctwofoo cthreefoo bar baz + + print ${foo}{one,two,three}$^arr +0:Brace expansion with arrays, with RC_EXPAND_PARAM (2) +>a b conefoo ctwofoo cthreefoo conebar ctwobar cthreebar conebaz ctwobaz cthreebaz + + print ${^foo}{one,two,three}$^arr +0:Brace expansion with arrays, with RC_EXPAND_PARAM (3) +>aonefoo atwofoo athreefoo aonebar atwobar athreebar aonebaz atwobaz athreebaz bonefoo btwofoo bthreefoo bonebar btwobar bthreebar bonebaz btwobaz bthreebaz conefoo ctwofoo cthreefoo conebar ctwobar cthreebar conebaz ctwobaz cthreebaz + + print X{01..4}Y +0:Numeric range expansion, padding (1) +>X01Y X02Y X03Y X04Y + + print X{1..04}Y +0:Numeric range expansion, padding (2) +>X01Y X02Y X03Y X04Y + + print X{7..12}Y +0:Numeric range expansion, padding (or not) (3) +>X7Y X8Y X9Y X10Y X11Y X12Y + + print X{07..12}Y +0:Numeric range expansion, padding (4) +>X07Y X08Y X09Y X10Y X11Y X12Y + + print X{7..012}Y +0:Numeric range expansion, padding (5) +>X007Y X008Y X009Y X010Y X011Y X012Y + + print X{4..1}Y +0:Numeric range expansion, decreasing +>X4Y X3Y X2Y X1Y + + print X{1..4}{1..4}Y +0:Numeric range expansion, combined braces +>X11Y X12Y X13Y X14Y X21Y X22Y X23Y X24Y X31Y X32Y X33Y X34Y X41Y X42Y X43Y X44Y + + print X{-4..4}Y +0:Numeric range expansion, negative numbers (1) +>X-4Y X-3Y X-2Y X-1Y X0Y X1Y X2Y X3Y X4Y + + print X{4..-4}Y +0:Numeric range expansion, negative numbers (2) +>X4Y X3Y X2Y X1Y X0Y X-1Y X-2Y X-3Y X-4Y + + print X{004..-4..2}Y +0:Numeric range expansion, stepping and padding (1) +>X004Y X002Y X000Y X-02Y X-04Y + + print X{4..-4..02}Y +0:Numeric range expansion, stepping and padding (1) +>X04Y X02Y X00Y X-2Y X-4Y + + print X{1..32..3}Y +0:Numeric range expansion, step alignment (1) +>X1Y X4Y X7Y X10Y X13Y X16Y X19Y X22Y X25Y X28Y X31Y + + print X{1..32..-3}Y +0:Numeric range expansion, step alignment (2) +>X31Y X28Y X25Y X22Y X19Y X16Y X13Y X10Y X7Y X4Y X1Y + + print X{32..1..3}Y +0:Numeric range expansion, step alignment (3) +>X32Y X29Y X26Y X23Y X20Y X17Y X14Y X11Y X8Y X5Y X2Y + + print X{32..1..-3}Y +0:Numeric range expansion, step alignment (4) +>X2Y X5Y X8Y X11Y X14Y X17Y X20Y X23Y X26Y X29Y X32Y + + setopt brace_ccl + print X{za-q521}Y + unsetopt brace_ccl +0:BRACE_CCL on +>X1Y X2Y X5Y XaY XbY XcY XdY XeY XfY XgY XhY XiY XjY XkY XlY XmY XnY XoY XpY XqY XzY + + print X{za-q521}Y +0:BRACE_CCL off +>X{za-q521}Y + + print -r hey{a..j}there +0:{char..char} ranges, simple case +>heyathere heybthere heycthere heydthere heyethere heyfthere heygthere heyhthere heyithere heyjthere + + print -r gosh{1,{Z..a},2}cripes +0:{char..char} ranges, ASCII ordering +>gosh1cripes goshZcripes gosh[cripes gosh\cripes gosh]cripes gosh^cripes gosh_cripes gosh`cripes goshacripes gosh2cripes + + print -r crumbs{y..p}ooh +0:{char..char} ranges, reverse +>crumbsyooh crumbsxooh crumbswooh crumbsvooh crumbsuooh crumbstooh crumbssooh crumbsrooh crumbsqooh crumbspooh + + print -r left{[..]}right +0:{char..char} ranges with tokenized characters +>left[right left\right left]right diff --git a/dotfiles/system/.zsh/modules/Test/E01options.ztst b/dotfiles/system/.zsh/modules/Test/E01options.ztst new file mode 100644 index 0000000..2bd4fdb --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/E01options.ztst @@ -0,0 +1,1313 @@ +# Test various shell options. +# Interactive options not tested here: +# ALWAYS_LAST_PROMPT +# ALWAYS_TO_END +# APPEND_HISTORY (history not maintained) +# AUTO_LIST +# AUTO_MENU +# AUTO_NAME_DIRS (named directory table not maintained) +# AUTO_PARAM_KEYS +# AUTO_PARAM_SLASH +# AUTO_REMOVE_SLASH +# AUTO_RESUME +# BANG_HIST +# BASH_AUTO_LIST +# BEEP (!) +# BG_NICE +# CHECK_JOBS +# COMPLETE_ALIASES +# COMPLETE_IN_WORD +# CORRECT +# CORRECT_ALL +# CSH_JUNKIE_HISTORY +# DVORAK +# EXTENDED_HISTORY +# FLOW_CONTROL +# GLOB_COMPLETE +# HIST_ALLOW_CLOBBER +# HIST_BEEP +# HIST_EXPIRE_DUPS_FIRST +# HIST_FIND_NO_DUPS +# HIST_IGNORE_ALL_DUPS +# HIST_IGNORE_DUPS (-h) +# HIST_IGNORE_SPACE (-g) +# HIST_NO_FUNCTIONS +# HIST_NO_STORE +# HIST_REDUCE_BLANKS +# HIST_SAVE_NO_DUPS +# HIST_VERIFY +# HUP +# IGNORE_EOF +# INC_APPEND_HISTORY +# INTERACTIVE +# INTERACTIVE_COMMENTS +# LIST_AMBIGUOUS +# LIST_BEEP +# LIST_PACKED +# LIST_ROWS_FIRST +# LIST_TYPES +# LOGIN +# LONG_LIST_JOBS +# MAIL_WARNING +# MENU_COMPLETE +# MONITOR +# NOTIFY +# OVERSTRIKE +# PRINT_EIGHT_BIT +# PROMPT_CR +# PUSHD_SILENT +# REC_EXACT +# RM_STAR_SILENT +# RM_STAR_WAIT +# SHARE_HISTORY +# SINGLE_LINE_ZLE +# SUN_KEYBOARD_HACK +# ZLE +# The following require SHINSTDIN and are not (yet) tested: +# AUTO_CD +# SHINSTDIN +# +# Other difficult things I haven't done: +# GLOBAL_RCS (uses fixed files outside build area) +# HASH_CMDS ) +# HASH_DIRS ) fairly seriously internal, hard to test at all +# HASH_LIST_ALL ) +# PRINT_EXIT_STATUS haven't worked out what this does yet, although +# Bart suggested a fix. +# PRIVILEGED (similar to GLOBAL_RCS) +# RCS ( " " " " ) +# SH_OPTION_LETTERS even I found this too dull to set up a test for +# SINGLE_COMMAND kills shell +# VERBOSE hard because done on input (c.f. SHINSTDIN). + +%prep + mkdir options.tmp && cd options.tmp + + mkdir tmpcd homedir + + touch tmpfile1 tmpfile2 + + mydir=$PWD + mydirt=`print -P %~` + mydirhome=`export HOME=$mydir/homedir; print -P %~` + catpath=$(which cat) + lspath==ls + +%test + + alias echo='print foo' + unsetopt aliases + # use eval else aliases are all parsed at start + eval echo bar + setopt aliases + eval echo bar + unalias echo +0:ALIASES option +>bar +>foo bar + + setopt allexport + testpm1=exported + unsetopt allexport + testpm2=unexported + print ${(t)testpm1} + print ${(t)testpm2} +0:ALL_EXPORT option +>scalar-export +>scalar + + # Count the number of directories on the stack. Don't care what they are. + dircount() { dirs -v | tail -1 | awk '{ print $1 + 1}'; } + unsetopt autopushd + cd tmpcd + dircount + cd .. + setopt autopushd + cd tmpcd + dircount + unsetopt autopushd + popd >/dev/null +0:AUTO_PUSHD option +>1 +>2 + + unsetopt badpattern + print [a + setopt badpattern + print [b +1:BAD_PATTERN option +>[a +?(eval):4: bad pattern: [b + + unsetopt bareglobqual nomatch + print *(.) + setopt bareglobqual nomatch + print *(.) +0:BARE_GLOB_QUAL option +>*(.) +>tmpfile1 tmpfile2 + + setopt braceccl + print {abcd} + unsetopt braceccl + print {abcd} +0:BRACE_CCL option +>a b c d +>{abcd} + +# Don't use NUL as a field separator in the following. + setopt braceccl + print {$'\0'-$'\5'} | IFS=' ' read -A chars + for c in $chars; do print $(( #c )); done + unsetopt braceccl +0:BRACE_CCL option starting from NUL +>0 +>1 +>2 +>3 +>4 +>5 + + setopt bsdecho + echo "histon\nimpington" + echo -e "girton\ncottenham" + unsetopt bsdecho + echo "newnham\ncomberton" +0:BSD_ECHO option +>histon\nimpington +>girton +>cottenham +>newnham +>comberton + + unsetopt c_bases + print $(( [#16]15 )) + print $(( [#8]9 )) + setopt c_bases + print $(( [#16]31 )) + print $(( [#8]17 )) + setopt octal_zeroes + print $(( [#8]19 )) + unsetopt c_bases octal_zeroes +0:C_BASES option +>16#F +>8#11 +>0x1F +>8#21 +>023 + + setopt cdablevars + # only absolute paths are eligible for ~-expansion + cdablevar1=tmpcd + (cd cdablevar1) + cdablevar2=$PWD/tmpcd + cd cdablevar2 + cd .. + print back in ${PWD:t} + unsetopt cdablevars + cd cdablevar2 +1q:CDABLE_VARS option +>back in options.tmp +?(eval):cd:4: no such file or directory: cdablevar1 +?(eval):cd:10: no such file or directory: cdablevar2 + +# CHASE_DOTS should go with CHASE_LINKS in B01cd.ztst +# which saves me having to write it here. + + setopt noclobber + rm -f foo1 bar1 rod1 + echo waterbeach >foo1 + (echo landbeach >foo1) + cat foo1 + (echo lode >>bar1) + [[ -f bar1 ]] && print That shouldn\'t be there. + echo denny >rod1 + echo wicken >>rod1 + cat rod1 + unsetopt noclobber + rm -f foo2 bar2 rod2 + echo ely >foo2 + echo march >foo2 + cat foo2 + echo wimpole >>bar2 + cat bar2 + echo royston >rod2 + echo foxton >>rod2 + cat rod2 + rm -f foo* bar* rod* +0:CLOBBER option +>waterbeach +>denny +>wicken +>march +>wimpole +>royston +>foxton +?(eval):4: file exists: foo1 +?(eval):6: no such file or directory: bar1 + + setopt cshjunkieloops + eval 'for f in swaffham bulbeck; print $f; end' + print next one should fail >&2 + unsetopt cshjunkieloops + eval 'for f in chesterton arbury; print $f; end' +1:CSH_JUNKIE_LOOPS option (for loop) +>swaffham +>bulbeck +?next one should fail +?(eval):1: parse error near `end' + +# ` emacs deconfusion + + setopt cshjunkiequotes + print this should cause an error >&2 + eval "print 'line one + line two'" + print this should not >&2 + eval "print 'line three\\ + line four'" + unsetopt cshjunkiequotes +0:CSH_JUNKIE_QUOTES option +>line three +> line four +?this should cause an error +?(eval):1: unmatched ' +?this should not + +# ' emacs deconfusion + + nullcmd() { print '$NULLCMD run'; } + readnullcmd() { print 'Running $READNULLCMD'; cat; } + NULLCMD=nullcmd + READNULLCMD=readnullcmd + setopt cshnullcmd + rm -f foo + print "This should fail" >&2 + (>foo) + print "This should succeed" >&2 + print "These are the contents of foo" >foo + cat foo + print "This should also fail" >&2 + (<foo) + unsetopt cshnullcmd + rm -f foo + >foo + <foo + rm -f foo +0:CSH_NULL_CMD option +>These are the contents of foo +>Running $READNULLCMD +>$NULLCMD run +?This should fail +?(eval):8: redirection with no command +?This should succeed +?This should also fail +?(eval):13: redirection with no command + +# nomatch should be overridden by cshnullglob + setopt nomatch cshnullglob + print tmp* nothing* blah + print -n 'hoping for no match: ' >&2 + (print nothing* blah) + print >&2 + unsetopt cshnullglob nomatch + print tmp* nothing* blah + print nothing* blah +0:CSH_NULL_GLOB option +>tmpcd tmpfile1 tmpfile2 blah +>tmpcd tmpfile1 tmpfile2 nothing* blah +>nothing* blah +?hoping for no match: (eval):4: no match +? + +# The trick is to avoid =cat being expanded in the output while $catpath is. + setopt NO_equals + print -n trick; print =cat + setopt equals + print -n trick; print =cat +0q:EQUALS option +>trick=cat +>trick$catpath + +# explanation of expected TRAPZERR output: from false and from +# testfn() with ERR_EXIT on (hmm, should we really get a second one from +# the function exiting?), then from the false only with ERR_EXIT off. + TRAPZERR() { print ZERR trapped; } + testfn() { setopt localoptions $2; print $1 before; false; print $1 after; } + (testfn on errexit) + testfn off + unfunction TRAPZERR testfn +0:ERR_EXIT option +>on before +>ZERR trapped +>ZERR trapped +>off before +>ZERR trapped +>off after + + (print before; setopt noexec; print after) +0:NO_EXEC option +>before + + (setopt noexec + typeset -A hash + hash['this is a string']) +0:NO_EXEC option should not attempt to parse subscripts + + (setopt noexec nomatch + echo *NonExistentFile*) +0:NO_EXEC option should not do globbing + + (setopt noexec + echo ${unset_var?Not an error}) +0:NO_EXEC should not test for unset variables + + (setopt noexec + : ${${string%[aeiou]*}/(#m)?(#e)/${(U)MATCH}} Rule 1 + : ${array[4,5][1][2,3]} Rule 2 + : ${${(P)foo[1,6]}[1,3]} Rule 3 + : "${${(@)array}[1,2]}" Rule 5 + : "${(@)${(@)array}[1,2]#?}" Rule 6 + : ${(el.20..X.)${bar}} Rule 11 success case) +0:NO_EXEC handles parameter substitution examples + + (setopt noexec + : ${(el.20..X.)$bar} Rule 11 failure case) +1:NO_EXEC does recognize bad substitution syntax +*?* bad substitution + + setopt NO_eval_lineno + eval 'print $LINENO' + setopt eval_lineno + eval 'print $LINENO' +0:EVAL_LINENO option +>2 +>1 + + # The EXTENDED_GLOB test doesn't test globbing fully --- it just tests + # that certain patterns are treated literally with the option off + # and as patterns with the option on. + testfn() { print -n "$1 $2 $3 "; if [[ $1 = ${~2} ]]; + then print yes; else print no; fi; } + tests=('a#' '?~b' '^aa') + strings=('a' 'aa' 'b' 'a#' '?~b' '^aa') + for opt in noextendedglob extendedglob; do + setopt $opt + for test in $tests; do + for string in $strings; do + testfn $string $test $opt + done + done + done +0:EXTENDED_GLOB option +>a a# noextendedglob no +>aa a# noextendedglob no +>b a# noextendedglob no +>a# a# noextendedglob yes +>?~b a# noextendedglob no +>^aa a# noextendedglob no +>a ?~b noextendedglob no +>aa ?~b noextendedglob no +>b ?~b noextendedglob no +>a# ?~b noextendedglob no +>?~b ?~b noextendedglob yes +>^aa ?~b noextendedglob no +>a ^aa noextendedglob no +>aa ^aa noextendedglob no +>b ^aa noextendedglob no +>a# ^aa noextendedglob no +>?~b ^aa noextendedglob no +>^aa ^aa noextendedglob yes +>a a# extendedglob yes +>aa a# extendedglob yes +>b a# extendedglob no +>a# a# extendedglob no +>?~b a# extendedglob no +>^aa a# extendedglob no +>a ?~b extendedglob yes +>aa ?~b extendedglob no +>b ?~b extendedglob no +>a# ?~b extendedglob no +>?~b ?~b extendedglob no +>^aa ?~b extendedglob no +>a ^aa extendedglob yes +>aa ^aa extendedglob no +>b ^aa extendedglob yes +>a# ^aa extendedglob yes +>?~b ^aa extendedglob yes +>^aa ^aa extendedglob yes + + foo() { print My name is $0; } + unsetopt functionargzero + foo + setopt functionargzero + foo + unfunction foo +0:FUNCTION_ARGZERO option +>My name is (anon) +>My name is foo + + setopt _NO_glob_ + print tmp* + set -o glob + print tmp* +0:GLOB option +>tmp* +>tmpcd tmpfile1 tmpfile2 + + showit() { local v; + for v in first second third; do + eval print \$$v \$\{\(t\)$v\} + done; + } + setit() { typeset -x first=inside1; + typeset +g -x second=inside2; + typeset -g -x third=inside3; + showit; + } + first=outside1 second=outside2 third=outside3 + unsetopt globalexport + setit + showit + setopt globalexport + setit + showit + unfunction setit showit +0:GLOBAL_EXPORT option +>inside1 scalar-local-export +>inside2 scalar-local-export +>inside3 scalar-export +>outside1 scalar +>outside2 scalar +>inside3 scalar-export +>inside1 scalar-export +>inside2 scalar-local-export +>inside3 scalar-export +>inside1 scalar-export +>outside2 scalar +>inside3 scalar-export + +# GLOB_ASSIGN is tested in A06assign.ztst. + + mkdir onlysomefiles + touch onlysomefiles/.thisfile onlysomefiles/thatfile + setopt globdots + print onlysomefiles/* + unsetopt globdots + print onlysomefiles/* + rm -rf onlysomefiles +0:GLOB_DOTS option +>onlysomefiles/.thisfile onlysomefiles/thatfile +>onlysomefiles/thatfile + + # we've tested this enough times already... + # could add some stuff for other sorts of expansion + foo='tmp*' + setopt globsubst + print ${foo} + unsetopt globsubst + print ${foo} +0:GLOB_SUBST option +>tmpcd tmpfile1 tmpfile2 +>tmp* + + setopt histsubstpattern + print *(:s/t??/TING/) + foo=(tmp*) + print ${foo:s/??p/THUMP/} + 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]}'/) + unsetopt histsubstpattern +0:HIST_SUBST_PATTERN option +>TINGcd TINGfile1 TINGfile2 homedir +>THUMPcd THUMPfile1 THUMPfile2 +>one.c Two.X Three.X +>homedir scrunchyfile1 scrunchyfile2 tmpcd + + setopt ignorebraces + echo X{a,b}Y + unsetopt ignorebraces + echo X{a,b}Y +0:IGNORE_BRACES option +>X{a,b}Y +>XaY XbY + + setopt ksh_arrays + array=(one two three) + print $array $array[2] + print ${array[0]} ${array[1]} ${array[2]} ${array[3]} + unsetopt ksh_arrays + print $array $array[2] + print ${array[0]} ${array[1]} ${array[2]} ${array[3]} + unset array +0:KSH_ARRAYS option +>one one[2] +>one two three +>one two three two +>one two three + + fpath=(.) + echo >foo 'echo foo loaded; foo() { echo foo run; }' + echo >bar 'bar() { echo bar run; }' + setopt kshautoload + autoload foo bar + foo + bar + unfunction foo bar + unsetopt kshautoload + autoload foo bar + foo + bar +0:KSH_AUTOLOAD option +>foo loaded +>foo run +>bar run +>foo loaded +>bar run + +# ksh_glob is tested by the glob tests. + + setopt kshoptionprint globassign + print set + setopt | grep kshoptionprint + setopt | grep globassign + unsetopt kshoptionprint + print unset + setopt | grep kshoptionprint + setopt | grep globassign + unsetopt globassign +0:KSH_OPTION_PRINT option +>set +>kshoptionprint on +>globassign on +>unset +>globassign + + # This test is now somewhat artificial as + # KSH_TYPESET only applies to the builtin + # interface. Tests to the more standard + # reserved word interface appear elsewhere. + ( + # reserved words are handled during parsing, + # hence eval... + disable -r typeset + eval ' + setopt kshtypeset + ktvars=(ktv1 ktv2) + typeset ktfoo=`echo arg1 arg2` $ktvars + print $+ktv1 $+ktv2 $+ktv3 + print $ktfoo + unsetopt kshtypeset + typeset noktfoo=`echo noktarg1 noktarg2` + print $noktfoo + print $+noktarg1 $+noktarg2 + unset ktfoo ktv1 ktv2 noktfoo noktarg2 + ' + ) +0:KSH_TYPESET option +>1 1 0 +>arg1 arg2 +>noktarg1 +>0 1 + + showopt() { setopt | egrep 'localoptions|ksharrays'; } + f1() { setopt localoptions ksharrays; showopt } + f2() { setopt ksharrays; showopt } + setopt kshoptionprint + showopt + f1 + showopt + f2 + showopt + unsetopt ksh_arrays +0:LOCAL_OPTIONS option +>ksharrays off +>localoptions off +>ksharrays on +>localoptions on +>ksharrays off +>localoptions off +>ksharrays on +>localoptions off +>ksharrays on +>localoptions off + +# LOCAL_TRAPS was tested in C03traps (phew). + + fn() { + local HOME=/any/old/name + print -l var=~ 'anything goes/here'=~ split=`echo maybe not`; + } + setopt magicequalsubst + fn + setopt kshtypeset + fn + unsetopt magicequalsubst kshtypeset + fn +0:MAGIC_EQUAL_SUBST option +>var=/any/old/name +>anything goes/here=/any/old/name +>split=maybe +>not +>var=/any/old/name +>anything goes/here=/any/old/name +>split=maybe not +>var=~ +>anything goes/here=~ +>split=maybe +>not + + setopt MARK_DIRS + print tmp* + unsetopt MARK_DIRS + print tmp* +0:MARK_DIRS option +>tmpcd/ tmpfile1 tmpfile2 +>tmpcd tmpfile1 tmpfile2 + +# maybe should be in A04redirect + print "This is in1" >in1 + print "This is in2" >in2 + unsetopt multios + print Test message >foo1 >foo2 + print foo1: $(<foo1) + print foo2: $(<foo2) + cat <in1 <in2 + setopt multios + print Test message >foo1 >foo2 + sleep 1 # damn, race in multios + print foo1: $(<foo1) + print foo2: $(<foo2) + cat <in1 <in2 + rm -f foo1 foo2 in1 in2 +0:MULTIOS option +>foo1: +>foo2: Test message +>This is in2 +>foo1: Test message +>foo2: Test message +>This is in1 +>This is in2 + +# This is trickier than it looks. There's a hack at the end of +# execcmd() to catch the multio processes attached to the +# subshell, which otherwise sort of get lost in the general turmoil. +# Without that, the multios aren't synchronous with the subshell +# or the main shell starting the "cat", so the output files appear +# empty. + setopt multios + ( echo hello ) >multio_out1 >multio_out2 && cat multio_out* +0:Multios attached to a subshell +>hello +>hello + +# This tests for another race in multios. + print -u $ZTST_fd 'This test hangs the shell when it fails...' + setopt multios + echo These are the contents of the file >multio_race.out + multio_race_fn() { cat; } + multio_race_fn <$(echo multio_race.out multio_race.out) +0:Fix for race with input multios +>These are the contents of the file +>These are the contents of the file + +# tried this with other things, but not on its own, so much. + unsetopt nomatch + print with nonomatch: flooble* + setopt nomatch + print with nomatch flooble* +1:NOMATCH option +>with nonomatch: flooble* +?(eval):4: no matches found: flooble* + +# NULL_GLOB should override NONOMATCH... + setopt nullglob nomatch + print frooble* tmp* + unsetopt nullglob nomatch + print frooble* tmp* +0:NULL_GLOB option +>tmpcd tmpfile1 tmpfile2 +>frooble* tmpcd tmpfile1 tmpfile2 + + touch ngs1.txt ngs2.txt ngs10.txt ngs20.txt ngs100.txt ngs200.txt + setopt numericglobsort + print -l ngs* + unsetopt numericglobsort + print -l ngs* +0:NUMERIC_GLOB_SORT option +>ngs1.txt +>ngs2.txt +>ngs10.txt +>ngs20.txt +>ngs100.txt +>ngs200.txt +>ngs1.txt +>ngs10.txt +>ngs100.txt +>ngs2.txt +>ngs20.txt +>ngs200.txt + + typeset -i 10 oznum + setopt octalzeroes + (( oznum = 012 + 013 )) + print $oznum + unsetopt octalzeroes + (( oznum = 012 + 013 )) + print $oznum + unset oznum +0:OCTAL_ZEROES options +>21 +>25 + + typeset -a oldpath + oldpath=($path) + mkdir pdt_topdir pathtestdir pdt_topdir/pathtestdir + print "#!/bin/sh\necho File in upper dir" >pathtestdir/findme + print "#!/bin/sh\necho File in lower dir" >pdt_topdir/pathtestdir/findme + chmod u+x pathtestdir/findme pdt_topdir/pathtestdir/findme + pathtestdir/findme + rm -f pathtestdir/findme + setopt pathdirs + path=($PWD $PWD/pdt_topdir) + pathtestdir/findme + print unsetting option... + unsetopt pathdirs + pathtestdir/findme + path=($oldpath) + unset oldpath + rm -rf pdt_topdir pathtestdir +0:PATH_DIRS option +>File in upper dir +>File in lower dir +>unsetting option... +?(eval):14: no such file or directory: pathtestdir/findme + + (setopt pathdirs; path+=( /usr/bin ); type ./env) +1:whence honours PATH_DIRS option +>./env not found + + setopt posixbuiltins + PATH= command -v print + PATH= command -V print + PATH= command print foo + unsetopt posixbuiltins + print unsetting... + PATH= command -V print + PATH= command print foo +127:POSIX_BUILTINS option +>print +>print is a shell builtin +>foo +>unsetting... +>print is a shell builtin +?(eval):8: command not found: print + + # With non-special command: original value restored + # With special builtin: new value kept + # With special builtin preceeded by "command": original value restored. + (setopt posixbuiltins + FOO=val0 + FOO=val1 true; echo $FOO + FOO=val2 times 1>/dev/null 2>&1; echo $FOO + FOO=val3 command times 1>/dev/null 2>&1; echo $FOO) +0:POSIX_BUILTINS and restoring variables +>val0 +>val2 +>val2 + +# PRINTEXITVALUE only works if shell input is coming from standard input. +# Goodness only knows why. + $ZTST_testdir/../Src/zsh -f <<<' + setopt printexitvalue + func() { + false + } + func + ' +1:PRINT_EXIT_VALUE option +?zsh: exit 1 + + $ZTST_testdir/../Src/zsh -f <<<' + setopt printexitvalue + () { false; } + ' +1:PRINT_EXIT_VALUE option for anonymous function +?zsh: exit 1 + + setopt promptbang + print -P ! + setopt nopromptbang + print -P ! +0:PROMPT_BANG option +>0 +>! + + unsetopt promptpercent + print -P '%/' + setopt promptpercent + print -P '%/' +0q:PROMPT_PERCENT option +>%/ +>$mydir + + setopt promptsubst + print -P '`echo waaah`' + unsetopt promptsubst + print -P '`echo waaah`' +0:PROMPT_SUBST option +>waaah +>`echo waaah` + + dirs + pushd $mydir/tmpcd + dirs + pushd $mydir/tmpcd + dirs + setopt pushdignoredups + pushd $mydir/tmpcd + dirs + unsetopt pushdignoredups + popd >/dev/null + popd >/dev/null +0q:PUSHD_IGNOREDUPS option +>$mydirt +>$mydirt/tmpcd $mydirt +>$mydirt/tmpcd $mydirt/tmpcd $mydirt +>$mydirt/tmpcd $mydirt/tmpcd $mydirt + + mkdir newcd + cd $mydir + pushd $mydir/tmpcd + pushd $mydir/newcd + dirs + pushd -0 + dirs + setopt pushdminus pushdsilent + pushd -0 + dirs + unsetopt pushdminus + popd >/dev/null + popd >/dev/null + cd $mydir +0q:PUSHD_MINUS option +>$mydirt/newcd $mydirt/tmpcd $mydirt +>$mydirt $mydirt/newcd $mydirt/tmpcd +>$mydirt $mydirt/newcd $mydirt/tmpcd + +# Do you have any idea how dull this is? + + (export HOME=$mydir/homedir + pushd $mydir/tmpcd + pushd + dirs + setopt pushdtohome + pushd + dirs + unsetopt pushdtohome + popd + pushd + popd + dirs) +0q:PUSHD_TO_HOME option +>$mydirhome $mydirhome/tmpcd +>~ $mydirhome $mydirhome/tmpcd +>$mydirhome + + array=(one two three four) + setopt rcexpandparam + print aa${array}bb + unsetopt rcexpandparam + print aa${array}bb +0:RC_EXPAND_PARAM option +>aaonebb aatwobb aathreebb aafourbb +>aaone two three fourbb + + setopt rcquotes + # careful, this is done when parsing a complete block + eval "print 'one''quoted''expression'" + unsetopt rcquotes + eval "print 'another''quoted''expression'" +0:RC_QUOTES option +>one'quoted'expression +>anotherquotedexpression + +# too lazy to test jobs -Z and ARGV0. + (setopt restricted; cd /) + (setopt restricted; PATH=/bin:/usr/bin) + (setopt restricted; /bin/ls) + (setopt restricted; hash ls=/bin/ls) + (setopt restricted; print ha >outputfile) + (setopt restricted; exec ls) + (setopt restricted; unsetopt restricted) + : +0:RESTRICTED option +?(eval):cd:1: restricted +?(eval):2: PATH: restricted +?(eval):3: /bin/ls: restricted +?(eval):hash:4: restricted: /bin/ls +?(eval):5: writing redirection not allowed in restricted mode +?(eval):exec:6: ls: restricted +?(eval):unsetopt:7: can't change option: restricted + +# ' emacs deconfusion + + fn() { + print =ls ={ls,} + local foo='=ls' + print ${~foo} + } + setopt shfileexpansion + fn + unsetopt shfileexpansion + fn +0q:SH_FILE_EXPANSION option +>$lspath =ls = +>=ls +>$lspath $lspath = +>$lspath + + testpat() { + if [[ $1 = ${~2} ]]; then print $1 $2 yes; else print $1 $2 no; fi + } + print option on + setopt shglob + repeat 2; do + for str in 'a(b|c)' ab; do + testpat $str 'a(b|c)' + done + for str in 'a<1-10>' a9; do + testpat $str 'a<1-10>' + done + [[ ! -o shglob ]] && break + print option off + unsetopt shglob + done +0:SH_GLOB option +>option on +>a(b|c) a(b|c) yes +>ab a(b|c) no +>a<1-10> a<1-10> yes +>a9 a<1-10> no +>option off +>a(b|c) a(b|c) no +>ab a(b|c) yes +>a<1-10> a<1-10> no +>a9 a<1-10> yes + + print this is bar >bar + fn() { + local NULLCMD=cat READNULLCMD=cat + { echo hello | >foo } 2>/dev/null + cat foo + <bar + } + setopt shnullcmd + print option set + fn + unsetopt shnullcmd + print option unset + fn + rm -f foo bar +0:SH_NULLCMD option +>option set +>option unset +>hello +>this is bar + + fn() { + eval 'for f in foo bar; print $f' + eval 'for f (word1 word2) print $f' + eval 'repeat 3 print nonsense' + } + unsetopt shortloops + print option unset + fn + setopt shortloops + print option set + fn +0:SHORT_LOOPS option +>option unset +>option set +>foo +>bar +>word1 +>word2 +>nonsense +>nonsense +>nonsense +?(eval):1: parse error near `print' +?(eval):1: parse error near `print' +?(eval):1: parse error near `print' + + fn() { print -l $*; } + setopt shwordsplit + print option set + repeat 2; do + foo='two words' + fn $foo + fn "${=foo}" + [[ ! -o shwordsplit ]] && break + unsetopt shwordsplit + print option unset + done +0:SH_WORD_SPLIT option +>option set +>two +>words +>two +>words +>option unset +>two words +>two +>words + + fn() { unset foo; print value is $foo; } + setopt nounset + print option unset unset by setting nounset + eval fn + print option unset reset + setopt unset + fn +0:UNSET option +>option unset unset by setting nounset +>option unset reset +>value is +?fn: foo: parameter not set + + fn1() { unset foo; print value 1 is ${foo#bar}; } + fn2() { unset foo; print value 2 is ${foo%bar}; } + fn3() { unset foo; print value 3 is ${foo/bar}; } + setopt nounset + print option unset unset by setting nounset + eval fn1 + eval fn2 + eval fn3 + print option unset reset + setopt unset + fn1 + fn2 + fn3 +0:UNSET option with operators +>option unset unset by setting nounset +>option unset reset +>value 1 is +>value 2 is +>value 3 is +?fn1: foo: parameter not set +?fn2: foo: parameter not set +?fn3: foo: parameter not set + + fn() { + emulate -L zsh + setopt warncreateglobal + foo1=bar1 + unset foo1 + foo1=bar2 + local foo2=bar3 + unset foo2 + foo2=bar4 + typeset -g foo3 + foo3=bar5 + fn2() { + foo3=bar6 + } + foo4=bar7 =true + (( foo5=8 )) + integer foo6=9 + (( foo6=10 )) + } + # don't pollute the test environment with the variables... + (fn) +0:WARN_CREATE_GLOBAL option +?fn:3: scalar parameter foo1 created globally in function fn +?fn:5: scalar parameter foo1 created globally in function fn +?fn:15: numeric parameter foo5 created globally in function fn + + fn() { + emulate -L zsh + setopt warncreateglobal + TZ=UTC date >&/dev/null + local um=$(TZ=UTC date 2>/dev/null) + } + fn +0:WARN_CREATE_GLOBAL negative cases + + ( + foo1=global1 foo2=global2 foo3=global3 foo4=global4 + integer foo5=5 + # skip foo6, defined in fn_wnv + foo7=(one two) + fn_wnv() { + # warns + foo1=bar1 + # doesn't warn + local foo2=bar3 + unset foo2 + # still doesn't warn + foo2=bar4 + # doesn't warn + typeset -g foo3=bar5 + # warns + foo3=bar6 + fn2() { + # warns if global option, not attribute + foo3=bar6 + } + fn2 + # doesn't warn + foo4=bar7 =true + # warns + (( foo5=8 )) + integer foo6=9 + # doesn't warn + (( foo6=10 )) + foo7[3]=three + foo7[4]=(four) + } + print option off >&2 + fn_wnv + print option on >&2 + setopt warnnestedvar + fn_wnv + unsetopt warnnestedvar + print function attribute on >&2 + functions -W fn_wnv + fn_wnv + print all off again >&2 + functions +W fn_wnv + fn_wnv + ) +0:WARN_NESTED_VAR option +?option off +?option on +?fn_wnv:2: scalar parameter foo1 set in enclosing scope in function fn_wnv +?fn_wnv:11: scalar parameter foo3 set in enclosing scope in function fn_wnv +?fn2:2: scalar parameter foo3 set in enclosing scope in function fn2 +?fn_wnv:20: numeric parameter foo5 set in enclosing scope in function fn_wnv +?function attribute on +?fn_wnv:2: scalar parameter foo1 set in enclosing scope in function fn_wnv +?fn_wnv:11: scalar parameter foo3 set in enclosing scope in function fn_wnv +?fn_wnv:20: numeric parameter foo5 set in enclosing scope in function fn_wnv +?all off again + + + ( + setopt warnnestedvar + () { + typeset -A a + : ${a[hello world]::=foo} + print ${(t)a} + key="hello world" + print $a[$key] + } + ) +0:No false positive on parameter used with subscripted assignment +>association-local +>foo + + ( + setopt warnnestedvar + () { + local var=(one two) + () { var=three; } + print $var + } + ) +0:Warn when changing type of nested variable: array to scalar. +?(anon): scalar parameter var set in enclosing scope in function (anon) +>three + + ( + setopt warnnestedvar + () { + local var=three + () { var=(one two); } + print $var + } + ) +0:Warn when changing type of nested variable: scalar to array. +?(anon): array parameter var set in enclosing scope in function (anon) +>one two + +# This really just tests if XTRACE is egregiously broken. +# To test it properly would need a full set of its own. + fn() { print message; } + PS4='+%N:%i> ' + setopt xtrace + fn + unsetopt xtrace + fn +0:XTRACE option +>message +>message +?+(eval):4> fn +?+fn:0> print message +?+(eval):5> unsetopt xtrace + + setopt ignoreclosebraces + eval "icb_test() { echo this is OK; }" + icb_test + icb_args() { print $#; } + eval "icb_args { this, is, ok, too }" +0:IGNORE_CLOSE_BRACES option +>this is OK +>6 + + (setopt pipefail + true | true | true + print $? + true | false | true + print $? + exit 2 | false | true + print $? + false | exit 2 | true + print $?) +0:PIPE_FAIL option +>0 +>1 +>1 +>2 + + for (( i = 0; i < 10; i++ )); do + () { + print $i + break + } + done +0:NO_LOCAL_LOOPS +>0 + + () { + emulate -L zsh + setopt localloops + for (( i = 0; i < 10; i++ )); do + () { + setopt nolocalloops # ignored in parent + print $i + break + } + done + } +0:LOCAL_LOOPS +>0 +>1 +>2 +>3 +>4 +>5 +>6 +>7 +>8 +>9 +?(anon):4: `break' active at end of function scope +?(anon):4: `break' active at end of function scope +?(anon):4: `break' active at end of function scope +?(anon):4: `break' active at end of function scope +?(anon):4: `break' active at end of function scope +?(anon):4: `break' active at end of function scope +?(anon):4: `break' active at end of function scope +?(anon):4: `break' active at end of function scope +?(anon):4: `break' active at end of function scope +?(anon):4: `break' active at end of function scope diff --git a/dotfiles/system/.zsh/modules/Test/E02xtrace.ztst b/dotfiles/system/.zsh/modules/Test/E02xtrace.ztst new file mode 100644 index 0000000..da6191c --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/E02xtrace.ztst @@ -0,0 +1,148 @@ +# Test that xtrace output is correctly generated + +%prep + mkdir xtrace.tmp && cd xtrace.tmp + + function xtf { + local regression_test_dummy_variable + print "$*" + } + function xtfx { + local regression_test_dummy_variable + print "Tracing: (){ builtin 2>file }" 2>>xtrace.err + { print "Tracing: (){ { builtin } 2>file }" } 2>>xtrace.err + } + echo 'print "$*"' > xt.in + +%test + + PS4='+%N:%i> ' + set -x + print 'Tracing: builtin' + print 'Tracing: builtin 2>file' 2>xtrace.err + cat <<<'Tracing: external' + cat <<<'Tracing: external 2>file' 2>>xtrace.err + ( print 'Tracing: ( builtin )' ) + ( print 'Tracing: ( builtin ) 2>file' ) 2>>xtrace.err + ( cat <<<'Tracing: ( external )' ) + ( cat <<<'Tracing: ( external ) 2>file' ) 2>>xtrace.err + { print 'Tracing: { builtin }' } + { print 'Tracing: { builtin } 2>file' } 2>>xtrace.err + { cat <<<'Tracing: { external }' } + { cat <<<'Tracing: { external } 2>file' } 2>>xtrace.err + repeat 1 do print 'Tracing: do builtin done'; done + repeat 1 do print 'Tracing: do builtin done 2>file'; done 2>>xtrace.err + repeat 1 do cat <<<'Tracing: do external done'; done + repeat 1 do cat <<<'Tracing: do external done 2>file'; done 2>>xtrace.err + xtf 'Tracing: function' + xtf 'Tracing: function 2>file' 2>>xtrace.err + xtfx + . ./xt.in 'Tracing: source' + . ./xt.in 'Tracing: source 2>file' 2>>xtrace.err + set +x + cat xtrace.err +0:xtrace with and without redirection +>Tracing: builtin +>Tracing: builtin 2>file +>Tracing: external +>Tracing: external 2>file +>Tracing: ( builtin ) +>Tracing: ( builtin ) 2>file +>Tracing: ( external ) +>Tracing: ( external ) 2>file +>Tracing: { builtin } +>Tracing: { builtin } 2>file +>Tracing: { external } +>Tracing: { external } 2>file +>Tracing: do builtin done +>Tracing: do builtin done 2>file +>Tracing: do external done +>Tracing: do external done 2>file +>Tracing: function +>Tracing: function 2>file +>Tracing: (){ builtin 2>file } +>Tracing: (){ { builtin } 2>file } +>Tracing: source +>Tracing: source 2>file +>+(eval):8> print 'Tracing: ( builtin ) 2>file' +>+(eval):10> cat +>+(eval):12> print 'Tracing: { builtin } 2>file' +>+(eval):14> cat +>+(eval):16> print 'Tracing: do builtin done 2>file' +>+(eval):18> cat +>+xtf:1> local regression_test_dummy_variable +>+xtf:2> print 'Tracing: function 2>file' +>+xtfx:3> print 'Tracing: (){ { builtin } 2>file }' +?+(eval):3> print 'Tracing: builtin' +?+(eval):4> print 'Tracing: builtin 2>file' +?+(eval):5> cat +?+(eval):6> cat +?+(eval):7> print 'Tracing: ( builtin )' +?+(eval):9> cat +?+(eval):11> print 'Tracing: { builtin }' +?+(eval):13> cat +?+(eval):15> print 'Tracing: do builtin done' +?+(eval):17> cat +?+(eval):19> xtf 'Tracing: function' +?+xtf:1> local regression_test_dummy_variable +?+xtf:2> print 'Tracing: function' +?+(eval):20> xtf 'Tracing: function 2>file' +?+(eval):21> xtfx +?+xtfx:1> local regression_test_dummy_variable +?+xtfx:2> print 'Tracing: (){ builtin 2>file }' +?+(eval):22> . ./xt.in 'Tracing: source' +?+./xt.in:1> print 'Tracing: source' +?+(eval):23> . ./xt.in 'Tracing: source 2>file' +?+./xt.in:1> print 'Tracing: source 2>file' +?+(eval):24> set +x + + typeset -ft xtf + xtf 'Tracing: function' +0:tracing function +>Tracing: function +?+xtf:1> local regression_test_dummy_variable +?+xtf:2> print 'Tracing: function' + + echo 'PS4="+%x:%I> " + fn() { + print This is fn. + } + : + fn + ' >fnfile + $ZTST_testdir/../Src/zsh -fx ./fnfile 2>errfile + grep '\./fnfile' errfile 1>&2 +0:Trace output with sourcefile and line number. +>This is fn. +?+./fnfile:1> PS4='+%x:%I> ' +?+./fnfile:5> : +?+./fnfile:6> fn +?+./fnfile:3> print This is fn. + + set -x + [[ 'f o' == 'f x'* || 'b r' != 'z o' && 'squashy sound' < 'squishy sound' ]] + [[ 'f o' = 'f x'* || 'b r' != 'z o' && 'squashy sound' < 'squishy sound' ]] + [[ -e nonexistentfile || ( -z '' && -t 3 ) ]] + set +x +0:Trace for conditions +?+(eval):2> [[ 'f o' == f\ x* || 'b r' != z\ o && 'squashy sound' < 'squishy sound' ]] +?+(eval):3> [[ 'f o' = f\ x* || 'b r' != z\ o && 'squashy sound' < 'squishy sound' ]] +?+(eval):4> [[ -e nonexistentfile || -z '' && -t 3 ]] +?+(eval):5> set +x + + # Part 1: Recurses into nested anonymous functions + fn() { + () { () { true } } + } + functions -T fn + fn + # Part 2: Doesn't recurse into named functions + gn() { true } + fn() { gn } + functions -T fn + fn +0:tracing recurses into anonymous functions +?+fn:1> '(anon)' +?+(anon):0> '(anon)' +?+(anon):0> true +?+fn:0> gn diff --git a/dotfiles/system/.zsh/modules/Test/Makefile.in b/dotfiles/system/.zsh/modules/Test/Makefile.in new file mode 100644 index 0000000..083df49 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/Makefile.in @@ -0,0 +1,75 @@ +# +# Makefile for Test subdirectory +# +# Copyright (c) 1999 Peter Stephensons +# All rights reserved. +# +# Permission is hereby granted, without written agreement and without +# license or royalty fees, to use, copy, modify, and distribute this +# software and to distribute modified versions of this software for any +# purpose, provided that the above copyright notice and the following +# two paragraphs appear in all copies of this software. +# +# In no event shall Peter Stephenson or the Zsh Development Group be liable +# to any party for direct, indirect, special, incidental, or consequential +# damages arising out of the use of this software and its documentation, +# even if Peter Stephenson and the Zsh Development Group have been advised of +# the possibility of such damage. +# +# Peter Stephenson and the Zsh Development Group specifically disclaim any +# warranties, including, but not limited to, the implied warranties of +# merchantability and fitness for a particular purpose. The software +# provided hereunder is on an "as is" basis, and Peter Stephenson and the +# Zsh Development Group have no obligation to provide maintenance, +# support, updates, enhancements, or modifications. +# + +subdir = Test +dir_top = .. +SUBDIRS = + +@VERSION_MK@ + +# source/build directories +VPATH = @srcdir@ +sdir = @srcdir@ +sdir_top = @top_srcdir@ +INSTALL = @INSTALL@ + +@DEFS_MK@ + +# ========== DEPENDENCIES FOR TESTING ========== + +check test: + if test -n "$(DLLD)"; then \ + cd $(dir_top) && DESTDIR= \ + $(MAKE) MODDIR=`pwd`/$(subdir)/Modules install.modules > /dev/null; \ + fi + if ZTST_testlist="`for f in $(sdir)/$(TESTNUM)*.ztst; \ + do echo $$f; done`" \ + ZTST_srcdir="$(sdir)" \ + ZTST_exe=$(dir_top)/Src/zsh@EXEEXT@ \ + $(dir_top)/Src/zsh@EXEEXT@ +Z -f $(sdir)/runtests.zsh; then \ + stat=0; \ + else \ + stat=1; \ + fi; \ + sleep 1; \ + rm -rf Modules .zcompdump; \ + exit $$stat + +# ========== DEPENDENCIES FOR CLEANUP ========== + +@CLEAN_MK@ + +mostlyclean-here: + rm -rf Modules .zcompdump *.tmp + +distclean-here: + rm -f Makefile + +realclean-here: + +# ========== DEPENDENCIES FOR MAINTENANCE ========== + +@CONFIG_MK@ diff --git a/dotfiles/system/.zsh/modules/Test/README b/dotfiles/system/.zsh/modules/Test/README new file mode 100644 index 0000000..d012277 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/README @@ -0,0 +1,30 @@ +There are now different sections, expressed by the first letter in the +scripts names: + + A: basic command parsing and execution + B: builtins + C: shell commands with special syntax + D: substititution + E: options + V: modules + W: builtin interactive commands and constructs + X: line editing + Y: completion + Z: separate systems and user contributions + +You will need to run these by using `make test' in the Test subdirectory of +the build area for your system (which may or may not be the same as the +Test subdirectory of the source tree), or the directory above. You can get +more information about the tests being performed with + ZTST_verbose=1 make check +(`test' is equivalent to `check') or change 1 to 2 for even more detail. + +Individual or groups of tests can be performed with + make TESTNUM=C02 check +or + make TESTNUM=C check +to perform just the test beginning C02, or all tests beginning C, +respectively. + +Instructions on how to write tests are given in B01cd.ztst, which acts as a +model. diff --git a/dotfiles/system/.zsh/modules/Test/V02zregexparse.ztst b/dotfiles/system/.zsh/modules/Test/V02zregexparse.ztst new file mode 100644 index 0000000..b4cec42 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/V02zregexparse.ztst @@ -0,0 +1,382 @@ +# Tests corresponding to the texinfo node `Conditional Expressions' + +%prep + + if ! zmodload zsh/zutil 2>/dev/null; then + ZTST_unimplemented="can't load the zsh/zutil module for testing" + fi + +%test + + zregexparse p1 p2 '' +0:empty + + zregexparse p1 p2 a /a/ +0:element + + zregexparse p1 p2 aaaaaa /a/ \# +0:closure + + zregexparse p1 p2 ab /a/ /b/ +0:concatenation + + zregexparse p1 p2 a /a/ \| /b/ +0:alternation 1 + + zregexparse p1 p2 b /a/ \| /b/ +0:alternation 2 + + zregexparse p1 p2 a \( /a/ \) +0:grouping + + zregexparse p1 p2 abbaaab \( /a/ \| /b/ \) \# +0:alternation, grouping and closure + + zregexparse p1 p2 abcdef /ab/ %cd% /cdef/ +0:lookahead 1 + + zregexparse p1 p2 abcdef /ab/ %ZZ% /cdef/ +1:lookahead 2 + + zregexparse p1 p2 abcd /ab/ %cd% '-print guard' ':print caction' /cd/ +0:pattern, lookahead, guard and completion action +>guard + + zregexparse p1 p2 abcd /ab/ %cd% '-print guard; false' ':print caction' /cd/ +1:guard failure +>guard +>caction + + zregexparse p1 p2 abcdef /ab/ '{print AB}' /cd/ '{print CD}' /ef/ '{print EF}' +0:action +>AB +>CD +>EF + + zregexparse p1 p2 aaa + print $? $p1 $p2 +0:aaa +>2 0 0 + + zregexparse p1 p2 aaa /a/ + print $? $p1 $p2 +0:aaa /a/ +>2 1 1 + + zregexparse p1 p2 aaa /a/ /a/ + print $? $p1 $p2 +0:aaa 2*/a/ +>2 2 2 + + zregexparse p1 p2 aaa /a/ /a/ /a/ + print $? $p1 $p2 +0:aaa 3*/a/ +>0 3 3 + + zregexparse p1 p2 aaa /a/ /a/ /a/ /a/ + print $? $p1 $p2 +0:aaa 4*/a/ +>1 3 3 + + zregexparse p1 p2 aaa /a/ /a/ /a/ /a/ /a/ + print $? $p1 $p2 +0:aaa 5*/a/ +>1 3 3 + + zregexparse p1 p2 aaa /aaa/ + print $? $p1 $p2 +0:aaa /aaa/ +>0 3 3 + + zregexparse p1 p2 aaa /aaa/ /a/ + print $? $p1 $p2 +0:aaa /aaa/ /a/ +>1 3 3 + + zregexparse p1 p2 aaa /a/ \# + print $? $p1 $p2 +0:aaa /aaa/ # +>0 3 3 + + zregexparse p1 p2 aaa /a/ \# \# + print $? $p1 $p2 +0:aaa /aaa/ # # +>0 3 3 + + zregexparse p1 p2 aaa \( /a/ \) + print $? $p1 $p2 +0:aaa ( /a/ ) +>2 1 1 + + zregexparse p1 p2 aaa \( /a/ \) \# + print $? $p1 $p2 +0:aaa ( /a/ ) # +>0 3 3 + + zregexparse p1 p2 aaa /a/ /b/ + print $? $p1 $p2 +0:aaa /a/ /b/ +>1 1 1 + + zregexparse p1 p2 a /a/ '{print A}' + print $? $p1 $p2 +0:a /a/ '{A}' +>A +>0 1 1 + + zregexparse p1 p2 a /b/ '{print A}' + print $? $p1 $p2 +0:a /b/ '{A}' +>1 0 0 + + zregexparse p1 p2 a /b/ ':print A' '{print B}' + print $? $p1 $p2 +0:a /b/ ':A' '{B}' +>A +>1 0 0 + + zregexparse p1 p2 ab /a/ '{print A}' + print $? $p1 $p2 +0:ab /a/ '{A}' +>2 1 1 + + zregexparse p1 p2 ab /a/ '{print A}' /b/ '{print B}' + print $? $p1 $p2 +0:ab /a/ '{A}' /b/ '{B}' +>A +>B +>0 2 2 + + zregexparse p1 p2 ab /a/ ':print A' '{print B}' /b/ ':print C' '{print D}' + print $? $p1 $p2 +0:ab /a/ ':A' '{B}' /b/ ':C' '{D}' +>B +>D +>0 2 2 + + zregexparse p1 p2 abc /a/ '{print A}' /b/ '{print B}' /c/ '{print C}' + print $? $p1 $p2 +0:abc /a/ '{A}' /b/ '{B}' /c/ '{C}' +>A +>B +>C +>0 3 3 + + zregexparse p1 p2 abz /a/ '{print A}' /b/ '{print B}' /c/ '{print C}' + print $? $p1 $p2 +0:abz /a/ '{A}' /b/ '{B}' /c/ '{C}' +>A +>1 2 2 + + zregexparse p1 p2 azz /a/ '{print A}' /b/ '{print B}' /c/ '{print C}' + print $? $p1 $p2 +0:azz /a/ '{A}' /b/ '{B}' /c/ '{C}' +>1 1 1 + + zregexparse p1 p2 aba '{print A}' /a/ '{print B}' /b/ '{print C}' /c/ '{print D}' + print $? $p1 $p2 +0:aba '{A}' /a/ '{B}' /b/ '{C}' /c/ '{D}' +>A +>B +>1 2 2 + + zregexparse p1 p2 a /a/ '{print "$match[1]"}' + print $? $p1 $p2 +0:a /a/ '{M1}' +>a +>0 1 1 + + zregexparse p1 p2 aaa /a/ '{print A}' // + print $? $p1 $p2 +0:aaa /a/ '{A}' // +>A +>2 1 1 + + zregexparse p1 p2 aaa /a/ '{print "$match[1]"}' // '{print A}' + print $? $p1 $p2 +0:aaa /a/ '{M1}' // '{A}' +>a +>2 1 1 + + zregexparse p1 p2 abcdef /a/ '{print $match[1]}' /b/ '{print $match[1]}' /c/ '{print $match[1]}' // '{print A}' + print $? $p1 $p2 +0:abcdef /a/ '{M1}' /b/ '{M1}' /c/ '{M1}' // '{A}' +>a +>b +>c +>2 3 3 + + zregexparse p1 p2 abcdef /a/ '{print A}' /b/ '{print B}' /c/ '{print C}' // '{print D}' + print $? $p1 $p2 +0:abcdef /a/ '{A}' /b/ '{B}' /c/ '{C}' // '{D}' +>A +>B +>C +>2 3 3 + + zregexparse p1 p2 a /a/ '{print A}' /b/ '{print B}' + print $? $p1 $p2 +0:a /a/ {A} /b/ {B} +>1 1 1 + + zregexparse p1 p2 abcdef \ + /a/ '-print Ga:$p1:$p2:$match[1]' '{print Aa:$p1:$p2:$match[1]}' \ + /b/ '-print Gb:$p1:$p2:$match[1]' '{print Ab:$p1:$p2:$match[1]}' \ + /c/ '-print Gc:$p1:$p2:$match[1]' '{print Ac:$p1:$p2:$match[1]}' \ + // + print $? $p1 $p2 +0:abcdef /a/ -Ga {Aa} /b/ -Gb {Aa} /c/ -Gc {Ac} // +>Ga:0:0:a +>Gb:1:1:b +>Aa:1:1:a +>Gc:2:2:c +>Ab:2:2:b +>Ac:3:3:c +>2 3 3 + + zregexparse p1 p2 abcdef \ + /a/ '-print Ga:$p1:$p2:$match[1]' '{print Aa:$p1:$p2:$match[1]}' \ + /b/ '-print Gb:$p1:$p2:$match[1]' '{print Ab:$p1:$p2:$match[1]}' \ + /c/ '-print Gc:$p1:$p2:$match[1]' '{print Ac:$p1:$p2:$match[1]}' \ + '/[]/' ':print F:$p1:$p2' + print $? $p1 $p2 +0:abcdef /a/ -Ga {Aa} /b/ -Gb {Ab} /c/ -Gc {Ac} /[]/ :F +>Ga:0:0:a +>Gb:1:1:b +>Aa:1:1:a +>Gc:2:2:c +>Ab:2:2:b +>F:3:3 +>1 3 3 + + zregexparse p1 p2 abcdef \ + /a/ '-print Ga:$p1:$p2:$match[1]' '{print Aa:$p1:$p2:$match[1]}' \ + /b/ '-print Gb:$p1:$p2:$match[1]' '{print Ab:$p1:$p2:$match[1]}' \ + /c/ '-print Gc:$p1:$p2:$match[1]' '{print Ac:$p1:$p2:$match[1]}' \ + \( '/[]/' ':print F1:$p1:$p2' \| /z/ ':print F2' \) + print $? $p1 $p2 +0:abcdef /a/ -Ga {Aa} /b/ -Gb {Ab} /c/ -Gc {Ac} ( /[]/ :F1 | /z/ :F2 ) +>Ga:0:0:a +>Gb:1:1:b +>Aa:1:1:a +>Gc:2:2:c +>Ab:2:2:b +>F1:3:3 +>F2 +>1 3 3 + + zregexparse p1 p2 a '/[]/' ':print A' + print $? $p1 $p2 +0:a /[]/ :A +>A +>1 0 0 + + zregexparse p1 p2 $'\0' $'/\0/' '{print A}' + print $? $p1 $p2 +0:"\0" /\0/ {A} +>A +>0 1 1 + + zregexparse p1 p2 $'\0' $'/\0/' '{print A}' '/ /' '{print B}' + print $? $p1 $p2 +0:"\0" /\0/ {A} / / {B} +>1 1 1 + + zregexparse p1 p2 abcdef \( '/?/' '{print $match[1]}' \) \# + print $? $p1 $p2 +0:abcdef ( /?/ {M1} ) # +>a +>b +>c +>d +>e +>f +>0 6 6 + + zregexparse p1 p2 abcdef \( '/c?|?/' '{print $match[1]}' \) \# + print $? $p1 $p2 +0:abcdef ( /c?|?/ {M1} ) # +>a +>b +>cd +>e +>f +>0 6 6 + + zregexparse p1 p2 abcacdef \( /a/ '{print $match[1]}' \| /b/ '{print $match[1]}' \| /c/ '{print $match[1]}' \) \# + print $? $p1 $p2 +0:abcacdef ( /a/ {M1} | /b/ {M1} | /c/ {M1} ) # +>a +>b +>c +>a +>1 5 5 + + zregexparse p1 p2 abcdef \( /a/ ':print A' \| /b/ ':print B' \| /c/ ':print C' \) \# + print $? $p1 $p2 +0:abcdef ( /a/ :A | /b/ :B | /c/ :C ) # +>A +>B +>C +>1 3 3 + + zregexparse p1 p2 abcdef \( /a/ ':print A' '{print $match[1]}' \| /b/ ':print B' '{print $match[1]}' \| /c/ ':print C' '{print $match[1]}' \) \# + print $? $p1 $p2 +0:abcdef ( /a/ :A {M1} | /b/ :B {M1} | /c/ :C {M1} ) # +>a +>b +>A +>B +>C +>1 3 3 + + zregexparse p1 p2 $'com\0xx' /$'[^\0]#\0'/ \( /$'[^\0]#\0'/ :'print A' /$'[^\0]#\0'/ :'print B' \) \# + print $? $p1 $p2 +0:"com\0xx" /W/ ( /W/ :A /W/ :B ) # +>A +>1 4 4 + + zregexparse p1 p2 $'com\0xx\0yy' /$'[^\0]#\0'/ \( /$'[^\0]#\0'/ :'print A' /$'[^\0]#\0'/ :'print B' \) \# + print $? $p1 $p2 +0:"com\0xx\0yy" /W/ ( /W/ :A /W/ :B ) # +>B +>1 7 7 + + zregexparse p1 p2 $'com\0xx\0yy\0zz' /$'[^\0]#\0'/ \( /$'[^\0]#\0'/ :'print A' /$'[^\0]#\0'/ :'print B' \) \# + print $? $p1 $p2 +0:"com\0xx\0yy\0zz" /W/ ( /W/ :A /W/ :B ) # +>A +>1 10 10 + + zregexparse p1 p2 abcdez /abc/ ':print A:$p1:$p2' /def/ ':print B:$p1:$p2' + print $? $p1 $p2 +0:abcdez /abc/ :A /def/ :B +>B:3:3 +>1 3 3 + + zregexparse p1 p2 abcdez /abc/+ ':print A:$p1:$p2' /def/ ':print B:$p1:$p2' + print $? $p1 $p2 +0:abcdez /abc/+ :A /def/ :B +>A:0:3 +>B:0:3 +>1 0 3 + + zregexparse p1 p2 abcdez /abc/+ ':print A:$p1:$p2' // /def/ ':print B:$p1:$p2' + print $? $p1 $p2 +0:abcdez /abc/+ :A // /def/ :B +>A:0:3 +>B:0:3 +>1 0 3 + + zregexparse p1 p2 abcdez /abc/+ ':print A:$p1:$p2' //- /def/ ':print B:$p1:$p2' + print $? $p1 $p2 +0:abcdez /abc/+ :A //- /def/ :B +>B:3:3 +>1 3 3 + + zregexparse p1 p2 $'ZZZZ\0abcdef' $'/ZZZZ\0/' /abc/+ ':print A:$p1:$p2' /dee/ ':print B:$p1:$p2' + print $? $p1 $p2 +0:"ZZZZ\0abcdef" /ZZZZ\0/ /abc/+ :A /dee/ :B +>A:5:8 +>B:5:8 +>1 5 8 diff --git a/dotfiles/system/.zsh/modules/Test/V03mathfunc.ztst b/dotfiles/system/.zsh/modules/Test/V03mathfunc.ztst new file mode 100644 index 0000000..1edb7a2 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/V03mathfunc.ztst @@ -0,0 +1,141 @@ +# Tests for the module zsh/mathfunc + +%prep + if ! zmodload zsh/mathfunc 2>/dev/null; then + ZTST_unimplemented="The module zsh/mathfunc is not available." + fi + +%test + # -g makes pi available in later tests + float -gF 5 pi + (( pi = 4 * atan(1.0) )) + print $pi +0:Basic operation with atan +>3.14159 + + float -F 5 result + (( result = atan(3,2) )) + print $result +0:atan with two arguments +>0.98279 + + print $(( atan(1,2,3) )) +1:atan can't take three arguments +?(eval):1: wrong number of arguments: atan(1,2,3) + + float r1=$(( rand48() )) + float r2=$(( rand48() )) + float r3=$(( rand48() )) + # Yes, this is a floating point equality test like they tell + # you not to do. As the pseudrandom sequence is deterministic, + # this is the right thing to do in this case. + if (( r1 == r2 )); then + print "Seed not updated correctly the first time" + else + print "First two random numbers differ, OK" + fi + if (( r2 == r3 )); then + print "Seed not updated correctly the second time" + else + print "Second two random numbers differ, OK" + fi +0:rand48 with default initialisation +F:This test fails if your math library doesn't have erand48(). +>First two random numbers differ, OK +>Second two random numbers differ, OK + + seed=f45677a6cbe4 + float r1=$(( rand48(seed) )) + float r2=$(( rand48(seed) )) + seed2=$seed + float r3=$(( rand48(seed) )) + float r4=$(( rand48(seed2) )) + # Yes, this is a floating point equality test like they tell + # you not to do. As the pseudrandom sequence is deterministic, + # this is the right thing to do in this case. + if (( r1 == r2 )); then + print "Seed not updated correctly the first time" + else + print "First two random numbers differ, OK" + fi + if (( r2 == r3 )); then + print "Seed not updated correctly the second time" + else + print "Second two random numbers differ, OK" + fi + if (( r3 == r4 )); then + print "Identical seeds generate identical numbers, OK" + else + print "Indeterminate result from identical seeds" + fi +0:rand48 with pre-generated seed +F:This test fails if your math library doesn't have erand48(). +>First two random numbers differ, OK +>Second two random numbers differ, OK +>Identical seeds generate identical numbers, OK + + float -F 5 pitest + (( pitest = 4.0 * atan(1) )) + # This is a string test of the output to 5 digits. + if [[ $pi = $pitest ]]; then + print "OK, atan on an integer seemed to work" + else + print "BAD: got $pitest instead of $pi" + fi +0:Conversion of arguments from integer +>OK, atan on an integer seemed to work + + float -F 5 result + typeset str + for str in 0 0.0 1 1.5 -1 -1.5; do + (( result = abs($str) )) + print $result + done +0:Use of abs on various numbers +>0.00000 +>0.00000 +>1.00000 +>1.50000 +>1.00000 +>1.50000 + + print $(( sqrt(-1) )) +1:Non-negative argument checking for square roots. +?(eval):1: math: argument to sqrt out of range + +# Simple test that the pseudorandom number generators are producing +# something that could conceivably be pseudorandom numbers in a +# linear range. Not a detailed quantitative verification. + integer N=10000 isource ok=1 + float -F f sum sumsq max max2 av sd + typeset -a randoms + randoms=('f = RANDOM' 'f = rand48()') + for isource in 1 2; do + (( sum = sumsq = max = 0 )) + repeat $N; do + let $randoms[$isource] + (( f > max )) && (( max = f )) + (( sum += f, sumsq += f * f )) + done + (( av = sum / N )) + (( sd = sqrt((sumsq - N * av * av) / (N-1)) )) + (( max2 = 0.5 * max )) + if (( av > max2 * 1.1 )) || (( av < max2 * 0.9 )); then + print "WARNING: average of random numbers is suspicious. + Was testing: $randoms[$isource]" + (( ok = 0 )) + fi + if (( sd < max / 4 )); then + print "WARNING: distribution of random numbers is suspicious. + Was testing: $randoms[$isource]" + (( ok = 0 )) + fi + done + (( ok )) +0:Test random number generator distributions are not grossly broken + + float -F 5 g l + (( g = gamma(2), l = lgamma(2) )) + print $g, $l +0:Test Gamma function gamma and lgamma +>1.00000, 0.00000 diff --git a/dotfiles/system/.zsh/modules/Test/V04features.ztst b/dotfiles/system/.zsh/modules/Test/V04features.ztst new file mode 100644 index 0000000..6939053 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/V04features.ztst @@ -0,0 +1,172 @@ +%prep + +# Do some tests on handling of features. +# This also does some slightly more sophisticated loading and +# unloading tests than we did in V01zmodload.ztst. +# +# We use zsh/datetime because it has a list of features that is short +# but contains two types. + + # Subshell for prep test so we can load individual features later + if ! (zmodload zsh/datetime 2>/dev/null); then + ZTST_unimplemented="can't load the zsh/datetime module for testing" + fi + +%test + zmodload -F zsh/datetime + zmodload -lF zsh/datetime +0:Loading modules with no features +>-b:strftime +>-p:EPOCHSECONDS +>-p:EPOCHREALTIME +>-p:epochtime + + zmodload -F zsh/datetime b:strftime + zmodload -lF zsh/datetime +0:Enabling features +>+b:strftime +>-p:EPOCHSECONDS +>-p:EPOCHREALTIME +>-p:epochtime + + zmodload -F zsh/datetime +p:EPOCHSECONDS -b:strftime + zmodload -lF zsh/datetime +0:Disabling features +>-b:strftime +>+p:EPOCHSECONDS +>-p:EPOCHREALTIME +>-p:epochtime + + zmodload -Fe zsh/datetime p:EPOCHSECONDS b:strftime +0:Testing existing features + + zmodload -Fe zsh/datetime +p:EPOCHSECONDS +0:Testing features are in given state (on feature is on) + + zmodload -Fe zsh/datetime -p:EPOCHSECONDS +1:Testing features are in given state (on feature is not off + + zmodload -Fe zsh/datetime +p:strftime +1:Testing features are in given state (off feature is not on) + + zmodload -Fe zsh/datetime -b:strftime +0:Testing features are in given state (off feature is off + + zmodload -Fe zsh/datetime p:EPOCHSECONDS b:strftime b:mktimebetter +1:Testing non-existent features + + zmodload -FlP dtf zsh/datetime + for feature in b:strftime p:EPOCHSECONDS; do + if [[ ${${dtf[(R)?$feature]}[1]} = + ]]; then + print $feature is enabled + else + print $feature is disabled + fi + done +0:Testing features via array parameter +>b:strftime is disabled +>p:EPOCHSECONDS is enabled + + fn() { + local EPOCHSECONDS=scruts + print $EPOCHSECONDS + print ${(t)EPOCHSECONDS} + } + fn + if [[ $EPOCHSECONDS = <-> ]]; then + print EPOCHSECONDS is a number + else + print EPOCHSECONDS is some random piece of junk + fi + print ${(t)EPOCHSECONDS} +0:Module special parameter is hidden by a local parameter +>scruts +>scalar-local +>EPOCHSECONDS is a number +>integer-readonly-hide-hideval-special + + typeset +h EPOCHSECONDS + fn() { + local EPOCHSECONDS=scruts + print Didn\'t get here >&2 + } + fn +1:Unhidden readonly special can't be assigned to when made local +?fn:1: read-only variable: EPOCHSECONDS + + zmodload -u zsh/datetime +0:Module unloaded + + zmodload -e zsh/datetime +1:Module doesn't exist when unloaded + + zmodload -Fe zsh/datetime p:EPOCHSECONDS +1:Module doesn't have features when unloaded + + fn() { + local EPOCHSECONDS=scrimf + zmodload zsh/datetime + } + fn +2:Failed to add parameter if local parameter present +?fn:2: Can't add module parameter `EPOCHSECONDS': local parameter exists +?fn:zsh/datetime:2: error when adding parameter `EPOCHSECONDS' + + zmodload -lF zsh/datetime +0:Feature state with loading after error enabling +>+b:strftime +>-p:EPOCHSECONDS +>+p:EPOCHREALTIME +>+p:epochtime + + zmodload -F zsh/datetime p:EPOCHSECONDS + zmodload -Fe zsh/datetime +p:EPOCHSECONDS +0:Successfully added feature parameter that previously failed + + fn() { + local EPOCHSECONDS=scrooble + zmodload -u zsh/datetime + print $EPOCHSECONDS + } + fn + print ${+EPOCHSECONDS} +0:Successfully unloaded a module despite a parameter being hidden +>scrooble +>0 + + EPOCHSECONDS=(any old parameter) + print -l $EPOCHSECONDS +0:Using parameter as normal after unloading is OK +>any +>old +>parameter + + print strftime is ${builtins[strftime]:-undefined} + zmodload -F zsh/datetime b:strftime + print strftime is ${builtins[strftime]:-undefined} + zmodload -F zsh/datetime -b:strftime + print strftime is ${builtins[strftime]:-undefined} +0:Enabling and disabling of builtins as features +>strftime is undefined +>strftime is defined +>strftime is undefined + + zmodload -u zsh/datetime + zmodload zsh/datetime +2:Loading won't override global parameter +?(eval):2: Can't add module parameter `EPOCHSECONDS': parameter already exists +?(eval):zsh/datetime:2: error when adding parameter `EPOCHSECONDS' + + unset EPOCHSECONDS + zmodload -F zsh/datetime p:EPOCHSECONDS + zmodload -Fe zsh/datetime +p:EPOCHSECONDS +0:unsetting a global parameter allows feature parameter to be enabled + + zmodload -F zsh/datetime -b:strftime -p:EPOCHSECONDS + zmodload zsh/datetime + zmodload -lF zsh/datetime +0:zmodload with no -F enables all features +>+b:strftime +>+p:EPOCHSECONDS +>+p:EPOCHREALTIME +>+p:epochtime diff --git a/dotfiles/system/.zsh/modules/Test/V05styles.ztst b/dotfiles/system/.zsh/modules/Test/V05styles.ztst new file mode 100644 index 0000000..ca95b63 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/V05styles.ztst @@ -0,0 +1,143 @@ +%prep + +# Test the use of styles, if the zsh/zutil module is available. + + if ! zmodload zsh/zutil 2>/dev/null; then + ZTST_unimplemented="can't load the zsh/zutil module for testing" + fi + +%test + zstyle :random:stuff any-old-style with any old value + zstyle :randomly:chosen some-other-style I can go on and on + zstyle -d + zstyle +0:zstyle -d restores a pristine state + +# patterns should be ordered by weight, so add in reverse order to check + zstyle ':ztst:context*' scalar-style other-scalar-value + zstyle ':ztst:context:*' scalar-style second-scalar-value + zstyle ':ztst:context:sub1' scalar-style scalar-value + zstyle ':ztst:context:sub1' array-style array value elements 'with spaces' + zstyle ':ztst:context*' boolean-style false + zstyle ':ztst:context:sub1' boolean-style true +0:defining styles + +# styles are now sorted, but patterns are in order of definition + zstyle +0:listing styles in default format +>array-style +> :ztst:context:sub1 array value elements 'with spaces' +>boolean-style +> :ztst:context:sub1 true +> :ztst:context* false +>scalar-style +> :ztst:context:sub1 scalar-value +> :ztst:context:* second-scalar-value +> :ztst:context* other-scalar-value + + zstyle -L +0:listing styles in zstyle format +>zstyle :ztst:context:sub1 array-style array value elements 'with spaces' +>zstyle :ztst:context:sub1 boolean-style true +>zstyle ':ztst:context*' boolean-style false +>zstyle :ztst:context:sub1 scalar-style scalar-value +>zstyle ':ztst:context:*' scalar-style second-scalar-value +>zstyle ':ztst:context*' scalar-style other-scalar-value + + zstyle -b :ztst:context:sub1 boolean-style bool; print $bool + zstyle -t :ztst:context:sub1 boolean-style +0:boolean test -b/-t + true +>yes + + zstyle -b :ztst:context:sub2 boolean-style bool; print $bool + zstyle -t :ztst:context:sub2 boolean-style +1:boolean test -b/-t + false +>no + + zstyle -b :ztst:context:sub1 boolean-unset-style bool; print $bool + zstyle -t :ztst:context:sub1 boolean-unset-style +2:boolean test -b/-t + unset +>no + + zstyle -T :ztst:context:sub1 boolean-style +0:boolean test -T + true + + zstyle -T :ztst:context:sub2 boolean-style +1:boolean test -T + false + + zstyle -T :ztst:context:sub1 boolean-unset-style +0:boolean test -T + unset + + zstyle -s :ztst:context:sub1 scalar-style scalar && print $scalar + zstyle -s :ztst:context:sub2 scalar-style scalar && print $scalar + zstyle -s :ztst:contextual-psychedelia scalar-style scalar && print $scalar + zstyle -s :ztst:contemplative scalar-style scalar || print no match +0:pattern matching rules +>scalar-value +>second-scalar-value +>other-scalar-value +>no match + + zstyle -s :ztst:context:sub1 array-style scalar + && print $scalar +0:scalar with separator +>array+value+elements+with spaces + + zstyle -e :ztst:\* eval-style 'reply=($something)' + something=(one two three) + zstyle -a :ztst:eval eval-style array && print -l $array +0:zstyle -e evaluations +>one +>two +>three + +# pattern ordering on output is not specified, so although in the +# current implementation it's deterministic we shouldn't +# assume it's always the same. Thus we sort the array. +# (It might be a nice touch to order patterns by weight, which is +# the way they are stored for each separate style.) + zstyle -g array && print -l ${(o)array} +0:retrieving patterns +>:ztst:* +>:ztst:context* +>:ztst:context:* +>:ztst:context:sub1 + + zstyle -m :ztst:context:sub1 array-style 'w* *s' +0:positive pattern match + + zstyle -m :ztst:context:sub1 array-style 'v' +1:negative pattern match + + zstyle -g array ':ztst:context*' && print -l $array +0:retrieving styles by pattern +>boolean-style +>scalar-style + + zstyle -g array ':ztst:context:sub1' array-style && print -l $array +0:retrieving values by pattern and name +>array +>value +>elements +>with spaces + + zstyle -d :ztst:context:sub1 + zstyle +0:deleting styles by pattern only +>boolean-style +> :ztst:context* false +>eval-style +>(eval) :ztst:* 'reply=($something)' +>scalar-style +> :ztst:context:* second-scalar-value +> :ztst:context* other-scalar-value + + zstyle -d :ztst:context\* scalar-style + zstyle +0:deleting styles by pattern and style name +>boolean-style +> :ztst:context* false +>eval-style +>(eval) :ztst:* 'reply=($something)' +>scalar-style +> :ztst:context:* second-scalar-value + diff --git a/dotfiles/system/.zsh/modules/Test/V07pcre.ztst b/dotfiles/system/.zsh/modules/Test/V07pcre.ztst new file mode 100644 index 0000000..ad17707 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/V07pcre.ztst @@ -0,0 +1,139 @@ +%prep + + if ! zmodload -F zsh/pcre C:pcre-match 2>/dev/null + then + ZTST_unimplemented="the zsh/pcre module is not available" + return 0 + fi +# Load the rest of the builtins + zmodload zsh/pcre + setopt rematch_pcre +# Find a UTF-8 locale. + setopt multibyte +# Don't let LC_* override our choice of locale. + unset -m LC_\* + mb_ok= + langs=(en_{US,GB}.{UTF-,utf}8 en.UTF-8 + $(locale -a 2>/dev/null | egrep 'utf8|UTF-8')) + for LANG in $langs; do + if [[ é = ? ]]; then + mb_ok=1 + break; + fi + done + if [[ -z $mb_ok ]]; then + ZTST_unimplemented="no UTF-8 locale or multibyte mode is not implemented" + else + print -u $ZTST_fd Testing PCRE multibyte with locale $LANG + mkdir multibyte.tmp && cd multibyte.tmp + fi + +%test + + [[ 'foo→bar' =~ .([^[:ascii:]]). ]] + print $MATCH + print $match[1] +0:Basic non-ASCII regexp matching +>o→b +>→ + + unset match mend + s=$'\u00a0' + [[ $s =~ '^.$' ]] && print OK + [[ A${s}B =~ .(.). && $match[1] == $s ]] && print OK + [[ A${s}${s}B =~ A([^[:ascii:]]*)B && $mend[1] == 3 ]] && print OK + unset s +0:Raw IMETA characters in input string +>OK +>OK +>OK + + [[ foo =~ f.+ ]] ; print $? + [[ foo =~ x.+ ]] ; print $? + [[ ! foo =~ f.+ ]] ; print $? + [[ ! foo =~ x.+ ]] ; print $? + [[ foo =~ f.+ && bar =~ b.+ ]] ; print $? + [[ foo =~ x.+ && bar =~ b.+ ]] ; print $? + [[ foo =~ f.+ && bar =~ x.+ ]] ; print $? + [[ ! foo =~ f.+ && bar =~ b.+ ]] ; print $? + [[ foo =~ f.+ && ! bar =~ b.+ ]] ; print $? + [[ ! ( foo =~ f.+ && bar =~ b.+ ) ]] ; print $? + [[ ! foo =~ x.+ && bar =~ b.+ ]] ; print $? + [[ foo =~ x.+ && ! bar =~ b.+ ]] ; print $? + [[ ! ( foo =~ x.+ && bar =~ b.+ ) ]] ; print $? +0:Regex result inversion detection +>0 +>1 +>1 +>0 +>0 +>1 +>1 +>1 +>1 +>1 +>0 +>1 +>0 + +# Note that PCRE_ANCHORED only means anchored at the start +# Also note that we don't unset MATCH/match on failed match (and it's an +# open issue as to whether or not we should) + pcre_compile '.(→.)' + pcre_match foo→bar + print $? $MATCH $match ; unset MATCH match + pcre_match foo.bar + print $? $MATCH $match ; unset MATCH match + pcre_match foo†bar + print $? $MATCH $match ; unset MATCH match + pcre_match foo→†ar + print $? $MATCH $match ; unset MATCH match + pcre_study + pcre_match foo→bar + print $? $MATCH $match ; unset MATCH match + pcre_compile -a '.(→.)' + pcre_match foo→bar + print $? $MATCH $match ; unset MATCH match + pcre_match o→bar + print $? $MATCH $match ; unset MATCH match + pcre_match o→b + print $? $MATCH $match ; unset MATCH match + pcre_compile 'x.(→.)' + pcre_match xo→t + print $? $MATCH $match ; unset MATCH match + pcre_match Xo→t + print $? $MATCH $match ; unset MATCH match + pcre_compile -i 'x.(→.)' + pcre_match xo→t + print $? $MATCH $match ; unset MATCH match + pcre_match Xo→t + print $? $MATCH $match ; unset MATCH match +0:pcre_compile interface testing: basic, anchored & case-insensitive +>0 o→b →b +>1 +>1 +>0 o→† →† +>0 o→b →b +>1 +>0 o→b →b +>0 o→b →b +>0 xo→t →t +>1 +>0 xo→t →t +>0 Xo→t →t + + string="The following zip codes: 78884 90210 99513" + pcre_compile -m "\d{5}" + pcre_match -b -- $string && print "$MATCH; ZPCRE_OP: $ZPCRE_OP" + pcre_match -b -n $ZPCRE_OP[(w)2] -- $string || print failed + print "$MATCH; ZPCRE_OP: $ZPCRE_OP" +0:pcre_match -b and pcre_match -n +>78884; ZPCRE_OP: 25 30 +>90210; ZPCRE_OP: 31 36 + +# Subshell because crash on failure + ( setopt re_match_pcre + [[ test.txt =~ '^(.*_)?(test)' ]] + echo $match[2] ) +0:regression for segmentation fault, workers/38307 +>test diff --git a/dotfiles/system/.zsh/modules/Test/V08zpty.ztst b/dotfiles/system/.zsh/modules/Test/V08zpty.ztst new file mode 100644 index 0000000..b0cbfa0 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/V08zpty.ztst @@ -0,0 +1,29 @@ +# zpty is required by tests of interactive modes of the shell itself. +# This tests some extra things. + +%prep + + 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 + + zpty cat cat + zpty -w cat a line of text + var= + zpty -r cat var && print -r -- ${var%%$'\r\n'} + zpty -d cat +0:zpty with a process that does not set up the terminal: internal write +>a line of text + + zpty cat cat + print a line of text | zpty -w cat + var= + zpty -r cat var && print -r -- ${var%%$'\r\n'} + zpty -d cat +0:zpty with a process that does not set up the terminal: write via stdin +>a line of text diff --git a/dotfiles/system/.zsh/modules/Test/V09datetime.ztst b/dotfiles/system/.zsh/modules/Test/V09datetime.ztst new file mode 100644 index 0000000..7905155 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/V09datetime.ztst @@ -0,0 +1,74 @@ +%prep + + if zmodload zsh/datetime 2>/dev/null; then + setopt multibyte + unset LC_ALL + LC_TIME=C + TZ=UTC+0 + # It's not clear this skip_extensions is correct, but the + # format in question is causing problems on Solaris. + # We'll revist this after the release. + [[ "$(strftime %^_10B 0)" = " JANUARY" ]] || skip_extensions=1 + [[ "$(LC_TIME=ja_JP.UTF-8 strftime %OS 1)" = 一 ]] || skip_japanese=1 + else + ZTST_unimplemented="can't load the zsh/datetime module for testing" + fi + +%test + + strftime %y 0 + strftime %Y 1000000000 + strftime %x 1200000000 + strftime %X 1200000001 +0:basic format specifiers +>70 +>2001 +>01/10/08 +>21:20:01 + + strftime %-m_%f_%K_%L 1181100000 + strftime %6. 0 +0:zsh extensions +>6_6_3_3 +>000000 + + if [[ $skip_extensions = 1 ]]; then + ZTST_skip="strftime extensions not supported" + elif [[ $skip_japanese = 1 ]]; then + ZTST_skip="Japanese UTF-8 locale not supported" + else + ( + LC_TIME=ja_JP.UTF-8 + strftime %Ey 1000000000 + strftime %Oy 1000000000 + strftime %Ex 1000000000 + strftime %OS 1000000000 + strftime %03Ey 650000000 + ) + fi +0:alternate format extensions +>13 +>一 +>平成13年09月09日 +>四十 +>002 + + if [[ $skip_extensions = 1 ]]; then + ZTST_skip="strftime extensions not supported" + else + ( + strftime '%#A' 0 + strftime '%^_10B' 0 + strftime %03Ey 650000000 + strftime %-Oe 0 + ) + fi +0:various extensions +>THURSDAY +> JANUARY +>090 +>1 + + print -r -- ${(V)"$(strftime $'%Y\0%m\0%d' 100000000)"} +0:Embedded nulls +>1973^@03^@03 diff --git a/dotfiles/system/.zsh/modules/Test/V10private.ztst b/dotfiles/system/.zsh/modules/Test/V10private.ztst new file mode 100644 index 0000000..78ecd48 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/V10private.ztst @@ -0,0 +1,304 @@ +# Tests for the zsh/param/private module + +%prep + + if ! zmodload zsh/param/private 2>/dev/null; then + ZTST_unimplemented="can't load the zsh/param/private module for testing" + else + # Do not use .tmp here, ztst.zsh will remove it too soon (see %cleanup) + mkdir private.TMP + sed -e 's,# test_zsh_param_private,zmodload zsh/param/private,' < $ZTST_srcdir/B02typeset.ztst > private.TMP/B02 + fi + +%test + + (zmodload -u zsh/param/private && zmodload zsh/param/private) +0:unload and reload the module without crashing + + typeset scalar_test=toplevel + () { + print $scalar_test + private scalar_test + print $+scalar_test + unset scalar_test + print $+scalar_test + } + print $scalar_test +0:basic scope hiding +>toplevel +>1 +>0 +>toplevel + + typeset scalar_test=toplevel + print $scalar_test + () { + private scalar_test=function + print $scalar_test + } + print $scalar_test +0:enter and exit a scope +>toplevel +>function +>toplevel + + print $+unset_test + () { + private unset_test + print $+unset_test + unset_test=setme + print $unset_test + } + print $+unset_test +0:variable defined only in scope +>0 +>1 +>setme +>0 + + # Depends on zsh-5.0.9 typeset keyword + typeset -a array_test=(top level) + () { + local -Pa array_test=(in function) + () { + private array_test + print $+array_test + } + print $array_test + } + print $array_test +0:nested scope with different type, correctly restored +>1 +>in function +>top level + + typeset -a array_test=(top level) + () { + private array_test + array_test=(in function) + } +1:type of private may not be changed by assignment +?(anon):2: array_test: attempt to assign array value to non-array + + typeset -A hash_test=(top level) + () { + setopt localoptions noglob + private hash_test[top] + } +1:associative array fields may not be private +?(anon):private:2: hash_test[top]: can't create local array elements + + () { + private path + } +1:tied params may not be private, part 1 +?(anon):private:1: can't change scope of existing param: path + + () { + private PATH + } +1:tied params may not be private, part 2 +?(anon):private:1: can't change scope of existing param: PATH + + () { + private -h path + print X$path + } +0:privates may hide tied paramters +>X + + # Deliberate type mismatch here + typeset -a hash_test=(top level) + typeset -p hash_test + inner () { + private -p hash_test + print ${(t)hash_test} ${(kv)hash_test} + } + outer () { + local -PA hash_test=(in function) + typeset -p 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 ) +>typeset -g -a hash_test=( top level ) +>array-local top level +>top level +F:note "typeset" rather than "private" in output from outer + + () { + private -a array_test + local array_test=scalar + } +1:private cannot be re-declared as local +?(anon):local:2: array_test: inconsistent type for assignment + + () { + local hash_test=scalar + private -A hash_test + } +1:local cannot be re-declared as private +?(anon):private:2: can't change scope of existing param: hash_test + + inner () { + print $+scalar_test + $ZTST_testdir/../Src/zsh -fc 'print X $scalar_test' + } + () { + private -x scalar_test=whaat + $ZTST_testdir/../Src/zsh -fc 'print X $scalar_test' + inner + print Y $scalar_test + } +0:exported private behaves like a local, part 1 +>X whaat +>0 +>X whaat +>Y whaat + + inner () { + typeset -p array_test + $ZTST_testdir/../Src/zsh -fc 'print X $array_test' + } + () { + local -Pax array_test=(whaat) + print Y $array_test + $ZTST_testdir/../Src/zsh -fc 'print X $array_test' + inner + } +0:exported private behaves like a local, part 2 (arrays do not export) +?inner:typeset:1: no such variable: array_test +>Y whaat +>X +>X + + inner () { + print $+scalar_test + $ZTST_testdir/../Src/zsh -fc 'print X $scalar_test' + } + () { + private scalar_test=whaat + export scalar_test + $ZTST_testdir/../Src/zsh -fc 'print X $scalar_test' + inner + () { + print $+scalar_test + $ZTST_testdir/../Src/zsh -fc 'print X $scalar_test' + } + print Y $scalar_test + } +0:exported private behaves like a local, part 3 (export does not change scope) +>X whaat +>0 +>X whaat +>0 +>X whaat +>Y whaat + + typeset -A hash_test=(top level) + () { + local -PA hash_test=(in function) + () { + print X ${(kv)hash_test} + } + print Y ${(kv)hash_test} + } + print ${(kv)hash_test} +0:privates are not visible in anonymous functions, part 1 +>X top level +>Y in function +>top level + + typeset -A hash_test=(top level) + () { + local -PA hash_test=(in function) + () { + print X ${(kv)hash_test} + hash_test[in]=deeper + } + print Y ${(kv)hash_test} + } + print ${(okv)hash_test} +0:privates are not visible in anonymous functions, part 2 +>X top level +>Y in function +>deeper in level top + + typeset -A hash_test=(top level) + () { + local -Pa array_test=(in function) + local -PA hash_test=($array_test) + () { + print X ${(kv)hash_test} + hash_test=(even deeper) + { + array_test+=(${(kv)hash_test}) + } always { + print ${array_test-array_test not set} ${(t)array_test} + } + } + print Y ${(kv)hash_test} Z $array_test + } + print ${(kv)hash_test} ${(t)array_test} +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 +F:future revision will create a global with this assignment + + typeset -a array_test + typeset -A hash_test=(top level) + () { + local -Pa array_test=(in function) + local -PA hash_test=($array_test) + () { + print X ${(kv)hash_test} + hash_test=(even deeper) + array_test+=(${(kv)hash_test}) + } + print Y ${(kv)hash_test} Z $array_test + } + print ${(kv)hash_test} $array_test +0:privates are not visible in anonymous functions, part 4 +>X top level +>Y in function Z in function +>even deeper even deeper + + typeset -A hash_test=(top level) + () { + local -PA hash_test=(in function) + () { + print X ${(kv)hash_test} + unset hash_test + } + print Y ${(kv)hash_test} + } + print ${(t)hash_test} ${(kv)hash_test} +0:privates are not visible in anonymous functions, part 5 +>X top level +>Y in function +> + + # Subshell because otherwise this silently dumps core when broken + ( () { private SECONDS } ) +1:special parameters cannot be made private +?(anon):private: can't change scope of existing param: SECONDS + + () { private -h SECONDS } +0:private parameter may hide a special parameter + + if (( UID )); then + ZTST_verbose=0 $ZTST_exe +Z -f $ZTST_srcdir/ztst.zsh private.TMP/B02 + else + ZTST_skip="cannot re-run typeset tests when tests run as superuser" + fi +0:typeset still works with zsh/param/private module loaded +*>* +*>* + +%clean + + rm -r private.TMP diff --git a/dotfiles/system/.zsh/modules/Test/W01history.ztst b/dotfiles/system/.zsh/modules/Test/W01history.ztst new file mode 100644 index 0000000..6ef9b11 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/W01history.ztst @@ -0,0 +1,60 @@ +# Tests for BANG_HIST replacements + +%prep + + if [[ -t 0 ]]; then print -u $ZTST_fd History tests write to /dev/tty; fi + +%test + + $ZTST_testdir/../Src/zsh -fis <<<' + print one two three four five six seven eight nine ten + print !:$ !:10 !:9 !:1 !:0 + print one two three four five six seven eight nine ten + print !:0-$ !:1-2 + ' 2>/dev/null +0:History word references +>one two three four five six seven eight nine ten +>ten ten nine one print +>one two three four five six seven eight nine ten +>print one two three four five six seven eight nine ten one two + + $ZTST_testdir/../Src/zsh -fis <<<' + print line one of an arbitrary series + print issue two for some mystery sequence + print !-1:5-$ + print !1:2 + print !2:2 + print !-3:1-$ + ' 2>/dev/null +0:History line numbering +>line one of an arbitrary series +>issue two for some mystery sequence +>mystery sequence +>one +>two +>mystery sequence + + $ZTST_testdir/../Src/zsh -fis <<<' + print All metaphor, Malachi, stilts and all + print !1:2:s/,/\\\\?/ !1:2:s/m/shm/:s/,/\!/ + print !1:2:& + print -l !1:2-3:gs/a/o/ + ' 2>/dev/null +0:History substitution +>All metaphor, Malachi, stilts and all +>metaphor? shmetaphor! +>metaphor! +>metophor, +>Molochi, + + $ZTST_testdir/../Src/zsh -fis <<<' + echo foo bar + echo $(!!) again + echo more $( !! )' 2>/dev/null +0:Regression test for history references in command substitution +>foo bar +>foo bar again +>more foo bar again +*?* +F:Check that a history bug introduced by workers/34160 is working again. +# Discarded line of error output consumes prompts printed by "zsh -i". diff --git a/dotfiles/system/.zsh/modules/Test/comptest b/dotfiles/system/.zsh/modules/Test/comptest new file mode 100644 index 0000000..166d0b4 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/comptest @@ -0,0 +1,177 @@ +comptestinit () { + setopt extendedglob + [[ -d $ZTST_testdir/Modules/zsh ]] && module_path=( $ZTST_testdir/Modules ) + fpath=( $ZTST_srcdir/../Functions/*~*/CVS(/) + $ZTST_srcdir/../Completion + $ZTST_srcdir/../Completion/*/*~*/CVS(/) ) + + zmodload zsh/zpty || return $? + + comptest_zsh=${ZSH:-zsh} + comptest_keymap=e + + while getopts vz: opt; do + case $opt in + z) comptest_zsh="$OPTARG";; + v) comptest_keymap="v";; + esac + done + (( OPTIND > 1 )) && shift $(( OPTIND - 1 )) + + export PS1="<PROMPT>" + zpty zsh "$comptest_zsh -f +Z" + + zpty -r zsh log1 "*<PROMPT>*" || { + print "first prompt hasn't appeared." + return 1 + } + + comptesteval \ +"export LC_ALL=${ZSH_TEST_LANG:-C}" \ +"emulate -R zsh" \ +"export ZDOTDIR=$ZTST_testdir" \ +"module_path=( $module_path )" \ +"fpath=( $fpath )" \ +"bindkey -$comptest_keymap" \ +'LISTMAX=10000000 +stty 38400 columns 80 rows 24 tabs -icanon -iexten +TERM=vt100 +KEYTIMEOUT=1 +setopt zle +autoload -U compinit +compinit -u +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> +" +zstyle ":completion:*:descriptions" format "<DESCRIPTION>%d</DESCRIPTION> +" +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 + print -lr - "<LBUFFER>$LBUFFER</LBUFFER>" "<RBUFFER>$RBUFFER</RBUFFER>" + zle clear-screen + zle -R +} +list-choices-with-report () { + print -lr "<WIDGET><list-choices>" + zle list-choices + zle clear-screen + zle -R +} +comp-finish () { + print "<WIDGET><finish>" + zle kill-whole-line + zle clear-screen + zle -R +} +zle-finish () { + local buffer="$BUFFER" cursor="$CURSOR" mark="$MARK" + (( region_active)) || unset mark + BUFFER="" + zle -I + zle clear-screen + zle redisplay + print -lr "<WIDGET><finish>" "BUFFER: $buffer" "CURSOR: $cursor" + (( $+mark )) && print -lr "MARK: $mark" + zle accept-line +} +zle -N expand-or-complete-with-report +zle -N list-choices-with-report +zle -N comp-finish +zle -N zle-finish +bindkey "^I" expand-or-complete-with-report +bindkey "^D" list-choices-with-report +bindkey "^Z" comp-finish +bindkey "^X" zle-finish +bindkey -a "^X" zle-finish +' +} + +zpty_flush() { + local junk + if zpty -r -t zsh junk \*; then + (( ZTST_verbose > 2 )) && print -n -u $ZTST_fd "$*: ${(V)junk}" + while zpty -r -t zsh junk \* ; do + (( ZTST_verbose > 2 )) && print -n -u $ZTST_fd "${(V)junk}" + done + (( ZTST_verbose > 2 )) && print -u $ZTST_fd '' + fi +} + +zpty_run() { + zpty -w zsh "$*" + zpty -r -m zsh log "*<PROMPT>*" || { + print "prompt hasn't appeared." + return 1 + } +} + +comptesteval () { + local tmp=/tmp/comptest.$$ + + 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 +} + +comptest () { + input="$*" + zpty -n -w zsh "$input"$'\C-Z' + zpty -r -m zsh log "*<WIDGET><finish>*<PROMPT>*" || { + print "failed to invoke finish widget." + return 1 + } + + logs=(${(s:<WIDGET>:)log}) + shift logs + + for log in "$logs[@]"; do + if [[ "$log" = (#b)*$'<LBUFFER>'(*)$'</LBUFFER>\r\n<RBUFFER>'(*)$'</RBUFFER>'* ]]; then + print -lr "line: {$match[1]}{$match[2]}" + fi + while (( ${(N)log#*(#b)(<LC><(??)><RC>(*)<EC>|<DESCRIPTION>(*)</DESCRIPTION>|<MESSAGE>(*)</MESSAGE>|<COMPADD>(*)</COMPADD>|<INSERT_POSITIONS>(*)</INSERT_POSITIONS>)} )); do + log="${log[$mend[1]+1,-1]}" + if (( 0 <= $mbegin[2] )); then + if [[ $match[2] != TC && $match[3] != \ # ]]; then + print -lr "$match[2]:{${match[3]%${(%):-%E}}}" + fi + elif (( 0 <= $mbegin[4] )); then + print -lr "DESCRIPTION:{$match[4]}" + elif (( 0 <= $mbegin[5] )); then + print -lr "MESSAGE:{$match[5]}" + elif (( 0 <= $mbegin[6] )); then + print -lr "COMPADD:{${${match[6]}//[$'\r\n']/}}" + elif (( 0 <= $mbegin[7] )); then + print -lr "INSERT_POSITIONS:{${${match[7]}//[$'\r\n']/}}" + fi + done + done +} + +zletest () { + local first=0 + for input; do + # zpty_flush Before zletest + # sleep for $KEYTIMEOUT + (( first++ )) && { sleep 2 & } | read -t 0.011 -u 0 -k 1 + zpty -n -w zsh "$input" + done + zpty -n -w zsh $'\C-X' + zpty -r -m zsh log "*<WIDGET><finish>*<PROMPT>*" || { + print "failed to invoke finish widget." + return 1 + } + # zpty_flush After zletest + print -lr "${(@)${(@ps:\r\n:)log##*<WIDGET><finish>}[2,-2]}" +} diff --git a/dotfiles/system/.zsh/modules/Test/runtests.zsh b/dotfiles/system/.zsh/modules/Test/runtests.zsh new file mode 100644 index 0000000..562234d --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/runtests.zsh @@ -0,0 +1,27 @@ +#!/bin/zsh -f + +emulate zsh + +# Run all specified tests, keeping count of which succeeded. +# The reason for this extra layer above the test script is to +# protect from catastrophic failure of an individual test. +# We could probably do that with subshells instead. + +integer success failure skipped retval +for file in "${(f)ZTST_testlist}"; do + $ZTST_exe +Z -f $ZTST_srcdir/ztst.zsh $file + retval=$? + if (( $retval == 2 )); then + (( skipped++ )) + elif (( $retval )); then + (( failure++ )) + else + (( success++ )) + fi +done +print "************************************** +$success successful test script${${success:#1}:+s}, \ +$failure failure${${failure:#1}:+s}, \ +$skipped skipped +**************************************" +return $(( failure ? 1 : 0 )) diff --git a/dotfiles/system/.zsh/modules/Test/ztst.zsh b/dotfiles/system/.zsh/modules/Test/ztst.zsh new file mode 100755 index 0000000..f172ae1 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/ztst.zsh @@ -0,0 +1,547 @@ +#!/bin/zsh -f +# The line above is just for convenience. Normally tests will be run using +# a specified version of zsh. With dynamic loading, any required libraries +# must already have been installed in that case. +# +# Takes one argument: the name of the test file. Currently only one such +# file will be processed each time ztst.zsh is run. This is slower, but +# much safer in terms of preserving the correct status. +# To avoid namespace pollution, all functions and parameters used +# only by the script begin with ZTST_. +# +# Options (without arguments) may precede the test file argument; these +# are interpreted as shell options to set. -x is probably the most useful. + +# Produce verbose messages if non-zero. +# If 1, produce reports of tests executed; if 2, also report on progress. +# Defined in such a way that any value from the environment is used. +: ${ZTST_verbose:=0} + +# We require all options to be reset, not just emulation options. +# Unfortunately, due to the crud which may be in /etc/zshenv this might +# still not be good enough. Maybe we should trick it somehow. +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_COLLATE ]] && LC_COLLATE=C +[[ -n $LC_NUMERIC ]] && LC_NUMERIC=C +[[ -n $LC_MESSAGES ]] && LC_MESSAGES=C +[[ -n $LANG ]] && LANG=C + +# Don't propagate variables that are set by default in the shell. +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. +zmodload zsh/parameter + +# Note that both the following are regular arrays, since we only use them +# in whole array assignments to/from $options. +# Options set in test code (i.e. by default all standard options) +ZTST_testopts=(${(kv)options}) + +setopt extendedglob nonomatch +while [[ $1 = [-+]* ]]; do + set $1 + shift +done +# Options set in main script +ZTST_mainopts=(${(kv)options}) + +# We run in the current directory, so remember it. +ZTST_testdir=$PWD +ZTST_testname=$1 + +integer ZTST_testfailed + +# This is POSIX nonsense. Because of the vague feeling someone, somewhere +# may one day need to examine the arguments of "tail" using a standard +# option parser, every Unix user in the world is expected to switch +# to using "tail -n NUM" instead of "tail -NUM". Older versions of +# tail don't support this. +tail() { + emulate -L zsh + + if [[ -z $TAIL_SUPPORTS_MINUS_N ]]; then + local test + test=$(echo "foo\nbar" | command tail -n 1 2>/dev/null) + if [[ $test = bar ]]; then + TAIL_SUPPORTS_MINUS_N=1 + else + TAIL_SUPPORTS_MINUS_N=0 + fi + fi + + integer argi=${argv[(i)-<->]} + + if [[ $argi -le $# && $TAIL_SUPPORTS_MINUS_N = 1 ]]; then + argv[$argi]=(-n ${argv[$argi][2,-1]}) + fi + + command tail "$argv[@]" +} + +# The source directory is not necessarily the current directory, +# but if $0 doesn't contain a `/' assume it is. +if [[ $0 = */* ]]; then + ZTST_srcdir=${0%/*} +else + ZTST_srcdir=$PWD +fi +[[ $ZTST_srcdir = /* ]] || ZTST_srcdir="$ZTST_testdir/$ZTST_srcdir" + +# Set the function autoload paths to correspond to this build of zsh. +fpath=( $ZTST_srcdir/../Functions/*~*/CVS(/) + $ZTST_srcdir/../Completion + $ZTST_srcdir/../Completion/*/*~*/CVS(/) ) + +: ${TMPPREFIX:=/tmp/zsh} +ZTST_tmp=${TMPPREFIX}.ztst.$$ +if ! rm -f $ZTST_tmp || ! mkdir -p $ZTST_tmp || ! chmod go-w $ZTST_tmp; then + print "Can't create $ZTST_tmp for exclusive use." >&2 + exit 1 +fi +# Temporary files for redirection inside tests. +ZTST_in=${ZTST_tmp}/ztst.in +# hold the expected output +ZTST_out=${ZTST_tmp}/ztst.out +ZTST_err=${ZTST_tmp}/ztst.err +# hold the actual output from the test +ZTST_tout=${ZTST_tmp}/ztst.tout +ZTST_terr=${ZTST_tmp}/ztst.terr + +ZTST_cleanup() { + cd $ZTST_testdir + rm -rf $ZTST_testdir/dummy.tmp $ZTST_testdir/*.tmp(N) ${ZTST_tmp} +} + +# This cleanup always gets performed, even if we abort. Later, +# we should try and arrange that any test-specific cleanup +# always gets called as well. +##trap 'print cleaning up... +##ZTST_cleanup' INT QUIT TERM +# Make sure it's clean now. +rm -rf dummy.tmp *.tmp + +# Report failure. Note that all output regarding the tests goes to stdout. +# That saves an unpleasant mixture of stdout and stderr to sort out. +ZTST_testfailed() { + print -r "Test $ZTST_testname failed: $1" + if [[ -n $ZTST_message ]]; then + print -r "Was testing: $ZTST_message" + fi + print -r "$ZTST_testname: test failed." + 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() { + local lev=$1 + shift + if [[ -n $ZTST_verbose && $ZTST_verbose -ge $lev ]]; then + print -r -u $ZTST_fd -- $* + fi +} +ZTST_hashmark() { + if [[ ZTST_verbose -le 0 && -t $ZTST_fd ]]; then + print -n -u$ZTST_fd -- ${(pl:SECONDS::\#::\#\r:)} + fi + (( SECONDS > COLUMNS+1 && (SECONDS -= COLUMNS) )) +} + +if [[ ! -r $ZTST_testname ]]; then + ZTST_testfailed "can't read test file." + exit 1 +fi + +exec {ZTST_fd}>&1 +exec {ZTST_input}<$ZTST_testname + +# The current line read from the test file. +ZTST_curline='' +# The current section being run +ZTST_cursect='' + +# Get a new input line. Don't mangle spaces; set IFS locally to empty. +# We shall skip comments at this level. +ZTST_getline() { + local IFS= + while true; do + read -u $ZTST_input -r ZTST_curline || return 1 + [[ $ZTST_curline == \#* ]] || return 0 + done +} + +# Get the name of the section. It may already have been read into +# $curline, or we may have to skip some initial comments to find it. +# If argument present, it's OK to skip the reset of the current section, +# so no error if we find garbage. +ZTST_getsect() { + local match mbegin mend + + while [[ $ZTST_curline != '%'(#b)([[:alnum:]]##)* ]]; do + ZTST_getline || return 1 + [[ $ZTST_curline = [[:blank:]]# ]] && continue + if [[ $# -eq 0 && $ZTST_curline != '%'[[:alnum:]]##* ]]; then + ZTST_testfailed "bad line found before or after section: +$ZTST_curline" + exit 1 + fi + done + # have the next line ready waiting + ZTST_getline + ZTST_cursect=${match[1]} + ZTST_verbose 2 "ZTST_getsect: read section name: $ZTST_cursect" + return 0 +} + +# Read in an indented code chunk for execution +ZTST_getchunk() { + # Code chunks are always separated by blank lines or the + # end of a section, so if we already have a piece of code, + # we keep it. Currently that shouldn't actually happen. + ZTST_code='' + # First find the chunk. + while [[ $ZTST_curline = [[:blank:]]# ]]; do + ZTST_getline || break + done + while [[ $ZTST_curline = [[:blank:]]##[^[:blank:]]* ]]; do + ZTST_code="${ZTST_code:+${ZTST_code} +}${ZTST_curline}" + ZTST_getline || break + done + ZTST_verbose 2 "ZTST_getchunk: read code chunk: +$ZTST_code" + [[ -n $ZTST_code ]] +} + +# Read in a piece for redirection. +ZTST_getredir() { + local char=${ZTST_curline[1]} fn + ZTST_redir=${ZTST_curline[2,-1]} + while ZTST_getline; do + [[ $ZTST_curline[1] = $char ]] || break + ZTST_redir="${ZTST_redir} +${ZTST_curline[2,-1]}" + done + ZTST_verbose 2 "ZTST_getredir: read redir for '$char': +$ZTST_redir" + + case $char in + ('<') fn=$ZTST_in + ;; + ('>') fn=$ZTST_out + ;; + ('?') fn=$ZTST_err + ;; + (*) ZTST_testfailed "bad redir operator: $char" + return 1 + ;; + esac + if [[ $ZTST_flags = *q* && $char = '<' ]]; then + # delay substituting output until variables are set + print -r -- "${(e)ZTST_redir}" >>$fn + else + print -r -- "$ZTST_redir" >>$fn + fi + + return 0 +} + +# Execute an indented chunk. Redirections will already have +# been set up, but we need to handle the options. +ZTST_execchunk() { + setopt localloops # don't let continue & break propagate out + options=($ZTST_testopts) + () { + unsetopt localloops + eval "$ZTST_code" + } + ZTST_status=$? + # careful... ksh_arrays may be in effect. + ZTST_testopts=(${(kv)options[*]}) + options=(${ZTST_mainopts[*]}) + ZTST_verbose 2 "ZTST_execchunk: status $ZTST_status" + return $ZTST_status +} + +# Functions for preparation and cleaning. +# When cleaning up (non-zero string argument), we ignore status. +ZTST_prepclean() { + # Execute indented code chunks. + while ZTST_getchunk; do + ZTST_execchunk >/dev/null || [[ -n $1 ]] || { + [[ -n "$ZTST_unimplemented" ]] || + ZTST_testfailed "non-zero status from preparation code: +$ZTST_code" && return 0 + } + done +} + +# diff wrapper +ZTST_diff() { + emulate -L zsh + setopt extendedglob + + local diff_out + integer diff_pat diff_ret + + case $1 in + (p) + diff_pat=1 + ;; + + (d) + ;; + + (*) + print "Bad ZTST_diff code: d for diff, p for pattern match" + ;; + esac + shift + + if (( diff_pat )); then + local -a diff_lines1 diff_lines2 + integer failed i + + diff_lines1=("${(f)$(<$argv[-2])}") + diff_lines2=("${(f)$(<$argv[-1])}") + if (( ${#diff_lines1} != ${#diff_lines2} )); then + failed=1 + else + for (( i = 1; i <= ${#diff_lines1}; i++ )); do + if [[ ${diff_lines2[i]} != ${~diff_lines1[i]} ]]; then + failed=1 + break + fi + done + fi + if (( failed )); then + print -rl "Pattern match failed:" \<${^diff_lines1} \>${^diff_lines2} + diff_ret=1 + fi + else + diff_out=$(diff "$@") + diff_ret="$?" + if [[ "$diff_ret" != "0" ]]; then + print -r -- "$diff_out" + fi + fi + + return "$diff_ret" +} + +ZTST_test() { + local last match mbegin mend found substlines + local diff_out diff_err + local ZTST_skip + + while true; do + rm -f $ZTST_in $ZTST_out $ZTST_err + touch $ZTST_in $ZTST_out $ZTST_err + ZTST_message='' + ZTST_failmsg='' + found=0 + diff_out=d + diff_err=d + + ZTST_verbose 2 "ZTST_test: looking for new test" + + while true; do + ZTST_verbose 2 "ZTST_test: examining line: +$ZTST_curline" + case $ZTST_curline in + (%*) if [[ $found = 0 ]]; then + break 2 + else + last=1 + break + fi + ;; + ([[:space:]]#) + if [[ $found = 0 ]]; then + ZTST_getline || break 2 + continue + else + break + fi + ;; + ([[:space:]]##[^[:space:]]*) ZTST_getchunk + if [[ $ZTST_curline == (#b)([-0-9]##)([[:alpha:]]#)(:*)# ]]; then + ZTST_xstatus=$match[1] + ZTST_flags=$match[2] + ZTST_message=${match[3]:+${match[3][2,-1]}} + else + ZTST_testfailed "expecting test status at: +$ZTST_curline" + return 1 + fi + ZTST_getline + found=1 + ;; + ('<'*) ZTST_getredir || return 1 + found=1 + ;; + ('*>'*) + ZTST_curline=${ZTST_curline[2,-1]} + diff_out=p + ;& + ('>'*) + ZTST_getredir || return 1 + found=1 + ;; + ('*?'*) + ZTST_curline=${ZTST_curline[2,-1]} + diff_err=p + ;& + ('?'*) + ZTST_getredir || return 1 + found=1 + ;; + ('F:'*) ZTST_failmsg="${ZTST_failmsg:+${ZTST_failmsg} +} ${ZTST_curline[3,-1]}" + ZTST_getline + found=1 + ;; + (*) ZTST_testfailed "bad line in test block: +$ZTST_curline" + return 1 + ;; + esac + done + + # If we found some code to execute... + if [[ -n $ZTST_code ]]; then + ZTST_hashmark + ZTST_verbose 1 "Running test: $ZTST_message" + ZTST_verbose 2 "ZTST_test: expecting status: $ZTST_xstatus" + ZTST_verbose 2 "Input: $ZTST_in, output: $ZTST_out, error: $ZTST_terr" + + ZTST_execchunk <$ZTST_in >$ZTST_tout 2>$ZTST_terr + + if [[ -n $ZTST_skip ]]; then + ZTST_verbose 0 "Test case skipped: $ZTST_skip" + ZTST_skip= + if [[ -n $last ]]; then + break + else + continue + fi + fi + + # First check we got the right status, if specified. + if [[ $ZTST_xstatus != - && $ZTST_xstatus != $ZTST_status ]]; then + ZTST_testfailed "bad status $ZTST_status, expected $ZTST_xstatus from: +$ZTST_code${$(<$ZTST_terr):+ +Error output: +$(<$ZTST_terr)}" + return 1 + fi + + ZTST_verbose 2 "ZTST_test: test produced standard output: +$(<$ZTST_tout) +ZTST_test: and standard error: +$(<$ZTST_terr)" + + # Now check output and error. + if [[ $ZTST_flags = *q* && -s $ZTST_out ]]; then + substlines="$(<$ZTST_out)" + rm -rf $ZTST_out + print -r -- "${(e)substlines}" >$ZTST_out + fi + if [[ $ZTST_flags != *d* ]] && ! ZTST_diff $diff_out -u $ZTST_out $ZTST_tout; then + ZTST_testfailed "output differs from expected as shown above for: +$ZTST_code${$(<$ZTST_terr):+ +Error output: +$(<$ZTST_terr)}" + return 1 + fi + if [[ $ZTST_flags = *q* && -s $ZTST_err ]]; then + substlines="$(<$ZTST_err)" + rm -rf $ZTST_err + print -r -- "${(e)substlines}" >$ZTST_err + fi + if [[ $ZTST_flags != *D* ]] && ! ZTST_diff $diff_err -u $ZTST_err $ZTST_terr; then + ZTST_testfailed "error output differs from expected as shown above for: +$ZTST_code" + return 1 + fi + fi + ZTST_verbose 1 "Test successful." + [[ -n $last ]] && break + done + + ZTST_verbose 2 "ZTST_test: all tests successful" + + # reset message to keep ZTST_testfailed output correct + ZTST_message='' +} + + +# Remember which sections we've done. +typeset -A ZTST_sects +ZTST_sects=(prep 0 test 0 clean 0) + +print "$ZTST_testname: starting." + +# Now go through all the different sections until the end. +# prep section may set ZTST_unimplemented, in this case the actual +# tests will be skipped +ZTST_skipok= +ZTST_unimplemented= +while [[ -z "$ZTST_unimplemented" ]] && ZTST_getsect $ZTST_skipok; do + case $ZTST_cursect in + (prep) if (( ${ZTST_sects[prep]} + ${ZTST_sects[test]} + \ + ${ZTST_sects[clean]} )); then + ZTST_testfailed "\`prep' section must come first" + exit 1 + fi + ZTST_prepclean + ZTST_sects[prep]=1 + ;; + (test) + if (( ${ZTST_sects[test]} + ${ZTST_sects[clean]} )); then + ZTST_testfailed "bad placement of \`test' section" + exit 1 + fi + # careful here: we can't execute ZTST_test before || or && + # because that affects the behaviour of traps in the tests. + ZTST_test + (( $? )) && ZTST_skipok=1 + ZTST_sects[test]=1 + ;; + (clean) + if (( ${ZTST_sects[test]} == 0 || ${ZTST_sects[clean]} )); then + ZTST_testfailed "bad use of \`clean' section" + else + ZTST_prepclean 1 + ZTST_sects[clean]=1 + fi + ZTST_skipok= + ;; + *) ZTST_testfailed "bad section name: $ZTST_cursect" + ;; + esac +done + +if [[ -n "$ZTST_unimplemented" ]]; then + print "$ZTST_testname: skipped ($ZTST_unimplemented)" + ZTST_testfailed=2 +elif (( ! $ZTST_testfailed )); then + print "$ZTST_testname: all tests successful." +fi +ZTST_cleanup +exit $(( ZTST_testfailed )) |
