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