[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/se3-unattended/var/se3/unattended/install/linuxaux/opt/perl/lib/5.10.0/Test/Harness/ -> Straps.pm (source)

   1  # -*- Mode: cperl; cperl-indent-level: 4 -*-
   2  package Test::Harness::Straps;
   3  
   4  use strict;
   5  use vars qw($VERSION);
   6  $VERSION = '0.26_01';
   7  
   8  use Config;
   9  use Test::Harness::Assert;
  10  use Test::Harness::Iterator;
  11  use Test::Harness::Point;
  12  use Test::Harness::Results;
  13  
  14  # Flags used as return values from our methods.  Just for internal 
  15  # clarification.
  16  my $YES   = (1==1);
  17  my $NO    = !$YES;
  18  
  19  =head1 NAME
  20  
  21  Test::Harness::Straps - detailed analysis of test results
  22  
  23  =head1 SYNOPSIS
  24  
  25    use Test::Harness::Straps;
  26  
  27    my $strap = Test::Harness::Straps->new;
  28  
  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);
  33  
  34    # UNIMPLEMENTED
  35    my %total = $strap->total_results;
  36  
  37    # Altering the behavior of the strap  UNIMPLEMENTED
  38    my $verbose_output = $strap->dump_verbose();
  39    $strap->dump_verbose_fh($output_filehandle);
  40  
  41  
  42  =head1 DESCRIPTION
  43  
  44  B<THIS IS ALPHA SOFTWARE> in that the interface is subject to change
  45  in incompatible ways.  It is otherwise stable.
  46  
  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
  52  be run.
  53  
  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
  56  comments.
  57  
  58  =head1 CONSTRUCTION
  59  
  60  =head2 new()
  61  
  62    my $strap = Test::Harness::Straps->new;
  63  
  64  Initialize a new strap.
  65  
  66  =cut
  67  
  68  sub new {
  69      my $class = shift;
  70      my $self  = bless {}, $class;
  71  
  72      $self->_init;
  73  
  74      return $self;
  75  }
  76  
  77  =for private $strap->_init
  78  
  79    $strap->_init;
  80  
  81  Initialize the internal state of a strap to make it ready for parsing.
  82  
  83  =cut
  84  
  85  sub _init {
  86      my($self) = shift;
  87  
  88      $self->{_is_vms}   = ( $^O eq 'VMS' );
  89      $self->{_is_win32} = ( $^O =~ /^(MS)?Win32$/ );
  90      $self->{_is_macos} = ( $^O eq 'MacOS' );
  91  }
  92  
  93  =head1 ANALYSIS
  94  
  95  =head2 $strap->analyze( $name, \@output_lines )
  96  
  97      my $results = $strap->analyze($name, \@test_output);
  98  
  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.
 101  See L<Results>.
 102  
 103  C<@test_output> should be the raw output from the test, including
 104  newlines.
 105  
 106  =cut
 107  
 108  sub analyze {
 109      my($self, $name, $test_output) = @_;
 110  
 111      my $it = Test::Harness::Iterator->new($test_output);
 112      return $self->_analyze_iterator($name, $it);
 113  }
 114  
 115  
 116  sub _analyze_iterator {
 117      my($self, $name, $it) = @_;
 118  
 119      $self->_reset_file_state;
 120      $self->{file} = $name;
 121  
 122      my $results = Test::Harness::Results->new;
 123  
 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};
 129      }
 130  
 131      $results->set_skip_all( $self->{skip_all} ) if defined $self->{skip_all};
 132  
 133      my $passed =
 134          (($results->max == 0) && defined $results->skip_all) ||
 135          ($results->max &&
 136           $results->seen &&
 137           $results->max == $results->seen &&
 138           $results->max == $results->ok);
 139  
 140      $results->set_passing( $passed ? 1 : 0 );
 141  
 142      return $results;
 143  }
 144  
 145  
 146  sub _analyze_line {
 147      my $self = shift;
 148      my $line = shift;
 149      my $results = shift;
 150  
 151      $self->{line}++;
 152  
 153      my $linetype;
 154      my $point = Test::Harness::Point->from_test_line( $line );
 155      if ( $point ) {
 156          $linetype = 'test';
 157  
 158          $results->inc_seen;
 159          $point->set_number( $self->{'next'} ) unless $point->number;
 160  
 161          # sometimes the 'not ' and the 'ok' are on different lines,
 162          # happens often on VMS if you do:
 163          #   print "not " unless $test;
 164          #   print "ok $num\n";
 165          if ( $self->{lone_not_line} && ($self->{lone_not_line} == $self->{line} - 1) ) {
 166              $point->set_ok( 0 );
 167          }
 168  
 169          if ( $self->{todo}{$point->number} ) {
 170              $point->set_directive_type( 'todo' );
 171          }
 172  
 173          if ( $point->is_todo ) {
 174              $results->inc_todo;
 175              $results->inc_bonus if $point->ok;
 176          }
 177          elsif ( $point->is_skip ) {
 178              $results->inc_skip;
 179          }
 180  
 181          $results->inc_ok if $point->pass;
 182  
 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";
 187              }
 188          }
 189          else {
 190              my $details = {
 191                  ok          => $point->pass,
 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 ),
 196              };
 197  
 198              assert( defined( $details->{ok} ) && defined( $details->{actual_ok} ) );
 199              $results->set_details( $point->number, $details );
 200          }
 201      } # test point
 202      elsif ( $line =~ /^not\s+$/ ) {
 203          $linetype = 'other';
 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};
 207      }
 208      elsif ( $self->_is_header($line) ) {
 209          $linetype = 'header';
 210  
 211          $self->{saw_header}++;
 212  
 213          $results->inc_max( $self->{max} );
 214      }
 215      elsif ( $self->_is_bail_out($line, \$self->{bailout_reason}) ) {
 216          $linetype = 'bailout';
 217          $self->{saw_bailout} = 1;
 218      }
 219      elsif (my $diagnostics = $self->_is_diagnostic_line( $line )) {
 220          $linetype = 'other';
 221          # XXX We can throw this away, really.
 222          my $test = $results->details->[-1];
 223          $test->{diagnostics} ||=  '';
 224          $test->{diagnostics}  .= $diagnostics;
 225      }
 226      else {
 227          $linetype = 'other';
 228      }
 229  
 230      $self->callback->($self, $line, $linetype, $results) if $self->callback;
 231  
 232      $self->{'next'} = $point->number + 1 if $point;
 233  } # _analyze_line
 234  
 235  
 236  sub _is_diagnostic_line {
 237      my ($self, $line) = @_;
 238      return if index( $line, '# Looks like you failed' ) == 0;
 239      $line =~ s/^#\s//;
 240      return $line;
 241  }
 242  
 243  =for private $strap->analyze_fh( $name, $test_filehandle )
 244  
 245      my $results = $strap->analyze_fh($name, $test_filehandle);
 246  
 247  Like C<analyze>, but it reads from the given filehandle.
 248  
 249  =cut
 250  
 251  sub analyze_fh {
 252      my($self, $name, $fh) = @_;
 253  
 254      my $it = Test::Harness::Iterator->new($fh);
 255      return $self->_analyze_iterator($name, $it);
 256  }
 257  
 258  =head2 $strap->analyze_file( $test_file )
 259  
 260      my $results = $strap->analyze_file($test_file);
 261  
 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.
 264  
 265  =cut
 266  
 267  sub analyze_file {
 268      my($self, $file) = @_;
 269  
 270      unless( -e $file ) {
 271          $self->{error} = "$file does not exist";
 272          return;
 273      }
 274  
 275      unless( -r $file ) {
 276          $self->{error} = "$file is not readable";
 277          return;
 278      }
 279  
 280      local $ENV{PERL5LIB} = $self->_INC2PERL5LIB;
 281      if ( $Test::Harness::Debug ) {
 282          local $^W=0; # ignore undef warnings
 283          print "# PERL5LIB=$ENV{PERL5LIB}\n";
 284      }
 285  
 286      # *sigh* this breaks under taint, but open -| is unportable.
 287      my $line = $self->_command_line($file);
 288  
 289      unless ( open(FILE, "$line|" )) {
 290          print "can't run $file. $!\n";
 291          return;
 292      }
 293  
 294      my $results = $self->analyze_fh($file, \*FILE);
 295      my $exit    = close FILE;
 296  
 297      $results->set_wait($?);
 298      if ( $? && $self->{_is_vms} ) {
 299          $results->set_exit($?);
 300      }
 301      else {
 302          $results->set_exit( _wait2exit($?) );
 303      }
 304      $results->set_passing(0) unless $? == 0;
 305  
 306      $self->_restore_PERL5LIB();
 307  
 308      return $results;
 309  }
 310  
 311  
 312  eval { require POSIX; &POSIX::WEXITSTATUS(0) };
 313  if( $@ ) {
 314      *_wait2exit = sub { $_[0] >> 8 };
 315  }
 316  else {
 317      *_wait2exit = sub { POSIX::WEXITSTATUS($_[0]) }
 318  }
 319  
 320  =for private $strap->_command_line( $file )
 321  
 322  Returns the full command line that will be run to test I<$file>.
 323  
 324  =cut
 325  
 326  sub _command_line {
 327      my $self = shift;
 328      my $file = shift;
 329  
 330      my $command =  $self->_command();
 331      my $switches = $self->_switches($file);
 332  
 333      $file = qq["$file"] if ($file =~ /\s/) && ($file !~ /^".*"$/);
 334      my $line = "$command $switches $file";
 335  
 336      return $line;
 337  }
 338  
 339  
 340  =for private $strap->_command()
 341  
 342  Returns the command that runs the test.  Combine this with C<_switches()>
 343  to build a command line.
 344  
 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.
 348  
 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.
 351  
 352  =cut
 353  
 354  sub _command {
 355      my $self = shift;
 356  
 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 !~ /^["']/;
 360      return $^X;
 361  }
 362  
 363  
 364  =for private $strap->_switches( $file )
 365  
 366  Formats and returns the switches necessary to run the test.
 367  
 368  =cut
 369  
 370  sub _switches {
 371      my($self, $file) = @_;
 372  
 373      my @existing_switches = $self->_cleaned_switches( $Test::Harness::Switches, $ENV{HARNESS_PERL_SWITCHES} );
 374      my @derived_switches;
 375  
 376      local *TEST;
 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";
 380  
 381      my $taint = ( $shebang =~ /^#!.*\bperl.*\s-\w*([Tt]+)/ );
 382      push( @derived_switches, "-$1" ) if $taint;
 383  
 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;
 390      }
 391  
 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}) && !/^".*"$/ );
 397      }
 398      return join( " ", @existing_switches, @derived_switches );
 399  }
 400  
 401  =for private $strap->_cleaned_switches( @switches_from_user )
 402  
 403  Returns only defined, non-blank, trimmed switches from the parms passed.
 404  
 405  =cut
 406  
 407  sub _cleaned_switches {
 408      my $self = shift;
 409  
 410      local $_;
 411  
 412      my @switches;
 413      for ( @_ ) {
 414      my $switch = $_;
 415      next unless defined $switch;
 416      $switch =~ s/^\s+//;
 417      $switch =~ s/\s+$//;
 418      push( @switches, $switch ) if $switch ne "";
 419      }
 420  
 421      return @switches;
 422  }
 423  
 424  =for private $strap->_INC2PERL5LIB
 425  
 426    local $ENV{PERL5LIB} = $self->_INC2PERL5LIB;
 427  
 428  Takes the current value of C<@INC> and turns it into something suitable
 429  for putting onto C<PERL5LIB>.
 430  
 431  =cut
 432  
 433  sub _INC2PERL5LIB {
 434      my($self) = shift;
 435  
 436      $self->{_old5lib} = $ENV{PERL5LIB};
 437  
 438      return join $Config{path_sep}, $self->_filtered_INC;
 439  }
 440  
 441  =for private $strap->_filtered_INC()
 442  
 443    my @filtered_inc = $self->_filtered_INC;
 444  
 445  Shortens C<@INC> by removing redundant and unnecessary entries.
 446  Necessary for OSes with limited command line lengths, like VMS.
 447  
 448  =cut
 449  
 450  sub _filtered_INC {
 451      my($self, @inc) = @_;
 452      @inc = @INC unless @inc;
 453  
 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;
 458  
 459      }
 460      elsif ( $self->{_is_win32} ) {
 461      # Lose any trailing backslashes in the Win32 paths
 462      s/[\\\/+]$// foreach @inc;
 463      }
 464  
 465      my %seen;
 466      $seen{$_}++ foreach $self->_default_inc();
 467      @inc = grep !$seen{$_}++, @inc;
 468  
 469      return @inc;
 470  }
 471  
 472  
 473  { # Without caching, _default_inc() takes a huge amount of time
 474      my %cache;
 475      sub _default_inc {
 476          my $self = shift;
 477          my $perl = $self->_command;
 478          $cache{$perl} ||= [do {
 479              local $ENV{PERL5LIB};
 480              my @inc =`$perl -le "print join qq[\\n], \@INC"`;
 481              chomp @inc;
 482          }];
 483          return @{$cache{$perl}};
 484      }
 485  }
 486  
 487  
 488  =for private $strap->_restore_PERL5LIB()
 489  
 490    $self->_restore_PERL5LIB;
 491  
 492  This restores the original value of the C<PERL5LIB> environment variable.
 493  Necessary on VMS, otherwise a no-op.
 494  
 495  =cut
 496  
 497  sub _restore_PERL5LIB {
 498      my($self) = shift;
 499  
 500      return unless $self->{_is_vms};
 501  
 502      if (defined $self->{_old5lib}) {
 503          $ENV{PERL5LIB} = $self->{_old5lib};
 504      }
 505  }
 506  
 507  =head1 Parsing
 508  
 509  Methods for identifying what sort of line you're looking at.
 510  
 511  =for private _is_diagnostic
 512  
 513      my $is_diagnostic = $strap->_is_diagnostic($line, \$comment);
 514  
 515  Checks if the given line is a comment.  If so, it will place it into
 516  C<$comment> (sans #).
 517  
 518  =cut
 519  
 520  sub _is_diagnostic {
 521      my($self, $line, $comment) = @_;
 522  
 523      if( $line =~ /^\s*\#(.*)/ ) {
 524          $$comment = $1;
 525          return $YES;
 526      }
 527      else {
 528          return $NO;
 529      }
 530  }
 531  
 532  =for private _is_header
 533  
 534    my $is_header = $strap->_is_header($line);
 535  
 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.
 540  
 541  =cut
 542  
 543  # Regex for parsing a header.  Will be run with /x
 544  my $Extra_Header_Re = <<'REGEX';
 545                         ^
 546                          (?: \s+ todo \s+ ([\d \t]+) )?      # optional todo set
 547                          (?: \s* \# \s* ([\w:]+\s?) (.*) )?     # optional skip with optional reason
 548  REGEX
 549  
 550  sub _is_header {
 551      my($self, $line) = @_;
 552  
 553      if( my($max, $extra) = $line =~ /^1\.\.(\d+)(.*)/ ) {
 554          $self->{max}  = $max;
 555          assert( $self->{max} >= 0,  'Max # of tests looks right' );
 556  
 557          if( defined $extra ) {
 558              my($todo, $skip, $reason) = $extra =~ /$Extra_Header_Re/xo;
 559  
 560              $self->{todo} = { map { $_ => 1 } split /\s+/, $todo } if $todo;
 561  
 562              if( $self->{max} == 0 ) {
 563                  $reason = '' unless defined $skip and $skip =~ /^Skip/i;
 564              }
 565  
 566              $self->{skip_all} = $reason;
 567          }
 568  
 569          return $YES;
 570      }
 571      else {
 572          return $NO;
 573      }
 574  }
 575  
 576  =for private _is_bail_out
 577  
 578    my $is_bail_out = $strap->_is_bail_out($line, \$reason);
 579  
 580  Checks if the line is a "Bail out!".  Places the reason for bailing
 581  (if any) in $reason.
 582  
 583  =cut
 584  
 585  sub _is_bail_out {
 586      my($self, $line, $reason) = @_;
 587  
 588      if( $line =~ /^Bail out!\s*(.*)/i ) {
 589          $$reason = $1 if $1;
 590          return $YES;
 591      }
 592      else {
 593          return $NO;
 594      }
 595  }
 596  
 597  =for private _reset_file_state
 598  
 599    $strap->_reset_file_state;
 600  
 601  Resets things like C<< $strap->{max} >> , C<< $strap->{skip_all} >>,
 602  etc. so it's ready to parse the next file.
 603  
 604  =cut
 605  
 606  sub _reset_file_state {
 607      my($self) = shift;
 608  
 609      delete @{$self}{qw(max skip_all todo too_many_tests)};
 610      $self->{line}       = 0;
 611      $self->{saw_header} = 0;
 612      $self->{saw_bailout}= 0;
 613      $self->{lone_not_line} = 0;
 614      $self->{bailout_reason} = '';
 615      $self->{'next'}       = 1;
 616  }
 617  
 618  =head1 EXAMPLES
 619  
 620  See F<examples/mini_harness.plx> for an example of use.
 621  
 622  =head1 AUTHOR
 623  
 624  Michael G Schwern C<< <schwern at pobox.com> >>, currently maintained by
 625  Andy Lester C<< <andy at petdance.com> >>.
 626  
 627  =head1 SEE ALSO
 628  
 629  L<Test::Harness>
 630  
 631  =cut
 632  
 633  sub _def_or_blank {
 634      return $_[0] if defined $_[0];
 635      return "";
 636  }
 637  
 638  sub set_callback {
 639      my $self = shift;
 640      $self->{callback} = shift;
 641  }
 642  
 643  sub callback {
 644      my $self = shift;
 645      return $self->{callback};
 646  }
 647  
 648  1;


Generated: Tue Mar 17 22:47:18 2015 Cross-referenced by PHPXref 0.7.1