File Coverage

blib/lib/Slay/Makefile/Gress.pm
Criterion Covered Total %
statement 31 33 93.9
branch n/a
condition n/a
subroutine 11 11 100.0
pod n/a
total 42 44 95.4


line stmt bran cond sub pod time code
1             package Slay::Makefile::Gress;
2              
3 3     3   2311 use Exporter;
  3         6  
  3         307  
4 3     3   17 use base Exporter;
  3         6  
  3         424  
5             @Slay::Makefile::Gress::EXPORT_OK = qw(do_tests);
6              
7 3     3   17 use strict;
  3         11  
  3         123  
8 3     3   214 use warnings;
  3         6  
  3         137  
9              
10 3     3   16 use File::Path qw(rmtree);
  3         4  
  3         349  
11 3     3   4065 use File::Copy::Recursive qw(dircopy);
  3         44924  
  3         390  
12              
13 3     3   6380 use Test::More;
  3         63623  
  3         33  
14 3     3   1369 use Carp;
  3         7  
  3         242  
15              
16 3     3   2937 use FindBin;
  3         3392  
  3         207  
17 3     3   2664 use lib "$FindBin::RealBin/../../tbin";
  3         2218  
  3         23  
18 3     3   5621 use Slay::Makefile 0.02;
  0            
  0            
19              
20             our $VERSION = "0.08";
21              
22             # $Id$
23              
24             =head1 NAME
25              
26             Slay::Makefile::Gress - Use Slay::Makefile for software regression testing
27              
28             =head1 DESCRIPTION
29              
30             This module provides support for running a set of regression tests
31             from a .t file by doing builds with a C file.
32              
33             =head1 USAGE
34              
35             To use this module, the .t file calling it should contain something like:
36              
37             use Slay::Makefile::Gress qw(do_tests);
38             do_tests("SlayMakefile", @ARGV);
39              
40             =head1 OVERVIEW
41              
42             The basic functionality of this module is to take an initialization
43             subdirectory (by default ending ".init") and copy it over to a run
44             directory (by default ending ".dir"), doing a C into the run
45             directory, and then parsing a C to define and run the
46             tests. A reasonable way to accomplish this is to have the
47             C create a file with extension ".ok" that is empty if
48             the test passes; frequently this file can be the output of a diff
49             between the test's output and an expect file. The C
50             can be in a parent directory so it can be shared by more than one
51             suite of tests (.t file). A shared C can use the
52             include mechanism to bring in a local C files in the
53             run directory. You can even use this methodology for developing
54             families of suites of tests, etc.
55              
56             =head1 ROUTINES
57              
58             =over
59              
60             =item C
61              
62             Runs a series of tests using C<$makefile> as the C
63             input. If C<@tests> are specified, it contains the list of tests,
64             each of which is a target that is built in order; otherwise the
65             dependencies of the C target will be built as the list of tests.
66             The following options are recognized:
67              
68             =over
69              
70             =item init
71              
72             The extension for the initialization directory. Default is '.init'.
73              
74             =item opts
75              
76             A hash reference to be passed to Slay::Makefile::new as its options list.
77              
78             =item pretest
79              
80             The name of the target to be built prior to running tests to set
81             everything up. Default is 'pretest'.
82              
83             =item run
84              
85             The extension for the run directory. Default is '.run'.
86              
87             =item skip
88              
89             The name for perl scripts to run to check whether all tests should be
90             skipped. The name is also used as an extension for a test's base name
91             to see if an individual tests should be skipped.
92              
93             =item test
94              
95             The name of the target whose dependencies gives the list of tests.
96             Default is 'test'.
97              
98             =back
99              
100             =back
101              
102             =head1 PROCESSING
103              
104             Processing proceeds by the following steps:
105              
106             =over
107              
108             =item 1.
109              
110             Search for an initialization directory with the same base name as the
111             .t file invoking C and extension equal to the C
112             option. Croaks if there is no such directory. For example, if the file
113             invoking c is C and the default initialization
114             extension is used, it looks for directory C.
115              
116             =item 2.
117              
118             Copy the initialization directory to a run directory and C into that
119             directory.
120              
121             =item 3.
122              
123             Check for a script with the name of the C option. If it exists,
124             execute it. If it returns a non-zero exit code, skip all the tests.
125             The text this script prints becomes the reason for skipping the tests.
126              
127             =item 4.
128              
129             Use C to parse the C<$makefile> file. Note that the
130             working directory is the run directory when this file is processed.
131              
132             =item 5.
133              
134             Do a C of the C target, if it exists.
135             The name of the C target is 'pretest' unless specified in the
136             options.
137              
138             =item 6.
139              
140             If C<@tests> is empty, create a list of tests to execute by getting
141             the dependencies of the C target. The name of the C
142             target is 'test' unless specified in the options.
143              
144             =item 7.
145              
146             For each test C,
147              
148             =over
149              
150             =item a.
151              
152             Check for a script with the same base name as C and the extension
153             C<.> and the name of the C option.. For example, if the default
154             value of the C option is used, then a test C would
155             use a script called C. If the script exists, execute
156             it and skip the test if it returns a non-zero exit code. The text
157             this script prints becomes the reason for skipping the test.
158              
159             =item b.
160              
161             Run C for target C.
162              
163             =item c.
164              
165             If no file C was generated, report a failed test as failing to
166             build the file. If C was generated, then it should be empty for a
167             passing test. Any text in the file is returned as the reason for the
168             tests's failure.
169              
170             =back
171              
172             =back
173              
174             =cut
175              
176             sub do_tests {
177             my ($makefile, @tests) = @_;
178              
179             # Get the options
180             my $options = pop @tests if ref($tests[-1]) eq 'HASH';
181             $options = {} unless $options;
182             $options->{init} ||= '.init';
183             $options->{opts} ||= { strict => 1 } ;
184             $options->{pretest} ||= 'pretest';
185             $options->{run} ||= '.run';
186             $options->{skip} ||= 'skip.pl';
187             $options->{test} ||= 'test';
188              
189             my $base = $FindBin::RealBin;
190             my ($myname) = $FindBin::RealScript =~ /(.*)\.t$/;
191             chdir $base;
192             my $init = "$myname$options->{init}";
193             croak "Error: No initialization directory '$init' for this test\n"
194             unless -d $init;
195             my $top = `pwd`;
196             chomp $top;
197             $top =~ s!/[^/]*$!! while $top && ! -d "$top/blib/lib";
198             my $use_lib = $top ? "-I$top/blib/lib" : '';
199              
200             # First create the run subdirectory for doing testing
201             my $run = "$myname$options->{run}";
202              
203             rmtree $run if -d $run;
204             dircopy $init, $run;
205              
206             chdir $run;
207              
208             # Check to see if we need to skip all tests
209             my $skip = $options->{skip};
210             if (-f $skip) {
211             chomp (my $error = `$^X $use_lib $skip 2>&1`);
212             plan(skip_all => "$error") if $?;
213             }
214              
215             my $sm = Slay::Makefile->new($options->{opts});
216              
217             # Have carp within Slay::Makefile trust Slay::Makefile::Gress
218             local @Slay::Makefile::CARP_NOT;
219             push @Slay::Makefile::CARP_NOT, 'Slay::Makefile::Gress';
220             my $errs = $sm->parse($makefile);
221              
222             # Run the pretest target, if any
223             eval { $sm->make($options->{pretest}); };
224              
225             # Get list of targets
226             if (! @tests) {
227             $sm->maker->check_targets($options->{test});
228             my ($rule, $deps, $matches) =
229             $sm->maker->get_rule_info($options->{test});
230             @tests = defined $rule && $deps ? @$deps : () ;
231             }
232             plan tests => 0+@tests;
233             TEST:
234             foreach my $test (@tests) {
235             (my $base_test = $test) =~ s/\. [^\.]+ \z//x;
236             if (-f "$base_test.$skip") {
237             # Check whether we need to skip this file
238             chomp (my $error = `$^X $use_lib $base_test.$skip 2>&1`);
239             SKIP:
240             {
241             skip($error, 1) if $?;
242             }
243             next TEST if $?
244             }
245             $sm->make($test);
246             my $ok = -r $test ? `cat $test` : "Failed to build $test";
247             is ($ok, '', $test);
248             }
249             }
250              
251             1;