File Coverage

blib/lib/Test/Class/Moose/Role/Executor.pm
Criterion Covered Total %
statement 251 267 94.0
branch 77 88 87.5
condition 5 6 83.3
subroutine 42 42 100.0
pod 6 6 100.0
total 381 409 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   29402 use strict;
  30         83  
  30         1700  
6 30     30   189 use warnings;
  30         66  
  30         1962  
7 30     30   212 use namespace::autoclean;
  30         66  
  30         260  
8              
9 30     30   2971 use 5.010000;
  30         135  
10              
11             our $VERSION = '1.00';
12              
13 30     30   210 use Moose::Role 2.0000;
  30         731  
  30         284  
14 30     30   217424 use Carp;
  30         68  
  30         3109  
15              
16 30     30   1581 use List::SomeUtils qw(uniq);
  30         14758  
  30         2184  
17 30     30   237 use List::Util qw(shuffle);
  30         102  
  30         2550  
18 30     30   1823 use Test2::API qw( test2_stack );
  30         98  
  30         1994  
19 30     30   19421 use Test2::Tools::AsyncSubtest qw( async_subtest );
  30         952522  
  30         3621  
20             BEGIN {
21 30     30   320 require Test2::AsyncSubtest::Hub;
22 30         201 Test2::AsyncSubtest::Hub->do_not_warn_on_plan();
23             }
24 30     30   2121 use Test::Class::Moose::AttributeRegistry;
  30         65  
  30         909  
25 30     30   177 use Test::Class::Moose::Config;
  30         62  
  30         1011  
26 30     30   12516 use Test::Class::Moose::Report::Class;
  30         362  
  30         1302  
27 30     30   21791 use Test::Class::Moose::Report::Instance;
  30         593  
  30         1526  
28 30     30   19542 use Test::Class::Moose::Report::Method;
  30         587  
  30         1811  
29 30     30   22941 use Test::Class::Moose::Report;
  30         607  
  30         1899  
30 30     30   21714 use Test::Class::Moose::Util qw( context_do );
  30         130  
  30         2379  
31 30     30   277 use Try::Tiny;
  30         67  
  30         116219  
32              
33             has 'test_configuration' => (
34             is => 'ro',
35             isa => 'Test::Class::Moose::Config',
36             required => 1,
37             );
38              
39             has 'test_report' => (
40             is => 'ro',
41             isa => 'Test::Class::Moose::Report',
42             builder => '_build_test_report',
43             );
44              
45             has 'is_parallel' => (
46             is => 'ro',
47             isa => 'Bool',
48             default => sub { ( ref $_[0] ) =~ /::Parallel$/ ? 1 : 0 },
49             );
50              
51             sub runtests {
52 31     31 1 443 my $self = shift;
53              
54 31         1622 my $report = $self->test_report;
55 31         1688 $report->_start_benchmark;
56 31         200 my @test_classes = $self->test_classes;
57              
58 31         208 $self->_validate_test_classes(@test_classes);
59              
60             context_do {
61 29     29   94 my $ctx = shift;
62              
63 29         272 $ctx->plan( scalar @test_classes );
64              
65 29         16949 $self->_run_test_classes(@test_classes);
66              
67 19 50       51455 $ctx->diag(<<"END") if $self->test_configuration->statistics;
68 0         0 Test classes: @{[ $report->num_test_classes ]}
69 0         0 Test instances: @{[ $report->num_test_instances ]}
70 0         0 Test methods: @{[ $report->num_test_methods ]}
71 0         0 Total tests run: @{[ $report->num_tests_run ]}
72             END
73              
74 19         144 $ctx->done_testing;
75 29         444 };
76              
77 19         1856 $report->_end_benchmark;
78 19         264 return $self;
79             }
80              
81             sub _validate_test_classes {
82 31     31   315 my $self = shift;
83              
84 31 100       120 my @bad = grep { !$_->isa('Test::Class::Moose') } @_
  158         912  
85             or return;
86              
87 2         6 my $msg = 'Found the following class';
88 2 100       8 $msg .= 'es' if @bad > 1;
89 2 100       21 $msg
    100          
90             .= ' that '
91             . ( @bad > 1 ? 'are' : 'is' ) . ' not '
92             . ( @bad > 1 ? 'subclasses' : 'a subclass' )
93             . " of Test::Class::Moose: @bad";
94 2 100       8 $msg .= ' (did you load '
95             . ( @bad > 1 ? 'these classes' : 'this class' ) . '?)';
96 2         127 die $msg;
97             }
98              
99             sub _run_test_classes {
100 19     19   60 my $self = shift;
101 19         113 my @test_classes = @_;
102              
103 19         71 for my $test_class (@test_classes) {
104             async_subtest(
105             $test_class,
106             { manual_skip_all => 1 },
107 35     35   35009 sub { $self->run_test_class($test_class) }
108 35         41769 )->finish;
109             }
110             }
111              
112 35     35   2055 sub _build_test_report { Test::Class::Moose::Report->new }
113              
114             sub run_test_class {
115 45     45 1 303 my $self = shift;
116 45         369 my $test_class = shift;
117              
118 45         15795 my $class_report
119             = Test::Class::Moose::Report::Class->new( name => $test_class );
120              
121 45         8049 $self->test_report->add_test_class($class_report);
122              
123 45         2884 $class_report->_start_benchmark;
124              
125 45         462 my $passed = $self->_run_test_instances( $test_class, $class_report );
126              
127 45         3304 $class_report->passed($passed);
128              
129 45         5988 $class_report->_end_benchmark;
130              
131 45         282 return $class_report;
132             }
133              
134             sub _run_test_instances {
135 45     45   228 my $self = shift;
136 45         116 my $test_class = shift;
137 45         103 my $class_report = shift;
138              
139 45         2947 my @test_instances = $test_class->_tcm_make_test_class_instances(
140             test_report => $self->test_report,
141             );
142              
143 45 100       356 unless (@test_instances) {
144             context_do {
145 2     2   15 my $ctx = shift;
146              
147 2         18 my $message = "Skipping '$test_class': no test instances found";
148 2         162 $class_report->skipped($message);
149 2         110 $class_report->passed(1);
150 2         57 $ctx->plan( 0, 'SKIP' => $message );
151 2         146 };
152 2         46 return 1;
153             }
154              
155             return context_do {
156 43     43   287 my $ctx = shift;
157              
158 43 100       407 $ctx->plan( scalar @test_instances )
159             if @test_instances > 1;
160              
161 43         3258 my $passed = 1;
162 43         244 for my $test_instance (
163 3         283 sort { $a->test_instance_name cmp $b->test_instance_name }
164             @test_instances )
165             {
166 46         319 my $instance_report = $self->_maybe_wrap_test_instance(
167             $test_instance,
168             $class_report,
169             @test_instances > 1,
170             );
171 46 100       2736 $passed = 0 if not $instance_report->passed;
172             }
173              
174 43         208 return $passed;
175 43         1416 };
176             }
177              
178             sub _maybe_wrap_test_instance {
179 46     46   256 my $self = shift;
180 46         126 my $test_instance = shift;
181 46         128 my $class_report = shift;
182 46         142 my $in_subtest = shift;
183              
184 46 100       471 return $self->run_test_instance(
185             $test_instance,
186             $class_report,
187             ) unless $in_subtest;
188              
189 6         25 my $instance_report;
190             async_subtest(
191             $test_instance->test_instance_name,
192             { manual_skip_all => 1 },
193             sub {
194 6     6   5750 $instance_report = $self->run_test_instance(
195             $test_instance,
196             $class_report,
197             );
198             },
199 6         302 )->finish;
200              
201 6         17294 return $instance_report;
202             }
203              
204             sub run_test_instance {
205 46     46 1 158 my ( $self, $test_instance, $class_report ) = @_;
206              
207 46         3167 my $test_instance_name = $test_instance->test_instance_name;
208 46         3362 my $instance_report = Test::Class::Moose::Report::Instance->new(
209             { name => $test_instance_name,
210             }
211             );
212              
213 46 100       2935 local $0 = "$0 - $test_instance_name"
214             if $self->test_configuration->set_process_name;
215              
216 46         2716 $instance_report->_start_benchmark;
217              
218 46         3092 $class_report->add_test_instance($instance_report);
219              
220 46         5324 my @test_methods = $self->_test_methods_for($test_instance);
221              
222             context_do {
223 46     46   113 my $ctx = shift;
224              
225 46 50       206 unless (@test_methods) {
226              
227 0         0 my $message
228             = "Skipping '$test_instance_name': no test methods found";
229 0         0 $instance_report->skipped($message);
230 0         0 $instance_report->passed(1);
231 0         0 $ctx->plan( 0, SKIP => $message );
232 0         0 return;
233             }
234              
235 46         2768 my $report = $self->test_report;
236              
237 46 100       423 unless (
238             $self->run_test_control_method(
239             $test_instance, 'test_startup', $instance_report,
240             )
241             )
242             {
243 2         109 $instance_report->passed(0);
244 2         10 return;
245             }
246              
247 44 100       2355 if ( my $message = $test_instance->test_skip ) {
248              
249             # test_startup skipped the class
250 4         250 $instance_report->skipped($message);
251              
252 4 100       33 if ( $test_instance->run_control_methods_on_skip ) {
253 1 50       9 $self->_run_shutdown( $test_instance, $instance_report )
254             or return;
255             }
256              
257 4         230 $instance_report->passed(1);
258 4         73 $ctx->plan( 0, SKIP => $message );
259 4         3405 return;
260             }
261              
262 40         567 $ctx->plan( scalar @test_methods );
263              
264 40         30344 my $all_passed = 1;
265 40         147 foreach my $test_method (@test_methods) {
266 110         855 my $method_report = $self->run_test_method(
267             $test_instance,
268             $test_method,
269             $instance_report,
270             );
271 110 100       5213 $all_passed = 0 if not $method_report->passed;
272             }
273 40         2369 $instance_report->passed($all_passed);
274              
275 40         261 $self->_run_shutdown( $test_instance, $instance_report );
276              
277             # finalize reporting
278 40         2321 $instance_report->_end_benchmark;
279 40 50       2293 if ( $self->test_configuration->show_timing ) {
280 0         0 my $time = $instance_report->time->duration;
281 0         0 $ctx->diag("$test_instance_name: $time");
282             }
283 46         1043 };
284              
285 46         1303 return $instance_report;
286             }
287              
288             sub _run_shutdown {
289 41     41   149 my ( $self, $test_instance, $instance_report ) = @_;
290              
291 41 50       179 return 1
292             if $self->run_test_control_method(
293             $test_instance, 'test_shutdown', $instance_report,
294             );
295              
296 0         0 $instance_report->passed(0);
297              
298 0         0 return 0;
299             }
300              
301             sub _test_methods_for {
302 187     187   21415 my ( $self, $thing ) = @_;
303              
304 187         993 my @filtered = $self->_filtered_test_methods($thing);
305 187 50       9290 return uniq(
306             $self->test_configuration->randomize
307             ? shuffle(@filtered)
308             : sort @filtered
309             );
310             }
311              
312             sub _filtered_test_methods {
313 187     187   606 my ( $self, $thing ) = @_;
314              
315 187         2188 my @method_list = $thing->test_methods;
316 187 100       13062 if ( my $include = $self->test_configuration->include ) {
317 4         10 @method_list = grep {/$include/} @method_list;
  18         46  
318             }
319 187 100       9459 if ( my $exclude = $self->test_configuration->exclude ) {
320 4         13 @method_list = grep { !/$exclude/ } @method_list;
  18         79  
321             }
322              
323 187 100       3596 my $test_class = ref $thing ? $thing->test_class : $thing;
324 187         1003 return $self->_filter_by_tag(
325             $test_class,
326             \@method_list
327             );
328             }
329              
330             sub _filter_by_tag {
331 187     187   577 my ( $self, $class, $methods ) = @_;
332              
333 187         811 my @filtered_methods = @$methods;
334 187 100       13695 if ( my $include = $self->test_configuration->include_tags ) {
335 12         21 my @new_method_list;
336 12         29 foreach my $method (@filtered_methods) {
337 57         106 foreach my $tag (@$include) {
338 76 100       231 if (Test::Class::Moose::AttributeRegistry->method_has_tag(
339             $class, $method, $tag
340             )
341             )
342             {
343 18         56 push @new_method_list => $method;
344             }
345             }
346             }
347 12         51 @filtered_methods = @new_method_list;
348             }
349 187 100       10091 if ( my $exclude = $self->test_configuration->exclude_tags ) {
350 8         52 my @new_method_list = @filtered_methods;
351 8         21 foreach my $method (@filtered_methods) {
352 22         44 foreach my $tag (@$exclude) {
353 25 100       70 if (Test::Class::Moose::AttributeRegistry->method_has_tag(
354             $class, $method, $tag
355             )
356             )
357             {
358             @new_method_list
359 8         18 = grep { $_ ne $method } @new_method_list;
  36         107  
360             }
361             }
362             }
363 8         24 @filtered_methods = @new_method_list;
364             }
365 187         1524 return @filtered_methods;
366             }
367              
368             my %TEST_CONTROL_METHODS = map { $_ => 1 } qw/
369             test_startup
370             test_setup
371             test_teardown
372             test_shutdown
373             /;
374              
375             sub run_test_control_method {
376 305     305 1 1255 my ( $self, $test_instance, $phase, $report_object ) = @_;
377              
378 305 100       15728 local $0 = "$0 - $phase"
379             if $self->test_configuration->set_process_name;
380              
381 305 50       1568 $TEST_CONTROL_METHODS{$phase}
382             or croak("Unknown test control method ($phase)");
383              
384 305 100       12869 my %report_args = (
385             name => $phase,
386             instance => (
387             $report_object->isa('Test::Class::Moose::Report::Method')
388             ? $report_object->instance
389             : $report_object
390             )
391             );
392 305         15978 my $phase_method_report
393             = Test::Class::Moose::Report::Method->new( \%report_args );
394              
395 305         785 my $set_meth = "set_${phase}_method";
396 305         20725 $report_object->$set_meth($phase_method_report);
397              
398             # It'd be nicer to start and end immediately after we call
399             # $test_instance->$phase but we can't guarantee that those calls would
400             # happen inside the try block.
401 305         15822 $phase_method_report->_start_benchmark;
402              
403             my $success = context_do {
404 305     305   698 my $ctx = shift;
405              
406             return try {
407 305         21953 my $count = $ctx->hub->count;
408 305         5162 $test_instance->$phase($report_object);
409 304 100       9427 croak "Tests may not be run in test control methods ($phase)"
410             unless $count == $ctx->hub->count;
411 303         2766 1;
412             }
413             catch {
414 2         306 my $error = $_;
415 2         84 my $class = $test_instance->test_class;
416 2         17 $ctx->ok( 0, "$class->$phase failed", [$error] );
417 2         1641 0;
418 305         3868 };
419 305         3434 };
420              
421 305         22605 $phase_method_report->_end_benchmark;
422              
423 305         1681 return $success;
424             }
425              
426             sub run_test_method {
427 110     110 1 403 my ( $self, $test_instance, $test_method, $instance_report ) = @_;
428              
429 110 100       6037 local $0 = "$0 - $test_method"
430             if $self->test_configuration->set_process_name;
431              
432 110         5461 my $method_report = Test::Class::Moose::Report::Method->new(
433             { name => $test_method, instance => $instance_report } );
434              
435 110         7982 $instance_report->add_test_method($method_report);
436              
437 110         5809 $test_instance->test_skip_clear;
438 110         624 $self->run_test_control_method(
439             $test_instance,
440             'test_setup',
441             $method_report,
442             );
443              
444 110         5620 $method_report->_start_benchmark;
445              
446 110         239 my $num_tests = 0;
447 110         4663 my $test_class = $test_instance->test_class;
448              
449             context_do {
450 110     110   282 my $ctx = shift;
451              
452 110         405 my $skipped;
453              
454             # If the call to ->$test_method fails then this subtest will fail and
455             # Test2::API will also include a diagnostic message with the error.
456             my $p = async_subtest(
457             $test_method,
458             { manual_skip_all => 1 },
459             sub {
460 110         114297 my $hub = test2_stack()->top;
461 110 100       7460 if ( my $message = $test_instance->test_skip ) {
462 3         172 $method_report->skipped($message);
463              
464             # I can't figure out how to get our current context in
465             # order to call $ctx->plan instead.
466             context_do {
467 3         24 shift->plan( 0, SKIP => $message );
468 3         38 };
469 3         44 $skipped = 1;
470 3         15 return 1;
471             }
472              
473 107         1652 $test_instance->$test_method($method_report);
474 102         2148844 $num_tests = $hub->count;
475             },
476 110         1769 )->finish;
477              
478 110         304703 $method_report->_end_benchmark;
479 110 50       5905 if ( $self->test_configuration->show_timing ) {
480 0         0 my $time = $method_report->time->duration;
481 0         0 $ctx->diag( $method_report->name . ": $time" );
482             }
483              
484             # $p will be undef if the tests failed but we want to stick to 0
485             # or 1.
486 110 100       10755 $method_report->passed( $p ? 1 : 0 );
487              
488 110 100 100     478 if ( !$skipped || $test_instance->run_control_methods_on_skip ) {
489 108 50       803 $self->run_test_control_method(
490             $test_instance,
491             'test_teardown',
492             $method_report,
493             ) or $method_report->passed(0);
494             }
495              
496 110         375 return $p;
497 110         1546 };
498              
499 110 100 66     8242 return $method_report unless $num_tests && !$method_report->is_skipped;
500              
501 102         5043 $method_report->num_tests_run($num_tests);
502 102 100       6135 $method_report->tests_planned($num_tests)
503             unless $method_report->has_plan;
504              
505 102         659 return $method_report;
506             }
507              
508             sub test_classes {
509 39     39 1 300 my $self = shift;
510              
511 39 100       2508 if ( my $classes = $self->test_configuration->test_classes ) {
512 5 50       12 return @{$classes} if @{$classes};
  5         60  
  5         35  
513             }
514              
515 34         221 my %metaclasses = Class::MOP::get_all_metaclasses();
516 34         5700 my @classes;
517 34         568 foreach my $class ( keys %metaclasses ) {
518 3618 100       8036 next if $class eq 'Test::Class::Moose';
519 3584 100       33631 push @classes => $class if $class->isa('Test::Class::Moose');
520             }
521              
522 34 50       2784 if ( $self->test_configuration->randomize_classes ) {
523 0         0 return shuffle(@classes);
524             }
525              
526 34         214 @classes = sort @classes;
527 34         977 return @classes;
528             }
529              
530             1;
531              
532             __END__
533              
534             =pod
535              
536             =encoding UTF-8
537              
538             =head1 NAME
539              
540             Test::Class::Moose::Role::Executor - Common code for Runner classes
541              
542             =head1 VERSION
543              
544             version 1.00
545              
546             =head1 DESCRIPTION
547              
548             This role implements the guts of this distribution, running all of your test
549             classes. It's public API can be wrapped by additional roles to provide
550             extensions to the default TCM behavior.
551              
552             The actual implementations are provided by
553             C<Test::Class::Moose::Executor::Sequential> and
554             C<Test::Class::Moose::Executor::Parallel>.
555              
556             =head1 API
557              
558             This role provides the following public methods for extensions. If you wrap any
559             of the methods related to test execution you are strongly encouraged to make
560             sure that the original method is called, as these methods implement the core
561             functionality of TCM.
562              
563             =head2 $executor->is_parallel
564              
565             This returns a boolean indicating whether or not this executor will run test
566             classes in parallel or not.
567              
568             =head2 $executor->test_configuration
569              
570             Returns the L<Test::Class::Moose::Config> object for this executor.
571              
572             =head2 $executor->test_report
573              
574             Returns the L<Test::Class::Moose::Report> object for this executor.
575              
576             =head2 $executor->test_classes
577              
578             Returns the list of test classes to be run, in the order that they should be
579             run.
580              
581             =head2 $executor->runtests
582              
583             This is the primary entry method for test executor. It is called without any
584             arguments and is expected to run all of the test classes defined in the test
585             configuration.
586              
587             =head2 $executor->run_test_class($test_class)
588              
589             This method is called once for each test class to be run. It is passed a single
590             argument, the I<name> of the test class to be run.
591              
592             =head2 $executor->run_test_instance($test_instance, $class_report)
593              
594             This method is called once for each instance of a test class to be run. For
595             most classes this is just called once but for classes which consume the
596             L<Test::Class::Moose::Role::Parameterized> role, it will be called more than
597             once.
598              
599             The first argument is the test class object to be run, and the second is an
600             instance of L<Test::Class::Moose::Report::Class> for the class being run.
601              
602             =head2 $executor->run_test_method($test_instance, $test_method, $instance_report)
603              
604             This method is called once for each test method in an instance to be run.
605              
606             The first argument is the test class object to be run, the second is a method
607             name, and the third is an instance of L<Test::Class::Moose::Report::Instance>
608             for the instance being run.
609              
610             =head2 $executor->run_test_control_method($test_instance, $control_method, $instance_report)
611              
612             This method is called once for each test method in an instance to be run.
613              
614             The first argument is the test class object to be run, the second is a control
615             method name (like C<'test_startup'>), and the third is an instance of
616             L<Test::Class::Moose::Report::Instance> for the instance being run.
617              
618             =head1 SUPPORT
619              
620             Bugs may be submitted at L<https://github.com/Test-More/test-class-moose/issues>.
621              
622             =head1 SOURCE
623              
624             The source code repository for Test-Class-Moose can be found at L<https://github.com/Test-More/test-class-moose>.
625              
626             =head1 AUTHORS
627              
628             =over 4
629              
630             =item *
631              
632             Curtis "Ovid" Poe <ovid@cpan.org>
633              
634             =item *
635              
636             Dave Rolsky <autarch@urth.org>
637              
638             =item *
639              
640             Chad Granum <exodist@cpan.org>
641              
642             =back
643              
644             =head1 COPYRIGHT AND LICENSE
645              
646             This software is copyright (c) 2012 - 2025 by Curtis "Ovid" Poe.
647              
648             This is free software; you can redistribute it and/or modify it under
649             the same terms as the Perl 5 programming language system itself.
650              
651             The full text of the license can be found in the
652             F<LICENSE> file included with this distribution.
653              
654             =cut