#!/usr/bin/perl -w

use strict;

use Test::More tests => 29;
use Test::Exception;

use IO::Async::ChildManager;

use POSIX qw( SIGTERM WIFEXITED WEXITSTATUS WIFSIGNALED WTERMSIG );

use IO::Async::Set::IO_Poll;

my $manager = IO::Async::ChildManager->new();

ok( defined $manager, '$manager defined' );
is( ref $manager, "IO::Async::ChildManager", 'ref $manager is IO::Async::ChildManager' );

my $handled;
$handled = $manager->SIGCHLD;

is( $handled, 0, '$handled while idle' );

is_deeply( [ $manager->list_watching ], [], 'list_watching while idle' );

my $set = IO::Async::Set::IO_Poll->new();

$set->attach_signal( CHLD => sub { $handled = $manager->SIGCHLD } );

my $kid = fork();
defined $kid or die "Cannot fork() - $!";

if( $kid == 0 ) {
   exit( 3 );
}

my $exitcode;

sub wait_for_exit
{
   my $ready = 0;
   undef $exitcode;

   my ( undef, $callerfile, $callerline ) = caller();

   while( !defined $exitcode ) {
      $_ = $set->loop_once( 10 ); # Give code a generous 10 seconds to exit
      die "Nothing was ready after 10 second wait; called at $callerfile line $callerline\n" if $_ == 0;
      $ready += $_;
   }

   $ready;
}

$manager->watch( $kid => sub { ( undef, $exitcode ) = @_; } );

ok( $manager->is_watching( $kid ), 'is_watching after adding $kid' );
is_deeply( [ $manager->list_watching ], [ $kid ], 'list_watching after adding $kid' );

my $ready;
$ready = wait_for_exit;

is( $ready, 1, '$ready after child exit' );
is( $handled, 1, '$handled after child exit' );

ok( WIFEXITED($exitcode),      'WIFEXITED($exitcode) after child exit' );
is( WEXITSTATUS($exitcode), 3, 'WEXITSTATUS($exitcode) after child exit' );

ok( !$manager->is_watching( $kid ), 'is_watching after child exit' );
is_deeply( [ $manager->list_watching ], [], 'list_watching after child exit' );

$kid = fork();
defined $kid or die "Cannot fork() - $!";

if( $kid == 0 ) {
   sleep( 10 );
   # Just in case the parent died already and didn't kill us
   exit( 0 );
}

$manager->watch( $kid => sub { ( undef, $exitcode ) = @_; } );

ok( $manager->is_watching( $kid ), 'is_watching after adding $kid' );
is_deeply( [ $manager->list_watching ], [ $kid ], 'list_watching after adding $kid' );

$ready = $set->loop_once( 0.1 );

ok( $manager->is_watching( $kid ), 'is_watching after loop' );
is_deeply( [ $manager->list_watching ], [ $kid ], 'list_watching after loop' );

is( $ready, 0, '$ready after no death' );

kill SIGTERM, $kid;

$ready = wait_for_exit;

is( $ready, 1, '$ready after child SIGTERM' );
is( $handled, 1, '$handled after child SIGTERM' );

ok( WIFSIGNALED($exitcode),          'WIFSIGNALED($exitcode) after SIGTERM' );
is( WTERMSIG($exitcode),    SIGTERM, 'WTERMSIG($exitcode) after SIGTERM' );

ok( !$manager->is_watching( $kid ), 'is_watching after child SIGTERM' );
is_deeply( [ $manager->list_watching ], [], 'list_watching after child SIGTERM' );

# Now lets test the integration with a ::Set

$set->detach_signal( 'CHLD' );
undef $manager;

dies_ok( sub { $set->watch_child( 1234 => sub { "DUMMY" } ) },
         'watch_child() before enable_childmanager() fails' );

$set->enable_childmanager;

dies_ok( sub { $set->enable_childmanager; },
         'enable_childmanager() again fails' );

$kid = fork();
defined $kid or die "Cannot fork() - $!";

if( $kid == 0 ) {
   exit( 5 );
}

$set->watch_child( $kid => sub { ( undef, $exitcode ) = @_; } );

$ready = wait_for_exit;

is( $ready, 1, '$ready after child exit for set' );

ok( WIFEXITED($exitcode),      'WIFEXITED($exitcode) after child exit for set' );
is( WEXITSTATUS($exitcode), 5, 'WEXITSTATUS($exitcode) after child exit for set' );

$set->disable_childmanager;

dies_ok( sub { $set->disable_childmanager; },
         'disable_childmanager() again fails' );


syntax highlighted by Code2HTML, v. 0.9.1