\errorcontextlines=\maxdimen
% \iffalse
%%
%% Copyright (C) 2006, 2007 QuinScape GmbH
%% http://www.quinscape.de
%
% This file may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either version 1.3
% of this license or (at your option) any later version.
% The latest version of this license is in
% http://www.latex-project.org/lppl.txt
% \fi
%
% \CheckSum{1258}
% \section{Using \texttt{qstest}}
%
% The basic idea of \texttt{qstest} is to let the user specify a
% number of tests that can be performed either at package load time or
% while running a separate test file through \LaTeX. If you are
% writing |.dtx| files, it is a good idea to use Docstrip `modules'
% for specifying which lines are to be used for testing. The file
% |qstest.dtx| from which both the style file as well as this
% documentation have been generated has been done in this manner.
%
% Since the following tests should be ignored when the |dtx| file is
% compiled itself, we use this for skipping over the tests:
% \begin{macrocode}
%<*dtx>
\iffalse
%
% \end{macrocode}
%
% A standalone test file actually does not need a preamble. We can
% load the packages with \cmd{\RequirePackage} and just go ahead. Let
% us demonstrate how to build such a test file by testing the |qstest|
% package itself:
% \begin{macrocode}
%<*test>
\RequirePackage{qstest}
% \end{macrocode}
%
% \subsection{Pattern and keyword lists}
%
% Check section \href{file:makematch#patterns}{``Match patterns and
% targets''} of the |makematch| package for an explanation of the
% comma-separated pattern and keyword lists. In a nutshell, both are
% lists of arbitrary material that is not expanded but rather used in
% sanitized (printable) form. Patterns may contain wildcard
% characters |*| matching zero or more characters, and may be preceded
% by |!| in order to negate a match from an earlier pattern in the
% pattern list. Leading spaces before an item in either list are
% discarded.
%
% \subsection{Specifying test sets}
%
% \DescribeMacro{\IncludeTests} This macro specifies a pattern list
% matched to tests' keyword lists in order to determine the tests to
% be included in this test run. The characters |*| and |!| are
% interpreted as explained above.
%
% For example,
% \begin{quote}
% |\IncludeTests{*, !\cs}|
% \end{quote}
% will run all tests except those that have a test keyword of |\cs| in
% their list of keywords. It is a good convention to specify the
% principal macro or environment to be tested as the first keyword.
%
% The default is to include all tests. If you are interspersing
% possibly expensive tests with your source file, you might want to
% specify something like
% \begin{quote}
% |\IncludeTests{*, !expensive}|
% \end{quote}
% or even
% \begin{quote}
% |\IncludeTests{}|
% \end{quote}
% in your document preamble, and then possibly override this on the
% command line with
% \begin{quote}
% |latex "\AtBeginDocument{\IncludeTests{*}}\input{file}"|
% \end{quote}
% or similar for getting a more complete test.
%
% \DescribeMacro{\TestErrors} This defines test patterns that will
% throw an error when failing. A test that throws an error will not
% also add a warning to the standard log file with extension |log|
% since that would be redundant.
%
% The default is |\TestErrors{*, !fails}|, have all tests that are not
% marked as broken throw an error when they fail.
%
% The throwing of errors does not depend on the logging settings (see
% below) for the default |log| file.
%
% \DescribeMacro{\LogTests} This macro receives three arguments. The
% first is the filename extension of a log file (the extension |log|
% is treated specially and uses package warning and info commands to
% log test failures and passes, respectively). The second is a
% keyword list that indicates which passed tests are to be logged.
% The third is a keyword list specifying which failed tests are to be
% logged. Let us open a file logging everything:
% \begin{macrocode}
\LogTests{lgout}{*}{*}
% \end{macrocode}
% The choice of |lgout| is made to make it possible to also have
% |lgin| for comparison purposes: the latter would be an |lgout| file
% from a previous, `definitive run', renamed and checked into version
% control, for the sake of being able to compare the log output from
% different versions.
%
% An already open log file stays open and just changes what is logged.
% By default, the standard |log|
% \mbox{(pseudo-)}\discretionary{}{}{}file is already open and logs
% everything.
%
% Passed and failed tests are not completely symmetric with regard to
% logging: test failures are logged and/or indicated on the individual
% failed assertions, while a successful test is only logged and/or
% indicated in summary.
%
% \DescribeMacro{\LogClose} You can explicitly close a log file if you
% want to reread it in the course of processing, or call an executable
% that would process it. The standard file with extension |log| will
% not actually get closed and flushed if you do this (though logging
% would stop on it), but all others might. The actual example for
% this follows after the tests. You can reopen a closed log file
% using \cmd{\LogTests}. It will then get rewritten from the
% beginning (with the exception for the standard |log| file, of
% course).
%
% \subsection{The tests}
%
% \DescribeEnv{qstest}
% Tests are performed within the |qstest| environment. The
% environment gets two arguments. The first is the name of the test
% recorded in the log file. The second is a list of test keywords
% used for deciding which tests are performed and logged.
%
% Before delving in the details of what kind of tests you can perform
% in this environment, we list the various commands that are given
% patterns and thus control what kind of tests are performed and
% logged.
%
% \DescribeMacro{\Expect}
% This is the workhorse for checking that values, dimensions, macros
% and other things are just like the test designer would expect them
% to be.
%
% This macro basically receives two brace-delimited
% arguments\footnote{The arguments are collected with a token register
% assignment. This gives several options for specifying them,
% including giving a token register without braces around it. It
% also makes it possible to precede the \textsl{balanced text} with
% \cmd{\expandafter} and similar expandable stuff.} and checks that
% they are equal after being passed through \cmd{\def} and sanitized.
% This means that you can't normally use |#| except when followed by a
% digit (all from 1 to 9 are allowed) or |#|. If you precede one of
% those arguments with |*| it gets passed through through \cmd{\edef}
% instead of \cmd{\def}. There may also be additional tokens like
% |\expandafter| before the opening brace. Note that the combination
% of \cmd{\edef} and |\the|\meta{token variable} can be used to pass
% through |#| characters without interpretation. e\TeX\ provides a
% similar effect with \cmd{\unexpanded}. So if you want to compare a
% token list that may contain isolated hash characters, you can do so
% by writing something like
% \begin{macrocode}
%<*etex>
\begin{qstest}{# in isolation}{\Expect, #, \unexpanded}
\toks0{# and #}
\Expect*{\the\toks0}*{\unexpanded{# and #}}
\end{qstest}
%
% \end{macrocode}
% Since the sanitized version will convert |#| macro parameters to the
% string |##|, you might also do this explicitly (and without e\TeX)
% as
% \begin{macrocode}
\begin{qstest}{# in isolation 2}{\Expect, #, \string}
\toks0{# and #}
\Expect*{\the\toks0}*{\string#\string# and \string#\string#}
\end{qstest}
% \end{macrocode}
% If the token register is guaranteed to only contain `proper' |#|
% characters that are either followed by another |#| or a digit, you
% can let the normal interpretation of a macro parameter for
% \cmd{\def} kick in and use this as
% \begin{macrocode}
\begin{qstest}{# as macro parameter}{\Expect, #}
\toks0{\def\xxx#1{}}
\Expect\expandafter{\the\toks0}{\def\xxx#1{}}
\end{qstest}
% \end{macrocode}
% In this manner, |#1| is interpreted (and sanitized) as a macro
% parameter on both sides, and consequently no doubling of |#|
% occurs.
%
% Before the comparison is done, both arguments are sanitized,
% converted into printing characters with standardized catcodes
% throughout\footnote{Spaces get catcode~10, all other characters
% catcode~12.}. A word of warning: both sanitization as well as
% using \cmd{\meaning} still depend on catcode settings, since
% single-letter control sequences (made from a catcode 11 letter)
% are followed by a space, and other single-character control
% sequences are not. For that reason, a standalone test file for
% \LaTeX\ class or package files will usually need to declare
% \begin{macrocode}
\makeatletter
% \end{macrocode}
% in order to make `|@|' a letter, like it usually is in such
% files.
%
% All of the of the following expectations would turn out correct:
% \begin{macrocode}
\begin{qstest}{Some LaTeX definitions}{\Expect}
\Expect*{\meaning\@gobble}{\long macro:#1->}
\Expect*{\the\maxdimen}{16383.99998pt}
\end{qstest}
% \end{macrocode}
% Note that there is no way to convert the contents of a box into a
% printable rendition, so with regard to boxes, you will be mostly
% reduced to check that dimensions of a box meet expectations.
%
% \subsection{Expecting \texttt{ifthen} conditions}
% \DescribeMacro{\ExpectIfThen}
% This is used for evaluating a condition as provided by the
% |ifthen| package. See its docs for the kind of condition that is
% possible there. You just specify one argument: the condition that
% you expect to be true. Here is an example:
% \begin{macrocode}
\RequirePackage{ifthen}
\begin{qstest}{\ExpectIfThen}{\ExpectIfThen}
\ExpectIfThen{\lengthtest{\maxdimen=16383.99998pt}\AND
\maxdimen>1000000000}
\end{qstest}
% \end{macrocode}
%
% \subsection{Dimension ranges}
%
% \DescribeMacro{\InRange} Sometimes we want to check whether some
% dimension is not exactly like some value, but rather in some range.
% We do this by specifying as the second argument to \cmd{\Expect} an
% artificial macro with two arguments specifying the range in
% question. This will make \cmd{\Expect} succeed if its first
% argument is in the range specified by the two arguments to
% \cmd{\InRange}.
%
% The range is specified as two \TeX\ dimens. If you use a dimen
% register and you want to have a possible error message display the
% value instead of the dimen register, you can do so by using the |*|
% modifier before \cmd{\InRange} (which will cause the value to be
% expanded before printing and comparing) and put \cmd{\the} before
% the dimen register since such register are not expandible by
% themselves.
%
% Here are some examples:
% \begin{macrocode}
\begin{qstest}{\InRange success}{\InRange}
\dimen@=10pt
\Expect*{\the\dimen@}\InRange{5pt}{15pt}
\Expect*{\the\dimen@}\InRange{10pt}{15pt}
\Expect*{\the\dimen@}\InRange{5pt}{10pt}
\end{qstest}
\begin{qstest}{\InRange failure}{\InRange, fails}
\dimen@=10pt \dimen@ii=9.99998pt
\Expect*{\the\dimen@}\InRange{5pt}{\dimen@ii}
\dimen@ii=10.00002pt
\Expect*{\the\dimen@}*\InRange{\the\dimen@ii}{15pt}
\end{qstest}
% \end{macrocode}
% \DescribeMacro{\NearTo} This requires e\TeX's arithmetic and will
% not be available for versions built without e\TeX\ support. The
% macro is used in lieu of an expected value and is similar to
% \cmd{InRange} in that it is a pseudovalue to be used for the second
% argument of \cmd{\Expect}. It makes \cmd{\Expect} succeed if its
% own mandatory argument is close to the first argument from
% \cmd{\Expect}, where closeness is defined as being within |0.05pt|.
% This size can be varied by specifying a different one as optional
% argument to \cmd{\NearTo}. Here is a test:
% \begin{macrocode}
%<*etex>
\begin{qstest}{\NearTo success}{\NearTo}
\dimen@=10pt
\Expect*{\the\dimen@}\NearTo{10.05pt}
\Expect*{\the\dimen@}\NearTo{9.95pt}
\Expect*{\the\dimen@}\NearTo[2pt]{12pt}
\Expect*{\the\dimen@}\NearTo[0.1pt]{9.9pt}
\end{qstest}
\begin{qstest}{\NearTo failure}{\NearTo, fails}
\dimen@=10pt
\Expect*{\the\dimen@}\NearTo{10.05002pt}
\Expect*{\the\dimen@}\NearTo[1pt]{11.00001pt}
\end{qstest}
%
% \end{macrocode}
%
% \subsection{Saved results}
%
% The purpose of saved results is to be able to check that the value
% has remained the same over passes. Results are given a unique label
% name and are written to an auxiliary file where they can be read in
% for the sake of comparison. One can use the normal |aux| file for
% this purpose, but it might be preferable to use a separate dedicated
% file. That way it is possible to input a versioned \emph{copy} of
% this file and have a fixed point of reference rather than the last
% run.
%
% While the |aux| file is read in automatically at the beginning of
% the document, this does not happen with explicitly named files. You
% have to read them in yourself, preferably using
% \begin{quote}
% |\InputIfFileExists|\marg{filename}|{}{}|
% \end{quote}
% so that no error is thrown when the file does not yet exist.
%
% \DescribeMacro{\SaveValueFile} This gets one argument specifying
% which file name to use for saving results. If this is specified, a
% special file is opened. If \cmd{\SaveValueFile} is not called, the
% standard |aux| file is used instead, but then you can only save
% values after |\begin||{document}|. |\jobname.qsout| seems like a
% useful file name to use here (the extension |out| is already in
% use by PDF\TeX).
% \begin{macrocode}
\begin{qstest}{\SavedValue}{\SavedValue}
\SaveValueFile{\jobname.qsout}
% \end{macrocode}
% If this were a real test instead of just documentation, we probably
% would have written something like
% \begin{quote}
% |\InputIfFileExists{\jobname.qsin}{}{}|
% \end{quote}
% first in order to read in values from a previous run. The given
% file would have been a copy of a previous |qsout| file, possibly
% checked into version control in order to make sure behavior is
% consistent across runs. If it is an error to not have such a file
% (once you have established appropriate testing), you can just write
% \begin{quote}
% |\input{\jobname.qsin}|
% \end{quote}
% instead, of course.
%
% \DescribeMacro{\CloseValueFile} This macro takes no argument and
% will close a value save file if one is open (using this has no
% effect if no file has been opened and values are saved on the |aux|
% file instead). We'll demonstrate use of it later. It is probably
% only necessary for testing |qstest| itself (namely, when you read in
% saved values in the same run), or when you do the
% processing/comparison with a previous version by executing commands
% via \TeX's |\write18| mechanism.
%
% \DescribeMacro{\SaveValue} This gets the label name as first
% argument. If you are using the non-e\TeX-version, the label name
% gets sanitized using \cmd{\string} and so can't deal with
% non-character material except at its immediate beginning. The
% e\TeX-version has no such constraint.
%
% The second argument is the same kind of argument as \cmd{\Expect}
% expects, namely something suitable for a token register assignment
% which is passed through \cmd{\def} if not preceded by |*|, and by
% \cmd{\edef} if preceded by |*|. The value is written out to the
% save file where it can be read in afterwards.
%
% Let us save a few values under different names now:
% \begin{macrocode}
\SaveValue{\InternalSetValue}*{\meaning\InternalSetValue}
\SaveValue{\IncludeTests}*{\meaning\IncludeTests}
\SaveValue{whatever}*{3.1415}
\SaveValue{\maxdimen}*{\the\maxdimen}
% \end{macrocode}
% \DescribeMacro{\InternalSetValue} A call to this macro is placed
% into the save file for each call of \cmd{\SaveValue}. The details
% are not really relevant: in case you run into problems while
% inputting the save file, it might be nice to know.
%
% \DescribeMacro{\SavedValue} This is used for retrieving a saved
% value. When used as the second argument to \cmd{\Expect}, it will
% default to the value of the first argument to \cmd{\Expect} unless
% it has been read in from a save file. This behavior is intended for
% making it easy to add tests and saved values and not get errors at
% first, until actually values from a previous test become available.
%
% Consequently, the following tests will all turn out true before we
% read in a file, simply because all the saved values are not yet
% defined and default to the expectations:
% \begin{macrocode}
\Expect{Whatever}\SavedValue{\InternalSetValue}
\Expect[\IncludeTests]{Whatever else}\SavedValue{\IncludeTests}
\Expect[whatever]{2.71828}\SavedValue{whatever}
\Expect[undefined]{1.618034}\SavedValue{undefined}
% \end{macrocode}
% After closing and rereading the file, we'll need to be more careful
% with our expectations, but the last line still works since there
% still is no saved value for ``undefined''.
% \begin{macrocode}
\CloseValueFile
\input{\jobname.qsout}
\Expect*{\meaning\InternalSetValue}\SavedValue{\InternalSetValue}
\Expect[\IncludeTests]*{\meaning\IncludeTests}%
\SavedValue{\IncludeTests}
\Expect[whatever]{3.1415}\SavedValue{whatever}
\Expect[undefined]{1.618034}\SavedValue{undefined}
\end{qstest}
% \end{macrocode}
% Ok, and now lets take the previous tests which succeeded again and
% let them fail now that the variables are defined:
% \begin{macrocode}
\begin{qstest}{\SavedValue failure}{\SavedValue,fails}
\Expect{Whatever}\SavedValue{\InternalSetValue}
\Expect[\IncludeTests]{Whatever else}\SavedValue{\IncludeTests}
\Expect{2.71828}\SavedValue{whatever}
\end{qstest}
% \end{macrocode}
%
%
% \subsection{Checking for call sequences}
% \DescribeEnv{ExpectCallSequence} The environment
% |ExpectCallSequence| tells the test system that macros are going to
% be called in a certain order and with particular types of arguments.
%
% It gets as an argument the expected call sequence. The call
% sequence contains entries that look like a macro definition:
% starting with the macro name followed with a macro argument list and
% a brace-enclosed substitution text that gets executed in place of
% the macro. The argument list given to this macro substitution will
% get as its first argument a macro with the original definition of
% the control sequence, so you can get at the original arguments for
% this particular macro call starting with |#2|. As a consequence, if
% you specify no arguments at all and an empty replacement text for
% the substitution, the original macro gets executed with the original
% arguments.
%
% \DescribeMacro{\CalledName} If you want to get back from the control
% sequence with the original meaning in |#1| to the original macro
% name, you can use \cmd{\CalledName} on it. This will expand to the
% original control sequence \emph{name}, all in printable characters
% fit for output or typesetting in a typewriter font (or use in
% \cmd{\csname}), but without leading backslash character. You can
% get to the control sequence itself by using
% \begin{quote}
% |\csname \CalledName#1\endcsname|
% \end{quote}
% and to a printable version including backslash by using
% \begin{quote}
% |\expandafter \string \csname \CalledName#1\endcsname|
% \end{quote}
%
% Going into more detail, a substitution function is basically defined
% using
% \begin{quote}
% |\protected| |\long| |\def|
% \end{quote}
% so it will not usually get expanded except when hit with
% \cmd{\expandafter} or actually being executed. Note that you can't
% use this on stuff that has to work at expansion time. This really
% works mainly with macros that would also be suitable candidates for
% \cmd{\DeclareRobustCommand}.
%
% It is also a bad idea to trace a conditional in this manner: while
% the substitution could be made to work when being executed, it will
% appear like an ordinary macro when being skipped, disturbing the
% conditional nesting.
%
% Only macros occuring somewhere in the call sequence will get
% tracked, other macros are not affected. The environment can
% actually get nested, in which case the outer sequences will get
% tracked independently from the inner sequence.
%
% This makes it possible to use |ExpectCallSequence| in order to
% spoof, for example, both input consuming and output producing macros
% without knowing the exact relationship of both.
%
% Apart from specifying macro calls, the call sequence specification
% can contain special characters that also carry meaning:
% \begin{description}
% \item[\texttt{\string`}] If this is set in the call sequence, it
% places the initial parsing state here. This will make it an error
% if non-matching entries occur at the start of the sequence, which
% otherwise is effectively enclosed with
% \begin{quote}
% |.{}*(|\meta{sequence}|).{}*|
% \end{quote}
% meaning that nonmatching entries before the first and after the
% last matching item of the sequence are ignored by default (this
% makes it closer to normal regexp matchers). Since the matching
% will then start at |`|, you can put macros before that position
% that you want to be flagged if they occur in the sequence, even
% when they are mentioned nowhere else (macros which would be an
% error if actually called). Also available as the more customary
% |^| character, but that tends to behave worse in \LaTeX-aware
% editors.
% \item[\texttt{\string'}] This indicates the last call sequence
% element to be matched. If any traced macros appear after this
% point, an error will get generated. Any immediately following
% call sequence entries will not get reached.
% \item[|.|] A single dot indicates a wildcard: any of the tracked
% control sequences might occur here. You still have to follow this
% with macro arguments and a braced replacement text. Wildcards are
% considered as a fallback when nothing else matches.
% \item[|(|\dots|)|] Parens may be used for grouping alternatives
% and\slash or combining items for the sake of repeating
% specifications, of which there are three:
% \item[|?|] If a question mark follows either a macro call, wildcard
% call, parenthesized group, or call sequence end, the item before
% it is optional.
% \item[|+|] A plus sign following an item means that this item may be
% repeated one or more times.
% \item[|*|] An asterisk following an item means that this item may be
% repeated zero or more times.
% \item[\texttt{\string|}] A vertical bar separates alternatives.
% Alternatives extend as far as possible, to the next bar, to an
% enclosing paren group, or to the start and\slash or end of the
% whole call sequence specification if nothing else intervenes.
% \end{description}
% Note that opposed to traditional regexp evaluation, no backtracking
% is employed: at each point in the call sequence, the next match is
% immediately chosen and a choice can (for obvious reasons) not be
% reverted. It is the task of the user to specify a call sequence in
% a sufficiently non-ambiguous manner that will make the call sequence
% tracing not pick dead ends.
% \begin{macrocode}
\begin{qstest}{ExpectCallSequence}{ExpectCallSequence}
\def\e{e} \def\f{f}
\def\g{g} \def\h{h}
\begin{ExpectCallSequence}{`\e#1{%
\Expect\expandafter{\csname\CalledName#1\endcsname}{\e }%
\Expect*{\meaning#1}{macro:->e}}+\f#1{}'}
\e \e \e \e \f
\end{ExpectCallSequence}
\end{qstest}
% \end{macrocode}
%
% \subsection{Ending a standalone test file}
% One finishes a standalone test file by calling the \LaTeX\ control
% sequence \cmd{\quit}. This stops processing even when we did not
% actually get into a document. We don't actually do this here since
% there will be further tests in the full documentation file.
% However, we will now close the log file we opened for this demonstration.
% \begin{macrocode}
\LogClose{lgout}
%
% \end{macrocode}
% \IfFileExists{qstest-qs.lgout}{%
% And now we will show the resulting standalone log file:
% \IndexInput{qstest-qs.lgout}}{%
% \GenericError{(\jobname) }%
% {`qstest-qs.lgout' is missing}
% {Please run `qstest-qs.tex' (unpacked by `qstest.ins') through LaTeX first.}
% {Documentation will be incomplete otherwise.}}
%
% We can now stop skipping if we are compiling the |dtx| file
% standalone.
% \begin{macrocode}
%<*dtx>
\fi
%
% \end{macrocode}
% \StopEventually{}
% \section{The driver file}
% \begin{macrocode}
%<*driver>
\documentclass{ltxdoc}
\usepackage{qstest}
\usepackage{hyperref}
\OnlyDescription
% \AlsoImplementation
\begin{document}
\GetFileInfo{qstest.sty}
\date{\filedate}
\title{\texttt{\filename}\\\fileinfo\\version \fileversion}
\author{David Kastrup\thanks{\href{mailto:dak@gnu.org}
{David.Kastrup@QuinScape.de}, \href{http://quinscape.de}{QuinScape
GmbH}}}
\maketitle
\DocInput{qstest.dtx}
\end{document}
%
% \end{macrocode}
% \hypertarget{installer}{%
% \section{The installer file}
% }
% If you did not get an installer file with the package, you can
% recreate it by running
% \begin{quote}
% |tex docstrip|
% \end{quote}
% and specifying |dtx| as input extension, |ins| as output extension,
% |installer| as option list and |qstest| as input file name.
%
% Installations with a non-e\TeX-based \LaTeX\ can try to remove the
% |etex| option from the style, but it will impact robustness and
% performance of the package.
%
% When called from a Makefile, you want to allow overwriting files,
% and you don't want to install into the final target tree. In this
% case, call
% \begin{quote}
% |tex "\let\Make=y \input makematch.ins"|
% \end{quote}
% \begin{macrocode}
%<*installer>
\input docstrip
\ifx\Make y\askforoverwritefalse\fi
\generate{
\file{qstest.drv}{\from{qstest.dtx}{driver,test,etex}}
\file{qstest-qs.tex}{\from{qstest.dtx}{test,etex}}
\file{makematch.drv}{\from{makematch.dtx}{driver,etex}}
\file{makematch-qs.tex}{\from{makematch.dtx}{test,etex}}
\ifx\Make y\else \usedir{tex/latex/qstest}\fi
\file{qstest.sty}{\from{qstest.dtx}{package,etex}}
\file{makematch.sty}{\from{makematch.dtx}{package,etex}}}
\endbatchfile
%
% \end{macrocode}
%
% \section{The Implementation}
% \subsection{Front matter}
% Some data and version magic from SVN keyword expansion.
% \begin{macrocode}
%<*package>
\NeedsTeXFormat{LaTeX2e}
\def\next$Id: #1.dtx #2 #3-#4-#5 #6${%
\ProvidesPackage{#1}[#3/#4/#5 1.#2 QuinScape Unit Test Package]
}
\next
$Id: qstest.dtx 7896 2007-02-21 14:03:26Z dkastrup $
\RequirePackage{makematch}
\newtoks\qst@tmptoks
% \end{macrocode}
%
% \subsection{Error logging and treatment}
% \begin{macro}{\TestErrors}
% This defines test patterns that will throw an error when failing.
% \begin{macrocode}
\newcommand*{\TestErrors}{\MakeMatcher[,]\qst@errorcheck}
\TestErrors{*, !fails}
\newif\ifqst@error
\def\qst@ifqst@error{\qst@errorcheck\qst@options
\qst@errortrue\qst@errorfalse\ifqst@error}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@failed}
% This is the internal macro we call for every failure. It gets the
% test message which is expanded and then sanitized.
% \begin{macrocode}
\long\def\qst@error#1{%
\global\qst@testpassedfalse
\global\let\qst@lastfailure\qst@testname
\edef\qst@message{Failed: \qst@testname^^J#1}%
\@onelevel@sanitize\qst@message
\qst@failed}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\ifqst@testpassed}
% This is used to flag failures in a test.
% \begin{macrocode}
\newif\ifqst@testpassed
% \end{macrocode}
% \end{macro}
%
% \subsection{Logging}
% \label{sec:logging}
% \begin{macro}{\LogTests}
% This macro receives three arguments. The first is the filename
% extension of a log file. The second is a keyword list that
% indicates which passed tests are to be logged. The third is a
% keyword list indicating which failed tests are to be logged.
% \begin{macrocode}
\newcommand{\LogTests}[3]{%
% \ifcsname qst:log:#1\endcsname \else
%<*!etex>
{\expandafter}\expandafter\ifx\csname qst:log:#1\endcsname
\@undefined
%!etex>
\expandafter\newwrite\csname qst:log:#1\endcsname
\qst@gpush\qst@passed{\qst@logentry+{#1}}%
\qst@gpush\qst@failed{\qst@logentry-{#1}}%
\global\expandafter\let\csname qst:+log:#1\endcsname\qst@matchnever
\global\expandafter\let\csname qst:-log:#1\endcsname\qst@matchnever
\fi
\expandafter\ifx \csname qst:+log:#1\endcsname \qst@matchnever
\PackageInfo{qstest}{Logging on: \jobname.#1}%
\ifnum\csname qst:log:#1\endcsname>\m@ne
\immediate\openout\csname qst:log:#1\endcsname\jobname.#1\relax
\fi
\fi
\MakeMatcher[,]\qst@tmp{#2}%
\global\expandafter\let\csname qst:+log:#1\endcsname\qst@tmp
\MakeMatcher[,]\qst@tmp{#3}%
\global\expandafter\let\csname qst:-log:#1\endcsname\qst@tmp
}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@gpush}
% \begin{macrocode}
%<*etex>
\long\def\qst@gpush#1#2{\xdef#1{\unexpanded{#2}%
\unexpanded\expandafter{#1}}}
%
%<*!etex>
\long\def\qst@gpush#1#2{\qst@tmptoks{#2}%
\qst@tmptoks\expandafter{\the\expandafter\qst@tmptoks#1}%
\xdef#1{\the\qst@tmptoks}}
%!etex>
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\qst@logentry}
% \begin{macrocode}
\def\qst@logentry#1#2{\qst@maybelog#1{#2}{%
\immediate\write\csname qst:log:#2\endcsname{\qst@message}}}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@maybelog}
% \begin{macrocode}
\def\qst@maybelog#1#2#3{\csname qst:#1log:#2\endcsname\qst@options
{#3}{}}
% \end{macrocode}
% \end{macro}
% If the user specifies |log| as the extension, we don't want to
% overwrite the log file. Instead we convert this to package warnings
% and infos.
% \begin{macrocode}
\expandafter\let\csname qst:log:log\endcsname\m@ne
\global\expandafter\let\csname qst:+log:log\endcsname\qst@matchalways
\global\expandafter\let\csname qst:-log:log\endcsname\qst@matchalways
\def\qst@passed{\qst@maybelog+{log}%
{\PackageInfo{qstest}{\qst@message}}}
\def\qst@failed{\ifqst@error
\PackageError{qstest}{\qst@message}{}%
\else
\qst@maybelog-{log}{\PackageWarning{qstest}{\qst@message}}%
\fi}
% \end{macrocode}
% \begin{macro}{\LogClose}
% This closes the specified log file again. This is needed if the
% log file gets processed during the \TeX\ run, like with verbatim
% input or by running a |\write18| process on it. It is possible to
% reopen the file later. Specifying |log| will not actually close
% the log file, but just stop logging.
% \begin{macrocode}
\newcommand*\LogClose[1]{%
\expandafter\ifx \csname qst:+log:#1\endcsname\qst@matchnever
\else
\PackageInfo{qstest}{Logging off: \jobname.#1}%
\ifnum\csname qst:log:#1\endcsname>\m@ne
\immediate\closeout\csname qst:log:#1\endcsname
\fi
\global\expandafter\let\csname qst:+log:#1\endcsname\qst@matchnever
\global\expandafter\let\csname qst:-log:#1\endcsname\qst@matchnever
\fi
}
%
%<*test>
\IncludeTests{*}
\makeatletter
\begin{qstest}{\LogClose}{}
\begin{ExpectCallSequence}{%
`\PackageInfo#1#2#3{\Expect[##1]{#2}{qstest}%
\Expect[##2]{#3}{Logging off: \jobname.log}%
#1{#2}{#3}}'}
\LogClose{log}
\end{ExpectCallSequence}
\end{qstest}
\begin{qstest}{\LogTests}{}
\begin{ExpectCallSequence}{%
\PackageInfo#1#2#3{\Expect[##1]{#2}{qstest}%
\Expect[##2]{#3}{Logging on: \jobname.log}%
#1{#2}{#3}}}
\LogTests{log}{*}{*}
\end{ExpectCallSequence}
\end{qstest}
%
%<*package>
% \end{macrocode}
% \end{macro}
%
% \subsection{The test code itself}
%
% \begin{macro}{\IncludeTests}
% This defines the test case patterns to be included. Keyword
% specifications and patterns are `sanitized': macros are not
% expanded, and everything but spaces is converted to catcode~12
% characters. At the start of a pattern or a keyword, spaces get
% ignored. Spaces inside of patterns or keywords should work as
% expected, however.
% \begin{macrocode}
\def\IncludeTests{\MakeMatcher[,]\qst@includecheck}
\let\qst@includecheck\qst@matchalways
% \end{macrocode}
% \end{macro}
% \begin{environment}{qstest}
% This is the environment for defining a test. |#1| is the name of
% the test for the log file. The name is sanitized, so control
% sequence names can be used verbatim. |#2| is a sequence of
% options that may be matched by the patterns from
% \cmd{\IncludeTests}. If there is no match the environment is
% skipped like a comment environment.
% \begin{macrocode}
\RequirePackage{verbatim}
\newenvironment{qstest}[2]{%
% \end{macrocode}
% We sanitize test name and options. In case people nest tests, we
% don't want to announce an outer test passed when a single inner test
% has passed. So we don't set \cmd{\qst@testpassedtrue} globally.
% While a mixture of local and global assignments should in general be
% avoided as it leads to ``savestack buildup'', this can't actually
% happen here since the local assignment is done only once at the
% start of a group.
% \begin{macrocode}
\qst@testpassedtrue
% \edef\qst@testname{\detokenize{#1}}%
%<*!etex>
\qst@tmptoks{#1}%
\edef\qst@testname{\the\qst@tmptoks}%
\@onelevel@sanitize\qst@testname
%!etex>
\MakeMatchTarget[,]\qst@options{#2}%
\let\ifqst@error\qst@ifqst@error
\qst@includecheck\qst@options\ignorespaces{%
\let\endqstest\endcomment}}%
{\ifqst@testpassed
\edef\qst@message{Passed: \qst@testname}%
\qst@passed
\else
\ifx\qst@testname\qst@lastfailure
\else
\edef\qst@message{Failed: \qst@testname}%
\qst@failed
\fi
\fi}
% \end{macrocode}
% \end{environment}
% \begin{macro}{\qst@testname}
% Let us set up a default test name in case any tests occur outside
% of a |qstest| environment.
% \begin{macrocode}
\def\qst@testname{unspecified}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\Expect}
% This macro basically receives two brace-delimited
% arguments\footnote{Actually, the arguments are collected with a
% token register assignment. This gives several options for
% specifying them, including giving a token register without
% braces around it.} and checks that they are equal after being
% passed through \cmd{\def} and sanitized.
% If you precede one of those arguments
% with |*| it gets passed through through \cmd{\edef} instead of
% \cmd{\def}. There may also be additional tokens like
% |\expandafter| before the opening brace.
% \begin{macrocode}
\newcommand\Expect[1][]{%
% \edef\qst@tmpc{\unexpanded{#1}}%
% \qst@tmptoks{#1}%
% \edef\qst@tmpc{\the\qst@tmptoks}%
\qst@expectarg\qst@defaultvalue\qst@expecttwo}
\def\qst@expectarg#1#2{%
\@ifstar{\qst@expectargii\edef#1#2}{\qst@expectargii\def#1#2}}
\def\qst@expectargii#1#2#3{\long\def\qst@tmp##1{%
#1#2####1####2####3####4####5####6####7####8####9{##1}%
\@onelevel@sanitize#2#3}%
\afterassignment\qst@expectargiii
\qst@tmptoks=}
\def\qst@expectargiii{\expandafter
\qst@tmp\expandafter{\the\qst@tmptoks}}
\def\qst@expecttwo{%
\ifx\qst@tmpc\@empty
\edef\qst@tmpc{\the\qst@tmptoks}%
\fi
\qst@expectarg\qst@tmpb\qst@expectfinish}
\def\qst@expectfinish{%
\ifx\qst@defaultvalue\qst@tmpb
\else
% \qst@tmptoks\expandafter{\qst@tmpc}%
\qst@error{
% \string\Expect: \the\qst@tmptoks^^J
% \string\Expect: \unexpanded\expandafter{\qst@tmpc}^^J
<\qst@tmpb^^J
>\qst@defaultvalue}%
\fi}
% \end{macrocode}
% \end{macro}
%
% \subsection{Dimension ranges}
%
% \begin{macro}{\InRange}
% This is used as a second argument for \cmd{\Expect}. It will
% make \cmd{\Expect} succeed if the first argument of \cmd{\Expect}
% happens to lie in the closed range defined by the two arguments of
% \cmd{\InRange}.
% \begin{macrocode}
\def\InRange#1#2{%
\ifcase
\ifdim\qst@defaultvalue<#1\@ne\fi
\ifdim\qst@defaultvalue>#2\@ne\fi
\tw@
\or
\qst@afterfi{{in [#1..#2]}}%
\or
\qst@afterfi{\expandafter{\qst@defaultvalue}}%
\fi}
% \end{macrocode}
% \begin{macro}{\qst@afterfi}
% This is just a helper macro cleaning up a following |\fi|.
% \begin{macrocode}
\def\qst@afterfi#1#2\fi{\fi#1}
% \end{macrocode}
% \end{macro}
% \end{macro}
% \begin{macro}{\NearTo}
% This is a pseudovalue like the last: it returns
% \cmd{\qst@defaultvalue} if the specified value |#1| is close to
% the given default value. There is an optional argument specifying
% the closeness which defaults to |0.05pt|. It requires e\TeX\
% since the arithmetic can't be done otherwise.
% \begin{macrocode}
%<*etex>
\newcommand*\NearTo[1]{%
\ifx[#1\expandafter\qst@nearto\expandafter[%]]
\else \qst@afterfi{\qst@nearto[0.05pt]{#1}}%
\fi}
\def\qst@nearto[#1]#2{%
\ifcase
\ifdim\qst@defaultvalue>\dimexpr#2+(#1)\relax \@ne\fi
\ifdim\qst@defaultvalue<\dimexpr#2-(#1)\relax \@ne\fi
\tw@
\or
\qst@afterfi{{near[#1] to #2}}%
\or
\qst@afterfi{\expandafter{\qst@defaultvalue}}%
\fi}
%
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\ExpectIfThen}
% This is used for evaluating a condition as provided by the
% |ifthen| package. See its docs for the kind of condition that is
% possible there.
% \begin{macrocode}
\newcommand\ExpectIfThen[1]{%
% \qst@tmptoks{#1}%
\ifthenelse{#1}{}{%
\qst@error{
% \string\ExpectIfThen: \unexpanded{#1}%
% \string\ExpectIfThen: \the\qst@tmptoks
}}}
% \end{macrocode}
% \end{macro}
%
%
% \begin{environment}{ExpectCallSequence}
% This tells the test system that macros are going to be called in a
% certain order and with particular types of arguments.
%
% The environment gets as an argument the expected call sequence.
% The call sequence contains entries that look like a macro
% definition: starting with the macro name followed with an argument
% list ended with |{| ^^A}%
% following it, followed by substitution code executed in place of
% the macro. The argument list actually starts with an implicit
% argument of |#1| which contains a macro with the original value of
% the control sequence, so the explicitly specified argument list
% starts with the argument called~|#2|.
%
% The substitution function is basically defined using
% \begin{quote}
% |\protected| |\long| |\def|
% \end{quote}
% so it will not usually get expanded except when hit with
% \cmd{\expandafter} or actually being executed. Note that you
% can't use this on stuff that has to work at expansion time. This
% really works mainly with macros that would also be suitable
% candidates for \cmd{\DeclareRobustCommand}.
%
% It is also a bad idea to trace a conditional in this manner: while
% the substitution should actually work when being executed, it will
% appear like an ordinary macro when being skipped, disturbing the
% conditional nesting.
% \begin{macro}{\qst@advance}
% We maintain a lot of counters and often need them expanded.
% Instead of allocating counter registers for them, we keep some of
% them in macros. Advancing such a macro is done using
% \cmd{\qst@advance}. Since we may do this in the middle of call
% tracing, we allocate our own temporary counter in the non-e\TeX\
% case.
% \begin{macrocode}
% \def\qst@advance#1{\edef#1{\number\numexpr(#1)+1}}
%<*!etex>
\newcount\qst@tmpcnt
\def\qst@advance#1{%
\qst@tmpcnt#1%
\advance\qst@tmpcnt\@ne
\edef#1{\number\qst@tmpcnt}}
%!etex>
% \end{macrocode}
% \end{macro}
%
% \subsection{The regexp state machine}
% The whole idea of call sequence checking is to match the calls
% happening throughout controlled macros to a sort of attributed
% regular expression. To facilitate that, we replace the macros by
% some fixed code exercising a state machine. For each state of the
% state machine, we define a state macro (taking one argument) that
% contains tokens to be matched followed by a corresponding action
% sequence in braces. The argument that this state macro takes is the
% control sequence in question itself: by using |#1| as the matching
% token, one can ensure a match at some place. This is used for
% implementing wildcards.
%
% An action sequence may be of three kinds:
% \begin{description}
% \item[explicit match] this matches a given token explicitly and
% causes the state to change to a defined new state, and causes an
% action to be called that has been user specified for this state transition.
% \item[wildcard match] instead of immediately making a state transition,
% the transition is remembered in case there is not explicit match:
% explicit matches have priority.
% \item[empty transition] this inserts the transitions of a different
% state recursively into the matching process. Every state inserted
% in this manner is recorded so as to not insert it again. This is
% not associated with a transition.
% \end{description}
%
% \begin{macro}{\qst@csmaketransition}
% This will perform an actual transition when called as an action
% code. It receives in |#1| the state to move to, in |#2| the
% transition number to use for the replacement code, in |#3| the
% code sequence to be replaced. Everything until the terminating
% |.| of the expanded transition table can be thrown away.
% \begin{macrocode}
\long\def\qst@csmaketransition#1#2#3#4.{%
\qst@cssetstate{#1}%
\expandafter
\endgroup
\csname qst@cstrans:#2\expandafter\endcsname
\csname qst@cssaved\romannumeral\qst@csscope>\string#3\endcsname}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\CalledName}
% This is used for getting back the original name from the saved
% sequence name as text.
% \begin{macrocode}
\def\CalledName#1{\expandafter\expandafter\expandafter\@gobble
\expandafter\strip@prefix\string#1}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\qst@csstate}
% \begin{macrocode}
\def\qst@csstate{\csname qst@csstate\romannumeral
\qst@csscope\endcsname}
% \end{macrocode}
% \end{macro}
% \begin{macro}{qst@cssetstate}
% \begin{macrocode}
\def\qst@cssetstate#1{%
\expandafter\expandafter\expandafter\xdef\qst@csstate{#1}}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\qst@csmakewildcard}
% This matches a wildcard. Since those are considered last, it just
% redefines \cmd{\qst@csnomatch} to a different default behavior when
% nothing matched explicitly. It also removes the replicated
% control sequence name in |#3| and restarts the matching process.
% Wildcards don't match the end of a match.
% \begin{macrocode}
\long\def\qst@csmakewildcard#1#2#3{\ifx#3\endExpectCallSequence
\else
\def\qst@csnomatch{\qst@csmaketransition{#1}{#2}}%
\fi
\qst@tmp}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@cscheckempty}
% This first checks whether the current state has already been
% considered (entering it into \cmd{\qst@lockout} if it has not),
% then expands its table here and resumes the search.
% \begin{macrocode}
\long\def\qst@cscheckempty#1#2{%
\ifcase\qst@cslockout{#1}\z@
\expandafter\def\expandafter\qst@cslockout
\expandafter##\expandafter1\expandafter{%
\qst@cslockout{##1}\ifnum##1=#1\@ne\fi}%
\expandafter\expandafter\expandafter\qst@tmp
\csname qst@csstate:#1\expandafter\endcsname\expandafter#2%
\or \expandafter\qst@tmp
\fi}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@csnext}
% This does the work of finding a transition from the current state
% given a specified lookahead token. It does this via defining
% \cmd{\qst@tmp} to a macro picking out the given token from the
% following string, taking the corresponding sequence to be called
% for it and calling it with the token as an argument.
%
% The lockout sequence in \cmd{\qst@cslockout} is preloaded with one
% comparison locking out the current state.
% \begin{macrocode}
\long\def\qst@csnext#1#2{%
\begingroup
\edef\qst@csscope{#1}%
\let\qst@csnomatch=\qst@csunexpected
\def\qst@cslockout##1{\ifnum\qst@csstate=##1\@ne\fi}%
\long\def\qst@tmp##1#2##2{##2#2}%
\expandafter\expandafter\expandafter\qst@tmp
\csname qst@csstate:\qst@csstate\endcsname#2%
#2{\qst@csnomatch}.}
%<*!etex>
\let\qst@csnextii\qst@csnext
\def\qst@csnext{\ifx\protect\@typeset@protect
\expandafter\qst@csnextii
\else \expandafter \@firstoftwo \expandafter \protect
\fi}
%!etex>
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\qst@csunexpected}
% This is the default if we have had no match:
% \begin{macrocode}
\long\def\qst@csunexpected#1#2.{%
\qst@error{
ExpectCallSequence: \string#1}%
\expandafter\expandafter\expandafter
\endgroup
\qst@cssavedcs#1%
}
\long\def\qst@cssavedcs#1{%
\csname qst@cssaved\romannumeral\qst@csscope>\string#1\endcsname}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@csaddstate}
% This does the actual work of adding something to the state table.
% Uses \cmd{\def}.
% \begin{macrocode}
\long\def\qst@csaddstate#1#2{%
% \ifcsname qst@csstate:#1\endcsname \else
% \expandafter\ifx\csname qst@csstate:#1\endcsname\relax
\expandafter\let\csname qst@csstate:#1\endcsname \@gobble
\fi
\long\expandafter
\def\csname qst@csstate:#1\expandafter\endcsname
\expandafter##\expandafter1\expandafter{\romannumeral
\expandafter\expandafter\expandafter\z@
\csname qst@csstate:#1\endcsname{##1}#2}}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@csaddempty}
% \begin{macrocode}
\def\qst@csaddempty#1#2{%
\expandafter\qst@csaddstate\expandafter{\number#1\expandafter}%
\expandafter{\expandafter##\expandafter1\expandafter{\expandafter
\qst@cscheckempty\expandafter{\number#2}}}}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@csallocstate}
% \begin{macro}{\qst@csalloctrans}
% Those maintain the currently available next state, and the
% currently available next transition.
% \begin{macrocode}
\def\qst@csallocstate{0}
\def\qst@csalloctrans{0}
% \end{macrocode}
% \end{macro}
% \end{macro}
% \begin{macro}{\qst@csscope}
% When we are nesting |ExpectCallSequence| environments,
% this keeps track of the scope we are in.
% \begin{macrocode}
\def\qst@csscope{0}
% \end{macrocode}
% \end{macro}
% \begin{macrocode}
\newenvironment{ExpectCallSequence}[1]
{%
\qst@advance\qst@csscope
\qst@cssetstate{\qst@csallocstate}%
\let\qst@csgroupstate\qst@csallocstate
\qst@advance\qst@csgroupstate
\qst@advance\qst@csgroupstate
\let\qst@csstack\@empty
\qst@csparse.{}*(#1){...}%
}{%
\def\qst@cssavedcs{\@gobble}%
\expandafter\qst@csnext\expandafter\qst@csscope
\csname end {ExpectCallSequence}\endcsname}
% \end{macrocode}
% \end{environment}
% \begin{macro}{\qst@csparse}
% This is doing the basic parsing. We are having some problems
% stopping non-e\TeX\ from hitting on the string space. This
% approach will not work with implicit characters like
% \cmd{\bgroup}, but then there are not too many of those around,
% and so the impact on string space should be tolerable.
% \begin{macrocode}
\long\def\qst@csparse#1{%
% \ifcsname qst@csparse:\string#1\endcsname
%<*!etex>
\ifcase
\ifcat\relax\noexpand#1%
\else
\expandafter \ifx \csname qst@csparse:\string#1\endcsname \relax
\else @ne \fi
\fi \m@ne
\or
%!etex>
\csname qst@csparse:\string#1\expandafter\expandafter\expandafter
\endcsname
\expandafter\@gobble
\else
\expandafter\qst@csparsedefault
\fi{#1}}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@csparse:...}
% This is what we do at the end of the patterns: we just jump to
% state~0. At state~0 we add a wildcard restoring any remaining
% macro redirection when it occurs, and let the end of the call
% sequence be allowed there to move to state~1. Oh, and we actually
% call the macro then.
% \begin{macrocode}
\expandafter\def\csname qst@cstrans:0\endcsname
#1{\expandafter\let\csname\CalledName#1\endcsname= #1%
\let#1=\@undefined
\csname\CalledName#1\endcsname}
\qst@csaddstate0{#1{\qst@csmakewildcard00}}
\qst@csaddempty{0}{1}
\expandafter\qst@csaddstate\expandafter1\expandafter{%
\csname end {ExpectCallSequence}\endcsname{\qst@csmaketransition21}}
\expandafter\let\csname qst@cstrans:1\endcsname\@gobble
\def\qst@csalloctrans{2}
\def\qst@csallocstate{3}
\expandafter\let\csname end {ExpectCallSequence}\endcsname
\endExpectCallSequence
\expandafter\def\csname qst@csparse:...\endcsname{%
\ifx\qst@csstack\@empty
\else
\PackageError{qstest}{Unmatched `(' in call sequence %)
specification}{You need to fix this}%
\loop
\qst@csstack
\ifx\qst@csstack\@empty
\else
\repeat
\fi
\qst@csaddempty{\qst@csallocstate}{0}%
\qst@advance\qst@csallocstate
\ignorespaces}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@csparsedefault}
% Ok, here is the default action. We resolve any pending state to
% the next available state and set \cmd{\qst@cslaststate} accordingly.
% Then we add a pending transition from the last state to the next
% state. The saved definition is not overwritten if the macro
% itself already has the proper setting. The purpose of that is to
% be able to preserve undefined macros.
% \begin{macrocode}
\long\def\qst@csparsedefault#1{%
%<*etex>
\ifcsname qst@cssaved\romannumeral\qst@csscope>\string#1\endcsname
\else \protected
%
%<*!etex>
{\expandafter}%
\expandafter\ifx\csname qst@cssaved\romannumeral\qst@csscope>%
\string#1\endcsname \@undefined
%!etex>
\edef\qst@tmp{\noexpand\qst@csnext \qst@csscope \noexpand#1}%
\ifx#1\qst@tmp
\else
\expandafter\let
\csname qst@cssaved\romannumeral\qst@csscope>\string#1\endcsname
= #1%
\let#1\qst@tmp
\fi
\fi
\qst@csparsetransition{#1}\qst@csmaketransition}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@csparsetransition}
% Ok, here we read in the code for an actual transition. This will
% create a new definition using \cmd{\def}.
% \begin{macrocode}
\long\def\qst@csparsetransition#1#2{%
\let\qst@cslaststate\qst@csallocstate
\qst@advance\qst@csallocstate
\long\edef\qst@tmp##1##2{%
\noexpand\qst@csaddstate{\qst@cslaststate}{%
##1{%
##2{\qst@csallocstate}%
{\qst@csalloctrans}}}}%
\qst@tmp{#1}{#2}%
\expandafter\qst@advance\expandafter\qst@csalloctrans
\expandafter\afterassignment\expandafter\qst@csparse
\expandafter\long\expandafter\def
\csname qst@cstrans:\qst@csalloctrans\endcsname}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@csparse:.}
% \begin{macrocode}
\expandafter\def\csname qst@csparse:.\endcsname{%
\qst@csparsetransition{##1}\qst@csmakewildcard}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@csparse:?}
% This turns the last element into an optional element.
% \begin{macrocode}
\expandafter\def\csname qst@csparse:?\endcsname{%
\qst@csensurelaststate?%
\qst@csaddempty\qst@cslaststate\qst@csallocstate
\let\qst@cslaststate\@undefined
\qst@csparse}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@csensurelaststate}
% This checks that we have a valid last state to hook onto. There
% is no really useful error recovery if we haven't.
% \begin{macrocode}
\def\qst@csensurelaststate#1{\ifx\qst@cslaststate\@undefined
\PackageError{qstest}{Unexpected `#1' in call sequence}{%
Expect chaos to ensue}%
\def\qst@cslaststate{0}%
\fi}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@csparse:*}
% This would need to make the pending state identical with the
% previous state while leaving all of it pending. But we can't
% retroactively make previous states pending again, so we instead
% make do with adding an empty transition from the previous state to
% the next pending one.
% \begin{macrocode}
\expandafter\def\csname qst@csparse:*\endcsname{%
\qst@csensurelaststate*%
\qst@csaddempty\qst@csallocstate\qst@cslaststate
\qst@advance\qst@csallocstate
\qst@csaddempty\qst@cslaststate\qst@csallocstate
\let\qst@cslaststate\@undefined
\qst@csparse}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@csparse:+}
% This is somewhat similar. Note that we have to add an empty
% transition at the end (like with |*| previously) to make sure that
% something like |\a{}+\b{}+| does not match |\a\b\a|.
% \begin{macrocode}
\expandafter\def\csname qst@csparse:+\endcsname{%
\qst@csensurelaststate+%
\qst@csaddempty\qst@csallocstate\qst@cslaststate
\let\qst@cslaststate\qst@csallocstate
\qst@advance\qst@csallocstate
\qst@csaddempty\qst@cslaststate\qst@csallocstate
\let\qst@cslaststate\@undefined
\qst@csparse}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@csparse:(}^^A%{)}
% Ok, now we are getting into grouping. This adds a bit of
% complexity, but it is actually rather containable (took a lot of
% work, though).
% \begin{macrocode}
\expandafter\def\csname qst@csparse:(\endcsname{%
% \qst@tmptoks\expandafter{\qst@csstack}%
\edef\qst@csstack{\def\noexpand\qst@csstack{%
% \unexpanded\expandafter{\qst@csstack}%
% \the\qst@tmptoks
}%
\def\noexpand\qst@csgroupstate{\qst@csgroupstate}}%
\let\qst@csgroupstate\qst@csallocstate
\qst@advance\qst@csallocstate
\qst@csaddempty\qst@csgroupstate\qst@csallocstate
\qst@csparse}
% \end{macrocode}
% \end{macro}
% \DeleteShortVerb\|
% \begin{macro}{\qst@csparse:|}
% \begin{macrocode}
\expandafter\def\csname qst@csparse:|\endcsname{%
\let\qst@cslaststate\qst@csallocstate
% \qst@tmptoks\expandafter{\qst@csstack}%
% \edef\qst@csstack{\the\qst@csstack
% \edef\qst@csstack{\unexpanded\expandafter{\qst@csstack}%
\noexpand\qst@csaddempty{\qst@cslaststate}%
\noexpand\qst@csallocstate}%
\qst@advance\qst@csallocstate
\qst@csaddempty\qst@csgroupstate\qst@csallocstate
\let\qst@cslaststate\@undefined
\qst@csparse}
% \end{macrocode}
% \end{macro}
% \MakeShortVerb\|
% \begin{macro}{\qst@csparse:)}^^A%{(}
% \begin{macrocode}
\expandafter\def\csname qst@csparse:)\endcsname{%
\let\qst@cslaststate\qst@csgroupstate
\ifx\qst@csstack\@empty
\PackageError{qstest}{Unmatched `)' in call sequence
specification}{You need to fix this}%
\fi
\qst@csstack
\qst@csparse
}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@csparse:`}
% We don't check whether this is actually the first encounter with
% |`|: we just reset the state machine to come here.
% \begin{macrocode}
\expandafter\def\csname qst@csparse:`\endcsname{%
\qst@cssetstate{\qst@csallocstate}%
\let\qst@cslaststate\@undefined
\qst@csparse}
\expandafter\let\csname qst@csparse:^\expandafter\endcsname
\csname qst@csparse:`\endcsname
% \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@csparse:'}
% \begin{macrocode}
\expandafter\def\csname qst@csparse:'\endcsname{%
\qst@csaddempty{\qst@csallocstate}{1}%
\let\qst@cslaststate\qst@csallocstate
\qst@advance\qst@csallocstate
\qst@csparse}
\expandafter\let\csname qst@csparse:$%$
\expandafter\endcsname\csname qst@csparse:'\endcsname
% \end{macrocode}
% \end{macro}
% Ok, let us test the state machine. First define two macros that we
% are going to replace:
% \begin{macrocode}
%
%<*test>
\begin{qstest}{ExpectCallSequence state machine}%
{ExpectCallSequence,internal}
\def\foo#1#2#3{\relax\relax}
\let\fie\relax
% \end{macrocode}
% The free state table starts at 3, the free transitions at 2.
% \begin{macrocode}
\Expect*{\qst@csallocstate}{3}
\Expect*{\qst@csalloctrans}{2}
% \end{macrocode}
% Now we have an implicit start into the call sequence that is |.{}*(|
% and thus takes away 3 states and one transition. The call sequence
% starts off with one ignored entry and then the real state, so we
% expect to be at state~7 initially.
% \begin{macrocode}
\begin{ExpectCallSequence}{\fie{}%
`\foo#1#2#3{#2\bar{#3}}(\bar#1#2{#2}|.#1{})+%
\foo#1{}}
\Expect*{\qst@csstate}{7}
\Expect*{\qst@csallocstate}{15}
% \end{macrocode}
% The state transitions actually happen before the respective code is
% called, so we can actually continue in the matching sequence while
% still working off the replacement for a macro:
% \begin{macrocode}
\foo{\Expect*{\qst@csstate}{8}}{\Expect*{\qst@csstate}{10}}
\Expect*{\qst@csstate}{10}
% \end{macrocode}
% \cmd{\fie} will match the wildcard. Note that we arrive at a
% different target state than previously. While this is
% counterintuively, we have to be aware that an empty transition from
% state~10 to state~12 exists, so both are actually the same state in
% reality.
% \begin{macrocode}
\fie
\Expect*{\qst@csstate}{12}
% \end{macrocode}
% Now \cmd{\foo} kicks us out of the matching. Again, state~14 looks
% strange, but it has an empty transition to state~0, the ``match
% everything until we are finished'' state.
% \begin{macrocode}
\foo
\Expect*{\qst@csstate}{14}
% \end{macrocode}
% So we actually make use of this state and call \cmd{\fie} after
% which it will have reverted to its original meaning:
% \begin{macrocode}
\fie
\Expect*{\qst@csstate}{0}
\Expect*{\meaning\fie}*{\meaning\relax}
% \end{macrocode}
% It is sort of amusing that the above assertion will fail if we write
% just
% \begin{quote}
% |\Expect*{\meaning\fie}{\relax}|
% \end{quote}
% since the meaning of \cmd{\relax} is not ended with a blank, while
% the printed string of its name does end with one. We check that use
% of \cmd{\foo} lets us revert its meaning as well.
% \begin{macrocode}
\foo\junk\junk\junk
\Expect*{\qst@csstate}{0}
\Expect*{\meaning\foo}{macro:#1#2#3->\relax \relax}
\end{ExpectCallSequence}
\end{qstest}
%
%<*package>
% \end{macrocode}
%
% \subsection{Saved results}
%
% The purpose of saved results is to be able to check that the value
% has remained the same over passes. Results are given a unique label
% name and are written to an auxiliary file where they can be read in
% for the sake of comparison. One can use the normal |.aux| file for
% this purpose, but it might be preferable to use a separate dedicated
% file. That way it is possible to input a versioned \emph{copy} of
% this file and have a fixed point of reference rather than the last
% run.
%
% While the |.aux| file is read in automatically at the beginning of
% the document, this does not happen with explicitly named files. You
% have to read them in yourself, preferably using
% \begin{quote}
% |\InputIfFileExists|\marg{filename}|{}{}|
% \end{quote}
% so that no error is thrown when the file does not yet exist.
%
% \begin{macro}{\SaveValueFile}
% This specifies which file name to use for saving results. If this
% is specified, a special file is opened. If one does not specify
% this, instead the standard |.aux| file is used instead.
% \begin{macrocode}
\newcommand*\SaveValueFile[1]{%
\ifnum\qst@savefile=\@auxout
\newwrite\qst@savefile
\else
\immediate\closeout\qst@savefile
\fi
\immediate\openout\qst@savefile #1\relax}
\def\qst@savefile{\@auxout}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\CloseValueFile}
% This is not likely to be used except for testing this package
% itself: it closes the given file so that it may be read in again
% immediately.
% \begin{macrocode}
\newcommand*\CloseValueFile{%
\ifnum\qst@savefile=\@auxout
\else
\immediate\closeout\qst@savefile
\fi}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\SaveValue}
% This gets the label name as first argument (which gets sanitized
% more or less, depending on whether you use e\TeX\ or not).
%
% The second argument is the same kind of argument as \cmd{\Expect}
% expects.
% \begin{macrocode}
\newcommand\SaveValue[1]{%
% \edef\qst@tmpc{\detokenize{#1}}%
% \edef\qst@tmpc{\string#1}%
\ifnum\qst@savefile=\@auxout
\ifx\@onlypreamble\AtBeginDocument
\else
\PackageError{qstest}{You need to specify \string\SaveValueFile^^J
for test files without document.}{I can't use
`\string\SaveValue' before either `\string\begin%
{document}'^^J
or `\string\SaveValueFile'}%
\fi
\fi
\qst@expectarg\qst@defaultvalue\qst@saveval}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@saveval}
% This is tricky: we don't want to turn either |^^M| or |^^J| into
% each other. But at least \TeX live will write out |^^J| as a
% single character (causing a line break) and only |^^M| will
% actually get escaped. It would be principally ok to just crank
% out line feeds unmodified, if only \TeX\ would not strip spaces at
% the end of a line. So we go through the string we want to put
% out, and change the single character |^^J| manually into the three
% characters |^^J| after which \TeX\ will preserve spaces.
% \begin{macrocode}
\def\qst@saveval{%
{\immediate\write\qst@savefile{\string\InternalSetValue^^J%
\expandafter\qst@cleanlf\qst@tmpc\qst@endcleanlf^^J%
\expandafter\qst@cleanlf\qst@defaultvalue\qst@endcleanlf^^J%
\string\EndInternalSetValue^^J}}}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@cleanlf}
% \begin{macro}{\qst@endcleanlf}
% \begin{macrocode}
\def\qst@cleanlf#1^^J{#1^%
^J\qst@cleanlf}
\def\qst@endcleanlf^%
^J\qst@cleanlf{:^^J}
% \end{macrocode}
% \end{macro}
% \end{macro}
% \begin{macro}{\InternalSetValue}
% The chosen catcodes are picked in order to have |^^xx| escapes
% still work out (\TeX\ can't be kept from using them occasionally).
% Of course, in case |^^| sequences have indeed been present in the
% original macro (like in \cmd{\qst@cleanlf} above), we get
% flummoxed. The spaces we read in here should be the same that
% occur during sanitization. All of this is a bit of compromise.
% Newlines separate keys and values.
% \begin{macrocode}
\newcommand*\InternalSetValue{\begingroup
\let\do\@makeother\dospecials
\endlinechar`\^^J
\catcode`\^7
\qst@setsavedii}
\begingroup
\catcode`\!=12
\lccode`\!=`\\
\lowercase{\endgroup
\def\qst@setsavedii#1:^^J#2:^^J!}EndInternalSetValue^^J{\endgroup
% \expandafter\gdef\csname qst@value:\detokenize{#1}\endcsname{#2}}
% \expandafter\gdef\csname qst@value:\string#1\endcsname{#2}}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\EndInternalSetValue}
% This should actually never get encountered:
% \begin{macrocode}
\newcommand*{\EndInternalSetValue}{%
\PackageError{qstest}{\string\InternalSetValue\space got confused}%
{You probably wrote something bad to the save file}}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\SavedValue}
% This is used for retrieving a saved value. If there is no such
% value, instead the current setting in \cmd{\qst@defaultvalue} is
% used.
% \begin{macrocode}
\def\SavedValue#1{%
%<*etex>
\ifcsname qst@value:\detokenize{#1}\endcsname
\expandafter\expandafter\expandafter
{\csname qst@value:\detokenize{#1}\expandafter\endcsname\expandafter}%
\else
\expandafter\expandafter\expandafter{\expandafter\qst@defaultvalue
\expandafter}%
%
%<*!etex>
\expandafter\ifx\csname qst@value:\string#1\endcsname\relax
\expandafter\expandafter\expandafter{\expandafter\qst@defaultvalue
\expandafter}%
\else
\expandafter\expandafter\expandafter
{\csname qst@value:\string#1\expandafter\endcsname\expandafter}%
%!etex>
\fi}
% \end{macrocode}
% \end{macro}
% \subsection{The End}
% The test can be ended either in the driver file, or in a standalone
% testfile. \cmd{\stop} is \LaTeX's way of ending the processing of
% an input file before any document processing or setup has been done.
% \begin{macrocode}
%
% \stop
% \end{macrocode}
% \Finale