1*5486feefSafresh1package Test2::AsyncSubtest;
2*5486feefSafresh1use strict;
3*5486feefSafresh1use warnings;
4*5486feefSafresh1
5*5486feefSafresh1use Test2::IPC;
6*5486feefSafresh1
7*5486feefSafresh1our $VERSION = '0.000162';
8*5486feefSafresh1
9*5486feefSafresh1our @CARP_NOT = qw/Test2::Util::HashBase/;
10*5486feefSafresh1
11*5486feefSafresh1use Carp qw/croak cluck confess/;
12*5486feefSafresh1use Test2::Util qw/get_tid CAN_THREAD CAN_FORK/;
13*5486feefSafresh1use Scalar::Util qw/blessed weaken/;
14*5486feefSafresh1use List::Util qw/first/;
15*5486feefSafresh1
16*5486feefSafresh1use Test2::API();
17*5486feefSafresh1use Test2::API::Context();
18*5486feefSafresh1use Test2::Util::Trace();
19*5486feefSafresh1use Test2::Util::Guard();
20*5486feefSafresh1use Time::HiRes();
21*5486feefSafresh1
22*5486feefSafresh1use Test2::AsyncSubtest::Hub();
23*5486feefSafresh1use Test2::AsyncSubtest::Event::Attach();
24*5486feefSafresh1use Test2::AsyncSubtest::Event::Detach();
25*5486feefSafresh1
26*5486feefSafresh1use Test2::Util::HashBase qw{
27*5486feefSafresh1    name hub
28*5486feefSafresh1    trace frame send_to
29*5486feefSafresh1    events
30*5486feefSafresh1    finished
31*5486feefSafresh1    active
32*5486feefSafresh1    stack
33*5486feefSafresh1    id cid uuid
34*5486feefSafresh1    children
35*5486feefSafresh1    _in_use
36*5486feefSafresh1    _attached pid tid
37*5486feefSafresh1    start_stamp stop_stamp
38*5486feefSafresh1};
39*5486feefSafresh1
40*5486feefSafresh1sub CAN_REALLY_THREAD {
41*5486feefSafresh1    return 0 unless CAN_THREAD;
42*5486feefSafresh1    return 0 unless eval { require threads; threads->VERSION('1.34'); 1 };
43*5486feefSafresh1    return 1;
44*5486feefSafresh1}
45*5486feefSafresh1
46*5486feefSafresh1
47*5486feefSafresh1my $UUID_VIA = Test2::API::_add_uuid_via_ref();
48*5486feefSafresh1my $CID = 1;
49*5486feefSafresh1my @STACK;
50*5486feefSafresh1
51*5486feefSafresh1sub TOP { @STACK ? $STACK[-1] : undef }
52*5486feefSafresh1
53*5486feefSafresh1sub init {
54*5486feefSafresh1    my $self = shift;
55*5486feefSafresh1
56*5486feefSafresh1    croak "'name' is a required attribute"
57*5486feefSafresh1        unless $self->{+NAME};
58*5486feefSafresh1
59*5486feefSafresh1    my $to = $self->{+SEND_TO} ||= Test2::API::test2_stack()->top;
60*5486feefSafresh1
61*5486feefSafresh1    $self->{+STACK} = [@STACK];
62*5486feefSafresh1    $_->{+_IN_USE}++ for reverse @STACK;
63*5486feefSafresh1
64*5486feefSafresh1    $self->{+TID}       = get_tid;
65*5486feefSafresh1    $self->{+PID}       = $$;
66*5486feefSafresh1    $self->{+CID}       = 'AsyncSubtest-' . $CID++;
67*5486feefSafresh1    $self->{+ID}        = 1;
68*5486feefSafresh1    $self->{+FINISHED}  = 0;
69*5486feefSafresh1    $self->{+ACTIVE}    = 0;
70*5486feefSafresh1    $self->{+_IN_USE}   = 0;
71*5486feefSafresh1    $self->{+CHILDREN}  = [];
72*5486feefSafresh1    $self->{+UUID} = ${$UUID_VIA}->() if defined $$UUID_VIA;
73*5486feefSafresh1
74*5486feefSafresh1    unless($self->{+HUB}) {
75*5486feefSafresh1        my $ipc = Test2::API::test2_ipc();
76*5486feefSafresh1        my $formatter = Test2::API::test2_stack->top->format;
77*5486feefSafresh1        my $args = delete $self->{hub_init_args} || {};
78*5486feefSafresh1        my $hub = Test2::AsyncSubtest::Hub->new(
79*5486feefSafresh1            %$args,
80*5486feefSafresh1            ipc       => $ipc,
81*5486feefSafresh1            nested    => $to->nested + 1,
82*5486feefSafresh1            buffered  => 1,
83*5486feefSafresh1            formatter => $formatter,
84*5486feefSafresh1        );
85*5486feefSafresh1        weaken($hub->{ast} = $self);
86*5486feefSafresh1        $self->{+HUB} = $hub;
87*5486feefSafresh1    }
88*5486feefSafresh1
89*5486feefSafresh1    $self->{+TRACE} ||= Test2::Util::Trace->new(
90*5486feefSafresh1        frame    => $self->{+FRAME} || [caller(1)],
91*5486feefSafresh1        buffered => $to->buffered,
92*5486feefSafresh1        nested   => $to->nested,
93*5486feefSafresh1        cid      => $self->{+CID},
94*5486feefSafresh1        uuid     => $self->{+UUID},
95*5486feefSafresh1        hid      => $to->hid,
96*5486feefSafresh1        huuid    => $to->uuid,
97*5486feefSafresh1    );
98*5486feefSafresh1
99*5486feefSafresh1    my $hub = $self->{+HUB};
100*5486feefSafresh1    $hub->set_ast_ids({}) unless $hub->ast_ids;
101*5486feefSafresh1    $hub->listen($self->_listener);
102*5486feefSafresh1}
103*5486feefSafresh1
104*5486feefSafresh1sub _listener {
105*5486feefSafresh1    my $self = shift;
106*5486feefSafresh1
107*5486feefSafresh1    my $events = $self->{+EVENTS} ||= [];
108*5486feefSafresh1
109*5486feefSafresh1    sub { push @$events => $_[1] };
110*5486feefSafresh1}
111*5486feefSafresh1
112*5486feefSafresh1sub context {
113*5486feefSafresh1    my $self = shift;
114*5486feefSafresh1
115*5486feefSafresh1    my $send_to = $self->{+SEND_TO};
116*5486feefSafresh1
117*5486feefSafresh1    confess "Attempt to close AsyncSubtest when original parent hub (a non async-subtest?) has ended"
118*5486feefSafresh1        if $send_to->ended;
119*5486feefSafresh1
120*5486feefSafresh1    return Test2::API::Context->new(
121*5486feefSafresh1        trace => $self->{+TRACE},
122*5486feefSafresh1        hub   => $send_to,
123*5486feefSafresh1    );
124*5486feefSafresh1}
125*5486feefSafresh1
126*5486feefSafresh1sub _gen_event {
127*5486feefSafresh1    my $self = shift;
128*5486feefSafresh1    my ($type, $id, $hub) = @_;
129*5486feefSafresh1
130*5486feefSafresh1    my $class = "Test2::AsyncSubtest::Event::$type";
131*5486feefSafresh1
132*5486feefSafresh1    return $class->new(
133*5486feefSafresh1        id    => $id,
134*5486feefSafresh1        trace => Test2::Util::Trace->new(
135*5486feefSafresh1            frame    => [caller(1)],
136*5486feefSafresh1            buffered => $hub->buffered,
137*5486feefSafresh1            nested   => $hub->nested,
138*5486feefSafresh1            cid      => $self->{+CID},
139*5486feefSafresh1            uuid     => $self->{+UUID},
140*5486feefSafresh1            hid      => $hub->hid,
141*5486feefSafresh1            huuid    => $hub->uuid,
142*5486feefSafresh1        ),
143*5486feefSafresh1    );
144*5486feefSafresh1}
145*5486feefSafresh1
146*5486feefSafresh1sub cleave {
147*5486feefSafresh1    my $self = shift;
148*5486feefSafresh1    my $id = $self->{+ID}++;
149*5486feefSafresh1    $self->{+HUB}->ast_ids->{$id} = 0;
150*5486feefSafresh1    return $id;
151*5486feefSafresh1}
152*5486feefSafresh1
153*5486feefSafresh1sub attach {
154*5486feefSafresh1    my $self = shift;
155*5486feefSafresh1    my ($id) = @_;
156*5486feefSafresh1
157*5486feefSafresh1    croak "An ID is required" unless $id;
158*5486feefSafresh1
159*5486feefSafresh1    croak "ID $id is not valid"
160*5486feefSafresh1        unless defined $self->{+HUB}->ast_ids->{$id};
161*5486feefSafresh1
162*5486feefSafresh1    croak "ID $id is already attached"
163*5486feefSafresh1        if $self->{+HUB}->ast_ids->{$id};
164*5486feefSafresh1
165*5486feefSafresh1    croak "You must attach INSIDE the child process/thread"
166*5486feefSafresh1        if $self->{+HUB}->is_local;
167*5486feefSafresh1
168*5486feefSafresh1    $self->{+_ATTACHED} = [ $$, get_tid, $id ];
169*5486feefSafresh1    $self->{+HUB}->send($self->_gen_event('Attach', $id, $self->{+HUB}));
170*5486feefSafresh1}
171*5486feefSafresh1
172*5486feefSafresh1sub detach {
173*5486feefSafresh1    my $self = shift;
174*5486feefSafresh1
175*5486feefSafresh1    if ($self->{+PID} == $$ && $self->{+TID} == get_tid) {
176*5486feefSafresh1        cluck "You must detach INSIDE the child process/thread ($$, " . get_tid . " instead of $self->{+PID}, $self->{+TID})";
177*5486feefSafresh1        return;
178*5486feefSafresh1    }
179*5486feefSafresh1
180*5486feefSafresh1    my $att = $self->{+_ATTACHED}
181*5486feefSafresh1        or croak "Not attached";
182*5486feefSafresh1
183*5486feefSafresh1    croak "Attempt to detach from wrong child"
184*5486feefSafresh1        unless $att->[0] == $$ && $att->[1] == get_tid;
185*5486feefSafresh1
186*5486feefSafresh1    my $id = $att->[2];
187*5486feefSafresh1
188*5486feefSafresh1    $self->{+HUB}->send($self->_gen_event('Detach', $id, $self->{+HUB}));
189*5486feefSafresh1
190*5486feefSafresh1    delete $self->{+_ATTACHED};
191*5486feefSafresh1}
192*5486feefSafresh1
193*5486feefSafresh1sub ready { return !shift->pending }
194*5486feefSafresh1sub pending {
195*5486feefSafresh1    my $self = shift;
196*5486feefSafresh1    my $hub = $self->{+HUB};
197*5486feefSafresh1    return -1 unless $hub->is_local;
198*5486feefSafresh1
199*5486feefSafresh1    $hub->cull;
200*5486feefSafresh1
201*5486feefSafresh1    return $self->{+_IN_USE} + keys %{$self->{+HUB}->ast_ids};
202*5486feefSafresh1}
203*5486feefSafresh1
204*5486feefSafresh1sub run {
205*5486feefSafresh1    my $self = shift;
206*5486feefSafresh1    my ($code, @args) = @_;
207*5486feefSafresh1
208*5486feefSafresh1    croak "AsyncSubtest->run() takes a codeblock as the first argument"
209*5486feefSafresh1        unless $code && ref($code) eq 'CODE';
210*5486feefSafresh1
211*5486feefSafresh1    $self->start;
212*5486feefSafresh1
213*5486feefSafresh1    my ($ok, $err, $finished);
214*5486feefSafresh1    T2_SUBTEST_WRAPPER: {
215*5486feefSafresh1        $ok = eval { $code->(@args); 1 };
216*5486feefSafresh1        $err = $@;
217*5486feefSafresh1
218*5486feefSafresh1        # They might have done 'BEGIN { skip_all => "whatever" }'
219*5486feefSafresh1        if (!$ok && $err =~ m/Label not found for "last T2_SUBTEST_WRAPPER"/) {
220*5486feefSafresh1            $ok  = undef;
221*5486feefSafresh1            $err = undef;
222*5486feefSafresh1        }
223*5486feefSafresh1        else {
224*5486feefSafresh1            $finished = 1;
225*5486feefSafresh1        }
226*5486feefSafresh1    }
227*5486feefSafresh1
228*5486feefSafresh1    $self->stop;
229*5486feefSafresh1
230*5486feefSafresh1    my $hub = $self->{+HUB};
231*5486feefSafresh1
232*5486feefSafresh1    if (!$finished) {
233*5486feefSafresh1        if(my $bailed = $hub->bailed_out) {
234*5486feefSafresh1            my $ctx = $self->context;
235*5486feefSafresh1            $ctx->bail($bailed->reason);
236*5486feefSafresh1            return;
237*5486feefSafresh1        }
238*5486feefSafresh1        my $code = $hub->exit_code;
239*5486feefSafresh1        $ok = !$code;
240*5486feefSafresh1        $err = "Subtest ended with exit code $code" if $code;
241*5486feefSafresh1    }
242*5486feefSafresh1
243*5486feefSafresh1    unless ($ok) {
244*5486feefSafresh1        my $e = Test2::Event::Exception->new(
245*5486feefSafresh1            error => $err,
246*5486feefSafresh1            trace => Test2::Util::Trace->new(
247*5486feefSafresh1                frame    => [caller(0)],
248*5486feefSafresh1                buffered => $hub->buffered,
249*5486feefSafresh1                nested   => $hub->nested,
250*5486feefSafresh1                cid      => $self->{+CID},
251*5486feefSafresh1                uuid     => $self->{+UUID},
252*5486feefSafresh1                hid      => $hub->hid,
253*5486feefSafresh1                huuid    => $hub->uuid,
254*5486feefSafresh1            ),
255*5486feefSafresh1        );
256*5486feefSafresh1        $hub->send($e);
257*5486feefSafresh1    }
258*5486feefSafresh1
259*5486feefSafresh1    return $hub->is_passing;
260*5486feefSafresh1}
261*5486feefSafresh1
262*5486feefSafresh1sub start {
263*5486feefSafresh1    my $self = shift;
264*5486feefSafresh1
265*5486feefSafresh1    croak "Subtest is already complete"
266*5486feefSafresh1        if $self->{+FINISHED};
267*5486feefSafresh1
268*5486feefSafresh1    $self->{+START_STAMP} = Time::HiRes::time() unless defined $self->{+START_STAMP};
269*5486feefSafresh1
270*5486feefSafresh1    $self->{+ACTIVE}++;
271*5486feefSafresh1
272*5486feefSafresh1    push @STACK => $self;
273*5486feefSafresh1    my $hub = $self->{+HUB};
274*5486feefSafresh1    my $stack = Test2::API::test2_stack();
275*5486feefSafresh1    $stack->push($hub);
276*5486feefSafresh1
277*5486feefSafresh1    return $hub->is_passing;
278*5486feefSafresh1}
279*5486feefSafresh1
280*5486feefSafresh1sub stop {
281*5486feefSafresh1    my $self = shift;
282*5486feefSafresh1
283*5486feefSafresh1    croak "Subtest is not active"
284*5486feefSafresh1        unless $self->{+ACTIVE}--;
285*5486feefSafresh1
286*5486feefSafresh1    croak "AsyncSubtest stack mismatch"
287*5486feefSafresh1        unless @STACK && $self == $STACK[-1];
288*5486feefSafresh1
289*5486feefSafresh1    $self->{+STOP_STAMP} = Time::HiRes::time();
290*5486feefSafresh1
291*5486feefSafresh1    pop @STACK;
292*5486feefSafresh1
293*5486feefSafresh1    my $hub = $self->{+HUB};
294*5486feefSafresh1    my $stack = Test2::API::test2_stack();
295*5486feefSafresh1    $stack->pop($hub);
296*5486feefSafresh1    return $hub->is_passing;
297*5486feefSafresh1}
298*5486feefSafresh1
299*5486feefSafresh1sub finish {
300*5486feefSafresh1    my $self = shift;
301*5486feefSafresh1    my %params = @_;
302*5486feefSafresh1
303*5486feefSafresh1    my $hub = $self->hub;
304*5486feefSafresh1
305*5486feefSafresh1    croak "Subtest is already finished"
306*5486feefSafresh1        if $self->{+FINISHED}++;
307*5486feefSafresh1
308*5486feefSafresh1    croak "Subtest can only be finished in the process/thread that created it"
309*5486feefSafresh1        unless $hub->is_local;
310*5486feefSafresh1
311*5486feefSafresh1    croak "Subtest is still active"
312*5486feefSafresh1        if $self->{+ACTIVE};
313*5486feefSafresh1
314*5486feefSafresh1    $self->wait;
315*5486feefSafresh1    $self->{+STOP_STAMP} = Time::HiRes::time() unless defined $self->{+STOP_STAMP};
316*5486feefSafresh1    my $stop_stamp = $self->{+STOP_STAMP};
317*5486feefSafresh1
318*5486feefSafresh1    my $todo       = $params{todo};
319*5486feefSafresh1    my $skip       = $params{skip};
320*5486feefSafresh1    my $empty      = !@{$self->{+EVENTS}};
321*5486feefSafresh1    my $no_asserts = !$hub->count;
322*5486feefSafresh1    my $collapse   = $params{collapse};
323*5486feefSafresh1    my $no_plan    = $params{no_plan} || ($collapse && $no_asserts) || $skip;
324*5486feefSafresh1
325*5486feefSafresh1    my $trace = Test2::Util::Trace->new(
326*5486feefSafresh1        frame    => $self->{+TRACE}->{frame},
327*5486feefSafresh1        buffered => $hub->buffered,
328*5486feefSafresh1        nested   => $hub->nested,
329*5486feefSafresh1        cid      => $self->{+CID},
330*5486feefSafresh1        uuid     => $self->{+UUID},
331*5486feefSafresh1        hid      => $hub->hid,
332*5486feefSafresh1        huuid    => $hub->uuid,
333*5486feefSafresh1    );
334*5486feefSafresh1
335*5486feefSafresh1    $hub->finalize($trace, !$no_plan)
336*5486feefSafresh1        unless $hub->no_ending || $hub->ended;
337*5486feefSafresh1
338*5486feefSafresh1    if ($hub->ipc) {
339*5486feefSafresh1        $hub->ipc->drop_hub($hub->hid);
340*5486feefSafresh1        $hub->set_ipc(undef);
341*5486feefSafresh1    }
342*5486feefSafresh1
343*5486feefSafresh1    return $hub->is_passing if $params{silent};
344*5486feefSafresh1
345*5486feefSafresh1    my $ctx = $self->context;
346*5486feefSafresh1
347*5486feefSafresh1    my $pass = 1;
348*5486feefSafresh1    if ($skip) {
349*5486feefSafresh1        $ctx->skip($self->{+NAME}, $skip);
350*5486feefSafresh1    }
351*5486feefSafresh1    else {
352*5486feefSafresh1        if ($collapse && $empty) {
353*5486feefSafresh1            $ctx->ok($hub->is_passing, $self->{+NAME});
354*5486feefSafresh1            return $hub->is_passing;
355*5486feefSafresh1        }
356*5486feefSafresh1
357*5486feefSafresh1        if ($collapse && $no_asserts) {
358*5486feefSafresh1            push @{$self->{+EVENTS}} => Test2::Event::Plan->new(trace => $trace, max => 0, directive => 'SKIP', reason => "No assertions");
359*5486feefSafresh1        }
360*5486feefSafresh1
361*5486feefSafresh1        my $e = $ctx->build_event(
362*5486feefSafresh1            'Subtest',
363*5486feefSafresh1            pass         => $hub->is_passing,
364*5486feefSafresh1            subtest_id   => $hub->id,
365*5486feefSafresh1            subtest_uuid => $hub->uuid,
366*5486feefSafresh1            name         => $self->{+NAME},
367*5486feefSafresh1            buffered     => 1,
368*5486feefSafresh1            subevents    => $self->{+EVENTS},
369*5486feefSafresh1            start_stamp  => $self->{+START_STAMP},
370*5486feefSafresh1            stop_stamp   => $self->{+STOP_STAMP},
371*5486feefSafresh1            $todo ? (
372*5486feefSafresh1                todo => $todo,
373*5486feefSafresh1                effective_pass => 1,
374*5486feefSafresh1            ) : (),
375*5486feefSafresh1        );
376*5486feefSafresh1
377*5486feefSafresh1        $ctx->hub->send($e);
378*5486feefSafresh1
379*5486feefSafresh1        unless ($e->effective_pass) {
380*5486feefSafresh1            $ctx->failure_diag($e);
381*5486feefSafresh1
382*5486feefSafresh1            $ctx->diag("Bad subtest plan, expected " . $hub->plan . " but ran " . $hub->count)
383*5486feefSafresh1                if $hub->plan && !$hub->check_plan && !grep {$_->causes_fail} @{$self->{+EVENTS}};
384*5486feefSafresh1        }
385*5486feefSafresh1
386*5486feefSafresh1        $pass = $e->pass;
387*5486feefSafresh1    }
388*5486feefSafresh1
389*5486feefSafresh1    $_->{+_IN_USE}-- for reverse @{$self->{+STACK}};
390*5486feefSafresh1
391*5486feefSafresh1    return $pass;
392*5486feefSafresh1}
393*5486feefSafresh1
394*5486feefSafresh1sub wait {
395*5486feefSafresh1    my $self = shift;
396*5486feefSafresh1
397*5486feefSafresh1    my $hub = $self->{+HUB};
398*5486feefSafresh1    my $children = $self->{+CHILDREN};
399*5486feefSafresh1
400*5486feefSafresh1    while (@$children) {
401*5486feefSafresh1        $hub->cull;
402*5486feefSafresh1        if (my $child = pop @$children) {
403*5486feefSafresh1            if (blessed($child)) {
404*5486feefSafresh1                $child->join;
405*5486feefSafresh1            }
406*5486feefSafresh1            else {
407*5486feefSafresh1                waitpid($child, 0);
408*5486feefSafresh1            }
409*5486feefSafresh1        }
410*5486feefSafresh1        else {
411*5486feefSafresh1            Time::HiRes::sleep('0.01');
412*5486feefSafresh1        }
413*5486feefSafresh1    }
414*5486feefSafresh1
415*5486feefSafresh1    $hub->cull;
416*5486feefSafresh1
417*5486feefSafresh1    cluck "Subtest '$self->{+NAME}': All children have completed, but we still appear to be pending"
418*5486feefSafresh1        if $hub->is_local && keys %{$self->{+HUB}->ast_ids};
419*5486feefSafresh1}
420*5486feefSafresh1
421*5486feefSafresh1sub fork {
422*5486feefSafresh1    croak "Forking is not supported" unless CAN_FORK;
423*5486feefSafresh1    my $self = shift;
424*5486feefSafresh1    my $id = $self->cleave;
425*5486feefSafresh1    my $pid = CORE::fork();
426*5486feefSafresh1
427*5486feefSafresh1    unless (defined $pid) {
428*5486feefSafresh1        delete $self->{+HUB}->ast_ids->{$id};
429*5486feefSafresh1        croak "Failed to fork";
430*5486feefSafresh1    }
431*5486feefSafresh1
432*5486feefSafresh1    if($pid) {
433*5486feefSafresh1        push @{$self->{+CHILDREN}} => $pid;
434*5486feefSafresh1        return $pid;
435*5486feefSafresh1    }
436*5486feefSafresh1
437*5486feefSafresh1    $self->attach($id);
438*5486feefSafresh1
439*5486feefSafresh1    return $self->_guard;
440*5486feefSafresh1}
441*5486feefSafresh1
442*5486feefSafresh1sub run_fork {
443*5486feefSafresh1    my $self = shift;
444*5486feefSafresh1    my ($code, @args) = @_;
445*5486feefSafresh1
446*5486feefSafresh1    my $f = $self->fork;
447*5486feefSafresh1    return $f unless blessed($f);
448*5486feefSafresh1
449*5486feefSafresh1    $self->run($code, @args);
450*5486feefSafresh1
451*5486feefSafresh1    $self->detach();
452*5486feefSafresh1    $f->dismiss();
453*5486feefSafresh1    exit 0;
454*5486feefSafresh1}
455*5486feefSafresh1
456*5486feefSafresh1sub run_thread {
457*5486feefSafresh1    croak "Threading is not supported"
458*5486feefSafresh1        unless CAN_REALLY_THREAD;
459*5486feefSafresh1
460*5486feefSafresh1    my $self = shift;
461*5486feefSafresh1    my ($code, @args) = @_;
462*5486feefSafresh1
463*5486feefSafresh1    my $id = $self->cleave;
464*5486feefSafresh1    my $thr =  threads->create(sub {
465*5486feefSafresh1        $self->attach($id);
466*5486feefSafresh1
467*5486feefSafresh1        $self->run($code, @args);
468*5486feefSafresh1
469*5486feefSafresh1        $self->detach(get_tid);
470*5486feefSafresh1        return 0;
471*5486feefSafresh1    });
472*5486feefSafresh1
473*5486feefSafresh1    push @{$self->{+CHILDREN}} => $thr;
474*5486feefSafresh1
475*5486feefSafresh1    return $thr;
476*5486feefSafresh1}
477*5486feefSafresh1
478*5486feefSafresh1sub _guard {
479*5486feefSafresh1    my $self = shift;
480*5486feefSafresh1
481*5486feefSafresh1    my ($pid, $tid) = ($$, get_tid);
482*5486feefSafresh1
483*5486feefSafresh1    return Test2::Util::Guard->new(sub {
484*5486feefSafresh1        return unless $$ == $pid && get_tid == $tid;
485*5486feefSafresh1
486*5486feefSafresh1        my $error = "Scope Leak";
487*5486feefSafresh1        if (my $ex = $@) {
488*5486feefSafresh1            chomp($ex);
489*5486feefSafresh1            $error .= " ($ex)";
490*5486feefSafresh1        }
491*5486feefSafresh1
492*5486feefSafresh1        cluck $error;
493*5486feefSafresh1
494*5486feefSafresh1        my $e = $self->context->build_event(
495*5486feefSafresh1            'Exception',
496*5486feefSafresh1            error => "$error\n",
497*5486feefSafresh1        );
498*5486feefSafresh1        $self->{+HUB}->send($e);
499*5486feefSafresh1        $self->detach();
500*5486feefSafresh1        exit 255;
501*5486feefSafresh1    });
502*5486feefSafresh1}
503*5486feefSafresh1
504*5486feefSafresh1sub DESTROY {
505*5486feefSafresh1    my $self = shift;
506*5486feefSafresh1    return unless $self->{+NAME};
507*5486feefSafresh1
508*5486feefSafresh1    if (my $att = $self->{+_ATTACHED}) {
509*5486feefSafresh1        return unless $self->{+HUB};
510*5486feefSafresh1        eval { $self->detach() };
511*5486feefSafresh1    }
512*5486feefSafresh1
513*5486feefSafresh1    return if $self->{+FINISHED};
514*5486feefSafresh1    return unless $self->{+PID} == $$;
515*5486feefSafresh1    return unless $self->{+TID} == get_tid;
516*5486feefSafresh1
517*5486feefSafresh1    local $@;
518*5486feefSafresh1    eval { $_->{+_IN_USE}-- for reverse @{$self->{+STACK}} };
519*5486feefSafresh1
520*5486feefSafresh1    warn "Subtest $self->{+NAME} did not finish!";
521*5486feefSafresh1    exit 255;
522*5486feefSafresh1}
523*5486feefSafresh1
524*5486feefSafresh11;
525*5486feefSafresh1
526*5486feefSafresh1__END__
527*5486feefSafresh1
528*5486feefSafresh1=pod
529*5486feefSafresh1
530*5486feefSafresh1=encoding UTF-8
531*5486feefSafresh1
532*5486feefSafresh1=head1 NAME
533*5486feefSafresh1
534*5486feefSafresh1Test2::AsyncSubtest - Object representing an async subtest.
535*5486feefSafresh1
536*5486feefSafresh1=head1 DESCRIPTION
537*5486feefSafresh1
538*5486feefSafresh1Regular subtests have a limited scope, they start, events are generated, then
539*5486feefSafresh1they close and send an L<Test2::Event::Subtest> event. This is a problem if you
540*5486feefSafresh1want the subtest to keep receiving events while other events are also being
541*5486feefSafresh1generated. This class implements subtests that stay open until you decide to
542*5486feefSafresh1close them.
543*5486feefSafresh1
544*5486feefSafresh1This is mainly useful for tools that start a subtest in one process and then
545*5486feefSafresh1spawn children. In many cases it is nice to let the parent process continue
546*5486feefSafresh1instead of waiting on the children.
547*5486feefSafresh1
548*5486feefSafresh1=head1 SYNOPSIS
549*5486feefSafresh1
550*5486feefSafresh1    use Test2::AsyncSubtest;
551*5486feefSafresh1
552*5486feefSafresh1    my $ast = Test2::AsyncSubtest->new(name => foo);
553*5486feefSafresh1
554*5486feefSafresh1    $ast->run(sub {
555*5486feefSafresh1        ok(1, "Event in parent" );
556*5486feefSafresh1    });
557*5486feefSafresh1
558*5486feefSafresh1    ok(1, "Event outside of subtest");
559*5486feefSafresh1
560*5486feefSafresh1    $ast->run_fork(sub {
561*5486feefSafresh1        ok(1, "Event in child process");
562*5486feefSafresh1    });
563*5486feefSafresh1
564*5486feefSafresh1    ...
565*5486feefSafresh1
566*5486feefSafresh1    $ast->finish;
567*5486feefSafresh1
568*5486feefSafresh1    done_testing;
569*5486feefSafresh1
570*5486feefSafresh1=head1 CONSTRUCTION
571*5486feefSafresh1
572*5486feefSafresh1    my $ast = Test2::AsyncSubtest->new( ... );
573*5486feefSafresh1
574*5486feefSafresh1=over 4
575*5486feefSafresh1
576*5486feefSafresh1=item name => $name (required)
577*5486feefSafresh1
578*5486feefSafresh1Name of the subtest. This construction argument is required.
579*5486feefSafresh1
580*5486feefSafresh1=item send_to => $hub (optional)
581*5486feefSafresh1
582*5486feefSafresh1Hub to which the final subtest event should be sent. This must be an instance
583*5486feefSafresh1of L<Test2::Hub> or a subclass. If none is specified then the current top hub
584*5486feefSafresh1will be used.
585*5486feefSafresh1
586*5486feefSafresh1=item trace => $trace (optional)
587*5486feefSafresh1
588*5486feefSafresh1File/Line to which errors should be attributed. This must be an instance of
589*5486feefSafresh1L<Test2::Util::Trace>. If none is specified then the file/line where the
590*5486feefSafresh1constructor was called will be used.
591*5486feefSafresh1
592*5486feefSafresh1=item hub => $hub (optional)
593*5486feefSafresh1
594*5486feefSafresh1Use this to specify a hub the subtest should use. By default a new hub is
595*5486feefSafresh1generated. This must be an instance of L<Test2::AsyncSubtest::Hub>.
596*5486feefSafresh1
597*5486feefSafresh1=back
598*5486feefSafresh1
599*5486feefSafresh1=head1 METHODS
600*5486feefSafresh1
601*5486feefSafresh1=head2 SIMPLE ACCESSORS
602*5486feefSafresh1
603*5486feefSafresh1=over 4
604*5486feefSafresh1
605*5486feefSafresh1=item $bool = $ast->active
606*5486feefSafresh1
607*5486feefSafresh1True if the subtest is active. The subtest is active if its hub appears in the
608*5486feefSafresh1global hub stack. This is true when C<< $ast->run(...) >> us running.
609*5486feefSafresh1
610*5486feefSafresh1=item $arrayref = $ast->children
611*5486feefSafresh1
612*5486feefSafresh1Get an arrayref of child processes/threads. Numerical items are PIDs, blessed
613*5486feefSafresh1items are L<threads> instances.
614*5486feefSafresh1
615*5486feefSafresh1=item $arrayref = $ast->events
616*5486feefSafresh1
617*5486feefSafresh1Get an arrayref of events that have been sent to the subtests hub.
618*5486feefSafresh1
619*5486feefSafresh1=item $bool = $ast->finished
620*5486feefSafresh1
621*5486feefSafresh1True if C<finished()> has already been called.
622*5486feefSafresh1
623*5486feefSafresh1=item $hub = $ast->hub
624*5486feefSafresh1
625*5486feefSafresh1The hub created for the subtest.
626*5486feefSafresh1
627*5486feefSafresh1=item $int = $ast->id
628*5486feefSafresh1
629*5486feefSafresh1Attach/Detach counter. Used internally, not useful to users.
630*5486feefSafresh1
631*5486feefSafresh1=item $str = $ast->name
632*5486feefSafresh1
633*5486feefSafresh1Name of the subtest.
634*5486feefSafresh1
635*5486feefSafresh1=item $pid = $ast->pid
636*5486feefSafresh1
637*5486feefSafresh1PID in which the subtest was created.
638*5486feefSafresh1
639*5486feefSafresh1=item $tid = $ast->tid
640*5486feefSafresh1
641*5486feefSafresh1Thread ID in which the subtest was created.
642*5486feefSafresh1
643*5486feefSafresh1=item $hub = $ast->send_to
644*5486feefSafresh1
645*5486feefSafresh1Hub to which the final subtest event should be sent.
646*5486feefSafresh1
647*5486feefSafresh1=item $arrayref = $ast->stack
648*5486feefSafresh1
649*5486feefSafresh1Stack of async subtests at the time this one was created. This is mainly for
650*5486feefSafresh1internal use.
651*5486feefSafresh1
652*5486feefSafresh1=item $trace = $ast->trace
653*5486feefSafresh1
654*5486feefSafresh1L<Test2::Util::Trace> instance used for error reporting.
655*5486feefSafresh1
656*5486feefSafresh1=back
657*5486feefSafresh1
658*5486feefSafresh1=head2 INTERFACE
659*5486feefSafresh1
660*5486feefSafresh1=over 4
661*5486feefSafresh1
662*5486feefSafresh1=item $ast->attach($id)
663*5486feefSafresh1
664*5486feefSafresh1Attach a subtest in a child/process to the original.
665*5486feefSafresh1
666*5486feefSafresh1B<Note:> C<< my $id = $ast->cleave >> must have been called in the parent
667*5486feefSafresh1process/thread before the child was started, the id it returns must be used in
668*5486feefSafresh1the call to C<< $ast->attach($id) >>
669*5486feefSafresh1
670*5486feefSafresh1=item $id = $ast->cleave
671*5486feefSafresh1
672*5486feefSafresh1Prepare a slot for a child process/thread to attach. This must be called BEFORE
673*5486feefSafresh1the child process or thread is started. The ID returned is used by C<attach()>.
674*5486feefSafresh1
675*5486feefSafresh1This must only be called in the original process/thread.
676*5486feefSafresh1
677*5486feefSafresh1=item $ctx = $ast->context
678*5486feefSafresh1
679*5486feefSafresh1Get an L<Test2::API::Context> instance that can be used to send events to the
680*5486feefSafresh1context in which the hub was created. This is not a canonical context, you
681*5486feefSafresh1should not call C<< $ctx->release >> on it.
682*5486feefSafresh1
683*5486feefSafresh1=item $ast->detach
684*5486feefSafresh1
685*5486feefSafresh1Detach from the parent in a child process/thread. This should be called just
686*5486feefSafresh1before the child exits.
687*5486feefSafresh1
688*5486feefSafresh1=item $ast->finish
689*5486feefSafresh1
690*5486feefSafresh1=item $ast->finish(%options)
691*5486feefSafresh1
692*5486feefSafresh1Finish the subtest, wait on children, and send the final subtest event.
693*5486feefSafresh1
694*5486feefSafresh1This must only be called in the original process/thread.
695*5486feefSafresh1
696*5486feefSafresh1B<Note:> This calls C<< $ast->wait >>.
697*5486feefSafresh1
698*5486feefSafresh1These are the options:
699*5486feefSafresh1
700*5486feefSafresh1=over 4
701*5486feefSafresh1
702*5486feefSafresh1=item collapse => 1
703*5486feefSafresh1
704*5486feefSafresh1This intelligently allows a subtest to be empty.
705*5486feefSafresh1
706*5486feefSafresh1If no events bump the test count then the subtest no final plan will be added.
707*5486feefSafresh1The subtest will not be considered a failure (normally an empty subtest is a
708*5486feefSafresh1failure).
709*5486feefSafresh1
710*5486feefSafresh1If there are no events at all the subtest will be collapsed into an
711*5486feefSafresh1L<Test2::Event::Ok> event.
712*5486feefSafresh1
713*5486feefSafresh1=item silent => 1
714*5486feefSafresh1
715*5486feefSafresh1This will prevent finish from generating a final L<Test2::Event::Subtest>
716*5486feefSafresh1event. This effectively ends the subtest without it effecting the parent
717*5486feefSafresh1subtest (or top level test).
718*5486feefSafresh1
719*5486feefSafresh1=item no_plan => 1
720*5486feefSafresh1
721*5486feefSafresh1This will prevent a final plan from being added to the subtest for you when
722*5486feefSafresh1none is directly specified.
723*5486feefSafresh1
724*5486feefSafresh1=item skip => "reason"
725*5486feefSafresh1
726*5486feefSafresh1This will issue an L<Test2::Event::Skip> instead of a subtest. This will throw
727*5486feefSafresh1an exception if any events have been seen, or if state implies events have
728*5486feefSafresh1occurred.
729*5486feefSafresh1
730*5486feefSafresh1=back
731*5486feefSafresh1
732*5486feefSafresh1=item $out = $ast->fork
733*5486feefSafresh1
734*5486feefSafresh1This is a slightly higher level interface to fork. Running it will fork your
735*5486feefSafresh1code in-place just like C<fork()>. It will return a pid in the parent, and an
736*5486feefSafresh1L<Test2::Util::Guard> instance in the child. An exception will be thrown if
737*5486feefSafresh1fork fails.
738*5486feefSafresh1
739*5486feefSafresh1It is recommended that you use C<< $ast->run_fork(sub { ... }) >> instead.
740*5486feefSafresh1
741*5486feefSafresh1=item $bool = $ast->pending
742*5486feefSafresh1
743*5486feefSafresh1True if there are child processes, threads, or subtests that depend on this
744*5486feefSafresh1one.
745*5486feefSafresh1
746*5486feefSafresh1=item $bool = $ast->ready
747*5486feefSafresh1
748*5486feefSafresh1This is essentially C<< !$ast->pending >>.
749*5486feefSafresh1
750*5486feefSafresh1=item $ast->run(sub { ... })
751*5486feefSafresh1
752*5486feefSafresh1Run the provided codeblock inside the subtest. This will push the subtest hub
753*5486feefSafresh1onto the stack, run the code, then pop the hub off the stack.
754*5486feefSafresh1
755*5486feefSafresh1=item $pid = $ast->run_fork(sub { ... })
756*5486feefSafresh1
757*5486feefSafresh1Same as C<< $ast->run() >>, except that the codeblock is run in a child
758*5486feefSafresh1process.
759*5486feefSafresh1
760*5486feefSafresh1You do not need to directly call C<wait($pid)>, that will be done for you when
761*5486feefSafresh1C<< $ast->wait >>, or C<< $ast->finish >> are called.
762*5486feefSafresh1
763*5486feefSafresh1=item my $thr = $ast->run_thread(sub { ... });
764*5486feefSafresh1
765*5486feefSafresh1B<** DISCOURAGED **> Threads cause problems. This method remains for anyone who
766*5486feefSafresh1REALLY wants it, but it is no longer supported. Tests for this functionality do
767*5486feefSafresh1not even run unless the AUTHOR_TESTING or T2_DO_THREAD_TESTS env vars are
768*5486feefSafresh1enabled.
769*5486feefSafresh1
770*5486feefSafresh1Same as C<< $ast->run() >>, except that the codeblock is run in a child
771*5486feefSafresh1thread.
772*5486feefSafresh1
773*5486feefSafresh1You do not need to directly call C<< $thr->join >>, that is done for you when
774*5486feefSafresh1C<< $ast->wait >>, or C<< $ast->finish >> are called.
775*5486feefSafresh1
776*5486feefSafresh1=item $passing = $ast->start
777*5486feefSafresh1
778*5486feefSafresh1Push the subtest hub onto the stack. Returns the current pass/fail status of
779*5486feefSafresh1the subtest.
780*5486feefSafresh1
781*5486feefSafresh1=item $ast->stop
782*5486feefSafresh1
783*5486feefSafresh1Pop the subtest hub off the stack. Returns the current pass/fail status of the
784*5486feefSafresh1subtest.
785*5486feefSafresh1
786*5486feefSafresh1=item $ast->wait
787*5486feefSafresh1
788*5486feefSafresh1Wait on all threads/processes that were started using C<< $ast->fork >>,
789*5486feefSafresh1C<< $ast->run_fork >>, or C<< $ast->run_thread >>.
790*5486feefSafresh1
791*5486feefSafresh1=back
792*5486feefSafresh1
793*5486feefSafresh1=head1 SOURCE
794*5486feefSafresh1
795*5486feefSafresh1The source code repository for Test2-AsyncSubtest can be found at
796*5486feefSafresh1F<https://github.com/Test-More/Test2-Suite/>.
797*5486feefSafresh1
798*5486feefSafresh1=head1 MAINTAINERS
799*5486feefSafresh1
800*5486feefSafresh1=over 4
801*5486feefSafresh1
802*5486feefSafresh1=item Chad Granum E<lt>exodist@cpan.orgE<gt>
803*5486feefSafresh1
804*5486feefSafresh1=back
805*5486feefSafresh1
806*5486feefSafresh1=head1 AUTHORS
807*5486feefSafresh1
808*5486feefSafresh1=over 4
809*5486feefSafresh1
810*5486feefSafresh1=item Chad Granum E<lt>exodist@cpan.orgE<gt>
811*5486feefSafresh1
812*5486feefSafresh1=back
813*5486feefSafresh1
814*5486feefSafresh1=head1 COPYRIGHT
815*5486feefSafresh1
816*5486feefSafresh1Copyright 2018 Chad Granum E<lt>exodist7@gmail.comE<gt>.
817*5486feefSafresh1
818*5486feefSafresh1This program is free software; you can redistribute it and/or
819*5486feefSafresh1modify it under the same terms as Perl itself.
820*5486feefSafresh1
821*5486feefSafresh1See F<http://dev.perl.org/licenses/>
822*5486feefSafresh1
823*5486feefSafresh1=cut
824