File Coverage

inc/Test2/Hub.pm
Criterion Covered Total %
statement 133 263 50.5
branch 47 126 37.3
condition 39 116 33.6
subroutine 20 35 57.1
pod 20 27 74.0
total 259 567 45.6


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