File Coverage

blib/lib/Test/Class.pm
Criterion Covered Total %
statement 306 307 99.6
branch 149 156 95.5
condition 18 18 100.0
subroutine 55 55 100.0
pod 16 16 100.0
total 544 552 98.5


line stmt bran cond sub pod time code
1 54     54   571388 use strict;
  54         91  
  54         2401  
2 54     54   258 use warnings;
  54         81  
  54         1582  
3 54     54   1144 use 5.006;
  54         148  
  54         2428  
4              
5             package Test::Class;
6              
7 54     54   31470 use Attribute::Handlers;
  54         236851  
  54         299  
8 54     54   1882 use Carp;
  54         79  
  54         3437  
9 54     54   28083 use MRO::Compat;
  54         126890  
  54         1761  
10 54     54   35295 use Storable qw(dclone);
  54         149270  
  54         4067  
11 54     54   14360 use Test::Builder;
  54         178512  
  54         1361  
12 54     54   21755 use Test::Class::MethodInfo;
  54         96  
  54         2290  
13              
14             our $VERSION = '0.48';
15              
16 54     54   297 use constant NO_PLAN => "no_plan";
  54         69  
  54         3951  
17 54     54   258 use constant SETUP => "setup";
  54         72  
  54         2284  
18 54     54   235 use constant TEST => "test";
  54         69  
  54         2047  
19 54     54   228 use constant TEARDOWN => "teardown";
  54         67  
  54         2059  
20 54     54   243 use constant STARTUP => "startup";
  54         65  
  54         2027  
21 54     54   236 use constant SHUTDOWN => "shutdown";
  54         72  
  54         30608  
22              
23              
24             our $Current_method = undef;
25 19     19 1 129 sub current_method { $Current_method }
26              
27              
28             my $Builder = Test::Builder->new;
29 6     6 1 98 sub builder { $Builder }
30              
31              
32             my $Tests = {};
33             my @Filters = ();
34              
35              
36             my %_Test; # inside-out object field indexed on $self
37              
38             sub DESTROY {
39 60     60   2047 my $self = shift;
40 60         1116 delete $_Test{ $self };
41             }
42              
43             sub _test_info {
44 936     936   858 my $self = shift;
45 936 100       3340 return ref($self) ? $_Test{$self} : $Tests;
46             }
47              
48             sub _method_info {
49 338     338   430 my ($self, $class, $method) = @_;
50 338         675 return( _test_info($self)->{$class}->{$method} );
51             }
52              
53             sub _methods_of_class {
54 598     598   631 my ( $self, $class ) = @_;
55 598 100       760 my $test_info = _test_info($self)
56             or die "Test::Class internals seem confused. Did you override "
57             . "new() in a sub-class or via multiple inheritance?\n";
58 597         540 return values %{ $test_info->{$class} };
  597         1697  
59             }
60              
61             sub _parse_attribute_args {
62 121   100 121   417 my $args = shift || '';
63 121         122 my $num_tests;
64             my $type;
65 121         494 $args =~ s/\s+//sg;
66 121         395 foreach my $arg (split /=>/, $args) {
67 145 100       492 if (Test::Class::MethodInfo->is_num_tests($arg)) {
    100          
68 93         278 $num_tests = $arg;
69             } elsif (Test::Class::MethodInfo->is_method_type($arg)) {
70 51         108 $type = $arg;
71             } else {
72 1         26 die 'bad attribute args';
73             }
74             }
75 120         543 return( $type, $num_tests );
76             }
77              
78             sub _is_public_method {
79 121     121   168 my ($class, $name) = @_;
80 121         120 my @parents = @{mro::get_linear_isa($class)};
  121         683  
81 121         172 shift @parents;
82 121         211 foreach my $parent_class ( @parents ) {
83 122 100       1212 return unless $parent_class->can( $name );
84 6 100       17 return if _method_info( $class, $parent_class, $name );
85             }
86 1         20 return 1;
87             }
88              
89             sub Test : ATTR(CODE,RAWDATA,BEGIN) {
90 122     122 1 176291 my ($class, $symbol, $code_ref, $attr, $args) = @_;
91 122 100       367 if ($symbol eq "ANON") {
92 1         55 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";
93             } else {
94 121         114 my $name = *{$symbol}{NAME};
  121         226  
95 121 100       269 warn "overriding public method $name with a test method in $class\n"
96             if _is_public_method( $class, $name );
97 121 100       180 eval { $class->add_testinfo($name, _parse_attribute_args($args)) }
  121         271  
98             || warn "bad test definition '$args' in $class->$name\n";
99             }
100 54     54   338 }
  54         75  
  54         378  
101              
102             sub Tests : ATTR(CODE,RAWDATA,BEGIN) {
103 6     6 1 2902 my ($class, $symbol, $code_ref, $attr, $args) = @_;
104 6   100     33 $args ||= 'no_plan';
105 6         16 Test( $class, $symbol, $code_ref, $attr, $args );
106 54     54   20516 }
  54         91  
  54         212  
107              
108             sub add_testinfo {
109 120     120 1 261 my($class, $name, $type, $num_tests) = @_;
110 120         373 $Tests->{$class}->{$name} = Test::Class::MethodInfo->new(
111             name => $name,
112             num_tests => $num_tests,
113             type => $type,
114             );
115             }
116              
117             sub _class_of {
118 633     633   650 my $self = shift;
119 633 100       1522 return ref $self ? ref $self : $self;
120             }
121              
122             sub new {
123 59     59 1 1136 my $proto = shift;
124 59         170 my $class = _class_of( $proto );
125 59 100       271 $proto = {} unless ref($proto);
126 59         246 my $self = bless {%$proto, @_}, $class;
127 59         6630 $_Test{$self} = dclone($Tests);
128 59         222 return($self);
129             }
130              
131             sub _get_methods {
132 302     302   539 my ( $self, @types ) = @_;
133 302         456 my $test_class = _class_of( $self );
134            
135 302   100     1236 my $test_method_regexp = $ENV{ TEST_METHOD } || '.*';
136 302         349 my $method_regexp = eval { qr/\A$test_method_regexp\z/ };
  302         2311  
137 302 100       616 die "TEST_METHOD ($test_method_regexp) is not a valid regexp: $@" if $@;
138            
139 301         429 my %methods = ();
140 301         397 foreach my $class ( @{mro::get_linear_isa( $test_class )} ) {
  301         912  
141             FILTER:
142 598         943 foreach my $info ( _methods_of_class( $self, $class ) ) {
143 757         1638 my $name = $info->name;
144              
145 757 100       1370 if ( $info->type eq TEST ) {
146             # determine if method is filtered, true if *any* filter
147             # returns false.
148 425         582 foreach my $filter ( @Filters ) {
149 90 100       165 next FILTER unless $filter->( $class, $name );
150             }
151             }
152              
153 723         965 foreach my $type ( @types ) {
154 857 100       1478 if ( $info->is_type( $type ) ) {
155 174 100 100     1447 $methods{ $name } = 1
156             unless $type eq TEST && $name !~ $method_regexp;
157             }
158             }
159             }
160             }
161              
162 300         755 my @methods = sort keys %methods;
163 300         1170 return @methods;
164             }
165              
166             sub _num_expected_tests {
167 47     47   52 my $self = shift;
168 47 100       105 if (my $reason = $self->SKIP_CLASS ) {
169 2 100       7 return $reason eq "1" ? 0 : 1;
170             };
171 45         84 my @test_methods = _get_methods($self, TEST);
172 45 100       174 return 0 unless @test_methods;
173 22         54 my @startup_shutdown_methods =
174             _get_methods($self, STARTUP, SHUTDOWN);
175 22         58 my $num_startup_shutdown_methods =
176             _total_num_tests($self, @startup_shutdown_methods);
177 22 100       62 return(NO_PLAN) if $num_startup_shutdown_methods eq NO_PLAN;
178 20         42 my @fixture_methods = _get_methods($self, SETUP, TEARDOWN);
179 20         45 my $num_fixture_tests = _total_num_tests($self, @fixture_methods);
180 20 100       75 return(NO_PLAN) if $num_fixture_tests eq NO_PLAN;
181 18         41 my $num_tests = _total_num_tests($self, @test_methods);
182 18 100       63 return(NO_PLAN) if $num_tests eq NO_PLAN;
183 14         49 return($num_startup_shutdown_methods + $num_tests + @test_methods * $num_fixture_tests);
184             }
185              
186             sub expected_tests {
187 23     23 1 221 my $total = 0;
188 23         53 foreach my $test (@_) {
189 53 100 100     85 if ( _isa_class( __PACKAGE__, $test ) ) {
    100          
190 47         95 my $n = _num_expected_tests($test);
191 47 100       136 return NO_PLAN if $n eq NO_PLAN;
192 39         75 $total += $n;
193             } elsif ( defined $test && $test =~ m/^\d+$/ ) {
194 4         12 $total += $test;
195             } else {
196 2 100       13 $test = 'undef' unless defined $test;
197 1         21 croak "$test is not a Test::Class or an integer";
198             }
199             }
200 13         38 return $total;
201             }
202              
203             sub _total_num_tests {
204 272     272   393 my ($self, @methods) = @_;
205 272         382 my $class = _class_of( $self );
206 272         331 my $total_num_tests = 0;
207 272         382 foreach my $method (@methods) {
208 273         350 foreach my $class (@{mro::get_linear_isa($class)}) {
  273         900  
209 312         505 my $info = _method_info($self, $class, $method);
210 312 100       829 next unless $info;
211 281         767 my $num_tests = $info->num_tests;
212 281 100       596 return(NO_PLAN) if ($num_tests eq NO_PLAN);
213 265         443 $total_num_tests += $num_tests;
214 265 100       1019 last unless $num_tests =~ m/^\+/
215             }
216             }
217 256         737 return($total_num_tests);
218             }
219              
220             sub _has_no_tests {
221 106     106   154 my ( $self, $method ) = @_;
222 106         203 return _total_num_tests( $self, $method ) eq '0';
223             }
224              
225             sub _all_ok_from {
226 104     104   128 my ($self, $start_test) = @_;
227              
228             # The Test::Builder 1.5 way to do it
229 104 50       786 if( $Builder->can("history") ) {
230 0         0 return $Builder->history->can_succeed;
231             }
232             # The Test::Builder 0.x way to do it
233             else {
234 104         255 my $current_test = $Builder->current_test;
235 104 100       780 return(1) if $start_test == $current_test;
236 99         391 my @results = ($Builder->summary)[$start_test .. $current_test-1];
237 99 100       1163 foreach my $result (@results) { return(0) unless $result }
  122         447  
238 87         752 return(1);
239             }
240             }
241              
242             sub _exception_failure {
243 9     9   19 my ($self, $method, $exception, $tests) = @_;
244 9         16 local $Test::Builder::Level = 3;
245 9         15 my $message = $method;
246 9 100 100     55 $message .= " (for test method '$Current_method')"
247             if defined $Current_method && $method ne $Current_method;
248 9         27 _show_header($self, @$tests);
249 9         64 chomp $exception;
250 9         44 $Builder->ok(0, "$message died ($exception)");
251 9         25 _threw_exception( $self, $method => 1 );
252             }
253              
254             my %threw_exception;
255             sub _threw_exception {
256 203     203   263 my ( $self, $method, $optional_value) = @_;
257 203         250 my $class = ref( $self );
258 203 100       480 $threw_exception{ $class }{ $method } = $optional_value
259             if defined $optional_value;
260 203         621 return $threw_exception{ $class }{ $method };
261             }
262              
263             sub _run_method {
264 106     106   154 my ($self, $method, $tests) = @_;
265 106         216 _threw_exception( $self, $method => 0 );
266 106         311 my $num_start = $Builder->current_test;
267 106         672 my $skip_reason;
268 106         167 my $original_ok = \&Test::Builder::ok;
269 54     54   74970 no warnings;
  54         118  
  54         79287  
270             local *Test::Builder::ok = sub {
271 129     129   6520 my ($builder, $test, $description) = @_;
272 129         193 local $Test::Builder::Level = $Test::Builder::Level+1;
273 129 100       298 unless ( defined($description) ) {
274 8         37 $description = $self->current_method;
275 8         21 $description =~ tr/_/ /;
276             }
277 129         290 my $is_ok = $original_ok->($builder, $test, $description);
278 129 100       38462 unless ( $is_ok ) {
279 17         40 my $class = ref $self;
280 17         80 $Builder->diag( " (in $class->$method)" );
281             }
282 129         1483 return $is_ok;
283 106         662 };
284 106         157 $skip_reason = eval {$self->$method};
  106         422  
285 104 100       579 $skip_reason = $method unless $skip_reason;
286 104         148 my $exception = $@;
287 104         241 my $num_done = $Builder->current_test - $num_start;
288 104         748 my $num_expected = _total_num_tests($self, $method);
289 104 100       256 $num_expected = $num_done if $num_expected eq NO_PLAN;
290 104 100       272 if ($num_done == $num_expected) {
    100          
291 97 100       210 _exception_failure($self, $method, $exception, $tests)
292             if $exception;
293             } elsif ($num_done > $num_expected) {
294 1         3 my $class = ref $self;
295 1         5 $Builder->diag("expected $num_expected test(s) in $class\::$method, $num_done completed\n");
296             } else {
297 6         18 until (($Builder->current_test - $num_start) >= $num_expected) {
298 8 100       308 if ($exception) {
299 4         14 _exception_failure($self, $method, $exception, $tests);
300 4         7 $skip_reason = "$method died";
301 4         12 $exception = '';
302             } else {
303 4 100       16 if ($self->fail_if_returned_early) {
304 2         7 my $class = ref $self;
305 2         9 $Builder->ok(0, "($class\::$method returned before plan complete)");
306             } else {
307 2         7 $Builder->skip( $skip_reason );
308             }
309             }
310             }
311             }
312 104         471 return(_all_ok_from($self, $num_start));
313             }
314              
315 2     2 1 7 sub fail_if_returned_early { 0 }
316              
317             sub _show_header {
318 110     110   179 my ($self, @tests) = @_;
319 110 100       367 return if $Builder->has_plan;
320 10         173 my $num_tests = Test::Class->expected_tests(@tests);
321 10 100       37 if ($num_tests eq NO_PLAN) {
322 1         4 $Builder->no_plan;
323             } else {
324 9         40 $Builder->expected_tests($num_tests);
325             }
326             }
327              
328             my %SKIP_THIS_CLASS = ();
329              
330             sub SKIP_CLASS {
331 112     112 1 241 my $class = shift;
332 112 100       288 $SKIP_THIS_CLASS{ $class } = shift if @_;
333 112         380 return $SKIP_THIS_CLASS{ $class };
334             }
335              
336             sub _isa_class {
337 139     139   199 my ( $class, $object_or_class ) = @_;
338 139 100       315 return unless defined $object_or_class;
339 138 50       322 return if $object_or_class eq 'Contextual::Return::Value';
340 138         189 return eval {
341 138 100       1837 $object_or_class->isa( $class ) and $object_or_class->can( 'runtests' )
342             };
343             }
344              
345             sub _test_classes {
346 37     37   137 my $class = shift;
347 37         55 return( @{mro::get_isarev($class)}, $class );
  37         287  
348             }
349              
350             sub runtests {
351 44     44 1 17658 my @tests = @_;
352 44 100 100     438 if (@tests == 1 && !ref($tests[0])) {
353 35         79 my $base_class = shift @tests;
354 35         135 @tests = _test_classes( $base_class );
355             }
356 44         100 my $all_passed = 1;
357 44         112 TEST_OBJECT: foreach my $t (@tests) {
358             # SHOULD ALSO ALLOW NO_PLAN
359 63 100       485 next if $t =~ m/^\d+$/;
360 60 100       191 croak "$t is not Test::Class or integer"
361             unless _isa_class( __PACKAGE__, $t );
362 59 100       318 if (my $reason = $t->SKIP_CLASS) {
363 3         8 _show_header($t, @tests);
364 3 100       100 $Builder->skip( $reason ) unless $reason eq "1";
365             } else {
366 56 100       388 $t = $t->new unless ref($t);
367 56         197 my @test_methods = _get_methods($t, TEST);
368 54 100       187 if ( @test_methods ) {
369 41         181 foreach my $method (_get_methods($t, STARTUP)) {
370 8 100       36 _show_header($t, @tests) unless _has_no_tests($t, $method);
371 8         457 my $method_passed = _run_method($t, $method, \@tests);
372 8 100       23 $all_passed = 0 unless $method_passed;
373 8 100       23 next TEST_OBJECT unless $method_passed;
374             }
375 40         96 my $class = ref($t);
376 40         122 my @setup = _get_methods($t, SETUP);
377 40         135 my @teardown = _get_methods($t, TEARDOWN);
378 40         105 foreach my $test ( @test_methods ) {
379 54         102 local $Current_method = $test;
380 54 100       203 $Builder->diag("\n$class->$test") if $ENV{TEST_VERBOSE};
381 54         433 my @methods_to_run = (@setup, $test, @teardown);
382 54         171 while ( my $method = shift @methods_to_run ) {
383 90 100       247 _show_header($t, @tests) unless _has_no_tests($t, $method);
384 90 100       1585 $all_passed = 0 unless _run_method($t, $method, \@tests);
385 88 100       187 if ( _threw_exception( $t, $method ) ) {
386 8 100       53 next if ($method eq $test);
387 2         5 my $num_to_skip = _total_num_tests($t, @methods_to_run);
388 2         14 $Builder->skip( "$method died" ) for ( 1 .. $num_to_skip );
389 2         188 last;
390             }
391             }
392             }
393 38         126 foreach my $method (_get_methods($t, SHUTDOWN)) {
394 8 100       19 _show_header($t, @tests) unless _has_no_tests($t, $method);
395 8 50       74 $all_passed = 0 unless _run_method($t, $method, \@tests);
396             }
397             }
398            
399             }
400             }
401 39         280 return($all_passed);
402             }
403              
404             sub _find_calling_test_class {
405 21     21   23 my $level = 0;
406 21         63 while (my $class = caller(++$level)) {
407 35 100       80 next if $class eq __PACKAGE__;
408 26 100       40 return $class if _isa_class( __PACKAGE__, $class );
409             }
410 1         14 return(undef);
411             }
412              
413             sub num_method_tests {
414 21     21 1 1212 my ($self, $method, $n) = @_;
415 21 100       41 my $class = _find_calling_test_class( $self )
416             or croak "not called in a Test::Class";
417 20 100       48 my $info = _method_info($self, $class, $method)
418             or croak "$method is not a test method of class $class";
419 19 100       57 $info->num_tests($n) if defined($n);
420 18         46 return( $info->num_tests );
421             }
422              
423             sub num_tests {
424 9     9 1 37 my $self = shift;
425 9 50       21 croak "num_tests need to be called within a test method"
426             unless defined $Current_method;
427 9         26 return( $self->num_method_tests( $Current_method, @_ ) );
428             }
429              
430             sub BAILOUT {
431 1     1 1 195 my ($self, $reason) = @_;
432 1         5 $Builder->BAILOUT($reason);
433             }
434              
435             sub _last_test_if_exiting_immediately {
436 4 100   4   17 $Builder->expected_tests || $Builder->current_test+1
437             }
438              
439             sub FAIL_ALL {
440 3     3 1 1181 my ($self, $reason) = @_;
441 3         11 my $last_test = _last_test_if_exiting_immediately();
442 3 50       40 $Builder->expected_tests( $last_test ) unless $Builder->has_plan;
443 3         24 $Builder->ok(0, $reason) until $Builder->current_test >= $last_test;
444 3 50       1558 my $num_failed = $Builder->can("history")
445             ? $Builder->history->fail_count : grep( !$_, $Builder->summary );
446 3 50       101 exit( $num_failed < 254 ? $num_failed : 254 );
447             }
448              
449             sub SKIP_ALL {
450 2     2 1 312 my ($self, $reason) = @_;
451 2 100       8 $Builder->skip_all( $reason ) unless $Builder->has_plan;
452 1         7 my $last_test = _last_test_if_exiting_immediately();
453 1         11 $Builder->skip( $reason )
454             until $Builder->current_test >= $last_test;
455 1         255 exit(0);
456             }
457              
458             sub add_filter {
459 6     6 1 185 my ( $class, $cb ) = @_;
460              
461 6 100       25 if ( not ref $cb eq 'CODE' ) {
462 1         217 croak "Filter isn't a code-ref"
463             }
464              
465 5         14 push @Filters, $cb;
466             }
467              
468             1;
469              
470             __END__