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 30     30   20185 use 5.010000;
  30         96  
6              
7             our $VERSION = '0.98';
8              
9 30     30   157 use Moose::Role 2.0000;
  30         493  
  30         189  
10 30     30   139498 use Carp;
  30         63  
  30         1608  
11 30     30   181 use namespace::autoclean;
  30         58  
  30         171  
12              
13 30     30   2750 use List::SomeUtils qw(uniq);
  30         10757  
  30         1540  
14 30     30   176 use List::Util qw(shuffle);
  30         58  
  30         1560  
15 30     30   156 use Test2::API qw( test2_stack );
  30         62  
  30         1504  
16 30     30   13011 use Test2::Tools::AsyncSubtest qw( async_subtest );
  30         567100  
  30         1619  
17 30     30   1054 use Test::Class::Moose::AttributeRegistry;
  30         79  
  30         619  
18 30     30   215 use Test::Class::Moose::Config;
  30         50  
  30         694  
19 30     30   9212 use Test::Class::Moose::Report::Class;
  30         91  
  30         883  
20 30     30   14917 use Test::Class::Moose::Report::Instance;
  30         117  
  30         1311  
21 30     30   16189 use Test::Class::Moose::Report::Method;
  30         140  
  30         1316  
22 30     30   17536 use Test::Class::Moose::Report;
  30         161  
  30         1260  
23 30     30   15884 use Test::Class::Moose::Util qw( context_do );
  30         77  
  30         1517  
24 30     30   200 use Try::Tiny;
  30         60  
  30         77244  
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 31     31 1 400 my $self = shift;
46              
47 31         909 my $report = $self->test_report;
48 31         933 $report->_start_benchmark;
49 31         140 my @test_classes = $self->test_classes;
50              
51 31         171 $self->_validate_test_classes(@test_classes);
52              
53             context_do {
54 29     29   72 my $ctx = shift;
55              
56 29         196 $ctx->plan( scalar @test_classes );
57              
58 29         10186 $self->_run_test_classes(@test_classes);
59              
60 19 50       31436 $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 19         107 $ctx->done_testing;
68 29         353 };
69              
70 19         922 $report->_end_benchmark;
71 19         137 return $self;
72             }
73              
74             sub _validate_test_classes {
75 31     31   63 my $self = shift;
76              
77 31 100       85 my @bad = grep { !$_->isa('Test::Class::Moose') } @_
  158         499  
78             or return;
79              
80 2         4 my $msg = 'Found the following class';
81 2 100       6 $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       8 $msg .= ' (did you load '
88             . ( @bad > 1 ? 'these classes' : 'this class' ) . '?)';
89 2         19 die $msg;
90             }
91              
92             sub _run_test_classes {
93 19     19   49 my $self = shift;
94 19         56 my @test_classes = @_;
95              
96 19         51 for my $test_class (@test_classes) {
97             async_subtest(
98             $test_class,
99             { manual_skip_all => 1 },
100 35     35   22800 sub { $self->run_test_class($test_class) }
101 35         26071 )->finish;
102             }
103             }
104              
105 35     35   12712 sub _build_test_report { Test::Class::Moose::Report->new }
106              
107             sub run_test_class {
108 45     45 1 295 my $self = shift;
109 45         246 my $test_class = shift;
110              
111 45         3706 my $class_report
112             = Test::Class::Moose::Report::Class->new( name => $test_class );
113              
114 45         2472 $self->test_report->add_test_class($class_report);
115              
116 45         1870 $class_report->_start_benchmark;
117              
118 45         228 my $passed = $self->_run_test_instances( $test_class, $class_report );
119              
120 45         2449 $class_report->passed($passed);
121              
122 45         1854 $class_report->_end_benchmark;
123              
124 45         203 return $class_report;
125             }
126              
127             sub _run_test_instances {
128 45     45   182 my $self = shift;
129 45         89 my $test_class = shift;
130 45         379 my $class_report = shift;
131              
132 45         1592 my @test_instances = $test_class->_tcm_make_test_class_instances(
133             test_report => $self->test_report,
134             );
135              
136 45 100       271 unless (@test_instances) {
137             context_do {
138 2     2   17 my $ctx = shift;
139              
140 2         10 my $message = "Skipping '$test_class': no test instances found";
141 2         92 $class_report->skipped($message);
142 2         124 $class_report->passed(1);
143 2         34 $ctx->plan( 0, 'SKIP' => $message );
144 2         70 };
145 2         51 return 1;
146             }
147              
148             return context_do {
149 43     43   336 my $ctx = shift;
150              
151 43 100       256 $ctx->plan( scalar @test_instances )
152             if @test_instances > 1;
153              
154 43         2118 my $passed = 1;
155 43         158 for my $test_instance (
156 3         143 sort { $a->test_instance_name cmp $b->test_instance_name }
157             @test_instances )
158             {
159 46         293 my $instance_report = $self->_maybe_wrap_test_instance(
160             $test_instance,
161             $class_report,
162             @test_instances > 1,
163             );
164 46 100       1944 $passed = 0 if not $instance_report->passed;
165             }
166              
167 43         185 return $passed;
168 43         1091 };
169             }
170              
171             sub _maybe_wrap_test_instance {
172 46     46   397 my $self = shift;
173 46         89 my $test_instance = shift;
174 46         267 my $class_report = shift;
175 46         178 my $in_subtest = shift;
176              
177 46 100       432 return $self->run_test_instance(
178             $test_instance,
179             $class_report,
180             ) unless $in_subtest;
181              
182 6         9 my $instance_report;
183             async_subtest(
184             $test_instance->test_instance_name,
185             { manual_skip_all => 1 },
186             sub {
187 6     6   5645 $instance_report = $self->run_test_instance(
188             $test_instance,
189             $class_report,
190             );
191             },
192 6         248 )->finish;
193              
194 6         16180 return $instance_report;
195             }
196              
197             sub run_test_instance {
198 46     46 1 145 my ( $self, $test_instance, $class_report ) = @_;
199              
200 46         1772 my $test_instance_name = $test_instance->test_instance_name;
201 46         2216 my $instance_report = Test::Class::Moose::Report::Instance->new(
202             { name => $test_instance_name,
203             }
204             );
205              
206 46 100       1712 local $0 = "$0 - $test_instance_name"
207             if $self->test_configuration->set_process_name;
208              
209 46         1570 $instance_report->_start_benchmark;
210              
211 46         2381 $class_report->add_test_instance($instance_report);
212              
213 46         466 my @test_methods = $self->_test_methods_for($test_instance);
214              
215             context_do {
216 46     46   162 my $ctx = shift;
217              
218 46 50       197 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 46         1753 my $report = $self->test_report;
229              
230 46 100       606 unless (
231             $self->run_test_control_method(
232             $test_instance, 'test_startup', $instance_report,
233             )
234             )
235             {
236 2         53 $instance_report->passed(0);
237 2         6 return;
238             }
239              
240 44 100       1593 if ( my $message = $test_instance->test_skip ) {
241              
242             # test_startup skipped the class
243 4         165 $instance_report->skipped($message);
244              
245 4 100       25 if ( $test_instance->run_control_methods_on_skip ) {
246 1 50       6 $self->_run_shutdown( $test_instance, $instance_report )
247             or return;
248             }
249              
250 4         146 $instance_report->passed(1);
251 4         36 $ctx->plan( 0, SKIP => $message );
252 4         2028 return;
253             }
254              
255 40         402 $ctx->plan( scalar @test_methods );
256              
257 40         38889 my $all_passed = 1;
258 40         147 foreach my $test_method (@test_methods) {
259 110         622 my $method_report = $self->run_test_method(
260             $test_instance,
261             $test_method,
262             $instance_report,
263             );
264 110 100       3039 $all_passed = 0 if not $method_report->passed;
265             }
266 40         1502 $instance_report->passed($all_passed);
267              
268 40         211 $self->_run_shutdown( $test_instance, $instance_report );
269              
270             # finalize reporting
271 40         1370 $instance_report->_end_benchmark;
272 40 50       1361 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 46         1120 };
277              
278 46         957 return $instance_report;
279             }
280              
281             sub _run_shutdown {
282 41     41   152 my ( $self, $test_instance, $instance_report ) = @_;
283              
284 41 50       122 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 187     187   12698 my ( $self, $thing ) = @_;
296              
297 187         649 my @filtered = $self->_filtered_test_methods($thing);
298 187 50       5104 return uniq(
299             $self->test_configuration->randomize
300             ? shuffle(@filtered)
301             : sort @filtered
302             );
303             }
304              
305             sub _filtered_test_methods {
306 187     187   370 my ( $self, $thing ) = @_;
307              
308 187         1307 my @method_list = $thing->test_methods;
309 187 100       6189 if ( my $include = $self->test_configuration->include ) {
310 4         9 @method_list = grep {/$include/} @method_list;
  18         51  
311             }
312 187 100       5059 if ( my $exclude = $self->test_configuration->exclude ) {
313 4         8 @method_list = grep { !/$exclude/ } @method_list;
  18         65  
314             }
315              
316 187 100       2282 my $test_class = ref $thing ? $thing->test_class : $thing;
317 187         522 return $self->_filter_by_tag(
318             $test_class,
319             \@method_list
320             );
321             }
322              
323             sub _filter_by_tag {
324 187     187   482 my ( $self, $class, $methods ) = @_;
325              
326 187         468 my @filtered_methods = @$methods;
327 187 100       4995 if ( my $include = $self->test_configuration->include_tags ) {
328 12         13 my @new_method_list;
329 12         23 foreach my $method (@filtered_methods) {
330 57         67 foreach my $tag (@$include) {
331 76 100       145 if (Test::Class::Moose::AttributeRegistry->method_has_tag(
332             $class, $method, $tag
333             )
334             )
335             {
336 18         41 push @new_method_list => $method;
337             }
338             }
339             }
340 12         27 @filtered_methods = @new_method_list;
341             }
342 187 100       4884 if ( my $exclude = $self->test_configuration->exclude_tags ) {
343 8         18 my @new_method_list = @filtered_methods;
344 8         14 foreach my $method (@filtered_methods) {
345 22         32 foreach my $tag (@$exclude) {
346 25 100       45 if (Test::Class::Moose::AttributeRegistry->method_has_tag(
347             $class, $method, $tag
348             )
349             )
350             {
351             @new_method_list
352 8         14 = grep { $_ ne $method } @new_method_list;
  36         63  
353             }
354             }
355             }
356 8         15 @filtered_methods = @new_method_list;
357             }
358 187         762 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 305     305 1 830 my ( $self, $test_instance, $phase, $report_object ) = @_;
370              
371 305 100       9682 local $0 = "$0 - $phase"
372             if $self->test_configuration->set_process_name;
373              
374 305 50       1120 $TEST_CONTROL_METHODS{$phase}
375             or croak("Unknown test control method ($phase)");
376              
377 305 100       8198 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 305         13817 my $phase_method_report
386             = Test::Class::Moose::Report::Method->new( \%report_args );
387              
388 305         737 my $set_meth = "set_${phase}_method";
389 305         14278 $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 305         11043 $phase_method_report->_start_benchmark;
395              
396             my $success = context_do {
397 305     305   547 my $ctx = shift;
398              
399             return try {
400 305         30858 my $count = $ctx->hub->count;
401 305         3349 $test_instance->$phase($report_object);
402 304 100       5692 croak "Tests may not be run in test control methods ($phase)"
403             unless $count == $ctx->hub->count;
404 303         1985 1;
405             }
406             catch {
407 2         239 my $error = $_;
408 2         55 my $class = $test_instance->test_class;
409 2         13 $ctx->ok( 0, "$class->$phase failed", [$error] );
410 2         1047 0;
411 305         3560 };
412 305         2395 };
413              
414 305         17768 $phase_method_report->_end_benchmark;
415              
416 305         1160 return $success;
417             }
418              
419             sub run_test_method {
420 110     110 1 1245 my ( $self, $test_instance, $test_method, $instance_report ) = @_;
421              
422 110 100       6169 local $0 = "$0 - $test_method"
423             if $self->test_configuration->set_process_name;
424              
425 110         5901 my $method_report = Test::Class::Moose::Report::Method->new(
426             { name => $test_method, instance => $instance_report } );
427              
428 110         4367 $instance_report->add_test_method($method_report);
429              
430 110         4528 $test_instance->test_skip_clear;
431 110         391 $self->run_test_control_method(
432             $test_instance,
433             'test_setup',
434             $method_report,
435             );
436              
437 110         3371 $method_report->_start_benchmark;
438              
439 110         198 my $num_tests = 0;
440 110         2912 my $test_class = $test_instance->test_class;
441              
442             context_do {
443 110     110   205 my $ctx = shift;
444              
445 110         311 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 110         76322 my $hub = test2_stack()->top;
454 110 100       4227 if ( my $message = $test_instance->test_skip ) {
455 3         103 $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         151 shift->plan( 0, SKIP => $message );
461 3         30 };
462 3         26 $skipped = 1;
463 3         7 return 1;
464             }
465              
466 107         960 $test_instance->$test_method($method_report);
467 102         2104182 $num_tests = $hub->count;
468             },
469 110         1508 )->finish;
470              
471 110         215949 $method_report->_end_benchmark;
472 110 50       4270 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 110 100       3904 $method_report->passed( $p ? 1 : 0 );
480              
481 110 100 100     406 if ( !$skipped || $test_instance->run_control_methods_on_skip ) {
482 108 50       490 $self->run_test_control_method(
483             $test_instance,
484             'test_teardown',
485             $method_report,
486             ) or $method_report->passed(0);
487             }
488              
489 110         285 return $p;
490 110         914 };
491              
492 110 100 66     4672 return $method_report unless $num_tests && !$method_report->is_skipped;
493              
494 102         2940 $method_report->num_tests_run($num_tests);
495 102 100       3216 $method_report->tests_planned($num_tests)
496             unless $method_report->has_plan;
497              
498 102         282 return $method_report;
499             }
500              
501             sub test_classes {
502 39     39 1 173 my $self = shift;
503              
504 39 100       1161 if ( my $classes = $self->test_configuration->test_classes ) {
505 5 50       10 return @{$classes} if @{$classes};
  5         22  
  5         21  
506             }
507              
508 34         212 my %metaclasses = Class::MOP::get_all_metaclasses();
509 34         3776 my @classes;
510 34         397 foreach my $class ( keys %metaclasses ) {
511 3618 100       5565 next if $class eq 'Test::Class::Moose';
512 3584 100       21416 push @classes => $class if $class->isa('Test::Class::Moose');
513             }
514              
515 34 50       1408 if ( $self->test_configuration->randomize_classes ) {
516 0         0 return shuffle(@classes);
517             }
518 34         1913 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.98
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