File Coverage

blib/lib/Test/Class/Moose/Role/Executor.pm
Criterion Covered Total %
statement 242 258 93.8
branch 77 88 87.5
condition 5 6 83.3
subroutine 39 39 100.0
pod 6 6 100.0
total 369 397 92.9


line stmt bran cond sub pod time code
1             package Test::Class::Moose::Role::Executor;
2              
3             # ABSTRACT: Common code for Runner classes
4              
5 29     29   19927 use 5.010000;
  29         117  
6              
7             our $VERSION = '0.97';
8              
9 29     29   162 use Moose::Role 2.0000;
  29         493  
  29         186  
10 29     29   132964 use Carp;
  29         92  
  29         1452  
11 29     29   175 use namespace::autoclean;
  29         59  
  29         192  
12              
13 29     29   3042 use List::SomeUtils qw(uniq);
  29         11173  
  29         1506  
14 29     29   157 use List::Util qw(shuffle);
  29         61  
  29         1769  
15 29     29   183 use Test2::API qw( test2_stack );
  29         67  
  29         1763  
16 29     29   13110 use Test2::Tools::AsyncSubtest qw( async_subtest );
  29         495978  
  29         1614  
17 29     29   1057 use Test::Class::Moose::AttributeRegistry;
  29         62  
  29         605  
18 29     29   264 use Test::Class::Moose::Config;
  29         74  
  29         666  
19 29     29   8342 use Test::Class::Moose::Report::Class;
  29         76  
  29         876  
20 29     29   14820 use Test::Class::Moose::Report::Instance;
  29         116  
  29         1275  
21 29     29   16888 use Test::Class::Moose::Report::Method;
  29         150  
  29         1346  
22 29     29   17879 use Test::Class::Moose::Report;
  29         133  
  29         1315  
23 29     29   16420 use Test::Class::Moose::Util qw( context_do );
  29         83  
  29         1514  
24 29     29   185 use Try::Tiny;
  29         62  
  29         71550  
25              
26             has 'test_configuration' => (
27             is => 'ro',
28             isa => 'Test::Class::Moose::Config',
29             required => 1,
30             );
31              
32             has 'test_report' => (
33             is => 'ro',
34             isa => 'Test::Class::Moose::Report',
35             builder => '_build_test_report',
36             );
37              
38             has 'is_parallel' => (
39             is => 'ro',
40             isa => 'Bool',
41             default => sub { ( ref $_[0] ) =~ /::Parallel$/ ? 1 : 0 },
42             );
43              
44             sub runtests {
45 30     30 1 385 my $self = shift;
46              
47 30         897 my $report = $self->test_report;
48 30         899 $report->_start_benchmark;
49 30         133 my @test_classes = $self->test_classes;
50              
51 30         166 $self->_validate_test_classes(@test_classes);
52              
53             context_do {
54 28     28   69 my $ctx = shift;
55              
56 28         205 $ctx->plan( scalar @test_classes );
57              
58 28         10009 $self->_run_test_classes(@test_classes);
59              
60 18 50       26824 $ctx->diag(<<"END") if $self->test_configuration->statistics;
61 0         0 Test classes: @{[ $report->num_test_classes ]}
62 0         0 Test instances: @{[ $report->num_test_instances ]}
63 0         0 Test methods: @{[ $report->num_test_methods ]}
64 0         0 Total tests run: @{[ $report->num_tests_run ]}
65             END
66              
67 18         96 $ctx->done_testing;
68 28         332 };
69              
70 18         928 $report->_end_benchmark;
71 18         116 return $self;
72             }
73              
74             sub _validate_test_classes {
75 30     30   71 my $self = shift;
76              
77 30 100       70 my @bad = grep { !$_->isa('Test::Class::Moose') } @_
  157         512  
78             or return;
79              
80 2         4 my $msg = 'Found the following class';
81 2 100       11 $msg .= 'es' if @bad > 1;
82 2 100       14 $msg
    100          
83             .= ' that '
84             . ( @bad > 1 ? 'are' : 'is' ) . ' not '
85             . ( @bad > 1 ? 'subclasses' : 'a subclass' )
86             . " of Test::Class::Moose: @bad";
87 2 100       6 $msg .= ' (did you load '
88             . ( @bad > 1 ? 'these classes' : 'this class' ) . '?)';
89 2         21 die $msg;
90             }
91              
92             sub _run_test_classes {
93 18     18   45 my $self = shift;
94 18         67 my @test_classes = @_;
95              
96 18         57 for my $test_class (@test_classes) {
97             async_subtest(
98             $test_class,
99             { manual_skip_all => 1 },
100 34     34   18768 sub { $self->run_test_class($test_class) }
101 34         25145 )->finish;
102             }
103             }
104              
105 34     34   11227 sub _build_test_report { Test::Class::Moose::Report->new }
106              
107             sub run_test_class {
108 44     44 1 227 my $self = shift;
109 44         296 my $test_class = shift;
110              
111 44         3604 my $class_report
112             = Test::Class::Moose::Report::Class->new( name => $test_class );
113              
114 44         1629 $self->test_report->add_test_class($class_report);
115              
116 44         1719 $class_report->_start_benchmark;
117              
118 44         262 my $passed = $self->_run_test_instances( $test_class, $class_report );
119              
120 44         2021 $class_report->passed($passed);
121              
122 44         1396 $class_report->_end_benchmark;
123              
124 44         163 return $class_report;
125             }
126              
127             sub _run_test_instances {
128 44     44   190 my $self = shift;
129 44         170 my $test_class = shift;
130 44         300 my $class_report = shift;
131              
132 44         1498 my @test_instances = $test_class->_tcm_make_test_class_instances(
133             test_report => $self->test_report,
134             );
135              
136 44 100       240 unless (@test_instances) {
137             context_do {
138 2     2   14 my $ctx = shift;
139              
140 2         9 my $message = "Skipping '$test_class': no test instances found";
141 2         92 $class_report->skipped($message);
142 2         93 $class_report->passed(1);
143 2         37 $ctx->plan( 0, 'SKIP' => $message );
144 2         78 };
145 2         40 return 1;
146             }
147              
148             return context_do {
149 42     42   252 my $ctx = shift;
150              
151 42 100       265 $ctx->plan( scalar @test_instances )
152             if @test_instances > 1;
153              
154 42         1785 my $passed = 1;
155 42         203 for my $test_instance (
156 3         162 sort { $a->test_instance_name cmp $b->test_instance_name }
157             @test_instances )
158             {
159 45         267 my $instance_report = $self->_maybe_wrap_test_instance(
160             $test_instance,
161             $class_report,
162             @test_instances > 1,
163             );
164 45 100       1538 $passed = 0 if not $instance_report->passed;
165             }
166              
167 42         141 return $passed;
168 42         1027 };
169             }
170              
171             sub _maybe_wrap_test_instance {
172 45     45   206 my $self = shift;
173 45         98 my $test_instance = shift;
174 45         135 my $class_report = shift;
175 45         171 my $in_subtest = shift;
176              
177 45 100       254 return $self->run_test_instance(
178             $test_instance,
179             $class_report,
180             ) unless $in_subtest;
181              
182 6         13 my $instance_report;
183             async_subtest(
184             $test_instance->test_instance_name,
185             { manual_skip_all => 1 },
186             sub {
187 6     6   3218 $instance_report = $self->run_test_instance(
188             $test_instance,
189             $class_report,
190             );
191             },
192 6         151 )->finish;
193              
194 6         8347 return $instance_report;
195             }
196              
197             sub run_test_instance {
198 45     45 1 116 my ( $self, $test_instance, $class_report ) = @_;
199              
200 45         1726 my $test_instance_name = $test_instance->test_instance_name;
201 45         1909 my $instance_report = Test::Class::Moose::Report::Instance->new(
202             { name => $test_instance_name,
203             }
204             );
205              
206 45 100       1695 local $0 = "$0 - $test_instance_name"
207             if $self->test_configuration->set_process_name;
208              
209 45         1604 $instance_report->_start_benchmark;
210              
211 45         1795 $class_report->add_test_instance($instance_report);
212              
213 45         301 my @test_methods = $self->_test_methods_for($test_instance);
214              
215             context_do {
216 45     45   93 my $ctx = shift;
217              
218 45 50       180 unless (@test_methods) {
219              
220 0         0 my $message
221             = "Skipping '$test_instance_name': no test methods found";
222 0         0 $instance_report->skipped($message);
223 0         0 $instance_report->passed(1);
224 0         0 $ctx->plan( 0, SKIP => $message );
225 0         0 return;
226             }
227              
228 45         1504 my $report = $self->test_report;
229              
230 45 100       268 unless (
231             $self->run_test_control_method(
232             $test_instance, 'test_startup', $instance_report,
233             )
234             )
235             {
236 2         55 $instance_report->passed(0);
237 2         4 return;
238             }
239              
240 43 100       1258 if ( my $message = $test_instance->test_skip ) {
241              
242             # test_startup skipped the class
243 4         142 $instance_report->skipped($message);
244              
245 4 100       23 if ( $test_instance->run_control_methods_on_skip ) {
246 1 50       5 $self->_run_shutdown( $test_instance, $instance_report )
247             or return;
248             }
249              
250 4         150 $instance_report->passed(1);
251 4         48 $ctx->plan( 0, SKIP => $message );
252 4         1837 return;
253             }
254              
255 39         356 $ctx->plan( scalar @test_methods );
256              
257 39         16187 my $all_passed = 1;
258 39         117 foreach my $test_method (@test_methods) {
259 109         442 my $method_report = $self->run_test_method(
260             $test_instance,
261             $test_method,
262             $instance_report,
263             );
264 109 100       3029 $all_passed = 0 if not $method_report->passed;
265             }
266 39         1349 $instance_report->passed($all_passed);
267              
268 39         191 $self->_run_shutdown( $test_instance, $instance_report );
269              
270             # finalize reporting
271 39         1314 $instance_report->_end_benchmark;
272 39 50       1195 if ( $self->test_configuration->show_timing ) {
273 0         0 my $time = $instance_report->time->duration;
274 0         0 $ctx->diag("$test_instance_name: $time");
275             }
276 45         718 };
277              
278 45         787 return $instance_report;
279             }
280              
281             sub _run_shutdown {
282 40     40   123 my ( $self, $test_instance, $instance_report ) = @_;
283              
284 40 50       131 return 1
285             if $self->run_test_control_method(
286             $test_instance, 'test_shutdown', $instance_report,
287             );
288              
289 0         0 $instance_report->passed(0);
290              
291 0         0 return 0;
292             }
293              
294             sub _test_methods_for {
295 186     186   11876 my ( $self, $thing ) = @_;
296              
297 186         527 my @filtered = $self->_filtered_test_methods($thing);
298 186 50       5068 return uniq(
299             $self->test_configuration->randomize
300             ? shuffle(@filtered)
301             : sort @filtered
302             );
303             }
304              
305             sub _filtered_test_methods {
306 186     186   388 my ( $self, $thing ) = @_;
307              
308 186         1783 my @method_list = $thing->test_methods;
309 186 100       6063 if ( my $include = $self->test_configuration->include ) {
310 4         9 @method_list = grep {/$include/} @method_list;
  18         52  
311             }
312 186 100       4843 if ( my $exclude = $self->test_configuration->exclude ) {
313 4         10 @method_list = grep { !/$exclude/ } @method_list;
  18         53  
314             }
315              
316 186 100       2308 my $test_class = ref $thing ? $thing->test_class : $thing;
317 186         1217 return $self->_filter_by_tag(
318             $test_class,
319             \@method_list
320             );
321             }
322              
323             sub _filter_by_tag {
324 186     186   481 my ( $self, $class, $methods ) = @_;
325              
326 186         449 my @filtered_methods = @$methods;
327 186 100       5346 if ( my $include = $self->test_configuration->include_tags ) {
328 12         16 my @new_method_list;
329 12         25 foreach my $method (@filtered_methods) {
330 57         76 foreach my $tag (@$include) {
331 76 100       146 if (Test::Class::Moose::AttributeRegistry->method_has_tag(
332             $class, $method, $tag
333             )
334             )
335             {
336 18         34 push @new_method_list => $method;
337             }
338             }
339             }
340 12         28 @filtered_methods = @new_method_list;
341             }
342 186 100       4984 if ( my $exclude = $self->test_configuration->exclude_tags ) {
343 8         16 my @new_method_list = @filtered_methods;
344 8         19 foreach my $method (@filtered_methods) {
345 22         30 foreach my $tag (@$exclude) {
346 25 100       52 if (Test::Class::Moose::AttributeRegistry->method_has_tag(
347             $class, $method, $tag
348             )
349             )
350             {
351             @new_method_list
352 8         13 = grep { $_ ne $method } @new_method_list;
  36         64  
353             }
354             }
355             }
356 8         17 @filtered_methods = @new_method_list;
357             }
358 186         895 return @filtered_methods;
359             }
360              
361             my %TEST_CONTROL_METHODS = map { $_ => 1 } qw/
362             test_startup
363             test_setup
364             test_teardown
365             test_shutdown
366             /;
367              
368             sub run_test_control_method {
369 301     301 1 791 my ( $self, $test_instance, $phase, $report_object ) = @_;
370              
371 301 100       8447 local $0 = "$0 - $phase"
372             if $self->test_configuration->set_process_name;
373              
374 301 50       831 $TEST_CONTROL_METHODS{$phase}
375             or croak("Unknown test control method ($phase)");
376              
377 301 100       7199 my %report_args = (
378             name => $phase,
379             instance => (
380             $report_object->isa('Test::Class::Moose::Report::Method')
381             ? $report_object->instance
382             : $report_object
383             )
384             );
385 301         8661 my $phase_method_report
386             = Test::Class::Moose::Report::Method->new( \%report_args );
387              
388 301         621 my $set_meth = "set_${phase}_method";
389 301         11083 $report_object->$set_meth($phase_method_report);
390              
391             # It'd be nicer to start and end immediately after we call
392             # $test_instance->$phase but we can't guarantee that those calls would
393             # happen inside the try block.
394 301         8891 $phase_method_report->_start_benchmark;
395              
396             my $success = context_do {
397 301     301   532 my $ctx = shift;
398              
399             return try {
400 301         13757 my $count = $ctx->hub->count;
401 301         3081 $test_instance->$phase($report_object);
402 300 100       5389 croak "Tests may not be run in test control methods ($phase)"
403             unless $count == $ctx->hub->count;
404 299         1865 1;
405             }
406             catch {
407 2         232 my $error = $_;
408 2         57 my $class = $test_instance->test_class;
409 2         13 $ctx->ok( 0, "$class->$phase failed", [$error] );
410 2         1032 0;
411 301         2467 };
412 301         2122 };
413              
414 301         11908 $phase_method_report->_end_benchmark;
415              
416 301         1140 return $success;
417             }
418              
419             sub run_test_method {
420 109     109 1 317 my ( $self, $test_instance, $test_method, $instance_report ) = @_;
421              
422 109 100       3326 local $0 = "$0 - $test_method"
423             if $self->test_configuration->set_process_name;
424              
425 109         3029 my $method_report = Test::Class::Moose::Report::Method->new(
426             { name => $test_method, instance => $instance_report } );
427              
428 109         4276 $instance_report->add_test_method($method_report);
429              
430 109         3323 $test_instance->test_skip_clear;
431 109         345 $self->run_test_control_method(
432             $test_instance,
433             'test_setup',
434             $method_report,
435             );
436              
437 109         3141 $method_report->_start_benchmark;
438              
439 109         171 my $num_tests = 0;
440 109         2592 my $test_class = $test_instance->test_class;
441              
442             context_do {
443 109     109   173 my $ctx = shift;
444              
445 109         207 my $skipped;
446              
447             # If the call to ->$test_method fails then this subtest will fail and
448             # Test2::API will also include a diagnostic message with the error.
449             my $p = async_subtest(
450             $test_method,
451             { manual_skip_all => 1 },
452             sub {
453 109         53385 my $hub = test2_stack()->top;
454 109 100       4118 if ( my $message = $test_instance->test_skip ) {
455 3         99 $method_report->skipped($message);
456              
457             # I can't figure out how to get our current context in
458             # order to call $ctx->plan instead.
459             context_do {
460 3         50 shift->plan( 0, SKIP => $message );
461 3         16 };
462 3         26 $skipped = 1;
463 3         10 return 1;
464             }
465              
466 106         959 $test_instance->$test_method($method_report);
467 101         2080148 $num_tests = $hub->count;
468             },
469 109         998 )->finish;
470              
471 109         161918 $method_report->_end_benchmark;
472 109 50       3312 if ( $self->test_configuration->show_timing ) {
473 0         0 my $time = $method_report->time->duration;
474 0         0 $ctx->diag( $method_report->name . ": $time" );
475             }
476              
477             # $p will be undef if the tests failed but we want to stick to 0
478             # or 1.
479 109 100       3256 $method_report->passed( $p ? 1 : 0 );
480              
481 109 100 100     317 if ( !$skipped || $test_instance->run_control_methods_on_skip ) {
482 107 50       308 $self->run_test_control_method(
483             $test_instance,
484             'test_teardown',
485             $method_report,
486             ) or $method_report->passed(0);
487             }
488              
489 109         254 return $p;
490 109         910 };
491              
492 109 100 66     4570 return $method_report unless $num_tests && !$method_report->is_skipped;
493              
494 101         3032 $method_report->num_tests_run($num_tests);
495 101 100       3322 $method_report->tests_planned($num_tests)
496             unless $method_report->has_plan;
497              
498 101         282 return $method_report;
499             }
500              
501             sub test_classes {
502 38     38 1 177 my $self = shift;
503              
504 38 100       1195 if ( my $classes = $self->test_configuration->test_classes ) {
505 5 50       16 return @{$classes} if @{$classes};
  5         25  
  5         23  
506             }
507              
508 33         188 my %metaclasses = Class::MOP::get_all_metaclasses();
509 33         3856 my @classes;
510 33         467 foreach my $class ( keys %metaclasses ) {
511 3520 100       5534 next if $class eq 'Test::Class::Moose';
512 3487 100       22364 push @classes => $class if $class->isa('Test::Class::Moose');
513             }
514              
515 33 50       1340 if ( $self->test_configuration->randomize_classes ) {
516 0         0 return shuffle(@classes);
517             }
518 33         2322 return sort @classes;
519             }
520              
521             1;
522              
523             __END__
524              
525             =pod
526              
527             =encoding UTF-8
528              
529             =head1 NAME
530              
531             Test::Class::Moose::Role::Executor - Common code for Runner classes
532              
533             =head1 VERSION
534              
535             version 0.97
536              
537             =head1 DESCRIPTION
538              
539             This role implements the guts of this distribution, running all of your test
540             classes. It's public API can be wrapped by additional roles to provide
541             extensions to the default TCM behavior.
542              
543             The actual implementations are provided by
544             C<Test::Class::Moose::Executor::Sequential> and
545             C<Test::Class::Moose::Executor::Parallel>.
546              
547             =head1 API
548              
549             This role provides the following public methods for extensions. If you wrap
550             any of the methods related to test execution you are strongly encouraged to
551             make sure that the original method is called, as these methods implement the
552             core functionality of TCM.
553              
554             =head2 $executor->is_parallel
555              
556             This returns a boolean indicating whether or not this executor will run test
557             classes in parallel or not.
558              
559             =head2 $executor->test_configuration
560              
561             Returns the L<Test::Class::Moose::Config> object for this executor.
562              
563             =head2 $executor->test_report
564              
565             Returns the L<Test::Class::Moose::Report> object for this executor.
566              
567             =head2 $executor->test_classes
568              
569             Returns the list of test classes to be run, in the order that they should be
570             run.
571              
572             =head2 $executor->runtests
573              
574             This is the primary entry method for test executor. It is called without any
575             arguments and is expected to run all of the test classes defined in the test
576             configuration.
577              
578             =head2 $executor->run_test_class($test_class)
579              
580             This method is called once for each test class to be run. It is passed a
581             single argument, the I<name> of the test class to be run.
582              
583             =head2 $executor->run_test_instance($test_instance, $class_report)
584              
585             This method is called once for each instance of a test class to be run. For
586             most classes this is just called once but for classes which consume the
587             L<Test::Class::Moose::Role::Parameterized> role, it will be called more than
588             once.
589              
590             The first argument is the test class object to be run, and the second is an
591             instance of L<Test::Class::Moose::Report::Class> for the class being run.
592              
593             =head2 $executor->run_test_method($test_instance, $test_method, $instance_report)
594              
595             This method is called once for each test method in an instance to be run.
596              
597             The first argument is the test class object to be run, the second is a method
598             name, and the third is an instance of L<Test::Class::Moose::Report::Instance>
599             for the instance being run.
600              
601             =head2 $executor->run_test_control_method($test_instance, $control_method, $instance_report)
602              
603             This method is called once for each test method in an instance to be run.
604              
605             The first argument is the test class object to be run, the second is a control
606             method name (like C<'test_startup'>), and the third is an instance of
607             L<Test::Class::Moose::Report::Instance> for the instance being run.
608              
609             =head1 SUPPORT
610              
611             Bugs may be submitted at L<https://github.com/houseabsolute/test-class-moose/issues>.
612              
613             I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
614              
615             =head1 SOURCE
616              
617             The source code repository for Test-Class-Moose can be found at L<https://github.com/houseabsolute/test-class-moose>.
618              
619             =head1 AUTHORS
620              
621             =over 4
622              
623             =item *
624              
625             Curtis "Ovid" Poe <ovid@cpan.org>
626              
627             =item *
628              
629             Dave Rolsky <autarch@urth.org>
630              
631             =back
632              
633             =head1 COPYRIGHT AND LICENSE
634              
635             This software is copyright (c) 2012 - 2019 by Curtis "Ovid" Poe.
636              
637             This is free software; you can redistribute it and/or modify it under
638             the same terms as the Perl 5 programming language system itself.
639              
640             The full text of the license can be found in the
641             F<LICENSE> file included with this distribution.
642              
643             =cut