File Coverage

blib/lib/Test/Harness/Straps.pm
Criterion Covered Total %
statement 200 215 93.0
branch 80 100 80.0
condition 23 39 58.9
subroutine 30 31 96.7
pod 3 6 50.0
total 336 391 85.9


line stmt bran cond sub pod time code
1             # -*- Mode: cperl; cperl-indent-level: 4 -*-
2             package Test::Harness::Straps;
3              
4 5     5   108011 use strict;
  5         14  
  5         221  
5 5     5   27 use vars qw($VERSION);
  5         10  
  5         297  
6             $VERSION = '0.30';
7              
8 5     5   29 use Config;
  5         20  
  5         210  
9 5     5   2768 use Test::Harness::Assert;
  5         13  
  5         339  
10 5     5   2828 use Test::Harness::Iterator;
  5         15  
  5         138  
11 5     5   1982 use Test::Harness::Point;
  5         9  
  5         130  
12 5     5   2665 use Test::Harness::Results;
  5         12  
  5         20687  
13              
14             # Flags used as return values from our methods. Just for internal
15             # clarification.
16             my $YES = (1==1);
17             my $NO = !$YES;
18              
19             =head1 NAME
20              
21             Test::Harness::Straps - detailed analysis of test results
22              
23             =head1 SYNOPSIS
24              
25             use Test::Harness::Straps;
26              
27             my $strap = Test::Harness::Straps->new;
28              
29             # Various ways to interpret a test
30             my $results = $strap->analyze($name, \@test_output);
31             my $results = $strap->analyze_fh($name, $test_filehandle);
32             my $results = $strap->analyze_file($test_file);
33              
34             # UNIMPLEMENTED
35             my %total = $strap->total_results;
36              
37             # Altering the behavior of the strap UNIMPLEMENTED
38             my $verbose_output = $strap->dump_verbose();
39             $strap->dump_verbose_fh($output_filehandle);
40              
41              
42             =head1 WARNING
43              
44             B No further development
45             is planned, no bugs will be fixed.
46              
47             For customizable TAP parsing please use L instead.
48              
49             =head1 DESCRIPTION
50              
51             Test::Harness is limited to printing out its results. This makes
52             analysis of the test results difficult for anything but a human. To
53             make it easier for programs to work with test results, we provide
54             Test::Harness::Straps. Instead of printing the results, straps
55             provide them as raw data. You can also configure how the tests are to
56             be run.
57              
58             The interface is currently incomplete. I contact the author
59             if you'd like a feature added or something change or just have
60             comments.
61              
62             =head1 CONSTRUCTION
63              
64             =head2 new()
65              
66             my $strap = Test::Harness::Straps->new;
67              
68             Initialize a new strap.
69              
70             =cut
71              
72             sub new {
73 52     52 1 25016 my $class = shift;
74 52         172 my $self = bless {}, $class;
75              
76 52         209 $self->_init;
77              
78 52         134 return $self;
79             }
80              
81             =for private $strap->_init
82              
83             $strap->_init;
84              
85             Initialize the internal state of a strap to make it ready for parsing.
86              
87             =cut
88              
89             sub _init {
90 52     52   89 my($self) = shift;
91              
92 52         320 $self->{_is_vms} = ( $^O eq 'VMS' );
93 52         254 $self->{_is_win32} = ( $^O =~ /^(MS)?Win32$/ );
94 52         178 $self->{_is_macos} = ( $^O eq 'MacOS' );
95             }
96              
97             =head1 ANALYSIS
98              
99             =head2 $strap->analyze( $name, \@output_lines )
100              
101             my $results = $strap->analyze($name, \@test_output);
102              
103             Analyzes the output of a single test, assigning it the given C<$name>
104             for use in the total report. Returns the C<$results> of the test.
105             See L.
106              
107             C<@test_output> should be the raw output from the test, including
108             newlines.
109              
110             =cut
111              
112             sub analyze {
113 0     0 1 0 my($self, $name, $test_output) = @_;
114              
115 0         0 my $it = Test::Harness::Iterator->new($test_output);
116 0         0 return $self->_analyze_iterator($name, $it);
117             }
118              
119              
120             sub _analyze_iterator {
121 41     41   159 my($self, $name, $it) = @_;
122              
123 41         513 $self->_reset_file_state;
124 41         281 $self->{file} = $name;
125              
126 41         865 my $results = Test::Harness::Results->new;
127              
128             # Set them up here so callbacks can have them.
129 41         517 $self->{totals}{$name} = $results;
130 41         357 while( defined(my $line = $it->next) ) {
131 239         1112 $self->_analyze_line($line, $results);
132 239 100       1363 last if $self->{saw_bailout};
133             }
134              
135 41 100       417 $results->set_skip_all( $self->{skip_all} ) if defined $self->{skip_all};
136              
137 41   66     403 my $passed =
138             (($results->max == 0) && defined $results->skip_all) ||
139             ($results->max &&
140             $results->seen &&
141             $results->max == $results->seen &&
142             $results->max == $results->ok);
143              
144 41 100       314 $results->set_passing( $passed ? 1 : 0 );
145              
146 41         382 return $results;
147             }
148              
149              
150             sub _analyze_line {
151 239     239   355 my $self = shift;
152 239         377 my $line = shift;
153 239         361 my $results = shift;
154              
155 239         433 $self->{line}++;
156              
157 239         273 my $linetype;
158 239         1644 my $point = Test::Harness::Point->from_test_line( $line );
159 239 100       1481 if ( $point ) {
    100          
    100          
    100          
    50          
160 170         309 $linetype = 'test';
161              
162 170         969 $results->inc_seen;
163 170 100       508 $point->set_number( $self->{'next'} ) unless $point->number;
164              
165             # sometimes the 'not ' and the 'ok' are on different lines,
166             # happens often on VMS if you do:
167             # print "not " unless $test;
168             # print "ok $num\n";
169 170 100 100     638 if ( $self->{lone_not_line} && ($self->{lone_not_line} == $self->{line} - 1) ) {
170 2         36 $point->set_ok( 0 );
171             }
172              
173 170 100       1250 if ( $self->{todo}{$point->number} ) {
174 16         62 $point->set_directive_type( 'todo' );
175             }
176              
177 170 100       706 if ( $point->is_todo ) {
    100          
178 18         245 $results->inc_todo;
179 18 100       282 $results->inc_bonus if $point->ok;
180             }
181             elsif ( $point->is_skip ) {
182 6         35 $results->inc_skip;
183             }
184              
185 170 100       756 $results->inc_ok if $point->pass;
186              
187 170 100 50     708 if ( ($point->number > 100_000) && ($point->number > ($self->{max}||100_000)) ) {
      66        
188 2 100       15 if ( !$self->{too_many_tests}++ ) {
189 1         4 warn "Enormous test number seen [test ", $point->number, "]\n";
190 1         15 warn "Can't detailize, too big.\n";
191             }
192             }
193             else {
194 168         481 my $details = {
195             ok => $point->pass,
196             actual_ok => $point->ok,
197             name => _def_or_blank( $point->description ),
198             type => _def_or_blank( $point->directive_type ),
199             reason => _def_or_blank( $point->directive_reason ),
200             };
201              
202 168   33     1471 assert( defined( $details->{ok} ) && defined( $details->{actual_ok} ) );
203 168         492 $results->set_details( $point->number, $details );
204             }
205             } # test point
206             elsif ( $line =~ /^not\s+$/ ) {
207 2         45 $linetype = 'other';
208             # Sometimes the "not " and "ok" will be on separate lines on VMS.
209             # We catch this and remember we saw it.
210 2         19 $self->{lone_not_line} = $self->{line};
211             }
212             elsif ( $self->_is_header($line) ) {
213 38         178 $linetype = 'header';
214              
215 38         88 $self->{saw_header}++;
216              
217 38         363 $results->inc_max( $self->{max} );
218             }
219             elsif ( $self->_is_bail_out($line, \$self->{bailout_reason}) ) {
220 1         3 $linetype = 'bailout';
221 1         2 $self->{saw_bailout} = 1;
222             }
223             elsif (my $diagnostics = $self->_is_diagnostic_line( $line )) {
224 28         45 $linetype = 'other';
225             # XXX We can throw this away, really.
226 28         180 my $test = $results->details->[-1];
227 28   100     215 $test->{diagnostics} ||= '';
228 28         102 $test->{diagnostics} .= $diagnostics;
229             }
230             else {
231 0         0 $linetype = 'other';
232             }
233              
234 239 100       877 $self->callback->($self, $line, $linetype, $results) if $self->callback;
235              
236 239 100       1520 $self->{'next'} = $point->number + 1 if $point;
237             } # _analyze_line
238              
239              
240             sub _is_diagnostic_line {
241 28     28   73 my ($self, $line) = @_;
242 28 50       110 return if index( $line, '# Looks like you failed' ) == 0;
243 28         187 $line =~ s/^#\s//;
244 28         115 return $line;
245             }
246              
247             =for private $strap->analyze_fh( $name, $test_filehandle )
248              
249             my $results = $strap->analyze_fh($name, $test_filehandle);
250              
251             Like C, but it reads from the given filehandle.
252              
253             =cut
254              
255             sub analyze_fh {
256 41     41 0 536 my($self, $name, $fh) = @_;
257              
258 41         2914 my $it = Test::Harness::Iterator->new($fh);
259 41         515 return $self->_analyze_iterator($name, $it);
260             }
261              
262             =head2 $strap->analyze_file( $test_file )
263              
264             my $results = $strap->analyze_file($test_file);
265              
266             Like C, but it runs the given C<$test_file> and parses its
267             results. It will also use that name for the total report.
268              
269             =cut
270              
271             sub analyze_file {
272 42     42 1 44951 my($self, $file) = @_;
273              
274 42 100       1124980 unless( -e $file ) {
275 1         15 $self->{error} = "$file does not exist";
276 1         7 return;
277             }
278              
279 41 50       708 unless( -r $file ) {
280 0         0 $self->{error} = "$file is not readable";
281 0         0 return;
282             }
283              
284 41         241 local $ENV{PERL5LIB} = $self->_INC2PERL5LIB;
285 41 50       252 if ( $Test::Harness::Debug ) {
286 0         0 local $^W=0; # ignore undef warnings
287 0         0 print "# PERL5LIB=$ENV{PERL5LIB}\n";
288             }
289              
290             # *sigh* this breaks under taint, but open -| is unportable.
291 41         244 my $line = $self->_command_line($file);
292              
293 41 50       46515144 unless ( open(FILE, "$line|" )) {
294 0         0 print "can't run $file. $!\n";
295 0         0 return;
296             }
297              
298 41         2209 my $results = $self->analyze_fh($file, \*FILE);
299 41         8465 my $exit = close FILE;
300              
301 41         602 $results->set_wait($?);
302 41 50 66     319 if ( $? && $self->{_is_vms} ) {
303 0         0 eval q{use vmsish "status"; $results->set_exit($?); };
304             }
305             else {
306 41         501 $results->set_exit( _wait2exit($?) );
307             }
308 41 100       206 $results->set_passing(0) unless $? == 0;
309              
310 41         258 $self->_restore_PERL5LIB();
311              
312 41         1151 return $results;
313             }
314              
315              
316             eval { require POSIX; &POSIX::WEXITSTATUS(0) };
317             if( $@ ) {
318             *_wait2exit = sub { $_[0] >> 8 };
319             }
320             else {
321 41     41   432 *_wait2exit = sub { POSIX::WEXITSTATUS($_[0]) }
322             }
323              
324             =for private $strap->_command_line( $file )
325              
326             Returns the full command line that will be run to test I<$file>.
327              
328             =cut
329              
330             sub _command_line {
331 41     41   81 my $self = shift;
332 41         78 my $file = shift;
333              
334 41         134 my $command = $self->_command();
335 41         451 my $switches = $self->_switches($file);
336              
337 41 50 33     213 $file = qq["$file"] if ($file =~ /\s/) && ($file !~ /^".*"$/);
338 41         172 my $line = "$command $switches $file";
339              
340 41         126 return $line;
341             }
342              
343              
344             =for private $strap->_command()
345              
346             Returns the command that runs the test. Combine this with C<_switches()>
347             to build a command line.
348              
349             Typically this is C<$^X>, but you can set C<$ENV{HARNESS_PERL}>
350             to use a different Perl than what you're running the harness under.
351             This might be to run a threaded Perl, for example.
352              
353             You can also overload this method if you've built your own strap subclass,
354             such as a PHP interpreter for a PHP-based strap.
355              
356             =cut
357              
358             sub _command {
359 84     84   119 my $self = shift;
360              
361 84 50       248 return $ENV{HARNESS_PERL} if defined $ENV{HARNESS_PERL};
362 84 50 33     261 return qq["$^X"] if $self->{_is_win32} && ($^X =~ /[^\w\.\/\\]/);
363 84         191 return $^X;
364             }
365              
366              
367             =for private $strap->_switches( $file )
368              
369             Formats and returns the switches necessary to run the test.
370              
371             =cut
372              
373             sub _switches {
374 41     41   77 my($self, $file) = @_;
375              
376 41         260 my @existing_switches = $self->_cleaned_switches( $Test::Harness::Switches, $ENV{HARNESS_PERL_SWITCHES} );
377 41         68 my @derived_switches;
378              
379 41         179 local *TEST;
380 41 50       2470 open(TEST, $file) or print "can't open $file. $!\n";
381 41         935 my $shebang = ;
382 41 50       860 close(TEST) or print "can't close $file. $!\n";
383              
384 41         195 my $taint = ( $shebang =~ /^#!.*\bperl.*\s-\w*([Tt]+)/ );
385 41 100       125 push( @derived_switches, "-$1" ) if $taint;
386              
387             # When taint mode is on, PERL5LIB is ignored. So we need to put
388             # all that on the command line as -Is.
389             # MacPerl's putenv is broken, so it will not see PERL5LIB, tainted or not.
390 41 100 66     529 if ( $taint || $self->{_is_macos} ) {
391 2         27 my @inc = $self->_filtered_INC;
392 2         8 push @derived_switches, map { "-I$_" } @inc;
  20         47  
393             }
394              
395             # Quote the argument if there's any whitespace in it, or if
396             # we're VMS, since VMS requires all parms quoted. Also, don't quote
397             # it if it's already quoted.
398 41         117 for ( @derived_switches ) {
399 22 50 33     105 $_ = qq["$_"] if ((/\s/ || $self->{_is_vms}) && !/^".*"$/ );
      33        
400             }
401 41         300 return join( " ", @existing_switches, @derived_switches );
402             }
403              
404             =for private $strap->_cleaned_switches( @switches_from_user )
405              
406             Returns only defined, non-blank, trimmed switches from the parms passed.
407              
408             =cut
409              
410             sub _cleaned_switches {
411 41     41   63 my $self = shift;
412              
413 41         89 local $_;
414              
415 41         68 my @switches;
416 41         130 for ( @_ ) {
417 82         249 my $switch = $_;
418 82 100       225 next unless defined $switch;
419 63         385 $switch =~ s/^\s+//;
420 63         434 $switch =~ s/\s+$//;
421 63 50       323 push( @switches, $switch ) if $switch ne "";
422             }
423              
424 41         473 return @switches;
425             }
426              
427             =for private $strap->_INC2PERL5LIB
428              
429             local $ENV{PERL5LIB} = $self->_INC2PERL5LIB;
430              
431             Takes the current value of C<@INC> and turns it into something suitable
432             for putting onto C.
433              
434             =cut
435              
436             sub _INC2PERL5LIB {
437 41     41   110 my($self) = shift;
438              
439 41         244 $self->{_old5lib} = $ENV{PERL5LIB};
440              
441 41         1929 return join $Config{path_sep}, $self->_filtered_INC;
442             }
443              
444             =for private $strap->_filtered_INC()
445              
446             my @filtered_inc = $self->_filtered_INC;
447              
448             Shortens C<@INC> by removing redundant and unnecessary entries.
449             Necessary for OSes with limited command line lengths, like VMS.
450              
451             =cut
452              
453             sub _filtered_INC {
454 43     43   128 my($self, @inc) = @_;
455 43 50       1460 @inc = @INC unless @inc;
456              
457 43 50       436 if( $self->{_is_vms} ) {
    50          
458             # VMS has a 255-byte limit on the length of %ENV entries, so
459             # toss the ones that involve perl_root, the install location
460 0         0 @inc = grep !/perl_root/i, @inc;
461              
462             }
463             elsif ( $self->{_is_win32} ) {
464             # Lose any trailing backslashes in the Win32 paths
465 0         0 s/[\\\/+]$// foreach @inc;
466             }
467              
468 43         77 my %seen;
469 43         214 $seen{$_}++ foreach $self->_default_inc();
470 43         1636 @inc = grep !$seen{$_}++, @inc;
471              
472 43         613 return @inc;
473             }
474              
475              
476             { # Without caching, _default_inc() takes a huge amount of time
477             my %cache;
478             sub _default_inc {
479 43     43   73 my $self = shift;
480 43         227 my $perl = $self->_command;
481 43   100     167 $cache{$perl} ||= [do {
482 2         14 local $ENV{PERL5LIB};
483 2         25646 my @inc =`$perl -le "print join qq[\\n], \@INC"`;
484 2         179 chomp @inc;
485             }];
486 43         82 return @{$cache{$perl}};
  43         557  
487             }
488             }
489              
490              
491             =for private $strap->_restore_PERL5LIB()
492              
493             $self->_restore_PERL5LIB;
494              
495             This restores the original value of the C environment variable.
496             Necessary on VMS, otherwise a no-op.
497              
498             =cut
499              
500             sub _restore_PERL5LIB {
501 41     41   106 my($self) = shift;
502              
503 41 50       200 return unless $self->{_is_vms};
504              
505 0 0       0 if (defined $self->{_old5lib}) {
506 0         0 $ENV{PERL5LIB} = $self->{_old5lib};
507             }
508             }
509              
510             =head1 Parsing
511              
512             Methods for identifying what sort of line you're looking at.
513              
514             =for private _is_diagnostic
515              
516             my $is_diagnostic = $strap->_is_diagnostic($line, \$comment);
517              
518             Checks if the given line is a comment. If so, it will place it into
519             C<$comment> (sans #).
520              
521             =cut
522              
523             sub _is_diagnostic {
524 5     5   1377 my($self, $line, $comment) = @_;
525              
526 5 100       25 if( $line =~ /^\s*\#(.*)/ ) {
527 3         9 $$comment = $1;
528 3         18 return $YES;
529             }
530             else {
531 2         11 return $NO;
532             }
533             }
534              
535             =for private _is_header
536              
537             my $is_header = $strap->_is_header($line);
538              
539             Checks if the given line is a header (1..M) line. If so, it places how
540             many tests there will be in C<< $strap->{max} >>, a list of which tests
541             are todo in C<< $strap->{todo} >> and if the whole test was skipped
542             C<< $strap->{skip_all} >> contains the reason.
543              
544             =cut
545              
546             # Regex for parsing a header. Will be run with /x
547             my $Extra_Header_Re = <<'REGEX';
548             ^
549             (?: \s+ todo \s+ ([\d \t]+) )? # optional todo set
550             (?: \s* \# \s* ([\w:]+\s?) (.*) )? # optional skip with optional reason
551             REGEX
552              
553             sub _is_header {
554 82     82   4802 my($self, $line) = @_;
555              
556 82 100       1006 if( my($max, $extra) = $line =~ /^1\.\.(\d+)(.*)/ ) {
557 47         269 $self->{max} = $max;
558 47         448 assert( $self->{max} >= 0, 'Max # of tests looks right' );
559              
560 47 50       146 if( defined $extra ) {
561 47         985 my($todo, $skip, $reason) = $extra =~ /$Extra_Header_Re/xo;
562              
563 47 100       258 $self->{todo} = { map { $_ => 1 } split /\s+/, $todo } if $todo;
  23         195  
564              
565 47 100       238 if( $self->{max} == 0 ) {
566 7 100 66     127 $reason = '' unless defined $skip and $skip =~ /^Skip/i;
567             }
568              
569 47         198 $self->{skip_all} = $reason;
570             }
571              
572 47         361 return $YES;
573             }
574             else {
575 35         254 return $NO;
576             }
577             }
578              
579             =for private _is_bail_out
580              
581             my $is_bail_out = $strap->_is_bail_out($line, \$reason);
582              
583             Checks if the line is a "Bail out!". Places the reason for bailing
584             (if any) in $reason.
585              
586             =cut
587              
588             sub _is_bail_out {
589 37     37   2675 my($self, $line, $reason) = @_;
590              
591 37 100       219 if( $line =~ /^Bail out!\s*(.*)/i ) {
592 5 100       36 $$reason = $1 if $1;
593 5         28 return $YES;
594             }
595             else {
596 32         296 return $NO;
597             }
598             }
599              
600             =for private _reset_file_state
601              
602             $strap->_reset_file_state;
603              
604             Resets things like C<< $strap->{max} >> , C<< $strap->{skip_all} >>,
605             etc. so it's ready to parse the next file.
606              
607             =cut
608              
609             sub _reset_file_state {
610 41     41   126 my($self) = shift;
611              
612 41         112 delete @{$self}{qw(max skip_all todo too_many_tests)};
  41         585  
613 41         315 $self->{line} = 0;
614 41         229 $self->{saw_header} = 0;
615 41         129 $self->{saw_bailout}= 0;
616 41         291 $self->{lone_not_line} = 0;
617 41         821 $self->{bailout_reason} = '';
618 41         210 $self->{'next'} = 1;
619             }
620              
621             =head1 EXAMPLES
622              
623             See F for an example of use.
624              
625             =head1 AUTHOR
626              
627             Michael G Schwern C<< >>, currently maintained by
628             Andy Lester C<< >>.
629              
630             =head1 SEE ALSO
631              
632             L
633              
634             =cut
635              
636             sub _def_or_blank {
637 504 100   504   1485 return $_[0] if defined $_[0];
638 425         2361 return "";
639             }
640              
641             sub set_callback {
642 1     1 0 944 my $self = shift;
643 1         4 $self->{callback} = shift;
644             }
645              
646             sub callback {
647 363     363 0 450 my $self = shift;
648 363         1717 return $self->{callback};
649             }
650              
651             1;