Initial commit of OpenSPARC T2 design and verification files.
[OpenSPARC-T2-DV] / tools / perl-5.8.0 / lib / 5.8.0 / Test / Harness / Straps.pm
CommitLineData
86530b38
AT
1# -*- Mode: cperl; cperl-indent-level: 4 -*-
2# $Id: Straps.pm,v 1.13 2002/06/19 21:01:04 schwern Exp $
3
4package Test::Harness::Straps;
5
6use strict;
7use vars qw($VERSION);
8use Config;
9$VERSION = '0.14';
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
61=head2 Construction
62
63=over 4
64
65=item B<new>
66
67 my $strap = Test::Harness::Straps->new;
68
69Initialize a new strap.
70
71=cut
72
73sub new {
74 my($proto) = shift;
75 my($class) = ref $proto || $proto;
76
77 my $self = bless {}, $class;
78 $self->_init;
79
80 return $self;
81}
82
83=begin _private
84
85=item B<_init>
86
87 $strap->_init;
88
89Initialize the internal state of a strap to make it ready for parsing.
90
91=cut
92
93sub _init {
94 my($self) = shift;
95
96 $self->{_is_vms} = $^O eq 'VMS';
97}
98
99=end _private
100
101=back
102
103=head2 Analysis
104
105=over 4
106
107=item B<analyze>
108
109 my %results = $strap->analyze($name, \@test_output);
110
111Analyzes the output of a single test, assigning it the given $name for
112use in the total report. Returns the %results of the test. See
113L<Results>.
114
115@test_output should be the raw output from the test, including newlines.
116
117=cut
118
119sub analyze {
120 my($self, $name, $test_output) = @_;
121
122 my $it = Test::Harness::Iterator->new($test_output);
123 return $self->_analyze_iterator($name, $it);
124}
125
126
127sub _analyze_iterator {
128 my($self, $name, $it) = @_;
129
130 $self->_reset_file_state;
131 $self->{file} = $name;
132 my %totals = (
133 max => 0,
134 seen => 0,
135
136 ok => 0,
137 todo => 0,
138 skip => 0,
139 bonus => 0,
140
141 details => []
142 );
143
144 # Set them up here so callbacks can have them.
145 $self->{totals}{$name} = \%totals;
146 while( defined(my $line = $it->next) ) {
147 $self->_analyze_line($line, \%totals);
148 last if $self->{saw_bailout};
149 }
150
151 $totals{skip_all} = $self->{skip_all} if defined $self->{skip_all};
152
153 my $passed = !$totals{max} ||
154 ($totals{max} && $totals{seen} &&
155 $totals{max} == $totals{seen} &&
156 $totals{max} == $totals{ok});
157 $totals{passing} = $passed ? 1 : 0;
158
159 return %totals;
160}
161
162
163sub _analyze_line {
164 my($self, $line, $totals) = @_;
165
166 my %result = ();
167
168 $self->{line}++;
169
170 my $type;
171 if( $self->_is_header($line) ) {
172 $type = 'header';
173
174 $self->{saw_header}++;
175
176 $totals->{max} += $self->{max};
177 }
178 elsif( $self->_is_test($line, \%result) ) {
179 $type = 'test';
180
181 $totals->{seen}++;
182 $result{number} = $self->{'next'} unless $result{number};
183
184 # sometimes the 'not ' and the 'ok' are on different lines,
185 # happens often on VMS if you do:
186 # print "not " unless $test;
187 # print "ok $num\n";
188 if( $self->{saw_lone_not} &&
189 ($self->{lone_not_line} == $self->{line} - 1) )
190 {
191 $result{ok} = 0;
192 }
193
194 my $pass = $result{ok};
195 $result{type} = 'todo' if $self->{todo}{$result{number}};
196
197 if( $result{type} eq 'todo' ) {
198 $totals->{todo}++;
199 $pass = 1;
200 $totals->{bonus}++ if $result{ok}
201 }
202 elsif( $result{type} eq 'skip' ) {
203 $totals->{skip}++;
204 $pass = 1;
205 }
206
207 $totals->{ok}++ if $pass;
208
209 if( $result{number} > 100000 ) {
210 warn "Enormous test number seen [test $result{number}]\n";
211 warn "Can't detailize, too big.\n";
212 }
213 else {
214 $totals->{details}[$result{number} - 1] =
215 {$self->_detailize($pass, \%result)};
216 }
217
218 # XXX handle counter mismatch
219 }
220 elsif ( $self->_is_bail_out($line, \$self->{bailout_reason}) ) {
221 $type = 'bailout';
222 $self->{saw_bailout} = 1;
223 }
224 else {
225 $type = 'other';
226 }
227
228 $self->{callback}->($self, $line, $type, $totals) if $self->{callback};
229
230 $self->{'next'} = $result{number} + 1 if $type eq 'test';
231}
232
233=item B<analyze_fh>
234
235 my %results = $strap->analyze_fh($name, $test_filehandle);
236
237Like C<analyze>, but it reads from the given filehandle.
238
239=cut
240
241sub analyze_fh {
242 my($self, $name, $fh) = @_;
243
244 my $it = Test::Harness::Iterator->new($fh);
245 $self->_analyze_iterator($name, $it);
246}
247
248=item B<analyze_file>
249
250 my %results = $strap->analyze_file($test_file);
251
252Like C<analyze>, but it runs the given $test_file and parses it's
253results. It will also use that name for the total report.
254
255=cut
256
257sub analyze_file {
258 my($self, $file) = @_;
259
260 unless( -e $file ) {
261 $self->{error} = "$file does not exist";
262 return;
263 }
264
265 unless( -r $file ) {
266 $self->{error} = "$file is not readable";
267 return;
268 }
269
270 local $ENV{PERL5LIB} = $self->_INC2PERL5LIB;
271
272 # Is this necessary anymore?
273 my $cmd = $self->{_is_vms} ? "MCR $^X" : $^X;
274
275 my $switches = $self->_switches($file);
276
277 # *sigh* this breaks under taint, but open -| is unportable.
278 unless( open(FILE, "$cmd $switches $file|") ) {
279 print "can't run $file. $!\n";
280 return;
281 }
282
283 my %results = $self->analyze_fh($file, \*FILE);
284 my $exit = close FILE;
285 $results{'wait'} = $?;
286 if( $? && $self->{_is_vms} ) {
287 eval q{use vmsish "status"; $results{'exit'} = $?};
288 }
289 else {
290 $results{'exit'} = _wait2exit($?);
291 }
292 $results{passing} = 0 unless $? == 0;
293
294 $self->_restore_PERL5LIB();
295
296 return %results;
297}
298
299
300eval { require POSIX; &POSIX::WEXITSTATUS(0) };
301if( $@ ) {
302 *_wait2exit = sub { $_[0] >> 8 };
303}
304else {
305 *_wait2exit = sub { POSIX::WEXITSTATUS($_[0]) }
306}
307
308
309=begin _private
310
311=item B<_switches>
312
313 my $switches = $self->_switches($file);
314
315Formats and returns the switches necessary to run the test.
316
317=cut
318
319sub _switches {
320 my($self, $file) = @_;
321
322 local *TEST;
323 open(TEST, $file) or print "can't open $file. $!\n";
324 my $first = <TEST>;
325 my $s = '';
326 $s .= " $ENV{'HARNESS_PERL_SWITCHES'}"
327 if exists $ENV{'HARNESS_PERL_SWITCHES'};
328
329 if ($first =~ /^#!.*\bperl.*\s-\w*([Tt]+)/) {
330 # When taint mode is on, PERL5LIB is ignored. So we need to put
331 # all that on the command line as -Is.
332 $s .= join " ", qq[ "-$1"], map {qq["-I$_"]} $self->_filtered_INC;
333 }
334 elsif ($^O eq 'MacOS') {
335 # MacPerl's putenv is broken, so it will not see PERL5LIB.
336 $s .= join " ", map {qq["-I$_"]} $self->_filtered_INC;
337 }
338
339 close(TEST) or print "can't close $file. $!\n";
340
341 return $s;
342}
343
344
345=item B<_INC2PERL5LIB>
346
347 local $ENV{PERL5LIB} = $self->_INC2PERL5LIB;
348
349Takes the current value of @INC and turns it into something suitable
350for putting onto PERL5LIB.
351
352=cut
353
354sub _INC2PERL5LIB {
355 my($self) = shift;
356
357 $self->{_old5lib} = $ENV{PERL5LIB};
358
359 return join $Config{path_sep}, $self->_filtered_INC;
360}
361
362=item B<_filtered_INC>
363
364 my @filtered_inc = $self->_filtered_INC;
365
366Shortens @INC by removing redundant and unnecessary entries.
367Necessary for OS's with limited command line lengths, like VMS.
368
369=cut
370
371sub _filtered_INC {
372 my($self, @inc) = @_;
373 @inc = @INC unless @inc;
374
375 # VMS has a 255-byte limit on the length of %ENV entries, so
376 # toss the ones that involve perl_root, the install location
377 # for VMS
378 if( $self->{_is_vms} ) {
379 @inc = grep !/perl_root/i, @inc;
380 }
381
382 return @inc;
383}
384
385
386=item B<_restore_PERL5LIB>
387
388 $self->_restore_PERL5LIB;
389
390This restores the original value of the PERL5LIB environment variable.
391Necessary on VMS, otherwise a no-op.
392
393=cut
394
395sub _restore_PERL5LIB {
396 my($self) = shift;
397
398 return unless $self->{_is_vms};
399
400 if (defined $self->{_old5lib}) {
401 $ENV{PERL5LIB} = $self->{_old5lib};
402 }
403}
404
405
406=end _private
407
408=back
409
410
411=begin _private
412
413=head2 Parsing
414
415Methods for identifying what sort of line you're looking at.
416
417=over 4
418
419=item B<_is_comment>
420
421 my $is_comment = $strap->_is_comment($line, \$comment);
422
423Checks if the given line is a comment. If so, it will place it into
424$comment (sans #).
425
426=cut
427
428sub _is_comment {
429 my($self, $line, $comment) = @_;
430
431 if( $line =~ /^\s*\#(.*)/ ) {
432 $$comment = $1;
433 return $YES;
434 }
435 else {
436 return $NO;
437 }
438}
439
440=item B<_is_header>
441
442 my $is_header = $strap->_is_header($line);
443
444Checks if the given line is a header (1..M) line. If so, it places
445how many tests there will be in $strap->{max}, a list of which tests
446are todo in $strap->{todo} and if the whole test was skipped
447$strap->{skip_all} contains the reason.
448
449=cut
450
451# Regex for parsing a header. Will be run with /x
452my $Extra_Header_Re = <<'REGEX';
453 ^
454 (?: \s+ todo \s+ ([\d \t]+) )? # optional todo set
455 (?: \s* \# \s* ([\w:]+\s?) (.*) )? # optional skip with optional reason
456REGEX
457
458sub _is_header {
459 my($self, $line) = @_;
460
461 if( my($max, $extra) = $line =~ /^1\.\.(\d+)(.*)/ ) {
462 $self->{max} = $max;
463 assert( $self->{max} >= 0, 'Max # of tests looks right' );
464
465 if( defined $extra ) {
466 my($todo, $skip, $reason) = $extra =~ /$Extra_Header_Re/xo;
467
468 $self->{todo} = { map { $_ => 1 } split /\s+/, $todo } if $todo;
469
470 $self->{skip_all} = $reason if defined $skip and $skip =~ /^Skip/i;
471 }
472
473 return $YES;
474 }
475 else {
476 return $NO;
477 }
478}
479
480=item B<_is_test>
481
482 my $is_test = $strap->_is_test($line, \%test);
483
484Checks if the $line is a test report (ie. 'ok/not ok'). Reports the
485result back in %test which will contain:
486
487 ok did it succeed? This is the literal 'ok' or 'not ok'.
488 name name of the test (if any)
489 number test number (if any)
490
491 type 'todo' or 'skip' (if any)
492 reason why is it todo or skip? (if any)
493
494If will also catch lone 'not' lines, note it saw them
495$strap->{saw_lone_not} and the line in $strap->{lone_not_line}.
496
497=cut
498
499my $Report_Re = <<'REGEX';
500 ^
501 (not\ )? # failure?
502 ok\b
503 (?:\s+(\d+))? # optional test number
504 \s*
505 (.*) # and the rest
506REGEX
507
508my $Extra_Re = <<'REGEX';
509 ^
510 (.*?) (?:(?:[^\\]|^)# (.*))?
511 $
512REGEX
513
514sub _is_test {
515 my($self, $line, $test) = @_;
516
517 # We pulverize the line down into pieces in three parts.
518 if( my($not, $num, $extra) = $line =~ /$Report_Re/ox ) {
519 my($name, $control) = split /(?:[^\\]|^)#/, $extra if $extra;
520 my($type, $reason) = $control =~ /^\s*(\S+)(?:\s+(.*))?$/ if $control;
521
522 $test->{number} = $num;
523 $test->{ok} = $not ? 0 : 1;
524 $test->{name} = $name;
525
526 if( defined $type ) {
527 $test->{type} = $type =~ /^TODO$/i ? 'todo' :
528 $type =~ /^Skip/i ? 'skip' : 0;
529 }
530 else {
531 $test->{type} = '';
532 }
533 $test->{reason} = $reason;
534
535 return $YES;
536 }
537 else{
538 # Sometimes the "not " and "ok" will be on seperate lines on VMS.
539 # We catch this and remember we saw it.
540 if( $line =~ /^not\s+$/ ) {
541 $self->{saw_lone_not} = 1;
542 $self->{lone_not_line} = $self->{line};
543 }
544
545 return $NO;
546 }
547}
548
549=item B<_is_bail_out>
550
551 my $is_bail_out = $strap->_is_bail_out($line, \$reason);
552
553Checks if the line is a "Bail out!". Places the reason for bailing
554(if any) in $reason.
555
556=cut
557
558sub _is_bail_out {
559 my($self, $line, $reason) = @_;
560
561 if( $line =~ /^Bail out!\s*(.*)/i ) {
562 $$reason = $1 if $1;
563 return $YES;
564 }
565 else {
566 return $NO;
567 }
568}
569
570=item B<_reset_file_state>
571
572 $strap->_reset_file_state;
573
574Resets things like $strap->{max}, $strap->{skip_all}, etc... so its
575ready to parse the next file.
576
577=cut
578
579sub _reset_file_state {
580 my($self) = shift;
581
582 delete @{$self}{qw(max skip_all todo)};
583 $self->{line} = 0;
584 $self->{saw_header} = 0;
585 $self->{saw_bailout}= 0;
586 $self->{saw_lone_not} = 0;
587 $self->{lone_not_line} = 0;
588 $self->{bailout_reason} = '';
589 $self->{'next'} = 1;
590}
591
592=back
593
594=end _private
595
596
597=head2 Results
598
599The %results returned from analyze() contain the following information:
600
601 passing true if the whole test is considered a pass
602 (or skipped), false if its a failure
603
604 exit the exit code of the test run, if from a file
605 wait the wait code of the test run, if from a file
606
607 max total tests which should have been run
608 seen total tests actually seen
609 skip_all if the whole test was skipped, this will
610 contain the reason.
611
612 ok number of tests which passed
613 (including todo and skips)
614
615 todo number of todo tests seen
616 bonus number of todo tests which
617 unexpectedly passed
618
619 skip number of tests skipped
620
621So a successful test should have max == seen == ok.
622
623
624There is one final item, the details.
625
626 details an array ref reporting the result of
627 each test looks like this:
628
629 $results{details}[$test_num - 1] =
630 { ok => is the test considered ok?
631 actual_ok => did it literally say 'ok'?
632 name => name of the test (if any)
633 type => 'skip' or 'todo' (if any)
634 reason => reason for the above (if any)
635 };
636
637Element 0 of the details is test #1. I tried it with element 1 being
638#1 and 0 being empty, this is less awkward.
639
640=begin _private
641
642=over 4
643
644=item B<_detailize>
645
646 my %details = $strap->_detailize($pass, \%test);
647
648Generates the details based on the last test line seen. $pass is true
649if it was considered to be a passed test. %test is the results of the
650test you're summarizing.
651
652=cut
653
654sub _detailize {
655 my($self, $pass, $test) = @_;
656
657 my %details = ( ok => $pass,
658 actual_ok => $test->{ok}
659 );
660
661 assert( !(grep !defined $details{$_}, keys %details),
662 'test contains the ok and actual_ok info' );
663
664 # We don't want these to be undef because they are often
665 # checked and don't want the checker to have to deal with
666 # uninitialized vars.
667 foreach my $piece (qw(name type reason)) {
668 $details{$piece} = defined $test->{$piece} ? $test->{$piece} : '';
669 }
670
671 return %details;
672}
673
674=back
675
676=end _private
677
678=head1 EXAMPLES
679
680See F<examples/mini_harness.plx> for an example of use.
681
682=head1 AUTHOR
683
684Michael G Schwern E<lt>schwern@pobox.comE<gt>
685
686=head1 SEE ALSO
687
688L<Test::Harness>
689
690=cut
691
692
6931;