C++ comments, bad.
[p5sagit/p5-mst-13.2.git] / lib / Test / Harness / Straps.pm
CommitLineData
13287dd5 1# -*- Mode: cperl; cperl-indent-level: 4 -*-
e4fc8a1e 2# $Id: Straps.pm,v 1.34 2003/11/23 00:02:11 andy Exp $
13287dd5 3
4package Test::Harness::Straps;
5
6use strict;
7use vars qw($VERSION);
8use Config;
e4fc8a1e 9$VERSION = '0.18';
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
375 # Quote all switches to prevent shell interference, or VMS downcasing
376 for ( @derived_switches ) {
bf9c4ad2 377 $_ = qq["$_"] if /\S/ && !/^".*"$/;
11c6125c 378 }
e4fc8a1e 379 return join( " ", @existing_switches, @derived_switches );
380}
13287dd5 381
e4fc8a1e 382=head2 C<_cleaned_switches>
13287dd5 383
e4fc8a1e 384 my @switches = $self->_cleaned_switches( @switches_from_user );
13287dd5 385
e4fc8a1e 386Returns only defined, non-blank, trimmed switches from the parms passed.
387
388=cut
389
390sub _cleaned_switches {
391 my $self = shift;
392
393 local $_;
394
395 my @switches;
396 for ( @_ ) {
397 my $switch = $_;
398 next unless defined $switch;
399 $switch =~ s/^\s+//;
400 $switch =~ s/\s+$//;
401 push( @switches, $switch ) if $switch ne "";
402 }
403
404 return @switches;
405}
13287dd5 406
cf2ab31a 407=head2 C<_INC2PERL5LIB>
13287dd5 408
409 local $ENV{PERL5LIB} = $self->_INC2PERL5LIB;
410
cf2ab31a 411Takes the current value of C<@INC> and turns it into something suitable
412for putting onto C<PERL5LIB>.
13287dd5 413
414=cut
415
416sub _INC2PERL5LIB {
417 my($self) = shift;
418
419 $self->{_old5lib} = $ENV{PERL5LIB};
420
27caa5c1 421 return join $Config{path_sep}, $self->_filtered_INC;
d5d4ec93 422}
13287dd5 423
cf2ab31a 424=head2 C<_filtered_INC>
13287dd5 425
426 my @filtered_inc = $self->_filtered_INC;
427
cf2ab31a 428Shortens C<@INC> by removing redundant and unnecessary entries.
429Necessary for OSes with limited command line lengths, like VMS.
13287dd5 430
431=cut
432
433sub _filtered_INC {
434 my($self, @inc) = @_;
435 @inc = @INC unless @inc;
436
13287dd5 437 if( $self->{_is_vms} ) {
e4fc8a1e 438 # VMS has a 255-byte limit on the length of %ENV entries, so
439 # toss the ones that involve perl_root, the install location
13287dd5 440 @inc = grep !/perl_root/i, @inc;
e4fc8a1e 441
442 } elsif ( $self->{_is_win32} ) {
443 # Lose any trailing backslashes in the Win32 paths
444 s/[\\\/+]$// foreach @inc;
13287dd5 445 }
446
e4fc8a1e 447 my %dupes;
448 @inc = grep !$dupes{$_}++, @inc;
449
13287dd5 450 return @inc;
451}
452
453
cf2ab31a 454=head2 C<_restore_PERL5LIB>
13287dd5 455
456 $self->_restore_PERL5LIB;
457
cf2ab31a 458This restores the original value of the C<PERL5LIB> environment variable.
13287dd5 459Necessary on VMS, otherwise a no-op.
460
461=cut
462
463sub _restore_PERL5LIB {
464 my($self) = shift;
465
466 return unless $self->{_is_vms};
467
468 if (defined $self->{_old5lib}) {
469 $ENV{PERL5LIB} = $self->{_old5lib};
470 }
471}
d5d4ec93 472
cf2ab31a 473=head1 Parsing
13287dd5 474
475Methods for identifying what sort of line you're looking at.
476
cf2ab31a 477=head2 C<_is_comment>
13287dd5 478
479 my $is_comment = $strap->_is_comment($line, \$comment);
480
481Checks if the given line is a comment. If so, it will place it into
cf2ab31a 482C<$comment> (sans #).
13287dd5 483
484=cut
485
486sub _is_comment {
487 my($self, $line, $comment) = @_;
488
489 if( $line =~ /^\s*\#(.*)/ ) {
490 $$comment = $1;
491 return $YES;
492 }
493 else {
494 return $NO;
495 }
496}
497
cf2ab31a 498=head2 C<_is_header>
13287dd5 499
500 my $is_header = $strap->_is_header($line);
501
cf2ab31a 502Checks if the given line is a header (1..M) line. If so, it places how
503many tests there will be in C<< $strap->{max} >>, a list of which tests
504are todo in C<< $strap->{todo} >> and if the whole test was skipped
505C<< $strap->{skip_all} >> contains the reason.
13287dd5 506
507=cut
508
509# Regex for parsing a header. Will be run with /x
510my $Extra_Header_Re = <<'REGEX';
511 ^
512 (?: \s+ todo \s+ ([\d \t]+) )? # optional todo set
513 (?: \s* \# \s* ([\w:]+\s?) (.*) )? # optional skip with optional reason
514REGEX
515
516sub _is_header {
517 my($self, $line) = @_;
518
519 if( my($max, $extra) = $line =~ /^1\.\.(\d+)(.*)/ ) {
520 $self->{max} = $max;
521 assert( $self->{max} >= 0, 'Max # of tests looks right' );
0be28027 522
523 if( defined $extra ) {
13287dd5 524 my($todo, $skip, $reason) = $extra =~ /$Extra_Header_Re/xo;
525
526 $self->{todo} = { map { $_ => 1 } split /\s+/, $todo } if $todo;
527
a72fde19 528 if( $self->{max} == 0 ) {
529 $reason = '' unless defined $skip and $skip =~ /^Skip/i;
530 }
531
532 $self->{skip_all} = $reason;
13287dd5 533 }
534
535 return $YES;
536 }
537 else {
538 return $NO;
539 }
540}
541
cf2ab31a 542=head2 C<_is_test>
13287dd5 543
544 my $is_test = $strap->_is_test($line, \%test);
545
546Checks if the $line is a test report (ie. 'ok/not ok'). Reports the
cf2ab31a 547result back in C<%test> which will contain:
13287dd5 548
549 ok did it succeed? This is the literal 'ok' or 'not ok'.
550 name name of the test (if any)
551 number test number (if any)
552
553 type 'todo' or 'skip' (if any)
554 reason why is it todo or skip? (if any)
555
556If will also catch lone 'not' lines, note it saw them
cf2ab31a 557C<< $strap->{saw_lone_not} >> and the line in C<< $strap->{lone_not_line} >>.
13287dd5 558
559=cut
560
561my $Report_Re = <<'REGEX';
562 ^
563 (not\ )? # failure?
564 ok\b
565 (?:\s+(\d+))? # optional test number
566 \s*
567 (.*) # and the rest
568REGEX
569
570my $Extra_Re = <<'REGEX';
571 ^
572 (.*?) (?:(?:[^\\]|^)# (.*))?
573 $
574REGEX
575
576sub _is_test {
577 my($self, $line, $test) = @_;
578
579 # We pulverize the line down into pieces in three parts.
580 if( my($not, $num, $extra) = $line =~ /$Report_Re/ox ) {
e4fc8a1e 581 my ($name, $control) = $extra ? split(/(?:[^\\]|^)#/, $extra) : ();
582 my ($type, $reason) = $control ? $control =~ /^\s*(\S+)(?:\s+(.*))?$/ : ();
13287dd5 583
584 $test->{number} = $num;
585 $test->{ok} = $not ? 0 : 1;
586 $test->{name} = $name;
587
588 if( defined $type ) {
589 $test->{type} = $type =~ /^TODO$/i ? 'todo' :
590 $type =~ /^Skip/i ? 'skip' : 0;
591 }
592 else {
593 $test->{type} = '';
594 }
595 $test->{reason} = $reason;
596
597 return $YES;
598 }
599 else{
e4fc8a1e 600 # Sometimes the "not " and "ok" will be on separate lines on VMS.
13287dd5 601 # We catch this and remember we saw it.
602 if( $line =~ /^not\s+$/ ) {
603 $self->{saw_lone_not} = 1;
604 $self->{lone_not_line} = $self->{line};
605 }
606
607 return $NO;
608 }
609}
610
cf2ab31a 611=head2 C<_is_bail_out>
13287dd5 612
613 my $is_bail_out = $strap->_is_bail_out($line, \$reason);
614
615Checks if the line is a "Bail out!". Places the reason for bailing
616(if any) in $reason.
617
618=cut
619
620sub _is_bail_out {
621 my($self, $line, $reason) = @_;
622
623 if( $line =~ /^Bail out!\s*(.*)/i ) {
624 $$reason = $1 if $1;
625 return $YES;
626 }
627 else {
628 return $NO;
629 }
630}
631
cf2ab31a 632=head2 C<_reset_file_state>
13287dd5 633
634 $strap->_reset_file_state;
635
cf2ab31a 636Resets things like C<< $strap->{max} >> , C<< $strap->{skip_all} >>,
637etc. so it's ready to parse the next file.
13287dd5 638
639=cut
640
641sub _reset_file_state {
642 my($self) = shift;
643
644 delete @{$self}{qw(max skip_all todo)};
645 $self->{line} = 0;
646 $self->{saw_header} = 0;
647 $self->{saw_bailout}= 0;
648 $self->{saw_lone_not} = 0;
649 $self->{lone_not_line} = 0;
650 $self->{bailout_reason} = '';
651 $self->{'next'} = 1;
652}
653
cf2ab31a 654=head1 Results
13287dd5 655
cf2ab31a 656The C<%results> returned from C<analyze()> contain the following
657information:
13287dd5 658
659 passing true if the whole test is considered a pass
660 (or skipped), false if its a failure
661
356733da 662 exit the exit code of the test run, if from a file
663 wait the wait code of the test run, if from a file
664
13287dd5 665 max total tests which should have been run
666 seen total tests actually seen
667 skip_all if the whole test was skipped, this will
668 contain the reason.
669
670 ok number of tests which passed
671 (including todo and skips)
672
673 todo number of todo tests seen
674 bonus number of todo tests which
675 unexpectedly passed
676
677 skip number of tests skipped
678
679So a successful test should have max == seen == ok.
680
681
682There is one final item, the details.
683
684 details an array ref reporting the result of
685 each test looks like this:
686
687 $results{details}[$test_num - 1] =
688 { ok => is the test considered ok?
689 actual_ok => did it literally say 'ok'?
690 name => name of the test (if any)
691 type => 'skip' or 'todo' (if any)
692 reason => reason for the above (if any)
693 };
694
695Element 0 of the details is test #1. I tried it with element 1 being
696#1 and 0 being empty, this is less awkward.
697
cf2ab31a 698=head2 C<_detailize>
13287dd5 699
700 my %details = $strap->_detailize($pass, \%test);
701
cf2ab31a 702Generates the details based on the last test line seen. C<$pass> is
703true if it was considered to be a passed test. C<%test> is the results
704of the test you're summarizing.
13287dd5 705
706=cut
707
708sub _detailize {
709 my($self, $pass, $test) = @_;
710
711 my %details = ( ok => $pass,
712 actual_ok => $test->{ok}
713 );
714
715 assert( !(grep !defined $details{$_}, keys %details),
716 'test contains the ok and actual_ok info' );
717
308957f5 718 # We don't want these to be undef because they are often
719 # checked and don't want the checker to have to deal with
720 # uninitialized vars.
13287dd5 721 foreach my $piece (qw(name type reason)) {
308957f5 722 $details{$piece} = defined $test->{$piece} ? $test->{$piece} : '';
13287dd5 723 }
724
725 return %details;
726}
727
13287dd5 728=head1 EXAMPLES
729
730See F<examples/mini_harness.plx> for an example of use.
731
732=head1 AUTHOR
733
cf2ab31a 734Michael G Schwern C<< <schwern@pobox.com> >>, currently maintained by
735Andy Lester C<< <andy@petdance.com> >>.
13287dd5 736
737=head1 SEE ALSO
738
739L<Test::Harness>
740
741=cut
742
13287dd5 7431;