File Coverage

blib/lib/Test/Class.pm
Criterion Covered Total %
statement 316 317 99.6
branch 151 158 95.5
condition 18 18 100.0
subroutine 59 59 100.0
pod 16 16 100.0
total 560 568 98.5


line stmt bran cond sub pod time code
1 55     55   616032 use strict;
  55         108  
  55         3219  
2 55     55   260 use warnings;
  55         78  
  55         1758  
3 55     55   1241 use 5.006;
  55         160  
  55         2399  
4              
5             package Test::Class;
6              
7 55     55   34646 use Attribute::Handlers;
  55         263888  
  55         339  
8 55     55   2095 use Carp;
  55         92  
  55         4297  
9 55     55   32165 use MRO::Compat;
  55         148708  
  55         1971  
10 55     55   39679 use Storable qw(dclone);
  55         172150  
  55         4485  
11 55     55   16365 use Test::Builder;
  55         198270  
  55         1516  
12 55     55   25429 use Test::Class::MethodInfo;
  55         107  
  55         1577  
13 55     55   29382 use Try::Tiny;
  55         67458  
  55         4298  
14              
15             our $VERSION = '0.50';
16              
17             my $Check_block_has_run;
18             {
19 55     55   362 no warnings 'void';
  55         88  
  55         3790  
20 54     54   363973 CHECK { $Check_block_has_run = 1 }
21             }
22              
23 55     55   268 use constant NO_PLAN => "no_plan";
  55         72  
  55         2688  
24 55     55   254 use constant SETUP => "setup";
  55         75  
  55         2427  
25 55     55   262 use constant TEST => "test";
  55         63  
  55         2207  
26 55     55   301 use constant TEARDOWN => "teardown";
  55         77  
  55         2270  
27 55     55   246 use constant STARTUP => "startup";
  55         79  
  55         2465  
28 55     55   252 use constant SHUTDOWN => "shutdown";
  55         76  
  55         31169  
29              
30              
31             our $Current_method = undef;
32 19     19 1 146 sub current_method { $Current_method }
33              
34              
35             my $Builder = Test::Builder->new;
36 6     6 1 93 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 60     60   2494 my $self = shift;
47 60         1233 delete $_Test{ $self };
48             }
49              
50             sub _test_info {
51 936     936   872 my $self = shift;
52 936 100       3498 return ref($self) ? $_Test{$self} : $Tests;
53             }
54              
55             sub _method_info {
56 338     338   435 my ($self, $class, $method) = @_;
57 338         521 return( _test_info($self)->{$class}->{$method} );
58             }
59              
60             sub _methods_of_class {
61 598     598   657 my ( $self, $class ) = @_;
62 598 100       819 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 597         606 return values %{ $test_info->{$class} };
  597         1801  
66             }
67              
68             sub _parse_attribute_args {
69 121   100 121   379 my $args = shift || '';
70 121         122 my $num_tests;
71             my $type;
72 121         455 $args =~ s/\s+//sg;
73 121         378 foreach my $arg (split /=>/, $args) {
74 145 100       490 if (Test::Class::MethodInfo->is_num_tests($arg)) {
    100          
75 93         207 $num_tests = $arg;
76             } elsif (Test::Class::MethodInfo->is_method_type($arg)) {
77 51         99 $type = $arg;
78             } else {
79 1         25 die 'bad attribute args';
80             }
81             }
82 120         655 return( $type, $num_tests );
83             }
84              
85             sub _is_public_method {
86 121     121   161 my ($class, $name) = @_;
87 121         176 my @parents = @{mro::get_linear_isa($class)};
  121         810  
88 121         180 shift @parents;
89 121         833 foreach my $parent_class ( @parents ) {
90 122 100       1260 return unless $parent_class->can( $name );
91 6 100       19 return if _method_info( $class, $parent_class, $name );
92             }
93 1         17 return 1;
94             }
95              
96             sub Test : ATTR(CODE,RAWDATA) {
97 122     122 1 15977 my ($class, $symbol, $code_ref, $attr, $args) = @_;
98 122 100       381 if ($symbol eq "ANON") {
99 1         44 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 121         127 my $name = *{$symbol}{NAME};
  121         247  
102 121 100       490 warn "overriding public method $name with a test method in $class\n"
103             if _is_public_method( $class, $name );
104 121 100       173 eval { $class->add_testinfo($name, _parse_attribute_args($args)) }
  121         258  
105             || warn "bad test definition '$args' in $class->$name\n";
106             }
107 55     55   364 }
  55         87  
  55         455  
108              
109             sub Tests : ATTR(CODE,RAWDATA) {
110 6     6 1 986 my ($class, $symbol, $code_ref, $attr, $args) = @_;
111 6   100     36 $args ||= 'no_plan';
112 6         20 Test( $class, $symbol, $code_ref, $attr, $args );
113 55     55   23374 }
  55         89  
  55         224  
114              
115             sub add_testinfo {
116 120     120 1 190 my($class, $name, $type, $num_tests) = @_;
117 120         425 $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 633     633   690 my $self = shift;
126 633 100       1579 return ref $self ? ref $self : $self;
127             }
128              
129             sub new {
130 59     59 1 1340 my $proto = shift;
131 59         203 my $class = _class_of( $proto );
132 59 100       324 $proto = {} unless ref($proto);
133 59         252 my $self = bless {%$proto, @_}, $class;
134 59         7297 $_Test{$self} = dclone($Tests);
135 59         247 return($self);
136             }
137              
138             sub _get_methods {
139 302     302   562 my ( $self, @types ) = @_;
140 302         484 my $test_class = _class_of( $self );
141              
142 302   100     1241 my $test_method_regexp = $ENV{ TEST_METHOD } || '.*';
143 302         396 my $method_regexp = eval { qr/\A$test_method_regexp\z/ };
  302         2190  
144 302 100       659 die "TEST_METHOD ($test_method_regexp) is not a valid regexp: $@" if $@;
145              
146 301         435 my %methods = ();
147 301         296 foreach my $class ( @{mro::get_linear_isa( $test_class )} ) {
  301         986  
148             FILTER:
149 598         998 foreach my $info ( _methods_of_class( $self, $class ) ) {
150 757         1745 my $name = $info->name;
151              
152 757 100       1438 if ( $info->type eq TEST ) {
153             # determine if method is filtered, true if *any* filter
154             # returns false.
155 425         618 foreach my $filter ( @Filters ) {
156 90 100       193 next FILTER unless $filter->( $class, $name );
157             }
158             }
159              
160 723         1048 foreach my $type ( @types ) {
161 857 100       1637 if ( $info->is_type( $type ) ) {
162 174 100 100     1544 $methods{ $name } = 1
163             unless $type eq TEST && $name !~ $method_regexp;
164             }
165             }
166             }
167             }
168              
169 300         793 my @methods = sort keys %methods;
170 300         1302 return @methods;
171             }
172              
173             sub _num_expected_tests {
174 47     47   61 my $self = shift;
175 47 100       117 if (my $reason = $self->SKIP_CLASS ) {
176 2 100       10 return $reason eq "1" ? 0 : 1;
177             };
178 45         97 my @test_methods = _get_methods($self, TEST);
179 45 100       136 return 0 unless @test_methods;
180 22         67 my @startup_shutdown_methods =
181             _get_methods($self, STARTUP, SHUTDOWN);
182 22         56 my $num_startup_shutdown_methods =
183             _total_num_tests($self, @startup_shutdown_methods);
184 22 100       76 return(NO_PLAN) if $num_startup_shutdown_methods eq NO_PLAN;
185 20         60 my @fixture_methods = _get_methods($self, SETUP, TEARDOWN);
186 20         49 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         88 my $num_tests = _total_num_tests($self, @test_methods);
189 18 100       64 return(NO_PLAN) if $num_tests eq NO_PLAN;
190 14         59 return($num_startup_shutdown_methods + $num_tests + @test_methods * $num_fixture_tests);
191             }
192              
193             sub expected_tests {
194 23     23 1 259 my $total = 0;
195 23         63 foreach my $test (@_) {
196 53 100 100     105 if ( _isa_class( __PACKAGE__, $test ) ) {
    100          
197 47         103 my $n = _num_expected_tests($test);
198 47 100       143 return NO_PLAN if $n eq NO_PLAN;
199 39         82 $total += $n;
200             } elsif ( defined $test && $test =~ m/^\d+$/ ) {
201 4         11 $total += $test;
202             } else {
203 2 100       14 $test = 'undef' unless defined $test;
204 1         24 croak "$test is not a Test::Class or an integer";
205             }
206             }
207 13         48 return $total;
208             }
209              
210             sub _total_num_tests {
211 272     272   502 my ($self, @methods) = @_;
212 272         435 my $class = _class_of( $self );
213 272         365 my $total_num_tests = 0;
214 272         473 foreach my $method (@methods) {
215 273         265 foreach my $class (@{mro::get_linear_isa($class)}) {
  273         851  
216 312         492 my $info = _method_info($self, $class, $method);
217 312 100       651 next unless $info;
218 281         761 my $num_tests = $info->num_tests;
219 281 100       651 return(NO_PLAN) if ($num_tests eq NO_PLAN);
220 265         389 $total_num_tests += $num_tests;
221 265 100       1058 last unless $num_tests =~ m/^\+/
222             }
223             }
224 256         799 return($total_num_tests);
225             }
226              
227             sub _has_no_tests {
228 106     106   392 my ( $self, $method ) = @_;
229 106         398 return _total_num_tests( $self, $method ) eq '0';
230             }
231              
232             sub _all_ok_from {
233 104     104   160 my ($self, $start_test) = @_;
234              
235             # The Test::Builder 1.5 way to do it
236 104 50       718 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 104         253 my $current_test = $Builder->current_test;
242 104 100       791 return(1) if $start_test == $current_test;
243 99         428 my @results = ($Builder->summary)[$start_test .. $current_test-1];
244 99 100       1148 foreach my $result (@results) { return(0) unless $result }
  122         494  
245 87         828 return(1);
246             }
247             }
248              
249             sub _exception_failure {
250 9     9   17 my ($self, $method, $exception, $tests) = @_;
251 9         17 local $Test::Builder::Level = 3;
252 9         17 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         29 _show_header($self, @$tests);
256 9         69 chomp $exception;
257 9         44 $Builder->ok(0, "$message died ($exception)");
258 9         23 _threw_exception( $self, $method => 1 );
259             }
260              
261             my %threw_exception;
262             sub _threw_exception {
263 203     203   266 my ( $self, $method, $optional_value) = @_;
264 203         258 my $class = ref( $self );
265 203 100       528 $threw_exception{ $class }{ $method } = $optional_value
266             if defined $optional_value;
267 203         765 return $threw_exception{ $class }{ $method };
268             }
269              
270             sub _run_method {
271 106     106   172 my ($self, $method, $tests) = @_;
272 106         226 _threw_exception( $self, $method => 0 );
273 106         295 my $num_start = $Builder->current_test;
274 106         819 my $skip_reason;
275 106         299 my $original_ok = \&Test::Builder::ok;
276 55     55   82390 no warnings;
  55         116  
  55         90487  
277             local *Test::Builder::ok = sub {
278 129     129   7006 my ($builder, $test, $description) = @_;
279 129         209 local $Test::Builder::Level = $Test::Builder::Level+1;
280 129 100       332 unless ( defined($description) ) {
281 8         39 $description = $self->current_method;
282 8         21 $description =~ tr/_/ /;
283             }
284 129         322 my $is_ok = $original_ok->($builder, $test, $description);
285 129 100       47088 unless ( $is_ok ) {
286 17         38 my $class = ref $self;
287 17         77 $Builder->diag( " (in $class->$method)" );
288             }
289 129         1477 return $is_ok;
290 106         785 };
291              
292 106         127 my $exception;
293 106     88   728 $skip_reason = try { $self->$method } catch { $exception = $_; undef };
  106         4162  
  9         286  
  9         33  
294 104 100       2319 $skip_reason = $method unless $skip_reason;
295              
296 104         315 my $num_done = $Builder->current_test - $num_start;
297 104         855 my $num_expected = _total_num_tests($self, $method);
298 104 100       265 $num_expected = $num_done if $num_expected eq NO_PLAN;
299 104 100       237 if ($num_done == $num_expected) {
    100          
300 97 100       222 _exception_failure($self, $method, $exception, $tests)
301             if $exception;
302             } elsif ($num_done > $num_expected) {
303 1         2 my $class = ref $self;
304 1         6 $Builder->diag("expected $num_expected test(s) in $class\::$method, $num_done completed\n");
305             } else {
306 6         18 until (($Builder->current_test - $num_start) >= $num_expected) {
307 8 100       257 if ($exception) {
308 4         14 _exception_failure($self, $method, $exception, $tests);
309 4         7 $skip_reason = "$method died";
310 4         21 $exception = '';
311             } else {
312 4 100       13 if ($self->fail_if_returned_early) {
313 2         7 my $class = ref $self;
314 2         6 $Builder->ok(0, "($class\::$method returned before plan complete)");
315             } else {
316 2         6 $Builder->skip( $skip_reason );
317             }
318             }
319             }
320             }
321 104         431 return(_all_ok_from($self, $num_start));
322             }
323              
324 2     2 1 4 sub fail_if_returned_early { 0 }
325              
326             sub _show_header {
327 110     110   213 my ($self, @tests) = @_;
328 110 100       443 return if $Builder->has_plan;
329 10         192 my $num_tests = Test::Class->expected_tests(@tests);
330 10 100       42 if ($num_tests eq NO_PLAN) {
331 1         4 $Builder->no_plan;
332             } else {
333 9         51 $Builder->expected_tests($num_tests);
334             }
335             }
336              
337             my %SKIP_THIS_CLASS = ();
338              
339             sub SKIP_CLASS {
340 112     112 1 274 my $class = shift;
341 112 100       334 $SKIP_THIS_CLASS{ $class } = shift if @_;
342 112         424 return $SKIP_THIS_CLASS{ $class };
343             }
344              
345             sub _isa_class {
346 139     139   236 my ( $class, $object_or_class ) = @_;
347 139 100       360 return unless defined $object_or_class;
348 138 50       356 return if $object_or_class eq 'Contextual::Return::Value';
349 138         195 return eval {
350 138 100       2125 $object_or_class->isa( $class ) and $object_or_class->can( 'runtests' )
351             };
352             }
353              
354             sub _test_classes {
355 37     37   118 my $class = shift;
356 37         70 return( @{mro::get_isarev($class)}, $class );
  37         290  
357             }
358              
359             sub runtests {
360 45 100   45 1 14094 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"
361             unless $Check_block_has_run;
362 44         130 my @tests = @_;
363 44 100 100     378 if (@tests == 1 && !ref($tests[0])) {
364 35         81 my $base_class = shift @tests;
365 35         131 @tests = _test_classes( $base_class );
366             }
367 44         110 my $all_passed = 1;
368 44         109 TEST_OBJECT: foreach my $t (@tests) {
369             # SHOULD ALSO ALLOW NO_PLAN
370 63 100       577 next if $t =~ m/^\d+$/;
371 60 100       215 croak "$t is not Test::Class or integer"
372             unless _isa_class( __PACKAGE__, $t );
373 59 100       337 if (my $reason = $t->SKIP_CLASS) {
374 3         10 _show_header($t, @tests);
375 3 100       167 $Builder->skip( $reason ) unless $reason eq "1";
376             } else {
377 56 100       425 $t = $t->new unless ref($t);
378 56         235 my @test_methods = _get_methods($t, TEST);
379 54 100       199 if ( @test_methods ) {
380 41         129 foreach my $method (_get_methods($t, STARTUP)) {
381 8 100       34 _show_header($t, @tests) unless _has_no_tests($t, $method);
382 8         1150 my $method_passed = _run_method($t, $method, \@tests);
383 8 100       25 $all_passed = 0 unless $method_passed;
384 8 100       30 next TEST_OBJECT unless $method_passed;
385             }
386 40         99 my $class = ref($t);
387 40         203 my @setup = _get_methods($t, SETUP);
388 40         175 my @teardown = _get_methods($t, TEARDOWN);
389 40         129 foreach my $test ( @test_methods ) {
390 54         131 local $Current_method = $test;
391 54 100       325 $Builder->diag("\n$class->$test") if $ENV{TEST_VERBOSE};
392 54         640 my @methods_to_run = (@setup, $test, @teardown);
393 54         213 while ( my $method = shift @methods_to_run ) {
394 90 100       229 _show_header($t, @tests) unless _has_no_tests($t, $method);
395 90 100       3262 $all_passed = 0 unless _run_method($t, $method, \@tests);
396 88 100       200 if ( _threw_exception( $t, $method ) ) {
397 8 100       52 next if ($method eq $test);
398 2         6 my $num_to_skip = _total_num_tests($t, @methods_to_run);
399 2         18 $Builder->skip( "$method died" ) for ( 1 .. $num_to_skip );
400 2         186 last;
401             }
402             }
403             }
404 38         130 foreach my $method (_get_methods($t, SHUTDOWN)) {
405 8 100       36 _show_header($t, @tests) unless _has_no_tests($t, $method);
406 8 50       88 $all_passed = 0 unless _run_method($t, $method, \@tests);
407             }
408             }
409              
410             }
411             }
412 39         277 return($all_passed);
413             }
414              
415             sub _find_calling_test_class {
416 21     21   28 my $level = 0;
417 21         84 while (my $class = caller(++$level)) {
418 35 100       97 next if $class eq __PACKAGE__;
419 26 100       60 return $class if _isa_class( __PACKAGE__, $class );
420             }
421 1         21 return(undef);
422             }
423              
424             sub num_method_tests {
425 21     21 1 631 my ($self, $method, $n) = @_;
426 21 100       45 my $class = _find_calling_test_class( $self )
427             or croak "not called in a Test::Class";
428 20 100       50 my $info = _method_info($self, $class, $method)
429             or croak "$method is not a test method of class $class";
430 19 100       67 $info->num_tests($n) if defined($n);
431 18         60 return( $info->num_tests );
432             }
433              
434             sub num_tests {
435 9     9 1 52 my $self = shift;
436 9 50       28 croak "num_tests need to be called within a test method"
437             unless defined $Current_method;
438 9         36 return( $self->num_method_tests( $Current_method, @_ ) );
439             }
440              
441             sub BAILOUT {
442 1     1 1 242 my ($self, $reason) = @_;
443 1         5 $Builder->BAILOUT($reason);
444             }
445              
446             sub _last_test_if_exiting_immediately {
447 4 100   4   22 $Builder->expected_tests || $Builder->current_test+1
448             }
449              
450             sub FAIL_ALL {
451 3     3 1 1294 my ($self, $reason) = @_;
452 3         14 my $last_test = _last_test_if_exiting_immediately();
453 3 50       59 $Builder->expected_tests( $last_test ) unless $Builder->has_plan;
454 3         33 $Builder->ok(0, $reason) until $Builder->current_test >= $last_test;
455 3 50       1694 my $num_failed = $Builder->can("history")
456             ? $Builder->history->fail_count : grep( !$_, $Builder->summary );
457 3 50       149 exit( $num_failed < 254 ? $num_failed : 254 );
458             }
459              
460             sub SKIP_ALL {
461 2     2 1 375 my ($self, $reason) = @_;
462 2 100       8 $Builder->skip_all( $reason ) unless $Builder->has_plan;
463 1         7 my $last_test = _last_test_if_exiting_immediately();
464 1         10 $Builder->skip( $reason )
465             until $Builder->current_test >= $last_test;
466 1         243 exit(0);
467             }
468              
469             sub add_filter {
470 6     6 1 253 my ( $class, $cb ) = @_;
471              
472 6 100       41 if ( not ref $cb eq 'CODE' ) {
473 1         193 croak "Filter isn't a code-ref"
474             }
475              
476 5         16 push @Filters, $cb;
477             }
478              
479             1;
480              
481             __END__