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   160154 use 5.006;
  11         29  
4              
5 11     11   43 use strict;
  11         19  
  11         225  
6 11     11   33 use warnings;
  11         10  
  11         622  
7              
8 11     11   45 use constant IS_WIN32 => ( $^O =~ /^(MS)?Win32$/ );
  11         13  
  11         1021  
9 11     11   47 use constant IS_VMS => ( $^O eq 'VMS' );
  11         17  
  11         518  
10              
11 11     11   4015 use TAP::Harness ();
  11         20  
  11         195  
12 11     11   4079 use TAP::Parser::Aggregator ();
  11         23  
  11         202  
13 11     11   4059 use TAP::Parser::Source ();
  11         16  
  11         184  
14 11     11   3749 use TAP::Parser::SourceHandler::Perl ();
  11         23  
  11         303  
15              
16 11     11   52 use Text::ParseWords qw(shellwords);
  11         13  
  11         453  
17              
18 11     11   40 use Config;
  11         14  
  11         357  
19 11     11   35 use base 'Exporter';
  11         12  
  11         817  
20              
21             # $ML $Last_ML_Print
22              
23             BEGIN {
24 11     11   641 eval q{use Time::HiRes 'time'};
  11     11   49  
  11         14  
  11         82  
25 11         19117 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.39
35              
36             =cut
37              
38             our $VERSION = '3.39';
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   4255 delete $ENV{HARNESS_ACTIVE};
52 10         62 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   59 my ( $harness, $aggregate, @tests ) = @_;
113              
114             # Don't propagate to our children
115 35         105 local $ENV{HARNESS_OPTIONS};
116              
117 35         88 _apply_extra_INC($harness);
118 35         88 _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   38 my $harness = shift;
124              
125             $harness->callback(
126             parser_args => sub {
127 60     60   65 my ( $args, $test ) = @_;
128 60         45 push @{ $args->{switches} }, map {"-I$_"} _filtered_inc();
  60         232  
  421         790  
129             }
130 35         181 );
131             }
132              
133             sub _aggregate_tests {
134 35     35   52 my ( $harness, $aggregate, @tests ) = @_;
135 35         131 $aggregate->start();
136 35         493 $harness->aggregate_tests( $aggregate, @tests );
137 35         202 $aggregate->stop();
138              
139             }
140              
141             sub runtests {
142 2     2 1 959 my @tests = @_;
143              
144             # shield against -l
145 2         6 local ( $\, $, );
146              
147 2         6 my $harness = _new_harness();
148 2         12 my $aggregate = TAP::Parser::Aggregator->new();
149              
150 2 50       7 local $ENV{PERL_USE_UNSAFE_INC} = 1 if not exists $ENV{PERL_USE_UNSAFE_INC};
151 2         6 _aggregate( $harness, $aggregate, @tests );
152              
153 2         58 $harness->formatter->summary($aggregate);
154              
155 2         11 my $total = $aggregate->total;
156 2         15 my $passed = $aggregate->passed;
157 2         7 my $failed = $aggregate->failed;
158              
159 2         7 my @parsers = $aggregate->parsers;
160              
161 2         3 my $num_bad = 0;
162 2         5 for my $parser (@parsers) {
163 2 100       10 $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     117 return $total && $total == $passed;
173             }
174              
175             sub _canon {
176 19     19   73 my @list = sort { $a <=> $b } @_;
  10         21  
177 19         26 my @ranges = ();
178 19         21 my $count = scalar @list;
179 19         33 my $pos = 0;
180              
181 19         48 while ( $pos < $count ) {
182 19         31 my $end = $pos + 1;
183 19   100     78 $end++ while $end < $count && $list[$end] <= $list[ $end - 1 ] + 1;
184 19 100       55 push @ranges, ( $end == $pos + 1 )
185             ? $list[$pos]
186             : join( '-', $list[$pos], $list[ $end - 1 ] );
187 19         38 $pos = $end;
188             }
189              
190 19         301 return join( ' ', @ranges );
191             }
192              
193             sub _new_harness {
194 38   100 38   522 my $sub_args = shift || {};
195              
196 38         83 my ( @lib, @switches );
197 38         118 my @opt = map { shellwords($_) } grep { defined } $Switches, $ENV{HARNESS_PERL_SWITCHES};
  46         1213  
  76         183  
198 38         2736 while ( my $opt = shift @opt ) {
199 53 100       192 if ( $opt =~ /^ -I (.*) $ /x ) {
200 2 100       10 push @lib, length($1) ? $1 : shift @opt;
201             }
202             else {
203 51         146 push @switches, $opt;
204             }
205             }
206              
207             # Do things the old way on VMS...
208 38         54 push @lib, _filtered_inc() if IS_VMS;
209              
210             # If $Verbose isn't numeric default to 1. This helps core.
211 38 50       123 my $verbosity = ( $Verbose ? ( $Verbose !~ /\d/ ) ? 1 : $Verbose : 0 );
    100          
212              
213 38         247 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       138 if exists $sub_args->{out};
225              
226 38   100     262 my $class = $ENV{HARNESS_SUBCLASS} || 'TAP::Harness';
227 38 100       112 if ( defined( my $env_opt = $ENV{HARNESS_OPTIONS} ) ) {
228 1         5 for my $opt ( split /:/, $env_opt ) {
229 2 100       7 if ( $opt =~ /^j(\d*)$/ ) {
    50          
    0          
    0          
230 1   50     5 $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         341 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   129 my @inc = grep { !ref } @INC; #28567
  836         1452  
258              
259 62 50       327 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         162 my @default_inc = _default_inc();
273              
274 62         99 my @new_inc;
275             my %seen;
276 62         189 for my $dir (@inc) {
277 835 100       1975 next if $seen{$dir}++;
278              
279 723 100 100     1180 if ( $dir eq ( $default_inc[0] || '' ) ) {
280 300         236 shift @default_inc;
281             }
282             else {
283 423         428 push @new_inc, $dir;
284             }
285              
286 723   66     2193 shift @default_inc while @default_inc and $seen{ $default_inc[0] };
287             }
288              
289 62         337 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   270 return @inc if @inc;
299              
300 5         31 local $ENV{PERL5LIB};
301 5         16 local $ENV{PERLLIB};
302              
303 5   33     24 my $perl = $ENV{HARNESS_PERL} || $^X;
304              
305             # Avoid using -l for the benefit of Perl 6
306 5         21087 chomp( @inc = `"$perl" -e "print join qq[\\n], \@INC, q[]"` );
307 5         140 return @inc;
308             }
309             }
310              
311             sub _check_sequence {
312 58     58   103 my @list = @_;
313 58         61 my $prev;
314 58         153 while ( my $next = shift @list ) {
315 145 100 100     394 return if defined $prev && $next <= $prev;
316 143         228 $prev = $next;
317             }
318              
319 56         107 return 1;
320             }
321              
322             sub execute_tests {
323 33     33 1 55459 my %args = @_;
324              
325 33         145 my $harness = _new_harness( \%args );
326 33         195 my $aggregate = TAP::Parser::Aggregator->new();
327              
328 33         248 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   114 my $parser = shift;
347             $parser->callback(
348             plan => sub {
349 52         59 my $plan = shift;
350 52 100       171 if ( $plan->directive eq 'SKIP' ) {
351 4         13 $tot{skipped}++;
352             }
353             }
354 58         753 );
355             }
356 33         220 );
357              
358 33 50       96 local $ENV{PERL_USE_UNSAFE_INC} = 1 if not exists $ENV{PERL_USE_UNSAFE_INC};
359 33         57 _aggregate( $harness, $aggregate, @{ $args{tests} } );
  33         96  
360              
361 33         907 $tot{bench} = $aggregate->elapsed;
362 33         643 my @tests = $aggregate->descriptions;
363              
364             # TODO: Work out the circumstances under which the files
365             # and tests totals can differ.
366 33         96 $tot{files} = $tot{tests} = scalar @tests;
367              
368 33         68 my %failedtests = ();
369 33         49 my %todo_passed = ();
370              
371 33         86 for my $test (@tests) {
372 58         208 my ($parser) = $aggregate->parsers($test);
373              
374 58         123 my @failed = $parser->failed;
375              
376 58         126 my $wstat = $parser->wait;
377 58         110 my $estat = $parser->exit;
378 58         96 my $planned = $parser->tests_planned;
379 58         110 my @errors = $parser->parse_errors;
380 58         148 my $passed = $parser->passed;
381 58         142 my $actual_passed = $parser->actual_passed;
382              
383 58         127 my $ok_seq = _check_sequence( $parser->actual_passed );
384              
385             # Duplicate exit, wait status semantics of old version
386 58 100 50     229 $estat ||= '' unless $wstat;
387 58   100     149 $wstat ||= '';
388              
389 58   100     128 $tot{max} += ( $planned || 0 );
390 58         119 $tot{bonus} += $parser->todo_passed;
391 58 100       104 $tot{ok} += $passed > $actual_passed ? $passed : $actual_passed;
392 58         121 $tot{sub_skipped} += $parser->skipped;
393 58         108 $tot{todo} += $parser->todo;
394              
395 58 100 100     287 if ( @failed || $estat || @errors ) {
      100        
396 25         39 $tot{bad}++;
397              
398 25 100       61 my $huh_planned = $planned ? undef : '??';
399 25 100       43 my $huh_errors = $ok_seq ? undef : '??';
400              
401 25   100     238 $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         39 $tot{good}++;
417             }
418              
419 58         129 my @todo = $parser->todo_passed;
420 58 100       161 if (@todo) {
421 2         13 $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         2161 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__