xref: /openbsd-src/gnu/usr.bin/perl/cpan/Test-Harness/lib/TAP/Parser/Aggregator.pm (revision 3d61058aa5c692477b6d18acfbbdb653a9930ff9)
1b39c5158Smillertpackage TAP::Parser::Aggregator;
2b39c5158Smillert
3b39c5158Smillertuse strict;
46fb12b70Safresh1use warnings;
5b39c5158Smillertuse Benchmark;
6b39c5158Smillert
76fb12b70Safresh1use base 'TAP::Object';
8b39c5158Smillert
9b39c5158Smillert=head1 NAME
10b39c5158Smillert
11b39c5158SmillertTAP::Parser::Aggregator - Aggregate TAP::Parser results
12b39c5158Smillert
13b39c5158Smillert=head1 VERSION
14b39c5158Smillert
15*3d61058aSafresh1Version 3.48
16b39c5158Smillert
17b39c5158Smillert=cut
18b39c5158Smillert
19*3d61058aSafresh1our $VERSION = '3.48';
20b39c5158Smillert
21b39c5158Smillert=head1 SYNOPSIS
22b39c5158Smillert
23b39c5158Smillert    use TAP::Parser::Aggregator;
24b39c5158Smillert
25b39c5158Smillert    my $aggregate = TAP::Parser::Aggregator->new;
26b39c5158Smillert    $aggregate->add( 't/00-load.t', $load_parser );
27b39c5158Smillert    $aggregate->add( 't/10-lex.t',  $lex_parser  );
28b39c5158Smillert
29b39c5158Smillert    my $summary = <<'END_SUMMARY';
30b39c5158Smillert    Passed:  %s
31b39c5158Smillert    Failed:  %s
32b39c5158Smillert    Unexpectedly succeeded: %s
33b39c5158Smillert    END_SUMMARY
34b39c5158Smillert    printf $summary,
35b39c5158Smillert           scalar $aggregate->passed,
36b39c5158Smillert           scalar $aggregate->failed,
37b39c5158Smillert           scalar $aggregate->todo_passed;
38b39c5158Smillert
39b39c5158Smillert=head1 DESCRIPTION
40b39c5158Smillert
41b39c5158SmillertC<TAP::Parser::Aggregator> collects parser objects and allows
42b39c5158Smillertreporting/querying their aggregate results.
43b39c5158Smillert
44b39c5158Smillert=head1 METHODS
45b39c5158Smillert
46b39c5158Smillert=head2 Class Methods
47b39c5158Smillert
48b39c5158Smillert=head3 C<new>
49b39c5158Smillert
50b39c5158Smillert my $aggregate = TAP::Parser::Aggregator->new;
51b39c5158Smillert
52b39c5158SmillertReturns a new C<TAP::Parser::Aggregator> object.
53b39c5158Smillert
54b39c5158Smillert=cut
55b39c5158Smillert
56b39c5158Smillert# new() implementation supplied by TAP::Object
57b39c5158Smillert
58b39c5158Smillertmy %SUMMARY_METHOD_FOR;
59b39c5158Smillert
60b39c5158SmillertBEGIN {    # install summary methods
61b39c5158Smillert    %SUMMARY_METHOD_FOR = map { $_ => $_ } qw(
62b39c5158Smillert      failed
63b39c5158Smillert      parse_errors
64b39c5158Smillert      passed
65b39c5158Smillert      skipped
66b39c5158Smillert      todo
67b39c5158Smillert      todo_passed
68b39c5158Smillert      total
69b39c5158Smillert      wait
70b39c5158Smillert      exit
71b39c5158Smillert    );
72b39c5158Smillert    $SUMMARY_METHOD_FOR{total}   = 'tests_run';
73b39c5158Smillert    $SUMMARY_METHOD_FOR{planned} = 'tests_planned';
74b39c5158Smillert
75898184e3Ssthen    for my $method ( keys %SUMMARY_METHOD_FOR ) {
76b39c5158Smillert        next if 'total' eq $method;
77b39c5158Smillert        no strict 'refs';
78b39c5158Smillert        *$method = sub {
79b39c5158Smillert            my $self = shift;
80b39c5158Smillert            return wantarray
81b39c5158Smillert              ? @{ $self->{"descriptions_for_$method"} }
82b39c5158Smillert              : $self->{$method};
83b39c5158Smillert        };
84b39c5158Smillert    }
85b39c5158Smillert}    # end install summary methods
86b39c5158Smillert
87b39c5158Smillertsub _initialize {
88b39c5158Smillert    my ($self) = @_;
89b39c5158Smillert    $self->{parser_for}  = {};
90b39c5158Smillert    $self->{parse_order} = [];
91898184e3Ssthen    for my $summary ( keys %SUMMARY_METHOD_FOR ) {
92b39c5158Smillert        $self->{$summary} = 0;
93b39c5158Smillert        next if 'total' eq $summary;
94b39c5158Smillert        $self->{"descriptions_for_$summary"} = [];
95b39c5158Smillert    }
96b39c5158Smillert    return $self;
97b39c5158Smillert}
98b39c5158Smillert
99b39c5158Smillert##############################################################################
100b39c5158Smillert
101b39c5158Smillert=head2 Instance Methods
102b39c5158Smillert
103b39c5158Smillert=head3 C<add>
104b39c5158Smillert
105b39c5158Smillert  $aggregate->add( $description => $parser );
106b39c5158Smillert
107b39c5158SmillertThe C<$description> is usually a test file name (but only by
108b39c5158Smillertconvention.)  It is used as a unique identifier (see e.g.
109b39c5158SmillertL<"parsers">.)  Reusing a description is a fatal error.
110b39c5158Smillert
111b39c5158SmillertThe C<$parser> is a L<TAP::Parser|TAP::Parser> object.
112b39c5158Smillert
113b39c5158Smillert=cut
114b39c5158Smillert
115b39c5158Smillertsub add {
116b39c5158Smillert    my ( $self, $description, $parser ) = @_;
117b39c5158Smillert    if ( exists $self->{parser_for}{$description} ) {
118b39c5158Smillert        $self->_croak( "You already have a parser for ($description)."
119b39c5158Smillert              . " Perhaps you have run the same test twice." );
120b39c5158Smillert    }
121b39c5158Smillert    push @{ $self->{parse_order} } => $description;
122b39c5158Smillert    $self->{parser_for}{$description} = $parser;
123b39c5158Smillert
124b39c5158Smillert    while ( my ( $summary, $method ) = each %SUMMARY_METHOD_FOR ) {
125b39c5158Smillert
126b39c5158Smillert        # Slightly nasty. Instead we should maybe have 'cooked' accessors
127b39c5158Smillert        # for results that may be masked by the parser.
128b39c5158Smillert        next
129b39c5158Smillert          if ( $method eq 'exit' || $method eq 'wait' )
130b39c5158Smillert          && $parser->ignore_exit;
131b39c5158Smillert
132b39c5158Smillert        if ( my $count = $parser->$method() ) {
133b39c5158Smillert            $self->{$summary} += $count;
134b39c5158Smillert            push @{ $self->{"descriptions_for_$summary"} } => $description;
135b39c5158Smillert        }
136b39c5158Smillert    }
137b39c5158Smillert
138b39c5158Smillert    return $self;
139b39c5158Smillert}
140b39c5158Smillert
141b39c5158Smillert##############################################################################
142b39c5158Smillert
143b39c5158Smillert=head3 C<parsers>
144b39c5158Smillert
145b39c5158Smillert  my $count   = $aggregate->parsers;
146b39c5158Smillert  my @parsers = $aggregate->parsers;
147b39c5158Smillert  my @parsers = $aggregate->parsers(@descriptions);
148b39c5158Smillert
149b39c5158SmillertIn scalar context without arguments, this method returns the number of parsers
150b39c5158Smillertaggregated.  In list context without arguments, returns the parsers in the
151b39c5158Smillertorder they were added.
152b39c5158Smillert
153b39c5158SmillertIf C<@descriptions> is given, these correspond to the keys used in each
154b39c5158Smillertcall to the add() method.  Returns an array of the requested parsers (in
155b39c5158Smillertthe requested order) in list context or an array reference in scalar
156b39c5158Smillertcontext.
157b39c5158Smillert
158b39c5158SmillertRequesting an unknown identifier is a fatal error.
159b39c5158Smillert
160b39c5158Smillert=cut
161b39c5158Smillert
162b39c5158Smillertsub parsers {
163b39c5158Smillert    my $self = shift;
164b39c5158Smillert    return $self->_get_parsers(@_) if @_;
165b39c5158Smillert    my $descriptions = $self->{parse_order};
166b39c5158Smillert    my @parsers      = @{ $self->{parser_for} }{@$descriptions};
167b39c5158Smillert
168b39c5158Smillert    # Note:  Because of the way context works, we must assign the parsers to
169b39c5158Smillert    # the @parsers array or else this method does not work as documented.
170b39c5158Smillert    return @parsers;
171b39c5158Smillert}
172b39c5158Smillert
173b39c5158Smillertsub _get_parsers {
174b39c5158Smillert    my ( $self, @descriptions ) = @_;
175b39c5158Smillert    my @parsers;
176898184e3Ssthen    for my $description (@descriptions) {
177b39c5158Smillert        $self->_croak("A parser for ($description) could not be found")
178b39c5158Smillert          unless exists $self->{parser_for}{$description};
179b39c5158Smillert        push @parsers => $self->{parser_for}{$description};
180b39c5158Smillert    }
181b39c5158Smillert    return wantarray ? @parsers : \@parsers;
182b39c5158Smillert}
183b39c5158Smillert
184b39c5158Smillert=head3 C<descriptions>
185b39c5158Smillert
186b39c5158SmillertGet an array of descriptions in the order in which they were added to
187b39c5158Smillertthe aggregator.
188b39c5158Smillert
189b39c5158Smillert=cut
190b39c5158Smillert
191b39c5158Smillertsub descriptions { @{ shift->{parse_order} || [] } }
192b39c5158Smillert
193b39c5158Smillert=head3 C<start>
194b39c5158Smillert
195b39c5158SmillertCall C<start> immediately before adding any results to the aggregator.
196b39c5158SmillertAmong other times it records the start time for the test run.
197b39c5158Smillert
198b39c5158Smillert=cut
199b39c5158Smillert
200b39c5158Smillertsub start {
201b39c5158Smillert    my $self = shift;
202b39c5158Smillert    $self->{start_time} = Benchmark->new;
203b39c5158Smillert}
204b39c5158Smillert
205b39c5158Smillert=head3 C<stop>
206b39c5158Smillert
207b39c5158SmillertCall C<stop> immediately after adding all test results to the aggregator.
208b39c5158Smillert
209b39c5158Smillert=cut
210b39c5158Smillert
211b39c5158Smillertsub stop {
212b39c5158Smillert    my $self = shift;
213b39c5158Smillert    $self->{end_time} = Benchmark->new;
214b39c5158Smillert}
215b39c5158Smillert
216b39c5158Smillert=head3 C<elapsed>
217b39c5158Smillert
218b39c5158SmillertElapsed returns a L<Benchmark> object that represents the running time
219b39c5158Smillertof the aggregated tests. In order for C<elapsed> to be valid you must
220b39c5158Smillertcall C<start> before running the tests and C<stop> immediately
221b39c5158Smillertafterwards.
222b39c5158Smillert
223b39c5158Smillert=cut
224b39c5158Smillert
225b39c5158Smillertsub elapsed {
226b39c5158Smillert    my $self = shift;
227b39c5158Smillert
228b39c5158Smillert    require Carp;
229b39c5158Smillert    Carp::croak
230b39c5158Smillert      q{Can't call elapsed without first calling start and then stop}
231b39c5158Smillert      unless defined $self->{start_time} && defined $self->{end_time};
232b39c5158Smillert    return timediff( $self->{end_time}, $self->{start_time} );
233b39c5158Smillert}
234b39c5158Smillert
235b39c5158Smillert=head3 C<elapsed_timestr>
236b39c5158Smillert
237b39c5158SmillertReturns a formatted string representing the runtime returned by
238b39c5158SmillertC<elapsed()>.  This lets the caller not worry about Benchmark.
239b39c5158Smillert
240b39c5158Smillert=cut
241b39c5158Smillert
242b39c5158Smillertsub elapsed_timestr {
243b39c5158Smillert    my $self = shift;
244b39c5158Smillert
245b39c5158Smillert    my $elapsed = $self->elapsed;
246b39c5158Smillert
247b39c5158Smillert    return timestr($elapsed);
248b39c5158Smillert}
249b39c5158Smillert
250b39c5158Smillert=head3 C<all_passed>
251b39c5158Smillert
252b39c5158SmillertReturn true if all the tests passed and no parse errors were detected.
253b39c5158Smillert
254b39c5158Smillert=cut
255b39c5158Smillert
256b39c5158Smillertsub all_passed {
257b39c5158Smillert    my $self = shift;
258b39c5158Smillert    return
259b39c5158Smillert         $self->total
260b39c5158Smillert      && $self->total == $self->passed
261b39c5158Smillert      && !$self->has_errors;
262b39c5158Smillert}
263b39c5158Smillert
264b39c5158Smillert=head3 C<get_status>
265b39c5158Smillert
266b39c5158SmillertGet a single word describing the status of the aggregated tests.
267b39c5158SmillertDepending on the outcome of the tests returns 'PASS', 'FAIL' or
268b39c5158Smillert'NOTESTS'. This token is understood by L<CPAN::Reporter>.
269b39c5158Smillert
270b39c5158Smillert=cut
271b39c5158Smillert
272b39c5158Smillertsub get_status {
273b39c5158Smillert    my $self = shift;
274b39c5158Smillert
275b39c5158Smillert    my $total  = $self->total;
276b39c5158Smillert    my $passed = $self->passed;
277b39c5158Smillert
278b39c5158Smillert    return
279b39c5158Smillert        ( $self->has_errors || $total != $passed ) ? 'FAIL'
280b39c5158Smillert      : $total ? 'PASS'
281b39c5158Smillert      :          'NOTESTS';
282b39c5158Smillert}
283b39c5158Smillert
284b39c5158Smillert##############################################################################
285b39c5158Smillert
286b39c5158Smillert=head2 Summary methods
287b39c5158Smillert
288b39c5158SmillertEach of the following methods will return the total number of corresponding
289b39c5158Smillerttests if called in scalar context.  If called in list context, returns the
290b39c5158Smillertdescriptions of the parsers which contain the corresponding tests (see C<add>
291b39c5158Smillertfor an explanation of description.
292b39c5158Smillert
293b39c5158Smillert=over 4
294b39c5158Smillert
295b39c5158Smillert=item * failed
296b39c5158Smillert
297b39c5158Smillert=item * parse_errors
298b39c5158Smillert
299b39c5158Smillert=item * passed
300b39c5158Smillert
301b39c5158Smillert=item * planned
302b39c5158Smillert
303b39c5158Smillert=item * skipped
304b39c5158Smillert
305b39c5158Smillert=item * todo
306b39c5158Smillert
307b39c5158Smillert=item * todo_passed
308b39c5158Smillert
309b39c5158Smillert=item * wait
310b39c5158Smillert
311b39c5158Smillert=item * exit
312b39c5158Smillert
313b39c5158Smillert=back
314b39c5158Smillert
315b39c5158SmillertFor example, to find out how many tests unexpectedly succeeded (TODO tests
316b39c5158Smillertwhich passed when they shouldn't):
317b39c5158Smillert
318b39c5158Smillert my $count        = $aggregate->todo_passed;
319b39c5158Smillert my @descriptions = $aggregate->todo_passed;
320b39c5158Smillert
321b39c5158SmillertNote that C<wait> and C<exit> are the totals of the wait and exit
322b39c5158Smillertstatuses of each of the tests. These values are totalled only to provide
323b39c5158Smillerta true value if any of them are non-zero.
324b39c5158Smillert
325b39c5158Smillert=cut
326b39c5158Smillert
327b39c5158Smillert##############################################################################
328b39c5158Smillert
329b39c5158Smillert=head3 C<total>
330b39c5158Smillert
331b39c5158Smillert  my $tests_run = $aggregate->total;
332b39c5158Smillert
333b39c5158SmillertReturns the total number of tests run.
334b39c5158Smillert
335b39c5158Smillert=cut
336b39c5158Smillert
337b39c5158Smillertsub total { shift->{total} }
338b39c5158Smillert
339b39c5158Smillert##############################################################################
340b39c5158Smillert
341b39c5158Smillert=head3 C<has_problems>
342b39c5158Smillert
343b39c5158Smillert  if ( $parser->has_problems ) {
344b39c5158Smillert      ...
345b39c5158Smillert  }
346b39c5158Smillert
347b39c5158SmillertIdentical to C<has_errors>, but also returns true if any TODO tests
348b39c5158Smillertunexpectedly succeeded.  This is more akin to "warnings".
349b39c5158Smillert
350b39c5158Smillert=cut
351b39c5158Smillert
352b39c5158Smillertsub has_problems {
353b39c5158Smillert    my $self = shift;
354b39c5158Smillert    return $self->todo_passed
355b39c5158Smillert      || $self->has_errors;
356b39c5158Smillert}
357b39c5158Smillert
358b39c5158Smillert##############################################################################
359b39c5158Smillert
360b39c5158Smillert=head3 C<has_errors>
361b39c5158Smillert
362b39c5158Smillert  if ( $parser->has_errors ) {
363b39c5158Smillert      ...
364b39c5158Smillert  }
365b39c5158Smillert
366b39c5158SmillertReturns true if I<any> of the parsers failed.  This includes:
367b39c5158Smillert
368b39c5158Smillert=over 4
369b39c5158Smillert
370b39c5158Smillert=item * Failed tests
371b39c5158Smillert
372b39c5158Smillert=item * Parse errors
373b39c5158Smillert
374b39c5158Smillert=item * Bad exit or wait status
375b39c5158Smillert
376b39c5158Smillert=back
377b39c5158Smillert
378b39c5158Smillert=cut
379b39c5158Smillert
380b39c5158Smillertsub has_errors {
381b39c5158Smillert    my $self = shift;
382b39c5158Smillert    return
383b39c5158Smillert         $self->failed
384b39c5158Smillert      || $self->parse_errors
385b39c5158Smillert      || $self->exit
386b39c5158Smillert      || $self->wait;
387b39c5158Smillert}
388b39c5158Smillert
389b39c5158Smillert##############################################################################
390b39c5158Smillert
391b39c5158Smillert=head3 C<todo_failed>
392b39c5158Smillert
393b39c5158Smillert  # deprecated in favor of 'todo_passed'.  This method was horribly misnamed.
394b39c5158Smillert
395b39c5158SmillertThis was a badly misnamed method.  It indicates which TODO tests unexpectedly
396b39c5158Smillertsucceeded.  Will now issue a warning and call C<todo_passed>.
397b39c5158Smillert
398b39c5158Smillert=cut
399b39c5158Smillert
400b39c5158Smillertsub todo_failed {
401b39c5158Smillert    warn
402b39c5158Smillert      '"todo_failed" is deprecated.  Please use "todo_passed".  See the docs.';
403b39c5158Smillert    goto &todo_passed;
404b39c5158Smillert}
405b39c5158Smillert
406b39c5158Smillert=head1 See Also
407b39c5158Smillert
408b39c5158SmillertL<TAP::Parser>
409b39c5158Smillert
410b39c5158SmillertL<TAP::Harness>
411b39c5158Smillert
412b39c5158Smillert=cut
413b39c5158Smillert
414b39c5158Smillert1;
415