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