package Test::SharedFork; use strict; use warnings; use base 'Test::Builder::Module'; our $VERSION = '0.35'; use Test::Builder 0.32; # 0.32 or later is needed use Test::SharedFork::Scalar; use Test::SharedFork::Array; use Test::SharedFork::Store; use Config; use 5.008000; { package # Test::SharedFork::Contextual; sub call { my $code = shift; my $wantarray = [caller(1)]->[5]; if ($wantarray) { my @result = $code->(); bless {result => \@result, wantarray => $wantarray}, __PACKAGE__; } elsif (defined $wantarray) { my $result = $code->(); bless {result => $result, wantarray => $wantarray}, __PACKAGE__; } else { { ; $code->(); } # void context bless {wantarray => $wantarray}, __PACKAGE__; } } sub result { my $self = shift; if ($self->{wantarray}) { return @{ $self->{result} }; } elsif (defined $self->{wantarray}) { return $self->{result}; } else { return; } } } my $STORE; sub _mangle_builder { my $builder = shift; if( $] >= 5.008001 && $Config{useithreads} && $INC{'threads.pm'} ) { die "# Current version of Test::SharedFork does not supports ithreads."; } if ($builder->can("coordinate_forks")) { # Use Test::Builder's implementation. $builder->new->coordinate_forks(1); } elsif($INC{'Test2/Global.pm'} || $INC{'Test2/API.pm'} || $INC{'Test2/Context.pm'}) { require Test2::Global; Test2::Global::test2_ipc_enable_polling(); # Check if we already have IPC my $stack = $builder->{Stack}; return if $stack->top->ipc; # Find a driver my ($driver) = Test2::Global::test2_ipc_drivers(); unless ($driver) { require Test2::IPC::Driver::Files; $driver = 'Test2::IPC::Driver::Files'; } # Add the IPC to all hubs my $ipc = $driver->new(); for my $hub (@$stack) { $hub->set_ipc($ipc); $ipc->add_hub($hub->hid); } } elsif($INC{'Test/Stream/Sync.pm'}) { require Test::Stream::IPC; Test::Stream::IPC->import('poll'); Test::Stream::IPC->enable_polling if Test::Stream::IPC->can('enable_polling'); my $stack = $builder->{Stack}; return if $stack->top->ipc; my ($driver) = Test::Stream::IPC->drivers; my $ipc = $driver->new(); for my $hub (@$stack) { $hub->set_ipc($ipc); $ipc->add_hub($hub->hid); } } else { # older Test::Builder $STORE = Test::SharedFork::Store->new( cb => sub { my $store = shift; tie $builder->{Curr_Test}, 'Test::SharedFork::Scalar', $store, 'Curr_Test'; tie $builder->{Is_Passing}, 'Test::SharedFork::Scalar', $store, 'Is_Passing'; tie @{ $builder->{Test_Results} }, 'Test::SharedFork::Array', $store, 'Test_Results'; }, init => +{ Test_Results => $builder->{Test_Results}, Curr_Test => $builder->{Curr_Test}, Is_Passing => 1, }, ); # make methods atomic. no strict 'refs'; no warnings 'redefine'; no warnings 'uninitialized'; for my $name (qw/ok skip todo_skip current_test is_passing/) { my $orig = *{"Test::Builder::${name}"}{CODE}; *{"Test::Builder::${name}"} = sub { local $Test::Builder::Level = $Test::Builder::Level + 1; local $Test::Builder::BLevel = $Test::Builder::BLevel + 1; my $lock = $STORE->get_lock(); # RAII $orig->(@_); }; }; } } BEGIN { my $builder = __PACKAGE__->builder; _mangle_builder($builder); } { # backward compatibility method sub parent { } sub child { } sub fork { fork() } } 1; __END__ =for stopwords slkjfd yappo konbuizm =head1 NAME Test::SharedFork - fork test =head1 SYNOPSIS use Test::More tests => 200; use Test::SharedFork; my $pid = fork(); if ($pid == 0) { # child ok 1, "child $_" for 1..100; } elsif ($pid) { # parent ok 1, "parent $_" for 1..100; waitpid($pid, 0); } else { die $!; } =head1 DESCRIPTION Test::SharedFork is utility module for Test::Builder. This module makes L safety in your test case. This module merges test count with parent process & child process. =head1 LIMITATIONS This version of the Test::SharedFork does not support ithreads, because L conflicts with L. =head1 AUTHOR Tokuhiro Matsuno Etokuhirom slkjfd gmail.comE yappo =head1 THANKS TO kazuhooku konbuizm =head1 SEE ALSO L, L, L =head1 LICENSE This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut