File Coverage

blib/lib/XAO/TestUtils.pm
Criterion Covered Total %
statement 83 140 59.2
branch 17 64 26.5
condition 2 17 11.7
subroutine 13 17 76.4
pod 2 4 50.0
total 117 242 48.3


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             XAO::TestUtils - testing framework for XAO modules
4              
5             =head1 SYNOPSIS
6              
7             In your Makefile.PL:
8              
9             test::
10             \$(PERL) -MXAO::TestUtils=xao_all_tests \\
11             -e'xao_all_tests("XAO::testcases::FS")'
12              
13             =head1 DESCRIPTION
14              
15             This module is intended for use only in testing of XAO modules and
16             modules based on XAO.
17              
18             For instance XAO::FS installs a set of tests in system perl
19             directories. XAO::TestUtils and these tests can then be used for testing
20             third party database drivers against this standard set of tests.
21              
22             Method details:
23              
24             =over
25              
26             =cut
27              
28             ###############################################################################
29             package XAO::TestUtils;
30 1     1   122986 use strict;
  1         2  
  1         40  
31 1     1   506 use XAO::Utils;
  1         2  
  1         76  
32 1     1   6 use File::Path;
  1         1  
  1         39  
33 1     1   3 use File::Basename;
  1         2  
  1         55  
34 1     1   380 use File::Copy;
  1         4136  
  1         50  
35 1     1   6 use File::Find;
  1         1  
  1         36  
36 1     1   599 use ExtUtils::Manifest qw(fullcheck maniread);
  1         4454  
  1         70  
37 1     1   488 use Test::Harness;
  1         36484  
  1         127  
38              
39             require Exporter;
40              
41 1     1   33 use vars qw(@ISA @EXPORT_OK @EXPORT $VERSION);
  1         1  
  1         1630  
42              
43             @ISA=qw(Exporter);
44             @EXPORT_OK=qw(xao_test_all xao_test
45             xao_mf_fix_permissions xao_mf_check_consistency
46             );
47             @EXPORT=();
48              
49             $VERSION=(0+sprintf('%u.%03u',(q$Id: TestUtils.pm,v 2.2 2006/04/22 01:57:44 am Exp $ =~ /\s(\d+)\.(\d+)\s/))) || die "Bad VERSION";
50              
51             ###############################################################################
52              
53             =item xao_test_all ($;@)
54              
55             Runs all tests for a given list of namespaces in random order. As a
56             special case if first argument is an integer it turns debug output on
57             using XAO::Utils set_debug() method.
58              
59             Can be called from command line:
60              
61             perl -MXAO::TestUtils=xao_test_all -e'xao_test_all(1,"testcases")'
62              
63             Test execution is the same as for run_tests() method, see below.
64              
65             =cut
66              
67             sub xao_test_all (;$@) {
68 1 50   1 1 46 XAO::Utils::set_debug(shift @_) if $_[0]=~/^\d+$/;
69              
70 1         1 my %tests;
71              
72             my $diradd=sub {
73 1     1   3 my ($dir,$namespace)=@_;
74 1 50       31 opendir(D,"$dir") || die "Can't open directory $dir: $!\n";
75 1         21 while(my $file=readdir(D)) {
76 11 100 66     24 next if $file eq 'base.pm' || $file eq 'Common';
77 10 100       21 next unless $file =~ /^(.*)\.pm$/;
78 8         25 $tests{$namespace . $1}=1;
79             }
80 1         78 closedir(D);
81 1         4 };
82              
83             # A namespace allows packages to pre-define test cases and for
84             # another package to reuse them (for example storage drivers for
85             # XAO::FS reuse the tests defined in XAO::FS).
86             #
87 1         2 foreach my $namespace (@_) {
88              
89             # Scanning @INC to find directory holding these tests
90             #
91 1         4 (my $namedir=$namespace)=~s/::/\//g;
92 1         1 foreach my $dir (@INC) {
93 8 50       359 next unless -d "$dir/$namedir";
94 0         0 $diradd->("$dir/$namedir",$namespace.'::');
95             }
96             }
97              
98             # More common is to have tests in t/testcases directory
99             #
100 1 50       20 if(-d 't/testcases') {
101             find({
102             no_chdir => 1,
103             wanted => sub {
104 10 100   10   130 return unless -d $File::Find::name;
105 1         4 (my $cp=$File::Find::name)=~s|/|::|g;
106 1         2 $cp=~s/^t:://;
107 1         2 $diradd->($File::Find::name,$cp.'::');
108             },
109 1         107 },'t/testcases');
110             }
111              
112             # Randomizing tests list order to make sure that tests do not depend on
113             # each other.
114             #
115 1         8 my @tests=keys %tests;
116 1         3 for(my $i=0; $i!=@tests; $i++) {
117 8         39 push(@tests,splice(@tests,rand(@tests),1));
118             }
119              
120 1         8 dprint "Tests: ".join(',',@tests);
121              
122 1         2 xao_test(@tests);
123             }
124              
125             ###############################################################################
126              
127             =item xao_test (@)
128              
129             Runs given tests in the given sequence. Tests are given as corresponding
130             unit package names. Example:
131              
132             xao_test('Basic','XAO::testcases::FS::Lists');
133              
134             It will create 'ta' directory in the current directory and will
135             store two files for each test case in there - one suitable for 'make
136             test' with '.t' extension and one for manual checking with debug
137             output enabled and in different human-readable output mode with '.pl'
138             extension. At a later time these tests can be individually re-run
139             manually using simply 'perl ta/testname.pl' command.
140              
141             Common prefix will be automatically removed from files.
142              
143             =cut
144              
145             sub xao_test (@) {
146 1     1 1 1 my $testdir='t';
147              
148 1 50       9 -d $testdir || mkdir "$testdir",0755 ||
149             die "Can't create '$testdir' directory: $!\n";
150              
151 1         1 my $prefix_count;
152             my $prefix;
153 1         2 foreach my $test (@_) {
154 8         10 my @p=split(/::/,$test);
155 8 100       9 if(defined $prefix) {
156 7         8 while($prefix_count) {
157 0         0 my $np=join('::',@p[0..$prefix_count]);
158 0 0 0     0 last if length($np) <= length($prefix) &&
159             $np eq substr($prefix,0,length($np));
160 0         0 $prefix_count--;
161             }
162             }
163             else {
164 1         1 $prefix_count=scalar(@p)-2;
165             }
166 8 50       9 last if $prefix_count<0;
167 8         13 $prefix=join('::',@p[0..$prefix_count]);
168             }
169              
170 1         2 $prefix_count++;
171 1         1 my %fnames;
172 1         2 foreach my $test (@_) {
173 8         20 my @p=split(/::/,$test);
174 8         20 my $testfile=join('_',@p[$prefix_count..$#p]);
175 8         21 $fnames{$test}=$testfile;
176 8         25 dprint "Test: $test file=$testfile";
177              
178             # Non-standard name (.xt vs .t) to avoid double executing by the
179             # standard build code.
180             #
181 8         8 my $tatest="$testdir/$testfile.xt";
182 8 50       1172 open(F,"> $tatest") || die "Can't create test script ($tatest): $!\n";
183 8         37 print F <
184             #!$^X
185             #### GENERATED AUTOMATICALLY, DO NOT EDIT ####
186             use strict;
187             use warnings;
188             use Test::Unit::Lite;
189              
190             unshift(\@INC,'t') if -d 't';
191              
192             my \$r=Test::Unit::HarnessUnit->new();
193             \$r->start('$test');
194             #### GENERATED AUTOMATICALLY, DO NOT EDIT ####
195             EOT
196 8         252 close(F);
197              
198             # Human friendlier output version
199             #
200 8 50       100 my $use_blib = -d 'blib' ? "\nuse blib;" : '';
201 8         13 my $pltest="$testdir/$testfile.pl";
202 8 50       2122 open(F,"> $pltest") || die "Can't create test script ($pltest): $!\n";
203 8         43 print F <
204             #!$^X
205             #### GENERATED AUTOMATICALLY, DO NOT EDIT ####
206             use strict;
207             use warnings;$use_blib
208             use Test::Unit::Lite;
209             use XAO::Utils;
210              
211             XAO::Utils::set_debug(1);
212              
213             unshift(\@INC,'t') if -d 't';
214              
215             my \$r=Test::Unit::TestRunner->new();
216             \$r->start('$test');
217             print "\\n";
218             #### GENERATED AUTOMATICALLY, DO NOT EDIT ####
219             EOT
220 8         210 close(F);
221              
222 8         99 chmod 0755, '$testdir/$testfile.pl';
223             }
224              
225             # Executing tests
226             #
227 1         18 print <<'END_OF_WARNING';
228             ===========================================================
229             If you see that a test failed, please run it as follows:
230              
231             perl t/failed_test_name.pl
232              
233             Send the output to the module author along with your perl
234             version and a note of what you think might be the reason.
235             -----------------------------------------------------------
236             END_OF_WARNING
237              
238             ### dprint join(",",(map { "$testdir/$fnames{$_}.t" } @_));
239              
240 1         3 runtests(map { "$testdir/$fnames{$_}.xt" } @_);
  8         23  
241              
242 1         13037469 print <
243             ===========================================================
244             EOT
245             }
246              
247             ###############################################################################
248              
249             sub xao_mf_check_consistency {
250 0 0   0 0   die "Must have MANIFEST in the current directory\n" unless -r 'MANIFEST';
251 0           my ($missing,$extra)=fullcheck();
252 0 0 0       if($missing && @$missing) {
253 0           die "There are missing files, aborting!\n";
254             }
255 0 0 0       if($extra && @$extra) {
256 0           warn "There are some new files, add them to the MANIFEST!\n";
257             }
258             }
259              
260             ###############################################################################
261              
262             sub xao_mf_fix_permissions {
263 0 0   0 0   die "Must have MANIFEST in the current directory\n" unless -r 'MANIFEST';
264              
265 0           my @skip;
266 0 0         if(open('F','MANIFEST.SKIP')) {
267 0           while() {
268 0 0         next unless /^(\S+)(\s*.*)?$/;
269 0           my $regex=$1;
270 0           push(@skip,qr/$regex/);
271             }
272 0           close(F);
273              
274             }
275              
276 0           my @modes;
277 0 0         if(open('F','MANIFEST.MODES')) {
278 0           while() {
279 0 0         next unless /^([0-7]+)\s+([0-7]+)\s+(.*?)\s*$/;
280 0           my $dirmode=oct($1);
281 0           my $filemode=oct($2);
282 0           my $regex=$3;
283              
284 0 0         warn "Strange dirmode $dirmode for $regex\n"
285             if ($dirmode&0500) != 0500;
286 0 0         warn "Strange filemode $filemode for $regex\n"
287             if ($filemode&0400) != 0400;
288              
289 0           push(@modes,{
290             regex => qr/$regex/,
291             filemode => $filemode,
292             dirmode => $dirmode,
293             });
294             }
295 0           close(F);
296             }
297              
298             find({
299             no_chdir => 1,
300             preprocess => sub {
301 0     0     my @list;
302 0           foreach my $fn (@_) {
303 0           my $file=$File::Find::dir . '/' . $fn;
304 0           $file=~s/^.\/(.*)$/$1/;
305              
306 0 0         next if $file =~ /(^|\/)(\.|\.\.)/;
307 0 0         if(grep { $file =~ $_ } @skip) {
  0            
308 0           dprint "Skipping $file";
309 0           next;
310             }
311              
312 0           push(@list,$fn);
313             }
314 0           return @list;
315             },
316             wanted => sub {
317 0     0     my $file=$File::Find::name;
318 0           $file=~s/^\.\/(.*)$/$1/;
319 0 0 0       die "Wrong file path '$file'" if $file =~ /^\// || $file =~ /\.\.\//;
320              
321 0           my $perm;
322 0           foreach my $ml (@modes) {
323 0 0         if($file =~ $ml->{'regex'}) {
324 0           dprint "Permission override for $file";
325 0           $perm=$ml;
326 0           last;
327             }
328             }
329             $perm||={
330 0   0       filemode => 0644,
331             dirmode => 0755,
332             };
333              
334 0 0         die "Can't stat $file\n" unless stat($file);
335              
336 0 0         my $newperm=-d _ ? $perm->{'dirmode'} : $perm->{'filemode'};
337 0           my $oldperm=((stat(_))[2]) & 07777;
338              
339 0 0         if($oldperm != $newperm) {
340 0           printf STDERR "Setting %s from %04o to %04o\n",$file,$oldperm,$newperm;
341 0 0         chmod($newperm,$file) ||
342             die "Can't change $file to ".sprintf('%04o',$newperm).": $!\n";
343             }
344             },
345 0           },'.');
346             }
347              
348             ###############################################################################
349             1;
350             __END__