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