[perl #32967] [PATCH] Re: More B bugs: svref_2object
[p5sagit/p5-mst-13.2.git] / lib / Test / Harness / Straps.pm
CommitLineData
13287dd5 1# -*- Mode: cperl; cperl-indent-level: 4 -*-
60e33a80 2# $Id: Straps.pm,v 1.35 2003/12/31 02:34:22 andy Exp $
13287dd5 3
4package Test::Harness::Straps;
5
6use strict;
7use vars qw($VERSION);
8use Config;
60e33a80 9$VERSION = '0.19';
13287dd5 10
11use Test::Harness::Assert;
12use Test::Harness::Iterator;
13
14# Flags used as return values from our methods. Just for internal
15# clarification.
16my $TRUE = (1==1);
17my $FALSE = !$TRUE;
18my $YES = $TRUE;
19my $NO = $FALSE;
20
21
22=head1 NAME
23
24Test::Harness::Straps - detailed analysis of test results
25
26=head1 SYNOPSIS
27
28 use Test::Harness::Straps;
29
30 my $strap = Test::Harness::Straps->new;
31
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);
36
37 # UNIMPLEMENTED
38 my %total = $strap->total_results;
39
40 # Altering the behavior of the strap UNIMPLEMENTED
41 my $verbose_output = $strap->dump_verbose();
42 $strap->dump_verbose_fh($output_filehandle);
43
44
45=head1 DESCRIPTION
46
47B<THIS IS ALPHA SOFTWARE> in that the interface is subject to change
48in incompatible ways. It is otherwise stable.
49
50Test::Harness is limited to printing out its results. This makes
51analysis of the test results difficult for anything but a human. To
52make it easier for programs to work with test results, we provide
53Test::Harness::Straps. Instead of printing the results, straps
54provide them as raw data. You can also configure how the tests are to
55be run.
56
57The interface is currently incomplete. I<Please> contact the author
58if you'd like a feature added or something change or just have
59comments.
60
cf2ab31a 61=head1 Construction
13287dd5 62
cf2ab31a 63=head2 C<new>
13287dd5 64
65 my $strap = Test::Harness::Straps->new;
66
67Initialize a new strap.
68
69=cut
70
71sub new {
72 my($proto) = shift;
73 my($class) = ref $proto || $proto;
74
75 my $self = bless {}, $class;
76 $self->_init;
77
78 return $self;
79}
80
cf2ab31a 81=head2 C<_init>
13287dd5 82
83 $strap->_init;
84
85Initialize the internal state of a strap to make it ready for parsing.
86
87=cut
88
89sub _init {
90 my($self) = shift;
91
e4fc8a1e 92 $self->{_is_vms} = ( $^O eq 'VMS' );
93 $self->{_is_win32} = ( $^O =~ /^(MS)?Win32$/ );
94 $self->{_is_macos} = ( $^O eq 'MacOS' );
13287dd5 95}
96
cf2ab31a 97=head1 Analysis
13287dd5 98
cf2ab31a 99=head2 C<analyze>
13287dd5 100
101 my %results = $strap->analyze($name, \@test_output);
102
cf2ab31a 103Analyzes the output of a single test, assigning it the given C<$name>
104for use in the total report. Returns the C<%results> of the test.
105See L<Results>.
13287dd5 106
cf2ab31a 107C<@test_output> should be the raw output from the test, including
108newlines.
13287dd5 109
110=cut
111
112sub analyze {
113 my($self, $name, $test_output) = @_;
114
115 my $it = Test::Harness::Iterator->new($test_output);
116 return $self->_analyze_iterator($name, $it);
117}
118
119
120sub _analyze_iterator {
121 my($self, $name, $it) = @_;
122
123 $self->_reset_file_state;
124 $self->{file} = $name;
125 my %totals = (
126 max => 0,
127 seen => 0,
128
129 ok => 0,
130 todo => 0,
131 skip => 0,
132 bonus => 0,
308957f5 133
13287dd5 134 details => []
135 );
136
308957f5 137 # Set them up here so callbacks can have them.
138 $self->{totals}{$name} = \%totals;
13287dd5 139 while( defined(my $line = $it->next) ) {
140 $self->_analyze_line($line, \%totals);
141 last if $self->{saw_bailout};
142 }
143
356733da 144 $totals{skip_all} = $self->{skip_all} if defined $self->{skip_all};
145
a72fde19 146 my $passed = ($totals{max} == 0 && defined $totals{skip_all}) ||
147 ($totals{max} && $totals{seen} &&
148 $totals{max} == $totals{seen} &&
149 $totals{max} == $totals{ok});
13287dd5 150 $totals{passing} = $passed ? 1 : 0;
151
13287dd5 152 return %totals;
153}
154
155
156sub _analyze_line {
157 my($self, $line, $totals) = @_;
158
159 my %result = ();
308957f5 160
13287dd5 161 $self->{line}++;
162
163 my $type;
164 if( $self->_is_header($line) ) {
165 $type = 'header';
166
167 $self->{saw_header}++;
308957f5 168
13287dd5 169 $totals->{max} += $self->{max};
170 }
171 elsif( $self->_is_test($line, \%result) ) {
172 $type = 'test';
173
174 $totals->{seen}++;
175 $result{number} = $self->{'next'} unless $result{number};
176
177 # sometimes the 'not ' and the 'ok' are on different lines,
178 # happens often on VMS if you do:
179 # print "not " unless $test;
180 # print "ok $num\n";
181 if( $self->{saw_lone_not} &&
182 ($self->{lone_not_line} == $self->{line} - 1) )
d5d4ec93 183 {
13287dd5 184 $result{ok} = 0;
185 }
186
187 my $pass = $result{ok};
188 $result{type} = 'todo' if $self->{todo}{$result{number}};
189
190 if( $result{type} eq 'todo' ) {
191 $totals->{todo}++;
192 $pass = 1;
193 $totals->{bonus}++ if $result{ok}
194 }
195 elsif( $result{type} eq 'skip' ) {
196 $totals->{skip}++;
197 $pass = 1;
198 }
199
200 $totals->{ok}++ if $pass;
201
a72fde19 202 if( $result{number} > 100000 && $result{number} > $self->{max} ) {
d5d4ec93 203 warn "Enormous test number seen [test $result{number}]\n";
356733da 204 warn "Can't detailize, too big.\n";
205 }
206 else {
207 $totals->{details}[$result{number} - 1] =
13287dd5 208 {$self->_detailize($pass, \%result)};
356733da 209 }
13287dd5 210
211 # XXX handle counter mismatch
212 }
213 elsif ( $self->_is_bail_out($line, \$self->{bailout_reason}) ) {
214 $type = 'bailout';
215 $self->{saw_bailout} = 1;
216 }
217 else {
218 $type = 'other';
219 }
220
221 $self->{callback}->($self, $line, $type, $totals) if $self->{callback};
222
223 $self->{'next'} = $result{number} + 1 if $type eq 'test';
224}
225
cf2ab31a 226=head2 C<analyze_fh>
13287dd5 227
228 my %results = $strap->analyze_fh($name, $test_filehandle);
229
230Like C<analyze>, but it reads from the given filehandle.
231
232=cut
233
234sub analyze_fh {
235 my($self, $name, $fh) = @_;
236
237 my $it = Test::Harness::Iterator->new($fh);
238 $self->_analyze_iterator($name, $it);
239}
240
cf2ab31a 241=head2 C<analyze_file>
13287dd5 242
243 my %results = $strap->analyze_file($test_file);
244
cf2ab31a 245Like C<analyze>, but it runs the given C<$test_file> and parses its
356733da 246results. It will also use that name for the total report.
13287dd5 247
248=cut
249
250sub analyze_file {
251 my($self, $file) = @_;
252
0be28027 253 unless( -e $file ) {
254 $self->{error} = "$file does not exist";
255 return;
256 }
257
258 unless( -r $file ) {
259 $self->{error} = "$file is not readable";
260 return;
261 }
262
13287dd5 263 local $ENV{PERL5LIB} = $self->_INC2PERL5LIB;
264
13287dd5 265 # *sigh* this breaks under taint, but open -| is unportable.
e4fc8a1e 266 my $line = $self->_command_line($file);
267 unless( open(FILE, "$line|") ) {
13287dd5 268 print "can't run $file. $!\n";
269 return;
270 }
271
272 my %results = $self->analyze_fh($file, \*FILE);
356733da 273 my $exit = close FILE;
274 $results{'wait'} = $?;
f0008e52 275 if( $? && $self->{_is_vms} ) {
276 eval q{use vmsish "status"; $results{'exit'} = $?};
277 }
278 else {
6e5a998b 279 $results{'exit'} = _wait2exit($?);
f0008e52 280 }
356733da 281 $results{passing} = 0 unless $? == 0;
13287dd5 282
283 $self->_restore_PERL5LIB();
284
285 return %results;
286}
287
6e5a998b 288
289eval { require POSIX; &POSIX::WEXITSTATUS(0) };
290if( $@ ) {
291 *_wait2exit = sub { $_[0] >> 8 };
292}
293else {
294 *_wait2exit = sub { POSIX::WEXITSTATUS($_[0]) }
295}
296
e4fc8a1e 297=head2 C<_command_line( $file )>
298
299 my $command_line = $self->_command_line();
300
301Returns the full command line that will be run to test I<$file>.
302
303=cut
304
305sub _command_line {
306 my $self = shift;
307 my $file = shift;
308
309 my $command = $self->_command();
310 my $switches = $self->_switches($file);
311
312 $file = qq["$file"] if ($file =~ /\s/) && ($file !~ /^".*"$/);
313 my $line = "$command $switches $file";
314
315 return $line;
316}
317
318
319=head2 C<_command>
320
321 my $command = $self->_command();
322
323Returns the command that runs the test. Combine this with _switches()
324to build a command line.
325
326Typically this is C<$^X>, but you can set C<$ENV{HARNESS_COMMAND}>
327to use a different Perl than what you're running the harness under.
328This might be to run a threaded Perl, for example.
329
330You can also overload this method if you've built your own strap subclass,
331such as a PHP interpreter for a PHP-based strap.
332
333=cut
334
335sub _command {
336 my $self = shift;
337
338 return $ENV{HARNESS_PERL} if defined $ENV{HARNESS_PERL};
339 return "MCR $^X" if $self->{_is_vms};
340 return Win32::GetShortPathName($^X) if $self->{_is_win32};
341 return $^X;
342}
343
6e5a998b 344
cf2ab31a 345=head2 C<_switches>
13287dd5 346
347 my $switches = $self->_switches($file);
348
349Formats and returns the switches necessary to run the test.
350
351=cut
352
353sub _switches {
354 my($self, $file) = @_;
355
e4fc8a1e 356 my @existing_switches = $self->_cleaned_switches( $Test::Harness::Switches, $ENV{HARNESS_PERL_SWITCHES} );
357 my @derived_switches;
358
13287dd5 359 local *TEST;
360 open(TEST, $file) or print "can't open $file. $!\n";
e4fc8a1e 361 my $shebang = <TEST>;
362 close(TEST) or print "can't close $file. $!\n";
363
364 my $taint = ( $shebang =~ /^#!.*\bperl.*\s-\w*([Tt]+)/ );
365 push( @derived_switches, "-$1" ) if $taint;
366
367 # When taint mode is on, PERL5LIB is ignored. So we need to put
368 # all that on the command line as -Is.
369 # MacPerl's putenv is broken, so it will not see PERL5LIB, tainted or not.
370 if ( $taint || $self->{_is_macos} ) {
371 my @inc = $self->_filtered_INC;
372 push @derived_switches, map { "-I$_" } @inc;
11c6125c 373 }
e4fc8a1e 374
60e33a80 375 # Quote the argument if there's any whitespace in it, or if
376 # we're VMS, since VMS requires all parms quoted. Also, don't quote
377 # it if it's already quoted.
e4fc8a1e 378 for ( @derived_switches ) {
60e33a80 379 $_ = qq["$_"] if ((/\s/ || $self->{_is_vms}) && !/^".*"$/ );
11c6125c 380 }
e4fc8a1e 381 return join( " ", @existing_switches, @derived_switches );
382}
13287dd5 383
e4fc8a1e 384=head2 C<_cleaned_switches>
13287dd5 385
e4fc8a1e 386 my @switches = $self->_cleaned_switches( @switches_from_user );
13287dd5 387
e4fc8a1e 388Returns only defined, non-blank, trimmed switches from the parms passed.
389
390=cut
391
392sub _cleaned_switches {
393 my $self = shift;
394
395 local $_;
396
397 my @switches;
398 for ( @_ ) {
399 my $switch = $_;
400 next unless defined $switch;
401 $switch =~ s/^\s+//;
402 $switch =~ s/\s+$//;
403 push( @switches, $switch ) if $switch ne "";
404 }
405
406 return @switches;
407}
13287dd5 408
cf2ab31a 409=head2 C<_INC2PERL5LIB>
13287dd5 410
411 local $ENV{PERL5LIB} = $self->_INC2PERL5LIB;
412
cf2ab31a 413Takes the current value of C<@INC> and turns it into something suitable
414for putting onto C<PERL5LIB>.
13287dd5 415
416=cut
417
418sub _INC2PERL5LIB {
419 my($self) = shift;
420
421 $self->{_old5lib} = $ENV{PERL5LIB};
422
27caa5c1 423 return join $Config{path_sep}, $self->_filtered_INC;
d5d4ec93 424}
13287dd5 425
cf2ab31a 426=head2 C<_filtered_INC>
13287dd5 427
428 my @filtered_inc = $self->_filtered_INC;
429
cf2ab31a 430Shortens C<@INC> by removing redundant and unnecessary entries.
431Necessary for OSes with limited command line lengths, like VMS.
13287dd5 432
433=cut
434
435sub _filtered_INC {
436 my($self, @inc) = @_;
437 @inc = @INC unless @inc;
438
13287dd5 439 if( $self->{_is_vms} ) {
e4fc8a1e 440 # VMS has a 255-byte limit on the length of %ENV entries, so
441 # toss the ones that involve perl_root, the install location
13287dd5 442 @inc = grep !/perl_root/i, @inc;
e4fc8a1e 443
444 } elsif ( $self->{_is_win32} ) {
445 # Lose any trailing backslashes in the Win32 paths
446 s/[\\\/+]$// foreach @inc;
13287dd5 447 }
448
e4fc8a1e 449 my %dupes;
450 @inc = grep !$dupes{$_}++, @inc;
451
13287dd5 452 return @inc;
453}
454
455
cf2ab31a 456=head2 C<_restore_PERL5LIB>
13287dd5 457
458 $self->_restore_PERL5LIB;
459
cf2ab31a 460This restores the original value of the C<PERL5LIB> environment variable.
13287dd5 461Necessary on VMS, otherwise a no-op.
462
463=cut
464
465sub _restore_PERL5LIB {
466 my($self) = shift;
467
468 return unless $self->{_is_vms};
469
470 if (defined $self->{_old5lib}) {
471 $ENV{PERL5LIB} = $self->{_old5lib};
472 }
473}
d5d4ec93 474
cf2ab31a 475=head1 Parsing
13287dd5 476
477Methods for identifying what sort of line you're looking at.
478
cf2ab31a 479=head2 C<_is_comment>
13287dd5 480
481 my $is_comment = $strap->_is_comment($line, \$comment);
482
483Checks if the given line is a comment. If so, it will place it into
cf2ab31a 484C<$comment> (sans #).
13287dd5 485
486=cut
487
488sub _is_comment {
489 my($self, $line, $comment) = @_;
490
491 if( $line =~ /^\s*\#(.*)/ ) {
492 $$comment = $1;
493 return $YES;
494 }
495 else {
496 return $NO;
497 }
498}
499
cf2ab31a 500=head2 C<_is_header>
13287dd5 501
502 my $is_header = $strap->_is_header($line);
503
cf2ab31a 504Checks if the given line is a header (1..M) line. If so, it places how
505many tests there will be in C<< $strap->{max} >>, a list of which tests
506are todo in C<< $strap->{todo} >> and if the whole test was skipped
507C<< $strap->{skip_all} >> contains the reason.
13287dd5 508
509=cut
510
511# Regex for parsing a header. Will be run with /x
512my $Extra_Header_Re = <<'REGEX';
513 ^
514 (?: \s+ todo \s+ ([\d \t]+) )? # optional todo set
515 (?: \s* \# \s* ([\w:]+\s?) (.*) )? # optional skip with optional reason
516REGEX
517
518sub _is_header {
519 my($self, $line) = @_;
520
521 if( my($max, $extra) = $line =~ /^1\.\.(\d+)(.*)/ ) {
522 $self->{max} = $max;
523 assert( $self->{max} >= 0, 'Max # of tests looks right' );
0be28027 524
525 if( defined $extra ) {
13287dd5 526 my($todo, $skip, $reason) = $extra =~ /$Extra_Header_Re/xo;
527
528 $self->{todo} = { map { $_ => 1 } split /\s+/, $todo } if $todo;
529
a72fde19 530 if( $self->{max} == 0 ) {
531 $reason = '' unless defined $skip and $skip =~ /^Skip/i;
532 }
533
534 $self->{skip_all} = $reason;
13287dd5 535 }
536
537 return $YES;
538 }
539 else {
540 return $NO;
541 }
542}
543
cf2ab31a 544=head2 C<_is_test>
13287dd5 545
546 my $is_test = $strap->_is_test($line, \%test);
547
548Checks if the $line is a test report (ie. 'ok/not ok'). Reports the
cf2ab31a 549result back in C<%test> which will contain:
13287dd5 550
551 ok did it succeed? This is the literal 'ok' or 'not ok'.
552 name name of the test (if any)
553 number test number (if any)
554
555 type 'todo' or 'skip' (if any)
556 reason why is it todo or skip? (if any)
557
558If will also catch lone 'not' lines, note it saw them
cf2ab31a 559C<< $strap->{saw_lone_not} >> and the line in C<< $strap->{lone_not_line} >>.
13287dd5 560
561=cut
562
563my $Report_Re = <<'REGEX';
564 ^
565 (not\ )? # failure?
566 ok\b
567 (?:\s+(\d+))? # optional test number
568 \s*
569 (.*) # and the rest
570REGEX
571
572my $Extra_Re = <<'REGEX';
573 ^
574 (.*?) (?:(?:[^\\]|^)# (.*))?
575 $
576REGEX
577
578sub _is_test {
579 my($self, $line, $test) = @_;
580
581 # We pulverize the line down into pieces in three parts.
582 if( my($not, $num, $extra) = $line =~ /$Report_Re/ox ) {
e4fc8a1e 583 my ($name, $control) = $extra ? split(/(?:[^\\]|^)#/, $extra) : ();
584 my ($type, $reason) = $control ? $control =~ /^\s*(\S+)(?:\s+(.*))?$/ : ();
13287dd5 585
586 $test->{number} = $num;
587 $test->{ok} = $not ? 0 : 1;
588 $test->{name} = $name;
589
590 if( defined $type ) {
591 $test->{type} = $type =~ /^TODO$/i ? 'todo' :
592 $type =~ /^Skip/i ? 'skip' : 0;
593 }
594 else {
595 $test->{type} = '';
596 }
597 $test->{reason} = $reason;
598
599 return $YES;
600 }
601 else{
e4fc8a1e 602 # Sometimes the "not " and "ok" will be on separate lines on VMS.
13287dd5 603 # We catch this and remember we saw it.
604 if( $line =~ /^not\s+$/ ) {
605 $self->{saw_lone_not} = 1;
606 $self->{lone_not_line} = $self->{line};
607 }
608
609 return $NO;
610 }
611}
612
cf2ab31a 613=head2 C<_is_bail_out>
13287dd5 614
615 my $is_bail_out = $strap->_is_bail_out($line, \$reason);
616
617Checks if the line is a "Bail out!". Places the reason for bailing
618(if any) in $reason.
619
620=cut
621
622sub _is_bail_out {
623 my($self, $line, $reason) = @_;
624
625 if( $line =~ /^Bail out!\s*(.*)/i ) {
626 $$reason = $1 if $1;
627 return $YES;
628 }
629 else {
630 return $NO;
631 }
632}
633
cf2ab31a 634=head2 C<_reset_file_state>
13287dd5 635
636 $strap->_reset_file_state;
637
cf2ab31a 638Resets things like C<< $strap->{max} >> , C<< $strap->{skip_all} >>,
639etc. so it's ready to parse the next file.
13287dd5 640
641=cut
642
643sub _reset_file_state {
644 my($self) = shift;
645
646 delete @{$self}{qw(max skip_all todo)};
647 $self->{line} = 0;
648 $self->{saw_header} = 0;
649 $self->{saw_bailout}= 0;
650 $self->{saw_lone_not} = 0;
651 $self->{lone_not_line} = 0;
652 $self->{bailout_reason} = '';
653 $self->{'next'} = 1;
654}
655
cf2ab31a 656=head1 Results
13287dd5 657
cf2ab31a 658The C<%results> returned from C<analyze()> contain the following
659information:
13287dd5 660
661 passing true if the whole test is considered a pass
662 (or skipped), false if its a failure
663
356733da 664 exit the exit code of the test run, if from a file
665 wait the wait code of the test run, if from a file
666
13287dd5 667 max total tests which should have been run
668 seen total tests actually seen
669 skip_all if the whole test was skipped, this will
670 contain the reason.
671
672 ok number of tests which passed
673 (including todo and skips)
674
675 todo number of todo tests seen
676 bonus number of todo tests which
677 unexpectedly passed
678
679 skip number of tests skipped
680
681So a successful test should have max == seen == ok.
682
683
684There is one final item, the details.
685
686 details an array ref reporting the result of
687 each test looks like this:
688
689 $results{details}[$test_num - 1] =
690 { ok => is the test considered ok?
691 actual_ok => did it literally say 'ok'?
692 name => name of the test (if any)
693 type => 'skip' or 'todo' (if any)
694 reason => reason for the above (if any)
695 };
696
697Element 0 of the details is test #1. I tried it with element 1 being
698#1 and 0 being empty, this is less awkward.
699
cf2ab31a 700=head2 C<_detailize>
13287dd5 701
702 my %details = $strap->_detailize($pass, \%test);
703
cf2ab31a 704Generates the details based on the last test line seen. C<$pass> is
705true if it was considered to be a passed test. C<%test> is the results
706of the test you're summarizing.
13287dd5 707
708=cut
709
710sub _detailize {
711 my($self, $pass, $test) = @_;
712
713 my %details = ( ok => $pass,
714 actual_ok => $test->{ok}
715 );
716
717 assert( !(grep !defined $details{$_}, keys %details),
718 'test contains the ok and actual_ok info' );
719
308957f5 720 # We don't want these to be undef because they are often
721 # checked and don't want the checker to have to deal with
722 # uninitialized vars.
13287dd5 723 foreach my $piece (qw(name type reason)) {
308957f5 724 $details{$piece} = defined $test->{$piece} ? $test->{$piece} : '';
13287dd5 725 }
726
727 return %details;
728}
729
13287dd5 730=head1 EXAMPLES
731
732See F<examples/mini_harness.plx> for an example of use.
733
734=head1 AUTHOR
735
cf2ab31a 736Michael G Schwern C<< <schwern@pobox.com> >>, currently maintained by
737Andy Lester C<< <andy@petdance.com> >>.
13287dd5 738
739=head1 SEE ALSO
740
741L<Test::Harness>
742
743=cut
744
13287dd5 7451;