]>
git.saurik.com Git - apple/security.git/blob - SecurityTests/regressions/inc/Test/Harness/Straps.pm
1 # -*- Mode: cperl; cperl-indent-level: 4 -*-
2 package Test
::Harness
::Straps
;
9 use Test
::Harness
::Assert
;
10 use Test
::Harness
::Iterator
;
11 use Test
::Harness
::Point
;
12 use Test
::Harness
::Results
;
14 # Flags used as return values from our methods. Just for internal
21 Test::Harness::Straps - detailed analysis of test results
25 use Test::Harness::Straps;
27 my $strap = Test::Harness::Straps->new;
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);
35 my %total = $strap->total_results;
37 # Altering the behavior of the strap UNIMPLEMENTED
38 my $verbose_output = $strap->dump_verbose();
39 $strap->dump_verbose_fh($output_filehandle);
44 B<THIS IS ALPHA SOFTWARE> in that the interface is subject to change
45 in incompatible ways. It is otherwise stable.
47 Test::Harness is limited to printing out its results. This makes
48 analysis of the test results difficult for anything but a human. To
49 make it easier for programs to work with test results, we provide
50 Test::Harness::Straps. Instead of printing the results, straps
51 provide them as raw data. You can also configure how the tests are to
54 The interface is currently incomplete. I<Please> contact the author
55 if you'd like a feature added or something change or just have
62 my $strap = Test::Harness::Straps->new;
64 Initialize a new strap.
70 my $self = bless {}, $class;
77 =for private $strap->_init
81 Initialize the internal state of a strap to make it ready for parsing.
88 $self->{_is_vms
} = ( $^O eq 'VMS' );
89 $self->{_is_win32
} = ( $^O =~ /^(MS)?Win32$/ );
90 $self->{_is_macos
} = ( $^O eq 'MacOS' );
95 =head2 $strap->analyze( $name, \@output_lines )
97 my $results = $strap->analyze($name, \@test_output);
99 Analyzes the output of a single test, assigning it the given C<$name>
100 for use in the total report. Returns the C<$results> of the test.
103 C<@test_output> should be the raw output from the test, including
109 my($self, $name, $test_output) = @_;
111 my $it = Test
::Harness
::Iterator-
>new($test_output);
112 return $self->_analyze_iterator($name, $it);
116 sub _analyze_iterator
{
117 my($self, $name, $it) = @_;
119 $self->_reset_file_state;
120 $self->{file
} = $name;
122 my $results = Test
::Harness
::Results-
>new;
124 # Set them up here so callbacks can have them.
125 $self->{totals
}{$name} = $results;
126 while( defined(my $line = $it->next) ) {
127 $self->_analyze_line($line, $results);
128 last if $self->{saw_bailout
};
131 $results->set_skip_all( $self->{skip_all
} ) if defined $self->{skip_all
};
134 (($results->max == 0) && defined $results->skip_all) ||
137 $results->max == $results->seen &&
138 $results->max == $results->ok);
140 $results->set_passing( $passed ? 1 : 0 );
154 my $point = Test
::Harness
::Point-
>from_test_line( $line );
159 $point->set_number( $self->{'next'} ) unless $point->number;
161 # sometimes the 'not ' and the 'ok' are on different lines,
162 # happens often on VMS if you do:
163 # print "not " unless $test;
165 if ( $self->{lone_not_line
} && ($self->{lone_not_line
} == $self->{line
} - 1) ) {
169 if ( $self->{todo
}{$point->number} ) {
170 $point->set_directive_type( 'todo' );
173 if ( $point->is_todo ) {
175 $results->inc_bonus if $point->ok;
177 elsif ( $point->is_skip ) {
181 $results->inc_ok if $point->pass;
183 if ( ($point->number > 100_000) && ($point->number > ($self->{max
}||100_000)) ) {
184 if ( !$self->{too_many_tests
}++ ) {
185 warn "Enormous test number seen [test ", $point->number, "]\n";
186 warn "Can't detailize, too big.\n";
192 actual_ok
=> $point->ok,
193 name
=> _def_or_blank
( $point->description ),
194 type
=> _def_or_blank
( $point->directive_type ),
195 reason
=> _def_or_blank
( $point->directive_reason ),
198 assert
( defined( $details->{ok
} ) && defined( $details->{actual_ok
} ) );
199 $results->set_details( $point->number, $details );
202 elsif ( $line =~ /^not\s+$/ ) {
204 # Sometimes the "not " and "ok" will be on separate lines on VMS.
205 # We catch this and remember we saw it.
206 $self->{lone_not_line
} = $self->{line
};
208 elsif ( $self->_is_header($line) ) {
209 $linetype = 'header';
211 $self->{saw_header
}++;
213 $results->inc_max( $self->{max
} );
215 elsif ( $self->_is_bail_out($line, \
$self->{bailout_reason
}) ) {
216 $linetype = 'bailout';
217 $self->{saw_bailout
} = 1;
219 elsif (my $diagnostics = $self->_is_diagnostic_line( $line )) {
221 # XXX We can throw this away, really.
222 my $test = $results->details->[-1];
223 $test->{diagnostics
} ||= '';
224 $test->{diagnostics
} .= $diagnostics;
230 $self->callback->($self, $line, $linetype, $results) if $self->callback;
232 $self->{'next'} = $point->number + 1 if $point;
236 sub _is_diagnostic_line
{
237 my ($self, $line) = @_;
238 return if index( $line, '# Looks like you failed' ) == 0;
243 =for private $strap->analyze_fh( $name, $test_filehandle )
245 my $results = $strap->analyze_fh($name, $test_filehandle);
247 Like C<analyze>, but it reads from the given filehandle.
252 my($self, $name, $fh) = @_;
254 my $it = Test
::Harness
::Iterator-
>new($fh);
255 return $self->_analyze_iterator($name, $it);
258 =head2 $strap->analyze_file( $test_file )
260 my $results = $strap->analyze_file($test_file);
262 Like C<analyze>, but it runs the given C<$test_file> and parses its
263 results. It will also use that name for the total report.
268 my($self, $file) = @_;
271 $self->{error
} = "$file does not exist";
276 $self->{error
} = "$file is not readable";
280 local $ENV{PERL5LIB
} = $self->_INC2PERL5LIB;
281 if ( $Test::Harness
::Debug
) {
282 local $^W=0; # ignore undef warnings
283 print "# PERL5LIB=$ENV{PERL5LIB}\n";
286 # *sigh* this breaks under taint, but open -| is unportable.
287 my $line = $self->_command_line($file);
289 unless ( open(FILE
, "$line|" )) {
290 print "can't run $file. $!\n";
294 my $results = $self->analyze_fh($file, \
*FILE
);
295 my $exit = close FILE
;
297 $results->set_wait($?);
298 if ( $? && $self->{_is_vms
} ) {
299 $results->set_exit($?);
302 $results->set_exit( _wait2exit
($?) );
304 $results->set_passing(0) unless $? == 0;
306 $self->_restore_PERL5LIB();
312 eval { require POSIX
; &POSIX
::WEXITSTATUS
(0) };
314 *_wait2exit
= sub { $_[0] >> 8 };
317 *_wait2exit
= sub { POSIX
::WEXITSTATUS
($_[0]) }
320 =for private $strap->_command_line( $file )
322 Returns the full command line that will be run to test I<$file>.
330 my $command = $self->_command();
331 my $switches = $self->_switches($file);
333 $file = qq["$file"] if ($file =~ /\s/) && ($file !~ /^".*"$/);
334 my $line = "$command $switches $file";
340 =for private $strap->_command()
342 Returns the command that runs the test. Combine this with C<_switches()>
343 to build a command line.
345 Typically this is C<$^X>, but you can set C<$ENV{HARNESS_PERL}>
346 to use a different Perl than what you're running the harness under.
347 This might be to run a threaded Perl, for example.
349 You can also overload this method if you've built your own strap subclass,
350 such as a PHP interpreter for a PHP-based strap.
357 return $ENV{HARNESS_PERL
} if defined $ENV{HARNESS_PERL
};
358 #return qq["$^X"] if $self->{_is_win32} && ($^X =~ /[^\w\.\/\\]/);
359 return qq["$^X"] if $^X =~ /\s/ and $^X !~ /^["']/;
364 =for private $strap->_switches( $file )
366 Formats and returns the switches necessary to run the test.
371 my($self, $file) = @_;
373 my @existing_switches = $self->_cleaned_switches( $Test::Harness
::Switches
, $ENV{HARNESS_PERL_SWITCHES
} );
374 my @derived_switches;
377 open(TEST
, $file) or print "can't open $file. $!\n";
378 my $shebang = <TEST
>;
379 close(TEST
) or print "can't close $file. $!\n";
381 my $taint = ( $shebang =~ /^#!.*\bperl.*\s-\w*([Tt]+)/ );
382 push( @derived_switches, "-$1" ) if $taint;
384 # When taint mode is on, PERL5LIB is ignored. So we need to put
385 # all that on the command line as -Is.
386 # MacPerl's putenv is broken, so it will not see PERL5LIB, tainted or not.
387 if ( $taint || $self->{_is_macos
} ) {
388 my @inc = $self->_filtered_INC;
389 push @derived_switches, map { "-I$_" } @inc;
392 # Quote the argument if there's any whitespace in it, or if
393 # we're VMS, since VMS requires all parms quoted. Also, don't quote
394 # it if it's already quoted.
395 for ( @derived_switches ) {
396 $_ = qq["$_"] if ((/\s/ || $self->{_is_vms
}) && !/^".*"$/ );
398 return join( " ", @existing_switches, @derived_switches );
401 =for private $strap->_cleaned_switches( @switches_from_user )
403 Returns only defined, non-blank, trimmed switches from the parms passed.
407 sub _cleaned_switches
{
415 next unless defined $switch;
418 push( @switches, $switch ) if $switch ne "";
424 =for private $strap->_INC2PERL5LIB
426 local $ENV{PERL5LIB} = $self->_INC2PERL5LIB;
428 Takes the current value of C<@INC> and turns it into something suitable
429 for putting onto C<PERL5LIB>.
436 $self->{_old5lib
} = $ENV{PERL5LIB
};
438 return join $Config{path_sep
}, $self->_filtered_INC;
441 =for private $strap->_filtered_INC()
443 my @filtered_inc = $self->_filtered_INC;
445 Shortens C<@INC> by removing redundant and unnecessary entries.
446 Necessary for OSes with limited command line lengths, like VMS.
451 my($self, @inc) = @_;
452 @inc = @INC unless @inc;
454 if( $self->{_is_vms
} ) {
455 # VMS has a 255-byte limit on the length of %ENV entries, so
456 # toss the ones that involve perl_root, the install location
457 @inc = grep !/perl_root/i, @inc;
460 elsif ( $self->{_is_win32
} ) {
461 # Lose any trailing backslashes in the Win32 paths
462 s/[\\\/+]$// foreach @inc;
466 $seen{$_}++ foreach $self->_default_inc();
467 @inc = grep !$seen{$_}++, @inc;
473 { # Without caching, _default_inc() takes a huge amount of time
477 my $perl = $self->_command;
478 $cache{$perl} ||= [do {
479 local $ENV{PERL5LIB
};
480 my @inc =`$perl -le "print join qq[\\n], \
@INC"`;
483 return @{$cache{$perl}};
488 =for private $strap->_restore_PERL5LIB()
490 $self->_restore_PERL5LIB;
492 This restores the original value of the C<PERL5LIB> environment variable.
493 Necessary on VMS, otherwise a no-op.
497 sub _restore_PERL5LIB {
500 return unless $self->{_is_vms};
502 if (defined $self->{_old5lib}) {
503 $ENV{PERL5LIB} = $self->{_old5lib};
509 Methods for identifying what sort of line you're looking at.
511 =for private _is_diagnostic
513 my $is_diagnostic = $strap->_is_diagnostic($line, \$comment);
515 Checks if the given line is a comment. If so, it will place it into
516 C<$comment> (sans #).
521 my($self, $line, $comment) = @_;
523 if( $line =~ /^\s*\#(.*)/ ) {
532 =for private _is_header
534 my $is_header = $strap->_is_header($line);
536 Checks if the given line is a header (1..M) line. If so, it places how
537 many tests there will be in C<< $strap->{max} >>, a list of which tests
538 are todo in C<< $strap->{todo} >> and if the whole test was skipped
539 C<< $strap->{skip_all} >> contains the reason.
543 # Regex for parsing a header. Will be run with /x
544 my $Extra_Header_Re = <<'REGEX';
546 (?: \s+ todo \s+ ([\d \t]+) )? # optional todo set
547 (?: \s* \# \s* ([\w:]+\s?) (.*) )? # optional skip with optional reason
551 my($self, $line) = @_;
553 if( my($max, $extra) = $line =~ /^1\.\.(\d+)(.*)/ ) {
555 assert
( $self->{max
} >= 0, 'Max # of tests looks right' );
557 if( defined $extra ) {
558 my($todo, $skip, $reason) = $extra =~ /$Extra_Header_Re/xo;
560 $self->{todo
} = { map { $_ => 1 } split /\s+/, $todo } if $todo;
562 if( $self->{max
} == 0 ) {
563 $reason = '' unless defined $skip and $skip =~ /^Skip/i;
566 $self->{skip_all
} = $reason;
576 =for private _is_bail_out
578 my $is_bail_out = $strap->_is_bail_out($line, \$reason);
580 Checks if the line is a "Bail out!". Places the reason for bailing
586 my($self, $line, $reason) = @_;
588 if( $line =~ /^Bail out!\s*(.*)/i ) {
597 =for private _reset_file_state
599 $strap->_reset_file_state;
601 Resets things like C<< $strap->{max} >> , C<< $strap->{skip_all} >>,
602 etc. so it's ready to parse the next file.
606 sub _reset_file_state
{
609 delete @{$self}{qw(max skip_all todo too_many_tests)};
611 $self->{saw_header
} = 0;
612 $self->{saw_bailout
}= 0;
613 $self->{lone_not_line
} = 0;
614 $self->{bailout_reason
} = '';
620 See F<examples/mini_harness.plx> for an example of use.
624 Michael G Schwern C<< <schwern at pobox.com> >>, currently maintained by
625 Andy Lester C<< <andy at petdance.com> >>.
634 return $_[0] if defined $_[0];
640 $self->{callback
} = shift;
645 return $self->{callback
};