1 # -*- Mode: cperl; cperl-indent-level: 4 -*-
2 # $Id: Straps.pm 450 2004-12-20 04:51:42Z andy $
4 package Test::Harness::Straps;
11 use Test::Harness::Assert;
12 use Test::Harness::Iterator;
14 # Flags used as return values from our methods. Just for internal
24 Test::Harness::Straps - detailed analysis of test results
28 use Test::Harness::Straps;
30 my $strap = Test::Harness::Straps->new;
32 # Various ways to interpret a test
33 my %results = $strap->analyze($name, \@test_output);
34 my %results = $strap->analyze_fh($name, $test_filehandle);
35 my %results = $strap->analyze_file($test_file);
38 my %total = $strap->total_results;
40 # Altering the behavior of the strap UNIMPLEMENTED
41 my $verbose_output = $strap->dump_verbose();
42 $strap->dump_verbose_fh($output_filehandle);
47 B<THIS IS ALPHA SOFTWARE> in that the interface is subject to change
48 in incompatible ways. It is otherwise stable.
50 Test::Harness is limited to printing out its results. This makes
51 analysis of the test results difficult for anything but a human. To
52 make it easier for programs to work with test results, we provide
53 Test::Harness::Straps. Instead of printing the results, straps
54 provide them as raw data. You can also configure how the tests are to
57 The interface is currently incomplete. I<Please> contact the author
58 if you'd like a feature added or something change or just have
65 my $strap = Test::Harness::Straps->new;
67 Initialize a new strap.
74 my $self = bless {}, $class;
84 Initialize the internal state of a strap to make it ready for parsing.
91 $self->{_is_vms} = ( $^O eq 'VMS' );
92 $self->{_is_win32} = ( $^O =~ /^(MS)?Win32$/ );
93 $self->{_is_macos} = ( $^O eq 'MacOS' );
98 =head2 $strap->analyze( $name, \@output_lines )
100 my %results = $strap->analyze($name, \@test_output);
102 Analyzes the output of a single test, assigning it the given C<$name>
103 for use in the total report. Returns the C<%results> of the test.
106 C<@test_output> should be the raw output from the test, including
112 my($self, $name, $test_output) = @_;
114 my $it = Test::Harness::Iterator->new($test_output);
115 return $self->_analyze_iterator($name, $it);
119 sub _analyze_iterator {
120 my($self, $name, $it) = @_;
122 $self->_reset_file_state;
123 $self->{file} = $name;
136 # Set them up here so callbacks can have them.
137 $self->{totals}{$name} = \%totals;
138 while( defined(my $line = $it->next) ) {
139 $self->_analyze_line($line, \%totals);
140 last if $self->{saw_bailout};
143 $totals{skip_all} = $self->{skip_all} if defined $self->{skip_all};
145 my $passed = ($totals{max} == 0 && defined $totals{skip_all}) ||
146 ($totals{max} && $totals{seen} &&
147 $totals{max} == $totals{seen} &&
148 $totals{max} == $totals{ok});
149 $totals{passing} = $passed ? 1 : 0;
156 my($self, $line, $totals) = @_;
163 if ( $self->_is_test($line, \%result) ) {
167 $result{number} = $self->{'next'} unless $result{number};
169 # sometimes the 'not ' and the 'ok' are on different lines,
170 # happens often on VMS if you do:
171 # print "not " unless $test;
173 if( $self->{saw_lone_not} &&
174 ($self->{lone_not_line} == $self->{line} - 1) )
179 my $pass = $result{ok};
180 $result{type} = 'todo' if $self->{todo}{$result{number}};
182 if( $result{type} eq 'todo' ) {
185 $totals->{bonus}++ if $result{ok}
187 elsif( $result{type} eq 'skip' ) {
192 $totals->{ok}++ if $pass;
194 if( $result{number} > 100000 && $result{number} > $self->{max} ) {
195 warn "Enormous test number seen [test $result{number}]\n";
196 warn "Can't detailize, too big.\n";
199 #Generates the details based on the last test line seen. C<$pass> is
200 #true if it was considered to be a passed test. C<%test> is the results
201 #of the test you're summarizing.
204 actual_ok => $result{ok}
207 assert( defined( $details->{ok} ) && defined( $details->{actual_ok} ) );
209 # We don't want these to be undef because they are often
210 # checked and don't want the checker to have to deal with
211 # uninitialized vars.
212 foreach my $piece (qw(name type reason)) {
213 $details->{$piece} = defined $result{$piece} ? $result{$piece} : '';
215 $totals->{details}[$result{number} - 1] = $details;
218 # XXX handle counter mismatch
220 elsif ( $self->_is_header($line) ) {
223 $self->{saw_header}++;
225 $totals->{max} += $self->{max};
227 elsif ( $self->_is_bail_out($line, \$self->{bailout_reason}) ) {
229 $self->{saw_bailout} = 1;
235 $self->{callback}->($self, $line, $type, $totals) if $self->{callback};
237 $self->{'next'} = $result{number} + 1 if $type eq 'test';
242 my %results = $strap->analyze_fh($name, $test_filehandle);
244 Like C<analyze>, but it reads from the given filehandle.
249 my($self, $name, $fh) = @_;
251 my $it = Test::Harness::Iterator->new($fh);
252 return $self->_analyze_iterator($name, $it);
255 =head2 C<analyze_file>
257 my %results = $strap->analyze_file($test_file);
259 Like C<analyze>, but it runs the given C<$test_file> and parses its
260 results. It will also use that name for the total report.
265 my($self, $file) = @_;
268 $self->{error} = "$file does not exist";
273 $self->{error} = "$file is not readable";
277 local $ENV{PERL5LIB} = $self->_INC2PERL5LIB;
278 if ( $Test::Harness::Debug ) {
279 local $^W=0; # ignore undef warnings
280 print "# PERL5LIB=$ENV{PERL5LIB}\n";
283 # *sigh* this breaks under taint, but open -| is unportable.
284 my $line = $self->_command_line($file);
285 unless( open(FILE, "$line|") ) {
286 print "can't run $file. $!\n";
290 my %results = $self->analyze_fh($file, \*FILE);
291 my $exit = close FILE;
292 $results{'wait'} = $?;
293 if( $? && $self->{_is_vms} ) {
294 eval q{use vmsish "status"; $results{'exit'} = $?};
297 $results{'exit'} = _wait2exit($?);
299 $results{passing} = 0 unless $? == 0;
301 $self->_restore_PERL5LIB();
307 eval { require POSIX; &POSIX::WEXITSTATUS(0) };
309 *_wait2exit = sub { $_[0] >> 8 };
312 *_wait2exit = sub { POSIX::WEXITSTATUS($_[0]) }
315 =head2 C<_command_line( $file )>
317 my $command_line = $self->_command_line();
319 Returns the full command line that will be run to test I<$file>.
327 my $command = $self->_command();
328 my $switches = $self->_switches($file);
330 $file = qq["$file"] if ($file =~ /\s/) && ($file !~ /^".*"$/);
331 my $line = "$command $switches $file";
339 my $command = $self->_command();
341 Returns the command that runs the test. Combine this with _switches()
342 to build a command line.
344 Typically this is C<$^X>, but you can set C<$ENV{HARNESS_COMMAND}>
345 to use a different Perl than what you're running the harness under.
346 This might be to run a threaded Perl, for example.
348 You can also overload this method if you've built your own strap subclass,
349 such as a PHP interpreter for a PHP-based strap.
356 return $ENV{HARNESS_PERL} if defined $ENV{HARNESS_PERL};
357 return "MCR $^X" if $self->{_is_vms};
358 return Win32::GetShortPathName($^X) if $self->{_is_win32};
365 my $switches = $self->_switches($file);
367 Formats and returns the switches necessary to run the test.
372 my($self, $file) = @_;
374 my @existing_switches = $self->_cleaned_switches( $Test::Harness::Switches, $ENV{HARNESS_PERL_SWITCHES} );
375 my @derived_switches;
378 open(TEST, $file) or print "can't open $file. $!\n";
379 my $shebang = <TEST>;
380 close(TEST) or print "can't close $file. $!\n";
382 my $taint = ( $shebang =~ /^#!.*\bperl.*\s-\w*([Tt]+)/ );
383 push( @derived_switches, "-$1" ) if $taint;
385 # When taint mode is on, PERL5LIB is ignored. So we need to put
386 # all that on the command line as -Is.
387 # MacPerl's putenv is broken, so it will not see PERL5LIB, tainted or not.
388 if ( $taint || $self->{_is_macos} ) {
389 my @inc = $self->_filtered_INC;
390 push @derived_switches, map { "-I$_" } @inc;
393 # Quote the argument if there's any whitespace in it, or if
394 # we're VMS, since VMS requires all parms quoted. Also, don't quote
395 # it if it's already quoted.
396 for ( @derived_switches ) {
397 $_ = qq["$_"] if ((/\s/ || $self->{_is_vms}) && !/^".*"$/ );
399 return join( " ", @existing_switches, @derived_switches );
402 =head2 C<_cleaned_switches>
404 my @switches = $self->_cleaned_switches( @switches_from_user );
406 Returns only defined, non-blank, trimmed switches from the parms passed.
410 sub _cleaned_switches {
418 next unless defined $switch;
421 push( @switches, $switch ) if $switch ne "";
427 =head2 C<_INC2PERL5LIB>
429 local $ENV{PERL5LIB} = $self->_INC2PERL5LIB;
431 Takes the current value of C<@INC> and turns it into something suitable
432 for putting onto C<PERL5LIB>.
439 $self->{_old5lib} = $ENV{PERL5LIB};
441 return join $Config{path_sep}, $self->_filtered_INC;
444 =head2 C<_filtered_INC>
446 my @filtered_inc = $self->_filtered_INC;
448 Shortens C<@INC> by removing redundant and unnecessary entries.
449 Necessary for OSes with limited command line lengths, like VMS.
454 my($self, @inc) = @_;
455 @inc = @INC unless @inc;
457 if( $self->{_is_vms} ) {
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 @inc = grep !/perl_root/i, @inc;
462 } elsif ( $self->{_is_win32} ) {
463 # Lose any trailing backslashes in the Win32 paths
464 s/[\\\/+]$// foreach @inc;
468 $seen{$_}++ foreach $self->_default_inc();
469 @inc = grep !$seen{$_}++, @inc;
478 local $ENV{PERL5LIB};
479 my $perl = $self->_command;
480 my @inc =`$perl -le "print join qq[\n], \@INC"`;
486 =head2 C<_restore_PERL5LIB>
488 $self->_restore_PERL5LIB;
490 This restores the original value of the C<PERL5LIB> environment variable.
491 Necessary on VMS, otherwise a no-op.
495 sub _restore_PERL5LIB {
498 return unless $self->{_is_vms};
500 if (defined $self->{_old5lib}) {
501 $ENV{PERL5LIB} = $self->{_old5lib};
507 Methods for identifying what sort of line you're looking at.
509 =head2 C<_is_comment>
511 my $is_comment = $strap->_is_comment($line, \$comment);
513 Checks if the given line is a comment. If so, it will place it into
514 C<$comment> (sans #).
519 my($self, $line, $comment) = @_;
521 if( $line =~ /^\s*\#(.*)/ ) {
532 my $is_header = $strap->_is_header($line);
534 Checks if the given line is a header (1..M) line. If so, it places how
535 many tests there will be in C<< $strap->{max} >>, a list of which tests
536 are todo in C<< $strap->{todo} >> and if the whole test was skipped
537 C<< $strap->{skip_all} >> contains the reason.
541 # Regex for parsing a header. Will be run with /x
542 my $Extra_Header_Re = <<'REGEX';
544 (?: \s+ todo \s+ ([\d \t]+) )? # optional todo set
545 (?: \s* \# \s* ([\w:]+\s?) (.*) )? # optional skip with optional reason
549 my($self, $line) = @_;
551 if( my($max, $extra) = $line =~ /^1\.\.(\d+)(.*)/ ) {
553 assert( $self->{max} >= 0, 'Max # of tests looks right' );
555 if( defined $extra ) {
556 my($todo, $skip, $reason) = $extra =~ /$Extra_Header_Re/xo;
558 $self->{todo} = { map { $_ => 1 } split /\s+/, $todo } if $todo;
560 if( $self->{max} == 0 ) {
561 $reason = '' unless defined $skip and $skip =~ /^Skip/i;
564 $self->{skip_all} = $reason;
576 my $is_test = $strap->_is_test($line, \%test);
578 Checks if the $line is a test report (ie. 'ok/not ok'). Reports the
579 result back in C<%test> which will contain:
581 ok did it succeed? This is the literal 'ok' or 'not ok'.
582 name name of the test (if any)
583 number test number (if any)
585 type 'todo' or 'skip' (if any)
586 reason why is it todo or skip? (if any)
588 It will also catch lone 'not' lines, note it saw them in
589 C<< $strap->{saw_lone_not} >> and the line in C<< $strap->{lone_not_line} >>.
593 my $Report_Re = <<'REGEX';
597 (?:\s+(\d+))? # optional test number
603 my($self, $line, $test) = @_;
605 # We pulverize the line down into pieces in three parts.
606 if( my($not, $num, $extra) = $line =~ /$Report_Re/ox ) {
607 ($test->{name}, my $control) = $extra ? split(/(?:[^\\]|^)#/, $extra) : ();
608 (my $type, $test->{reason}) = $control ? $control =~ /^\s*(\S+)(?:\s+(.*))?$/ : ();
610 $test->{number} = $num;
611 $test->{ok} = $not ? 0 : 1;
613 if( defined $type ) {
614 $test->{type} = $type =~ /^TODO$/i ? 'todo' :
615 $type =~ /^Skip/i ? 'skip' : 0;
624 # Sometimes the "not " and "ok" will be on separate lines on VMS.
625 # We catch this and remember we saw it.
626 if( $line =~ /^not\s+$/ ) {
627 $self->{saw_lone_not} = 1;
628 $self->{lone_not_line} = $self->{line};
635 =head2 C<_is_bail_out>
637 my $is_bail_out = $strap->_is_bail_out($line, \$reason);
639 Checks if the line is a "Bail out!". Places the reason for bailing
645 my($self, $line, $reason) = @_;
647 if( $line =~ /^Bail out!\s*(.*)/i ) {
656 =head2 C<_reset_file_state>
658 $strap->_reset_file_state;
660 Resets things like C<< $strap->{max} >> , C<< $strap->{skip_all} >>,
661 etc. so it's ready to parse the next file.
665 sub _reset_file_state {
668 delete @{$self}{qw(max skip_all todo)};
670 $self->{saw_header} = 0;
671 $self->{saw_bailout}= 0;
672 $self->{saw_lone_not} = 0;
673 $self->{lone_not_line} = 0;
674 $self->{bailout_reason} = '';
680 The C<%results> returned from C<analyze()> contain the following
683 passing true if the whole test is considered a pass
684 (or skipped), false if its a failure
686 exit the exit code of the test run, if from a file
687 wait the wait code of the test run, if from a file
689 max total tests which should have been run
690 seen total tests actually seen
691 skip_all if the whole test was skipped, this will
694 ok number of tests which passed
695 (including todo and skips)
697 todo number of todo tests seen
698 bonus number of todo tests which
701 skip number of tests skipped
703 So a successful test should have max == seen == ok.
706 There is one final item, the details.
708 details an array ref reporting the result of
709 each test looks like this:
711 $results{details}[$test_num - 1] =
712 { ok => is the test considered ok?
713 actual_ok => did it literally say 'ok'?
714 name => name of the test (if any)
715 type => 'skip' or 'todo' (if any)
716 reason => reason for the above (if any)
719 Element 0 of the details is test #1. I tried it with element 1 being
720 #1 and 0 being empty, this is less awkward.
724 See F<examples/mini_harness.plx> for an example of use.
728 Michael G Schwern C<< <schwern@pobox.com> >>, currently maintained by
729 Andy Lester C<< <andy@petdance.com> >>.