summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Test/Makefile.in62
-rw-r--r--Test/cd.ztst97
-rwxr-xr-xTest/ztst.zsh316
3 files changed, 475 insertions, 0 deletions
diff --git a/Test/Makefile.in b/Test/Makefile.in
new file mode 100644
index 000000000..d7ce57b8a
--- /dev/null
+++ b/Test/Makefile.in
@@ -0,0 +1,62 @@
+#
+# Makefile for Test subdirectory
+#
+# Copyright (c) 1999 Peter Stephensons
+# All rights reserved.
+#
+# Permission is hereby granted, without written agreement and without
+# license or royalty fees, to use, copy, modify, and distribute this
+# software and to distribute modified versions of this software for any
+# purpose, provided that the above copyright notice and the following
+# two paragraphs appear in all copies of this software.
+#
+# In no event shall Peter Stephenson or the Zsh Development Group be liable
+# to any party for direct, indirect, special, incidental, or consequential
+# damages arising out of the use of this software and its documentation,
+# even if Peter Stephenson and the Zsh Development Group have been advised of
+# the possibility of such damage.
+#
+# Peter Stephenson and the Zsh Development Group specifically disclaim any
+# warranties, including, but not limited to, the implied warranties of
+# merchantability and fitness for a particular purpose. The software
+# provided hereunder is on an "as is" basis, and Peter Stephenson and the
+# Zsh Development Group have no obligation to provide maintenance,
+# support, updates, enhancements, or modifications.
+#
+
+subdir = Test
+dir_top = ..
+SUBDIRS =
+
+@VERSION_MK@
+
+# source/build directories
+VPATH = @srcdir@
+sdir = @srcdir@
+sdir_top = @top_srcdir@
+INSTALL = @INSTALL@
+
+@DEFS_MK@
+
+# ========== DEPENDENCIES FOR TESTING ==========
+
+tests:
+ for f in *.ztst; do \
+ ../Src/zsh ztst.zsh $$f; \
+ done
+
+# ========== DEPENDENCIES FOR CLEANUP ==========
+
+@CLEAN_MK@
+
+mostlyclean-here:
+ rm -rf *.tmp
+
+distclean-here:
+ rm -f Makefile
+
+realclean-here:
+
+# ========== DEPENDENCIES FOR MAINTENANCE ==========
+
+@CONFIG_MK@
diff --git a/Test/cd.ztst b/Test/cd.ztst
new file mode 100644
index 000000000..142628ae9
--- /dev/null
+++ b/Test/cd.ztst
@@ -0,0 +1,97 @@
+# This file serves as a model for how to write tests, so is more heavily
+# commented that the others. All tests are run in the Test subdirectory
+# of the distribution, which must be writable. They should end with
+# the suffix `.ztst': this is not required by the test harness itself,
+# but it is needed by the Makefile to run all the tests.
+
+# Blank lines with no other special meaning (e.g. separating chunks of
+# code) and all those with a `#' in the first column are ignored.
+
+# All section names start with a % in the first column. The names
+# must be in the expected order, though not all sections are required.
+# The sections are %prep (preparatory setup: code executed should return
+# status 0, but no other tests are performed), %test (the main tests), and
+# %clean (to cleanup: the code is simply unconditionally executed).
+#
+# Literal shell code to be evaluated must be indented with any number
+# of spaces and/or tabs, to differentiate it from tags with a special
+# meaning to the test harness. Note that this is true even in sections
+# where there are no such tags. Also note that file descriptor 9
+# is reserved for input from the test script; if ZTST_verbose is set,
+# output is sent to the original stdout via fd 8. Option settings
+# are preserved between the execution of different code chunks;
+# initially, all standard zsh options (the effect of `emulate -R zsh')
+# are set.
+
+%prep
+# This optional section prepares the test, creating directories and files
+# and so on. Chunks of code are separated by blank lines (which is not
+# necessary before the end of the section); each chunk of code is evaluated
+# in one go and must return status 0, or the preparation is deemed to have
+# failed and the test ends with an appropriate error message. Standard
+# output from this section is redirected to /dev/null, but standard error
+# is not redirected.
+#
+# Tests should use subdirectories ending in `.tmp'. These will be
+# removed with all the contents even if the test is aborted.
+ mkdir cdtst.tmp cdtst.tmp/real cdtst.tmp/sub
+
+ ln -s ../real cdtst.tmp/sub/fake
+
+ mydir=$PWD
+
+%test
+# This is where the tests are run. It consists of blocks separated
+# by blank lines. Each block has the same format and there may be any
+# number of them. It consists of indented code, plus optional sets of lines
+# beginning '<', '>' and '?' which may appear in any order. These correspond
+# to stdin (fed to the code), stdout (compared with code output) and
+# stderr (compared with code error output) respectively. These subblocks
+# may occur in any order, but the natural one is: code, stdin, stdout,
+# stderr.
+#
+# The rules for '<', '>' and '?' lines are the same: only the first
+# character is stripped, with subsequent whitespace being significant;
+# lines are subject to ordinary quoted shell expansion (i.e. not globbing).
+#
+# Each chunk of indented code is to be evaluated in one go and is to
+# be followed by a line starting (in the first column) with
+# the expected status returned by the code when run, or - if it is
+# irrelevant. This can be followed by a `:' and a message describing the
+# test, which will be printed if the test fails, along with a
+# description of the failure that occurred. The `:' and message are
+# optional, but highly recommended.
+#
+# If either or both of the '>' and '?' sets of lines is absent, it is
+# assumed the corresponding output should be empty and it is an error if it
+# is not. If '<' is empty, stdin is an empty (but opened) file.
+#
+# TODO: flags to the post-code status line indicating that diffs are
+# not to be performed.
+ cd cdtst.tmp/sub/fake &&
+ pwd &&
+ print $PWD
+0:Preserving symbolic links in the current directory string
+>$mydir/cdtst.tmp/sub/fake
+>$mydir/cdtst.tmp/sub/fake
+
+ cd ../../.. &&
+ pwd &&
+ print $PWD
+0:Changing directory up through symbolic links without following them
+>$mydir
+>$mydir
+
+ setopt chaselinks
+ cd cdtst.tmp/sub/fake &&
+ pwd &&
+ print $PWD
+0:Resolving symbolic links with chaselinks set
+>$mydir/cdtst.tmp/real
+>$mydir/cdtst.tmp/real
+
+%clean
+# This optional section cleans up after the test, if necessary,
+# e.g. killing processes etc. This is in addition to the removal of *.tmp
+# subdirectories. This is essentially like %prep, except that status
+# return values are ignored.
diff --git a/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