File Coverage

blib/lib/Test/Class/Moose/Role/Executor.pm
Criterion Covered Total %
statement 249 265 93.9
branch 77 88 87.5
condition 5 6 83.3
subroutine 41 41 100.0
pod 6 6 100.0
total 378 406 93.1


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