File Coverage

blib/lib/Test/Spec/Context.pm
Criterion Covered Total %
statement 231 263 87.8
branch 58 96 60.4
condition 17 27 62.9
subroutine 50 52 96.1
pod 0 20 0.0
total 356 458 77.7


line stmt bran cond sub pod time code
1             package Test::Spec::Context;
2 15     15   116 use strict;
  15         38  
  15         530  
3 15     15   92 use warnings;
  15         35  
  15         554  
4              
5             ########################################################################
6             # NO USER-SERVICEABLE PARTS INSIDE.
7             ########################################################################
8              
9 15     15   93 use Carp ();
  15         35  
  15         244  
10 15     15   84 use List::Util ();
  15         36  
  15         237  
11 15     15   82 use Scalar::Util ();
  15         40  
  15         252  
12 15     15   83 use Test::More ();
  15         34  
  15         401  
13 15     15   90 use Test::Spec qw(*TODO $Debug :constants);
  15         33  
  15         110  
14 15     15   5367 use Test::Spec::Example;
  15         81  
  15         558  
15 15     15   5093 use Test::Spec::TodoExample;
  15         54  
  15         51451  
16              
17             our @CARP_NOT = ();
18              
19             my $_StackDepth = 0;
20             my $_AroundStackDepth = 1;
21              
22             sub new {
23 190     190 0 439 my $class = shift;
24 190         517 my $self = bless {}, $class;
25              
26 190 50       589 if (@_) {
27 0         0 my $args = shift;
28 0 0 0     0 if (@_ || ref($args) ne 'HASH') {
29 0         0 Carp::croak "usage: $class->new(\\%args)";
30             }
31 0         0 while (my ($name,$val) = each (%$args)) {
32 0         0 $self->$name($val);
33             }
34             }
35              
36 190         366 my $this = $self;
37 190         846 Scalar::Util::weaken($this);
38             $self->on_enter(sub {
39             $this && $this->_debug(sub {
40 0         0 printf STDERR "%s[%s]\n", ' ' x $_StackDepth, $this->_debug_name;
41 0         0 $_StackDepth++;
42 546 50   546   3350 });
43 190         1159 });
44              
45             $self->on_leave(sub {
46             $this && $this->_debug(sub {
47 0         0 $_StackDepth--;
48 0         0 printf STDERR "%s[/%s]\n", ' ' x $_StackDepth, $this->_debug_name;
49 546 50   546   3400 });
50 190         1094 });
51              
52 190         719 return $self;
53             }
54              
55             sub clone {
56 4     4 0 5 my $orig = shift;
57 4         15 my $clone = bless { %$orig }, ref($orig);
58              
59 4         10 my $orig_contexts = $clone->context_lookup;
60 4         8 my $new_contexts = Test::Spec::_ixhash();
61              
62 4         14 while (my ($name,$ctx) = each %$orig_contexts) {
63 0         0 my $new_ctx = $ctx->clone;
64 0         0 $new_ctx->parent($clone);
65 0         0 $new_contexts->{$name} = $new_ctx;
66             }
67 4         36 $clone->{_context_lookup} = $new_contexts;
68              
69 4         20 return $clone;
70             }
71              
72             # The reference we keep to our parent causes the garbage collector to
73             # destroy the innermost context first, which is what we want. If that
74             # becomes untrue at some point, it will be easy enough to descend the
75             # hierarchy and run the after("all") tests that way.
76             sub DESTROY {
77 189     189   501 my $self = shift;
78             # no need to tear down what was never set up
79 189 100       503 if ($self->_has_run_before_all) {
80 59         169 $self->_run_after_all_once;
81             }
82             }
83              
84             sub name {
85 548     548 0 818 my $self = shift;
86 548 100       1187 $self->{_name} = shift if @_;
87             return exists($self->{_name})
88             ? $self->{_name}
89 548 50       1567 : ($self->{_name} = '');
90             }
91              
92             sub parent {
93 7080     7080 0 10667 my $self = shift;
94 7080 100       12941 if (@_) {
95 169         380 $self->{_parent} = shift;
96 169 50       729 Scalar::Util::weaken($self->{_parent}) if ref($self->{_parent});
97             }
98 7080         17235 return $self->{_parent};
99             }
100              
101             sub class {
102 1596     1596 0 2193 my $self = shift;
103 1596 100       2830 $self->{_class} = shift if @_;
104 1596 100       3069 if ($self->{_class}) {
    50          
105 692         3091 return $self->{_class};
106             }
107             elsif ($self->parent) {
108 904         1448 return $self->parent->class;
109             }
110             else {
111 0         0 return undef;
112             }
113             }
114              
115             sub context_lookup {
116 271     271 0 408 my $self = shift;
117 271   66     959 return $self->{_context_lookup} ||= Test::Spec::_ixhash();
118             }
119              
120             sub before_blocks {
121 443     443 0 795 my $self = shift;
122 443   100     1921 return $self->{_before_blocks} ||= [];
123             }
124              
125             sub after_blocks {
126 415     415 0 764 my $self = shift;
127 415   100     1809 return $self->{_after_blocks} ||= [];
128             }
129              
130             sub around_blocks {
131 549     549 0 1178 my $self = shift;
132 549   100     2705 return $self->{_around_blocks} ||= [];
133             }
134              
135             sub tests {
136 343     343 0 508 my $self = shift;
137 343   100     1698 return $self->{_tests} ||= [];
138             }
139              
140             sub on_enter_blocks {
141 1681     1681 0 2806 my $self = shift;
142 1681   100     6011 return $self->{_on_enter_blocks} ||= [];
143             }
144              
145             sub on_leave_blocks {
146 1681     1681 0 2756 my $self = shift;
147 1681   100     6279 return $self->{_on_leave_blocks} ||= [];
148             }
149              
150             # private attributes
151             sub _has_run_before_all {
152 604     604   1098 my $self = shift;
153 604 100       1507 $self->{__has_run_before_all} = shift if @_;
154 604         2956 return $self->{__has_run_before_all};
155             }
156              
157             sub _has_run_after_all {
158 118     118   193 my $self = shift;
159 118 100       311 $self->{__has_run_after_all} = shift if @_;
160 118         262 return $self->{__has_run_after_all};
161             }
162              
163             sub _debug {
164 1110     1110   2856 my ($self,$code) = @_;
165 1110 50       2358 return unless $self->_debugging;
166 0         0 $code->();
167             }
168              
169             sub _debug_name {
170 0     0   0 my $self = shift;
171 0 0       0 $self->name || '(anonymous)';
172             }
173              
174             sub _debugging {
175 1110     1110   1918 my $self = shift;
176             # env var can be set greater than 1 for definition phase debug.
177             # otherwise, any true value means debug execution
178 1110 50 33     4162 if ($Debug > 1) {
    50          
179 0         0 return 1;
180             }
181             elsif ($Debug && $self->class->phase == EXECUTION_PHASE) {
182 0         0 return 1;
183             }
184 1110         3392 return;
185             }
186              
187             sub on_enter {
188 262     262 0 626 my ($self,$callback) = @_;
189 262         448 push @{ $self->on_enter_blocks }, $callback;
  262         657  
190              
191             # Handle case where an on_enter was added during a context declaration.
192             # This allows stubs being set up to be valid both in that current Perl
193             # context and later in spec context.
194 262 100       1120 if (Test::Spec->in_context($self)) {
195 72 50       396 if (not $self->{_has_run_on_enter}{$callback}++) {
196 72         231 $callback->();
197             }
198             }
199 262         666 return;
200             }
201              
202             sub on_leave {
203 262     262 0 683 my ($self,$callback) = @_;
204 262         449 push @{ $self->on_leave_blocks }, $callback;
  262         650  
205             }
206              
207             sub ancestors {
208 165     165 0 482 my ($self) = @_;
209 165 50       437 return $self->parent ? ($self->parent, $self->parent->ancestors) : ();
210             }
211              
212             sub ancestor_of {
213 165     165 0 493 my ($self,$other) = @_;
214 165     0   881 return !!List::Util::first { $other == $_ } $self->ancestors;
  0         0  
215             }
216              
217             sub contexts {
218 228     228 0 339 my $self = shift;
219 228         283 my @ctx = values %{ $self->context_lookup };
  228         389  
220 228 50       3388 return wantarray ? @ctx : \@ctx;
221             }
222              
223             # recurse into child contexts to count total tests for a package
224             sub _count_tests {
225 161     161   226 my $self = shift;
226 161         268 my @descendant = map { $_->_count_tests } $self->contexts;
  42         158  
227 161         210 return @{$self->tests} + List::Util::sum(0, @descendant);
  161         263  
228             }
229              
230             sub _run_callback {
231 828     828   2077 my ($self,$type,$pool,@args) = @_;
232 828         1758 my @subs = map { $_->{code} } grep { $_->{type} eq $type } @$pool;
  114         385  
  176         843  
233 828         3366 for my $code (@subs) {
234 114         399 $code->(@args);
235             }
236             }
237              
238             sub _run_before {
239 415     415   734 my $self = shift;
240 415         757 my $type = shift;
241 415         1078 return $self->_run_callback($type,$self->before_blocks,@_);
242             }
243              
244             sub _run_before_all_once {
245 354     354   706 my $self = shift;
246 354 100       789 return if $self->_has_run_before_all;
247 61         515 $self->_has_run_before_all(1);
248 61         439 return $self->_run_before('all',@_);
249             }
250              
251             sub _run_after {
252 413     413   798 my $self = shift;
253 413         779 my $type = shift;
254 413         1139 return $self->_run_callback($type,$self->after_blocks,@_);
255             }
256              
257             sub _run_after_all_once {
258 59     59   118 my $self = shift;
259 59 50       140 return if $self->_has_run_after_all;
260 59         177 $self->_has_run_after_all(1);
261 59         138 return $self->_run_after('all',@_);
262             }
263              
264             # join by spaces and strip leading/extra spaces
265             sub _concat {
266 128     128   307 my ($self,@pieces) = @_;
267 128         402 my $str = join(' ', @pieces);
268 128         1496 $str =~ s{\A\s+|\s+\z}{}s;
269 128         914 $str =~ s{\s+}{ }sg;
270 128         319 return $str;
271             }
272              
273             sub _materialize_tests {
274 63     63   766 my ($self, $digits, @context_stack) = @_;
275              
276             # include the name of the context in test reports
277 63         156 push @context_stack, $self;
278              
279             # need to know how many tests there are, so we can make a lexically
280             # sortable test name using numeric prefix.
281 63 100       159 if (not defined $digits) {
282             my $top_level_sum = List::Util::sum(
283 25         280 map { $_->_count_tests } $self->class->contexts
  119         253  
284             );
285 25 50       123 if ($top_level_sum == 0) {
286 0         0 warn "no examples defined in spec package " . $self->class;
287 0         0 return;
288             }
289 25         159 $digits = 1 + int( log($top_level_sum) / log(10) );
290             }
291              
292             # Create a test sub like 't001_enough_mucus'
293 63         203 my $format = "t%0${digits}d_%s";
294              
295 63         98 for my $t (@{ $self->tests }) {
  63         134  
296 128         375 my $description = $self->_concat((map { $_->name } @context_stack), $t->{name});
  354         567  
297 128         338 my $test_number = 1 + scalar($self->class->tests);
298 128         357 my $sub_name = sprintf $format, $test_number, $self->_make_safe($description);
299              
300             # create a test subroutine in the correct package
301 128         226 my $example;
302 128 50 33     529 if (!$t->{code} || $t->{todo}) {
303             $example = Test::Spec::TodoExample->new({
304             name => $sub_name,
305             reason => $t->{tdoo},
306 0         0 description => $description,
307             builder => $self->_builder,
308             });
309             }
310             else {
311             $example = Test::Spec::Example->new({
312             name => $sub_name,
313             description => $description,
314             code => $t->{code},
315 128         361 context => $self,
316             builder => $self->_builder,
317             });
318             }
319              
320 128         402 $self->class->add_test($example);
321             }
322              
323             # recurse to child contexts
324 63         165 for my $ctx ($self->contexts) {
325 38         140 $ctx->_materialize_tests($digits, @context_stack);
326             }
327             }
328              
329             sub _builder {
330 128     128   239 shift->class->builder;
331             }
332              
333             sub _make_safe {
334 128     128   228 my ($self,$str) = @_;
335 128 50 33     458 return '' unless (defined($str) && length($str));
336 128         355 $str = lc($str);
337 128         391 $str =~ s{'}{}g;
338 128         951 $str =~ s{\W+}{_}g;
339 128         1845 $str =~ s{_+}{_}g;
340 128         540 return $str;
341             }
342              
343             # Recurse to run the entire on_enter chain, starting from the top.
344             # Propagate exceptions in the same way that _run_on_leave does, for the same
345             # reasons.
346             sub _run_on_enter {
347 1419     1419   2457 my $self = shift;
348 1419         2373 my @errs;
349 1419 100       2734 if ($self->parent) {
350 873         1430 eval { $self->parent->_run_on_enter };
  873         1619  
351 873 50       1895 push @errs, $@ if $@;
352             }
353 1419         2374 for my $on_enter (@{ $self->on_enter_blocks }) {
  1419         2856  
354 3874 100       12222 next if $self->{_has_run_on_enter}{$on_enter}++;
355 1241         2162 eval { $on_enter->() };
  1241         2970  
356 1241 50       4872 push @errs, $@ if $@;
357             }
358 1419 50       3448 die join("\n", @errs) if @errs;
359 1419         3021 return;
360             }
361              
362             # Recurse to run the entire on_leave chain, starting from the bottom (and in
363             # reverse "unwinding" order).
364             # Propagate all exceptions only after running all on_leave blocks. This allows
365             # mocks (and whatever else) to test their expectations after the test has
366             # completed.
367             sub _run_on_leave {
368 1419     1419   2347 my $self = shift;
369 1419         2034 my @errs;
370 1419         1993 for my $on_leave (reverse @{ $self->on_leave_blocks }) {
  1419         2665  
371 3946 100       12241 next if $self->{_has_run_on_leave}{$on_leave}++;
372 1313         2165 eval { $on_leave->() };
  1313         3178  
373 1313 50       4678 push @errs, $@ if $@;
374             }
375 1419 100       3543 if ($self->parent) {
376 873         1648 eval { $self->parent->_run_on_leave };
  873         1711  
377 873 50       1748 push @errs, $@ if $@;
378             }
379 1419 50       2934 die join("\n", @errs) if @errs;
380 1419         2587 return;
381             }
382              
383             # for giving individual tests mortal, anonymous contexts that are used for
384             # mocking/stubbing hooks.
385             sub _in_anonymous_context {
386 129     129   382 my ($self,$code,$example) = @_;
387 129         561 my $context = Test::Spec::Context->new;
388 129         472 $context->name('');
389 129         397 $context->parent($self);
390 129         372 $context->class($self->class);
391 129         487 $context->contextualize($code, $example);
392             }
393              
394             # Runs $code within a context (specifically, having been wrapped
395             # with on_enter/on_leave setup and teardown,
396             # and with around blocks).
397             sub contextualize {
398 546     546 0 1344 my ($self,$code,$example) = @_;
399 546         1101 local $Test::Spec::_Current_Context = $self;
400 546         1515 local $self->{_has_run_on_enter} = {};
401 546         1370 local $self->{_has_run_on_leave} = {};
402 546         1081 local $TODO = $TODO;
403 546         1546 my @errs;
404              
405 546         984 eval { $self->_run_on_enter };
  546         1427  
406 546 50       1393 push @errs, $@ if $@;
407              
408 546 50       1395 if (not @errs) {
409 546         1543 $code = $self->wrap_code_with_around_blocks($code,$example);
410              
411 546         961 eval { $code->($example) };
  546         1745  
412 546 50       75033 push @errs, $@ if $@;
413             }
414              
415             # always run despite errors, since on_enter might have set up stuff that
416             # needs to be torn down, before another on_enter died
417 546         1114 eval { $self->_run_on_leave };
  546         1367  
418 546 50       1968 push @errs, $@ if $@;
419              
420 546 50       1292 if (@errs) {
421 0 0       0 if ($TODO) {
422             # make it easy for tests to declare todo status, just "$TODO++"
423 0 0       0 $TODO = "(unimplemented)" if $TODO =~ /^\d+$/;
424             # expected to fail
425 0         0 Test::More::ok(1);
426             }
427             else {
428             # rethrow
429 0         0 die join("\n", @errs);
430             }
431             }
432              
433 546         3724 return;
434             }
435              
436             # Wraps $code within a context with around blocks.
437             sub wrap_code_with_around_blocks {
438 546     546 0 1303 my ($self,$code,$example) = @_;
439 546         877 for (@{ $self->around_blocks }) {
  546         1197  
440 9         31 $code = $self->wrap_code_with_around_block($code,$_,$example);
441             }
442 546         1251 return $code;
443             }
444              
445             # Wraps $code within a context with around block.
446             sub wrap_code_with_around_block {
447 9     9 0 25 my ($self,$inner_code,$block,$example) = @_;
448              
449 9         17 my $this = $self;
450 9         38 Scalar::Util::weaken($this);
451              
452             return sub {
453 9     9   19 my $yield_ok = 0;
454             local $Test::Spec::Yield = sub {
455 9         18 $yield_ok = 1;
456 9         28 $inner_code->($example);
457 9         33 };
458             $this && $this->_debug(sub {
459 0         0 printf STDERR "%s[around CODE %s] %s {\n", '__' x $_AroundStackDepth, $self->_debug_name, "$block";
460 0         0 $_AroundStackDepth++;
461 9 50       65 });
462              
463 9         67 $block->{code}->($example);
464            
465             $this && $this->_debug(sub {
466 0         0 $_AroundStackDepth--;
467 0         0 printf STDERR "%s[/around CODE %s] %s }\n", '__' x $_AroundStackDepth, $self->_debug_name, "$block";
468 9 50       73 });
469 9 50       60 unless ($yield_ok) {
470 0           local @CARP_NOT = qw( Test::Spec Test::Spec::Example );
471 0           Carp::croak "around CODE doesn't call yield";
472             }
473 9         53 };
474             }
475              
476             #
477             # Copyright (c) 2010-2011 by Informatics Corporation of America.
478             #
479             # This program is free software; you can redistribute it and/or modify it
480             # under the same terms as Perl itself.
481             #
482              
483             1;