1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
|
\section{The test suite}
The subdirectory \C{check} contains a test suite for \FORM. Using the autoconf
facilities the checks can be started with the command \C{make check}.
Otherwise, one can issue the command \C{ruby form.rb} in the \C{check}
directory.
The test suite is written in the language
Ruby\footnote{\LINK{http://www.ruby-lang.org}}. Ruby itself already offers a
unit testing framework and this is used with as minimal as possible extensions
to make the creation of test cases for \FORM\ programs easy. All the extensions
to the built-in Ruby testing framework (\C{Test::Unit}) are contained in the
file \C{form.rb}. This file also contains code to load test cases from other
\C{*.rb} files in the \C{check} directory. Therefore all test cases are
contained in appropriately named \C{*.rb} files. The makefile's purpose is to
integrate the call \C{ruby form.rb} into the autoconf system.
{\it Side note:}
The choice to use Ruby and its built-in test framework was taken for several
reasons: It makes sense to use or adapt already existing testing frameworks in
order to keep the extra cost of maintenance as low as possible for the \FORM\
programmers. There are numerous systems available on the market, some are part
of a language runtime environment (libraries), and some are dedicated programs
with a custom configuration language. Since the tests for \FORM\ programs center
mainly about text processing, i.e. comparing the textual \FORM\ output to a
correct answer, we need powerful text processing facilities like pattern
matching. But we also need file operations and information from the operating
system to check the run of a \FORM\ program, eventually. All this is readily
available in the testing frameworks of scripting languages, like Ruby, Python,
or Tcl. Ruby was ultimately chosen, because the mixing of \FORM\ code with the
steering scripting language code looked nicest, and the small amount of extra
(Ruby) syntax necessary makes it convenient to add new test cases.
A new test case can be implemented in the following way. First of all, we need a
\FORM\ program that is to be run. It might be a program that exhibits an actual
bug in (a previous version of) \FORM\ or that contains generic code that should
be guaranteed to work, also in coming releases of \FORM. It might also be code
that deliberately crashes \FORM\ or causes other errors, like syntax errors, if
this behavior of \FORM\ is to be assumed. Usually, the \FORM\ program is rather
short or can be made such. In this case, we are going to mix the Ruby and the
\FORM\ code in one file. Alternatively, the \FORM\ program can also be kept in a
separate file. This option will be discussed later.
Now, either one choses an existing \C{*.rb} file (not \C{form.rb}) or starts a
new one. The name of the file should fit the test case scenario. In this file we
need to define a Ruby class that will contain our \FORM\ code as well as the
checks (assertions) we want to impose on the run.
The generic frame of this test case definition looks like this:
\begin{verbatim}
class [Test name] < FormTest
def setup
[Setup code, usually this includes the FORM program code]
end
def test1
[Execution code, and the assertion and testing code]
end
end
\end{verbatim}
The text in the brackets [ ] needs to be filled with our specific code. The
details of the Ruby code itself will be explained later. For a start, it is
usually advisable just to copy an existing test case and modify it.
Every class defined in this way will be used for the testing. First, Ruby will
run the code in the class method \C{setup}, and then it runs \C{test1}.
A complete test might look like this:
\begin{verbatim}
class SymbolIdTest < FormTest
def setup
input <<-EOF
S x, y;
L f = (x+y)^100;
id x = y;
print;
.end
EOF
end
def test1
execute FORM
assert no_problem
assert result("f") =~
pattern("1267650600228229401496703205376*y^100;")
end
end
\end{verbatim}
We have chose the name \C{SymbolIdTest} for our class. We defined the \FORM\
program in-line with a so called here document (\C{<<-EOF ... EOF}). We do run
the \FORM\ executable. Alternatives would be \TFORM, for example. The assertions
we have are that no problem occurred, i.e. no syntax error, no runtime error, or
similar things. We also check the output of our \FORM\ program. We compare via
pattern matching the result of the expression \C{f} with the correct answer. The
function \C{result()} extracts the appropriate line from the output, \C{=\~{}} is
the pattern matching operator in Ruby, and the function \C{pattern()} prepares
special characters like the caret (\^{}) for the pattern matcher.
Next time we run the test suite, our test will be run as well. If no assertions
are violated, we will only see the number of successful tests and assertions
increased in the summary output.
Even though the extra Ruby syntax is kept to a minimum and is rather
straightforward, some remarks about the Ruby language are useful here. Classes
are defined by the keyword \C{class}, and methods (or functions) are declared
with the keyword \C{def}. These definitions are always ended with the keyword
\C{end}. \C{FormTest} is a class defined in \C{form.rb} that contains all the
special code for \FORM\ test and that is derived from the built-in Ruby test
case class \C{TestCase}. For every test case we derive again from this class
(\C{class B < A} says that \C{B} is derived from \C{A}). We don't need
semicolons to end a line and indentation is arbitrary. Class names should be
capitalized. In Ruby, parentheses around the arguments of functions can often be
omitted. We use this possibility when we call the functions \C{input},
\C{execute}, and \C{assert}. We could have written \C{execute(FORM)} as well,
for example. The here document (\C{<<-EOF ... EOF}) can also use other markers
instead of \C{EOF}, of course. The minus sign before \C{EOF} allows the end
marker to be indented. Comments are started with a \#.
One class can actually contain more than one test. The testing framework will
call the method \C{setup} and then a method whose name starts with \C{test}
(Note: in newer versions of Ruby the name could be just \C{test}, but older
versions ($\ge$1.8.x) require at least one following extra character). If there are
more methods starting with \C{test}, each will be called and for each \C{setup}
will be called first.
In \C{setup} we need to prepare everything for the execution of \FORM. We can
either use \C{input} to in-line the source directly, or we can use
\C{input\_file} with a string as an argument to reference an external file,
e.g.
\begin{verbatim}
input_file "parsebug.frm"
\end{verbatim}
The function \C{input} will create a temporary
\FORM\ file for the contents. The name of the file is defined in \C{form.rb}.
The executable will later be run with the given name or the name of the
temporary file as an argument. If additional arguments need to be given to the
executable, the function \C{extra\_parameter} can be used, like e.g.
\begin{verbatim}
extra_parameter "-w4 -l"
\end{verbatim}
Sometimes one might need to prepare more things for a \FORM\ run, like setting
up certain files or starting an external program. This needs to be done
by ordinary Ruby code. For this, some more of the Ruby language needs to be
known by the user.
In the class methods with a name starting with \C{test} we put the code to run
the \FORM\ executable and to test the outcome. Usually, the first line will be
the call to the executable itself, either
\begin{verbatim}
execute FORM
\end{verbatim}
or
\begin{verbatim}
execute TFORM
\end{verbatim}
(\PARFORM\ is not supported yet). The function \C{execute} will run the
executable with the necessary or requested arguments, but it will run it under
the supervision of the \C{strace} system utility. Therefore \C{strace} needs to
be present on the system (options to enable or disable the use of \C{strace}
will probably be added in the future). \C{strace} is used to get detailed
information about the return value or possible failure states of the executable.
The output of \C{strace} will be saved in a temporary file and made available to
the test case programmer in a Ruby variable. The regular output and the error
channel output will be available in Ruby variables as well.
The Ruby variables containing the output are \C{\@@strace\_out}, \C{\@@stdout},
and \C{\@@stderr} (the leading \@@-sign is Ruby syntax for specifying instance
variables, i.e. variables belonging to a certain object). These variables are the
primary source for doing tests. In principle, these variables can be
investigated directly, for example via pattern matching like
\begin{verbatim}
if @strace_out =~ /Segmentation fault/
...
end
\end{verbatim}
which checks whether a segmentation fault has occurred (the slashes in Ruby
define a pattern). But for the most common cases some test functions
exist that encapsulate necessary pattern matching details. These functions
return true or false values which can be used as arguments to the \C{assert}
function. The \C{assert} function raises an error if the argument is false.
Available tests functions are:
\begin{tabular}{lp{20em}}
\C{crash} & true if a crash (segmentation fault) occurred \\
\C{warning} & true if \FORM\ has issued a warning \\
\C{compile\_error} & true if \FORM\ has found a syntax error \\
\C{runtime\_error} & true if \FORM\ has terminated prematurely \\
\C{error} & true if \C{compile\_error} or \C{runtime\_error} is true or
the standard error channel contains data \\
\C{problem} & true if \C{warning} or \C{error} or \C{crash} is true
\end{tabular}
Additionally, the logical opposite of each function exists with a name starting
with \C{no\_}, like \C{no\_problem} or \C{no\_crash}.
There is also the function \C{return\_value} which gives the return value of the
\FORM\ program as an integer, so one could do a check like
\begin{verbatim}
assert return_value == 66
\end{verbatim}
If pattern matching is coded directly, like in our example, some details have to
be considered. The operator \C{=\~{}} will try to match a string with a pattern.
The variables like \C{\@@stdout} are actually strings (they do contain the
carriage return and/or line feed for multi-line output). Patterns in Ruby are
written between slashes and various characters are interpreted in a special way
(following the widely used regex-syntax).
There are four functions to facilitate things: \C{result()}, \C{pattern()},
\C{exact\_result()}, and \C{exact\_pattern()}. \C{result()} takes a string being
the name of an expression and returns a string that only contains the lines
belonging to the last output of this expression. If it is not the last output of
an expression that is wished for, a second numeric parameter can be given that
specifies the index of the output (counting starts at 0). While \C{result()}
removes all line breaks and whitespaces, \C{exact\_result()} leaves them in
place. \C{pattern()} transforms special characters in the given string, removes
whitespaces and line breaks, and returns the string as a pattern. Since \FORM\
expressions usually contain a lot of special characters like +, *, ., etc. they
cannot not be simply used in a pattern. \C{pattern()} transforms these
characters automatically into the correct regex equivalent, e.g. + becomes
\textbackslash +. With it, a \FORM\ expression can be directly given as an
argument and used in a pattern matching (see example). \C{exact\_pattern()} does
not treat whitespaces and line breaks in a special way as \C{pattern()} does and
can therefore be used when a exact comparison is required (if for example a bug
in the output functions of \FORM\ had caused some whitespace or line breaks to
be missing and a test case were required to check for this behavior).
If one doesn't want or cannot use the \C{assert} function, one can signal a test
failure to the testing framework by raising an \C{AssertionFailedError}
directly, like for example
\begin{verbatim}
if return_value != 2
raise AssertionFailedError.new("return value is wrong!")
end
\end{verbatim}
Suppose a \FORM\ program should have deleted some file (\C{\#remove}), one could
implement the following test
\begin{verbatim}
if File.exist?("thenameofthefile")
raise AssertionFailedError.new("File still exists!")
end
\end{verbatim}
The testing framework actually not only calls \C{setup} and each \C{test} method
but also a method called \C{teardown}. This method is responsible for cleaning
up things at the end of each test run. The class \C{FormTest} provides such a
\C{teardown} method that will be inherited by the users test case class unless
it is overwritten. It calls the method \C{remove\_files} to delete all temporary
files that have been created so far. \C{remove\_files} can be called by the user
directly. If \C{teardown} is to be replaced by a specific implementation, it is
advisable to still call \C{FormTest}'s \C{teardown} (using Ruby's command
\C{super}), like for example
\begin{verbatim}
...
def teardown
super
File.delete("extra.log")
end
...
\end{verbatim}
At last, a complete example as it is actually contained in the repository.
{\scriptsize
\begin{verbatim}
#[ SparseTable1 :
=begin
Bugs reported 2004-04-06 by Misha Tentukov
PrintTable and FillExpression did not work with non-sparse tables
Fixed 2005-09-27
=end
class SparseTable1 < FormTest
def setup
input <<-EOF
cf f;
s x;
ctable Tab(1:`TableSize');
ctable TabNew(1:`TableSize');
#do i=1,`TableSize',1
Fill Tab(`i')=f(`i');
.sort
#enddo
* BUG1 (not all elements are printed):
PrintTable Tab;
bracket x;
.sort
L expr1=table_(Tab,x);
print;
.sort
bracket x;
.sort
* BUG 2 ( seems only TabNew(1) is ok - further everything is broken):
Fillexpression TabNew=expr1(x);
.sort
#do i=1,`TableSize'
L e`i'=TabNew(`i');
#enddo
print;
.sort
.end
EOF
extra_parameter "-D TableSize=10"
end
def test1
execute FORM
assert no_problem
assert result("expr1") =~ pattern(<<-EOF
f(1)*x + f(2)*x^2 + f(3)*x^3 + f(4)*x^4 + f(5)*x^5 + f(6)*x^6 + f(7)*x^7
+ f(8)*x^8 + f(9)*x^9 + f(10)*x^10;
EOF
)
assert result("e10") =~ /\s+f\(10\);/
end
end
#] SparseTable1 :
\end{verbatim}}
Some remarks. Folds are used (to structure a long file). \C{=begin} and \C{=end}
define a commentary block. Here useful information are given about the bug that
triggered the test case. The input is not modified compared to the original
\FORM\ program, it is just directly pasted into this Ruby file. We use
\C{extra\_parameter} to define a preprocessor variable for the run. We check
\C{expr1} to a multi-line reference. Since we use \C{pattern()} (instead of
\C{exact\_pattern()}), we can be sloppy about the indentation and the whitespaces.
The expression \C{e10} is matched to a pattern done "by hand" instead (just to
show the principle). For such a test case, where we are mostly interested about
the correctness of the calculation, the first assertion (\C{assert no\_problem})
is a standard.
|