File Coverage

blib/lib/Test2/API.pm
Criterion Covered Total %
statement 255 276 92.3
branch 74 94 78.7
condition 52 81 64.2
subroutine 67 76 88.1
pod 35 39 89.7
total 483 566 85.3


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