File Coverage

blib/lib/Test/Class/Moose/Report.pm
Criterion Covered Total %
statement 66 66 100.0
branch 5 8 62.5
condition n/a
subroutine 17 17 100.0
pod 8 9 88.8
total 96 100 96.0


line stmt bran cond sub pod time code
1             package Test::Class::Moose::Report;
2              
3             # ABSTRACT: Test information for Test::Class::Moose
4              
5 30     30   456 use strict;
  30         80  
  30         1448  
6 30     30   209 use warnings;
  30         74  
  30         2383  
7 30     30   251 use namespace::autoclean;
  30         67  
  30         369  
8              
9 30     30   3265 use 5.010000;
  30         125  
10              
11             our $VERSION = '1.00';
12              
13 30     30   239 use Carp;
  30         65  
  30         3085  
14 30     30   229 use Moose;
  30         75  
  30         401  
15             with 'Test::Class::Moose::Role::HasTimeReport';
16              
17 30     30   190004 use List::Util qw( first sum0 );
  30         96  
  30         32585  
18              
19             has test_classes => (
20             is => 'ro',
21             traits => ['Array'],
22             isa => 'ArrayRef[Test::Class::Moose::Report::Class]',
23             default => sub { [] },
24             handles => {
25             _all_test_classes => 'elements',
26             add_test_class => 'push',
27             num_test_classes => 'count',
28             },
29             );
30              
31             sub num_test_instances {
32 8     8 1 5622 my $self = shift;
33 8         44 return sum0 map { $_->num_test_instances } $self->all_test_classes;
  24         1702  
34             }
35              
36             sub num_test_methods {
37 8     8 1 4857 my $self = shift;
38 54         2767 return scalar grep { !$_->is_skipped }
39 24         1638 map { $_->all_test_methods }
40 8         40 map { $_->all_test_instances } $self->all_test_classes;
  24         1715  
41             }
42              
43             sub num_tests_run {
44 9     9 1 11204 my $self = shift;
45 51         2460 return sum0 map { $_->num_tests_run }
46 54         3022 grep { !$_->is_skipped }
47 25         1818 map { $_->all_test_methods }
48 9         44 map { $_->all_test_instances } $self->all_test_classes;
  25         1646  
49             }
50              
51             sub all_test_classes {
52 39     39 1 293821 my $self = shift;
53 39         3088 return $self->_all_test_classes;
54             }
55              
56             sub current_class {
57 153     153 1 314 my $self = shift;
58 153         7636 return $self->test_classes->[-1];
59             }
60              
61             sub current_instance {
62 153     153 1 750 my $self = shift;
63 153 50       415 my $current_class = $self->current_class or return;
64 153         937 return $current_class->current_instance;
65             }
66              
67             sub current_method {
68 87     87 1 210 my $self = shift;
69 87 50       286 my $current_instance = $self->current_instance or return;
70 87         572 return $current_instance->current_method;
71             }
72              
73             sub plan {
74 13     13 0 76 my ( $self, $plan ) = @_;
75 13 50       55 my $current_method = $self->current_method
76             or croak(q{You tried to plan but we don't have a test method yet!});
77 13         164 $current_method->plan($plan);
78             }
79              
80             sub timing_data {
81 6     6 1 104 my $self = shift;
82              
83 6         374 my %t = ( time => $self->time->as_hashref );
84              
85 6         41 for my $class ( $self->all_test_classes ) {
86 20         1278 my $class_inner = $t{class}{ $class->name }
87             = { time => $class->time->as_hashref };
88              
89 20         1348 for my $instance ( $class->all_test_instances ) {
90 20         1062 my $instance_inner = $class_inner->{instance}{ $instance->name }
91             = { time => $instance->time->as_hashref };
92              
93 20         116 $self->_populate_control_timing_data(
94             $instance_inner, $instance,
95             qw( test_startup test_shutdown )
96             );
97              
98 20         1393 for my $method ( $instance->all_test_methods ) {
99 45         2240 my $method_inner = $instance_inner->{method}{ $method->name }
100             = { time => $method->time->as_hashref };
101              
102 45         206 $self->_populate_control_timing_data(
103             $method_inner, $method,
104             qw( test_setup test_teardown ),
105             );
106             }
107             }
108             }
109              
110 6         66 return \%t;
111             }
112              
113             sub _populate_control_timing_data {
114 65     65   134 my $self = shift;
115 65         149 my $hashref = shift;
116 65         110 my $report = shift;
117              
118 65         192 for my $control (@_) {
119 130 100       256 my $control_method = $report->${ \( $control . '_method' ) }
  130         7395  
120             or next;
121             $hashref->{control}{$control}{time}
122 126         6024 = $control_method->time->as_hashref;
123             }
124              
125 65         238 return;
126             }
127              
128             __PACKAGE__->meta->make_immutable;
129              
130             1;
131              
132             __END__
133              
134             =pod
135              
136             =encoding UTF-8
137              
138             =head1 NAME
139              
140             Test::Class::Moose::Report - Test information for Test::Class::Moose
141              
142             =head1 VERSION
143              
144             version 1.00
145              
146             =head1 SYNOPSIS
147              
148             use Test::Class::Moose::Runner;
149              
150             my $runner = Test::Class::Moose::Runner->new;
151             $runner->runtests;
152             my $report = $runner->test_report;
153              
154             =head1 DESCRIPTION
155              
156             When working with larger test suites, it's useful to have full reporting
157             information available about the test suite. The reporting features of
158             L<Test::Class::Moose> allow you to report on the number of test class instances
159             and methods run (and number of tests), along with timing information to help
160             you track down which tests are running slowly. You can even run tests on your
161             report information:
162              
163             #!/usr/bin/env perl
164             use lib 'lib';
165             use Test::Most;
166             use Test::Class::Moose::Load qw(t/lib);
167             my $test_suite = Test::Class::Moose->new;
168              
169             subtest 'run the test suite' => sub {
170             $test_suite->runtests;
171             };
172              
173             my $report = $test_suite->test_report;
174             my $duration = $report->time->duration;
175             diag "Test suite run time: $duration";
176              
177             foreach my $class ($report->all_test_classes) {
178             my $class_name = $class->name;
179             subtest "report for class:$class_name" => sub {
180             ok !$class->is_skipped, "class:$class_name was not skipped";
181             ok $class->passed, "class:$class_name passed";
182              
183             my @i = $class->all_test_instances;
184             is scalar @i, 1, "tested one instance for $class_name";
185              
186             foreach my $instance (@i) {
187             my $instance_name = $instance->name;
188             subtest "report for instance:$instance_name" => sub {
189             ok !$instance->is_skipped,
190             "instance:$instance_name was not skipped";
191             ok $instance->passed, "instance:$instance_name passed";
192              
193             my @methods = $instance->all_test_methods;
194             is_deeply
195             [ sort map { $_->name } @methods ],
196             $expected_methods{$class_name},
197             "instance:$instance_name ran the expected methods";
198              
199             foreach my $method (@methods) {
200             my $method_name = $method->name;
201             subtest "report for method:$method_name" => sub {
202             ok !$method->is_skipped,
203             "$method_name was not skipped";
204             cmp_ok $method->num_tests_run, '>', 0,
205             '... and some tests should have been run';
206             _test_report_time($method);
207             };
208             }
209             };
210             }
211             };
212             }
213              
214             Reporting is currently in alpha. The interface is not guaranteed to be stable.
215              
216             =for Pod::Coverage plan
217              
218             =begin comment
219              
220             This is the code I used to generate the example in this module (plus a little
221             manual editing to move the time key to the top of each nested hashref). This is
222             based on the timing data from running basic.t.
223              
224             my $t = $report->timing_data; delete $t->{class}{'TestsFor::Basic::Subclass'};
225             delete
226             $t->{class}{'TestsFor::Basic'}{instance}{'TestsFor::Basic'}{method}{test_reporting};
227             delete
228             $t->{class}{'TestsFor::Basic'}{instance}{'TestsFor::Basic'}{method}{test_this_baby};
229             use Devel::Dwarn; Dwarn _fudge($t);
230              
231             sub _fudge { my $t = shift;
232              
233             use Data::Visitor::Callback;
234              
235             Data::Visitor::Callback->new(
236             hash => sub {
237             shift;
238             my $h = shift;
239              
240             for my $k ( grep { exists $h->{$_} } qw( real system user ) ) {
241             if ($h->{$k} ) {
242             $h->{$k} *= 10_000;
243             }
244             else {
245             $h->{$k} = $h->{real} * ($k eq 'system' ? 0.15 : 0.85);
246             }
247             }
248              
249             return $h;
250             },
251             )->visit($t);
252              
253             return $t;
254             }
255              
256             =end comment
257              
258             =head1 METHODS
259              
260             The top level report object for the whole test suite is returned from the
261             L<Test::Class::Moose::Runner> object's C<test_report> method.
262              
263             This object provides the following methods:
264              
265             =head2 C<all_test_classes>
266              
267             Returns an array of L<Test::Class::Moose::Report::Class> objects.
268              
269             =head2 C<num_test_classes>
270              
271             Integer. The number of test classes run.
272              
273             =head2 C<num_test_instances>
274              
275             Integer. The number of test instances run.
276              
277             =head2 C<num_test_methods>
278              
279             Integer. The number of test methods that the runner tried to run.
280              
281             =head2 C<num_tests_run>
282              
283             Integer. The number of tests run.
284              
285             =head2 C<current_class>
286              
287             Returns the L<Test::Class::Moose::Report::Class> for the test class currently
288             being run, if it exists. This may return C<undef>.
289              
290             =head2 C<current_instance>
291              
292             Returns the L<Test::Class::Moose::Report::Instance> for the test class instance
293             currently being run, if it exists. This may return C<undef>.
294              
295             =head2 C<current_method>
296              
297             Returns the L<Test::Class::Moose::Report::Method> for the test method currently
298             being run, if one exists. This may return C<undef>.
299              
300             =head2 C<time>
301              
302             Returns a L<Test::Class::Moose::Report::Time> object. This object represents
303             the duration of the entire test suite.
304              
305             =head2 C<timing_data>
306              
307             Returns a complex nested hashref containing timing data for the entire test
308             run. This is primarily intended for serialization or shipping the data to code
309             in other languages. If you want to analyze timing data from the same process as
310             the test report, you might as well just use the Perl API.
311              
312             See L</TIMING DATA STRUCTURE> for an example of the full structure.
313              
314             At the top level of the data structure are two keys, C<time> and C<class>. The
315             C<time> key is replicated through different levels of the structure. It always
316             contains three keys:
317              
318             { real => 1.0001,
319             system => 0.94,
320             user => 0.1,
321             }
322              
323             The C<class> key in turn contains a hashref keyed by class names. For each
324             class, there is a C<time> key and an C<instance> key.
325              
326             The C<instance> key contains a hashref keyed on instance names. For each
327             instance, there is a hashref with C<time>, C<control>, and C<method> keys.
328              
329             The C<control> key contains a hashref keyed on the control method names,
330             C<test_startup> and C<test_shutdown>. Each of those keys contains a hashref
331             containing C<time> key.
332              
333             The C<method> keys are the names of the methods that were run for that test
334             instance. Each of those keys is in turn a hashref containing C<control> and
335             C<time> keys. The C<control> key contains a hashref keyed on the control method
336             names, C<test_setup> and C<test_teardown>.
337              
338             =head1 TRUSTED METHODS
339              
340             The following L<Test::Class::Moose::Report> methods are for internal use only
341             and are called by L<Test::Class::Moose>. They are included here for those who
342             might want to hack on L<Test::Class::Moose>.
343              
344             =head2 C<_inc_test_methods>
345              
346             $statistics->_inc_test_methods; # increments by 1
347             $statistics->_inc_test_methods($x); # increments by $x
348              
349             =head2 C<_inc_tests>
350              
351             $statistics->_inc_tests; # increments by 1
352             $statistics->_inc_tests($x); # increments by $x
353              
354             =head1 TIMING DATA STRUCTURE
355              
356             Here's an example of what the entire timing data structure looks like:
357              
358             { time => {
359             real => "90.2795791625977",
360             system => "13.5419368743896",
361             user => 100
362             },
363             class => {
364             "TestsFor::Basic" => {
365             time => {
366             real => "37.7511978149414",
367             system => "5.66267967224121",
368             user => "32.0885181427002"
369             },
370             instance => {
371             "TestsFor::Basic" => {
372             time => {
373             real => "27.4395942687988",
374             system => "4.11593914031982",
375             user => "23.323655128479"
376             },
377             control => {
378             test_shutdown => {
379             time => {
380             real => "0.240802764892578",
381             system => "0.0361204147338867",
382             user => "0.204682350158691"
383             },
384             },
385             test_startup => {
386             time => {
387             real => "0.360012054443359",
388             system => "0.0540018081665039",
389             user => "0.306010246276855"
390             },
391             },
392             },
393             method => {
394             test_me => {
395             time => {
396             real => "4.6992301940918",
397             system => "0.70488452911377",
398             user => "3.99434566497803"
399             },
400             control => {
401             test_setup => {
402             time => {
403             real => "0.510215759277344",
404             system => "0.0765323638916016",
405             user => "0.433683395385742"
406             },
407             },
408             test_teardown => {
409             time => {
410             real => "0.269412994384766",
411             system => "0.0404119491577148",
412             user => "0.229001045227051"
413             },
414             },
415             },
416             },
417             },
418             },
419             },
420             },
421             },
422             }
423              
424             =head1 SUPPORT
425              
426             Bugs may be submitted at L<https://github.com/Test-More/test-class-moose/issues>.
427              
428             =head1 SOURCE
429              
430             The source code repository for Test-Class-Moose can be found at L<https://github.com/Test-More/test-class-moose>.
431              
432             =head1 AUTHORS
433              
434             =over 4
435              
436             =item *
437              
438             Curtis "Ovid" Poe <ovid@cpan.org>
439              
440             =item *
441              
442             Dave Rolsky <autarch@urth.org>
443              
444             =item *
445              
446             Chad Granum <exodist@cpan.org>
447              
448             =back
449              
450             =head1 COPYRIGHT AND LICENSE
451              
452             This software is copyright (c) 2012 - 2025 by Curtis "Ovid" Poe.
453              
454             This is free software; you can redistribute it and/or modify it under
455             the same terms as the Perl 5 programming language system itself.
456              
457             The full text of the license can be found in the
458             F<LICENSE> file included with this distribution.
459              
460             =cut