File Coverage

inc/Test2/API.pm
Criterion Covered Total %
statement 118 284 41.5
branch 17 98 17.3
condition 25 92 27.1
subroutine 40 77 51.9
pod 35 39 89.7
total 235 590 39.8


line stmt bran cond sub pod time code
1             #line 1
2 6     6   41 package Test2::API;
  6         24  
  6         172  
3 6     6   30 use strict;
  6         12  
  6         239  
4             use warnings;
5              
6 6   50 6   102 BEGIN {
7 6         557 $ENV{TEST_ACTIVE} ||= 1;
8             $ENV{TEST2_ACTIVE} = 1;
9             }
10              
11             our $VERSION = '1.302073';
12              
13              
14             my $INST;
15 24 100   24 1 332 my $ENDING = 0;
16 0     0 1 0 sub test2_set_is_end { ($ENDING) = @_ ? @_ : (1) }
17             sub test2_get_is_end { $ENDING }
18 6     6   3297  
  6         19  
  6         25  
19             use Test2::API::Instance(\$INST);
20             # Set the exit status
21 6     6   26 END {
22 6         36 test2_set_is_end(); # See gh #16
23             $INST->set_exit();
24             }
25              
26             # See gh #16
27 6     6   42 {
  6         14  
  6         432  
28 6 50   6   466 no warnings;
  2     2   148  
29             INIT { eval 'END { test2_set_is_end() }; 1' or die $@ }
30             }
31              
32 6     6   58 BEGIN {
  6         14  
  6         591  
33 6 50 33 6   62 no warnings 'once';
      0        
34 6         164 if($] ge '5.014' || $ENV{T2_CHECK_DEPTH} || $Test2::API::DO_DEPTH_CHECK) {
35             *DO_DEPTH_CHECK = sub() { 1 };
36             }
37 0         0 else {
38             *DO_DEPTH_CHECK = sub() { 0 };
39             }
40             }
41 6     6   41  
  6         16  
  6         133  
42             use Test2::Util::Trace();
43 6     6   2726  
  6         15  
  6         112  
44 6     6   2481 use Test2::Hub::Subtest();
  6         20  
  6         117  
45 6     6   35 use Test2::Hub::Interceptor();
  6         11  
  6         136  
46             use Test2::Hub::Interceptor::Terminator();
47 6     6   2479  
  6         19  
  6         116  
48 6     6   2518 use Test2::Event::Ok();
  6         18  
  6         114  
49 6     6   2489 use Test2::Event::Diag();
  6         18  
  6         114  
50 6     6   2511 use Test2::Event::Note();
  6         17  
  6         119  
51 6     6   2501 use Test2::Event::Plan();
  6         17  
  6         118  
52 6     6   2437 use Test2::Event::Bail();
  6         21  
  6         113  
53 6     6   2486 use Test2::Event::Exception();
  6         16  
  6         274  
54 6     6   2493 use Test2::Event::Waiting();
  6         13  
  6         113  
55 6     6   2421 use Test2::Event::Skip();
  6         18  
  6         148  
56             use Test2::Event::Subtest();
57 6     6   35  
  6         11  
  6         363  
58 6     6   34 use Carp qw/carp croak confess longmess/;
  6         11  
  6         263  
59 6     6   39 use Scalar::Util qw/blessed weaken/;
  6         13  
  6         665  
60             use Test2::Util qw/get_tid/;
61              
62             our @EXPORT_OK = qw{
63             context release
64             context_do
65             no_context
66             intercept
67             run_subtest
68              
69             test2_init_done
70             test2_load_done
71              
72             test2_set_is_end
73             test2_get_is_end
74              
75             test2_pid
76             test2_tid
77             test2_stack
78             test2_no_wait
79              
80             test2_add_callback_context_aquire
81             test2_add_callback_context_acquire
82             test2_add_callback_context_init
83             test2_add_callback_context_release
84             test2_add_callback_exit
85             test2_add_callback_post_load
86             test2_list_context_aquire_callbacks
87             test2_list_context_acquire_callbacks
88             test2_list_context_init_callbacks
89             test2_list_context_release_callbacks
90             test2_list_exit_callbacks
91             test2_list_post_load_callbacks
92              
93             test2_ipc
94             test2_ipc_drivers
95             test2_ipc_add_driver
96             test2_ipc_polling
97             test2_ipc_disable_polling
98             test2_ipc_enable_polling
99             test2_ipc_get_pending
100             test2_ipc_set_pending
101             test2_ipc_enable_shm
102              
103             test2_formatter
104             test2_formatters
105             test2_formatter_add
106             test2_formatter_set
107 6     6   47 };
  6         17024  
108             BEGIN { require Exporter; our @ISA = qw(Exporter) }
109              
110             my $STACK = $INST->stack;
111             my $CONTEXTS = $INST->contexts;
112             my $INIT_CBS = $INST->context_init_callbacks;
113             my $ACQUIRE_CBS = $INST->context_acquire_callbacks;
114 6     6 1 33  
115 6     6 1 22 sub test2_init_done { $INST->finalized }
116             sub test2_load_done { $INST->loaded }
117 0     0 0 0  
118 0     0 0 0 sub test2_pid { $INST->pid }
119 6     6 1 35 sub test2_tid { $INST->tid }
120             sub test2_stack { $INST->stack }
121 0 0   0 1 0 sub test2_no_wait {
122 0         0 $INST->set_no_wait(@_) if @_;
123             $INST->no_wait;
124             }
125 0     0 1 0  
126 6     6 0 35 sub test2_add_callback_context_acquire { $INST->add_context_acquire_callback(@_) }
127 0     0 1 0 sub test2_add_callback_context_aquire { $INST->add_context_acquire_callback(@_) }
128 0     0 1 0 sub test2_add_callback_context_init { $INST->add_context_init_callback(@_) }
129 6     6 1 25 sub test2_add_callback_context_release { $INST->add_context_release_callback(@_) }
130 0     0 1 0 sub test2_add_callback_exit { $INST->add_exit_callback(@_) }
131 0     0 0 0 sub test2_add_callback_post_load { $INST->add_post_load_callback(@_) }
  0         0  
132 0     0 1 0 sub test2_list_context_aquire_callbacks { @{$INST->context_acquire_callbacks} }
  0         0  
133 0     0 1 0 sub test2_list_context_acquire_callbacks { @{$INST->context_acquire_callbacks} }
  0         0  
134 0     0 1 0 sub test2_list_context_init_callbacks { @{$INST->context_init_callbacks} }
  0         0  
135 0     0 1 0 sub test2_list_context_release_callbacks { @{$INST->context_release_callbacks} }
  0         0  
136 0     0 1 0 sub test2_list_exit_callbacks { @{$INST->exit_callbacks} }
  0         0  
137             sub test2_list_post_load_callbacks { @{$INST->post_load_callbacks} }
138 6     6 1 30  
139 0     0 1 0 sub test2_ipc { $INST->ipc }
140 0     0 1 0 sub test2_ipc_add_driver { $INST->add_ipc_driver(@_) }
  0         0  
141 0     0 1 0 sub test2_ipc_drivers { @{$INST->ipc_drivers} }
142 0     0 1 0 sub test2_ipc_polling { $INST->ipc_polling }
143 0     0 1 0 sub test2_ipc_enable_polling { $INST->enable_ipc_polling }
144 0     0 1 0 sub test2_ipc_disable_polling { $INST->disable_ipc_polling }
145 0     0 1 0 sub test2_ipc_get_pending { $INST->get_ipc_pending }
146 0     0 1 0 sub test2_ipc_set_pending { $INST->set_ipc_pending(@_) }
147             sub test2_ipc_enable_shm { $INST->ipc_enable_shm }
148 6     6 1 34  
149 0     0 1 0 sub test2_formatter { $INST->formatter }
  0         0  
150 6     6 1 89 sub test2_formatters { @{$INST->formatters} }
151             sub test2_formatter_add { $INST->add_formatter(@_) }
152 0     0 1 0 sub test2_formatter_set {
153 0 0       0 my ($formatter) = @_;
154 0 0       0 croak "No formatter specified" unless $formatter;
155 0         0 croak "Global Formatter already set" if $INST->formatter_set;
156             $INST->set_formatter($formatter);
157             }
158              
159 6     6   24 # Private, for use in Test2::API::Context
160 0     0   0 sub _contexts_ref { $INST->contexts }
161 0     0   0 sub _context_acquire_callbacks_ref { $INST->context_acquire_callbacks }
162 6     6   32 sub _context_init_callbacks_ref { $INST->context_init_callbacks }
163             sub _context_release_callbacks_ref { $INST->context_release_callbacks }
164              
165 0     0   0 # Private, for use in Test2::IPC
166             sub _set_ipc { $INST->set_ipc(@_) }
167              
168 0     0 1 0 sub context_do(&;@) {
169 0         0 my $code = shift;
170             my @args = @_;
171 0         0  
172             my $ctx = context(level => 1);
173 0         0  
174             my $want = wantarray;
175 0         0  
176 0         0 my @out;
177 0 0       0 my $ok = eval {
    0          
178             $want ? @out = $code->($ctx, @args) :
179             defined($want) ? $out[0] = $code->($ctx, @args) :
180 0         0 $code->($ctx, @args) ;
181             1;
182 0         0 };
183             my $err = $@;
184 0         0  
185             $ctx->release;
186 0 0       0  
187             die $err unless $ok;
188 0 0       0  
189 0 0       0 return @out if $want;
190 0         0 return $out[0] if defined $want;
191             return;
192             }
193              
194 0     0 1 0 sub no_context(&;$) {
195 0   0     0 my ($code, $hid) = @_;
196             $hid ||= $STACK->top->hid;
197 0         0  
198 0         0 my $ctx = $CONTEXTS->{$hid};
199 0         0 delete $CONTEXTS->{$hid};
  0         0  
  0         0  
200 0         0 my $ok = eval { $code->(); 1 };
201             my $err = $@;
202 0         0  
203 0         0 $CONTEXTS->{$hid} = $ctx;
204             weaken($CONTEXTS->{$hid});
205 0 0       0  
206             die $err unless $ok;
207 0         0  
208             return;
209             };
210              
211             sub context {
212             # We need to grab these before anything else to ensure they are not
213 231     231 1 1104 # changed.
214             my ($errno, $eval_error, $child_error) = (0 + $!, $@, $?);
215 231         1123  
216             my %params = (level => 0, wrapped => 0, @_);
217              
218             # If something is getting a context then the sync system needs to be
219 231 100       695 # considered loaded...
220             $INST->load unless $INST->{loaded};
221 231 50       589  
222             croak "context() called, but return value is ignored"
223             unless defined wantarray;
224 231   66     654  
225 231   66     923 my $stack = $params{stack} || $STACK;
226 231         425 my $hub = $params{hub} || (@$stack ? $stack->[-1] : $stack->top);
227 231         411 my $hid = $hub->{hid};
228             my $current = $CONTEXTS->{$hid};
229 231         761  
230 231 50       493 $_->(\%params) for @$ACQUIRE_CBS;
  0         0  
231             map $_->(\%params), @{$hub->{_context_acquire}} if $hub->{_context_acquire};
232              
233             # This is for https://github.com/Test-More/test-more/issues/16
234 231   50     723 # and https://rt.perl.org/Public/Bug/Display.html?id=127774
235 231   33     1046 my $phase = ${^GLOBAL_PHASE} || 'NA';
236             my $end_phase = $ENDING || $phase eq 'END' || $phase eq 'DESTRUCT';
237 231         421  
238 231 50       1675 my $level = 1 + $params{level};
239 231 50 33     692 my ($pkg, $file, $line, $sub) = $end_phase ? caller(0) : caller($level);
240 0 0       0 unless ($pkg || $end_phase) {
241 0   0     0 confess "Could not find context at depth $level" unless $params{fudge};
242             ($pkg, $file, $line, $sub) = caller(--$level) while ($level >= 0 && !$pkg);
243             }
244 231         408  
245 231   100     3827 my $depth = $level;
      66        
      100        
246 231         422 $depth++ while DO_DEPTH_CHECK && !$end_phase && (!$current || $depth <= $current->{_depth} + $params{wrapped}) && caller($depth + 1);
247 231   66     1027 $depth -= $params{wrapped};
248             my $depth_ok = !DO_DEPTH_CHECK || $end_phase || !$current || $current->{_depth} < $depth;
249 231 50 66     778  
      33        
250 0   0     0 if ($current && $params{on_release} && $depth_ok) {
251 0         0 $current->{_on_release} ||= [];
  0         0  
252             push @{$current->{_on_release}} => $params{on_release};
253             }
254              
255 231 100 50     2522 # I know this is ugly....
      66        
256             ($!, $@, $?) = ($errno, $eval_error, $child_error) and return bless(
257             {
258             %$current,
259             _is_canon => undef,
260             errno => $errno,
261             eval_error => $eval_error,
262             child_error => $child_error,
263             _is_spawn => [$pkg, $file, $line, $sub],
264             },
265             'Test2::API::Context'
266             ) if $current && $depth_ok;
267              
268 92 50       256 # Handle error condition of bad level
269 0 0       0 if ($current) {
  0         0  
270             unless (${$current->{_aborted}}) {
271 0 0       0 _canon_error($current, [$pkg, $file, $line, $sub, $depth])
272             unless $current->{_is_canon};
273 0 0       0  
274             _depth_error($current, [$pkg, $file, $line, $sub, $depth])
275             unless $depth_ok;
276             }
277 0 0       0  
278             $current->release if $current->{_is_canon};
279 0         0  
280             delete $CONTEXTS->{$hid};
281             }
282              
283             # Directly bless the object here, calling new is a noticeable performance
284 92         664 # hit with how often this needs to be called.
285             my $trace = bless(
286             {
287             frame => [$pkg, $file, $line, $sub],
288             pid => $$,
289             tid => get_tid(),
290             },
291             'Test2::Util::Trace'
292             );
293              
294             # Directly bless the object here, calling new is a noticeable performance
295 92         181 # hit with how often this needs to be called.
296             my $aborted = 0;
297             $current = bless(
298             {
299             _aborted => \$aborted,
300             stack => $stack,
301             hub => $hub,
302             trace => $trace,
303             _is_canon => 1,
304             _depth => $depth,
305             errno => $errno,
306             eval_error => $eval_error,
307 92 50       785 child_error => $child_error,
308             $params{on_release} ? (_on_release => [$params{on_release}]) : (),
309             },
310             'Test2::API::Context'
311             );
312 92         235  
313 92         428 $CONTEXTS->{$hid} = $current;
314             weaken($CONTEXTS->{$hid});
315 92         226  
316 92 50       267 $_->($current) for @$INIT_CBS;
  0         0  
317             map $_->($current), @{$hub->{_context_init}} if $hub->{_context_init};
318 92 50       192  
319             $params{on_init}->($current) if $params{on_init};
320 92         336  
321             ($!, $@, $?) = ($errno, $eval_error, $child_error);
322 92         433  
323             return $current;
324             }
325              
326 0     0   0 sub _depth_error {
327             _existing_error(@_, <<" EOT");
328             context() was called to retrieve an existing context, however the existing
329             context was created in a stack frame at the same, or deeper level. This usually
330             means that a tool failed to release the context when it was finished.
331             EOT
332             }
333              
334 0     0   0 sub _canon_error {
335             _existing_error(@_, <<" EOT");
336             context() was called to retrieve an existing context, however the existing
337             context has an invalid internal state (!_canon_count). This should not normally
338             happen unless something is mucking about with internals...
339             EOT
340             }
341              
342 0     0   0 sub _existing_error {
343 0         0 my ($ctx, $details, $msg) = @_;
344             my ($pkg, $file, $line, $sub, $depth) = @$details;
345 0         0  
346 0         0 my $oldframe = $ctx->{trace}->frame;
347             my $olddepth = $ctx->{_depth};
348 0         0  
349             my $mess = longmess();
350 0         0  
351             warn <<" EOT";
352             $msg
353             Old context details:
354             File: $oldframe->[1]
355             Line: $oldframe->[2]
356             Tool: $oldframe->[3]
357             Depth: $olddepth
358              
359             New context details:
360             File: $file
361             Line: $line
362             Tool: $sub
363             Depth: $depth
364              
365             Trace: $mess
366              
367             Removing the old context and creating a new one...
368             EOT
369             }
370              
371 71     71 1 249 sub release($;$) {
372 71         255 $_[0]->release;
373             return $_[1];
374             }
375              
376 0     0 1 0 sub intercept(&) {
377             my $code = shift;
378 0         0  
379             my $ctx = context();
380 0         0  
381 0 0       0 my $ipc;
382 0         0 if (my $global_ipc = test2_ipc()) {
383 0         0 my $driver = blessed($global_ipc);
384             $ipc = $driver->new;
385             }
386 0         0  
387             my $hub = Test2::Hub::Interceptor->new(
388             ipc => $ipc,
389             no_ending => 1,
390             );
391 0         0  
392 0     0   0 my @events;
  0         0  
393             $hub->listen(sub { push @events => $_[1] });
394 0         0  
395 0         0 $ctx->stack->top; # Make sure there is a top hub before we begin.
396             $ctx->stack->push($hub);
397 0         0  
398             my ($ok, $err) = (1, undef);
399             T2_SUBTEST_WRAPPER: {
400 0         0 # Do not use 'try' cause it localizes __DIE__
  0         0  
  0         0  
  0         0  
401 0         0 $ok = eval { $code->(hub => $hub, context => $ctx->snapshot); 1 };
402             $err = $@;
403              
404 0 0 0     0 # They might have done 'BEGIN { skip_all => "whatever" }'
      0        
      0        
405 0         0 if (!$ok && $err =~ m/Label not found for "last T2_SUBTEST_WRAPPER"/ || (blessed($err) && $err->isa('Test2::Hub::Interceptor::Terminator'))) {
406 0         0 $ok = 1;
407             $err = undef;
408             }
409             }
410 0         0  
411 0         0 $hub->cull;
412             $ctx->stack->pop($hub);
413 0         0  
414 0         0 my $trace = $ctx->trace;
415             $ctx->release;
416 0 0       0  
417             die $err unless $ok;
418 0 0 0     0  
      0        
419             $hub->finalize($trace, 1)
420             if $ok
421             && !$hub->no_ending
422             && !$hub->ended;
423 0         0  
424             return \@events;
425             }
426              
427 0     0 1 0 sub run_subtest {
428             my ($name, $code, $params, @args) = @_;
429 0 0       0  
430 0         0 $params = {buffered => $params} unless ref $params;
431 0         0 my $buffered = delete $params->{buffered};
432             my $inherit_trace = delete $params->{inherit_trace};
433 0         0  
434             my $ctx = context();
435 0 0       0  
436             $ctx->note($name) unless $buffered;
437 0         0  
438             my $parent = $ctx->hub;
439 0   0     0  
440 0         0 my $stack = $ctx->stack || $STACK;
441             my $hub = $stack->new_hub(
442             class => 'Test2::Hub::Subtest',
443             %$params,
444             );
445 0         0  
446 0 0       0 my @events;
447 0     0   0 $hub->set_nested( $parent->isa('Test2::Hub::Subtest') ? $parent->nested + 1 : 1 );
  0         0  
448             $hub->listen(sub { push @events => $_[1] });
449 0 0       0  
    0          
450 0 0       0 if ($buffered) {
451 0 0       0 if (my $format = $hub->format) {
452 0 0       0 my $hide = $format->can('hide_buffered') ? $format->hide_buffered : 1;
453             $hub->format(undef) if $hide;
454             }
455             }
456             elsif (! $parent->format) {
457             # If our parent has no format that means we're in a buffered subtest
458             # and now we're trying to run a streaming subtest. There's really no
459             # way for that to work, so we need to force the use of a buffered
460             # subtest here as
461 0         0 # well. https://github.com/Test-More/test-more/issues/721
462             $buffered = 1;
463             }
464 0 0       0  
465 0         0 if ($inherit_trace) {
466             my $orig = $code;
467 0     0   0 $code = sub {
468             my $st_ctx = Test2::API::Context->new(
469             trace => $ctx->trace,
470             hub => $hub,
471 0         0 );
472 0         0 $st_ctx->do_in_context($orig, @args);
473             };
474             }
475 0         0  
476             my ($ok, $err, $finished);
477             T2_SUBTEST_WRAPPER: {
478 0         0 # Do not use 'try' cause it localizes __DIE__
  0         0  
  0         0  
  0         0  
479 0         0 $ok = eval { $code->(@args); 1 };
480             $err = $@;
481              
482 0 0 0     0 # They might have done 'BEGIN { skip_all => "whatever" }'
      0        
      0        
483 0         0 if (!$ok && $err =~ m/Label not found for "last T2_SUBTEST_WRAPPER"/ || (blessed($err) && blessed($err) eq 'Test::Builder::Exception')) {
484 0         0 $ok = undef;
485             $err = undef;
486             }
487 0         0 else {
488             $finished = 1;
489             }
490 0         0 }
491             $stack->pop($hub);
492 0         0  
493             my $trace = $ctx->trace;
494 0 0       0  
495 0 0       0 if (!$finished) {
496 0         0 if(my $bailed = $hub->bailed_out) {
497             $ctx->bail($bailed->reason);
498 0         0 }
499 0         0 my $code = $hub->exit_code;
500 0 0       0 $ok = !$code;
501             $err = "Subtest ended with exit code $code" if $code;
502             }
503 0 0 0     0  
      0        
504             $hub->finalize($trace, 1)
505             if $ok
506             && !$hub->no_ending
507             && !$hub->ended;
508 0   0     0  
509 0         0 my $pass = $ok && $hub->is_passing;
510             my $e = $ctx->build_event(
511             'Subtest',
512             pass => $pass,
513             name => $name,
514             subtest_id => $hub->id,
515             buffered => $buffered,
516             subevents => \@events,
517             );
518 0         0  
519             my $plan_ok = $hub->check_plan;
520 0         0  
521             $ctx->hub->send($e);
522 0 0       0  
523             $ctx->failure_diag($e) unless $e->pass;
524 0 0       0  
525             $ctx->diag("Caught exception in subtest: $err") unless $ok;
526 0 0 0     0  
527             $ctx->diag("Bad subtest plan, expected " . $hub->plan . " but ran " . $hub->count)
528             if defined($plan_ok) && !$plan_ok;
529 0         0  
530 0         0 $ctx->release;
531             return $pass;
532             }
533              
534             # There is a use-cycle between API and API/Context. Context needs to use some
535             # API functions as the package is compiling. Test2::API::context() needs
536             # Test2::API::Context to be loaded, but we cannot 'require' the module there as
537             # it causes a very noticeable performance impact with how often context() is
538             # called.
539             require Test2::API::Context;
540              
541             1;
542              
543             __END__