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   263 use strict;
  30         85  
  30         1205  
6 30     30   195 use warnings;
  30         71  
  30         1124  
7 30     30   189 use namespace::autoclean;
  30         91  
  30         341  
8              
9 30     30   3173 use 5.010000;
  30         135  
10              
11             our $VERSION = '0.99';
12              
13 30     30   206 use Carp;
  30         88  
  30         2342  
14 30     30   225 use Moose;
  30         75  
  30         261  
15             with 'Test::Class::Moose::Role::HasTimeReport';
16              
17 30     30   163160 use List::Util qw( first sum0 );
  30         85  
  30         29238  
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 3776 my $self = shift;
33 8         43 return sum0 map { $_->num_test_instances } $self->all_test_classes;
  24         1017  
34             }
35              
36             sub num_test_methods {
37 8     8 1 3537 my $self = shift;
38 54         1808 return scalar grep { !$_->is_skipped }
39 24         973 map { $_->all_test_methods }
40 8         36 map { $_->all_test_instances } $self->all_test_classes;
  24         963  
41             }
42              
43             sub num_tests_run {
44 9     9 1 8408 my $self = shift;
45 51         1607 return sum0 map { $_->num_tests_run }
46 54         1763 grep { !$_->is_skipped }
47 25         1008 map { $_->all_test_methods }
48 9         57 map { $_->all_test_instances } $self->all_test_classes;
  25         1070  
49             }
50              
51             sub all_test_classes {
52 39     39 1 205103 my $self = shift;
53 39         2029 return $self->_all_test_classes;
54             }
55              
56             sub current_class {
57 153     153 1 320 my $self = shift;
58 153         4995 return $self->test_classes->[-1];
59             }
60              
61             sub current_instance {
62 153     153 1 666 my $self = shift;
63 153 50       412 my $current_class = $self->current_class or return;
64 153         743 return $current_class->current_instance;
65             }
66              
67             sub current_method {
68 87     87 1 216 my $self = shift;
69 87 50       283 my $current_instance = $self->current_instance or return;
70 87         500 return $current_instance->current_method;
71             }
72              
73             sub plan {
74 13     13 0 41 my ( $self, $plan ) = @_;
75 13 50       40 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         77 $current_method->plan($plan);
78             }
79              
80             sub timing_data {
81 6     6 1 98 my $self = shift;
82              
83 6         222 my %t = ( time => $self->time->as_hashref );
84              
85 6         42 for my $class ( $self->all_test_classes ) {
86 20         719 my $class_inner = $t{class}{ $class->name }
87             = { time => $class->time->as_hashref };
88              
89 20         886 for my $instance ( $class->all_test_instances ) {
90 20         757 my $instance_inner = $class_inner->{instance}{ $instance->name }
91             = { time => $instance->time->as_hashref };
92              
93 20         99 $self->_populate_control_timing_data(
94             $instance_inner, $instance,
95             qw( test_startup test_shutdown )
96             );
97              
98 20         869 for my $method ( $instance->all_test_methods ) {
99 45         1566 my $method_inner = $instance_inner->{method}{ $method->name }
100             = { time => $method->time->as_hashref };
101              
102 45         165 $self->_populate_control_timing_data(
103             $method_inner, $method,
104             qw( test_setup test_teardown ),
105             );
106             }
107             }
108             }
109              
110 6         61 return \%t;
111             }
112              
113             sub _populate_control_timing_data {
114 65     65   124 my $self = shift;
115 65         104 my $hashref = shift;
116 65         121 my $report = shift;
117              
118 65         164 for my $control (@_) {
119 130 100       229 my $control_method = $report->${ \( $control . '_method' ) }
  130         4872  
120             or next;
121             $hashref->{control}{$control}{time}
122 126         3896 = $control_method->time->as_hashref;
123             }
124              
125 65         180 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 0.99
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 (@c) {
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/houseabsolute/test-class-moose/issues>.
427              
428             I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
429              
430             =head1 SOURCE
431              
432             The source code repository for Test-Class-Moose can be found at L<https://github.com/houseabsolute/test-class-moose>.
433              
434             =head1 AUTHORS
435              
436             =over 4
437              
438             =item *
439              
440             Curtis "Ovid" Poe <ovid@cpan.org>
441              
442             =item *
443              
444             Dave Rolsky <autarch@urth.org>
445              
446             =back
447              
448             =head1 COPYRIGHT AND LICENSE
449              
450             This software is copyright (c) 2012 - 2021 by Curtis "Ovid" Poe.
451              
452             This is free software; you can redistribute it and/or modify it under
453             the same terms as the Perl 5 programming language system itself.
454              
455             The full text of the license can be found in the
456             F<LICENSE> file included with this distribution.
457              
458             =cut