File Coverage

blib/lib/Test2/API/Instance.pm
Criterion Covered Total %
statement 251 263 95.4
branch 98 124 79.0
condition 38 61 62.3
subroutine 36 37 97.3
pod 15 17 88.2
total 438 502 87.2


line stmt bran cond sub pod time code
1             package Test2::API::Instance;
2 62     62   1200 use strict;
  57         63  
  57         1250  
3 57     57   156 use warnings;
  57         57  
  57         2933  
4              
5             our $VERSION = '0.000043';
6             $VERSION = eval $VERSION; ## no critic (BuiltinFunctions::ProhibitStringyEval)
7              
8             our @CARP_NOT = qw/Test2::API Test2::API::Instance Test2::IPC::Driver Test2::Formatter/;
9 57     57   188 use Carp qw/confess carp/;
  57         58  
  57         2926  
10 57     57   186 use Scalar::Util qw/reftype/;
  57         77  
  57         3145  
11              
12 57     57   7652 use Test2::Util qw/get_tid USE_THREADS CAN_FORK pkg_to_file try/;
  57         87  
  57         2784  
13              
14 57     57   17832 use Test2::Util::Trace();
  57         68  
  57         904  
15 57     57   18364 use Test2::API::Stack();
  57         90  
  57         1930  
16              
17 57         205 use Test2::Util::HashBase qw{
18             pid tid
19             no_wait
20             finalized loaded
21             ipc stack formatter
22             contexts
23              
24             ipc_shm_size
25             ipc_shm_last
26             ipc_shm_id
27             ipc_polling
28             ipc_drivers
29             formatters
30              
31             exit_callbacks
32             post_load_callbacks
33             context_acquire_callbacks
34             context_init_callbacks
35             context_release_callbacks
36 57     57   235 };
  57         56  
37              
38             # Wrap around the getters that should call _finalize.
39             BEGIN {
40 57     57   716 for my $finalizer (IPC, FORMATTER) {
41 114         1606 my $orig = __PACKAGE__->can($finalizer);
42             my $new = sub {
43 207     207   292 my $self = shift;
44 207 100       618 $self->_finalize unless $self->{+FINALIZED};
45 201         579 $self->$orig;
46 114         942 };
47              
48 57     57   904 no strict 'refs';
  57         927  
  57         3375  
49 57     57   170 no warnings 'redefine';
  57         752  
  57         3221  
50 114         113 *{$finalizer} = $new;
  114         128068  
51             }
52             }
53              
54             sub import {
55 78     78   103 my $class = shift;
56 78 100       663 return unless @_;
57 57         72 my ($ref) = @_;
58 57         147 $$ref = $class->new;
59             }
60              
61 62     62 0 126 sub init { $_[0]->reset }
62              
63             sub reset {
64 157     157 1 1068 my $self = shift;
65              
66 157         740 $self->{+PID} = $$;
67 157         179 $self->{+TID} = get_tid();
68 157         207 $self->{+CONTEXTS} = {};
69              
70 157         219 $self->{+IPC_DRIVERS} = [];
71 157         799 $self->{+IPC_POLLING} = undef;
72              
73 157         202 $self->{+FORMATTERS} = [];
74 157         150 $self->{+FORMATTER} = undef;
75              
76 157         157 $self->{+FINALIZED} = undef;
77 157         188 $self->{+IPC} = undef;
78              
79 157         194 $self->{+NO_WAIT} = 0;
80 157         143 $self->{+LOADED} = 0;
81              
82 157         172 $self->{+EXIT_CALLBACKS} = [];
83 157         159 $self->{+POST_LOAD_CALLBACKS} = [];
84 157         154 $self->{+CONTEXT_ACQUIRE_CALLBACKS} = [];
85 157         129 $self->{+CONTEXT_INIT_CALLBACKS} = [];
86 157         301 $self->{+CONTEXT_RELEASE_CALLBACKS} = [];
87              
88 157         723 $self->{+STACK} = Test2::API::Stack->new;
89             }
90              
91             sub _finalize {
92 94     94   262 my $self = shift;
93 94         131 my ($caller) = @_;
94 94   50     1032 $caller ||= [caller(1)];
95              
96 94         194 $self->{+FINALIZED} = $caller;
97              
98 94 100       252 unless ($self->{+FORMATTER}) {
99 89         109 my ($formatter, $source);
100 89 100       253 if ($ENV{T2_FORMATTER}) {
    100          
101 15         15 $source = "set by the 'T2_FORMATTER' environment variable";
102              
103 15 50       70 if ($ENV{T2_FORMATTER} =~ m/^(\+)?(.*)$/) {
104 15 100       55 $formatter = $1 ? $2 : "Test2::Formatter::$2"
105             }
106             else {
107 0         0 $formatter = '';
108             }
109             }
110 74         252 elsif (@{$self->{+FORMATTERS}}) {
111 1         1 ($formatter) = @{$self->{+FORMATTERS}};
  1         2  
112 1         2 $source = "Most recently added";
113             }
114             else {
115 73         137 $formatter = 'Test2::Formatter::TAP';
116 73         112 $source = 'default formatter';
117             }
118              
119 89 100 66     1028 unless (ref($formatter) || $formatter->can('write')) {
120 60         228 my $file = pkg_to_file($formatter);
121 60     60   339 my ($ok, $err) = try { require $file };
  60         25632  
122 60 100       288 unless ($ok) {
123 5         20 my $line = "* COULD NOT LOAD FORMATTER '$formatter' ($source) *";
124 5         10 my $border = '*' x length($line);
125 5         25 die "\n\n $border\n $line\n $border\n\n$err";
126             }
127             }
128              
129 84         218 $self->{+FORMATTER} = $formatter;
130             }
131              
132             # Turn on IPC if threads are on, drivers are reigstered, or the Test2::IPC
133             # module is loaded.
134 89 100 100     293 return unless USE_THREADS || $INC{'Test2/IPC.pm'} || @{$self->{+IPC_DRIVERS}};
  39         214  
135              
136             # Turn on polling by default, people expect it.
137 53         154 $self->enable_ipc_polling;
138              
139 53 100       51 unless (@{$self->{+IPC_DRIVERS}}) {
  53         142  
140 50     50   258 my ($ok, $error) = try { require Test2::IPC::Driver::Files };
  50         9293  
141 50 50       177 die $error unless $ok;
142 50         66 push @{$self->{+IPC_DRIVERS}} => 'Test2::IPC::Driver::Files';
  50         161  
143             }
144              
145 53         72 for my $driver (@{$self->{+IPC_DRIVERS}}) {
  53         123  
146 53 100 66     608 next unless $driver->can('is_viable') && $driver->is_viable;
147 52 50       197 $self->{+IPC} = $driver->new or next;
148 52 50       153 $self->ipc_enable_shm if $self->{+IPC}->use_shm;
149 52         103 return;
150             }
151              
152 1         6 die "IPC has been requested, but no viable drivers were found. Aborting...\n";
153             }
154              
155 11 100   11 1 155 sub formatter_set { $_[0]->{+FORMATTER} ? 1 : 0 }
156              
157             sub add_formatter {
158 1     1 1 1 my $self = shift;
159 1         2 my ($formatter) = @_;
160 1         1 unshift @{$self->{+FORMATTERS}} => $formatter;
  1         3  
161              
162 1 50       26 return unless $self->{+FINALIZED};
163              
164             # Why is the @CARP_NOT entry not enough?
165 0         0 local %Carp::Internal = %Carp::Internal;
166 0         0 $Carp::Internal{'Test2::Formatter'} = 1;
167              
168 0         0 carp "Formatter $formatter loaded too late to be used as the global formatter";
169             }
170              
171             sub add_context_acquire_callback {
172 3     3 0 4 my $self = shift;
173 3         3 my ($code) = @_;
174              
175 3   50     11 my $rtype = reftype($code) || "";
176              
177 3 50 33     17 confess "Context-acquire callbacks must be coderefs"
178             unless $code && $rtype eq 'CODE';
179              
180 3         4 push @{$self->{+CONTEXT_ACQUIRE_CALLBACKS}} => $code;
  3         6  
181             }
182              
183             sub add_context_init_callback {
184 57     57 1 61 my $self = shift;
185 57         65 my ($code) = @_;
186              
187 57   50     377 my $rtype = reftype($code) || "";
188              
189 57 50 33     288 confess "Context-init callbacks must be coderefs"
190             unless $code && $rtype eq 'CODE';
191              
192 57         54 push @{$self->{+CONTEXT_INIT_CALLBACKS}} => $code;
  57         158  
193             }
194              
195             sub add_context_release_callback {
196 4     4 1 6 my $self = shift;
197 4         4 my ($code) = @_;
198              
199 4   50     21 my $rtype = reftype($code) || "";
200              
201 4 50 33     27 confess "Context-release callbacks must be coderefs"
202             unless $code && $rtype eq 'CODE';
203              
204 4         15 push @{$self->{+CONTEXT_RELEASE_CALLBACKS}} => $code;
  4         15  
205             }
206              
207             sub add_post_load_callback {
208 18     18 1 65 my $self = shift;
209 18         26 my ($code) = @_;
210              
211 18   50     54 my $rtype = reftype($code) || "";
212              
213 18 100 66     742 confess "Post-load callbacks must be coderefs"
214             unless $code && $rtype eq 'CODE';
215              
216 13         14 push @{$self->{+POST_LOAD_CALLBACKS}} => $code;
  13         20  
217 13 100       40 $code->() if $self->{+LOADED};
218             }
219              
220             sub load {
221 67     67 1 120 my $self = shift;
222 67 100       197 unless ($self->{+LOADED}) {
223             # This is for https://github.com/Test-More/test-more/issues/16
224             # and https://rt.perl.org/Public/Bug/Display.html?id=127774
225             # END blocks run in reverse order. This insures the END block is loaded
226             # as late as possible. It will not solve all cases, but it helps.
227 55 50   55   7624 eval "END { Test2::API::test2_set_is_end() }; 1" or die $@;
  62         3573  
228              
229 62         255 $self->{+LOADED} = 1;
230 62         101 $_->() for @{$self->{+POST_LOAD_CALLBACKS}};
  62         251  
231             }
232 67         156 return $self->{+LOADED};
233             }
234              
235             sub add_exit_callback {
236 18     18 1 63 my $self = shift;
237 18         25 my ($code) = @_;
238 18   50     61 my $rtype = reftype($code) || "";
239              
240 18 100 66     533 confess "End callbacks must be coderefs"
241             unless $code && $rtype eq 'CODE';
242              
243 13         14 push @{$self->{+EXIT_CALLBACKS}} => $code;
  13         23  
244             }
245              
246             sub add_ipc_driver {
247 5     5 1 20 my $self = shift;
248 5         7 my ($driver) = @_;
249 5         4 unshift @{$self->{+IPC_DRIVERS}} => $driver;
  5         30  
250              
251 5 100       36 return unless $self->{+FINALIZED};
252              
253             # Why is the @CARP_NOT entry not enough?
254 2         12 local %Carp::Internal = %Carp::Internal;
255 2         5 $Carp::Internal{'Test2::IPC::Driver'} = 1;
256              
257 2         410 carp "IPC driver $driver loaded too late to be used as the global ipc driver";
258             }
259              
260             sub enable_ipc_polling {
261 56     56 1 74 my $self = shift;
262              
263             $self->add_context_init_callback(
264             # This is called every time a context is created, it needs to be fast.
265             # $_[0] is a context object
266             sub {
267 604 100   604   1030 return unless $self->{+IPC_POLLING};
268 602 100       933 return $_[0]->{hub}->cull unless $self->{+IPC_SHM_ID};
269              
270 592         401 my $val;
271             {
272 592 50       424 shmread($self->{+IPC_SHM_ID}, $val, 0, $self->{+IPC_SHM_SIZE}) or return;
  592         10107  
273              
274 592 100       2775 return if $val eq $self->{+IPC_SHM_LAST};
275 8         28 $self->{+IPC_SHM_LAST} = $val;
276             }
277              
278 8         46 $_[0]->{hub}->cull;
279             }
280 56 100       172 ) unless defined $self->ipc_polling;
281              
282 56         159 $self->set_ipc_polling(1);
283             }
284              
285             sub ipc_enable_shm {
286 52     52 1 68 my $self = shift;
287              
288 52 100       137 return 1 if defined $self->{+IPC_SHM_ID};
289              
290             my ($ok, $err) = try {
291 27     27   9659 require IPC::SysV;
292              
293 27         19036 my $ipc_key = IPC::SysV::IPC_PRIVATE();
294 27 50       565 my $shm_size = $self->{+IPC}->can('shm_size') ? $self->{+IPC}->shm_size : 64;
295 27 100       619 my $shm_id = shmget($ipc_key, $shm_size, 0666) or die;
296              
297 23         69 my $initial = 'a' x $shm_size;
298 23 50       634 shmwrite($shm_id, $initial, 0, $shm_size) or die;
299              
300 23         64 $self->{+IPC_SHM_SIZE} = $shm_size;
301 23         47 $self->{+IPC_SHM_ID} = $shm_id;
302 23         83 $self->{+IPC_SHM_LAST} = $initial;
303 27         185 };
304              
305 27         386 return $ok;
306             }
307              
308             sub get_ipc_pending {
309 0     0 1 0 my $self = shift;
310 0 0       0 return -1 unless defined $self->{+IPC_SHM_ID};
311 0         0 my $val;
312 0 0       0 shmread($self->{+IPC_SHM_ID}, $val, 0, $self->{+IPC_SHM_SIZE}) or return -1;
313 0 0       0 return 0 if $val eq $self->{+IPC_SHM_LAST};
314 0         0 $self->{+IPC_SHM_LAST} = $val;
315 0         0 return 1;
316             }
317              
318             sub set_ipc_pending {
319 33     33 1 62 my $self = shift;
320              
321 33 100       144 return undef unless defined $self->{+IPC_SHM_ID};
322              
323 20         63 my ($val) = @_;
324              
325 20 50       49 confess "value is required for set_ipc_pending"
326             unless $val;
327              
328 20         412 shmwrite($self->{+IPC_SHM_ID}, $val, 0, $self->{+IPC_SHM_SIZE});
329             }
330              
331             sub disable_ipc_polling {
332 3     3 1 4 my $self = shift;
333 3 100       13 return unless defined $self->{+IPC_POLLING};
334 2         4 $self->{+IPC_POLLING} = 0;
335             }
336              
337             sub _ipc_wait {
338 23     23   2733 my $fail = 0;
339              
340 23         89 while (CAN_FORK) {
341 37         15072129 my $pid = CORE::wait();
342 37         136 my $err = $?;
343 37 100       170 last if $pid == -1;
344 14 100       98 next unless $err;
345 6         24 $fail++;
346 6         15 $err = $err >> 8;
347 6         250 warn "Process $pid did not exit cleanly (status: $err)\n";
348             }
349              
350 23         61 if (USE_THREADS) {
351             for my $t (threads->list()) {
352             $t->join;
353             # In older threads we cannot check if a thread had an error unless
354             # we control it and its return.
355             my $err = $t->can('error') ? $t->error : undef;
356             next unless $err;
357             my $tid = $t->tid();
358             $fail++;
359             chomp($err);
360             warn "Thread $tid did not end cleanly: $err\n";
361             }
362             }
363              
364 23 100       147 return 0 unless $fail;
365 6         60 return 255;
366             }
367              
368             sub set_exit {
369 91     91 1 3974 my $self = shift;
370              
371 91         158 my $exit = $?;
372 91         117 my $new_exit = $exit;
373              
374 91 100 66     421 if ($INC{'Test/Builder.pm'} && $Test::Builder::VERSION ne $Test2::API::VERSION) {
375 3         12 print STDERR <<" EOT";
376              
377             ********************************************************************************
378             * *
379             * Test::Builder -- Test2::API version mismatch detected *
380             * *
381             ********************************************************************************
382             Test2::API Version: $Test2::API::VERSION
383             Test::Builder Version: $Test::Builder::VERSION
384              
385             This is not a supported configuration, you will have problems.
386              
387             EOT
388             }
389              
390 91         121 for my $ctx (values %{$self->{+CONTEXTS}}) {
  91         475  
391 5 100       17 next unless $ctx;
392              
393 1 50 33     6 next if $ctx->_aborted && ${$ctx->_aborted};
  0         0  
394              
395             # Only worry about contexts in this PID
396 1   50     5 my $trace = $ctx->trace || next;
397 1 50       5 next unless $trace->pid == $$;
398              
399             # Do not worry about contexts that have no hub
400 1   50     4 my $hub = $ctx->hub || next;
401              
402             # Do not worry if the state came to a sudden end.
403 1 50       4 next if $hub->bailed_out;
404 1 50       3 next if defined $hub->skip_reason;
405              
406             # now we worry
407 1         4 $trace->alert("context object was never released! This means a testing tool is behaving very badly");
408              
409 1         6 $exit = 255;
410 1         1 $new_exit = 255;
411             }
412              
413 91 100 100     655 if ($self->{+PID} != $$ or $self->{+TID} != get_tid()) {
414 13         37 $? = $exit;
415 13         6 return;
416             }
417              
418 78 50       424 my @hubs = $self->{+STACK} ? $self->{+STACK}->all : ();
419              
420 78 100 66     489 if (@hubs and $self->{+IPC} and !$self->{+NO_WAIT}) {
      66        
421 16         44 local $?;
422 16         20 my %seen;
423 16         30 for my $hub (reverse @hubs) {
424 16 50       92 my $ipc = $hub->ipc or next;
425 16 50       83 next if $seen{$ipc}++;
426 16         87 $ipc->waiting();
427             }
428              
429 16         49 my $ipc_exit = _ipc_wait();
430 16   100     137 $new_exit ||= $ipc_exit;
431             }
432              
433             # None of this is necessary if we never got a root hub
434 78 100       231 if(my $root = shift @hubs) {
435 66         536 my $trace = Test2::Util::Trace->new(
436             frame => [__PACKAGE__, __FILE__, 0, __PACKAGE__ . '::END'],
437             detail => __PACKAGE__ . ' END Block finalization',
438             );
439 66         275 my $ctx = Test2::API::Context->new(
440             trace => $trace,
441             hub => $root,
442             );
443              
444 66 100       485 if (@hubs) {
445 6         21 $ctx->diag("Test ended with extra hubs on the stack!");
446 6         9 $new_exit = 255;
447             }
448              
449 66 100       220 unless ($root->no_ending) {
450 63         158 local $?;
451 63 100       213 $root->finalize($trace) unless $root->ended;
452 63         81 $_->($ctx, $exit, \$new_exit) for @{$self->{+EXIT_CALLBACKS}};
  63         213  
453 63   100     580 $new_exit ||= $root->failed;
454             }
455             }
456              
457 78 100       213 $new_exit = 255 if $new_exit > 255;
458              
459 78 100       230 if ($new_exit) {
460 22         2215 require Test2::API::Breakage;
461 22         97 my @warn = Test2::API::Breakage->report();
462              
463 22 100       51 if (@warn) {
464 3         9 print STDERR "\nYou have loaded versions of test modules known to have problems with Test2.\nThis could explain some test failures.\n";
465 3         9 print STDERR "$_\n" for @warn;
466 3         6 print STDERR "\n";
467             }
468             }
469              
470 78         91 $? = $new_exit;
471             }
472              
473             1;
474              
475             __END__