| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package TAP::Formatter::Base; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 17 |  |  | 17 |  | 5878 | use strict; | 
|  | 17 |  |  |  |  | 23 |  | 
|  | 17 |  |  |  |  | 444 |  | 
| 4 | 17 |  |  | 17 |  | 64 | use warnings; | 
|  | 17 |  |  |  |  | 19 |  | 
|  | 17 |  |  |  |  | 435 |  | 
| 5 | 17 |  |  | 17 |  | 58 | use base 'TAP::Base'; | 
|  | 17 |  |  |  |  | 21 |  | 
|  | 17 |  |  |  |  | 1410 |  | 
| 6 | 17 |  |  | 17 |  | 1672 | use POSIX qw(strftime); | 
|  | 17 |  |  |  |  | 17209 |  | 
|  | 17 |  |  |  |  | 77 |  | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  | my $MAX_ERRORS = 5; | 
| 9 |  |  |  |  |  |  | my %VALIDATION_FOR; | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  | BEGIN { | 
| 12 |  |  |  |  |  |  | %VALIDATION_FOR = ( | 
| 13 | 2 |  |  |  |  | 3 | directives => sub { shift; shift }, | 
|  | 2 |  |  |  |  | 9 |  | 
| 14 | 66 |  |  |  |  | 70 | verbosity  => sub { shift; shift }, | 
|  | 66 |  |  |  |  | 149 |  | 
| 15 | 0 |  |  |  |  | 0 | normalize  => sub { shift; shift }, | 
|  | 0 |  |  |  |  | 0 |  | 
| 16 | 41 |  |  |  |  | 40 | timer      => sub { shift; shift }, | 
|  | 41 |  |  |  |  | 93 |  | 
| 17 | 6 |  |  |  |  | 7 | failures   => sub { shift; shift }, | 
|  | 6 |  |  |  |  | 19 |  | 
| 18 | 3 |  |  |  |  | 2 | comments   => sub { shift; shift }, | 
|  | 3 |  |  |  |  | 8 |  | 
| 19 | 1 |  |  |  |  | 2 | errors     => sub { shift; shift }, | 
|  | 1 |  |  |  |  | 12 |  | 
| 20 | 40 |  |  |  |  | 49 | color      => sub { shift; shift }, | 
|  | 40 |  |  |  |  | 110 |  | 
| 21 | 86 |  |  |  |  | 93 | jobs       => sub { shift; shift }, | 
|  | 86 |  |  |  |  | 243 |  | 
| 22 | 2 |  |  |  |  | 3 | show_count => sub { shift; shift }, | 
|  | 2 |  |  |  |  | 8 |  | 
| 23 |  |  |  |  |  |  | stdout     => sub { | 
| 24 | 15 |  |  |  |  | 21 | my ( $self, $ref ) = @_; | 
| 25 |  |  |  |  |  |  |  | 
| 26 | 15 | 100 |  |  |  | 36 | $self->_croak("option 'stdout' needs a filehandle") | 
| 27 |  |  |  |  |  |  | unless $self->_is_filehandle($ref); | 
| 28 |  |  |  |  |  |  |  | 
| 29 | 13 |  |  |  |  | 30 | return $ref; | 
| 30 |  |  |  |  |  |  | }, | 
| 31 | 17 |  |  | 17 |  | 10946 | ); | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | sub _is_filehandle { | 
| 34 | 15 |  |  | 15 |  | 15 | my ( $self, $ref ) = @_; | 
| 35 |  |  |  |  |  |  |  | 
| 36 | 15 | 50 |  |  |  | 30 | return 0 if !defined $ref; | 
| 37 |  |  |  |  |  |  |  | 
| 38 | 15 | 100 |  |  |  | 39 | return 1 if ref $ref eq 'GLOB';    # lexical filehandle | 
| 39 | 13 | 100 | 100 |  |  | 45 | return 1 if !ref $ref && ref \$ref eq 'GLOB'; # bare glob like *STDOUT | 
| 40 |  |  |  |  |  |  |  | 
| 41 | 12 | 100 |  |  |  | 13 | return 1 if eval { $ref->can('print') }; | 
|  | 12 |  |  |  |  | 90 |  | 
| 42 |  |  |  |  |  |  |  | 
| 43 | 2 |  |  |  |  | 17 | return 0; | 
| 44 |  |  |  |  |  |  | } | 
| 45 |  |  |  |  |  |  |  | 
| 46 | 17 |  |  |  |  | 55 | my @getter_setters = qw( | 
| 47 |  |  |  |  |  |  | _longest | 
| 48 |  |  |  |  |  |  | _printed_summary_header | 
| 49 |  |  |  |  |  |  | _colorizer | 
| 50 |  |  |  |  |  |  | ); | 
| 51 |  |  |  |  |  |  |  | 
| 52 | 17 |  |  |  |  | 190 | __PACKAGE__->mk_methods( @getter_setters, keys %VALIDATION_FOR ); | 
| 53 |  |  |  |  |  |  | } | 
| 54 |  |  |  |  |  |  |  | 
| 55 |  |  |  |  |  |  | =head1 NAME | 
| 56 |  |  |  |  |  |  |  | 
| 57 |  |  |  |  |  |  | TAP::Formatter::Base - Base class for harness output delegates | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | =head1 VERSION | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | Version 3.38 | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | =cut | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | our $VERSION = '3.38'; | 
| 66 |  |  |  |  |  |  |  | 
| 67 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | This provides console orientated output formatting for TAP::Harness. | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 72 |  |  |  |  |  |  |  | 
| 73 |  |  |  |  |  |  | use TAP::Formatter::Console; | 
| 74 |  |  |  |  |  |  | my $harness = TAP::Formatter::Console->new( \%args ); | 
| 75 |  |  |  |  |  |  |  | 
| 76 |  |  |  |  |  |  | =cut | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | sub _initialize { | 
| 79 | 88 |  |  | 88 |  | 118 | my ( $self, $arg_for ) = @_; | 
| 80 | 88 |  | 100 |  |  | 212 | $arg_for ||= {}; | 
| 81 |  |  |  |  |  |  |  | 
| 82 | 88 |  |  |  |  | 302 | $self->SUPER::_initialize($arg_for); | 
| 83 | 88 |  |  |  |  | 345 | my %arg_for = %$arg_for;    # force a shallow copy | 
| 84 |  |  |  |  |  |  |  | 
| 85 | 88 |  |  |  |  | 323 | $self->verbosity(0); | 
| 86 |  |  |  |  |  |  |  | 
| 87 | 88 |  |  |  |  | 330 | for my $name ( keys %VALIDATION_FOR ) { | 
| 88 | 958 |  |  |  |  | 738 | my $property = delete $arg_for{$name}; | 
| 89 | 958 | 100 |  |  |  | 1301 | if ( defined $property ) { | 
| 90 | 262 |  |  |  |  | 242 | my $validate = $VALIDATION_FOR{$name}; | 
| 91 | 262 |  |  |  |  | 512 | $self->$name( $self->$validate($property) ); | 
| 92 |  |  |  |  |  |  | } | 
| 93 |  |  |  |  |  |  | } | 
| 94 |  |  |  |  |  |  |  | 
| 95 | 86 | 50 |  |  |  | 278 | if ( my @props = keys %arg_for ) { | 
| 96 | 0 |  |  |  |  | 0 | $self->_croak( | 
| 97 |  |  |  |  |  |  | "Unknown arguments to " . __PACKAGE__ . "::new (@props)" ); | 
| 98 |  |  |  |  |  |  | } | 
| 99 |  |  |  |  |  |  |  | 
| 100 | 86 | 100 |  |  |  | 262 | $self->stdout( \*STDOUT ) unless $self->stdout; | 
| 101 |  |  |  |  |  |  |  | 
| 102 | 86 | 100 |  |  |  | 223 | if ( $self->color ) { | 
| 103 | 1 |  |  |  |  | 328 | require TAP::Formatter::Color; | 
| 104 | 1 |  |  |  |  | 9 | $self->_colorizer( TAP::Formatter::Color->new ); | 
| 105 |  |  |  |  |  |  | } | 
| 106 |  |  |  |  |  |  |  | 
| 107 | 86 |  |  |  |  | 312 | return $self; | 
| 108 |  |  |  |  |  |  | } | 
| 109 |  |  |  |  |  |  |  | 
| 110 | 423 |  |  | 423 | 1 | 882 | sub verbose      { shift->verbosity >= 1 } | 
| 111 | 398 |  |  | 398 | 1 | 784 | sub quiet        { shift->verbosity <= -1 } | 
| 112 | 142 |  |  | 142 | 1 | 319 | sub really_quiet { shift->verbosity <= -2 } | 
| 113 | 44 |  |  | 44 | 1 | 92 | sub silent       { shift->verbosity <= -3 } | 
| 114 |  |  |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | =head1 METHODS | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | =head2 Class Methods | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | =head3 C | 
| 120 |  |  |  |  |  |  |  | 
| 121 |  |  |  |  |  |  | my %args = ( | 
| 122 |  |  |  |  |  |  | verbose => 1, | 
| 123 |  |  |  |  |  |  | ) | 
| 124 |  |  |  |  |  |  | my $harness = TAP::Formatter::Console->new( \%args ); | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | The constructor returns a new C object. If | 
| 127 |  |  |  |  |  |  | a L is created with no C a | 
| 128 |  |  |  |  |  |  | C is automatically created. If any of the | 
| 129 |  |  |  |  |  |  | following options were given to TAP::Harness->new they well be passed to | 
| 130 |  |  |  |  |  |  | this constructor which accepts an optional hashref whose allowed keys are: | 
| 131 |  |  |  |  |  |  |  | 
| 132 |  |  |  |  |  |  | =over 4 | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | =item * C | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | Set the verbosity level. | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | =item * C | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  | Printing individual test results to STDOUT. | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  | =item * C | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | Append run time for each test to output. Uses L if available. | 
| 145 |  |  |  |  |  |  |  | 
| 146 |  |  |  |  |  |  | =item * C | 
| 147 |  |  |  |  |  |  |  | 
| 148 |  |  |  |  |  |  | Show test failures (this is a no-op if C is selected). | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  | =item * C | 
| 151 |  |  |  |  |  |  |  | 
| 152 |  |  |  |  |  |  | Show test comments (this is a no-op if C is selected). | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | =item * C | 
| 155 |  |  |  |  |  |  |  | 
| 156 |  |  |  |  |  |  | Suppressing some test output (mostly failures while tests are running). | 
| 157 |  |  |  |  |  |  |  | 
| 158 |  |  |  |  |  |  | =item * C | 
| 159 |  |  |  |  |  |  |  | 
| 160 |  |  |  |  |  |  | Suppressing everything but the tests summary. | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | =item * C | 
| 163 |  |  |  |  |  |  |  | 
| 164 |  |  |  |  |  |  | Suppressing all output. | 
| 165 |  |  |  |  |  |  |  | 
| 166 |  |  |  |  |  |  | =item * C | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | If parse errors are found in the TAP output, a note of this will be made | 
| 169 |  |  |  |  |  |  | in the summary report.  To see all of the parse errors, set this argument to | 
| 170 |  |  |  |  |  |  | true: | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | errors => 1 | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  | =item * C | 
| 175 |  |  |  |  |  |  |  | 
| 176 |  |  |  |  |  |  | If set to a true value, only test results with directives will be displayed. | 
| 177 |  |  |  |  |  |  | This overrides other settings such as C, C, or C. | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | =item * C | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | A filehandle for catching standard output. | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | =item * C | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | If defined specifies whether color output is desired. If C is not | 
| 186 |  |  |  |  |  |  | defined it will default to color output if color support is available on | 
| 187 |  |  |  |  |  |  | the current platform and output is not being redirected. | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | =item * C | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | The number of concurrent jobs this formatter will handle. | 
| 192 |  |  |  |  |  |  |  | 
| 193 |  |  |  |  |  |  | =item * C | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | Boolean value.  If false, disables the C test count which shows up while | 
| 196 |  |  |  |  |  |  | tests are running. | 
| 197 |  |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  | =back | 
| 199 |  |  |  |  |  |  |  | 
| 200 |  |  |  |  |  |  | Any keys for which the value is C will be ignored. | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | =cut | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | # new supplied by TAP::Base | 
| 205 |  |  |  |  |  |  |  | 
| 206 |  |  |  |  |  |  | =head3 C | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | Called by Test::Harness before any test output is generated. | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | This is an advisory and may not be called in the case where tests are | 
| 211 |  |  |  |  |  |  | being supplied to Test::Harness by an iterator. | 
| 212 |  |  |  |  |  |  |  | 
| 213 |  |  |  |  |  |  | =cut | 
| 214 |  |  |  |  |  |  |  | 
| 215 |  |  |  |  |  |  | sub prepare { | 
| 216 | 79 |  |  | 79 | 1 | 131 | my ( $self, @tests ) = @_; | 
| 217 |  |  |  |  |  |  |  | 
| 218 | 79 |  |  |  |  | 102 | my $longest = 0; | 
| 219 |  |  |  |  |  |  |  | 
| 220 | 79 |  |  |  |  | 204 | for my $test (@tests) { | 
| 221 | 107 | 100 |  |  |  | 246 | $longest = length $test if length $test > $longest; | 
| 222 |  |  |  |  |  |  | } | 
| 223 |  |  |  |  |  |  |  | 
| 224 | 79 |  |  |  |  | 238 | $self->_longest($longest); | 
| 225 |  |  |  |  |  |  | } | 
| 226 |  |  |  |  |  |  |  | 
| 227 | 0 |  |  | 0 |  | 0 | sub _format_now { strftime "[%H:%M:%S]", localtime } | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  | sub _format_name { | 
| 230 | 105 |  |  | 105 |  | 159 | my ( $self, $test ) = @_; | 
| 231 | 105 |  |  |  |  | 141 | my $name = $test; | 
| 232 | 105 |  |  |  |  | 473 | my $periods = '.' x ( $self->_longest + 2 - length $test ); | 
| 233 | 105 |  |  |  |  | 230 | $periods = " $periods "; | 
| 234 |  |  |  |  |  |  |  | 
| 235 | 105 | 50 |  |  |  | 291 | if ( $self->timer ) { | 
| 236 | 0 |  |  |  |  | 0 | my $stamp = $self->_format_now(); | 
| 237 | 0 |  |  |  |  | 0 | return "$stamp $name$periods"; | 
| 238 |  |  |  |  |  |  | } | 
| 239 |  |  |  |  |  |  | else { | 
| 240 | 105 |  |  |  |  | 315 | return "$name$periods"; | 
| 241 |  |  |  |  |  |  | } | 
| 242 |  |  |  |  |  |  |  | 
| 243 |  |  |  |  |  |  | } | 
| 244 |  |  |  |  |  |  |  | 
| 245 |  |  |  |  |  |  | =head3 C | 
| 246 |  |  |  |  |  |  |  | 
| 247 |  |  |  |  |  |  | Called to create a new test session. A test session looks like this: | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | my $session = $formatter->open_test( $test, $parser ); | 
| 250 |  |  |  |  |  |  | while ( defined( my $result = $parser->next ) ) { | 
| 251 |  |  |  |  |  |  | $session->result($result); | 
| 252 |  |  |  |  |  |  | exit 1 if $result->is_bailout; | 
| 253 |  |  |  |  |  |  | } | 
| 254 |  |  |  |  |  |  | $session->close_test; | 
| 255 |  |  |  |  |  |  |  | 
| 256 |  |  |  |  |  |  | =cut | 
| 257 |  |  |  |  |  |  |  | 
| 258 |  |  |  |  |  |  | sub open_test { | 
| 259 | 0 |  |  | 0 | 1 | 0 | die "Unimplemented."; | 
| 260 |  |  |  |  |  |  | } | 
| 261 |  |  |  |  |  |  |  | 
| 262 |  |  |  |  |  |  | sub _output_success { | 
| 263 | 18 |  |  | 18 |  | 33 | my ( $self, $msg ) = @_; | 
| 264 | 18 |  |  |  |  | 46 | $self->_output($msg); | 
| 265 |  |  |  |  |  |  | } | 
| 266 |  |  |  |  |  |  |  | 
| 267 |  |  |  |  |  |  | =head3 C | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | $harness->summary( $aggregate ); | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | C prints the summary report after all tests are run. The first | 
| 272 |  |  |  |  |  |  | argument is an aggregate to summarise. An optional second argument may | 
| 273 |  |  |  |  |  |  | be set to a true value to indicate that the summary is being output as a | 
| 274 |  |  |  |  |  |  | result of an interrupted test run. | 
| 275 |  |  |  |  |  |  |  | 
| 276 |  |  |  |  |  |  | =cut | 
| 277 |  |  |  |  |  |  |  | 
| 278 |  |  |  |  |  |  | sub summary { | 
| 279 | 44 |  |  | 44 | 1 | 64 | my ( $self, $aggregate, $interrupted ) = @_; | 
| 280 |  |  |  |  |  |  |  | 
| 281 | 44 | 100 |  |  |  | 121 | return if $self->silent; | 
| 282 |  |  |  |  |  |  |  | 
| 283 | 38 |  |  |  |  | 109 | my @t     = $aggregate->descriptions; | 
| 284 | 38 |  |  |  |  | 60 | my $tests = \@t; | 
| 285 |  |  |  |  |  |  |  | 
| 286 | 38 |  |  |  |  | 92 | my $runtime = $aggregate->elapsed_timestr; | 
| 287 |  |  |  |  |  |  |  | 
| 288 | 38 |  |  |  |  | 2581 | my $total  = $aggregate->total; | 
| 289 | 38 |  |  |  |  | 113 | my $passed = $aggregate->passed; | 
| 290 |  |  |  |  |  |  |  | 
| 291 | 38 | 50 |  |  |  | 81 | if ( $self->timer ) { | 
| 292 | 0 |  |  |  |  | 0 | $self->_output( $self->_format_now(), "\n" ); | 
| 293 |  |  |  |  |  |  | } | 
| 294 |  |  |  |  |  |  |  | 
| 295 | 38 | 50 |  |  |  | 81 | $self->_failure_output("Test run interrupted!\n") | 
| 296 |  |  |  |  |  |  | if $interrupted; | 
| 297 |  |  |  |  |  |  |  | 
| 298 |  |  |  |  |  |  | # TODO: Check this condition still works when all subtests pass but | 
| 299 |  |  |  |  |  |  | # the exit status is nonzero | 
| 300 |  |  |  |  |  |  |  | 
| 301 | 38 | 100 |  |  |  | 96 | if ( $aggregate->all_passed ) { | 
| 302 | 24 |  |  |  |  | 74 | $self->_output_success("All tests successful.\n"); | 
| 303 |  |  |  |  |  |  | } | 
| 304 |  |  |  |  |  |  |  | 
| 305 |  |  |  |  |  |  | # ~TODO option where $aggregate->skipped generates reports | 
| 306 | 38 | 100 | 100 |  |  | 342 | if ( $total != $passed or $aggregate->has_problems ) { | 
| 307 | 15 |  |  |  |  | 46 | $self->_output("\nTest Summary Report"); | 
| 308 | 15 |  |  |  |  | 240 | $self->_output("\n-------------------\n"); | 
| 309 | 15 |  |  |  |  | 190 | for my $test (@$tests) { | 
| 310 | 15 |  |  |  |  | 75 | $self->_printed_summary_header(0); | 
| 311 | 15 |  |  |  |  | 50 | my ($parser) = $aggregate->parsers($test); | 
| 312 | 15 |  |  |  |  | 75 | $self->_output_summary_failure( | 
| 313 |  |  |  |  |  |  | 'failed', | 
| 314 |  |  |  |  |  |  | [ '  Failed test:  ', '  Failed tests:  ' ], | 
| 315 |  |  |  |  |  |  | $test, $parser | 
| 316 |  |  |  |  |  |  | ); | 
| 317 | 15 |  |  |  |  | 34 | $self->_output_summary_failure( | 
| 318 |  |  |  |  |  |  | 'todo_passed', | 
| 319 |  |  |  |  |  |  | "  TODO passed:   ", $test, $parser | 
| 320 |  |  |  |  |  |  | ); | 
| 321 |  |  |  |  |  |  |  | 
| 322 |  |  |  |  |  |  | # ~TODO this cannot be the default | 
| 323 |  |  |  |  |  |  | #$self->_output_summary_failure( 'skipped', "  Tests skipped: " ); | 
| 324 |  |  |  |  |  |  |  | 
| 325 | 15 | 50 |  |  |  | 36 | if ( my $exit = $parser->exit ) { | 
|  |  | 50 |  |  |  |  |  | 
| 326 | 0 |  |  |  |  | 0 | $self->_summary_test_header( $test, $parser ); | 
| 327 | 0 |  |  |  |  | 0 | $self->_failure_output("  Non-zero exit status: $exit\n"); | 
| 328 |  |  |  |  |  |  | } | 
| 329 |  |  |  |  |  |  | elsif ( my $wait = $parser->wait ) { | 
| 330 | 0 |  |  |  |  | 0 | $self->_summary_test_header( $test, $parser ); | 
| 331 | 0 |  |  |  |  | 0 | $self->_failure_output("  Non-zero wait status: $wait\n"); | 
| 332 |  |  |  |  |  |  | } | 
| 333 |  |  |  |  |  |  |  | 
| 334 | 15 | 100 |  |  |  | 31 | if ( my @errors = $parser->parse_errors ) { | 
| 335 | 4 |  |  |  |  | 8 | my $explain; | 
| 336 | 4 | 50 | 33 |  |  | 19 | if ( @errors > $MAX_ERRORS && !$self->errors ) { | 
| 337 | 0 |  |  |  |  | 0 | $explain | 
| 338 |  |  |  |  |  |  | = "Displayed the first $MAX_ERRORS of " | 
| 339 |  |  |  |  |  |  | . scalar(@errors) | 
| 340 |  |  |  |  |  |  | . " TAP syntax errors.\n" | 
| 341 |  |  |  |  |  |  | . "Re-run prove with the -p option to see them all.\n"; | 
| 342 | 0 |  |  |  |  | 0 | splice @errors, $MAX_ERRORS; | 
| 343 |  |  |  |  |  |  | } | 
| 344 | 4 |  |  |  |  | 15 | $self->_summary_test_header( $test, $parser ); | 
| 345 | 4 |  |  |  |  | 18 | $self->_failure_output( | 
| 346 |  |  |  |  |  |  | sprintf "  Parse errors: %s\n", | 
| 347 |  |  |  |  |  |  | shift @errors | 
| 348 |  |  |  |  |  |  | ); | 
| 349 | 4 |  |  |  |  | 43 | for my $error (@errors) { | 
| 350 | 0 |  |  |  |  | 0 | my $spaces = ' ' x 16; | 
| 351 | 0 |  |  |  |  | 0 | $self->_failure_output("$spaces$error\n"); | 
| 352 |  |  |  |  |  |  | } | 
| 353 | 4 | 50 |  |  |  | 17 | $self->_failure_output($explain) if $explain; | 
| 354 |  |  |  |  |  |  | } | 
| 355 |  |  |  |  |  |  | } | 
| 356 |  |  |  |  |  |  | } | 
| 357 | 38 |  |  |  |  | 50 | my $files = @$tests; | 
| 358 | 38 |  |  |  |  | 136 | $self->_output("Files=$files, Tests=$total, $runtime\n"); | 
| 359 | 38 |  |  |  |  | 461 | my $status = $aggregate->get_status; | 
| 360 | 38 |  |  |  |  | 104 | $self->_output("Result: $status\n"); | 
| 361 |  |  |  |  |  |  | } | 
| 362 |  |  |  |  |  |  |  | 
| 363 |  |  |  |  |  |  | sub _output_summary_failure { | 
| 364 | 30 |  |  | 30 |  | 38 | my ( $self, $method, $name, $test, $parser ) = @_; | 
| 365 |  |  |  |  |  |  |  | 
| 366 |  |  |  |  |  |  | # ugly hack.  Must rethink this :( | 
| 367 | 30 | 100 |  |  |  | 62 | my $output = $method eq 'failed' ? '_failure_output' : '_output'; | 
| 368 |  |  |  |  |  |  |  | 
| 369 | 30 | 100 |  |  |  | 77 | if ( my @r = $parser->$method() ) { | 
| 370 | 13 |  |  |  |  | 39 | $self->_summary_test_header( $test, $parser ); | 
| 371 | 13 | 100 |  |  |  | 47 | my ( $singular, $plural ) | 
| 372 |  |  |  |  |  |  | = 'ARRAY' eq ref $name ? @$name : ( $name, $name ); | 
| 373 | 13 | 50 |  |  |  | 42 | $self->$output( @r == 1 ? $singular : $plural ); | 
| 374 | 13 |  |  |  |  | 129 | my @results = $self->_balanced_range( 40, @r ); | 
| 375 | 13 |  |  |  |  | 50 | $self->$output( sprintf "%s\n" => shift @results ); | 
| 376 | 13 |  |  |  |  | 98 | my $spaces = ' ' x 16; | 
| 377 | 13 |  |  |  |  | 43 | while (@results) { | 
| 378 | 0 |  |  |  |  | 0 | $self->$output( sprintf "$spaces%s\n" => shift @results ); | 
| 379 |  |  |  |  |  |  | } | 
| 380 |  |  |  |  |  |  | } | 
| 381 |  |  |  |  |  |  | } | 
| 382 |  |  |  |  |  |  |  | 
| 383 |  |  |  |  |  |  | sub _summary_test_header { | 
| 384 | 17 |  |  | 17 |  | 21 | my ( $self, $test, $parser ) = @_; | 
| 385 | 17 | 100 |  |  |  | 31 | return if $self->_printed_summary_header; | 
| 386 | 15 |  |  |  |  | 32 | my $spaces = ' ' x ( $self->_longest - length $test ); | 
| 387 | 15 | 50 |  |  |  | 28 | $spaces = ' ' unless $spaces; | 
| 388 | 15 |  |  |  |  | 35 | my $output = $self->_get_output_method($parser); | 
| 389 | 15 |  |  |  |  | 47 | my $wait   = $parser->wait; | 
| 390 | 15 | 50 |  |  |  | 32 | defined $wait or $wait = '(none)'; | 
| 391 | 15 |  |  |  |  | 52 | $self->$output( | 
| 392 |  |  |  |  |  |  | sprintf "$test$spaces(Wstat: %s Tests: %d Failed: %d)\n", | 
| 393 |  |  |  |  |  |  | $wait, $parser->tests_run, scalar $parser->failed | 
| 394 |  |  |  |  |  |  | ); | 
| 395 | 15 |  |  |  |  | 185 | $self->_printed_summary_header(1); | 
| 396 |  |  |  |  |  |  | } | 
| 397 |  |  |  |  |  |  |  | 
| 398 |  |  |  |  |  |  | sub _output { | 
| 399 | 162 |  |  | 162 |  | 180 | my $self = shift; | 
| 400 |  |  |  |  |  |  |  | 
| 401 | 162 |  |  |  |  | 183 | print { $self->stdout } @_; | 
|  | 162 |  |  |  |  | 328 |  | 
| 402 |  |  |  |  |  |  | } | 
| 403 |  |  |  |  |  |  |  | 
| 404 |  |  |  |  |  |  | sub _failure_output { | 
| 405 | 68 |  |  | 68 |  | 71 | my $self = shift; | 
| 406 |  |  |  |  |  |  |  | 
| 407 | 68 |  |  |  |  | 136 | $self->_output(@_); | 
| 408 |  |  |  |  |  |  | } | 
| 409 |  |  |  |  |  |  |  | 
| 410 |  |  |  |  |  |  | sub _balanced_range { | 
| 411 | 14 |  |  | 14 |  | 762 | my ( $self, $limit, @range ) = @_; | 
| 412 | 14 |  |  |  |  | 42 | @range = $self->_range(@range); | 
| 413 | 14 |  |  |  |  | 18 | my $line = ""; | 
| 414 | 14 |  |  |  |  | 15 | my @lines; | 
| 415 | 14 |  |  |  |  | 16 | my $curr = 0; | 
| 416 | 14 |  |  |  |  | 31 | while (@range) { | 
| 417 | 17 | 100 |  |  |  | 39 | if ( $curr < $limit ) { | 
|  |  | 50 |  |  |  |  |  | 
| 418 | 16 |  |  |  |  | 30 | my $range = ( shift @range ) . ", "; | 
| 419 | 16 |  |  |  |  | 27 | $line .= $range; | 
| 420 | 16 |  |  |  |  | 40 | $curr += length $range; | 
| 421 |  |  |  |  |  |  | } | 
| 422 |  |  |  |  |  |  | elsif (@range) { | 
| 423 | 1 |  |  |  |  | 7 | $line =~ s/, $//; | 
| 424 | 1 |  |  |  |  | 3 | push @lines => $line; | 
| 425 | 1 |  |  |  |  | 2 | $line = ''; | 
| 426 | 1 |  |  |  |  | 4 | $curr = 0; | 
| 427 |  |  |  |  |  |  | } | 
| 428 |  |  |  |  |  |  | } | 
| 429 | 14 | 50 |  |  |  | 39 | if ($line) { | 
| 430 | 14 |  |  |  |  | 59 | $line =~ s/, $//; | 
| 431 | 14 |  |  |  |  | 23 | push @lines => $line; | 
| 432 |  |  |  |  |  |  | } | 
| 433 | 14 |  |  |  |  | 33 | return @lines; | 
| 434 |  |  |  |  |  |  | } | 
| 435 |  |  |  |  |  |  |  | 
| 436 |  |  |  |  |  |  | sub _range { | 
| 437 | 15 |  |  | 15 |  | 994 | my ( $self, @numbers ) = @_; | 
| 438 |  |  |  |  |  |  |  | 
| 439 |  |  |  |  |  |  | # shouldn't be needed, but subclasses might call this | 
| 440 | 15 |  |  |  |  | 31 | @numbers = sort { $a <=> $b } @numbers; | 
|  | 20 |  |  |  |  | 19 |  | 
| 441 | 15 |  |  |  |  | 21 | my ( $min, @range ); | 
| 442 |  |  |  |  |  |  |  | 
| 443 | 15 |  |  |  |  | 51 | for my $i ( 0 .. $#numbers ) { | 
| 444 | 25 |  |  |  |  | 37 | my $num  = $numbers[$i]; | 
| 445 | 25 |  |  |  |  | 33 | my $next = $numbers[ $i + 1 ]; | 
| 446 | 25 | 100 | 100 |  |  | 94 | if ( defined $next && $next == $num + 1 ) { | 
|  |  | 100 |  |  |  |  |  | 
| 447 | 6 | 100 |  |  |  | 13 | if ( !defined $min ) { | 
| 448 | 4 |  |  |  |  | 7 | $min = $num; | 
| 449 |  |  |  |  |  |  | } | 
| 450 |  |  |  |  |  |  | } | 
| 451 |  |  |  |  |  |  | elsif ( defined $min ) { | 
| 452 | 4 |  |  |  |  | 7 | push @range => "$min-$num"; | 
| 453 | 4 |  |  |  |  | 7 | undef $min; | 
| 454 |  |  |  |  |  |  | } | 
| 455 |  |  |  |  |  |  | else { | 
| 456 | 15 |  |  |  |  | 33 | push @range => $num; | 
| 457 |  |  |  |  |  |  | } | 
| 458 |  |  |  |  |  |  | } | 
| 459 | 15 |  |  |  |  | 43 | return @range; | 
| 460 |  |  |  |  |  |  | } | 
| 461 |  |  |  |  |  |  |  | 
| 462 |  |  |  |  |  |  | sub _get_output_method { | 
| 463 | 54 |  |  | 54 |  | 54 | my ( $self, $parser ) = @_; | 
| 464 | 54 | 100 |  |  |  | 90 | return $parser->has_problems ? '_failure_output' : '_output'; | 
| 465 |  |  |  |  |  |  | } | 
| 466 |  |  |  |  |  |  |  | 
| 467 |  |  |  |  |  |  | 1; |