File Coverage

blib/lib/Test/Class.pm
Criterion Covered Total %
statement 320 321 99.6
branch 153 160 95.6
condition 18 18 100.0
subroutine 60 60 100.0
pod 17 17 100.0
total 568 576 98.6


line stmt bran cond sub pod time code
1 55     55   2125376 use strict;
  55         514  
  55         1563  
2 55     55   275 use warnings;
  55         108  
  55         1318  
3 55     55   1272 use 5.006;
  55         188  
4              
5             package Test::Class;
6              
7 55     55   30254 use Attribute::Handlers;
  55         264326  
  55         279  
8 55     55   1826 use Carp;
  55         111  
  55         3179  
9 55     55   26718 use MRO::Compat;
  55         116020  
  55         1840  
10 55     55   34440 use Storable qw(dclone);
  55         172936  
  55         3540  
11 55     55   13545 use Test::Builder;
  55         1127716  
  55         1518  
12 55     55   25498 use Test::Class::MethodInfo;
  55         150  
  55         1481  
13 55     55   26922 use Try::Tiny;
  55         112153  
  55         4018  
14              
15             our $VERSION = '0.51';
16              
17             my $Check_block_has_run;
18             {
19 55     55   387 no warnings 'void';
  55         113  
  55         2990  
20 54     54   383310 CHECK { $Check_block_has_run = 1 }
21             }
22              
23 55     55   316 use constant NO_PLAN => "no_plan";
  55         121  
  55         3032  
24 55     55   346 use constant SETUP => "setup";
  55         131  
  55         2878  
25 55     55   320 use constant TEST => "test";
  55         122  
  55         2629  
26 55     55   508 use constant TEARDOWN => "teardown";
  55         121  
  55         2652  
27 55     55   345 use constant STARTUP => "startup";
  55         172  
  55         2881  
28 55     55   341 use constant SHUTDOWN => "shutdown";
  55         128  
  55         37047  
29              
30              
31             our $Current_method = undef;
32 33     33 1 256 sub current_method { $Current_method }
33              
34              
35             my $Builder = Test::Builder->new;
36 6     6 1 175 sub builder { $Builder }
37              
38              
39             my $Tests = {};
40             my @Filters = ();
41              
42              
43             my %_Test; # inside-out object field indexed on $self
44              
45             sub DESTROY {
46 64     64   2791 my $self = shift;
47 64         2679 delete $_Test{ $self };
48             }
49              
50             sub _test_info {
51 996     996   1354 my $self = shift;
52 996 100       3404 return ref($self) ? $_Test{$self} : $Tests;
53             }
54              
55             sub _method_info {
56 358     358   697 my ($self, $class, $method) = @_;
57 358         618 return( _test_info($self)->{$class}->{$method} );
58             }
59              
60             sub _methods_of_class {
61 638     638   1429 my ( $self, $class ) = @_;
62 638 100       1045 my $test_info = _test_info($self)
63             or die "Test::Class internals seem confused. Did you override "
64             . "new() in a sub-class or via multiple inheritance?\n";
65 637         958 return values %{ $test_info->{$class} };
  637         1837  
66             }
67              
68             sub _parse_attribute_args {
69 131   100 131   436 my $args = shift || '';
70 131         207 my $num_tests;
71             my $type;
72 131         453 $args =~ s/\s+//sg;
73 131         405 foreach my $arg (split /=>/, $args) {
74 155 100       503 if (Test::Class::MethodInfo->is_num_tests($arg)) {
    100          
75 103         240 $num_tests = $arg;
76             } elsif (Test::Class::MethodInfo->is_method_type($arg)) {
77 51         118 $type = $arg;
78             } else {
79 1         36 die 'bad attribute args';
80             }
81             }
82 130         512 return( $type, $num_tests );
83             }
84              
85             sub _is_public_method {
86 131     131   261 my ($class, $name) = @_;
87 131         179 my @parents = @{mro::get_linear_isa($class)};
  131         523  
88 131         229 shift @parents;
89 131         234 foreach my $parent_class ( @parents ) {
90 132 100       1193 return unless $parent_class->can( $name );
91 6 100       18 return if _method_info( $class, $parent_class, $name );
92             }
93 1         17 return 1;
94             }
95              
96             sub Test : ATTR(CODE,RAWDATA) {
97 132     132 1 28864 my ($class, $symbol, $code_ref, $attr, $args) = @_;
98 132 100       440 if ($symbol eq "ANON") {
99 1         65 warn "cannot test anonymous subs - you probably loaded a Test::Class too late (after the CHECK block was run). See 'A NOTE ON LOADING TEST CLASSES' in perldoc Test::Class for more details\n";
100             } else {
101 131         198 my $name = *{$symbol}{NAME};
  131         263  
102 131 100       301 warn "overriding public method $name with a test method in $class\n"
103             if _is_public_method( $class, $name );
104 131 100       301 eval { $class->add_testinfo($name, _parse_attribute_args($args)) }
  131         264  
105             || warn "bad test definition '$args' in $class->$name\n";
106             }
107 55     55   469 }
  55         657  
  55         458  
108              
109             sub Tests : ATTR(CODE,RAWDATA) {
110 16     16 1 3182 my ($class, $symbol, $code_ref, $attr, $args) = @_;
111 16   100     64 $args ||= 'no_plan';
112 16         32 Test( $class, $symbol, $code_ref, $attr, $args );
113 55     55   26607 }
  55         151  
  55         218  
114              
115             sub add_testinfo {
116 130     130 1 311 my($class, $name, $type, $num_tests) = @_;
117 130         387 $Tests->{$class}->{$name} = Test::Class::MethodInfo->new(
118             name => $name,
119             num_tests => $num_tests,
120             type => $type,
121             );
122             }
123              
124             sub _class_of {
125 677     677   969 my $self = shift;
126 677 100       1663 return ref $self ? ref $self : $self;
127             }
128              
129             sub new {
130 63     63 1 1735 my $proto = shift;
131 63         245 my $class = _class_of( $proto );
132 63 100       253 $proto = {} unless ref($proto);
133 63         231 my $self = bless {%$proto, @_}, $class;
134 63         7526 $_Test{$self} = dclone($Tests);
135 63         322 return($self);
136             }
137              
138             sub _get_methods {
139 322     322   790 my ( $self, @types ) = @_;
140 322         601 my $test_class = _class_of( $self );
141              
142 322   100     1226 my $test_method_regexp = $ENV{ TEST_METHOD } || '.*';
143 322         520 my $method_regexp = eval { qr/\A$test_method_regexp\z/ };
  322         1991  
144 322 100       844 die "TEST_METHOD ($test_method_regexp) is not a valid regexp: $@" if $@;
145              
146 321         517 my %methods = ();
147 321         506 foreach my $class ( @{mro::get_linear_isa( $test_class )} ) {
  321         983  
148             FILTER:
149 638         1290 foreach my $info ( _methods_of_class( $self, $class ) ) {
150 807         1901 my $name = $info->name;
151              
152 807 100       1683 if ( $info->type eq TEST ) {
153             # determine if method is filtered, true if *any* filter
154             # returns false.
155 475         871 foreach my $filter ( @Filters ) {
156 90 100       214 next FILTER unless $filter->( $class, $name );
157             }
158             }
159              
160 773         1317 foreach my $type ( @types ) {
161 907 100       1700 if ( $info->is_type( $type ) ) {
162 184 100 100     1324 $methods{ $name } = 1
163             unless $type eq TEST && $name !~ $method_regexp;
164             }
165             }
166             }
167             }
168              
169 320         929 my @methods = sort keys %methods;
170 320         1347 return @methods;
171             }
172              
173             sub _num_expected_tests {
174 47     47   76 my $self = shift;
175 47 100       106 if (my $reason = $self->SKIP_CLASS ) {
176 2 100       6 return $reason eq "1" ? 0 : 1;
177             };
178 45         101 my @test_methods = _get_methods($self, TEST);
179 45 100       214 return 0 unless @test_methods;
180 22         95 my @startup_shutdown_methods =
181             _get_methods($self, STARTUP, SHUTDOWN);
182 22         84 my $num_startup_shutdown_methods =
183             _total_num_tests($self, @startup_shutdown_methods);
184 22 100       71 return(NO_PLAN) if $num_startup_shutdown_methods eq NO_PLAN;
185 20         67 my @fixture_methods = _get_methods($self, SETUP, TEARDOWN);
186 20         82 my $num_fixture_tests = _total_num_tests($self, @fixture_methods);
187 20 100       102 return(NO_PLAN) if $num_fixture_tests eq NO_PLAN;
188 18         53 my $num_tests = _total_num_tests($self, @test_methods);
189 18 100       89 return(NO_PLAN) if $num_tests eq NO_PLAN;
190 14         67 return($num_startup_shutdown_methods + $num_tests + @test_methods * $num_fixture_tests);
191             }
192              
193             sub expected_tests {
194 23     23 1 355 my $total = 0;
195 23         64 foreach my $test (@_) {
196 53 100 100     103 if ( _isa_class( __PACKAGE__, $test ) ) {
    100          
197 47         111 my $n = _num_expected_tests($test);
198 47 100       154 return NO_PLAN if $n eq NO_PLAN;
199 39         95 $total += $n;
200             } elsif ( defined $test && $test =~ m/^\d+$/ ) {
201 4         13 $total += $test;
202             } else {
203 2 100       16 $test = 'undef' unless defined $test;
204 1         20 croak "$test is not a Test::Class or an integer";
205             }
206             }
207 13         44 return $total;
208             }
209              
210             sub _total_num_tests {
211 292     292   625 my ($self, @methods) = @_;
212 292         520 my $class = _class_of( $self );
213 292         461 my $total_num_tests = 0;
214 292         521 foreach my $method (@methods) {
215 293         442 foreach my $class (@{mro::get_linear_isa($class)}) {
  293         798  
216 332         575 my $info = _method_info($self, $class, $method);
217 332 100       746 next unless $info;
218 301         773 my $num_tests = $info->num_tests;
219 301 100       769 return(NO_PLAN) if ($num_tests eq NO_PLAN);
220 277         497 $total_num_tests += $num_tests;
221 277 100       911 last unless $num_tests =~ m/^\+/
222             }
223             }
224 268         1025 return($total_num_tests);
225             }
226              
227             sub _has_no_tests {
228 116     116   264 my ( $self, $method ) = @_;
229 116         289 return _total_num_tests( $self, $method ) eq '0';
230             }
231              
232             sub _all_ok_from {
233 114     114   252 my ($self, $start_test) = @_;
234              
235             # The Test::Builder 1.5 way to do it
236 114 50       591 if( $Builder->can("history") ) {
237 0         0 return $Builder->history->can_succeed;
238             }
239             # The Test::Builder 0.x way to do it
240             else {
241 114         348 my $current_test = $Builder->current_test;
242 114 100       12470 return(1) if $start_test == $current_test;
243 109         453 my @results = ($Builder->summary)[$start_test .. $current_test-1];
244 109 100       13663 foreach my $result (@results) { return(0) unless $result }
  140         496  
245 94         836 return(1);
246             }
247             }
248              
249             sub _exception_failure {
250 9     9   30 my ($self, $method, $exception, $tests) = @_;
251 9         19 local $Test::Builder::Level = 3;
252 9         18 my $message = $method;
253 9 100 100     58 $message .= " (for test method '$Current_method')"
254             if defined $Current_method && $method ne $Current_method;
255 9         35 _show_header($self, @$tests);
256 9         1072 chomp $exception;
257 9         55 $Builder->ok(0, "$message died ($exception)");
258 9         33 _threw_exception( $self, $method => 1 );
259             }
260              
261             my %threw_exception;
262             sub _threw_exception {
263 223     223   469 my ( $self, $method, $optional_value) = @_;
264 223         383 my $class = ref( $self );
265 223 100       641 $threw_exception{ $class }{ $method } = $optional_value
266             if defined $optional_value;
267 223         754 return $threw_exception{ $class }{ $method };
268             }
269              
270             sub _run_method {
271 116     116   279 my ($self, $method, $tests) = @_;
272 116         339 _threw_exception( $self, $method => 0 );
273 116         317 my $num_start = $Builder->current_test;
274 116         12835 my $skip_reason;
275 116         249 my $original_ok = \&Test::Builder::ok;
276 55     55   90209 no warnings;
  55         136  
  55         95334  
277             local *Test::Builder::ok = sub {
278 146     146   14827 my ($builder, $test, $description) = @_;
279 146         292 local $Test::Builder::Level = $Test::Builder::Level+1;
280 146 100       392 unless ( defined($description) ) {
281 22         70 $description = $self->current_method;
282 22         62 $description =~ tr/_/ /;
283             }
284 146         388 my $is_ok = $original_ok->($builder, $test, $description);
285 146 100       57656 unless ( $is_ok ) {
286 20         54 my $class = ref $self;
287 20         104 $Builder->diag( " (in $class->$method)" );
288             }
289 146         5378 return $is_ok;
290 116         755 };
291              
292 116         229 my $exception;
293 116     43   710 $skip_reason = try { $self->$method } catch { $exception = $_; undef };
  116         6519  
  9         549  
  9         40  
294 114 100       3493 $skip_reason = $method unless $skip_reason;
295              
296 114         371 my $num_done = $Builder->current_test - $num_start;
297 114         12836 my $num_expected = _total_num_tests($self, $method);
298 114 100       329 $num_expected = $num_done if $num_expected eq NO_PLAN;
299 114 100       298 if ($num_done == $num_expected) {
    100          
300 101 100       248 _exception_failure($self, $method, $exception, $tests)
301             if $exception;
302             } elsif ($num_done > $num_expected) {
303 5         10 local $Test::Builder::Level = $Test::Builder::Level+1;
304 5         10 my $class = ref $self;
305 5 100       25 if ($self->fail_if_returned_late) {
306 2         15 $Builder->ok(0, "expected $num_expected test(s) in $class\::$method, $num_done completed");
307             } else {
308 3         18 $Builder->diag("expected $num_expected test(s) in $class\::$method, $num_done completed\n");
309             }
310             } else {
311 8         26 until (($Builder->current_test - $num_start) >= $num_expected) {
312 10 100       1734 if ($exception) {
313 4         21 _exception_failure($self, $method, $exception, $tests);
314 4         12 $skip_reason = "$method died";
315 4         20 $exception = '';
316             } else {
317 6         10 local $Test::Builder::Level = $Test::Builder::Level+1;
318 6 100       22 if ($self->fail_if_returned_early) {
319 3         14 my $class = ref $self;
320 3         13 $Builder->ok(0, "($class\::$method returned before plan complete)");
321             } else {
322 3         11 $Builder->skip( $skip_reason );
323             }
324             }
325             }
326             }
327 114         2600 return(_all_ok_from($self, $num_start));
328             }
329              
330 3     3 1 8 sub fail_if_returned_early { 0 }
331 3     3 1 10 sub fail_if_returned_late { 0 }
332              
333             sub _show_header {
334 120     120   337 my ($self, @tests) = @_;
335 120 100       373 return if $Builder->has_plan;
336 10         1917 my $num_tests = Test::Class->expected_tests(@tests);
337 10 100       44 if ($num_tests eq NO_PLAN) {
338 1         3 $Builder->no_plan;
339             } else {
340 9         53 $Builder->expected_tests($num_tests);
341             }
342             }
343              
344             my %SKIP_THIS_CLASS = ();
345              
346             sub SKIP_CLASS {
347 116     116 1 483 my $class = shift;
348 116 100       302 $SKIP_THIS_CLASS{ $class } = shift if @_;
349 116         409 return $SKIP_THIS_CLASS{ $class };
350             }
351              
352             sub _isa_class {
353 143     143   309 my ( $class, $object_or_class ) = @_;
354 143 100       361 return unless defined $object_or_class;
355 142 50       352 return if $object_or_class eq 'Contextual::Return::Value';
356 142         215 return eval {
357 142 100       1801 $object_or_class->isa( $class ) and $object_or_class->can( 'runtests' )
358             };
359             }
360              
361             sub _test_classes {
362 41     41   312 my $class = shift;
363 41         73 return( @{mro::get_isarev($class)}, $class );
  41         275  
364             }
365              
366             sub runtests {
367 49 100   49 1 61775 die "Test::Class was loaded too late (after the CHECK block was run), or you may have redefined a test_ sub. See 'A NOTE ON LOADING TEST CLASSES' in perldoc Test::Class for more details\n"
368             unless $Check_block_has_run;
369 48         168 my @tests = @_;
370 48 100 100     342 if (@tests == 1 && !ref($tests[0])) {
371 39         95 my $base_class = shift @tests;
372 39         130 @tests = _test_classes( $base_class );
373             }
374 48         139 my $all_passed = 1;
375 48         142 TEST_OBJECT: foreach my $t (@tests) {
376             # SHOULD ALSO ALLOW NO_PLAN
377 67 100       724 next if $t =~ m/^\d+$/;
378 64 100       195 croak "$t is not Test::Class or integer"
379             unless _isa_class( __PACKAGE__, $t );
380 63 100       311 if (my $reason = $t->SKIP_CLASS) {
381 3         12 _show_header($t, @tests);
382 3 100       768 $Builder->skip( $reason ) unless $reason eq "1";
383             } else {
384 60 100       343 $t = $t->new unless ref($t);
385 60         230 my @test_methods = _get_methods($t, TEST);
386 58 100       220 if ( @test_methods ) {
387 45         148 foreach my $method (_get_methods($t, STARTUP)) {
388 8 100       25 _show_header($t, @tests) unless _has_no_tests($t, $method);
389 8         2176 my $method_passed = _run_method($t, $method, \@tests);
390 8 100       23 $all_passed = 0 unless $method_passed;
391 8 100       28 next TEST_OBJECT unless $method_passed;
392             }
393 44         119 my $class = ref($t);
394 44         138 my @setup = _get_methods($t, SETUP);
395 44         158 my @teardown = _get_methods($t, TEARDOWN);
396 44         146 foreach my $test ( @test_methods ) {
397 64         138 local $Current_method = $test;
398 64 100       252 $Builder->diag("\n$class->$test") if $ENV{TEST_VERBOSE};
399 64         1207 my @methods_to_run = (@setup, $test, @teardown);
400 64         197 while ( my $method = shift @methods_to_run ) {
401 100 100       230 _show_header($t, @tests) unless _has_no_tests($t, $method);
402 100 100       13961 $all_passed = 0 unless _run_method($t, $method, \@tests);
403 98 100       260 if ( _threw_exception( $t, $method ) ) {
404 8 100       57 next if ($method eq $test);
405 2         6 my $num_to_skip = _total_num_tests($t, @methods_to_run);
406 2         17 $Builder->skip( "$method died" ) for ( 1 .. $num_to_skip );
407 2         825 last;
408             }
409             }
410             }
411 42         201 foreach my $method (_get_methods($t, SHUTDOWN)) {
412 8 100       28 _show_header($t, @tests) unless _has_no_tests($t, $method);
413 8 50       815 $all_passed = 0 unless _run_method($t, $method, \@tests);
414             }
415             }
416              
417             }
418             }
419 43         208 return($all_passed);
420             }
421              
422             sub _find_calling_test_class {
423 21     21   34 my $level = 0;
424 21         68 while (my $class = caller(++$level)) {
425 35 100       87 next if $class eq __PACKAGE__;
426 26 100       49 return $class if _isa_class( __PACKAGE__, $class );
427             }
428 1         11 return(undef);
429             }
430              
431             sub num_method_tests {
432 21     21 1 1358 my ($self, $method, $n) = @_;
433 21 100       41 my $class = _find_calling_test_class( $self )
434             or croak "not called in a Test::Class";
435 20 100       52 my $info = _method_info($self, $class, $method)
436             or croak "$method is not a test method of class $class";
437 19 100       59 $info->num_tests($n) if defined($n);
438 18         63 return( $info->num_tests );
439             }
440              
441             sub num_tests {
442 9     9 1 45 my $self = shift;
443 9 50       36 croak "num_tests need to be called within a test method"
444             unless defined $Current_method;
445 9         31 return( $self->num_method_tests( $Current_method, @_ ) );
446             }
447              
448             sub BAILOUT {
449 1     1 1 1355 my ($self, $reason) = @_;
450 1         4 $Builder->BAILOUT($reason);
451             }
452              
453             sub _last_test_if_exiting_immediately {
454 4 100   4   19 $Builder->expected_tests || $Builder->current_test+1
455             }
456              
457             sub FAIL_ALL {
458 3     3 1 4326 my ($self, $reason) = @_;
459 3         14 my $last_test = _last_test_if_exiting_immediately();
460 3 50       488 $Builder->expected_tests( $last_test ) unless $Builder->has_plan;
461 3         328 $Builder->ok(0, $reason) until $Builder->current_test >= $last_test;
462 3 50       3964 my $num_failed = $Builder->can("history")
463             ? $Builder->history->fail_count : grep( !$_, $Builder->summary );
464 3 50       729 exit( $num_failed < 254 ? $num_failed : 254 );
465             }
466              
467             sub SKIP_ALL {
468 2     2 1 748 my ($self, $reason) = @_;
469 2 100       9 $Builder->skip_all( $reason ) unless $Builder->has_plan;
470 1         124 my $last_test = _last_test_if_exiting_immediately();
471 1         122 $Builder->skip( $reason )
472             until $Builder->current_test >= $last_test;
473 1         1429 exit(0);
474             }
475              
476             sub add_filter {
477 6     6 1 766 my ( $class, $cb ) = @_;
478              
479 6 100       31 if ( not ref $cb eq 'CODE' ) {
480 1         188 croak "Filter isn't a code-ref"
481             }
482              
483 5         20 push @Filters, $cb;
484             }
485              
486             1;
487              
488             __END__