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