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   2223141 use strict;
  55         493  
  55         1734  
2 55     55   287 use warnings;
  55         95  
  55         1487  
3 55     55   1331 use 5.006;
  55         786  
4              
5             package Test::Class;
6              
7 55     55   32276 use Attribute::Handlers;
  55         278000  
  55         338  
8 55     55   2065 use Carp;
  55         114  
  55         3713  
9 55     55   31271 use MRO::Compat;
  55         121182  
  55         1970  
10 55     55   38514 use Storable qw(dclone);
  55         181806  
  55         4182  
11 55     55   14673 use Test::Builder;
  55         1176718  
  55         1639  
12 55     55   29602 use Test::Class::MethodInfo;
  55         141  
  55         1545  
13 55     55   29435 use Try::Tiny;
  55         116927  
  55         4240  
14              
15             our $VERSION = '0.52';
16              
17             my $Check_block_has_run;
18             {
19 55     55   421 no warnings 'void';
  55         136  
  55         3344  
20 54     54   402037 CHECK { $Check_block_has_run = 1 }
21             }
22              
23 55     55   329 use constant NO_PLAN => "no_plan";
  55         123  
  55         3050  
24 55     55   356 use constant SETUP => "setup";
  55         127  
  55         2913  
25 55     55   338 use constant TEST => "test";
  55         115  
  55         2627  
26 55     55   337 use constant TEARDOWN => "teardown";
  55         113  
  55         2717  
27 55     55   353 use constant STARTUP => "startup";
  55         208  
  55         2920  
28 55     55   358 use constant SHUTDOWN => "shutdown";
  55         136  
  55         38629  
29              
30              
31             our $Current_method = undef;
32 33     33 1 226 sub current_method { $Current_method }
33              
34              
35             my $Builder = Test::Builder->new;
36 6     6 1 167 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   3026 my $self = shift;
47 64         2861 delete $_Test{ $self };
48             }
49              
50             sub _test_info {
51 996     996   1332 my $self = shift;
52 996 100       3403 return ref($self) ? $_Test{$self} : $Tests;
53             }
54              
55             sub _method_info {
56 358     358   664 my ($self, $class, $method) = @_;
57 358         572 return( _test_info($self)->{$class}->{$method} );
58             }
59              
60             sub _methods_of_class {
61 638     638   1354 my ( $self, $class ) = @_;
62 638 100       995 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         909 return values %{ $test_info->{$class} };
  637         1821  
66             }
67              
68             sub _parse_attribute_args {
69 131   100 131   373 my $args = shift || '';
70 131         205 my $num_tests;
71             my $type;
72 131         471 $args =~ s/\s+//sg;
73 131         427 foreach my $arg (split /=>/, $args) {
74 155 100       531 if (Test::Class::MethodInfo->is_num_tests($arg)) {
    100          
75 103         231 $num_tests = $arg;
76             } elsif (Test::Class::MethodInfo->is_method_type($arg)) {
77 51         121 $type = $arg;
78             } else {
79 1         32 die 'bad attribute args';
80             }
81             }
82 130         586 return( $type, $num_tests );
83             }
84              
85             sub _is_public_method {
86 131     131   270 my ($class, $name) = @_;
87 131         184 my @parents = @{mro::get_linear_isa($class)};
  131         508  
88 131         228 shift @parents;
89 131         250 foreach my $parent_class ( @parents ) {
90 132 100       1107 return unless $parent_class->can( $name );
91 6 100       17 return if _method_info( $class, $parent_class, $name );
92             }
93 1         18 return 1;
94             }
95              
96             sub Test : ATTR(CODE,RAWDATA) {
97 132     132 1 27619 my ($class, $symbol, $code_ref, $attr, $args) = @_;
98 132 100       410 if ($symbol eq "ANON") {
99 1         61 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         188 my $name = *{$symbol}{NAME};
  131         267  
102 131 100       325 warn "overriding public method $name with a test method in $class\n"
103             if _is_public_method( $class, $name );
104 131 100       258 eval { $class->add_testinfo($name, _parse_attribute_args($args)) }
  131         293  
105             || warn "bad test definition '$args' in $class->$name\n";
106             }
107 55     55   470 }
  55         760  
  55         483  
108              
109             sub Tests : ATTR(CODE,RAWDATA) {
110 16     16 1 3711 my ($class, $symbol, $code_ref, $attr, $args) = @_;
111 16   100     65 $args ||= 'no_plan';
112 16         35 Test( $class, $symbol, $code_ref, $attr, $args );
113 55     55   28709 }
  55         160  
  55         223  
114              
115             sub add_testinfo {
116 130     130 1 311 my($class, $name, $type, $num_tests) = @_;
117 130         398 $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   967 my $self = shift;
126 677 100       1624 return ref $self ? ref $self : $self;
127             }
128              
129             sub new {
130 63     63 1 1957 my $proto = shift;
131 63         268 my $class = _class_of( $proto );
132 63 100       242 $proto = {} unless ref($proto);
133 63         242 my $self = bless {%$proto, @_}, $class;
134 63         8117 $_Test{$self} = dclone($Tests);
135 63         338 return($self);
136             }
137              
138             sub _get_methods {
139 322     322   747 my ( $self, @types ) = @_;
140 322         638 my $test_class = _class_of( $self );
141              
142 322   100     1195 my $test_method_regexp = $ENV{ TEST_METHOD } || '.*';
143 322         502 my $method_regexp = eval { qr/\A$test_method_regexp\z/ };
  322         2082  
144 322 100       778 die "TEST_METHOD ($test_method_regexp) is not a valid regexp: $@" if $@;
145              
146 321         488 my %methods = ();
147 321         476 foreach my $class ( @{mro::get_linear_isa( $test_class )} ) {
  321         965  
148             FILTER:
149 638         1303 foreach my $info ( _methods_of_class( $self, $class ) ) {
150 807         1938 my $name = $info->name;
151              
152 807 100       1715 if ( $info->type eq TEST ) {
153             # determine if method is filtered, true if *any* filter
154             # returns false.
155 475         805 foreach my $filter ( @Filters ) {
156 90 100       208 next FILTER unless $filter->( $class, $name );
157             }
158             }
159              
160 773         1292 foreach my $type ( @types ) {
161 907 100       1632 if ( $info->is_type( $type ) ) {
162 184 100 100     1345 $methods{ $name } = 1
163             unless $type eq TEST && $name !~ $method_regexp;
164             }
165             }
166             }
167             }
168              
169 320         1031 my @methods = sort keys %methods;
170 320         1247 return @methods;
171             }
172              
173             sub _num_expected_tests {
174 47     47   76 my $self = shift;
175 47 100       102 if (my $reason = $self->SKIP_CLASS ) {
176 2 100       7 return $reason eq "1" ? 0 : 1;
177             };
178 45         105 my @test_methods = _get_methods($self, TEST);
179 45 100       146 return 0 unless @test_methods;
180 22         71 my @startup_shutdown_methods =
181             _get_methods($self, STARTUP, SHUTDOWN);
182 22         75 my $num_startup_shutdown_methods =
183             _total_num_tests($self, @startup_shutdown_methods);
184 22 100       80 return(NO_PLAN) if $num_startup_shutdown_methods eq NO_PLAN;
185 20         48 my @fixture_methods = _get_methods($self, SETUP, TEARDOWN);
186 20         54 my $num_fixture_tests = _total_num_tests($self, @fixture_methods);
187 20 100       64 return(NO_PLAN) if $num_fixture_tests eq NO_PLAN;
188 18         44 my $num_tests = _total_num_tests($self, @test_methods);
189 18 100       60 return(NO_PLAN) if $num_tests eq NO_PLAN;
190 14         55 return($num_startup_shutdown_methods + $num_tests + @test_methods * $num_fixture_tests);
191             }
192              
193             sub expected_tests {
194 23     23 1 334 my $total = 0;
195 23         60 foreach my $test (@_) {
196 53 100 100     110 if ( _isa_class( __PACKAGE__, $test ) ) {
    100          
197 47         112 my $n = _num_expected_tests($test);
198 47 100       134 return NO_PLAN if $n eq NO_PLAN;
199 39         75 $total += $n;
200             } elsif ( defined $test && $test =~ m/^\d+$/ ) {
201 4         14 $total += $test;
202             } else {
203 2 100       14 $test = 'undef' unless defined $test;
204 1         33 croak "$test is not a Test::Class or an integer";
205             }
206             }
207 13         45 return $total;
208             }
209              
210             sub _total_num_tests {
211 292     292   606 my ($self, @methods) = @_;
212 292         527 my $class = _class_of( $self );
213 292         447 my $total_num_tests = 0;
214 292         487 foreach my $method (@methods) {
215 293         394 foreach my $class (@{mro::get_linear_isa($class)}) {
  293         866  
216 332         591 my $info = _method_info($self, $class, $method);
217 332 100       750 next unless $info;
218 301         829 my $num_tests = $info->num_tests;
219 301 100       725 return(NO_PLAN) if ($num_tests eq NO_PLAN);
220 277         456 $total_num_tests += $num_tests;
221 277 100       891 last unless $num_tests =~ m/^\+/
222             }
223             }
224 268         892 return($total_num_tests);
225             }
226              
227             sub _has_no_tests {
228 116     116   324 my ( $self, $method ) = @_;
229 116         282 return _total_num_tests( $self, $method ) eq '0';
230             }
231              
232             sub _all_ok_from {
233 114     114   231 my ($self, $start_test) = @_;
234              
235             # The Test::Builder 1.5 way to do it
236 114 50       609 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         304 my $current_test = $Builder->current_test;
242 114 100       12142 return(1) if $start_test == $current_test;
243 109         466 my @results = ($Builder->summary)[$start_test .. $current_test-1];
244 109 100       13102 foreach my $result (@results) { return(0) unless $result }
  140         487  
245 94         820 return(1);
246             }
247             }
248              
249             sub _exception_failure {
250 9     9   28 my ($self, $method, $exception, $tests) = @_;
251 9         21 local $Test::Builder::Level = 3;
252 9         17 my $message = $method;
253 9 100 100     55 $message .= " (for test method '$Current_method')"
254             if defined $Current_method && $method ne $Current_method;
255 9         35 _show_header($self, @$tests);
256 9         963 chomp $exception;
257 9         66 $Builder->ok(0, "$message died ($exception)");
258 9         32 _threw_exception( $self, $method => 1 );
259             }
260              
261             my %threw_exception;
262             sub _threw_exception {
263 223     223   464 my ( $self, $method, $optional_value) = @_;
264 223         377 my $class = ref( $self );
265 223 100       586 $threw_exception{ $class }{ $method } = $optional_value
266             if defined $optional_value;
267 223         702 return $threw_exception{ $class }{ $method };
268             }
269              
270             sub _run_method {
271 116     116   286 my ($self, $method, $tests) = @_;
272 116         315 _threw_exception( $self, $method => 0 );
273 116         333 my $num_start = $Builder->current_test;
274 116         12734 my $skip_reason;
275 116         248 my $original_ok = \&Test::Builder::ok;
276 55     55   89921 no warnings;
  55         145  
  55         98421  
277             local *Test::Builder::ok = sub {
278 146     146   14668 my ($builder, $test, $description) = @_;
279 146         311 local $Test::Builder::Level = $Test::Builder::Level+1;
280 146 100       394 unless ( defined($description) ) {
281 22         74 $description = $self->current_method;
282 22         64 $description =~ tr/_/ /;
283             }
284 146         403 my $is_ok = $original_ok->($builder, $test, $description);
285 146 100       57119 unless ( $is_ok ) {
286 20         53 my $class = ref $self;
287 20         102 $Builder->diag( " (in $class->$method)" );
288             }
289 146         5125 return $is_ok;
290 116         805 };
291              
292 116         223 my $exception;
293 116     42   724 $skip_reason = try { $self->$method } catch { $exception = $_; undef };
  116         6611  
  9         411  
  9         36  
294 114 100       3533 $skip_reason = $method unless $skip_reason;
295              
296 114         371 my $num_done = $Builder->current_test - $num_start;
297 114         13554 my $num_expected = _total_num_tests($self, $method);
298 114 100       314 $num_expected = $num_done if $num_expected eq NO_PLAN;
299 114 100       302 if ($num_done == $num_expected) {
    100          
300 101 100       252 _exception_failure($self, $method, $exception, $tests)
301             if $exception;
302             } elsif ($num_done > $num_expected) {
303 5         13 local $Test::Builder::Level = $Test::Builder::Level+1;
304 5         8 my $class = ref $self;
305 5 100       24 if ($self->fail_if_returned_late) {
306 2         16 $Builder->ok(0, "expected $num_expected test(s) in $class\::$method, $num_done completed");
307             } else {
308 3         22 $Builder->diag("expected $num_expected test(s) in $class\::$method, $num_done completed\n");
309             }
310             } else {
311 8         39 until (($Builder->current_test - $num_start) >= $num_expected) {
312 10 100       1629 if ($exception) {
313 4         16 _exception_failure($self, $method, $exception, $tests);
314 4         7 $skip_reason = "$method died";
315 4         18 $exception = '';
316             } else {
317 6         13 local $Test::Builder::Level = $Test::Builder::Level+1;
318 6 100       24 if ($self->fail_if_returned_early) {
319 3         12 my $class = ref $self;
320 3         16 $Builder->ok(0, "($class\::$method returned before plan complete)");
321             } else {
322 3         12 $Builder->skip( $skip_reason );
323             }
324             }
325             }
326             }
327 114         2676 return(_all_ok_from($self, $num_start));
328             }
329              
330 3     3 1 8 sub fail_if_returned_early { 0 }
331 3     3 1 9 sub fail_if_returned_late { 0 }
332              
333             sub _show_header {
334 120     120   290 my ($self, @tests) = @_;
335 120 100       444 return if $Builder->has_plan;
336 10         1948 my $num_tests = Test::Class->expected_tests(@tests);
337 10 100       38 if ($num_tests eq NO_PLAN) {
338 1         5 $Builder->no_plan;
339             } else {
340 9         60 $Builder->expected_tests($num_tests);
341             }
342             }
343              
344             my %SKIP_THIS_CLASS = ();
345              
346             sub SKIP_CLASS {
347 116     116 1 453 my $class = shift;
348 116 100       296 $SKIP_THIS_CLASS{ $class } = shift if @_;
349 116         401 return $SKIP_THIS_CLASS{ $class };
350             }
351              
352             sub _isa_class {
353 143     143   306 my ( $class, $object_or_class ) = @_;
354 143 100       635 return unless defined $object_or_class;
355 142 50       341 return if $object_or_class eq 'Contextual::Return::Value';
356 142         219 return eval {
357 142 100       1670 $object_or_class->isa( $class ) and $object_or_class->can( 'runtests' )
358             };
359             }
360              
361             sub _test_classes {
362 41     41   316 my $class = shift;
363 41         74 return( @{mro::get_isarev($class)}, $class );
  41         288  
364             }
365              
366             sub runtests {
367 49 100   49 1 60483 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         160 my @tests = @_;
370 48 100 100     349 if (@tests == 1 && !ref($tests[0])) {
371 39         94 my $base_class = shift @tests;
372 39         145 @tests = _test_classes( $base_class );
373             }
374 48         118 my $all_passed = 1;
375 48         143 TEST_OBJECT: foreach my $t (@tests) {
376             # SHOULD ALSO ALLOW NO_PLAN
377 67 100       729 next if $t =~ m/^\d+$/;
378 64 100       222 croak "$t is not Test::Class or integer"
379             unless _isa_class( __PACKAGE__, $t );
380 63 100       339 if (my $reason = $t->SKIP_CLASS) {
381 3         11 _show_header($t, @tests);
382 3 100       775 $Builder->skip( $reason ) unless $reason eq "1";
383             } else {
384 60 100       352 $t = $t->new unless ref($t);
385 60         229 my @test_methods = _get_methods($t, TEST);
386 58 100       203 if ( @test_methods ) {
387 45         130 foreach my $method (_get_methods($t, STARTUP)) {
388 8 100       30 _show_header($t, @tests) unless _has_no_tests($t, $method);
389 8         2128 my $method_passed = _run_method($t, $method, \@tests);
390 8 100       25 $all_passed = 0 unless $method_passed;
391 8 100       30 next TEST_OBJECT unless $method_passed;
392             }
393 44         122 my $class = ref($t);
394 44         108 my @setup = _get_methods($t, SETUP);
395 44         122 my @teardown = _get_methods($t, TEARDOWN);
396 44         109 foreach my $test ( @test_methods ) {
397 64         201 local $Current_method = $test;
398 64 100       249 $Builder->diag("\n$class->$test") if $ENV{TEST_VERBOSE};
399 64         1133 my @methods_to_run = (@setup, $test, @teardown);
400 64         198 while ( my $method = shift @methods_to_run ) {
401 100 100       249 _show_header($t, @tests) unless _has_no_tests($t, $method);
402 100 100       13983 $all_passed = 0 unless _run_method($t, $method, \@tests);
403 98 100       254 if ( _threw_exception( $t, $method ) ) {
404 8 100       48 next if ($method eq $test);
405 2         8 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         836 last;
408             }
409             }
410             }
411 42         936 foreach my $method (_get_methods($t, SHUTDOWN)) {
412 8 100       27 _show_header($t, @tests) unless _has_no_tests($t, $method);
413 8 50       837 $all_passed = 0 unless _run_method($t, $method, \@tests);
414             }
415             }
416              
417             }
418             }
419 43         207 return($all_passed);
420             }
421              
422             sub _find_calling_test_class {
423 21     21   34 my $level = 0;
424 21         70 while (my $class = caller(++$level)) {
425 35 100       81 next if $class eq __PACKAGE__;
426 26 100       56 return $class if _isa_class( __PACKAGE__, $class );
427             }
428 1         12 return(undef);
429             }
430              
431             sub num_method_tests {
432 21     21 1 1346 my ($self, $method, $n) = @_;
433 21 100       45 my $class = _find_calling_test_class( $self )
434             or croak "not called in a Test::Class";
435 20 100       53 my $info = _method_info($self, $class, $method)
436             or croak "$method is not a test method of class $class";
437 19 100       66 $info->num_tests($n) if defined($n);
438 18         47 return( $info->num_tests );
439             }
440              
441             sub num_tests {
442 9     9 1 44 my $self = shift;
443 9 50       25 croak "num_tests need to be called within a test method"
444             unless defined $Current_method;
445 9         29 return( $self->num_method_tests( $Current_method, @_ ) );
446             }
447              
448             sub BAILOUT {
449 1     1 1 1612 my ($self, $reason) = @_;
450 1         4 $Builder->BAILOUT($reason);
451             }
452              
453             sub _last_test_if_exiting_immediately {
454 4 100   4   18 $Builder->expected_tests || $Builder->current_test+1
455             }
456              
457             sub FAIL_ALL {
458 3     3 1 4105 my ($self, $reason) = @_;
459 3         9 my $last_test = _last_test_if_exiting_immediately();
460 3 50       481 $Builder->expected_tests( $last_test ) unless $Builder->has_plan;
461 3         324 $Builder->ok(0, $reason) until $Builder->current_test >= $last_test;
462 3 50       3874 my $num_failed = $Builder->can("history")
463             ? $Builder->history->fail_count : grep( !$_, $Builder->summary );
464 3 50       683 exit( $num_failed < 254 ? $num_failed : 254 );
465             }
466              
467             sub SKIP_ALL {
468 2     2 1 785 my ($self, $reason) = @_;
469 2 100       7 $Builder->skip_all( $reason ) unless $Builder->has_plan;
470 1         119 my $last_test = _last_test_if_exiting_immediately();
471 1         125 $Builder->skip( $reason )
472             until $Builder->current_test >= $last_test;
473 1         1468 exit(0);
474             }
475              
476             sub add_filter {
477 6     6 1 799 my ( $class, $cb ) = @_;
478              
479 6 100       30 if ( not ref $cb eq 'CODE' ) {
480 1         187 croak "Filter isn't a code-ref"
481             }
482              
483 5         18 push @Filters, $cb;
484             }
485              
486             1;
487              
488             __END__