File Coverage

blib/lib/Test/Harness.pm
Criterion Covered Total %
statement 172 183 93.9
branch 44 54 81.4
condition 34 42 80.9
subroutine 27 28 96.4
pod 2 2 100.0
total 279 309 90.2


line stmt bran cond sub pod time code
1             package Test::Harness;
2              
3 11     11   793547 use 5.006;
  11         51  
4              
5 11     11   86 use strict;
  11         30  
  11         296  
6 11     11   66 use warnings;
  11         26  
  11         794  
7              
8 11     11   80 use constant IS_WIN32 => ( $^O =~ /^(MS)?Win32$/ );
  11         407  
  11         1421  
9 11     11   82 use constant IS_VMS => ( $^O eq 'VMS' );
  11         27  
  11         680  
10              
11 11     11   6250 use TAP::Harness ();
  11         137  
  11         358  
12 11     11   5698 use TAP::Parser::Aggregator ();
  11         46  
  11         300  
13 11     11   5362 use TAP::Parser::Source ();
  11         34  
  11         422  
14 11     11   5436 use TAP::Parser::SourceHandler::Perl ();
  11         41  
  11         885  
15              
16 11     11   85 use Text::ParseWords qw(shellwords);
  11         27  
  11         681  
17              
18 11     11   72 use Config;
  11         30  
  11         445  
19 11     11   60 use base 'Exporter';
  11         25  
  11         1540  
20              
21             # $ML $Last_ML_Print
22              
23             BEGIN {
24 11     11   724 eval q{use Time::HiRes 'time'};
  11     11   73  
  11         26  
  11         96  
25 11         26162 our $has_time_hires = !$@;
26             }
27              
28             =head1 NAME
29              
30             Test::Harness - Run Perl standard test scripts with statistics
31              
32             =head1 VERSION
33              
34             Version 3.40_01
35              
36             =cut
37              
38             our $VERSION = '3.40_01';
39              
40             # Backwards compatibility for exportable variable names.
41             *verbose = *Verbose;
42             *switches = *Switches;
43             *debug = *Debug;
44              
45             $ENV{HARNESS_ACTIVE} = 1;
46             $ENV{HARNESS_VERSION} = $VERSION;
47              
48             END {
49              
50             # For VMS.
51 10     10   12235 delete $ENV{HARNESS_ACTIVE};
52 10         99 delete $ENV{HARNESS_VERSION};
53             }
54              
55             our @EXPORT = qw(&runtests);
56             our @EXPORT_OK = qw(&execute_tests $verbose $switches);
57              
58             our $Verbose = $ENV{HARNESS_VERBOSE} || 0;
59             our $Debug = $ENV{HARNESS_DEBUG} || 0;
60             our $Switches = '-w';
61             our $Columns = $ENV{HARNESS_COLUMNS} || $ENV{COLUMNS} || 80;
62             $Columns--; # Some shells have trouble with a full line of text.
63             our $Timer = $ENV{HARNESS_TIMER} || 0;
64             our $Color = $ENV{HARNESS_COLOR} || 0;
65             our $IgnoreExit = $ENV{HARNESS_IGNORE_EXIT} || 0;
66              
67             =head1 SYNOPSIS
68              
69             use Test::Harness;
70              
71             runtests(@test_files);
72              
73             =head1 DESCRIPTION
74              
75             Although, for historical reasons, the L distribution
76             takes its name from this module it now exists only to provide
77             L with an interface that is somewhat backwards compatible
78             with L 2.xx. If you're writing new code consider using
79             L directly instead.
80              
81             Emulation is provided for C and C but the
82             pluggable 'Straps' interface that previous versions of L
83             supported is not reproduced here. Straps is now available as a stand
84             alone module: L.
85              
86             See L, L for the main documentation for this
87             distribution.
88              
89             =head1 FUNCTIONS
90              
91             The following functions are available.
92              
93             =head2 runtests( @test_files )
94              
95             This runs all the given I<@test_files> and divines whether they passed
96             or failed based on their output to STDOUT (details above). It prints
97             out each individual test which failed along with a summary report and
98             a how long it all took.
99              
100             It returns true if everything was ok. Otherwise it will C with
101             one of the messages in the DIAGNOSTICS section.
102              
103             =cut
104              
105             sub _has_taint {
106 0     0   0 my $test = shift;
107 0         0 return TAP::Parser::SourceHandler::Perl->get_taint(
108             TAP::Parser::Source->shebang($test) );
109             }
110              
111             sub _aggregate {
112 35     35   129 my ( $harness, $aggregate, @tests ) = @_;
113              
114             # Don't propagate to our children
115 35         186 local $ENV{HARNESS_OPTIONS};
116              
117 35         162 _apply_extra_INC($harness);
118 35         134 _aggregate_tests( $harness, $aggregate, @tests );
119             }
120              
121             # Make sure the child sees all the extra junk in @INC
122             sub _apply_extra_INC {
123 35     35   80 my $harness = shift;
124              
125             $harness->callback(
126             parser_args => sub {
127 60     60   192 my ( $args, $test ) = @_;
128 60         129 push @{ $args->{switches} }, map {"-I$_"} _filtered_inc();
  60         352  
  421         1594  
129             }
130 35         291 );
131             }
132              
133             sub _aggregate_tests {
134 35     35   134 my ( $harness, $aggregate, @tests ) = @_;
135 35         316 $aggregate->start();
136 35         850 $harness->aggregate_tests( $aggregate, @tests );
137 35         263 $aggregate->stop();
138              
139             }
140              
141             sub runtests {
142 2     2 1 3020 my @tests = @_;
143              
144             # shield against -l
145 2         11 local ( $\, $, );
146              
147 2         13 my $harness = _new_harness();
148 2         18 my $aggregate = TAP::Parser::Aggregator->new();
149              
150 2 50       9 local $ENV{PERL_USE_UNSAFE_INC} = 1 if not exists $ENV{PERL_USE_UNSAFE_INC};
151 2         10 _aggregate( $harness, $aggregate, @tests );
152              
153 2         115 $harness->formatter->summary($aggregate);
154              
155 2         14 my $total = $aggregate->total;
156 2         21 my $passed = $aggregate->passed;
157 2         13 my $failed = $aggregate->failed;
158              
159 2         13 my @parsers = $aggregate->parsers;
160              
161 2         7 my $num_bad = 0;
162 2         12 for my $parser (@parsers) {
163 2 100       15 $num_bad++ if $parser->has_problems;
164             }
165              
166 2 100       48 die(sprintf(
167             "Failed %d/%d test programs. %d/%d subtests failed.\n",
168             $num_bad, scalar @parsers, $failed, $total
169             )
170             ) if $num_bad;
171              
172 1   33     173 return $total && $total == $passed;
173             }
174              
175             sub _canon {
176 19     19   148 my @list = sort { $a <=> $b } @_;
  10         42  
177 19         60 my @ranges = ();
178 19         47 my $count = scalar @list;
179 19         45 my $pos = 0;
180              
181 19         73 while ( $pos < $count ) {
182 19         53 my $end = $pos + 1;
183 19   100     118 $end++ while $end < $count && $list[$end] <= $list[ $end - 1 ] + 1;
184 19 100       99 push @ranges, ( $end == $pos + 1 )
185             ? $list[$pos]
186             : join( '-', $list[$pos], $list[ $end - 1 ] );
187 19         74 $pos = $end;
188             }
189              
190 19         425 return join( ' ', @ranges );
191             }
192              
193             sub _new_harness {
194 38   100 38   1408 my $sub_args = shift || {};
195              
196 38         114 my ( @lib, @switches );
197 38         207 my @opt = map { shellwords($_) } grep { defined } $Switches, $ENV{HARNESS_PERL_SWITCHES};
  46         1470  
  76         297  
198 38         4330 while ( my $opt = shift @opt ) {
199 53 100       300 if ( $opt =~ /^ -I (.*) $ /x ) {
200 2 100       22 push @lib, length($1) ? $1 : shift @opt;
201             }
202             else {
203 51         234 push @switches, $opt;
204             }
205             }
206              
207             # Do things the old way on VMS...
208 38         105 push @lib, _filtered_inc() if IS_VMS;
209              
210             # If $Verbose isn't numeric default to 1. This helps core.
211 38 50       192 my $verbosity = ( $Verbose ? ( $Verbose !~ /\d/ ) ? 1 : $Verbose : 0 );
    100          
212              
213 38         335 my $args = {
214             timer => $Timer,
215             directives => our $Directives,
216             lib => \@lib,
217             switches => \@switches,
218             color => $Color,
219             verbosity => $verbosity,
220             ignore_exit => $IgnoreExit,
221             };
222              
223             $args->{stdout} = $sub_args->{out}
224 38 50       173 if exists $sub_args->{out};
225              
226 38   100     362 my $class = $ENV{HARNESS_SUBCLASS} || 'TAP::Harness';
227 38 100       197 if ( defined( my $env_opt = $ENV{HARNESS_OPTIONS} ) ) {
228 1         4 for my $opt ( split /:/, $env_opt ) {
229 2 100       9 if ( $opt =~ /^j(\d*)$/ ) {
    50          
    0          
    0          
230 1   50     7 $args->{jobs} = $1 || 9;
231             }
232             elsif ( $opt eq 'c' ) {
233 1         3 $args->{color} = 1;
234             }
235             elsif ( $opt =~ m/^f(.*)$/ ) {
236 0         0 my $fmt = $1;
237 0         0 $fmt =~ s/-/::/g;
238 0         0 $args->{formatter_class} = $fmt;
239             }
240             elsif ( $opt =~ m/^a(.*)$/ ) {
241 0         0 my $archive = $1;
242 0         0 $class = "TAP::Harness::Archive";
243 0         0 $args->{archive} = $archive;
244             }
245             else {
246 0         0 die "Unknown HARNESS_OPTIONS item: $opt\n";
247             }
248             }
249             }
250              
251 38         816 return TAP::Harness->_construct( $class, $args );
252             }
253              
254             # Get the parts of @INC which are changed from the stock list AND
255             # preserve reordering of stock directories.
256             sub _filtered_inc {
257 62     62   426 my @inc = grep { !ref } @INC; #28567
  836         3186  
258              
259 62 50       546 if (IS_VMS) {
260              
261             # VMS has a 255-byte limit on the length of %ENV entries, so
262             # toss the ones that involve perl_root, the install location
263             @inc = grep !/perl_root/i, @inc;
264              
265             }
266 0         0 elsif (IS_WIN32) {
267              
268             # Lose any trailing backslashes in the Win32 paths
269 0         0 s/[\\\/]+$// for @inc;
270             }
271              
272 62         295 my @default_inc = _default_inc();
273              
274 62         218 my @new_inc;
275             my %seen;
276 62         301 for my $dir (@inc) {
277 835 100       4321 next if $seen{$dir}++;
278              
279 723 100 100     2283 if ( $dir eq ( $default_inc[0] || '' ) ) {
280 300         513 shift @default_inc;
281             }
282             else {
283 423         1102 push @new_inc, $dir;
284             }
285              
286 723   66     3203 shift @default_inc while @default_inc and $seen{ $default_inc[0] };
287             }
288              
289 62         637 return @new_inc;
290             }
291              
292             {
293              
294             # Cache this to avoid repeatedly shelling out to Perl.
295             my @inc;
296              
297             sub _default_inc {
298 62 100   62   565 return @inc if @inc;
299              
300 5         36 local $ENV{PERL5LIB};
301 5         22 local $ENV{PERLLIB};
302              
303 5   33     36 my $perl = $ENV{HARNESS_PERL} || $^X;
304              
305             # Avoid using -l for the benefit of Perl 6
306 5         38842 chomp( @inc = `"$perl" -e "print join qq[\\n], \@INC, q[]"` );
307 5         201 return @inc;
308             }
309             }
310              
311             sub _check_sequence {
312 58     58   286 my @list = @_;
313 58         120 my $prev;
314 58         201 while ( my $next = shift @list ) {
315 145 100 100     574 return if defined $prev && $next <= $prev;
316 143         429 $prev = $next;
317             }
318              
319 56         162 return 1;
320             }
321              
322             sub execute_tests {
323 33     33 1 131586 my %args = @_;
324              
325 33         228 my $harness = _new_harness( \%args );
326 33         272 my $aggregate = TAP::Parser::Aggregator->new();
327              
328 33         410 my %tot = (
329             bonus => 0,
330             max => 0,
331             ok => 0,
332             bad => 0,
333             good => 0,
334             files => 0,
335             tests => 0,
336             sub_skipped => 0,
337             todo => 0,
338             skipped => 0,
339             bench => undef,
340             );
341              
342             # Install a callback so we get to see any plans the
343             # harness executes.
344             $harness->callback(
345             made_parser => sub {
346 58     58   289 my $parser = shift;
347             $parser->callback(
348             plan => sub {
349 52         193 my $plan = shift;
350 52 100       240 if ( $plan->directive eq 'SKIP' ) {
351 4         28 $tot{skipped}++;
352             }
353             }
354 58         1640 );
355             }
356 33         374 );
357              
358 33 50       144 local $ENV{PERL_USE_UNSAFE_INC} = 1 if not exists $ENV{PERL_USE_UNSAFE_INC};
359 33         114 _aggregate( $harness, $aggregate, @{ $args{tests} } );
  33         153  
360              
361 33         1618 $tot{bench} = $aggregate->elapsed;
362 33         1248 my @tests = $aggregate->descriptions;
363              
364             # TODO: Work out the circumstances under which the files
365             # and tests totals can differ.
366 33         151 $tot{files} = $tot{tests} = scalar @tests;
367              
368 33         95 my %failedtests = ();
369 33         93 my %todo_passed = ();
370              
371 33         154 for my $test (@tests) {
372 58         281 my ($parser) = $aggregate->parsers($test);
373              
374 58         255 my @failed = $parser->failed;
375              
376 58         226 my $wstat = $parser->wait;
377 58         220 my $estat = $parser->exit;
378 58         218 my $planned = $parser->tests_planned;
379 58         244 my @errors = $parser->parse_errors;
380 58         235 my $passed = $parser->passed;
381 58         288 my $actual_passed = $parser->actual_passed;
382              
383 58         227 my $ok_seq = _check_sequence( $parser->actual_passed );
384              
385             # Duplicate exit, wait status semantics of old version
386 58 100 50     419 $estat ||= '' unless $wstat;
387 58   100     289 $wstat ||= '';
388              
389 58   100     247 $tot{max} += ( $planned || 0 );
390 58         534 $tot{bonus} += $parser->todo_passed;
391 58 100       221 $tot{ok} += $passed > $actual_passed ? $passed : $actual_passed;
392 58         249 $tot{sub_skipped} += $parser->skipped;
393 58         218 $tot{todo} += $parser->todo;
394              
395 58 100 100     458 if ( @failed || $estat || @errors ) {
      100        
396 25         74 $tot{bad}++;
397              
398 25 100       94 my $huh_planned = $planned ? undef : '??';
399 25 100       88 my $huh_errors = $ok_seq ? undef : '??';
400              
401 25   100     333 $failedtests{$test} = {
      100        
      66        
402             'canon' => $huh_planned
403             || $huh_errors
404             || _canon(@failed)
405             || '??',
406             'estat' => $estat,
407             'failed' => $huh_planned
408             || $huh_errors
409             || scalar @failed,
410             'max' => $huh_planned || $planned,
411             'name' => $test,
412             'wstat' => $wstat
413             };
414             }
415             else {
416 33         101 $tot{good}++;
417             }
418              
419 58         257 my @todo = $parser->todo_passed;
420 58 100       294 if (@todo) {
421 2         18 $todo_passed{$test} = {
422             'canon' => _canon(@todo),
423             'estat' => $estat,
424             'failed' => scalar @todo,
425             'max' => scalar $parser->todo,
426             'name' => $test,
427             'wstat' => $wstat
428             };
429             }
430             }
431              
432 33         3553 return ( \%tot, \%failedtests, \%todo_passed );
433             }
434              
435             =head2 execute_tests( tests => \@test_files, out => \*FH )
436              
437             Runs all the given C<@test_files> (just like C) but
438             doesn't generate the final report. During testing, progress
439             information will be written to the currently selected output
440             filehandle (usually C), or to the filehandle given by the
441             C parameter. The I is optional.
442              
443             Returns a list of two values, C<$total> and C<$failed>, describing the
444             results. C<$total> is a hash ref summary of all the tests run. Its
445             keys and values are this:
446              
447             bonus Number of individual todo tests unexpectedly passed
448             max Number of individual tests ran
449             ok Number of individual tests passed
450             sub_skipped Number of individual tests skipped
451             todo Number of individual todo tests
452              
453             files Number of test files ran
454             good Number of test files passed
455             bad Number of test files failed
456             tests Number of test files originally given
457             skipped Number of test files skipped
458              
459             If C<< $total->{bad} == 0 >> and C<< $total->{max} > 0 >>, you've
460             got a successful test.
461              
462             C<$failed> is a hash ref of all the test scripts that failed. Each key
463             is the name of a test script, each value is another hash representing
464             how that script failed. Its keys are these:
465              
466             name Name of the test which failed
467             estat Script's exit value
468             wstat Script's wait status
469             max Number of individual tests
470             failed Number which failed
471             canon List of tests which failed (as string).
472              
473             C<$failed> should be empty if everything passed.
474              
475             =cut
476              
477             1;
478             __END__