zsh-workers
 help / color / mirror / code / Atom feed
From: Bart Schaefer <schaefer@brasslantern.com>
To: Zsh hackers list <zsh-workers@zsh.org>
Subject: [PATCH] Tests for nofork substitutions
Date: Mon, 24 Jul 2023 23:25:52 -0700	[thread overview]
Message-ID: <CAH+w=7ZYKN4T6vUc8zd4SB4knfsbbcBaccGgm9wKqGEF707ZhQ@mail.gmail.com> (raw)

[-- Attachment #1: Type: text/plain, Size: 1187 bytes --]

Suggestions for additional tests?

Some remarks on these tests:

1) In the example
   ${| print -v REPLY one word here; setopt shwordsplit }

Although the current context is used so the setopt persists after the
command is executed, the substitution code has already decided what
options apply to the result, so the words in this example are not
split.  It would be pretty complicated to invert that.

2) It occurred to me that ${|var| ...} is just another way to write
${${| ...}:-$var} (if REPLY remains unset).  So the former could be
dropped with no loss of functionality, if consensus deems it to be
fluff.

3) Note that ${|...} always substitutes at least an empty string, so
${${|...}-alt} is never "alt".

4) In the example
    print ${|return 1} $?
The value printed for $? is 1 but the status of the whole expression
is 0, that is, it is the status of the "print" command.

5) If a test sets the errreturn option, the whole test harness breaks
silently.  I encountered some other instances of that too, e.g., only
about half of the tests were run but the harness still reported "all
tests successful".

6) While writing the above I thought of another test I could add.  Later.

[-- Attachment #2: nofork-tests.txt --]
[-- Type: text/plain, Size: 7136 bytes --]

diff --git b/Test/D10nofork.ztst a/Test/D10nofork.ztst
new file mode 100644
index 000000000..6e14f343e
--- /dev/null
+++ a/Test/D10nofork.ztst
@@ -0,0 +1,326 @@
+# Tests for "nofork" command substitution.
+
+%prep
+  mkdir nofork.tmp
+  touch nofork.tmp/file{1,2}.txt
+
+  purr() { print -r -- "$@" }
+  purl() { print -rl -- "$@" }
+
+%test
+
+  REPLY=OUTER
+  purr ${| REPLY=INNER } $REPLY
+0:Basic substitution and REPLY scoping
+>INNER OUTER
+
+  purr ${| REPLY=first}:${| REPLY=second}:$REPLY
+0:re-scoping of REPLY in one statement
+>first:second:OUTER
+
+  purr BEGIN${| printf -v REPLY '%s\n' one two three ; }END
+0:Adjacent words
+>BEGINone
+>two
+>three
+>END
+
+  purr "BEGIN${| printf -v REPLY '%s\n' one two three }END"
+0:Adjacent words and quoting, part 1
+>BEGINone
+>two
+>three
+>END
+
+  purr BEGIN"${| printf -v REPLY '%s\n' one two three }"END
+0:Adjacent words and quoting, part 2
+>BEGINone
+>two
+>three
+>END
+
+  purr BEGIN"${|
+   printf -v REPLY '%s\n'\
+    one two three
+  }"END
+0:Embedded newlines
+>BEGINone
+>two
+>three
+>END
+
+  purr BEGIN"${|
+   printf -v REPLY $'%s\n' one two three
+  }"END
+0:Embedded newlines and $'...'
+>BEGINone
+>two
+>three
+>END
+
+  purl ${| print -v REPLY one word here; setopt shwordsplit }
+  purl ${| print -v REPLY three words here }
+  purl "and ${| print -v REPLY one word here }"
+  unsetopt shwordsplit
+0:test word splitting on result
+F:setting option inside is too late for that substitution
+>one word here
+>three
+>words
+>here
+>and one word here
+
+ (
+  cd nofork.tmp
+  setopt globsubst
+  print ${| REPLY=f* }
+  print ${| REPLY=f? }*
+  unsetopt globsubst
+  print ${| REPLY=f* }
+  print ${| REPLY=f? }*
+ )
+1:globsubst on result
+>file1.txt file2.txt
+>file1.txt file2.txt
+>f*
+?(eval):8: no matches found: f?*
+
+  purr ${| REPLY=$'trailing newlines remain\n\n' }
+0:newline removal should not occur
+>trailing newlines remain
+>
+>
+
+  () {
+   purr ${| REPLY=$* ; shift 2 }
+   purr $*
+  } these are arguments
+0:access to context $argv
+>these are arguments
+>arguments
+
+  purr ${:-${| REPLY=${REPLY:-buried}}}
+  purr ${:-"${| REPLY=${REPLY:-more buried}}"}
+0:nofork inside parameter scope
+>buried
+>more buried
+
+  : ${(e):-'${| REPLY=oops'}
+1:unclosed braces are sometimes a bad substitution
+F:This seems silly, but see A01grammar ${(e):-'${'} test
+?(eval):1: bad substitution
+
+  purr ${| REPLY=oops
+1:other times lack of closing brace is merely unexpected
+F:Why not use this error in the previous case as well?
+?(eval):1: closing brace expected
+
+# Next tests check that the PS2 stack is properly managed on error
+
+  purr ${| REPLY=${REPLY:-buried}}}
+1:unbalanced braces, part 0
+?(eval):1: parse error near `}'
+
+  purr ${:-${| REPLY=${REPLY:-buried}}
+1:unbalanced braces, part 1
+?(eval):1: closing brace expected
+
+  purr ${:-"${| REPLY=${REPLY:-more buried}"}
+1:unbalanced braces, part 2
+?(eval):1: unmatched "
+
+  purr ${:-"${| REPLY=${REPLY:-more buried"}}}
+1:unbalanced braces, part 3
+?(eval):1: unmatched "
+
+  purr ${:-"${| REPLY=${REPLY:-more buried}}}"
+1:unbalanced braces, part 4
+?(eval):1: closing brace expected
+
+# Same tests with leading space (future-proofing)
+
+  purr ${ purr ${REPLY:-buried}}}
+1:unbalanced braces, part 0+
+?(eval):1: parse error near `}'
+
+  purr ${:-${ purr ${REPLY:-buried}}
+1:unbalanced braces, part 1+
+?(eval):1: closing brace expected
+
+  purr ${:-"${ purr ${REPLY:-more buried}"}
+1:unbalanced braces, part 2+
+?(eval):1: unmatched "
+
+  purr ${:-"${ purr ${REPLY:-more buried"}}}
+1:unbalanced braces, part 3+
+?(eval):1: unmatched "
+
+  purr ${:-"${ purr ${REPLY:-more buried}}}"
+1:unbalanced braces, part 4+
+?(eval):1: closing brace expected
+
+  purr ${ purr STDOUT }
+0f:capture stdout
+>STDOUT
+
+# end PS2 stack tests 
+
+  purr $(print outside ${| REPLY=inside })
+  purr BEGIN$(print outside ${| REPLY=inside })END
+  purr "BEGIN$(print outside ${| REPLY=inside })END"
+  purr outside ${| REPLY=$(print inside)}
+  purr "outside ${| REPLY=$(print inside)}"
+0:mixing with forking cmdsubst
+>outside inside
+>BEGINoutside insideEND
+>BEGINoutside insideEND
+>outside inside
+>outside inside
+
+  purr `purr outside ${| REPLY=inside }`
+  purr "outside `purr ${| REPLY=inside }`"
+  purr outside ${| REPLY=`purr inside`}
+  purr "outside ${| REPLY=`purr inside`}"
+  purr outside "`purr ${| REPLY="${:-inside}"}`"
+  purr "outside ${| REPLY=`purr ${:-inside}`}"
+0:mixing with backticks
+>outside inside
+>outside inside
+>outside inside
+>outside inside
+>outside inside
+>outside inside
+
+  purr ${| REPLY=$(( 9 + 17 )) }
+  purr $(( 9 + ${| REPLY=17 } ))
+0:mixing with arithemetic
+>26
+>26
+
+  unset reply
+  purl ${|reply| reply=(1 2 ${| REPLY=3 } 4) }
+  typeset -p reply
+0:array behavior with global assignment
+>1
+>2
+>3
+>4
+>typeset -g -a reply=( 1 2 3 4 )
+
+  unset outer
+  purr "${|
+   outer=OUTER
+   REPLY=INNER
+   return 7
+   OUTER=NOTREACHED
+  } $outer $?"
+0:return statement inside, part 1
+F:status of "print" should hide return
+>INNER OUTER 7
+
+  unset outer
+  purr "${|
+   outer=OUTER
+   REPLY=INNER
+   return 7
+   OUTER=NOTREACHED
+  } $outer $?"
+  print REACHED $OUTER
+0:return statement inside, part 2
+>INNER OUTER 7
+>REACHED
+
+  unset outer
+  purr "${|
+   # Localoptions needed to avoid breaking test harness?
+   # The setopt command affects surrounding context
+   setopt localoptions errreturn
+   outer=OUTER
+   REPLY=INNER
+   false
+   OUTER=NOTREACHED
+  } $outer $?"
+  print REACHED $OUTER ${options[errreturn]}
+0:errreturn works inside and remains outside
+>INNER OUTER 1
+>REACHED on
+
+ (
+  unset outer
+  purr "${|
+   outer=OUTER
+   REPLY=INNER
+   exit 7
+   OUTER=NOTREACHED
+  } $outer $OUTER $?"
+  print NOT REACHED
+ )
+7:exit statement inside
+
+ (
+  unset outer
+  purr "${|
+   setopt errexit
+   outer=OUTER
+   REPLY=INNER
+   false
+   OUTER=NOTREACHED
+  } $outer $?"
+  print NOT REACHED
+ )
+1:errexit inside
+
+  outer=GLOBAL
+  purr "${|
+   local outer=LOCAL
+   REPLY=INNER
+  } $outer $?"
+0:local declaration inside
+>INNER GLOBAL 0
+
+  unset zz
+  outer=GLOBAL
+  purr "${|zz|
+   local outer=LOCAL
+   zz=NONLOCAL
+  } $outer $?"
+  print $zz
+0:local declaration, global assignment, part 1
+>NONLOCAL GLOBAL 0
+>NONLOCAL
+
+  unset zz
+  outer=GLOBAL
+  purr "${${|
+   local outer=LOCAL
+   zz=NONLOCAL
+  }:-$zz} $outer $?"
+0:local declaration, global assignment, part 2 (evaluation order)
+>NONLOCAL GLOBAL 0
+
+  : ${| fn1() { () { print -v REPLY $'Defined Function' } } }
+  print "IN${| fn2() { () { print "${:-Second }${|fn1}" } } }OUT"
+  fn2
+0:function definition, brace nesting, quote nesting
+>INOUT
+>Second Defined Function
+
+  <<-EOF
+	${| REPLY=$'in a here document\n' }
+	EOF
+0:here-document behavior
+F:Fiddly here to get EOF past the test syntax
+>in a here document
+>
+
+  <<<${| REPLY="in a here string" }
+0:here-string behavior
+>in a here string
+
+  print -u $ZTST_fd ${ZTST_testname}: TEST COMPLETE
+0:make sure we got to the end
+F:some tests might silently break the test harness
+
+%clean
+
+  unfunction purr purl

                 reply	other threads:[~2023-07-25  6:26 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAH+w=7ZYKN4T6vUc8zd4SB4knfsbbcBaccGgm9wKqGEF707ZhQ@mail.gmail.com' \
    --to=schaefer@brasslantern.com \
    --cc=zsh-workers@zsh.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).