#!/usr/bin/perl -w
# vim: ts=2 sw=2 expandtab
# Tests session detaching.
use strict;
use lib qw(./mylib ../mylib);
# Trace output local to this test program.
sub DEBUG () { 0 }
sub POE::Kernel::ASSERT_DEFAULT () { 1 }
BEGIN {
package
POE::Kernel;
use constant TRACE_DEFAULT => exists($INC{'Devel/Cover.pm'});
}
use Test::More tests => 9;
use POE;
# Moved "global" test accumulation variables out of the "main" session
# because it was becoming a peer to the others that had been detached.
# Sometimes "main" would be stopped before the others, and the program
# would fail when they tried to post results back to it.
my $test_trace = "";
# Spawn a grandchild.
sub spawn_grandchild {
my $grandchild_id = shift;
POE::Session->create(
inline_states => {
_start => sub {
my $kernel = $_[KERNEL];
$kernel->alias_set( $grandchild_id );
DEBUG and warn $_[SESSION]->ID, " has started.\n";
},
_parent => sub {
my ($kernel, $old_parent, $new_parent) = @_[KERNEL, ARG0, ARG1];
my $old_alias = $kernel->call($old_parent, "get_alias");
my $new_alias;
if (ref($new_parent) eq 'POE::Kernel') {
$new_alias = 'kernel';
}
else {
$new_alias = $kernel->call($new_parent, "get_alias");
}
$test_trace .= "(p $grandchild_id $old_alias $new_alias)";
},
_child => sub {
my ($kernel, $op, $child) = @_[KERNEL, ARG0, ARG1];
my $child_alias = $kernel->call($child, 'get_alias' );
$test_trace .= "(c $grandchild_id $op $child_alias)";
},
get_alias => sub {
return $grandchild_id;
},
detach_self => sub {
$_[KERNEL]->detach_myself();
},
detach_child => sub {
$_[KERNEL]->detach_child( $_[ARG0] );
},
_stop => sub {
my $kernel = $_[KERNEL];
DEBUG and warn $_[SESSION]->ID, " stopped.\n";
},
},
);
# To prevent this from returning a session reference.
undef;
}
# Spawn a child.
sub spawn_child {
my $child_id = shift;
my $alias = "a$child_id";
POE::Session->create(
inline_states => {
_start => sub {
my $kernel = $_[KERNEL];
$kernel->alias_set( $alias );
$kernel->yield( 'spawn_grandchildren' );
DEBUG and warn $_[SESSION]->ID, " has started.\n";
},
spawn_grandchildren => sub {
spawn_grandchild( $alias . "_1" );
spawn_grandchild( $alias . "_2" );
spawn_grandchild( $alias . "_3" );
},
_parent => sub {
my ($kernel, $old_parent, $new_parent) = @_[KERNEL, ARG0, ARG1];
my $old_alias = $kernel->call($old_parent, 'get_alias');
my $new_alias;
if (ref($new_parent) eq 'POE::Kernel') {
$new_alias = 'kernel';
}
else {
$new_alias = $kernel->call($new_parent, 'get_alias');
}
$test_trace .= "(p $child_id $old_alias $new_alias)";
},
_child => sub {
my ($kernel, $op, $child) = @_[KERNEL, ARG0, ARG1];
my $child_alias = $kernel->call($child, 'get_alias' );
$test_trace .= "(c $child_id $op $child_alias)";
},
get_alias => sub {
return $child_id;
},
detach_self => sub {
my $kernel = $_[KERNEL];
$kernel->detach_myself();
},
detach_child => sub {
my $kernel = $_[KERNEL];
$kernel->detach_child( $_[ARG0] );
},
_stop => sub {
my $kernel = $_[KERNEL];
DEBUG and warn $_[SESSION]->ID, " has stopped.\n";
},
},
);
# To prevent this from returning a session reference.
undef;
}
# Spawn the main session. This will spawn children, which will spawn
# grandchildren. Then the main session will perform controlled
# detaches and watch the results.
POE::Session->create(
inline_states => {
_start => sub {
my ($kernel, $heap) = @_[KERNEL, HEAP];
$heap->{idle_count} = 0;
$kernel->alias_set( 'main' );
$kernel->yield( 'spawn_children' );
DEBUG and warn $_[SESSION]->ID, " has started.\n";
},
spawn_children => sub {
my $kernel = $_[KERNEL];
spawn_child( 1 );
spawn_child( 2 );
spawn_child( 3 );
$kernel->delay( run_tests => 0.5 );
},
get_alias => sub {
return 'main';
},
detach_self => sub {
my $kernel = $_[KERNEL];
$kernel->detach_myself();
},
detach_child => sub {
my $kernel = $_[KERNEL];
$kernel->detach_child( $_[ARG0] );
},
run_tests => sub {
my ($kernel, $heap) = @_[KERNEL, HEAP];
$test_trace = "";
$kernel->call( a1_1 => 'detach_self' );
is(
$test_trace, '(c 1 lose a1_1)(p a1_1 1 kernel)',
"a1_1 detached itself"
);
$test_trace = '';
$kernel->call( a2_1 => 'detach_self' );
is(
$test_trace, '(c 2 lose a2_1)(p a2_1 2 kernel)',
"a2_1 detached itself"
);
$test_trace = '';
$kernel->call( a3_1 => 'detach_self' );
is(
$test_trace, '(c 3 lose a3_1)(p a3_1 3 kernel)',
"a3_1 detached itself"
);
$test_trace = '';
$kernel->call( a1 => detach_child => 'a1_2' );
is(
$test_trace, '(c 1 lose a1_2)(p a1_2 1 kernel)',
"a1 detached child a1_2"
);
$test_trace = '';
$kernel->call( a2 => detach_child => 'a2_2' );
is(
$test_trace, '(c 2 lose a2_2)(p a2_2 2 kernel)',
"a2 detached child a2_2"
);
$test_trace = '';
$kernel->call( a3 => detach_child => 'a3_2' );
is(
$test_trace, '(c 3 lose a3_2)(p a3_2 3 kernel)',
"a3 detached child a3_2"
);
$test_trace = '';
$kernel->call( a1 => 'detach_self' );
is(
$test_trace, '(c main lose 1)(p 1 main kernel)',
"a1 detached itself"
);
$test_trace = '';
$kernel->call( main => detach_child => 'a2' );
is(
$test_trace, '(c main lose 2)(p 2 main kernel)',
"a2 detached itself"
);
},
_parent => sub {
my $old_alias = $_[KERNEL]->call( $_[ARG0], 'get_alias' );
my $new_alias;
if (ref($_[ARG1]) eq 'POE::Kernel') {
$new_alias = 'kernel';
}
else {
$new_alias = $_[KERNEL]->call( $_[ARG1], 'get_alias' );
}
$test_trace .= "(p main $old_alias $new_alias)";
},
_child => sub {
my $child_alias = $_[KERNEL]->call( $_[ARG1], 'get_alias' );
$test_trace .= "(c main $_[ARG0] $child_alias)";
},
_stop => sub {
DEBUG and warn $_[SESSION]->ID, " has stopped.\n";
},
grandchild_parent => sub {
my $old_alias = $_[KERNEL]->call( $_[ARG1], 'get_alias' );
my $new_alias;
if (ref($_[ARG2]) eq 'POE::Kernel') {
$new_alias = 'kernel';
}
else {
$new_alias = $_[KERNEL]->call( $_[ARG2], 'get_alias' );
}
$test_trace .= "(p $_[ARG0] $old_alias $new_alias)";
},
grandchild_child => sub {
my $child_alias = $_[KERNEL]->call( $_[ARG2], 'get_alias' );
$test_trace .= "(c $_[ARG0] $_[ARG1] $child_alias)";
},
},
);
POE::Kernel->run();
# Final test to see if the remaining sessions died properly. The
# trace string can be nondeterministic. Split it, sort it, and rejoin
# it so it's always in a known order.
substr($test_trace, 0, 1) = '';
substr($test_trace, -1, 1) = '';
$test_trace = '(' . (join ')(', sort split /\)\(/, $test_trace) . ')';
is(
$test_trace,
join(
"",
"(c 1 lose a1_3)",
"(c 2 lose a2_3)",
"(c 3 lose a3_3)",
"(c main lose 2)",
"(c main lose 3)",
"(p 2 main kernel)"
),
"session destruction order"
);
1;