| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package TAPx::Harness; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 2 |  |  | 2 |  | 36403 | use strict; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 80 |  | 
| 4 | 2 |  |  | 2 |  | 10 | use warnings; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 68 |  | 
| 5 | 2 |  |  | 2 |  | 3080 | use Benchmark; | 
|  | 2 |  |  |  |  | 30875 |  | 
|  | 2 |  |  |  |  | 18 |  | 
| 6 | 2 |  |  | 2 |  | 509 | use File::Spec; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 49 |  | 
| 7 | 2 |  |  | 2 |  | 13 | use File::Path; | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 153 |  | 
| 8 |  |  |  |  |  |  |  | 
| 9 | 2 |  |  | 2 |  | 557 | use TAPx::Base; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 52 |  | 
| 10 | 2 |  |  | 2 |  | 541 | use TAPx::Parser; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 55 |  | 
| 11 | 2 |  |  | 2 |  | 534 | use TAPx::Parser::Aggregator; | 
|  | 2 |  |  |  |  | 6 |  | 
|  | 2 |  |  |  |  | 72 |  | 
| 12 | 2 |  |  | 2 |  | 518 | use TAPx::Parser::YAML; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 117 |  | 
| 13 |  |  |  |  |  |  |  | 
| 14 | 2 |  |  | 2 |  | 13 | use vars qw($VERSION @ISA); | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 413 |  | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | @ISA = qw(TAPx::Base); | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | =head1 NAME | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | TAPx::Harness - Run Perl test scripts with statistics | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | =head1 VERSION | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | Version 0.50_07 | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | =cut | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | $VERSION = '0.50_07'; | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | $ENV{HARNESS_ACTIVE}  = 1; | 
| 31 |  |  |  |  |  |  | $ENV{HARNESS_VERSION} = $VERSION; | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | END { | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | # For VMS. | 
| 36 | 2 |  |  | 2 |  | 3756 | delete $ENV{HARNESS_ACTIVE}; | 
| 37 | 2 |  |  |  |  | 31 | delete $ENV{HARNESS_VERSION}; | 
| 38 |  |  |  |  |  |  | } | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | my $TIME_HIRES; | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | BEGIN { | 
| 43 | 2 |  |  | 2 |  | 192 | eval 'use Time::HiRes qw(time)'; | 
|  | 2 |  |  | 2 |  | 13 |  | 
|  | 2 |  |  |  |  | 5 |  | 
|  | 2 |  |  |  |  | 17 |  | 
| 44 | 2 |  |  |  |  | 1303 | $TIME_HIRES = !$@; | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | } | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | This is a simple test harness which allows tests to be run and results | 
| 51 |  |  |  |  |  |  | automatically aggregated and output to STDOUT. | 
| 52 |  |  |  |  |  |  |  | 
| 53 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 54 |  |  |  |  |  |  |  | 
| 55 |  |  |  |  |  |  | use TAPx::Harness; | 
| 56 |  |  |  |  |  |  | my $harness = TAPx::Harness->new( \%args ); | 
| 57 |  |  |  |  |  |  | $harness->runtests(@tests); | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | =cut | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | my %VALIDATION_FOR; | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | sub _error { | 
| 64 | 50 |  |  | 50 |  | 72 | my $self = shift; | 
| 65 | 50 | 100 |  |  |  | 176 | return $self->{error} unless @_; | 
| 66 | 6 |  |  |  |  | 18 | $self->{error} = shift; | 
| 67 |  |  |  |  |  |  | } | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | BEGIN { | 
| 70 |  |  |  |  |  |  | %VALIDATION_FOR = ( | 
| 71 |  |  |  |  |  |  | lib => sub { | 
| 72 | 8 |  |  |  |  | 12 | my ( $self, $libs ) = @_; | 
| 73 | 8 | 100 |  |  |  | 32 | $libs = [$libs] unless 'ARRAY' eq ref $libs; | 
| 74 | 8 |  |  |  |  | 11 | my @bad_libs; | 
| 75 | 8 |  |  |  |  | 19 | foreach my $lib (@$libs) { | 
| 76 | 12 | 100 |  |  |  | 209 | unless ( -d $lib ) { | 
| 77 | 6 |  |  |  |  | 13 | push @bad_libs, $lib; | 
| 78 |  |  |  |  |  |  | } | 
| 79 |  |  |  |  |  |  | } | 
| 80 | 8 | 100 |  |  |  | 26 | if (@bad_libs) { | 
| 81 | 4 |  |  |  |  | 8 | my $dirs = 'lib'; | 
| 82 | 4 | 100 |  |  |  | 12 | $dirs .= 's' if @bad_libs > 1; | 
| 83 | 4 |  |  |  |  | 30 | $self->_error("No such $dirs (@bad_libs)"); | 
| 84 |  |  |  |  |  |  | } | 
| 85 | 8 |  |  |  |  | 16 | return [ map {"-I$_"} @$libs ]; | 
|  | 12 |  |  |  |  | 46 |  | 
| 86 |  |  |  |  |  |  | }, | 
| 87 |  |  |  |  |  |  | switches => sub { | 
| 88 | 6 |  |  |  |  | 12 | my ( $self, $switches ) = @_; | 
| 89 | 6 | 100 |  |  |  | 25 | $switches = [$switches] unless 'ARRAY' eq ref $switches; | 
| 90 | 6 | 100 |  |  |  | 116 | my @switches = map { /^-/ ? $_ : "-$_" } @$switches; | 
|  | 10 |  |  |  |  | 54 |  | 
| 91 | 6 |  |  |  |  | 9 | my %found = map { $_ => 0 } @switches; | 
|  | 10 |  |  |  |  | 33 |  | 
| 92 | 6 |  |  |  |  | 11 | @switches = grep { !$found{$_}++ } @switches; | 
|  | 10 |  |  |  |  | 32 |  | 
| 93 | 6 |  |  |  |  | 20 | return \@switches; | 
| 94 |  |  |  |  |  |  | }, | 
| 95 | 1 |  |  |  |  | 3 | directives   => sub { shift; shift }, | 
|  | 1 |  |  |  |  | 3 |  | 
| 96 | 6 |  |  |  |  | 9 | verbose      => sub { shift; shift }, | 
|  | 6 |  |  |  |  | 12 |  | 
| 97 | 0 |  |  |  |  | 0 | timer        => sub { shift; shift }, | 
|  | 0 |  |  |  |  | 0 |  | 
| 98 | 4 |  |  |  |  | 8 | failures     => sub { shift; shift }, | 
|  | 4 |  |  |  |  | 6 |  | 
| 99 | 0 |  |  |  |  | 0 | errors       => sub { shift; shift }, | 
|  | 0 |  |  |  |  | 0 |  | 
| 100 | 3 |  |  |  |  | 5 | quiet        => sub { shift; shift }, | 
|  | 3 |  |  |  |  | 5 |  | 
| 101 | 3 |  |  |  |  | 7 | really_quiet => sub { shift; shift }, | 
|  | 3 |  |  |  |  | 5 |  | 
| 102 | 3 |  |  |  |  | 6 | exec         => sub { shift; shift }, | 
|  | 3 |  |  |  |  | 7 |  | 
| 103 |  |  |  |  |  |  | execrc => sub { | 
| 104 | 4 |  |  |  |  | 10 | my ( $self, $execrc ) = @_; | 
| 105 | 4 | 100 |  |  |  | 112 | unless ( -f $execrc ) { | 
| 106 | 2 |  |  |  |  | 13 | $self->_error("Cannot find execrc ($execrc)"); | 
| 107 |  |  |  |  |  |  | } | 
| 108 | 4 |  |  |  |  | 11 | return $execrc; | 
| 109 |  |  |  |  |  |  | }, | 
| 110 | 2 |  |  | 2 |  | 76 | ); | 
| 111 | 2 |  |  |  |  | 8 | my @getter_setters = qw/ | 
| 112 |  |  |  |  |  |  | _curr_parser | 
| 113 |  |  |  |  |  |  | _curr_test | 
| 114 |  |  |  |  |  |  | _execrc | 
| 115 |  |  |  |  |  |  | _longest | 
| 116 |  |  |  |  |  |  | _newline_printed | 
| 117 |  |  |  |  |  |  | _printed_summary_header | 
| 118 |  |  |  |  |  |  | /; | 
| 119 |  |  |  |  |  |  |  | 
| 120 | 2 |  |  |  |  | 11 | foreach my $method ( @getter_setters, keys %VALIDATION_FOR ) { | 
| 121 | 2 |  |  | 2 |  | 13 | no strict 'refs'; | 
|  | 2 |  |  |  |  | 2 |  | 
|  | 2 |  |  |  |  | 418 |  | 
| 122 | 34 | 100 | 100 |  |  | 126 | if ( $method eq 'lib' || $method eq 'switches' ) { | 
| 123 |  |  |  |  |  |  | *$method = sub { | 
| 124 | 60 |  |  | 60 |  | 98 | my $self = shift; | 
| 125 | 60 | 100 |  |  |  | 149 | unless (@_) { | 
| 126 | 50 |  | 100 |  |  | 206 | $self->{$method} ||= []; | 
| 127 |  |  |  |  |  |  | return | 
| 128 | 50 | 100 |  |  |  | 220 | wantarray ? @{ $self->{$method} } : $self->{$method}; | 
|  | 20 |  |  |  |  | 62 |  | 
| 129 |  |  |  |  |  |  | } | 
| 130 | 10 | 50 |  |  |  | 27 | $self->_croak("Too many arguments to &\$method") | 
| 131 |  |  |  |  |  |  | if @_ > 1; | 
| 132 | 10 |  |  |  |  | 15 | my $args = shift; | 
| 133 | 10 | 50 |  |  |  | 24 | $args = [$args] unless ref $args; | 
| 134 | 10 |  |  |  |  | 21 | $self->{$method} = $args; | 
| 135 | 10 |  |  |  |  | 23 | return $self; | 
| 136 | 4 |  |  |  |  | 29 | }; | 
| 137 |  |  |  |  |  |  | } | 
| 138 |  |  |  |  |  |  | else { | 
| 139 |  |  |  |  |  |  | *$method = sub { | 
| 140 | 426 |  |  | 426 |  | 591 | my $self = shift; | 
| 141 | 426 | 100 |  |  |  | 2087 | return $self->{$method} unless @_; | 
| 142 | 123 |  |  |  |  | 336 | $self->{$method} = shift; | 
| 143 | 30 |  |  |  |  | 23837 | }; | 
| 144 |  |  |  |  |  |  | } | 
| 145 |  |  |  |  |  |  | } | 
| 146 |  |  |  |  |  |  | } | 
| 147 |  |  |  |  |  |  |  | 
| 148 |  |  |  |  |  |  | ############################################################################## | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  | =head1 METHODS | 
| 151 |  |  |  |  |  |  |  | 
| 152 |  |  |  |  |  |  | =head2 Class methods | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | =head3 C | 
| 155 |  |  |  |  |  |  |  | 
| 156 |  |  |  |  |  |  | my %args = ( | 
| 157 |  |  |  |  |  |  | verbose => 1, | 
| 158 |  |  |  |  |  |  | lib     => [ 'lib', 'blib/lib' ], | 
| 159 |  |  |  |  |  |  | ) | 
| 160 |  |  |  |  |  |  | my $harness = TAPx::Harness->new( \%args ); | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | The constructor returns a new C object.  It accepts an optional | 
| 163 |  |  |  |  |  |  | hashref whose allowed keys are: | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | =over 4 | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | =item * C | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | Print individual test results to STDOUT. | 
| 170 |  |  |  |  |  |  |  | 
| 171 |  |  |  |  |  |  | =item * C | 
| 172 |  |  |  |  |  |  |  | 
| 173 |  |  |  |  |  |  | Append run time for each test to output. Uses Time::HiRes if available. | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  | =item * C | 
| 176 |  |  |  |  |  |  |  | 
| 177 |  |  |  |  |  |  | Only show test failures (this is a no-op if C is selected). | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | =item * C | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | Accepts a scalar value or array ref of scalar values indicating which paths to | 
| 182 |  |  |  |  |  |  | allowed libraries should be included if Perl tests are executed.  Naturally, | 
| 183 |  |  |  |  |  |  | this only makes sense in the context of tests written in Perl. | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | =item * C | 
| 186 |  |  |  |  |  |  |  | 
| 187 |  |  |  |  |  |  | Accepts a scalar value or array ref of scalar values indicating which switches | 
| 188 |  |  |  |  |  |  | should be included if Perl tests are executed.  Naturally, this only makes | 
| 189 |  |  |  |  |  |  | sense in the context of tests written in Perl. | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | =item * C | 
| 192 |  |  |  |  |  |  |  | 
| 193 |  |  |  |  |  |  | Suppress some test output (mostly failures while tests are running). | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | =item * C | 
| 196 |  |  |  |  |  |  |  | 
| 197 |  |  |  |  |  |  | Suppress everything but the tests summary. | 
| 198 |  |  |  |  |  |  |  | 
| 199 |  |  |  |  |  |  | =item * C | 
| 200 |  |  |  |  |  |  |  | 
| 201 |  |  |  |  |  |  | Typically, Perl tests are run through this.  However, anything which spits out | 
| 202 |  |  |  |  |  |  | TAP is fine.  You can use this argument to specify the name of the program | 
| 203 |  |  |  |  |  |  | (and optional switches) to run your tests with: | 
| 204 |  |  |  |  |  |  |  | 
| 205 |  |  |  |  |  |  | exec => '/usr/bin/ruby -w' | 
| 206 |  |  |  |  |  |  |  | 
| 207 |  |  |  |  |  |  | =item * C | 
| 208 |  |  |  |  |  |  |  | 
| 209 |  |  |  |  |  |  | Location of 'execrc' file.  See L below. | 
| 210 |  |  |  |  |  |  |  | 
| 211 |  |  |  |  |  |  | =item * C | 
| 212 |  |  |  |  |  |  |  | 
| 213 |  |  |  |  |  |  | If parse errors are found in the TAP output, a note of this will be made | 
| 214 |  |  |  |  |  |  | in the summary report.  To see all of the parse errors, set this argument to | 
| 215 |  |  |  |  |  |  | true: | 
| 216 |  |  |  |  |  |  |  | 
| 217 |  |  |  |  |  |  | errors => 1 | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | =item * C | 
| 220 |  |  |  |  |  |  |  | 
| 221 |  |  |  |  |  |  | If set to a true value, only test results with directives will be displayed. | 
| 222 |  |  |  |  |  |  | This overrides other settings such as C or C. | 
| 223 |  |  |  |  |  |  |  | 
| 224 |  |  |  |  |  |  | =back | 
| 225 |  |  |  |  |  |  |  | 
| 226 |  |  |  |  |  |  | =cut | 
| 227 |  |  |  |  |  |  |  | 
| 228 |  |  |  |  |  |  | # new supplied by TAPx::Base | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  | { | 
| 231 |  |  |  |  |  |  | my @legal_callback = qw( | 
| 232 |  |  |  |  |  |  | made_parser | 
| 233 |  |  |  |  |  |  | ); | 
| 234 |  |  |  |  |  |  |  | 
| 235 |  |  |  |  |  |  | sub _initialize { | 
| 236 | 28 |  |  | 28 |  | 37 | my ( $self, $arg_for ) = @_; | 
| 237 | 28 |  | 100 |  |  | 98 | $arg_for ||= {}; | 
| 238 | 28 |  |  |  |  | 122 | $self->SUPER::_initialize( $arg_for, \@legal_callback ); | 
| 239 | 28 |  |  |  |  | 91 | my %arg_for = %$arg_for;    # force a shallow copy | 
| 240 |  |  |  |  |  |  |  | 
| 241 | 28 |  |  |  |  | 110 | foreach my $name ( keys %VALIDATION_FOR ) { | 
| 242 | 296 |  |  |  |  | 332 | my $property = delete $arg_for{$name}; | 
| 243 | 296 | 100 |  |  |  | 591 | if ( defined $property ) { | 
| 244 | 38 |  |  |  |  | 57 | my $validate = $VALIDATION_FOR{$name}; | 
| 245 |  |  |  |  |  |  |  | 
| 246 | 38 |  |  |  |  | 96 | my $value = $self->$validate($property); | 
| 247 | 38 | 100 |  |  |  | 114 | if ( $self->_error ) { | 
| 248 | 6 |  |  |  |  | 15 | $self->_croak; | 
| 249 |  |  |  |  |  |  | } | 
| 250 | 32 |  |  |  |  | 111 | $self->$name($value); | 
| 251 |  |  |  |  |  |  | } | 
| 252 |  |  |  |  |  |  | } | 
| 253 | 22 | 100 |  |  |  | 92 | if ( my @props = keys %arg_for ) { | 
| 254 | 2 |  |  |  |  | 20 | $self->_croak("Unknown arguments to TAPx::Harness::new (@props)"); | 
| 255 |  |  |  |  |  |  | } | 
| 256 | 20 |  |  |  |  | 59 | $self->_read_execrc; | 
| 257 | 20 | 100 |  |  |  | 49 | $self->quiet(0) unless $self->quiet;    # suppress unit warnings | 
| 258 | 20 | 100 |  |  |  | 46 | $self->really_quiet(0) unless $self->really_quiet; | 
| 259 | 20 |  |  |  |  | 105 | return $self; | 
| 260 |  |  |  |  |  |  | } | 
| 261 |  |  |  |  |  |  | } | 
| 262 |  |  |  |  |  |  |  | 
| 263 |  |  |  |  |  |  | sub _read_execrc { | 
| 264 | 20 |  |  | 20 |  | 33 | my $self = shift; | 
| 265 | 20 |  |  |  |  | 61 | $self->_execrc( {} ); | 
| 266 | 20 | 100 |  |  |  | 58 | my $execrc = $self->execrc or return; | 
| 267 | 2 |  |  |  |  | 1007 | my $data   = TAPx::Parser::YAML->read($execrc); | 
| 268 | 2 |  |  |  |  | 16 | my $tests  = $data->[0]{tests}; | 
| 269 |  |  |  |  |  |  |  | 
| 270 | 2 |  |  |  |  | 4 | my %exec_for; | 
| 271 | 2 |  |  |  |  | 6 | foreach my $exec (@$tests) { | 
| 272 | 0 | 0 |  |  |  | 0 | if ( '*' eq $exec->[-1] ) { | 
| 273 | 0 |  |  |  |  | 0 | pop @$exec; | 
| 274 |  |  |  |  |  |  |  | 
| 275 |  |  |  |  |  |  | # don't override command line | 
| 276 | 0 | 0 |  |  |  | 0 | $self->exec($exec) unless $self->exec; | 
| 277 |  |  |  |  |  |  | } | 
| 278 |  |  |  |  |  |  | else { | 
| 279 | 0 |  |  |  |  | 0 | $exec_for{ $exec->[-1] } = $exec; | 
| 280 |  |  |  |  |  |  | } | 
| 281 |  |  |  |  |  |  | } | 
| 282 | 2 |  |  |  |  | 16 | $self->_execrc( \%exec_for ); | 
| 283 | 2 |  |  |  |  | 16 | return $self; | 
| 284 |  |  |  |  |  |  | } | 
| 285 |  |  |  |  |  |  |  | 
| 286 |  |  |  |  |  |  | ############################################################################## | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  | =head2 Instance Methods | 
| 289 |  |  |  |  |  |  |  | 
| 290 |  |  |  |  |  |  | =head3 C | 
| 291 |  |  |  |  |  |  |  | 
| 292 |  |  |  |  |  |  | $harness->runtests(@tests); | 
| 293 |  |  |  |  |  |  |  | 
| 294 |  |  |  |  |  |  | Accepts and array of C<@tests> to be run.  This should generally be the names | 
| 295 |  |  |  |  |  |  | of test files, but this is not required.  Each element in C<@tests> will be | 
| 296 |  |  |  |  |  |  | passed to C as a C.  See C for more | 
| 297 |  |  |  |  |  |  | information. | 
| 298 |  |  |  |  |  |  |  | 
| 299 |  |  |  |  |  |  | Tests will be run in the order found. | 
| 300 |  |  |  |  |  |  |  | 
| 301 |  |  |  |  |  |  | If the environment variable PERL_TEST_HARNESS_DUMP_TAP is defined it | 
| 302 |  |  |  |  |  |  | should name a directory into which a copy of the raw TAP for each test | 
| 303 |  |  |  |  |  |  | will be written. TAP is written to files named for each test. | 
| 304 |  |  |  |  |  |  | Subdirectories will be created as needed. | 
| 305 |  |  |  |  |  |  |  | 
| 306 |  |  |  |  |  |  | =cut | 
| 307 |  |  |  |  |  |  |  | 
| 308 |  |  |  |  |  |  | sub runtests { | 
| 309 | 9 |  |  | 9 | 1 | 16366 | my ( $self, @tests ) = @_; | 
| 310 |  |  |  |  |  |  |  | 
| 311 | 9 |  |  |  |  | 137 | my $aggregate = TAPx::Parser::Aggregator->new; | 
| 312 |  |  |  |  |  |  |  | 
| 313 | 9 |  |  |  |  | 40 | my $results = $self->aggregate_tests( $aggregate, @tests ); | 
| 314 |  |  |  |  |  |  |  | 
| 315 | 9 |  |  |  |  | 1152 | $self->summary($results); | 
| 316 |  |  |  |  |  |  | } | 
| 317 |  |  |  |  |  |  |  | 
| 318 |  |  |  |  |  |  | =head3 C | 
| 319 |  |  |  |  |  |  |  | 
| 320 |  |  |  |  |  |  | $harness->aggregate_tests( $aggregate, @tests ); | 
| 321 |  |  |  |  |  |  |  | 
| 322 |  |  |  |  |  |  | Tests will be run in the order found. | 
| 323 |  |  |  |  |  |  |  | 
| 324 |  |  |  |  |  |  | =cut | 
| 325 |  |  |  |  |  |  |  | 
| 326 |  |  |  |  |  |  | sub aggregate_tests { | 
| 327 | 9 |  |  | 9 | 1 | 28 | my ( $self, $aggregate, @tests ) = @_; | 
| 328 |  |  |  |  |  |  |  | 
| 329 | 9 |  |  |  |  | 11 | my $longest = 0; | 
| 330 |  |  |  |  |  |  |  | 
| 331 | 9 |  |  |  |  | 16 | my $tests_without_extensions = 0; | 
| 332 | 9 |  |  |  |  | 22 | foreach my $test (@tests) { | 
| 333 | 10 | 100 |  |  |  | 61 | $longest = length $test if length $test > $longest; | 
| 334 | 10 | 50 |  |  |  | 52 | if ( $test !~ /\.\w+$/ ) { | 
| 335 | 10 |  |  |  |  | 28 | $tests_without_extensions = 1; | 
| 336 |  |  |  |  |  |  | } | 
| 337 |  |  |  |  |  |  | } | 
| 338 | 9 |  |  |  |  | 31 | $self->_longest($longest); | 
| 339 |  |  |  |  |  |  |  | 
| 340 | 9 |  |  |  |  | 59 | my $start_time = Benchmark->new; | 
| 341 |  |  |  |  |  |  |  | 
| 342 | 9 |  |  |  |  | 177 | my $really_quiet = $self->really_quiet; | 
| 343 | 9 |  |  |  |  | 23 | foreach my $test (@tests) { | 
| 344 | 10 |  |  |  |  | 16 | my $extra = 0; | 
| 345 | 10 |  |  |  |  | 23 | my $name  = $test; | 
| 346 | 10 | 50 |  |  |  | 27 | unless ($tests_without_extensions) { | 
| 347 | 0 | 0 |  |  |  | 0 | if ( $name =~ s/(\.\w+)$// ) {    # strip the .t or .pm | 
| 348 | 0 |  |  |  |  | 0 | $extra = length $1; | 
| 349 |  |  |  |  |  |  | } | 
| 350 |  |  |  |  |  |  | } | 
| 351 | 10 |  |  |  |  | 45 | my $periods = '.' x ( $longest + $extra + 4 - length $test ); | 
| 352 |  |  |  |  |  |  |  | 
| 353 | 10 |  |  |  |  | 61 | my $parser = $self->_runtest( "$name$periods", $test ); | 
| 354 | 10 |  |  |  |  | 133 | $aggregate->add( $test, $parser ); | 
| 355 |  |  |  |  |  |  | } | 
| 356 |  |  |  |  |  |  |  | 
| 357 |  |  |  |  |  |  | return { | 
| 358 | 9 |  |  |  |  | 211 | start     => $start_time, | 
| 359 |  |  |  |  |  |  | end       => Benchmark->new, | 
| 360 |  |  |  |  |  |  | aggregate => $aggregate, | 
| 361 |  |  |  |  |  |  | tests     => \@tests | 
| 362 |  |  |  |  |  |  | }; | 
| 363 |  |  |  |  |  |  | } | 
| 364 |  |  |  |  |  |  |  | 
| 365 |  |  |  |  |  |  | ############################################################################## | 
| 366 |  |  |  |  |  |  |  | 
| 367 |  |  |  |  |  |  | =head1 SUBCLASSING | 
| 368 |  |  |  |  |  |  |  | 
| 369 |  |  |  |  |  |  | C is designed to be (mostly) easy to subclass.  If you don't | 
| 370 |  |  |  |  |  |  | like how a particular feature functions, just override the desired methods. | 
| 371 |  |  |  |  |  |  |  | 
| 372 |  |  |  |  |  |  | =head2 Methods | 
| 373 |  |  |  |  |  |  |  | 
| 374 |  |  |  |  |  |  | The following methods are one's you may wish to override if you want to | 
| 375 |  |  |  |  |  |  | subclass C. | 
| 376 |  |  |  |  |  |  |  | 
| 377 |  |  |  |  |  |  | =head3 C | 
| 378 |  |  |  |  |  |  |  | 
| 379 |  |  |  |  |  |  | $harness->summary( \%args ); | 
| 380 |  |  |  |  |  |  |  | 
| 381 |  |  |  |  |  |  | C prints the summary report after all tests are run.  The argument is | 
| 382 |  |  |  |  |  |  | a hashref with the following keys: | 
| 383 |  |  |  |  |  |  |  | 
| 384 |  |  |  |  |  |  | =over 4 | 
| 385 |  |  |  |  |  |  |  | 
| 386 |  |  |  |  |  |  | =item * C | 
| 387 |  |  |  |  |  |  |  | 
| 388 |  |  |  |  |  |  | This is created with C<< Benchmark->new >> and it the time the tests started. | 
| 389 |  |  |  |  |  |  | You can print a useful summary time, if desired, with: | 
| 390 |  |  |  |  |  |  |  | 
| 391 |  |  |  |  |  |  | $self->output(timestr( timediff( Benchmark->new, $start_time ), 'nop' )); | 
| 392 |  |  |  |  |  |  |  | 
| 393 |  |  |  |  |  |  | =item * C | 
| 394 |  |  |  |  |  |  |  | 
| 395 |  |  |  |  |  |  | This is the C object for all of the tests run. | 
| 396 |  |  |  |  |  |  |  | 
| 397 |  |  |  |  |  |  | =item * C | 
| 398 |  |  |  |  |  |  |  | 
| 399 |  |  |  |  |  |  | This is an array reference of all test names.  To get the C | 
| 400 |  |  |  |  |  |  | object for individual tests: | 
| 401 |  |  |  |  |  |  |  | 
| 402 |  |  |  |  |  |  | my $aggregate = $args->{aggregate}; | 
| 403 |  |  |  |  |  |  | my $tests     = $args->{tests}; | 
| 404 |  |  |  |  |  |  |  | 
| 405 |  |  |  |  |  |  | foreach my $name ( @$tests ) { | 
| 406 |  |  |  |  |  |  | my ($parser) = $aggregate->parsers($test); | 
| 407 |  |  |  |  |  |  | ... do something with $parser | 
| 408 |  |  |  |  |  |  | } | 
| 409 |  |  |  |  |  |  |  | 
| 410 |  |  |  |  |  |  | This is a bit clunky and will be cleaned up in a later release. | 
| 411 |  |  |  |  |  |  |  | 
| 412 |  |  |  |  |  |  | =back | 
| 413 |  |  |  |  |  |  |  | 
| 414 |  |  |  |  |  |  | =cut | 
| 415 |  |  |  |  |  |  |  | 
| 416 |  |  |  |  |  |  | sub summary { | 
| 417 | 9 |  |  | 9 | 1 | 21 | my ( $self, $arg_for ) = @_; | 
| 418 | 9 |  |  |  |  | 37 | my ( $start_time, $aggregate, $tests ) | 
| 419 |  |  |  |  |  |  | = @$arg_for{qw< start aggregate tests >}; | 
| 420 |  |  |  |  |  |  |  | 
| 421 | 9 |  | 33 |  |  | 41 | my $end_time = $arg_for->{end} || Benchmark->new; | 
| 422 |  |  |  |  |  |  |  | 
| 423 | 9 |  |  |  |  | 156 | my $runtime = timestr( timediff( $end_time, $start_time ), 'nop' ); | 
| 424 |  |  |  |  |  |  |  | 
| 425 | 9 |  |  |  |  | 1802 | my $total  = $aggregate->total; | 
| 426 | 9 |  |  |  |  | 41 | my $passed = $aggregate->passed; | 
| 427 |  |  |  |  |  |  |  | 
| 428 |  |  |  |  |  |  | # TODO: Check this condition still works when all subtests pass but | 
| 429 |  |  |  |  |  |  | # the exit status is nonzero | 
| 430 |  |  |  |  |  |  |  | 
| 431 | 9 | 100 | 66 |  |  | 143 | if ( $total && $total == $passed && !$aggregate->has_problems ) { | 
|  |  |  | 66 |  |  |  |  | 
| 432 | 5 |  |  |  |  | 23 | $self->output("All tests successful.\n"); | 
| 433 |  |  |  |  |  |  | } | 
| 434 | 9 | 100 | 66 |  |  | 194 | if (   $total != $passed | 
|  |  |  | 100 |  |  |  |  | 
| 435 |  |  |  |  |  |  | or $aggregate->has_problems | 
| 436 |  |  |  |  |  |  | or $aggregate->skipped ) | 
| 437 |  |  |  |  |  |  | { | 
| 438 | 5 |  |  |  |  | 28 | $self->output("\nTest Summary Report"); | 
| 439 | 5 |  |  |  |  | 140 | $self->output("\n-------------------\n"); | 
| 440 | 5 |  |  |  |  | 105 | foreach my $test (@$tests) { | 
| 441 | 5 |  |  |  |  | 17 | $self->_printed_summary_header(0); | 
| 442 | 5 |  |  |  |  | 25 | my ($parser) = $aggregate->parsers($test); | 
| 443 | 5 |  |  |  |  | 20 | $self->_curr_test($test); | 
| 444 | 5 |  |  |  |  | 21 | $self->_curr_parser($parser); | 
| 445 | 5 |  |  |  |  | 80 | $self->_output_summary_failure( 'failed', "  Failed tests:  " ); | 
| 446 | 5 |  |  |  |  | 14 | $self->_output_summary_failure( | 
| 447 |  |  |  |  |  |  | 'todo_passed', | 
| 448 |  |  |  |  |  |  | "  TODO passed:   " | 
| 449 |  |  |  |  |  |  | ); | 
| 450 | 5 |  |  |  |  | 17 | $self->_output_summary_failure( 'skipped', "  Tests skipped: " ); | 
| 451 |  |  |  |  |  |  |  | 
| 452 | 5 | 50 |  |  |  | 15 | if ( my $exit = $parser->exit ) { | 
| 453 | 0 |  |  |  |  | 0 | $self->_summary_test_header( $test, $parser ); | 
| 454 | 0 |  |  |  |  | 0 | $self->failure_output("  Non-zero exit status: $exit\n"); | 
| 455 |  |  |  |  |  |  | } | 
| 456 |  |  |  |  |  |  |  | 
| 457 | 5 | 100 |  |  |  | 17 | if ( my @errors = $parser->parse_errors ) { | 
| 458 | 1 |  |  |  |  | 7 | $self->_summary_test_header( $test, $parser ); | 
| 459 | 1 | 50 | 33 |  |  | 7 | if ( $self->errors || 1 == @errors ) { | 
| 460 | 1 |  |  |  |  | 9 | $self->failure_output( | 
| 461 |  |  |  |  |  |  | sprintf "  Parse errors: %s\n", | 
| 462 |  |  |  |  |  |  | shift @errors | 
| 463 |  |  |  |  |  |  | ); | 
| 464 | 1 |  |  |  |  | 23 | foreach my $error (@errors) { | 
| 465 | 0 |  |  |  |  | 0 | my $spaces = ' ' x 16; | 
| 466 | 0 |  |  |  |  | 0 | $self->failure_output("$spaces$error\n"); | 
| 467 |  |  |  |  |  |  | } | 
| 468 |  |  |  |  |  |  | } | 
| 469 |  |  |  |  |  |  | else { | 
| 470 | 0 |  |  |  |  | 0 | $self->failure_output( | 
| 471 |  |  |  |  |  |  | "  Errors encountered while parsing tap\n"); | 
| 472 |  |  |  |  |  |  | } | 
| 473 |  |  |  |  |  |  | } | 
| 474 |  |  |  |  |  |  | } | 
| 475 |  |  |  |  |  |  | } | 
| 476 | 9 |  |  |  |  | 34 | my $files = @$tests; | 
| 477 | 9 |  |  |  |  | 65 | $self->output("Files=$files, Tests=$total, $runtime\n"); | 
| 478 |  |  |  |  |  |  | } | 
| 479 |  |  |  |  |  |  |  | 
| 480 |  |  |  |  |  |  | sub _output_summary_failure { | 
| 481 | 15 |  |  | 15 |  | 55 | my ( $self, $method, $name ) = @_; | 
| 482 |  |  |  |  |  |  |  | 
| 483 |  |  |  |  |  |  | # ugly hack.  Must rethink this :( | 
| 484 | 15 | 100 |  |  |  | 52 | my $output = $method eq 'failed' ? 'failure_output' : 'output'; | 
| 485 | 15 |  |  |  |  | 35 | my $test   = $self->_curr_test; | 
| 486 | 15 |  |  |  |  | 34 | my $parser = $self->_curr_parser; | 
| 487 | 15 | 100 |  |  |  | 44 | if ( $parser->$method ) { | 
| 488 | 5 |  |  |  |  | 25 | $self->_summary_test_header( $test, $parser ); | 
| 489 | 5 |  |  |  |  | 15 | $self->$output($name); | 
| 490 | 5 |  |  |  |  | 94 | my @results = $self->balanced_range( 40, $parser->$method ); | 
| 491 | 5 |  |  |  |  | 29 | $self->$output( sprintf "%s\n" => shift @results ); | 
| 492 | 5 |  |  |  |  | 61 | my $spaces = ' ' x 16; | 
| 493 | 5 |  |  |  |  | 25 | while (@results) { | 
| 494 | 0 |  |  |  |  | 0 | $self->$output( sprintf "$spaces%s\n" => shift @results ); | 
| 495 |  |  |  |  |  |  | } | 
| 496 |  |  |  |  |  |  | } | 
| 497 |  |  |  |  |  |  | } | 
| 498 |  |  |  |  |  |  |  | 
| 499 |  |  |  |  |  |  | sub _summary_test_header { | 
| 500 | 6 |  |  | 6 |  | 12 | my ( $self, $test, $parser ) = @_; | 
| 501 | 6 | 100 |  |  |  | 14 | return if $self->_printed_summary_header; | 
| 502 | 5 |  |  |  |  | 32 | my $spaces = ' ' x ( $self->_longest - length $test ); | 
| 503 | 5 | 50 |  |  |  | 17 | $spaces = ' ' unless $spaces; | 
| 504 | 5 |  |  |  |  | 18 | my $output = $self->_get_output_method($parser); | 
| 505 | 5 |  |  |  |  | 25 | $self->$output( | 
| 506 |  |  |  |  |  |  | sprintf "$test$spaces(Wstat: %d Tests: %d Failed: %d)\n", | 
| 507 |  |  |  |  |  |  | $parser->wait, $parser->tests_run, scalar $parser->failed | 
| 508 |  |  |  |  |  |  | ); | 
| 509 | 5 |  |  |  |  | 144 | $self->_printed_summary_header(1); | 
| 510 |  |  |  |  |  |  | } | 
| 511 |  |  |  |  |  |  |  | 
| 512 |  |  |  |  |  |  | ############################################################################## | 
| 513 |  |  |  |  |  |  |  | 
| 514 |  |  |  |  |  |  | =head3 C | 
| 515 |  |  |  |  |  |  |  | 
| 516 |  |  |  |  |  |  | $harness->output(@list_of_strings_to_output); | 
| 517 |  |  |  |  |  |  |  | 
| 518 |  |  |  |  |  |  | All output from C is driven through this method.  If you would | 
| 519 |  |  |  |  |  |  | like to redirect output somewhere else, just override this method. | 
| 520 |  |  |  |  |  |  |  | 
| 521 |  |  |  |  |  |  | =cut | 
| 522 |  |  |  |  |  |  |  | 
| 523 |  |  |  |  |  |  | sub output { | 
| 524 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 525 | 0 |  |  |  |  | 0 | print @_; | 
| 526 |  |  |  |  |  |  | } | 
| 527 |  |  |  |  |  |  |  | 
| 528 |  |  |  |  |  |  | ############################################################################## | 
| 529 |  |  |  |  |  |  |  | 
| 530 |  |  |  |  |  |  | =head3 C | 
| 531 |  |  |  |  |  |  |  | 
| 532 |  |  |  |  |  |  | $harness->failure_output(@list_of_strings_to_output); | 
| 533 |  |  |  |  |  |  |  | 
| 534 |  |  |  |  |  |  | Identical to C | 
| 535 |  |  |  |  |  |  | a failure. | 
| 536 |  |  |  |  |  |  |  | 
| 537 |  |  |  |  |  |  | =cut | 
| 538 |  |  |  |  |  |  |  | 
| 539 |  |  |  |  |  |  | sub failure_output { | 
| 540 | 16 |  |  | 16 | 1 | 53 | shift->output(@_); | 
| 541 |  |  |  |  |  |  | } | 
| 542 |  |  |  |  |  |  |  | 
| 543 |  |  |  |  |  |  | ############################################################################## | 
| 544 |  |  |  |  |  |  |  | 
| 545 |  |  |  |  |  |  | =head3 C | 
| 546 |  |  |  |  |  |  |  | 
| 547 |  |  |  |  |  |  | my @ranges = $harness->balanced_range( $limit, @numbers ); | 
| 548 |  |  |  |  |  |  |  | 
| 549 |  |  |  |  |  |  | Given a limit in the number of characters and a list of numbers, this method | 
| 550 |  |  |  |  |  |  | first creates a range of numbers with C and then groups them into | 
| 551 |  |  |  |  |  |  | individual strings which are roughly the length of C<$limit>.  Returns an | 
| 552 |  |  |  |  |  |  | array of strings. | 
| 553 |  |  |  |  |  |  |  | 
| 554 |  |  |  |  |  |  | =cut | 
| 555 |  |  |  |  |  |  |  | 
| 556 |  |  |  |  |  |  | sub balanced_range { | 
| 557 | 7 |  |  | 7 | 1 | 2436 | my ( $self, $limit, @range ) = @_; | 
| 558 | 7 |  |  |  |  | 38 | @range = $self->range(@range); | 
| 559 | 7 |  |  |  |  | 33 | my $line = ""; | 
| 560 | 7 |  |  |  |  | 11 | my @lines; | 
| 561 | 7 |  |  |  |  | 15 | my $curr = 0; | 
| 562 | 7 |  |  |  |  | 22 | while (@range) { | 
| 563 | 13 | 100 |  |  |  | 52 | if ( $curr < $limit ) { | 
|  |  | 50 |  |  |  |  |  | 
| 564 | 11 |  |  |  |  | 22 | my $range = ( shift @range ) . ", "; | 
| 565 | 11 |  |  |  |  | 22 | $line .= $range; | 
| 566 | 11 |  |  |  |  | 34 | $curr += length $range; | 
| 567 |  |  |  |  |  |  | } | 
| 568 |  |  |  |  |  |  | elsif (@range) { | 
| 569 | 2 |  |  |  |  | 12 | $line =~ s/, $//; | 
| 570 | 2 |  |  |  |  | 4 | push @lines => $line; | 
| 571 | 2 |  |  |  |  | 4 | $line = ''; | 
| 572 | 2 |  |  |  |  | 6 | $curr = 0; | 
| 573 |  |  |  |  |  |  | } | 
| 574 |  |  |  |  |  |  | } | 
| 575 | 7 | 50 |  |  |  | 23 | if ($line) { | 
| 576 | 7 |  |  |  |  | 69 | $line =~ s/, $//; | 
| 577 | 7 |  |  |  |  | 20 | push @lines => $line; | 
| 578 |  |  |  |  |  |  | } | 
| 579 | 7 |  |  |  |  | 30 | return @lines; | 
| 580 |  |  |  |  |  |  | } | 
| 581 |  |  |  |  |  |  |  | 
| 582 |  |  |  |  |  |  | ############################################################################## | 
| 583 |  |  |  |  |  |  |  | 
| 584 |  |  |  |  |  |  | =head3 C | 
| 585 |  |  |  |  |  |  |  | 
| 586 |  |  |  |  |  |  | my @range = $harness->range(@list_of_numbers); | 
| 587 |  |  |  |  |  |  |  | 
| 588 |  |  |  |  |  |  | Taks a list of numbers, sorts them, and returns a list of ranged strings: | 
| 589 |  |  |  |  |  |  |  | 
| 590 |  |  |  |  |  |  | print join ', ' $harness->range( 2, 7, 1, 3, 10, 9  ); | 
| 591 |  |  |  |  |  |  | # 1-3, 7, 9-10 | 
| 592 |  |  |  |  |  |  |  | 
| 593 |  |  |  |  |  |  | =cut | 
| 594 |  |  |  |  |  |  |  | 
| 595 |  |  |  |  |  |  | sub range { | 
| 596 | 7 |  |  | 7 | 1 | 22 | my ( $self, @numbers ) = @_; | 
| 597 |  |  |  |  |  |  |  | 
| 598 |  |  |  |  |  |  | # shouldn't be needed, but subclasses might call this | 
| 599 | 7 |  |  |  |  | 28 | @numbers = sort { $a <=> $b } @numbers; | 
|  | 20 |  |  |  |  | 30 |  | 
| 600 | 7 |  |  |  |  | 13 | my ( $min, @range ); | 
| 601 |  |  |  |  |  |  |  | 
| 602 | 7 |  |  |  |  | 38 | foreach my $i ( 0 .. $#numbers ) { | 
| 603 | 17 |  |  |  |  | 28 | my $num  = $numbers[$i]; | 
| 604 | 17 |  |  |  |  | 27 | my $next = $numbers[ $i + 1 ]; | 
| 605 | 17 | 100 | 100 |  |  | 96 | if ( defined $next && $next == $num + 1 ) { | 
|  |  | 100 |  |  |  |  |  | 
| 606 | 6 | 100 |  |  |  | 15 | if ( !defined $min ) { | 
| 607 | 4 |  |  |  |  | 11 | $min = $num; | 
| 608 |  |  |  |  |  |  | } | 
| 609 |  |  |  |  |  |  | } | 
| 610 |  |  |  |  |  |  | elsif ( defined $min ) { | 
| 611 | 4 |  |  |  |  | 11 | push @range => "$min-$num"; | 
| 612 | 4 |  |  |  |  | 14 | undef $min; | 
| 613 |  |  |  |  |  |  | } | 
| 614 |  |  |  |  |  |  | else { | 
| 615 | 7 |  |  |  |  | 42 | push @range => $num; | 
| 616 |  |  |  |  |  |  | } | 
| 617 |  |  |  |  |  |  | } | 
| 618 | 7 |  |  |  |  | 32 | return @range; | 
| 619 |  |  |  |  |  |  | } | 
| 620 |  |  |  |  |  |  |  | 
| 621 |  |  |  |  |  |  | ############################################################################## | 
| 622 |  |  |  |  |  |  |  | 
| 623 |  |  |  |  |  |  | =head3 C | 
| 624 |  |  |  |  |  |  |  | 
| 625 |  |  |  |  |  |  | $harness->output_test_failure($parser); | 
| 626 |  |  |  |  |  |  |  | 
| 627 |  |  |  |  |  |  | As individual test programs are run, if a test program fails, this method is | 
| 628 |  |  |  |  |  |  | called to spit out the list of failed tests. | 
| 629 |  |  |  |  |  |  |  | 
| 630 |  |  |  |  |  |  | =cut | 
| 631 |  |  |  |  |  |  |  | 
| 632 |  |  |  |  |  |  | sub output_test_failure { | 
| 633 | 4 |  |  | 4 | 1 | 20 | my ( $self, $parser ) = @_; | 
| 634 | 4 | 100 |  |  |  | 18 | return if $self->really_quiet; | 
| 635 |  |  |  |  |  |  |  | 
| 636 | 3 |  |  |  |  | 19 | my $tests_run     = $parser->tests_run; | 
| 637 | 3 |  |  |  |  | 13 | my $tests_planned = $parser->tests_planned; | 
| 638 |  |  |  |  |  |  |  | 
| 639 | 3 | 50 |  |  |  | 23 | my $total = | 
| 640 |  |  |  |  |  |  | defined $tests_planned | 
| 641 |  |  |  |  |  |  | ? $tests_planned | 
| 642 |  |  |  |  |  |  | : $tests_run; | 
| 643 |  |  |  |  |  |  |  | 
| 644 | 3 |  |  |  |  | 17 | my $passed = $parser->passed; | 
| 645 |  |  |  |  |  |  |  | 
| 646 |  |  |  |  |  |  | # The total number of fails includes any tests that were planned but | 
| 647 |  |  |  |  |  |  | # didn't run | 
| 648 | 3 |  |  |  |  | 14 | my $failed = $parser->failed + $total - $tests_run; | 
| 649 | 3 |  |  |  |  | 10 | my $exit   = $parser->exit; | 
| 650 |  |  |  |  |  |  |  | 
| 651 |  |  |  |  |  |  | # TODO: $flist isn't used anywhere | 
| 652 |  |  |  |  |  |  | # my $flist  = join ", " => $self->range( $parser->failed ); | 
| 653 |  |  |  |  |  |  |  | 
| 654 | 3 | 50 |  |  |  | 10 | if ( my $exit = $parser->exit ) { | 
| 655 | 0 |  |  |  |  | 0 | my $wstat = $parser->wait; | 
| 656 | 0 |  |  |  |  | 0 | my $status = sprintf( "%d (wstat %d, 0x%x)", $exit, $wstat, $wstat ); | 
| 657 | 0 |  |  |  |  | 0 | $self->failure_output(" Dubious, test returned $status\n"); | 
| 658 |  |  |  |  |  |  | } | 
| 659 |  |  |  |  |  |  |  | 
| 660 | 3 | 50 |  |  |  | 33 | if ( $failed == 0 ) { | 
| 661 | 0 |  |  |  |  | 0 | $self->failure_output(" All $total subtests passed "); | 
| 662 |  |  |  |  |  |  | } | 
| 663 |  |  |  |  |  |  | else { | 
| 664 | 3 |  |  |  |  | 24 | $self->failure_output(" Failed $failed/$total subtests "); | 
| 665 | 3 | 50 |  |  |  | 98 | if ( !$total ) { | 
| 666 | 0 |  |  |  |  | 0 | $self->failure_output("\nNo tests run!"); | 
| 667 |  |  |  |  |  |  | } | 
| 668 |  |  |  |  |  |  | } | 
| 669 |  |  |  |  |  |  |  | 
| 670 | 3 | 50 |  |  |  | 16 | if ( my $skipped = $parser->skipped ) { | 
| 671 | 0 |  |  |  |  | 0 | $passed -= $skipped; | 
| 672 | 0 | 0 |  |  |  | 0 | my $test = 'subtest' . ( $skipped != 1 ? 's' : '' ); | 
| 673 | 0 |  |  |  |  | 0 | $self->output("\n\t(less $skipped skipped $test: $passed okay)"); | 
| 674 |  |  |  |  |  |  | } | 
| 675 |  |  |  |  |  |  |  | 
| 676 | 3 | 50 |  |  |  | 11 | if ( my $failed = $parser->todo_passed ) { | 
| 677 | 0 | 0 |  |  |  | 0 | my $test = $failed > 1 ? 'tests' : 'test'; | 
| 678 | 0 |  |  |  |  | 0 | $self->output("\n\t($failed TODO $test unexpectedly succeeded)"); | 
| 679 |  |  |  |  |  |  | } | 
| 680 |  |  |  |  |  |  |  | 
| 681 | 3 |  |  |  |  | 15 | $self->output("\n"); | 
| 682 |  |  |  |  |  |  | } | 
| 683 |  |  |  |  |  |  |  | 
| 684 |  |  |  |  |  |  | sub _runtest { | 
| 685 | 10 |  |  | 10 |  | 20 | my ( $self, $leader, $test ) = @_; | 
| 686 |  |  |  |  |  |  |  | 
| 687 | 10 |  |  |  |  | 64 | my $execrc       = $self->_execrc; | 
| 688 | 10 |  |  |  |  | 27 | my $really_quiet = $self->really_quiet; | 
| 689 | 10 |  |  |  |  | 43 | my $show_count   = $self->_should_show_count; | 
| 690 | 10 | 100 |  |  |  | 88 | $self->output($leader) unless $really_quiet; | 
| 691 |  |  |  |  |  |  |  | 
| 692 | 10 |  |  |  |  | 305 | my %args = ( source => $test ); | 
| 693 | 10 | 50 |  |  |  | 67 | my @switches = $self->lib if $self->lib; | 
| 694 | 10 | 50 |  |  |  | 35 | push @switches => $self->switches if $self->switches; | 
| 695 | 10 |  |  |  |  | 36 | $args{switches} = \@switches; | 
| 696 |  |  |  |  |  |  |  | 
| 697 | 10 | 50 |  |  |  | 48 | if ( my $exec = $execrc->{$test} ) { | 
|  |  | 100 |  |  |  |  |  | 
| 698 | 0 |  |  |  |  | 0 | $args{exec} = $exec; | 
| 699 | 0 |  |  |  |  | 0 | delete $args{source}; | 
| 700 |  |  |  |  |  |  | } | 
| 701 |  |  |  |  |  |  | elsif ( $exec = $self->exec ) { | 
| 702 | 2 |  |  |  |  | 12 | $args{exec} = [ @$exec, $test ]; | 
| 703 | 2 |  |  |  |  | 10 | delete $args{source}; | 
| 704 |  |  |  |  |  |  | } | 
| 705 |  |  |  |  |  |  |  | 
| 706 | 10 |  |  |  |  | 40 | $args{spool} = $self->_open_spool($test); | 
| 707 |  |  |  |  |  |  |  | 
| 708 | 10 |  |  |  |  | 90 | my $parser = TAPx::Parser->new( \%args ); | 
| 709 |  |  |  |  |  |  |  | 
| 710 | 10 |  |  |  |  | 190 | $self->_make_callback( 'made_parser', $parser ); | 
| 711 |  |  |  |  |  |  |  | 
| 712 | 10 |  |  |  |  | 52 | my $plan = ''; | 
| 713 | 10 |  |  |  |  | 108 | $self->_newline_printed(0); | 
| 714 | 10 |  |  |  |  | 63 | my $start_time = time(); | 
| 715 | 10 |  |  |  |  | 51 | my $output     = 'output'; | 
| 716 | 10 |  |  |  |  | 172 | while ( defined( my $result = $parser->next ) ) { | 
| 717 | 27 |  |  |  |  | 291 | $output = $self->_get_output_method($parser); | 
| 718 | 27 | 50 |  |  |  | 120 | if ( $result->is_bailout ) { | 
| 719 | 0 |  |  |  |  | 0 | $self->failure_output( | 
| 720 |  |  |  |  |  |  | "Bailout called.  Further testing stopped:  " | 
| 721 |  |  |  |  |  |  | . $result->explanation | 
| 722 |  |  |  |  |  |  | . "\n" ); | 
| 723 | 0 |  |  |  |  | 0 | exit 1; | 
| 724 |  |  |  |  |  |  | } | 
| 725 | 27 | 100 |  |  |  | 86 | unless ($plan) { | 
| 726 | 10 |  | 50 |  |  | 38 | $plan = '/' . ( $parser->tests_planned || 0 ) . ' '; | 
| 727 |  |  |  |  |  |  | } | 
| 728 | 27 | 50 | 33 |  |  | 118 | if ( $show_count && $result->is_test ) { | 
| 729 | 0 | 0 |  |  |  | 0 | $self->$output( "\r$leader" . $result->number . $plan ) | 
| 730 |  |  |  |  |  |  | unless $really_quiet; | 
| 731 | 0 |  |  |  |  | 0 | $self->_newline_printed(0); | 
| 732 |  |  |  |  |  |  | } | 
| 733 | 27 |  |  |  |  | 165 | $self->_process( $parser, $result ); | 
| 734 |  |  |  |  |  |  | } | 
| 735 |  |  |  |  |  |  |  | 
| 736 | 10 |  |  |  |  | 96 | $self->_close_spool; | 
| 737 |  |  |  |  |  |  |  | 
| 738 | 10 | 50 |  |  |  | 21 | if ($show_count) { | 
| 739 | 0 |  |  |  |  | 0 | my $spaces = ' ' x ( | 
| 740 |  |  |  |  |  |  | 1 + length($leader) + length($plan) + length( $parser->tests_run ) | 
| 741 |  |  |  |  |  |  | ); | 
| 742 | 0 | 0 |  |  |  | 0 | $self->$output("\r$spaces\r$leader") unless $really_quiet; | 
| 743 |  |  |  |  |  |  | } | 
| 744 | 10 | 100 |  |  |  | 31 | if ( !$parser->has_problems ) { | 
| 745 | 6 | 100 |  |  |  | 19 | unless ($really_quiet) { | 
| 746 | 5 |  |  |  |  | 13 | my $time_report = ''; | 
| 747 | 5 | 50 |  |  |  | 25 | if ( $self->timer ) { | 
| 748 | 0 |  |  |  |  | 0 | my $elapsed = time - $start_time; | 
| 749 | 0 | 0 | 0 |  |  | 0 | $time_report = $TIME_HIRES | 
| 750 |  |  |  |  |  |  | ? sprintf( ' %8d ms', $elapsed * 1000 ) | 
| 751 |  |  |  |  |  |  | : sprintf( ' %8s s', $elapsed || '<1' ); | 
| 752 |  |  |  |  |  |  | } | 
| 753 |  |  |  |  |  |  |  | 
| 754 | 5 |  |  |  |  | 32 | $self->output("ok$time_report\n"); | 
| 755 |  |  |  |  |  |  | } | 
| 756 |  |  |  |  |  |  | } | 
| 757 |  |  |  |  |  |  | else { | 
| 758 | 4 |  |  |  |  | 31 | $self->output_test_failure($parser); | 
| 759 |  |  |  |  |  |  | } | 
| 760 | 10 |  |  |  |  | 383 | return $parser; | 
| 761 |  |  |  |  |  |  | } | 
| 762 |  |  |  |  |  |  |  | 
| 763 |  |  |  |  |  |  | sub _open_spool { | 
| 764 | 10 |  |  | 10 |  | 16 | my $self = shift; | 
| 765 | 10 |  |  |  |  | 15 | my $test = shift; | 
| 766 |  |  |  |  |  |  |  | 
| 767 | 10 | 50 |  |  |  | 138 | if ( my $spool_dir = $ENV{PERL_TEST_HARNESS_DUMP_TAP} ) { | 
| 768 | 0 |  |  |  |  | 0 | my $spool = File::Spec->catfile( $spool_dir, $test ); | 
| 769 |  |  |  |  |  |  |  | 
| 770 |  |  |  |  |  |  | # Make the directory | 
| 771 | 0 |  |  |  |  | 0 | my ( $vol, $dir, $file ) = File::Spec->splitpath($spool); | 
| 772 | 0 |  |  |  |  | 0 | my $path = File::Spec->catdir( $vol, $dir ); | 
| 773 | 0 |  |  |  |  | 0 | eval { mkpath($path) }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 774 | 0 | 0 |  |  |  | 0 | $self->_croak($@) if $@; | 
| 775 |  |  |  |  |  |  |  | 
| 776 | 0 | 0 |  |  |  | 0 | open( my $spool_handle, '>', $spool ) | 
| 777 |  |  |  |  |  |  | or $self->_croak(" Can't write $spool ( $! ) "); | 
| 778 | 0 |  |  |  |  | 0 | return $self->{spool} = $spool_handle; | 
| 779 |  |  |  |  |  |  | } | 
| 780 |  |  |  |  |  |  |  | 
| 781 | 10 |  |  |  |  | 61 | return; | 
| 782 |  |  |  |  |  |  | } | 
| 783 |  |  |  |  |  |  |  | 
| 784 |  |  |  |  |  |  | sub _close_spool { | 
| 785 | 10 |  |  | 10 |  | 15 | my $self = shift; | 
| 786 |  |  |  |  |  |  |  | 
| 787 | 10 | 50 |  |  |  | 48 | if ( my $spool_handle = delete $self->{spool} ) { | 
| 788 | 0 | 0 |  |  |  | 0 | close($spool_handle) | 
| 789 |  |  |  |  |  |  | or $self->_croak(" Error closing TAP spool file( $! ) \n "); | 
| 790 |  |  |  |  |  |  | } | 
| 791 |  |  |  |  |  |  | } | 
| 792 |  |  |  |  |  |  |  | 
| 793 |  |  |  |  |  |  | sub _process { | 
| 794 | 27 |  |  | 27 |  | 79 | my ( $self, $parser, $result ) = @_; | 
| 795 | 27 | 100 |  |  |  | 509 | return if $self->really_quiet; | 
| 796 | 22 | 100 |  |  |  | 67 | if ( $self->_should_display( $parser, $result ) ) { | 
| 797 | 15 | 100 |  |  |  | 37 | unless ( $self->_newline_printed ) { | 
| 798 | 6 | 50 |  |  |  | 32 | $self->output("\n") unless $self->quiet; | 
| 799 | 6 |  |  |  |  | 216 | $self->_newline_printed(1); | 
| 800 |  |  |  |  |  |  | } | 
| 801 | 15 | 50 |  |  |  | 32 | $self->output( $result->as_string . "\n" ) unless $self->quiet; | 
| 802 |  |  |  |  |  |  | } | 
| 803 |  |  |  |  |  |  | } | 
| 804 |  |  |  |  |  |  |  | 
| 805 |  |  |  |  |  |  | sub _get_output_method { | 
| 806 | 32 |  |  | 32 |  | 50 | my ( $self, $parser ) = @_; | 
| 807 | 32 | 100 |  |  |  | 119 | return $parser->has_problems ? 'failure_output' : 'output'; | 
| 808 |  |  |  |  |  |  | } | 
| 809 |  |  |  |  |  |  |  | 
| 810 |  |  |  |  |  |  | # XXX this really needs some cleanup! | 
| 811 |  |  |  |  |  |  | sub _should_display { | 
| 812 | 22 |  |  | 22 |  | 30 | my ( $self, $parser, $result ) = @_; | 
| 813 | 22 | 100 |  |  |  | 375 | if ( $self->directives ) { | 
| 814 | 4 |  |  |  |  | 66 | return $result->has_directive; | 
| 815 |  |  |  |  |  |  | } | 
| 816 | 18 | 50 |  |  |  | 44 | return if $self->really_quiet; | 
| 817 | 18 |  | 33 |  |  | 66 | return $self->verbose && !$self->failures | 
| 818 |  |  |  |  |  |  | || ( $result->is_comment | 
| 819 |  |  |  |  |  |  | && !$self->quiet | 
| 820 |  |  |  |  |  |  | && ( $result->is_test || !$parser->in_todo ) ) | 
| 821 |  |  |  |  |  |  | || $self->_should_show_failure($result); | 
| 822 |  |  |  |  |  |  | } | 
| 823 |  |  |  |  |  |  |  | 
| 824 |  |  |  |  |  |  | sub _should_show_count { | 
| 825 |  |  |  |  |  |  |  | 
| 826 |  |  |  |  |  |  | # we need this because if someone tries to redirect the output, it can get | 
| 827 |  |  |  |  |  |  | # very garbled from the carriage returns (\r) in the count line. | 
| 828 | 0 |  | 0 | 0 |  | 0 | return !shift->verbose && -t STDOUT; | 
| 829 |  |  |  |  |  |  | } | 
| 830 |  |  |  |  |  |  |  | 
| 831 |  |  |  |  |  |  | sub _should_show_failure { | 
| 832 | 5 |  |  | 5 |  | 16 | my ( $self, $result ) = @_; | 
| 833 | 5 | 100 |  |  |  | 23 | return if !$result->is_test; | 
| 834 | 3 |  | 33 |  |  | 23 | return $self->failures && !$result->is_ok; | 
| 835 |  |  |  |  |  |  | } | 
| 836 |  |  |  |  |  |  |  | 
| 837 |  |  |  |  |  |  | sub _croak { | 
| 838 | 8 |  |  | 8 |  | 13 | my ( $self, $message ) = @_; | 
| 839 | 8 | 100 |  |  |  | 21 | unless ($message) { | 
| 840 | 6 |  |  |  |  | 13 | $message = $self->_error; | 
| 841 |  |  |  |  |  |  | } | 
| 842 | 8 |  |  |  |  | 31 | $self->SUPER::_croak($message); | 
| 843 |  |  |  |  |  |  | } | 
| 844 |  |  |  |  |  |  |  | 
| 845 |  |  |  |  |  |  | =head1 USING EXECRC | 
| 846 |  |  |  |  |  |  |  | 
| 847 |  |  |  |  |  |  | B:  this functionality is still experimental.  While we intend to | 
| 848 |  |  |  |  |  |  | support it, the file format may change. | 
| 849 |  |  |  |  |  |  |  | 
| 850 |  |  |  |  |  |  | Sometimes you want to use different executables to run different tests.  If | 
| 851 |  |  |  |  |  |  | that's the case, you'll need to create an C file.  This file should be | 
| 852 |  |  |  |  |  |  | a YAML file.  This should be representative a hash with one key, C, | 
| 853 |  |  |  |  |  |  | whose value is an array of array references.  Each terminating array reference | 
| 854 |  |  |  |  |  |  | should be a list of the exact arguments which eventually get executed. | 
| 855 |  |  |  |  |  |  |  | 
| 856 |  |  |  |  |  |  | --- | 
| 857 |  |  |  |  |  |  | tests: | 
| 858 |  |  |  |  |  |  | # this is the default for all files | 
| 859 |  |  |  |  |  |  | - | 
| 860 |  |  |  |  |  |  | - /usr/bin/perl | 
| 861 |  |  |  |  |  |  | - -wT | 
| 862 |  |  |  |  |  |  | - * | 
| 863 |  |  |  |  |  |  |  | 
| 864 |  |  |  |  |  |  | # whoops!  We have a ruby test here! | 
| 865 |  |  |  |  |  |  | - | 
| 866 |  |  |  |  |  |  | - /usr/bin/ruby | 
| 867 |  |  |  |  |  |  | - t/ruby.t | 
| 868 |  |  |  |  |  |  |  | 
| 869 |  |  |  |  |  |  | # let's test some web pages | 
| 870 |  |  |  |  |  |  | - | 
| 871 |  |  |  |  |  |  | - /usr/bin/perl | 
| 872 |  |  |  |  |  |  | - -w | 
| 873 |  |  |  |  |  |  | - bin/test_html.pl | 
| 874 |  |  |  |  |  |  | - http://www.google.com/ | 
| 875 |  |  |  |  |  |  | - | 
| 876 |  |  |  |  |  |  | - /usr/bin/perl | 
| 877 |  |  |  |  |  |  | - -w | 
| 878 |  |  |  |  |  |  | - bin/test_html.pl | 
| 879 |  |  |  |  |  |  | - http://www.yahoo.com/ | 
| 880 |  |  |  |  |  |  |  | 
| 881 |  |  |  |  |  |  | If the terminating element in an array is '*', then the rest of the array are | 
| 882 |  |  |  |  |  |  | the default arguments used to run any test. | 
| 883 |  |  |  |  |  |  |  | 
| 884 |  |  |  |  |  |  | Blank lines are allowed.  Lines beginning with a '#' are comments (the '#' may | 
| 885 |  |  |  |  |  |  | have spaces in front of it). | 
| 886 |  |  |  |  |  |  |  | 
| 887 |  |  |  |  |  |  | So for the above C file, if it's named 'my_execrc' (as it is in the | 
| 888 |  |  |  |  |  |  | C directory which comes with this distribution), then you could | 
| 889 |  |  |  |  |  |  | potentially run it like this, if you're using the C utility: | 
| 890 |  |  |  |  |  |  |  | 
| 891 |  |  |  |  |  |  | runtests --execrc my_execrc t/ - < list_of_urls.txt | 
| 892 |  |  |  |  |  |  |  | 
| 893 |  |  |  |  |  |  | Then for a test named C, it will be executed | 
| 894 |  |  |  |  |  |  | with: | 
| 895 |  |  |  |  |  |  |  | 
| 896 |  |  |  |  |  |  | /usr/bin/ruby -w t/test_is_written_in_ruby.t | 
| 897 |  |  |  |  |  |  |  | 
| 898 |  |  |  |  |  |  | If the list of urls contains "http://www.google.com/", it will be executed as | 
| 899 |  |  |  |  |  |  | follows: | 
| 900 |  |  |  |  |  |  |  | 
| 901 |  |  |  |  |  |  | /usr/bin/perl test_html.pl http://www.google.com/ | 
| 902 |  |  |  |  |  |  |  | 
| 903 |  |  |  |  |  |  | Of course, if C outputs anything other than TAP, this will fail. | 
| 904 |  |  |  |  |  |  |  | 
| 905 |  |  |  |  |  |  | See the C in the C directory for a ready-to-run example. | 
| 906 |  |  |  |  |  |  |  | 
| 907 |  |  |  |  |  |  | =head1 REPLACING | 
| 908 |  |  |  |  |  |  |  | 
| 909 |  |  |  |  |  |  | If you like the C utility and L but you want your own | 
| 910 |  |  |  |  |  |  | harness, all you need to do is write one and provide C and C | 
| 911 |  |  |  |  |  |  | methods.  Then you can use the C utility like so: | 
| 912 |  |  |  |  |  |  |  | 
| 913 |  |  |  |  |  |  | runtests --harness My::Test::Harness | 
| 914 |  |  |  |  |  |  |  | 
| 915 |  |  |  |  |  |  | Note that while C accepts a list of tests (or things to be tested), | 
| 916 |  |  |  |  |  |  | C has a fairly rich set of arguments.  You'll probably want to read over | 
| 917 |  |  |  |  |  |  | this code carefully to see how all of them are being used. | 
| 918 |  |  |  |  |  |  |  | 
| 919 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 920 |  |  |  |  |  |  |  | 
| 921 |  |  |  |  |  |  | L | 
| 922 |  |  |  |  |  |  |  | 
| 923 |  |  |  |  |  |  | =cut | 
| 924 |  |  |  |  |  |  |  | 
| 925 |  |  |  |  |  |  | 1; |