# vim: ts=2 sw=2 expandtab

# Tests propagation of signals through the session ancestry

use warnings;
use strict;

use Test::More tests => 7;

sub POE::Kernel::ASSERT_DEFAULT () { 1 }

BEGIN {
  package POE::Kernel;
  use constant TRACE_DEFAULT => exists($INC{'Devel/Cover.pm'});
}

use POE;

{
	my @log;

	my $session = POE::Session->create(
		inline_states => {
			_start => sub {
				push @log, [ enter_start => $_[SESSION] ];
				$_[KERNEL]->sig("foo" => "foo");
				$_[KERNEL]->signal( $_[SESSION], "foo" );
				push @log, [ leave_start => $_[SESSION] ];
			},
			_child   => sub { push @log, [ child => $_[SESSION] ] },
			_stop    => sub { push @log, [ stop => $_[SESSION] ] },
			_default => sub { push @log, [ default => @_[STATE, SESSION] ] },
			foo      => sub { push @log, [ foo => $_[SESSION] ] },
		},
	);

	POE::Kernel->run;

	is_deeply(
		\@log,
		[
			[ enter_start => $session ],
			[ leave_start => $session ],
			[ foo => $session ],
			[ stop => $session ],
		],
		"simple signal on one session",
	);
}

{
	my @log;

	my $child;
	my $session = POE::Session->create(
		inline_states => {
			_start => sub {
				push @log, [ enter_start => $_[SESSION] ];
				$child = POE::Session->create(
					inline_states => {
						_start => sub {
							push @log, [ enter_start => $_[SESSION] ];
							$_[KERNEL]->delay("bar" => 0.1);
							push @log, [ leave_start => $_[SESSION] ];
						},
						_child   => sub { push @log, [ child => $_[SESSION] ] },
						_stop    => sub { push @log, [ stop => $_[SESSION] ] },
						_default => sub { push @log, [ default => @_[ARG0, SESSION] ] },
						foo      => sub { push @log, [ foo => $_[SESSION] ] },
					},
				);
				$_[KERNEL]->sig("foo" => "foo");
				$_[KERNEL]->signal( $_[SESSION], "foo" );
				push @log, [ leave_start => $_[SESSION] ];
			},
			_child   => sub { push @log, [ child => $_[SESSION] ] },
			_stop    => sub { push @log, [ stop => $_[SESSION] ] },
			_default => sub { push @log, [ default => @_[ARG0, SESSION] ] },
			foo      => sub { push @log, [ foo => $_[SESSION] ] },
		},
	);

	POE::Kernel->run;

	is_deeply(
		\@log,
		[
			[ enter_start => $session ],
			[ enter_start => $child ],
			[ leave_start => $child ],
			[ child => $session ],
			[ leave_start => $session ],
			[ foo => $session ],
			[ default => bar => $child ],
			[ stop => $child ],
			[ child => $session ],
			[ stop => $session ],
		],
		"signal on parent, oblivious child",
	);
}

{
	my @log;

	my $child;
	my $session = POE::Session->create(
		inline_states => {
			_start => sub {
				push @log, [ enter_start => $_[SESSION] ];
				$child = POE::Session->create(
					inline_states => {
						_start => sub {
							push @log, [ enter_start => $_[SESSION] ];
							$_[KERNEL]->delay("bar" => 0.1);
							$_[KERNEL]->sig("foo" => "foo");
							push @log, [ leave_start => $_[SESSION] ];
						},
						_child   => sub { push @log, [ child => $_[SESSION] ] },
						_stop    => sub { push @log, [ stop => $_[SESSION] ] },
						_default => sub { push @log, [ default => @_[ARG0, SESSION] ] },
						foo      => sub { push @log, [ foo => $_[SESSION] ] },
					},
				);
				$_[KERNEL]->sig("foo" => "foo");
				$_[KERNEL]->signal( $_[SESSION], "foo" );
				push @log, [ leave_start => $_[SESSION] ];
			},
			_child   => sub { push @log, [ child => $_[SESSION] ] },
			_stop    => sub { push @log, [ stop => $_[SESSION] ] },
			_default => sub { push @log, [ default => @_[ARG0, SESSION] ] },
			foo      => sub { push @log, [ foo => $_[SESSION] ] },
		},
	);

	POE::Kernel->run;

	is_deeply(
		\@log,
		[
			[ enter_start => $session ],
			[ enter_start => $child ],
			[ leave_start => $child ],
			[ child => $session ],
			[ leave_start => $session ],
			[ foo => $child ],
			[ foo => $session ],
			[ default => bar => $child ],
			[ stop => $child ],
			[ child => $session ],
			[ stop => $session ],
		],
		"signal on child, then parent",
	);
}

{
	my @log;

	my $child;
	my $session = POE::Session->create(
		inline_states => {
			_start => sub {
				push @log, [ enter_start => $_[SESSION] ];
				$child = POE::Session->create(
					inline_states => {
						_start => sub {
							push @log, [ enter_start => $_[SESSION] ];
							$_[KERNEL]->delay("bar" => 1);
							$_[KERNEL]->sig("TERM" => "TERM");
							push @log, [ leave_start => $_[SESSION] ];
						},
						_child   => sub { push @log, [ child => $_[SESSION] ] },
						_stop    => sub { push @log, [ stop => $_[SESSION] ] },
						_default => sub { push @log, [ default => @_[ARG0, SESSION] ] },
						foo      => sub { push @log, [ foo => $_[SESSION] ] },
					},
				);
				$_[KERNEL]->sig("TERM" => "TERM");
				$_[KERNEL]->signal( $_[SESSION], "TERM" );
				push @log, [ leave_start => $_[SESSION] ];
			},
			_child   => sub { push @log, [ child => $_[SESSION] ] },
			_stop    => sub { push @log, [ stop => $_[SESSION] ] },
			_default => sub { push @log, [ default => @_[ARG0, SESSION] ] },
			foo      => sub { push @log, [ foo => $_[SESSION] ] },
		},
	);

	POE::Kernel->run;

	is_deeply(
		\@log,
		[
			[ enter_start => $session ],
			[ enter_start => $child ],
			[ leave_start => $child ],
			[ child => $session ],
			[ leave_start => $session ],
			[ default => TERM => $child ],
			[ default => TERM => $session ],
			[ stop => $child ],
			[ child => $session ],
			[ stop => $session ],
		],
		"TERM signal on child, then parent",
	);
}

{
	my @log;

	my $child;
	my $session = POE::Session->create(
		inline_states => {
			_start => sub {
				push @log, [ enter_start => $_[SESSION] ];
				$child = POE::Session->create(
					inline_states => {
						_start => sub {
							push @log, [ enter_start => $_[SESSION] ];
							$_[KERNEL]->delay("bar" => 1);
							push @log, [ leave_start => $_[SESSION] ];
						},
						_child   => sub { push @log, [ child => $_[SESSION] ] },
						_stop    => sub { push @log, [ stop => $_[SESSION] ] },
						_default => sub { push @log, [ default => @_[ARG0, SESSION] ] },
						foo      => sub { push @log, [ foo => $_[SESSION] ] },
					},
				);
				$_[KERNEL]->signal( $_[SESSION], "TERM" );
				push @log, [ leave_start => $_[SESSION] ];
			},
			_child   => sub { push @log, [ child => $_[SESSION] ] },
			_stop    => sub { push @log, [ stop => $_[SESSION] ] },
			_default => sub { push @log, [ default => @_[ARG0, SESSION] ] },
			foo      => sub { push @log, [ foo => $_[SESSION] ] },
		},
	);

	POE::Kernel->run;

	is_deeply(
		\@log,
		[
			[ enter_start => $session ],
			[ enter_start => $child ],
			[ leave_start => $child ],
			[ child => $session ],
			[ leave_start => $session ],
			[ stop => $child ],
			[ child => $session ],
			[ stop => $session ],
		],
		"TERM signal with no handlers on child, then parent",
	);
}

{
	my @log;

	my ( $child, $grandchild );
	my $session = POE::Session->create(
		inline_states => {
			_start => sub {
				push @log, [ enter_start => $_[SESSION] ];
				$child = POE::Session->create(
					inline_states => {
						_start => sub {
							push @log, [ enter_start => $_[SESSION] ];
							$grandchild = POE::Session->create(
								inline_states => {
									_start => sub {
										push @log, [ enter_start => $_[SESSION] ];
										$_[KERNEL]->delay("bar" => 1);
										push @log, [ leave_start => $_[SESSION] ];
									},
									_child   => sub { push @log, [ child => $_[SESSION] ] },
									_stop    => sub { push @log, [ stop => $_[SESSION] ] },
									_default => sub { push @log, [ default => @_[ARG0, SESSION] ] },
									foo      => sub { push @log, [ foo => $_[SESSION] ] },
								},
							);
							$_[KERNEL]->sig( TERM => "TERM" );
							$_[KERNEL]->delay("bar" => 1);
							push @log, [ leave_start => $_[SESSION] ];
						},
						_child   => sub { push @log, [ child => $_[SESSION] ] },
						_stop    => sub { push @log, [ stop => $_[SESSION] ] },
						_default => sub { push @log, [ default => @_[ARG0, SESSION] ] },
						foo      => sub { push @log, [ foo => $_[SESSION] ] },
					},
				);
				$_[KERNEL]->signal( $_[SESSION], "TERM" );
				push @log, [ leave_start => $_[SESSION] ];
			},
			_child   => sub { push @log, [ child => $_[SESSION] ] },
			_stop    => sub { push @log, [ stop => $_[SESSION] ] },
			_default => sub { push @log, [ default => @_[ARG0, SESSION] ] },
			foo      => sub { push @log, [ foo => $_[SESSION] ] },
		},
	);

	POE::Kernel->run;

	is_deeply(
		\@log,
		[
			[ enter_start => $session ],
			[ enter_start => $child ],
			[ enter_start => $grandchild ],
			[ leave_start => $grandchild ],
			[ child => $child ],
			[ leave_start => $child ],
			[ child => $session ],
			[ leave_start => $session ],
			[ default => TERM => $child ],
			[ stop => $grandchild ],
			[ child => $child ],
			[ stop => $child ],
			[ child => $session ],
			[ stop => $session ],
		],
		"TERM signal on granchild, then child (with handler), then parent",
	);
}

{
	my @log;

	my ( $child, $grandchild );
	my $session = POE::Session->create(
		inline_states => {
			_start => sub {
				push @log, [ enter_start => $_[SESSION] ];
				$child = POE::Session->create(
					inline_states => {
						_start => sub {
							push @log, [ enter_start => $_[SESSION] ];
							$grandchild = POE::Session->create(
								inline_states => {
									_start => sub {
										push @log, [ enter_start => $_[SESSION] ];
										$_[KERNEL]->delay("bar" => 1);
										push @log, [ leave_start => $_[SESSION] ];
									},
									_child   => sub { push @log, [ child => $_[SESSION] ] },
									_stop    => sub { push @log, [ stop => $_[SESSION] ] },
									_default => sub { push @log, [ default => @_[ARG0, SESSION] ] },
									foo      => sub { push @log, [ foo => $_[SESSION] ] },
								},
							);
							$_[KERNEL]->delay("bar" => 1);
							push @log, [ leave_start => $_[SESSION] ];
						},
						_child   => sub { push @log, [ child => $_[SESSION] ] },
						_stop    => sub { push @log, [ stop => $_[SESSION] ] },
						_default => sub { push @log, [ default => @_[ARG0, SESSION] ] },
						foo      => sub { push @log, [ foo => $_[SESSION] ] },
					},
				);
				$_[KERNEL]->signal( $_[SESSION], "TERM" );
				push @log, [ leave_start => $_[SESSION] ];
			},
			_child   => sub { push @log, [ child => $_[SESSION] ] },
			_stop    => sub { push @log, [ stop => $_[SESSION] ] },
			_default => sub { push @log, [ default => @_[ARG0, SESSION] ] },
			foo      => sub { push @log, [ foo => $_[SESSION] ] },
		},
	);

	POE::Kernel->run;

	is_deeply(
		\@log,
		[
			[ enter_start => $session ],
			[ enter_start => $child ],
			[ enter_start => $grandchild ],
			[ leave_start => $grandchild ],
			[ child => $child ],
			[ leave_start => $child ],
			[ child => $session ],
			[ leave_start => $session ],
			[ stop => $grandchild ],
			[ child => $child ],
			[ stop => $child ],
			[ child => $session ],
			[ stop => $session ],
		],
		"TERM signal with no handlers on granchild, then child, then parent",
	);
}

1;
