File Coverage

blib/lib/Test/Class/Moose/Report.pm
Criterion Covered Total %
statement 60 60 100.0
branch 5 8 62.5
condition n/a
subroutine 15 15 100.0
pod 8 9 88.8
total 88 92 95.6


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