Fix a docstring.
[docutils.git] / prest / t / Common.smak
blobaee15728aeca432b77134b57818a9f0c25c9cd5d
1 # -*- perl -*-
2 # A common SlayMakefile for executing tests
3 # It is intended to be called from two levels lower in the directory
4 # hierarchy using "slaymake -f ../../Common.smak <target>".
8 =head1 NAME
10 Common.smak
12 =head1 DESCRIPTION
14 Used as the common C<Slay::Makefile> input for all the
15 C<Text::Restructured> tests.  It contains all the infrastructure needed
16 for executing the python tests from docutils, while still providing
17 tailorability for tests that do not fit the framework.  The tests are
18 divided into suites (e.g., C<10_parse>), each of which can have more
19 than one C<.t> file in it with its associated C<.init> directory to
20 implement multiple related tests.  The customizability comes the
21 following files (relative to the C<.dir> directory created by C<gress.pm>):
23 =over
25 =item ../Suite.smak
27 If it exists, overrides variables for the entire suite.
29 =item Dir.smak
31 If it exists, allows overriding for the directory and for individual
32 tests within the directory
34 =back
36 =head1 TARGETS
38 =over
40 =item pretest
42 Returns the dependencies in global variable C<@PRETESTS>, which is
43 initialized to the targets C<unpack> and C<find_oks>, but can be added
44 to by the custom C<Slay::Makefile> files above.
46 =item unpack
48 Checks for a python test file and, if it exists, calls the
49 C<extract_tests> script from the C<tbin> directory on it to create a
50 series of C<.rst> and C<.dom> files for testing.
52 =item find_oks
54 Sets the global variable C<@OKS> to contain the list of C<.rst> files
55 in the directory with the C<.rst> extension replaced by C<.ok>.
56 Override this target if there is a different way to determine the list
57 of targets corresponding to tests.
59 =item test
61 Returns the global variable C<@OKS>.
63 =item %.ok
65 Looks for an expect file for the C<$1> part (the part matching the
66 first C<%>) in order to determine which writer to use when calling
67 C<prest>.  For writer C<xyz>, the expect file should be one of
69 =over
71 =item C<$1.xyz>
73 The unpacked python expects all have this form.
75 =item C<$1.myxyz>
77 Sometimes prest intentionally does not match the python output, so
78 C<$1.myxyz> is used in preference to C<$1.xyz> if both exist.
80 =item C<$1.xyz.re>
82 Sometimes a regular expression difference, using the C<rediff> script
83 in the C<tbin> directory, needs to be done for fault grading.  This
84 file contains regular expressions and is used in preference to either
85 of the previous two.
87 =back
89 Note: There are two exceptions to the one-to-one mapping between
90 C<xyz> and the writer namely, namely C<tex> runs the C<latex> writer
91 and C<idx> runs the C<index> writer.
93 This target returns an error if no expect file for a known writer is
94 found.
96 After determining which writer is appropriate, this target returns two
97 dependencies, one of which will be the output of the writer
98 (C<$1.xyz.out>), and the other of which is the file against which
99 fault grading occurs.  If the expect file has extension C<.xyz.re>,
100 then C<rediff> is used for fault grading, and the fault grading file
101 is C<$1.xyz.re>.  Otherwise, C<diff> is used for fault grading and 
102 the fault grading file is C<$1.xyz.df>.  If a C<diff> fault grading
103 fails, then the <$1.ok> target repeats the fault grading using
104 C<rediff>.
106 =item %.%.df
108 Copies C<$1.stderr>, if it exists, and the first of C<$1.my$2> or
109 C<$1.$2> to the target, where C<$2> is the writer.  Thus, any error
110 messages printed to STDERR become part of the fault grading.
112 =item %.%.re
114 If there was already a C<$1.$2.re> file, this rule does nothing.
115 Otherwise, it was invoked because the straight C<diff> failed.  It passes
116 C<$1.stderr>, if it exists, and the first of C<$1.my$2> or C<$1.$2> as
117 arguments to the script C<re$2> (e.g., C<redom>), located in the
118 C<tbin> directory.  The purpose of such a script is to elide with
119 regular expressions that part of the output that can validly differ.
121 =item %.%.out%
123 Dependencies are C<$1.rst> (if it exists), the C<prest> script, and
124 the file containing the implemention of the writer C<$2>.  Invokes
126   perl $PERL_FILE_FLAGS{$1} $PERL_DIR_FLAGS $PERL_SUITE_FLAGS \
127        $PERL_WRT_FLAGS{$2} $PERL_FLAGS -I <root>/blib/lib \
128        <root>/blib/script/prest -w $2 \
129        $PREST_FLAGS $PREST_WRT_FLAGS{$2} $PREST_SUITE_FLAGS \
130        $PREST_DIR_FLAGS $PREST_FILE_FLAGS{$1} [$1.rst] \
131        $POSTPROC_FILE{$1} > $target
133 If C<$3> is null, then any output to STDERR is captured in the output
134 file; otherwise the output file contains only the prest output to STDOUT.
135 If there is no C<$1.rst> file, then C<$PREST_FILE_FLAGS{$1}> should
136 contain the list of input files.
138 =back
140 =head1 GLOBAL VARIABLES
142 As can be seen above, there are a number of global variables that
143 affect the processing of the prest command:
145 =over
147 =item C<$PERL_DIR_FLAGS> (Dir.smak)
149 Flags for perl invocation of prest for a directory.
151 =item C<$PERL_SUITE_FLAGS> (Suite.smak)
153 Flags for perl invocation of prest for a suite.
155 =item C<%PERL_WRT_FLAGS> (Suite.smak or Perl.smak)
157 Flags for perl invocation of prest for specific writers (indexed by
158 writer).
160 =item C<%PERL_FILE_FLAGS> (Dir.smak)
162 Flags for perl invocation of prest for specific files (indexed by base
163 filename).
165 =item C<$PREST_DIR_FLAGS> (Dir.smak)
167 Writer- and file-independent flags for prest for a directory.
169 =item C<$PREST_FLAGS> (Suite.smak or Dir.smak)
171 Writer- and file-independent flags for prest.  Defaults to 
173   -D source='test data' -D xformoff='.*' -D align=0
175 unless already set.
177 =item C<$PREST_SUITE_FLAGS> (Suite.smak)
179 Writer- and file-independent flags for prest for a suite.
181 =item C<%PREST_WRT_FLAGS> (Suite.smak or Dir.smak)
183 Flags for specific writers (indexed by writer).  C<$PREST_WRT_FLAGS{dom}>
184 defaults to "-W nobackn" unless already set.
186 =item C<%PREST_FILE_FLAGS> (Dir.smak)
188 Flags for specific files (indexed by base filename).
190 =item C<%POSTPROC_FILE> (Dir.smak)
192 Text for postprocessing output before check for specific files
193 (indexed by base filename).  Usually starts with "|".
195 =back
197 =head1 ENVIRONMENT VARIABLES
199 =over
201 =item C<COVER>
203 If set, runs perl with flags to create a coverage database using
204 C<Devel::Cover>.
206 =item C<DEBUG>
208 If set, runs perl with the C<-d> flag (debug mode).
210 =item C<PRINT>
212 If set, echoes to STDERR the commands to be executed, but does not
213 actually execute them.
215 =item C<TRACE>
217 If set, echoes to STDERR the commands as they execute.
219 =back
221 =cut
224 default: test
227     use strict;
228     use vars qw(@OKS @PRETESTS);
229     # Flags that can be set in Suite.smak and Dir.smak:
230     #   $EXTRACT_TEST_FLAGS Flags for extract_tests script
231     #   $PERL_DIR_FLAGS     Flags for perl invocation of prest for a directory
232     #   $PERL_SUITE_FLAGS   Flags for perl invocation of prest for a suite
233     #   %PERL_WRT_FLAGS     Flags for perl invocation of prest for specific
234     #                       writers (indexed by writer)
235     #   %PERL_FILE_FLAGS    Flags for perl infocation of prest for specific
236     #                       files (indexed by base filename)
237     #   $PREST_DIR_FLAGS    Generic flags for prest for a directory
238     #   $PREST_FLAGS        Generic flags for prest
239     #   $PREST_SUITE_FLAGS  Generic flags for prest for a suite
240     #   %PREST_WRT_FLAGS    Flags for specific writers (indexed by writer)
241     #   %PREST_FILE_FLAGS   Flags for specific files (indexed by base filename)
242     #   %POSTPROC_FILE      Text for postprocessing output before check for
243     #                       specific files (indexed by base filename).  Usually
244     #                       starts with "|".
245     
246     use vars qw(%EXT_TO_WRT $EXTRACT_TEST_FLAGS $PREST_FLAGS
247                 $PERL_DIR_FLAGS $PERL_SUITE_FLAGS %PERL_WRT_FLAGS
248                 %PERL_FILE_FLAGS
249                 %POSTPROC_FILE
250                 $PREST_DIR_FLAGS $PREST_SUITE_FLAGS %PREST_WRT_FLAGS
251                 %PREST_FILE_FLAGS);
253     %EXT_TO_WRT = (tex => 'latex', idx => 'index');
254     $EXTRACT_TEST_FLAGS = '';
255     @PRETESTS = qw(unpack find_oks);
256     # Executes a command line while possibly echoing it to the screen
257     sub execute ( $ ) {
258         my ($cmd) = @_;
259         print STDERR "\t$cmd\n" if $ENV{TRACE} || $ENV{PRINT};
260         system $cmd unless $ENV{PRINT};
261         return;
262     }
265 # Include the suite's SlayMakefile if it exists
266 -include ../Suite.smak
268 # Include the directory's SlayMakefile if it exists
269 -include Dir.smak
271 test: { @OKS }
273 pretest: { @PRETESTS } 
276     # Set up our make variables
277     use strict;
278     use vars qw($EXTRACT_TESTS $LIB $PERL $PERL_FLAGS $SCRIPT $TBIN $TOP
279                 @WRITERS);
281     # Find our top directory
282     chomp ($TOP = `pwd`);
283     $TOP =~ s!/t/.*$!!;
284     $TBIN   = "$TOP/tbin";
285     $SCRIPT = "$TOP/blib/script";
286     $LIB    = "$TOP/blib/lib";
287     $EXTRACT_TESTS = "$TBIN/extract_tests";
288     $PERL   = $^X;
289     # Get list of writers
290     my ($dir, @writers, %writer_seen);
291     foreach $dir (@INC) {
292         push @writers, glob("$dir/Text/Restructured/Writer/*.wrt")
293     }
294     @WRITERS = grep(! $writer_seen{$_}++,
295                     grep(s|.*/([^/]+)\.wrt$|$1|, @writers));
296     eval "use lib \$LIB; use Text::Restructured::PrestConfig;";
298     my @flags;
299     push @flags, '-T' if $Text::Restructured::PrestConfig::TAINT =~ /^y/i;
300     push @flags, '-d' if $ENV{DEBUG};
301     push @flags, '-MDevel::Cover=-db,../../cover_db,-silent,1,-summary,0'
302         if $ENV{COVER};
303     push @flags, q(-M-warnings) if $ENV{COVER};
304     $PERL_FLAGS = "@flags";
305     # Default version of PREST_FLAGS
306     $PREST_FLAGS = q(-D source='test data' -D xformoff='.*' -D align=0)
307         unless defined $PREST_FLAGS;
308     $PREST_FLAGS .= q( -D no_line_directives) if $ENV{DEBUG};
309     # Default version of PREST_WRT_FLAGS for dom
310     $PREST_WRT_FLAGS{dom} = '-W nobackn' unless defined $PREST_WRT_FLAGS{dom};
313 # Unpack the .py file if it exists
314 unpack:
315         { 
316             my @test_pys = <test_*.py>;
317             foreach (@test_pys) {
318                 execute "$PERL $EXTRACT_TESTS $EXTRACT_TEST_FLAGS $_";
319             }
320         }
322 find_oks:
323         {
324             @OKS = <*.rst>;
325             s/\.rst$/.ok/ foreach @OKS;
326         }
328 # Fault grading.  This is complicated because we can do either diff or
329 # rediff for checking and we can build the targets in a number of ways,
330 # depending upon which writer needs to be used, which depends upon
331 # what kinds of expects we have
332 %.ok:   {
333             my($maker, $target, $matches) = @_;
334             my $m = $matches->[0];
335             # Search for .xyz or .myxyz or .xyzre files for each of
336             # the different writers xyz
337             my @writers = grep(-f "$m.$_" || -f "$m.my$_" ||
338                                -f "$m.$_.re",
339                                (@WRITERS, qw(tex idx txt)));
340             my $writer = $writers[0];
341             die "Cannot find expect file for $m" unless defined $writer;
342             my $ext = -f "$m.r" || -f "$m.$writer.re" ? 're' : 'df';
343             ("$m.$writer.$ext", "$m.$writer.out");
344         }
345         # ---- ACTIONS ----
346         {
347             my($maker, $target, $deps, $matches) = @_;
348             my $is_diff = $deps->[0] =~ /\.df/;
349             my $diffre = "$PERL $TBIN/diffre";
350             my $prog = $is_diff ? 'diff' : $diffre;
351             execute "$prog @$deps[0..1] > $target";
352             if (! -z $target && $is_diff) {
353                 $deps->[0] =~ s/\.df$/.re/;
354                 $maker->make($deps->[0]);
355                 execute "$diffre @$deps[0..1] > $target";
356             }
357         }
359 # Make a straight diff file
360 %.%.df: { 
361             my($maker, $target, $matches) = @_;
362             my @exp = grep(-f $_, "$matches->[0].my$matches->[1]",
363                            "$matches->[0].$matches->[1]");
364             my @files;
365             push @files, "$matches->[0].stderr" if -f "$matches->[0].stderr";
366             push @files, $exp[0];
367             @files;
368         }
369         # ---- ACTIONS ----
370         cat $DEP0 $DEP1 > $TARGET
372 # Make a regular expression diff file
373 %.%.re: { 
374             my($maker, $target, $matches) = @_;
375             my @exp = grep(-f $_, "$matches->[0].my$matches->[1]",
376                            "$matches->[0].$matches->[1]");
377             my @files;
378             push @files, "$matches->[0].stderr" if -f "$matches->[0].stderr";
379             push @files, $exp[0] if $exp[0];
380             @files;
381         }
382         # ---- ACTIONS ----
383         {
384             my($maker, $target, $deps, $matches) = @_;
385             my $dep = $deps->[-1];
386             $dep =~ /\. (my)? ([^\.]+?) \z/x;
387             my $re = $2;
388             execute "$PERL $TBIN/re$re @$deps > $target";
389         }
391 # Run prest with the appropriate writer
392 # Anything after the ".out" causes the output to be generated without STDERR.
393 %.%.out%: {
394             # Add the prest script and the appropriate writer as dependencies
395             my($maker, $target, $matches) = @_;
396             my $writer = $EXT_TO_WRT{$matches->[1]} || $matches->[1];
397             my @deps;
398             # Make the .rst file a dependency if it exists (it usually does)
399             push @deps, "$matches->[0].rst" if -f "$matches->[0].rst";
400             # Add the prest script and perl modules to the dependencies
401             push @deps, ("$SCRIPT/prest",
402                          "$LIB/Text/Restructured/Writer/$writer.wrt");
403             push @deps, (<$LIB/Text/*.pm>, <$LIB/Text/Restructured/*.pm>,
404                          <$LIB/Text/Restructured/Directive/*.pm>);
405             @deps
406         }
407         # ---- ACTIONS ----
408         {
409             my($maker, $target, $deps, $matches) = @_;
410             my $m0 = $matches->[0];
411             my $writer = $EXT_TO_WRT{$matches->[1]} || $matches->[1];
412             # Note: since these are usually -I flags, the local ones come first
413             my @perl_flags =
414                 grep(defined $_, $PERL_FILE_FLAGS{$m0},
415                      $PERL_DIR_FLAGS, $PERL_SUITE_FLAGS,
416                      $PERL_WRT_FLAGS{$writer}, $PERL_FLAGS, );
417             my $prest_cmd = "$PERL @perl_flags -I $LIB $SCRIPT/prest";
418             my @flags =
419                 grep(defined $_, $PREST_FLAGS, $PREST_WRT_FLAGS{$writer},
420                      $PREST_SUITE_FLAGS, $PREST_DIR_FLAGS, 
421                      $PREST_FILE_FLAGS{$m0});
422             push @flags, "$m0.rst" if -f "$m0.rst";
423             my $redir    = $matches->[2] ? '' : '2>&1' ;
424             my $postproc = $POSTPROC_FILE{$m0} || '';
425             my $redirect = $ENV{DEBUG} ? '' : "> $target";
426             execute "$prest_cmd -w $writer @flags $redir $postproc |
427                      grep -v 'Wide character'$redirect";
428         }