File Coverage

blib/lib/Test/Able/Role/Meta/Class.pm
Criterion Covered Total %
statement 183 187 97.8
branch 61 70 87.1
condition 15 18 83.3
subroutine 26 26 100.0
pod 6 6 100.0
total 291 307 94.7


line stmt bran cond sub pod time code
1             package Test::Able::Role::Meta::Class;
2              
3 8     8   3635 use Moose::Role;
  8         27768  
  8         37  
4 8     8   35938 use Moose::Util::TypeConstraints;
  8         15  
  8         39  
5 8     8   12159 use Scalar::Util;
  8         12  
  8         341  
6 8     8   36 use strict;
  8         9  
  8         209  
7 8     8   3936 use Test::Able::Role::Meta::Method;
  8         22  
  8         293  
8 8     8   3266 use Test::Able::Method::Array;
  8         17  
  8         272  
9 8     8   37 use warnings;
  8         8  
  8         12796  
10              
11             with qw( Test::Able::Planner );
12              
13             =head1 NAME
14              
15             Test::Able::Role::Meta::Class - Main metarole
16              
17             =head1 DESCRIPTION
18              
19             This metarole gets applied to the Moose::Meta::Class metaclass objects
20             for all Test::Able objects. This metarole also pulls in
21             L<Test::Able::Planner>.
22              
23             =head1 ATTRIBUTES
24              
25             =over
26              
27             =item method_types
28              
29             The names of the different types of test-related methods.
30             The default set is startup, setup, test, teardown, and shutdown.
31              
32             =cut
33              
34             has 'method_types' => (
35             is => 'ro', isa => 'ArrayRef', lazy_build => 1,
36             );
37              
38             =item *_methods
39              
40             The test-related method lists. There will be one for each method
41             type. The default set will be:
42              
43             startup_methods
44             setup_methods
45             test_methods
46             teardown_methods
47             shutdown_methods
48              
49             These lists are what forms the basis of the test execution plan.
50              
51             The lists themselves will be coerced into L<Test::Able::Method::Array> objects
52             just for the convenience of overloading for hash access. The elements of the
53             lists will be L<Test::Able::Role::Meta::Method>-based method metaclass
54             objects.
55              
56             =cut
57              
58             for ( @{ __PACKAGE__->_build_method_types } ) {
59             has "${_}_methods" => (
60             is => 'rw', isa => 'Test::Able::MethodArray', lazy_build => 1,
61             coerce => 1,
62             trigger => sub {
63             my ( $self, $value, ) = @_;
64              
65             $self->clear_plan;
66              
67             return;
68             },
69             );
70             }
71              
72             subtype 'Test::Able::MethodArray'
73             => as 'Object'
74             => where { $_->isa( 'Test::Able::Method::Array' ); };
75              
76             coerce 'Test::Able::MethodArray'
77             => from 'ArrayRef'
78             => via { bless( $_, 'Test::Able::Method::Array' ); };
79              
80             =item test_objects
81              
82             The list of L<Test::Able::Object>-based objects that the test runner
83             object will iterate through to make up the test run.
84              
85             =cut
86              
87             has 'test_objects' => (
88             is => 'rw', isa => 'ArrayRef', lazy_build => 1,
89             );
90              
91             =item current_test_object
92              
93             The test object that is currently being executed (or introspected).
94              
95             =cut
96              
97             has 'current_test_object' => (
98             is => 'rw', isa => 'Object', clearer => 'clear_current_test_object',
99             );
100              
101             =item current_test_method
102              
103             The method metaclass object of the associated test method.
104             This is only useful from within a setup or teardown method.
105             Its also available in the test method itself but current_method()
106             would be exactly the same in a test method and its shorter to type.
107              
108             =cut
109              
110             has 'current_test_method' => (
111             is => 'rw', isa => 'Object', clearer => 'clear_current_test_method',
112             );
113              
114             =item current_method
115              
116             The method metaclass object of the currently executing test-related
117             method.
118              
119             =cut
120              
121             has 'current_method' => (
122             is => 'rw', isa => 'Object', clearer => 'clear_current_method',
123             );
124              
125             =item test_runner_object
126              
127             The test object that will be running the show. It may itself be in the
128             test_objects list. The run_tests() method sets this value to its invocant.
129              
130             =cut
131              
132             has 'test_runner_object' => (
133             is => 'rw', isa => 'Object',
134             );
135              
136             =item dry_run
137              
138             Setting this true will cause all test-related method execution to be skipped.
139             This means things like method exception handling, method plan handling, and
140             Test::Builder integration will also not happen. One use of this could be to
141             print out the execution plan. The default is 0.
142              
143             =cut
144              
145             has 'dry_run' => (
146             is => 'rw', isa => 'Bool', default => 0,
147             );
148              
149             =item on_method_plan_fail
150              
151             Determines what is done, if anything, when the observed method plan doesn't
152             match the expected method plan after the test-related method runs. If this
153             attribute is not set then nothing special is done. Setting this to log or die
154             will cause the failure to be logged via log() or just died upon. The default
155             is log.
156              
157             =cut
158              
159             enum 'Test::Able::MethodPlanFailAction' => qw( die log );
160              
161             has 'on_method_plan_fail' => (
162             is => 'rw', isa => 'Test::Able::MethodPlanFailAction', default => 'log',
163             clearer => 'clear_on_method_plan_fail',
164             );
165              
166             =item on_method_exception
167              
168             Determines what is done, if anything, when an exception is thrown within a
169             test-related method.
170              
171             If this attribute isn't set then the exception is simply rethrown. This is
172             the default.
173              
174             If its set to "continue" then the exception will be silently ignored.
175              
176             And if set to "continue_at_level" the exception will also be silently ignored
177             and the test runner will skip over lower levels, if there are any, of the test
178             execution plan. The levels are defined as follows. The startup and shutdown
179             methods are at the first level. The setup and teardown methods are the second
180             level. And test methods are the third and last level. Or in visual form:
181              
182             startup
183             setup
184             test
185             teardown
186             shutdown
187              
188             In addition, when this attribute is set to continue or continue_at_level the
189             exceptions will be recorded in the method_exceptions attribute of the
190             currently executing test object.
191              
192             There is only one way to cause a fatal exception when this attribute is set to
193             continue or continue_at_level. And that is to throw a
194             L<Test::Able::FatalException> exception.
195              
196             =cut
197              
198             enum 'Test::Able::MethodExceptionAction' => qw( continue continue_at_level );
199              
200             has 'on_method_exception' => (
201             is => 'rw', isa => 'Test::Able::MethodExceptionAction',
202             clearer => 'clear_on_method_exception',
203             );
204              
205             =item method_exceptions
206              
207             List of exceptions that have occurred while inside a test-related method in
208             this test object. Each element of the list is a hashref that looks like this:
209              
210             {
211             method => $self->current_method,
212             exception => $exception,
213             }
214              
215             =back
216              
217             =cut
218              
219             has 'method_exceptions' => (
220             is => 'rw', isa => 'ArrayRef[HashRef]', lazy_build => 1,
221             );
222              
223             sub _build_method_types {
224 20     20   35 my ( $self, ) = @_;
225              
226 20         415 return [ qw( startup setup test teardown shutdown ) ];
227             }
228              
229             sub _build_startup_methods {
230 18     18   26 my ( $self, ) = @_;
231              
232 18         54 return $self->build_methods( 'startup' );
233             }
234              
235             sub _build_setup_methods {
236 18     18   24 my ( $self, ) = @_;
237              
238 18         49 return $self->build_methods( 'setup' );
239             }
240              
241             sub _build_test_methods {
242 20     20   33 my ( $self, ) = @_;
243              
244 20         49 return $self->build_methods( 'test' );
245             }
246              
247             sub _build_teardown_methods {
248 18     18   36 my ( $self, ) = @_;
249              
250 18         48 return $self->build_methods( 'teardown' );
251             }
252              
253             sub _build_shutdown_methods {
254 18     18   55 my ( $self, ) = @_;
255              
256 18         41 return $self->build_methods( 'shutdown' );
257             }
258              
259             sub _build_test_objects {
260 7     7   12 my ( $self, ) = @_;
261              
262 7 100       222 return $self->current_test_object
263             ? [ $self->current_test_object, ] : [];
264             }
265              
266 14     14   472 sub _build_method_exceptions { []; }
267              
268             =head1 METHODS
269              
270             =over
271              
272             =item run_tests
273              
274             The main test runner method. Iterates over test_objects list calling
275             run_methods() to run through the test execution plan.
276              
277             Manages test_runner_object, current_test_object, runner_plan, and
278             last_runner_plan along the way.
279              
280             =cut
281             sub run_tests {
282 31     31 1 406 my ( $self, ) = @_;
283              
284 31         1064 $self->test_runner_object( $self, );
285 31         39 for my $test_object ( @{ $self->test_objects } ) {
  31         906  
286 42         111 $test_object->meta->test_runner_object( $self, );
287             }
288              
289             # Initial plan calc.
290 31         867 $self->runner_plan;
291              
292 31         904 $self->log( "$self->run_tests() called but there are no test objects" )
293 31 50       42 unless @{ $self->test_objects };
294 31         53 for my $test_object ( @{ $self->test_objects } ) {
  31         868  
295 42         161 $test_object->meta->current_test_object( $test_object );
296              
297 42         66 my $exceptions_before_startup = @{ $self->method_exceptions };
  42         1357  
298 42         138 $test_object->meta->run_methods( 'startup' );
299 41         1258 $test_object->meta->run_methods( 'test' )
300 41 100       97 if $exceptions_before_startup == @{ $self->method_exceptions };
301 40         276 $test_object->meta->run_methods( 'shutdown' );
302              
303 40         215 $test_object->meta->clear_current_test_object;
304             }
305              
306             # Finalize planning for this run.
307 29         999 $self->clear_runner_plan;
308 29         839 $self->runner_plan;
309 29         1082 $self->clear_last_runner_plan;
310              
311 29         110 return;
312             }
313              
314             =item run_methods
315              
316             Executes a test-related method list as part of the test execution plan. Takes
317             one argument and that's the name of the test-related method type. Also, for
318             each test method, it calls run_methods() for the setup and teardown method
319             lists.
320              
321             =cut
322              
323             sub run_methods {
324 489     489 1 32317 my ( $self, $type, ) = @_;
325              
326 489         920 my $accessor_name = $type . '_methods';
327 489         16206 my $methods = $self->$accessor_name;
328 489         543 my $count = @{ $methods };
  489         995  
329 489         457 my $i;
330 489         554 for my $method ( @{ $methods } ) {
  489         973  
331 2074         1830 my $setup_exception_count;
332 2074 100       3628 if ( $type eq 'test' ) {
333 158         4983 $self->current_test_method( $method );
334 158         165 my $exceptions_before_setup = @{ $self->method_exceptions };
  158         4750  
335 158 100       4904 $self->run_methods( 'setup' ) if $method->do_setup;
336 158         5107 $setup_exception_count
337 158         797 = @{ $self->method_exceptions } - $exceptions_before_setup;
338             }
339              
340 2074         5950 my $method_name = $method->name;
341 2074 100       3407 unless ( $setup_exception_count ) {
342 2070         62192 $self->current_method( $method );
343 2070         63192 $self->log(
344             $self->current_test_object . '->' . $method_name
345             . "($type/" . $method->plan . ")"
346             . '('. ++$i . "/$count)"
347             );
348             }
349              
350 2074 100 100     63055 unless ( $setup_exception_count || $self->dry_run ) {
351 1913         51261 my $tests_before = $self->builder->{Curr_Test};
352              
353 1913         2244 eval { $self->current_test_object->$method_name; };
  1913         57292  
354 1913 100       1515515 if ( my $exception = $@ ) {
355 8 100       310 die $exception unless $self->on_method_exception;
356              
357 7         228 my $test_object_meta = $self->current_test_object->meta;
358 7         249 push(
359 7         193 @{ $test_object_meta->method_exceptions },
360             {
361             method => $self->current_method,
362             exception => $exception,
363             }
364             );
365              
366 7 100 66     55 die $exception if Scalar::Util::blessed( $exception )
367             && $exception->isa( 'Test::Able::FatalException' );
368             }
369              
370 1911 100 66     71918 if ( $self->on_method_plan_fail && $method->plan =~ /^\d+$/ ) {
371 1905         55241 my $tests_diff = $self->builder->{Curr_Test} - $tests_before;
372 1905 50       51532 if ( $tests_diff != $method->plan ) {
373 0         0 my $msg = "Method $method_name planned " . $method->plan
374             . " tests but ran $tests_diff.";
375 0 0       0 if ( $self->on_method_plan_fail eq 'die' ) {
376 0         0 die "$msg\n";
377             }
378 0         0 else { $self->log( $msg ); }
379             }
380             }
381             }
382              
383 2072 100       4496 if ( $type eq 'test' ) {
384 157 100       5246 $self->run_methods( 'teardown' ) if $method->do_teardown;
385 157         5941 $self->clear_current_test_method;
386             }
387 2072         72132 $self->clear_current_method;
388             }
389              
390 487         1396 return;
391             }
392              
393             =item build_methods
394              
395             Builds a test-related method list from the method metaclass objects associated
396             with this metaclass object. The method list is sorted alphabetically by
397             method name. Takes one argument and that's the name of the test-related
398             method type.
399              
400             =cut
401              
402             sub build_methods {
403 92     92 1 124 my ( $self, $type, ) = @_;
404              
405 92         180 my @methods;
406 92         2823 for my $method ( $self->current_test_object->meta->get_all_methods ) {
407 2985 100       59325 if ( $method->can( 'type' ) ) {
408 1777         48164 my $method_type = $method->type;
409 1777 100 100     6278 push( @methods, $method )
410             if defined $method_type && $method_type eq $type;
411             }
412             }
413              
414 458 50       12385 return bless(
415             [ sort {
416 87         969 $a->order <=> $b->order || $a->name cmp $b->name
417             } @methods ],
418             'Test::Able::Method::Array'
419             );
420             }
421              
422             =item build_all_methods
423              
424             Convenience method to call build_methods() for all method types.
425              
426             =cut
427              
428             sub build_all_methods {
429 37     37 1 438 my ( $self, ) = @_;
430              
431 37         50 for my $type ( @{ $self->method_types } ) {
  37         1130  
432 185         286 my $accessor_name = $type . '_methods';
433 185         231 my $has_name = 'has_' . $type . '_methods';
434 185 100       6422 $self->$accessor_name unless $self->$has_name;
435             }
436              
437 37         90 return;
438             }
439              
440             =item clear_all_methods
441              
442             Convenience method to clear all the test-related method lists out.
443              
444             =cut
445              
446             sub clear_all_methods {
447 5     5 1 2199 my ( $self, ) = @_;
448              
449 5         8 for my $type ( @{ $self->method_types } ) {
  5         165  
450 25         45 my $clear_name = 'clear_' . $type . '_methods';
451 25         24 my $has_name = 'has_' . $type . '_methods';
452 25 50       862 $self->$clear_name if $self->$has_name;
453             }
454              
455 5         38 return;
456             }
457              
458             =item log
459              
460             All logging goes through this method. It sends its args along to
461             Test::Builder::diag. And only if $ENV{TEST_VERBOSE} is set.
462              
463             =cut
464              
465             sub log {
466 2070     2070 1 3298 my $self = shift;
467              
468 2070 100       5020 $self->builder->diag( @_ ) if $ENV{ 'TEST_VERBOSE' };
469              
470 2070         7840 return;
471             }
472              
473             sub _build_plan {
474 33     33   49 my ( $self, ) = @_;
475              
476 33         32 my $plan;
477 124         3852 my $test_method_with_setup_count = grep {
478 33         1013 $_->do_setup;
479 33         38 } @{ $self->test_methods };
480 124         3721 my $test_method_with_teardown_count = grep {
481 33         1036 $_->do_teardown;
482 33         51 } @{ $self->test_methods };
483 33         51 METHOD_TYPE: for my $type ( @{ $self->method_types } ) {
  33         994  
484 163         228 my $accessor_name = $type . '_methods';
485 163         149 for my $method ( @{ $self->$accessor_name } ) {
  163         4859  
486 602 100       15344 if ( $method->plan eq 'no_plan' ) {
487 6         168 $plan = $method->plan;
488 6         22 last METHOD_TYPE;
489             }
490             else {
491 596 100       952 if ( $accessor_name eq 'setup_methods' ) {
    100          
492 120         3065 $plan
493             += $method->plan * $test_method_with_setup_count;
494             }
495             elsif ( $accessor_name eq 'teardown_methods' ) {
496 129         3182 $plan
497             += $method->plan * $test_method_with_teardown_count;
498             }
499 347         8946 else { $plan += $method->plan; }
500             }
501             }
502             }
503 33 50       86 $plan = 'no_plan' unless defined $plan;
504              
505 33         949 return $plan;
506             }
507              
508             =item clear_plan
509              
510             Special purpose plan clearer that dumps the test object's plan and the test
511             runner's plan in one shot.
512              
513             =back
514              
515             =cut
516              
517             #TODO: Could change this if Class::MOP bug 41449 is resolved.
518             #sub clear_plan {
519             before 'clear_plan' => sub {
520             my ( $self, ) = @_;
521              
522             delete $self->{ 'plan' };
523             delete $self->{ 'runner_plan' };
524              
525             return;
526             };
527             #}
528              
529             # Hack Test::Builder because it doesn't do plan alterations.
530             sub _build_runner_plan {
531 55     55   85 my ( $self, ) = @_;
532              
533 55         1510 $self->_hack_test_builder( $self->builder );
534              
535             # Compute current plan.
536 55         71 my $plan;
537 55         67 for my $test_object ( @{ $self->test_objects } ) {
  55         1594  
538 63         170 $test_object->meta->current_test_object( $test_object );
539              
540 63         195 my $object_plan = $test_object->meta->plan;
541 63 100       182 if ( $object_plan eq 'no_plan' ) {
542 11         22 $plan = $object_plan;
543 11         21 last;
544             }
545 52         103 else { $plan += $object_plan; }
546              
547 52         164 $test_object->meta->clear_current_test_object;
548             }
549 55 50       146 $plan = 'no_plan' unless defined $plan;
550              
551 55 100       1603 return $plan if $self->dry_run;
552              
553 47 100       1338 $self->builder->no_plan unless $self->builder->has_plan;
554              
555             # Update Test::Builder.
556 47 100 100     1415 if ( $self->builder->{No_Plan} || $self->builder->{was_No_Plan} ) {
557 33 100       158 if ( $plan =~ /^\d+$/ ) {
558 32 100       1101 if ( $self->has_last_runner_plan ) {
559 17         505 my $last = $self->last_runner_plan;
560 17 50       55 my $plan_diff = $plan - ( $last eq 'no_plan' ? 0 : $last );
561 17         442 $self->builder->{Expected_Tests} += $plan_diff;
562             }
563             else {
564 15         384 $self->builder->{Expected_Tests} += $plan;
565             }
566 32         805 $self->builder->{No_Plan} = 0;
567 32         814 $self->builder->{was_No_Plan} = 1;
568 32         887 $self->last_runner_plan( $plan );
569             }
570 1         27 else { $self->builder->{No_Plan} = 1; }
571             }
572              
573 47         1259 return $plan;
574             }
575              
576             #TODO: dump this ASAP.
577             # Hack Test::Builder cause it doesn't do deferred plans; yet.
578             my $hacked_test_builder;
579             sub _hack_test_builder {
580 55     55   67 my ( $self, ) = @_;
581              
582 55 100       153 return if $hacked_test_builder;
583 8         11 $hacked_test_builder++;
584 8     8   45 no warnings 'redefine';
  8         10  
  8         1215  
585 8         23 my $original_sub = \&Test::Builder::_ending;
586             *Test::Builder::_ending = sub {
587 8     8   2706 my $builder = shift;
588              
589 8 100 66     145 if ( $builder->{was_No_Plan} && $self->runner_plan =~ /\d+/ ) {
590 3         81 $builder->expected_tests( $self->builder->{Expected_Tests} );
591 3         775 $builder->no_header( 1 );
592             }
593              
594 8         56 return $builder->$original_sub( @_, );
595 8         72 };
596             }
597              
598             =head1 AUTHOR
599              
600             Justin DeVuyst, C<justin@devuyst.com>
601              
602             =head1 COPYRIGHT AND LICENSE
603              
604             Copyright 2009 by Justin DeVuyst.
605              
606             This library is free software, you can redistribute it and/or modify it under
607             the same terms as Perl itself.
608              
609             =cut
610              
611             1;