File Coverage

blib/lib/Test2/Hub.pm
Criterion Covered Total %
statement 247 249 99.2
branch 97 114 85.0
condition 86 117 73.5
subroutine 34 34 100.0
pod 20 26 76.9
total 484 540 89.6


line stmt bran cond sub pod time code
1             package Test2::Hub;
2 57     57   518 use strict;
  57         50  
  57         1253  
3 57     57   171 use warnings;
  57         64  
  57         1884  
4              
5             our $VERSION = '0.000044';
6              
7 57     57   173 use Carp qw/carp croak confess/;
  57         53  
  57         2632  
8 57     57   198 use Test2::Util qw/get_tid/;
  57         76  
  57         1981  
9              
10 57     57   193 use Scalar::Util qw/weaken/;
  57         68  
  57         2197  
11              
12 57     57   18220 use Test2::Util::ExternalMeta qw/meta get_meta set_meta delete_meta/;
  57         79  
  57         4027  
13 57         280 use Test2::Util::HashBase qw{
14             pid tid hid ipc
15             no_ending
16             _filters
17             _pre_filters
18             _listeners
19             _follow_ups
20             _formatter
21             _context_acquire
22             _context_init
23             _context_release
24              
25             count
26             failed
27             ended
28             bailed_out
29             _passing
30             _plan
31             skip_reason
32 57     57   252 };
  57         62  
33              
34             my $ID_POSTFIX = 1;
35             sub init {
36 173     173 0 207 my $self = shift;
37              
38 173         548 $self->{+PID} = $$;
39 173         278 $self->{+TID} = get_tid();
40 173         582 $self->{+HID} = join '-', $self->{+PID}, $self->{+TID}, $ID_POSTFIX++;
41              
42 173         252 $self->{+COUNT} = 0;
43 173         229 $self->{+FAILED} = 0;
44 173         250 $self->{+_PASSING} = 1;
45              
46 173 100       413 if (my $formatter = delete $self->{formatter}) {
47 5         11 $self->format($formatter);
48             }
49              
50 173 100       496 if (my $ipc = $self->{+IPC}) {
51 12         31 $ipc->add_hub($self->{+HID});
52             }
53             }
54              
55             sub reset_state {
56 3     3 1 10 my $self = shift;
57              
58 3         4 $self->{+COUNT} = 0;
59 3         4 $self->{+FAILED} = 0;
60 3         3 $self->{+_PASSING} = 1;
61              
62 3         4 delete $self->{+_PLAN};
63 3         2 delete $self->{+ENDED};
64 3         3 delete $self->{+BAILED_OUT};
65 3         5 delete $self->{+SKIP_REASON};
66             }
67              
68             sub inherit {
69 60     60 0 61 my $self = shift;
70 60         64 my ($from, %params) = @_;
71              
72             $self->{+_FORMATTER} = $from->{+_FORMATTER}
73 60 100 33     256 unless $self->{+_FORMATTER} || exists($params{formatter});
74              
75 60 100 66     229 if ($from->{+IPC} && !$self->{+IPC} && !exists($params{ipc})) {
      100        
76 25         28 my $ipc = $from->{+IPC};
77 25         44 $self->{+IPC} = $ipc;
78 25         60 $ipc->add_hub($self->{+HID});
79             }
80              
81 60 100       149 if (my $ls = $from->{+_LISTENERS}) {
82 9         8 push @{$self->{+_LISTENERS}} => grep { $_->{inherit} } @$ls;
  9         18  
  9         12  
83             }
84              
85 60 100       170 if (my $fs = $from->{+_FILTERS}) {
86 12         12 push @{$self->{+_FILTERS}} => grep { $_->{inherit} } @$fs;
  12         33  
  6         21  
87             }
88             }
89              
90             sub format {
91 256     256 1 293 my $self = shift;
92              
93 256         296 my $old = $self->{+_FORMATTER};
94 256 100       592 ($self->{+_FORMATTER}) = @_ if @_;
95              
96 256         974 return $old;
97             }
98              
99             sub is_local {
100 6     6 0 9 my $self = shift;
101             return $$ == $self->{+PID}
102 6   66     44 && get_tid() == $self->{+TID};
103             }
104              
105             sub listen {
106 59     59 1 105 my $self = shift;
107 59         64 my ($sub, %params) = @_;
108              
109             carp "Useless addition of a listener in a child process or thread!"
110 59 50 33     389 if $$ != $self->{+PID} || get_tid() != $self->{+TID};
111              
112 59 100 66     372 croak "listen only takes coderefs for arguments, got '$sub'"
113             unless ref $sub && ref $sub eq 'CODE';
114              
115 58         55 push @{$self->{+_LISTENERS}} => { %params, code => $sub };
  58         164  
116              
117 58         97 $sub; # Intentional return.
118             }
119              
120             sub unlisten {
121 1     1 1 6 my $self = shift;
122              
123             carp "Useless removal of a listener in a child process or thread!"
124 1 50 33     7 if $$ != $self->{+PID} || get_tid() != $self->{+TID};
125              
126 1         1 my %subs = map {$_ => $_} @_;
  1         4  
127              
128 1         2 @{$self->{+_LISTENERS}} = grep { !$subs{$_->{code}} } @{$self->{+_LISTENERS}};
  1         4  
  2         4  
  1         3  
129             }
130              
131             sub filter {
132 11     11 1 46 my $self = shift;
133 11         19 my ($sub, %params) = @_;
134              
135             carp "Useless addition of a filter in a child process or thread!"
136 11 50 33     61 if $$ != $self->{+PID} || get_tid() != $self->{+TID};
137              
138 11 100 66     127 croak "filter only takes coderefs for arguments, got '$sub'"
139             unless ref $sub && ref $sub eq 'CODE';
140              
141 10         9 push @{$self->{+_FILTERS}} => { %params, code => $sub };
  10         158  
142              
143 10         21 $sub; # Intentional Return
144             }
145              
146             sub unfilter {
147 2     2 1 5 my $self = shift;
148             carp "Useless removal of a filter in a child process or thread!"
149 2 50 33     16 if $$ != $self->{+PID} || get_tid() != $self->{+TID};
150 2         5 my %subs = map {$_ => $_} @_;
  2         7  
151 2         2 @{$self->{+_FILTERS}} = grep { !$subs{$_->{code}} } @{$self->{+_FILTERS}};
  2         6  
  3         7  
  2         5  
152             }
153              
154             sub pre_filter {
155 4     4 1 42 my $self = shift;
156 4         6 my ($sub, %params) = @_;
157              
158 4 100 66     82 croak "pre_filter only takes coderefs for arguments, got '$sub'"
159             unless ref $sub && ref $sub eq 'CODE';
160              
161 3         3 push @{$self->{+_PRE_FILTERS}} => { %params, code => $sub };
  3         9  
162              
163 3         5 $sub; # Intentional Return
164             }
165              
166             sub pre_unfilter {
167 1     1 1 4 my $self = shift;
168 1         2 my %subs = map {$_ => $_} @_;
  1         5  
169 1         1 @{$self->{+_PRE_FILTERS}} = grep { !$subs{$_->{code}} } @{$self->{+_PRE_FILTERS}};
  1         4  
  2         8  
  1         2  
170             }
171              
172             sub follow_up {
173 4     4 0 44 my $self = shift;
174 4         4 my ($sub) = @_;
175              
176             carp "Useless addition of a follow-up in a child process or thread!"
177 4 50 33     20 if $$ != $self->{+PID} || get_tid() != $self->{+TID};
178              
179 4 100 66     256 croak "follow_up only takes coderefs for arguments, got '$sub'"
180             unless ref $sub && ref $sub eq 'CODE';
181              
182 2         2 push @{$self->{+_FOLLOW_UPS}} => $sub;
  2         6  
183             }
184              
185             *add_context_aquire = \&add_context_acquire;
186             sub add_context_acquire {
187 1     1 1 5 my $self = shift;
188 1         4 my ($sub) = @_;
189              
190 1 50 33     6 croak "add_context_acquire only takes coderefs for arguments, got '$sub'"
191             unless ref $sub && ref $sub eq 'CODE';
192              
193 1         1 push @{$self->{+_CONTEXT_ACQUIRE}} => $sub;
  1         1  
194              
195 1         2 $sub; # Intentional return.
196             }
197              
198             *remove_context_aquire = \&remove_context_acquire;
199             sub remove_context_acquire {
200 1     1 1 4 my $self = shift;
201 1         1 my %subs = map {$_ => $_} @_;
  1         3  
202 1         1 @{$self->{+_CONTEXT_ACQUIRE}} = grep { !$subs{$_} == $_ } @{$self->{+_CONTEXT_ACQUIRE}};
  1         2  
  1         2  
  1         2  
203             }
204              
205             sub add_context_init {
206 1     1 1 7 my $self = shift;
207 1         1 my ($sub) = @_;
208              
209 1 50 33     6 croak "add_context_init only takes coderefs for arguments, got '$sub'"
210             unless ref $sub && ref $sub eq 'CODE';
211              
212 1         2 push @{$self->{+_CONTEXT_INIT}} => $sub;
  1         3  
213              
214 1         2 $sub; # Intentional return.
215             }
216              
217             sub remove_context_init {
218 1     1 1 9 my $self = shift;
219 1         2 my %subs = map {$_ => $_} @_;
  1         4  
220 1         3 @{$self->{+_CONTEXT_INIT}} = grep { !$subs{$_} == $_ } @{$self->{+_CONTEXT_INIT}};
  1         3  
  1         4  
  1         1  
221             }
222              
223             sub add_context_release {
224 1     1 1 6 my $self = shift;
225 1         1 my ($sub) = @_;
226              
227 1 50 33     6 croak "add_context_release only takes coderefs for arguments, got '$sub'"
228             unless ref $sub && ref $sub eq 'CODE';
229              
230 1         1 push @{$self->{+_CONTEXT_RELEASE}} => $sub;
  1         2  
231              
232 1         3 $sub; # Intentional return.
233             }
234              
235             sub remove_context_release {
236 1     1 1 4 my $self = shift;
237 1         1 my %subs = map {$_ => $_} @_;
  1         3  
238 1         1 @{$self->{+_CONTEXT_RELEASE}} = grep { !$subs{$_} == $_ } @{$self->{+_CONTEXT_RELEASE}};
  1         2  
  1         3  
  1         1  
239             }
240              
241             sub send {
242 1249     1249 1 1872 my $self = shift;
243 1249         1061 my ($e) = @_;
244              
245 1249 100       1919 if ($self->{+_PRE_FILTERS}) {
246 6         3 for (@{$self->{+_PRE_FILTERS}}) {
  6         9  
247 8         10 $e = $_->{code}->($self, $e);
248 8 100       26 return unless $e;
249             }
250             }
251              
252 1246   100     2475 my $ipc = $self->{+IPC} || return $self->process($e);
253              
254 643 100       1639 if($e->global) {
255 1         4 $ipc->send($self->{+HID}, $e, 'GLOBAL');
256 1         6 return $self->process($e);
257             }
258              
259             return $ipc->send($self->{+HID}, $e)
260 642 100 66     2597 if $$ != $self->{+PID} || get_tid() != $self->{+TID};
261              
262 631         913 $self->process($e);
263             }
264              
265             sub process {
266 1257     1257 1 936 my $self = shift;
267 1257         1013 my ($e) = @_;
268              
269 1257 100       1771 if ($self->{+_FILTERS}) {
270 18         14 for (@{$self->{+_FILTERS}}) {
  18         39  
271 17         31 $e = $_->{code}->($self, $e);
272 17 100       79 return unless $e;
273             }
274             }
275              
276 1248         1268 my $type = ref($e);
277 1248         1167 my $is_ok = $type eq 'Test2::Event::Ok';
278 1248   100     2898 my $no_fail = $type eq 'Test2::Event::Diag' || $type eq 'Test2::Event::Note';
279 1248 100       2367 my $causes_fail = $is_ok ? !$e->{effective_pass} : $no_fail ? 0 : $e->causes_fail;
    100          
280              
281 1248 100 100     3021 $self->{+COUNT}++ if $is_ok || (!$no_fail && $e->increments_count);
      66        
282 1248 100 100     1694 $self->{+FAILED}++ and $self->{+_PASSING} = 0 if $causes_fail;
283              
284 1248 100 100     2643 my $callback = $e->callback($self) unless $is_ok || $no_fail;
285              
286 1248         1072 my $count = $self->{+COUNT};
287              
288 1248 100       3367 $self->{+_FORMATTER}->write($e, $count) if $self->{+_FORMATTER};
289              
290 1248 100       3557 if ($self->{+_LISTENERS}) {
291 277         300 $_->{code}->($self, $e, $count) for @{$self->{+_LISTENERS}};
  277         831  
292             }
293              
294 1248 100 100     4261 return $e if $is_ok || $no_fail;
295              
296 167         524 my $code = $e->terminate;
297 167 100       332 $self->terminate($code, $e) if defined $code;
298              
299 162         406 return $e;
300             }
301              
302             sub terminate {
303 2     2 0 2 my $self = shift;
304 2         3 my ($code) = @_;
305 2         21 exit($code);
306             }
307              
308             sub cull {
309 143     143 1 170 my $self = shift;
310              
311 143   100     360 my $ipc = $self->{+IPC} || return;
312 70 100 66     389 return if $self->{+PID} != $$ || $self->{+TID} != get_tid();
313              
314             # No need to do IPC checks on culled events
315 64         211 $self->process($_) for $ipc->cull($self->{+HID});
316             }
317              
318             sub finalize {
319 112     112 0 138 my $self = shift;
320 112         153 my ($trace, $do_plan) = @_;
321              
322 112         249 $self->cull();
323              
324 112         189 my $plan = $self->{+_PLAN};
325 112         134 my $count = $self->{+COUNT};
326 112         119 my $failed = $self->{+FAILED};
327              
328             # return if NOTHING was done.
329 112 100 100     581 return unless $do_plan || defined($plan) || $count || $failed;
      100        
      100        
330              
331 98 100       222 unless ($self->{+ENDED}) {
332 95 100       283 if ($self->{+_FOLLOW_UPS}) {
333 2         2 $_->($trace, $self) for reverse @{$self->{+_FOLLOW_UPS}};
  2         8  
334             }
335              
336             # These need to be refreshed now
337 95         108 $plan = $self->{+_PLAN};
338 95         102 $count = $self->{+COUNT};
339 95         106 $failed = $self->{+FAILED};
340              
341 95 100 100     736 if (($plan && $plan eq 'NO PLAN') || ($do_plan && !$plan)) {
      66        
      66        
342 84         443 $self->send(
343             Test2::Event::Plan->new(
344             trace => $trace,
345             max => $count,
346             )
347             );
348             }
349 95         337 $plan = $self->{+_PLAN};
350             }
351              
352 98         255 my $frame = $trace->frame;
353 98 100       226 if($self->{+ENDED}) {
354 3         6 my (undef, $ffile, $fline) = @{$self->{+ENDED}};
  3         5  
355 3         6 my (undef, $sfile, $sline) = @$frame;
356              
357 3         18 die <<" EOT"
358             Test already ended!
359             First End: $ffile line $fline
360             Second End: $sfile line $sline
361             EOT
362             }
363              
364 95         135 $self->{+ENDED} = $frame;
365 95         244 $self->is_passing(); # Generate the final boolean.
366             }
367              
368             sub is_passing {
369 208     208 1 182 my $self = shift;
370              
371 208 100       380 ($self->{+_PASSING}) = @_ if @_;
372              
373             # If we already failed just return 0.
374 208   100     406 my $pass = $self->{+_PASSING} || return 0;
375 196 100       328 return $self->{+_PASSING} = 0 if $self->{+FAILED};
376              
377 188         181 my $count = $self->{+COUNT};
378 188         170 my $ended = $self->{+ENDED};
379 188         186 my $plan = $self->{+_PLAN};
380              
381 188 100 100     436 return $pass if !$count && $plan && $plan =~ m/^SKIP$/;
      66        
382              
383 186 100 100     794 return $self->{+_PASSING} = 0
      100        
384             if $ended && (!$count || !$plan);
385              
386 176 100 66     824 return $pass unless $plan && $plan =~ m/^\d+$/;
387              
388 111 50       213 if ($ended) {
389 111 50       221 return $self->{+_PASSING} = 0 if $count != $plan;
390             }
391             else {
392 0 0       0 return $self->{+_PASSING} = 0 if $count > $plan;
393             }
394              
395 111         258 return $pass;
396             }
397              
398             sub plan {
399 119     119 1 277 my $self = shift;
400              
401 119 100       285 return $self->{+_PLAN} unless @_;
402              
403 110         141 my ($plan) = @_;
404              
405 110 50       224 confess "You cannot unset the plan"
406             unless defined $plan;
407              
408             confess "You cannot change the plan"
409 110 100 100     451 if $self->{+_PLAN} && $self->{+_PLAN} !~ m/^NO PLAN$/;
410              
411 109 100       588 confess "'$plan' is not a valid plan! Plan must be an integer greater than 0, 'NO PLAN', or 'SKIP'"
412             unless $plan =~ m/^(\d+|NO PLAN|SKIP)$/;
413              
414 108         283 $self->{+_PLAN} = $plan;
415             }
416              
417             sub check_plan {
418 40     40 1 36 my $self = shift;
419              
420 40 50       72 return undef unless $self->{+ENDED};
421 40   100     82 my $plan = $self->{+_PLAN} || return undef;
422              
423 32 50       102 return 1 if $plan !~ m/^\d+$/;
424              
425 32 50       79 return 1 if $plan == $self->{+COUNT};
426 0         0 return 0;
427             }
428              
429             sub DESTROY {
430 114     114   739 my $self = shift;
431 114   100     847 my $ipc = $self->{+IPC} || return;
432 51 100       718 return unless $$ == $self->{+PID};
433 50 50       89 return unless get_tid() == $self->{+TID};
434              
435 50         182 $ipc->drop_hub($self->{+HID});
436             }
437              
438             1;
439              
440             __END__