File Coverage

inc/Test2/Hub.pm
Criterion Covered Total %
statement 111 263 42.2
branch 39 126 30.9
condition 24 116 20.6
subroutine 17 35 48.5
pod 20 27 74.0
total 211 567 37.2


line stmt bran cond sub pod time code
1             #line 1
2 1     1   8 package Test2::Hub;
  1         2  
  1         32  
3 1     1   7 use strict;
  1         2  
  1         49  
4             use warnings;
5              
6             our $VERSION = '1.302073';
7              
8 1     1   7  
  1         3  
  1         58  
9 1     1   7 use Carp qw/carp croak confess/;
  1         2  
  1         63  
10             use Test2::Util qw/get_tid ipc_separator/;
11 1     1   23  
  1         2  
  1         55  
12             use Scalar::Util qw/weaken/;
13 1     1   385  
  1         2  
  1         68  
14 1         4 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 1     1   5 skip_reason
  1         1  
35             };
36              
37             my $ID_POSTFIX = 1;
38 1     1 0 2 sub init {
39             my $self = shift;
40 1         4  
41 1         3 $self->{+PID} = $$;
42 1         6 $self->{+TID} = get_tid();
43             $self->{+HID} = join ipc_separator, $self->{+PID}, $self->{+TID}, $ID_POSTFIX++;
44 1         2  
45 1         2 $self->{+COUNT} = 0;
46 1         1 $self->{+FAILED} = 0;
47             $self->{+_PASSING} = 1;
48 1 50       2  
49 0         0 if (my $formatter = delete $self->{formatter}) {
50             $self->format($formatter);
51             }
52 1 50       4  
53 0         0 if (my $ipc = $self->{+IPC}) {
54             $ipc->add_hub($self->{+HID});
55             }
56             }
57 1     1 0 12  
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 0     0 0 0 sub inherit {
74 0         0 my $self = shift;
75             my ($from, %params) = @_;
76              
77 0 0 0     0 $self->{+_FORMATTER} = $from->{+_FORMATTER}
78             unless $self->{+_FORMATTER} || exists($params{formatter});
79 0 0 0     0  
      0        
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 0 0       0  
86 0         0 if (my $ls = $from->{+_LISTENERS}) {
  0         0  
  0         0  
87             push @{$self->{+_LISTENERS}} => grep { $_->{inherit} } @$ls;
88             }
89 0 0       0  
90 0         0 if (my $pfs = $from->{+_PRE_FILTERS}) {
  0         0  
  0         0  
91             push @{$self->{+_PRE_FILTERS}} => grep { $_->{inherit} } @$pfs;
92             }
93 0 0       0  
94 0         0 if (my $fs = $from->{+_FILTERS}) {
  0         0  
  0         0  
95             push @{$self->{+_FILTERS}} => grep { $_->{inherit} } @$fs;
96             }
97             }
98              
99 6     6 1 8 sub format {
100             my $self = shift;
101 6         8  
102 6 100       11 my $old = $self->{+_FORMATTER};
103             ($self->{+_FORMATTER}) = @_ if @_;
104 6         15  
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 0     0 1 0 sub listen {
115 0         0 my $self = shift;
116             my ($sub, %params) = @_;
117              
118 0 0 0     0 carp "Useless addition of a listener in a child process or thread!"
119             if $$ != $self->{+PID} || get_tid() != $self->{+TID};
120 0 0 0     0  
121             croak "listen only takes coderefs for arguments, got '$sub'"
122             unless ref $sub && ref $sub eq 'CODE';
123 0         0  
  0         0  
124             push @{$self->{+_LISTENERS}} => { %params, code => $sub };
125 0         0  
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 1     1 1 2 sub pre_filter {
164 1         3 my $self = shift;
165             my ($sub, %params) = @_;
166 1 50 33     5  
167             croak "pre_filter only takes coderefs for arguments, got '$sub'"
168             unless ref $sub && ref $sub eq 'CODE';
169 1         1  
  1         5  
170             push @{$self->{+_PRE_FILTERS}} => { %params, code => $sub };
171 1         3  
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 124     124 1 149 sub send {
251 124         188 my $self = shift;
252             my ($e) = @_;
253 124 50       303  
254 124         142 if ($self->{+_PRE_FILTERS}) {
  124         256  
255 124         298 for (@{$self->{+_PRE_FILTERS}}) {
256 124 50       237 $e = $_->{code}->($self, $e);
257             return unless $e;
258             }
259             }
260 124   50     398  
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 124     124 1 200 sub process {
275 124         154 my $self = shift;
276             my ($e) = @_;
277 124 50       204  
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 124         215  
285 124         165 my $type = ref($e);
286 124   33     278 my $is_ok = $type eq 'Test2::Event::Ok';
287 124 50       282 my $no_fail = $type eq 'Test2::Event::Diag' || $type eq 'Test2::Event::Note';
    100          
288 124   66     233 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 124 100       229  
291 124 50 33     206 $self->{+COUNT}++ if $counted;
292 124 50       193 $self->{+FAILED}++ if $causes_fail && $counted;
293             $self->{+_PASSING} = 0 if $causes_fail;
294 124 100 66     292  
295             my $callback = $e->callback($self) unless $is_ok || $no_fail;
296 124         172  
297             my $count = $self->{+COUNT};
298 124 50       550  
299             $self->{+_FORMATTER}->write($e, $count) if $self->{+_FORMATTER};
300 124 50       9019  
301 0         0 if ($self->{+_LISTENERS}) {
  0         0  
302             $_->{code}->($self, $e, $count) for @{$self->{+_LISTENERS}};
303             }
304 124 100 66     388  
305             return $e if $is_ok || $no_fail;
306 1         4  
307 1 50       2 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 1         10  
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 1     1 1 2 sub cull {
323             my $self = shift;
324 1   50     5  
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 1     1 0 3 sub finalize {
333 1         2 my $self = shift;
334             my ($trace, $do_plan) = @_;
335 1         5  
336             $self->cull();
337 1         3  
338 1         2 my $plan = $self->{+_PLAN};
339 1         3 my $count = $self->{+COUNT};
340 1         2 my $failed = $self->{+FAILED};
341             my $active = $self->{+ACTIVE};
342              
343 1 0 33     9 # return if NOTHING was done.
      33        
      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 1 50       2  
349 1 50       4 unless ($self->{+ENDED}) {
350 0         0 if ($self->{+_FOLLOW_UPS}) {
  0         0  
351             $_->($trace, $self) for reverse @{$self->{+_FOLLOW_UPS}};
352             }
353              
354 1         2 # These need to be refreshed now
355 1         2 $plan = $self->{+_PLAN};
356 1         2 $count = $self->{+COUNT};
357             $failed = $self->{+FAILED};
358 1 50 33     10  
      33        
      33        
359 0         0 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 1         2 }
367             $plan = $self->{+_PLAN};
368             }
369 1         4  
370 1 50       3 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 1         2  
382 1         4 $self->{+ENDED} = $frame;
383             my $pass = $self->is_passing(); # Generate the final boolean.
384 1 50       7  
385             $self->{+_FORMATTER}->finalize($plan, $count, $failed, $pass, $self->is_subtest) if $self->{+_FORMATTER};
386 1         3  
387             return $pass;
388             }
389              
390 3     3 1 3 sub is_passing {
391             my $self = shift;
392 3 50       6  
393             ($self->{+_PASSING}) = @_ if @_;
394              
395 3 50       7 # If we already failed just return 0.
396 3 50       6 my $pass = $self->{+_PASSING} or return 0;
397             return $self->{+_PASSING} = 0 if $self->{+FAILED};
398 3         5  
399 3         3 my $count = $self->{+COUNT};
400 3         5 my $ended = $self->{+ENDED};
401             my $plan = $self->{+_PLAN};
402 3 0 33     6  
      33        
403             return $pass if !$count && $plan && $plan =~ m/^SKIP$/;
404 3 50 33     19  
      33        
405             return $self->{+_PASSING} = 0
406             if $ended && (!$count || !$plan);
407 3 50 33     14  
408             return $pass unless $plan && $plan =~ m/^\d+$/;
409 3 50       5  
410 3 50       6 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 3         8  
417             return $pass;
418             }
419              
420 5     5 1 7 sub plan {
421             my $self = shift;
422 5 100       28  
423             return $self->{+_PLAN} unless @_;
424 1         13  
425             my ($plan) = @_;
426 1 50       3  
427             confess "You cannot unset the plan"
428             unless defined $plan;
429              
430 1 50 33     2 confess "You cannot change the plan"
431             if $self->{+_PLAN} && $self->{+_PLAN} !~ m/^NO PLAN$/;
432 1 50       33  
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 1         5  
436             $self->{+_PLAN} = $plan;
437             }
438              
439 0     0 1   sub check_plan {
440             my $self = shift;
441 0 0          
442 0   0       return undef unless $self->{+ENDED};
443             my $plan = $self->{+_PLAN} || return undef;
444 0 0          
445             return 1 if $plan !~ m/^\d+$/;
446 0 0          
447 0           return 1 if $plan == $self->{+COUNT};
448             return 0;
449             }
450              
451 0     0     sub DESTROY {
452 0   0       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__