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   563076 use strict;
  54         98  
  54         1934  
2 54     54   242 use warnings;
  54         90  
  54         1388  
3 54     54   1116 use 5.006;
  54         142  
  54         2147  
4              
5             package Test::Class;
6              
7 54     54   30927 use Attribute::Handlers;
  54         233251  
  54         277  
8 54     54   1878 use Carp;
  54         97  
  54         3276  
9 54     54   27057 use MRO::Compat;
  54         126716  
  54         1743  
10 54     54   34348 use Storable qw(dclone);
  54         149069  
  54         3808  
11 54     54   13974 use Test::Builder;
  54         183144  
  54         1452  
12 54     54   21950 use Test::Class::MethodInfo;
  54         107  
  54         2191  
13              
14             our $VERSION = '0.49';
15              
16 54     54   332 use constant NO_PLAN => "no_plan";
  54         71  
  54         3732  
17 54     54   246 use constant SETUP => "setup";
  54         64  
  54         2015  
18 54     54   242 use constant TEST => "test";
  54         85  
  54         1946  
19 54     54   226 use constant TEARDOWN => "teardown";
  54         71  
  54         2020  
20 54     54   239 use constant STARTUP => "startup";
  54         75  
  54         2029  
21 54     54   239 use constant SHUTDOWN => "shutdown";
  54         69  
  54         29717  
22              
23              
24             our $Current_method = undef;
25 19     19 1 121 sub current_method { $Current_method }
26              
27              
28             my $Builder = Test::Builder->new;
29 6     6 1 70 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   2320 my $self = shift;
40 60         1064 delete $_Test{ $self };
41             }
42              
43             sub _test_info {
44 936     936   954 my $self = shift;
45 936 100       3348 return ref($self) ? $_Test{$self} : $Tests;
46             }
47              
48             sub _method_info {
49 338     338   410 my ($self, $class, $method) = @_;
50 338         690 return( _test_info($self)->{$class}->{$method} );
51             }
52              
53             sub _methods_of_class {
54 598     598   663 my ( $self, $class ) = @_;
55 598 100       893 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         572 return values %{ $test_info->{$class} };
  597         1708  
59             }
60              
61             sub _parse_attribute_args {
62 121   100 121   370 my $args = shift || '';
63 121         126 my $num_tests;
64             my $type;
65 121         413 $args =~ s/\s+//sg;
66 121         382 foreach my $arg (split /=>/, $args) {
67 145 100       517 if (Test::Class::MethodInfo->is_num_tests($arg)) {
    100          
68 93         209 $num_tests = $arg;
69             } elsif (Test::Class::MethodInfo->is_method_type($arg)) {
70 51         104 $type = $arg;
71             } else {
72 1         24 die 'bad attribute args';
73             }
74             }
75 120         522 return( $type, $num_tests );
76             }
77              
78             sub _is_public_method {
79 121     121   151 my ($class, $name) = @_;
80 121         127 my @parents = @{mro::get_linear_isa($class)};
  121         734  
81 121         173 shift @parents;
82 121         203 foreach my $parent_class ( @parents ) {
83 122 100       1220 return unless $parent_class->can( $name );
84 6 100       16 return if _method_info( $class, $parent_class, $name );
85             }
86 1         16 return 1;
87             }
88              
89             sub Test : ATTR(CODE,RAWDATA,BEGIN) {
90 122     122 1 178523 my ($class, $symbol, $code_ref, $attr, $args) = @_;
91 122 100       370 if ($symbol eq "ANON") {
92 1         52 warn "cannot test anonymous subs - you probably loaded a Test::Class too late. See 'A NOTE ON LOADING TEST CLASSES' in perldoc Test::Class for more details\n";
93             } else {
94 121         128 my $name = *{$symbol}{NAME};
  121         222  
95 121 100       270 warn "overriding public method $name with a test method in $class\n"
96             if _is_public_method( $class, $name );
97 121 100       187 eval { $class->add_testinfo($name, _parse_attribute_args($args)) }
  121         258  
98             || warn "bad test definition '$args' in $class->$name\n";
99             }
100 54     54   357 }
  54         63  
  54         372  
101              
102             sub Tests : ATTR(CODE,RAWDATA,BEGIN) {
103 6     6 1 2423 my ($class, $symbol, $code_ref, $attr, $args) = @_;
104 6   100     34 $args ||= 'no_plan';
105 6         15 Test( $class, $symbol, $code_ref, $attr, $args );
106 54     54   19697 }
  54         83  
  54         216  
107              
108             sub add_testinfo {
109 120     120 1 263 my($class, $name, $type, $num_tests) = @_;
110 120         534 $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   661 my $self = shift;
119 633 100       1453 return ref $self ? ref $self : $self;
120             }
121              
122             sub new {
123 59     59 1 1178 my $proto = shift;
124 59         172 my $class = _class_of( $proto );
125 59 100       252 $proto = {} unless ref($proto);
126 59         219 my $self = bless {%$proto, @_}, $class;
127 59         6234 $_Test{$self} = dclone($Tests);
128 59         257 return($self);
129             }
130              
131             sub _get_methods {
132 302     302   512 my ( $self, @types ) = @_;
133 302         439 my $test_class = _class_of( $self );
134            
135 302   100     1254 my $test_method_regexp = $ENV{ TEST_METHOD } || '.*';
136 302         325 my $method_regexp = eval { qr/\A$test_method_regexp\z/ };
  302         1997  
137 302 100       598 die "TEST_METHOD ($test_method_regexp) is not a valid regexp: $@" if $@;
138            
139 301         414 my %methods = ();
140 301         287 foreach my $class ( @{mro::get_linear_isa( $test_class )} ) {
  301         928  
141             FILTER:
142 598         921 foreach my $info ( _methods_of_class( $self, $class ) ) {
143 757         1664 my $name = $info->name;
144              
145 757 100       1368 if ( $info->type eq TEST ) {
146             # determine if method is filtered, true if *any* filter
147             # returns false.
148 425         562 foreach my $filter ( @Filters ) {
149 90 100       190 next FILTER unless $filter->( $class, $name );
150             }
151             }
152              
153 723         964 foreach my $type ( @types ) {
154 857 100       1525 if ( $info->is_type( $type ) ) {
155 174 100 100     1441 $methods{ $name } = 1
156             unless $type eq TEST && $name !~ $method_regexp;
157             }
158             }
159             }
160             }
161              
162 300         771 my @methods = sort keys %methods;
163 300         1209 return @methods;
164             }
165              
166             sub _num_expected_tests {
167 47     47   58 my $self = shift;
168 47 100       108 if (my $reason = $self->SKIP_CLASS ) {
169 2 100       6 return $reason eq "1" ? 0 : 1;
170             };
171 45         86 my @test_methods = _get_methods($self, TEST);
172 45 100       131 return 0 unless @test_methods;
173 22         55 my @startup_shutdown_methods =
174             _get_methods($self, STARTUP, SHUTDOWN);
175 22         55 my $num_startup_shutdown_methods =
176             _total_num_tests($self, @startup_shutdown_methods);
177 22 100       69 return(NO_PLAN) if $num_startup_shutdown_methods eq NO_PLAN;
178 20         43 my @fixture_methods = _get_methods($self, SETUP, TEARDOWN);
179 20         45 my $num_fixture_tests = _total_num_tests($self, @fixture_methods);
180 20 100       69 return(NO_PLAN) if $num_fixture_tests eq NO_PLAN;
181 18         45 my $num_tests = _total_num_tests($self, @test_methods);
182 18 100       61 return(NO_PLAN) if $num_tests eq NO_PLAN;
183 14         53 return($num_startup_shutdown_methods + $num_tests + @test_methods * $num_fixture_tests);
184             }
185              
186             sub expected_tests {
187 23     23 1 202 my $total = 0;
188 23         58 foreach my $test (@_) {
189 53 100 100     97 if ( _isa_class( __PACKAGE__, $test ) ) {
    100          
190 47         85 my $n = _num_expected_tests($test);
191 47 100       175 return NO_PLAN if $n eq NO_PLAN;
192 39         80 $total += $n;
193             } elsif ( defined $test && $test =~ m/^\d+$/ ) {
194 4         14 $total += $test;
195             } else {
196 2 100       14 $test = 'undef' unless defined $test;
197 1         21 croak "$test is not a Test::Class or an integer";
198             }
199             }
200 13         39 return $total;
201             }
202              
203             sub _total_num_tests {
204 272     272   424 my ($self, @methods) = @_;
205 272         407 my $class = _class_of( $self );
206 272         413 my $total_num_tests = 0;
207 272         395 foreach my $method (@methods) {
208 273         336 foreach my $class (@{mro::get_linear_isa($class)}) {
  273         1030  
209 312         554 my $info = _method_info($self, $class, $method);
210 312 100       787 next unless $info;
211 281         787 my $num_tests = $info->num_tests;
212 281 100       673 return(NO_PLAN) if ($num_tests eq NO_PLAN);
213 265         373 $total_num_tests += $num_tests;
214 265 100       1062 last unless $num_tests =~ m/^\+/
215             }
216             }
217 256         777 return($total_num_tests);
218             }
219              
220             sub _has_no_tests {
221 106     106   147 my ( $self, $method ) = @_;
222 106         225 return _total_num_tests( $self, $method ) eq '0';
223             }
224              
225             sub _all_ok_from {
226 104     104   144 my ($self, $start_test) = @_;
227              
228             # The Test::Builder 1.5 way to do it
229 104 50       800 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         257 my $current_test = $Builder->current_test;
235 104 100       791 return(1) if $start_test == $current_test;
236 99         414 my @results = ($Builder->summary)[$start_test .. $current_test-1];
237 99 100       1110 foreach my $result (@results) { return(0) unless $result }
  122         502  
238 87         824 return(1);
239             }
240             }
241              
242             sub _exception_failure {
243 9     9   15 my ($self, $method, $exception, $tests) = @_;
244 9         16 local $Test::Builder::Level = 3;
245 9         12 my $message = $method;
246 9 100 100     56 $message .= " (for test method '$Current_method')"
247             if defined $Current_method && $method ne $Current_method;
248 9         27 _show_header($self, @$tests);
249 9         62 chomp $exception;
250 9         40 $Builder->ok(0, "$message died ($exception)");
251 9         23 _threw_exception( $self, $method => 1 );
252             }
253              
254             my %threw_exception;
255             sub _threw_exception {
256 203     203   251 my ( $self, $method, $optional_value) = @_;
257 203         267 my $class = ref( $self );
258 203 100       496 $threw_exception{ $class }{ $method } = $optional_value
259             if defined $optional_value;
260 203         641 return $threw_exception{ $class }{ $method };
261             }
262              
263             sub _run_method {
264 106     106   146 my ($self, $method, $tests) = @_;
265 106         210 _threw_exception( $self, $method => 0 );
266 106         275 my $num_start = $Builder->current_test;
267 106         656 my $skip_reason;
268 106         170 my $original_ok = \&Test::Builder::ok;
269 54     54   76774 no warnings;
  54         110  
  54         78235  
270             local *Test::Builder::ok = sub {
271 129     129   6214 my ($builder, $test, $description) = @_;
272 129         211 local $Test::Builder::Level = $Test::Builder::Level+1;
273 129 100       307 unless ( defined($description) ) {
274 8         42 $description = $self->current_method;
275 8         19 $description =~ tr/_/ /;
276             }
277 129         297 my $is_ok = $original_ok->($builder, $test, $description);
278 129 100       37982 unless ( $is_ok ) {
279 17         31 my $class = ref $self;
280 17         75 $Builder->diag( " (in $class->$method)" );
281             }
282 129         1216 return $is_ok;
283 106         642 };
284 106         154 $skip_reason = eval {$self->$method};
  106         414  
285 104 100       648 $skip_reason = $method unless $skip_reason;
286 104         129 my $exception = $@;
287 104         254 my $num_done = $Builder->current_test - $num_start;
288 104         779 my $num_expected = _total_num_tests($self, $method);
289 104 100       262 $num_expected = $num_done if $num_expected eq NO_PLAN;
290 104 100       227 if ($num_done == $num_expected) {
    100          
291 97 100       224 _exception_failure($self, $method, $exception, $tests)
292             if $exception;
293             } elsif ($num_done > $num_expected) {
294 1         1 my $class = ref $self;
295 1         7 $Builder->diag("expected $num_expected test(s) in $class\::$method, $num_done completed\n");
296             } else {
297 6         16 until (($Builder->current_test - $num_start) >= $num_expected) {
298 8 100       248 if ($exception) {
299 4         18 _exception_failure($self, $method, $exception, $tests);
300 4         6 $skip_reason = "$method died";
301 4         11 $exception = '';
302             } else {
303 4 100       10 if ($self->fail_if_returned_early) {
304 2         7 my $class = ref $self;
305 2         7 $Builder->ok(0, "($class\::$method returned before plan complete)");
306             } else {
307 2         6 $Builder->skip( $skip_reason );
308             }
309             }
310             }
311             }
312 104         431 return(_all_ok_from($self, $num_start));
313             }
314              
315 2     2 1 4 sub fail_if_returned_early { 0 }
316              
317             sub _show_header {
318 110     110   175 my ($self, @tests) = @_;
319 110 100       363 return if $Builder->has_plan;
320 10         201 my $num_tests = Test::Class->expected_tests(@tests);
321 10 100       42 if ($num_tests eq NO_PLAN) {
322 1         6 $Builder->no_plan;
323             } else {
324 9         48 $Builder->expected_tests($num_tests);
325             }
326             }
327              
328             my %SKIP_THIS_CLASS = ();
329              
330             sub SKIP_CLASS {
331 112     112 1 260 my $class = shift;
332 112 100       292 $SKIP_THIS_CLASS{ $class } = shift if @_;
333 112         366 return $SKIP_THIS_CLASS{ $class };
334             }
335              
336             sub _isa_class {
337 139     139   211 my ( $class, $object_or_class ) = @_;
338 139 100       311 return unless defined $object_or_class;
339 138 50       319 return if $object_or_class eq 'Contextual::Return::Value';
340 138         227 return eval {
341 138 100       1878 $object_or_class->isa( $class ) and $object_or_class->can( 'runtests' )
342             };
343             }
344              
345             sub _test_classes {
346 37     37   122 my $class = shift;
347 37         56 return( @{mro::get_isarev($class)}, $class );
  37         278  
348             }
349              
350             sub runtests {
351 44     44 1 10357 my @tests = @_;
352 44 100 100     397 if (@tests == 1 && !ref($tests[0])) {
353 35         79 my $base_class = shift @tests;
354 35         122 @tests = _test_classes( $base_class );
355             }
356 44         91 my $all_passed = 1;
357 44         105 TEST_OBJECT: foreach my $t (@tests) {
358             # SHOULD ALSO ALLOW NO_PLAN
359 63 100       466 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       297 if (my $reason = $t->SKIP_CLASS) {
363 3         9 _show_header($t, @tests);
364 3 100       99 $Builder->skip( $reason ) unless $reason eq "1";
365             } else {
366 56 100       365 $t = $t->new unless ref($t);
367 56         211 my @test_methods = _get_methods($t, TEST);
368 54 100       186 if ( @test_methods ) {
369 41         108 foreach my $method (_get_methods($t, STARTUP)) {
370 8 100       38 _show_header($t, @tests) unless _has_no_tests($t, $method);
371 8         445 my $method_passed = _run_method($t, $method, \@tests);
372 8 100       22 $all_passed = 0 unless $method_passed;
373 8 100       27 next TEST_OBJECT unless $method_passed;
374             }
375 40         98 my $class = ref($t);
376 40         118 my @setup = _get_methods($t, SETUP);
377 40         127 my @teardown = _get_methods($t, TEARDOWN);
378 40         94 foreach my $test ( @test_methods ) {
379 54         101 local $Current_method = $test;
380 54 100       191 $Builder->diag("\n$class->$test") if $ENV{TEST_VERBOSE};
381 54         419 my @methods_to_run = (@setup, $test, @teardown);
382 54         172 while ( my $method = shift @methods_to_run ) {
383 90 100       251 _show_header($t, @tests) unless _has_no_tests($t, $method);
384 90 100       1446 $all_passed = 0 unless _run_method($t, $method, \@tests);
385 88 100       207 if ( _threw_exception( $t, $method ) ) {
386 8 100       43 next if ($method eq $test);
387 2         6 my $num_to_skip = _total_num_tests($t, @methods_to_run);
388 2         17 $Builder->skip( "$method died" ) for ( 1 .. $num_to_skip );
389 2         187 last;
390             }
391             }
392             }
393 38         116 foreach my $method (_get_methods($t, SHUTDOWN)) {
394 8 100       23 _show_header($t, @tests) unless _has_no_tests($t, $method);
395 8 50       85 $all_passed = 0 unless _run_method($t, $method, \@tests);
396             }
397             }
398            
399             }
400             }
401 39         268 return($all_passed);
402             }
403              
404             sub _find_calling_test_class {
405 21     21   20 my $level = 0;
406 21         70 while (my $class = caller(++$level)) {
407 35 100       74 next if $class eq __PACKAGE__;
408 26 100       42 return $class if _isa_class( __PACKAGE__, $class );
409             }
410 1         48 return(undef);
411             }
412              
413             sub num_method_tests {
414 21     21 1 838 my ($self, $method, $n) = @_;
415 21 100       38 my $class = _find_calling_test_class( $self )
416             or croak "not called in a Test::Class";
417 20 100       41 my $info = _method_info($self, $class, $method)
418             or croak "$method is not a test method of class $class";
419 19 100       51 $info->num_tests($n) if defined($n);
420 18         48 return( $info->num_tests );
421             }
422              
423             sub num_tests {
424 9     9 1 33 my $self = shift;
425 9 50       19 croak "num_tests need to be called within a test method"
426             unless defined $Current_method;
427 9         25 return( $self->num_method_tests( $Current_method, @_ ) );
428             }
429              
430             sub BAILOUT {
431 1     1 1 185 my ($self, $reason) = @_;
432 1         4 $Builder->BAILOUT($reason);
433             }
434              
435             sub _last_test_if_exiting_immediately {
436 4 100   4   15 $Builder->expected_tests || $Builder->current_test+1
437             }
438              
439             sub FAIL_ALL {
440 3     3 1 972 my ($self, $reason) = @_;
441 3         10 my $last_test = _last_test_if_exiting_immediately();
442 3 50       42 $Builder->expected_tests( $last_test ) unless $Builder->has_plan;
443 3         23 $Builder->ok(0, $reason) until $Builder->current_test >= $last_test;
444 3 50       1476 my $num_failed = $Builder->can("history")
445             ? $Builder->history->fail_count : grep( !$_, $Builder->summary );
446 3 50       97 exit( $num_failed < 254 ? $num_failed : 254 );
447             }
448              
449             sub SKIP_ALL {
450 2     2 1 457 my ($self, $reason) = @_;
451 2 100       10 $Builder->skip_all( $reason ) unless $Builder->has_plan;
452 1         7 my $last_test = _last_test_if_exiting_immediately();
453 1         10 $Builder->skip( $reason )
454             until $Builder->current_test >= $last_test;
455 1         246 exit(0);
456             }
457              
458             sub add_filter {
459 6     6 1 215 my ( $class, $cb ) = @_;
460              
461 6 100       35 if ( not ref $cb eq 'CODE' ) {
462 1         161 croak "Filter isn't a code-ref"
463             }
464              
465 5         14 push @Filters, $cb;
466             }
467              
468             1;
469              
470             __END__