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