about summary refs log tree commit diff
path: root/Test/ztst.zsh
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-12-07 22:42:20 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-12-07 22:42:20 +0000
commitd4ce23998b0037a58fe33148dcbdb769c2da8c51 (patch)
tree5ed1876bc7415d3de85f9a211c5bf198c997f8d9 /Test/ztst.zsh
parentbd443fb06f226277b78ea2a327140825cf423351 (diff)
downloadzsh-d4ce23998b0037a58fe33148dcbdb769c2da8c51.tar.gz
zsh-d4ce23998b0037a58fe33148dcbdb769c2da8c51.tar.xz
zsh-d4ce23998b0037a58fe33148dcbdb769c2da8c51.zip
Initial revision
Diffstat (limited to 'Test/ztst.zsh')
-rwxr-xr-xTest/ztst.zsh316
1 files changed, 316 insertions, 0 deletions
diff --git a/Test/ztst.zsh b/Test/ztst.zsh
new file mode 100755
index 000000000..d3d03e883
--- /dev/null
+++ b/Test/ztst.zsh
@@ -0,0 +1,316 @@
+#!/usr/local/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.
+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
+
+# 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 -i 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
+
+# Temporary files for redirection inside tests.
+ZTST_in=${TMPPREFIX-:/tmp/zsh}.ztst.in.$$
+# hold the expected output
+ZTST_out=${TMPPREFIX-:/tmp/zsh}.ztst.out.$$
+ZTST_err=${TMPPREFIX-:/tmp/zsh}.ztst.err.$$
+# hold the actual output from the test
+ZTST_tout=${TMPPREFIX-:/tmp/zsh}.ztst.tout.$$
+ZTST_terr=${TMPPREFIX-:/tmp/zsh}.ztst.terr.$$
+
+ZTST_cleanup() {
+  rm -rf $ZTST_testdir/dummy.tmp $ZTST_testdir/*.tmp \
+         $ZTST_in $ZTST_out $ZTST_err $ZTST_tout $ZTST_terr
+}
+
+# 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 "Test $ZTST_testname failed: $1"
+  if [[ -n $ZTST_message ]]; then
+    print "Was testing: $ZTST_message"
+  fi
+  ZTST_cleanup
+  exit 1
+}
+
+# Print messages if $ZTST_verbose is non-empty
+ZTST_verbose() {
+  local lev=$1
+  shift
+  [[ -n $ZTST_verbose && $ZTST_verbose -ge $lev ]] && print $* >&8
+}
+
+[[ ! -r $ZTST_testname ]] && ZTST_testfailed "can't read test file."
+
+[[ -n $ZTST_verbose && $ZTST_verbose -ge 0 ]] && exec 8>&1
+exec 9<$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 ZTST_curline <&9 || 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.
+ZTST_getsect() {
+  local match mbegin mend
+
+  while [[ $ZTST_curline != '%'(#b)([[:alnum:]]##)* ]]; do
+    ZTST_getline || return 1
+    [[ $ZTST_curline = [[:blank:]]# ]] && continue
+    if [[ $ZTST_curline != '%'[[:alnum:]]##* ]]; then
+      ZTST_testfailed "bad line found before or after section:
+$ZTST_curline"
+    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]}
+  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"
+}
+
+# Execute an indented chunk.  Redirections will already have
+# been set up, but we need to handle the options.
+ZTST_execchunk() {
+  options=($ZTST_testopts)
+  eval "$ZTST_code"
+  ZTST_status=$?
+  ZTST_verbose 2 "ZTST_execchunk: status $ZTST_status"
+  ZTST_testopts=(${(kv)options})
+  options=($ZTST_mainopts)
+  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 ]] ||
+    ZTST_testfailed "non-zero status from preparation code:
+$ZTST_code"
+  done
+}
+
+ZTST_test() {
+  local last match mbegin mend found
+
+  while true; do
+    rm -f $ZTST_in $ZTST_out $ZTST_err
+    touch $ZTST_in $ZTST_out $ZTST_err
+    ZTST_message=''
+    found=0
+
+    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
+	  [[ $ZTST_curline != [-0-9]* ]] &&
+	  ZTST_testfailed "expecting test status at:
+$ZTST_curline"
+          ZTST_xstatus=$ZTST_curline
+	  if [[ $ZTST_curline == (#b)([^:]##):(*) ]]; then
+	    ZTST_xstatus=$match[1]
+	    ZTST_message=$match[2]
+	  fi
+	  ZTST_getline
+	  found=1
+	  ;;
+	'<'*) ZTST_getredir
+	  print -r "${(e)ZTST_redir}" >>$ZTST_in
+	  found=1
+	  ;;
+	'>'*) ZTST_getredir
+          print -r "${(e)ZTST_redir}" >>$ZTST_out
+	  found=1
+	  ;;
+	'?'*) ZTST_getredir
+	  print -r "${(e)ZTST_redir}" >>$ZTST_err
+	  found=1
+	  ;;
+	*) ZTST_testfailed "bad line in test block:
+$ZTST_curline"
+          ;;
+      esac
+    done
+
+    # If we found some code to execute...
+    if [[ -n $ZTST_code ]]; then
+      ZTST_verbose 1 "Running test:
+$ZTST_message"
+      ZTST_verbose 2 "ZTST_test: expecting status: $ZTST_xstatus"
+
+      ZTST_execchunk <$ZTST_in >$ZTST_tout 2>$ZTST_terr
+
+      # 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"
+      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 ! diff -c $ZTST_out $ZTST_tout; then
+	ZTST_testfailed "output differs from expected as shown above for:
+$ZTST_code"
+      fi
+      if ! diff -c $ZTST_err $ZTST_terr; then
+	ZTST_testfailed "error output differs from expected as shown above for:
+$ZTST_code"
+      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)
+
+# Now go through all the different sections until the end.
+while ZTST_getsect; do
+  case $ZTST_cursect in
+    prep) if (( ${ZTST_sects[prep]} + ${ZTST_sects[test]} + \
+	        ${ZTST_sects[clean]} )); then
+	    ZTST_testfailed "\`prep' section must come first"
+	  fi
+	  ZTST_prepclean
+	  ZTST_sects[prep]=1
+	  ;;
+    test)
+	  if (( ${ZTST_sects[test]} + ${ZTST_sects[clean]} )); then
+	    ZTST_testfailed "bad placement of \`test' section"
+	  fi
+	  ZTST_test
+	  ZTST_sects[test]=1
+	  ;;
+    clean)
+	   if (( ${ZTST_sects[test]} == 0 || ${ZTST_sects[clean]} )); then
+	     ZTST_testfailed "bad use of \`clean' section"
+	   fi
+	   ZTST_prepclean 1
+	   ZTST_sects[clean]=1
+	   ;;
+    *) ZTST_testfailed "bad section name: $ZTST_cursect"
+       ;;
+  esac
+done
+
+print "$ZTST_testname: all tests successful."
+ZTST_cleanup
+exit 0