use Forks::Super ':test';
use Test::More tests => 10;
use Carp;
use strict;
use warnings;

# can we set priority, cpu affinity on daemon jobs?
# do daemon jobs recognize names? delays? dependencies?

our $CWD = &Cwd::getcwd;
if (${^TAINT}) {
    my $ipc_dir = Forks::Super::Job::Ipc::_choose_dedicated_dirname();
    if (! eval {$ipc_dir = Cwd::abs_path($ipc_dir)}) {
	$ipc_dir = Cwd::getcwd() . "/" . $ipc_dir;
    }
    ($ipc_dir) = $ipc_dir =~ /(.*)/;
    Forks::Super::Job::Ipc::set_ipc_dir($ipc_dir);
    ($CWD) = $CWD =~ /(.*)/;
    ($^X) = $^X =~ /(.*)/;
    $ENV{PATH}='';
}

# need a separate test for MSWin32
if ($^O eq 'MSWin32') {
  SKIP: {
      skip "test $0 not for use with MSWin32", 10;
    }
    exit;
}

my $pid = fork { sub => sub { sleep 5 } };
my $base_priority = get_os_priority($pid);
my $np = Forks::Super::Config::CONFIG_module("Sys::CpuAffinity")
    ? Sys::CpuAffinity::getNumCpus() : 0;

my $daemon = fork {
    sub => sub { sleep 10 },
    daemon => 1,
    os_priority => $base_priority + 1,
    cpu_affinity => $np > 1 ? 2 : 1
};

# it may take a second or three after the background process is launched
# for F::S to update its priority and CPU affinity

my ($new_priority, $affinity);
for (1..10) {

    $new_priority = get_os_priority($daemon);
    if ($new_priority != -99) {
	if ($np > 1 && Forks::Super::Config::CONFIG('Sys::CpuAffinity')) {
	    $affinity = Sys::CpuAffinity::getAffinity($daemon);
	    last if $new_priority != $base_priority && $affinity == 2;
	} else {
	    last if $new_priority != $base_priority;
	}
    }
    if ($_==8) {
	warn "priority/affinity $new_priority/$affinity still not ",
	    "at expected levels after 8 seconds";
    }
    sleep 1;
}

ok($new_priority == $base_priority + 1,
   "set os priority on daemon process")
    or diag("failed to update priority $base_priority => $new_priority");

SKIP: {
    if ($np <= 1) {
	skip "one processor, can't test set CPU affinity", 1;
    }
    if (!Forks::Super::Config::CONFIG('Sys::CpuAffinity')) {
	skip 'Sys::CpuAffinity not avail. Skip CPU affinity test', 1;
    }
    ok($affinity == 2, "set CPU affinity on daemon process")
	or diag("affinity of $daemon was $affinity, expected 2; ",
	        "Sys::CpuAffinity v. $Sys::CpuAffinity::VERSION");
}
$daemon->kill('KILL');


my $t = Time::HiRes::time();
my $d1 = fork {
    daemon => 1,
    sub => sub { sleep 5 },
    delay => 3
};
ok($d1->{state} eq 'DEFERRED', 'daemon job was delayed');
for (1..5) {
    Forks::Super::pause(1) while $d1->{state} eq 'DEFERRED';
}
ok($d1->{state} ne 'DEFERRED', 'daemon job was started');
ok(!Forks::Super::Util::isValidPid($d1->{pid}),"pid is for deferred job");
ok(Forks::Super::Util::isValidPid($d1->{real_pid}),"real pid is valid");

my $n1 = fork {
    daemon => 0,
    sub => sub {
        print time-$^T," DAEMON 1 MONITOR START\n";
	sleep 1 while CORE::kill 0, $d1->{real_pid};
        print time-$^T," DAEMON 1 MONITOR COMPLETE\n";
    },
    name => 'daemon1 monitor'
};
sleep 1;

my $d2 = fork {
    daemon => 1,
    name => 'daemon2',
    depend_on => 'daemon1 monitor',
    sub => sub { sleep 1 },
    on_busy => 'queue',
    debug => 0,
};

ok($d2->{state} eq 'DEFERRED', '2nd daemon is deferred');
Forks::Super::Util::pause(6);

ok($d1 && $d2, "daemon procs launched") or diag("d1=$d1, d2=$d2");
ok($d1->{start} > $t + 2, "daemon1 launch was delayed");
ok($d2->{start} >= ($n1->{end}||0),                            ### 10 ###
   "daemon2 launch waited for daemon1")
    or diag("expected d2 start $d2->{start} >= $n1->{end} d1 monitor end");


#############################################################################

sub get_os_priority {
    my ($pid) = @_;

    # freebsd: on error, getpriority returns -1 and sets $!

    my $p;
    local $! = 0;
    eval {
	$p = getpriority(0, $pid);
    };
    if ($@ eq '') {
	if ($p == -1 && $!) {
	    carp "get_os_priority($pid): $!";
	    return -99;
	}
	return $p;
    }

    if ($^O eq 'MSWin32') {
	return Forks::Super::Job::OS::Win32::get_priority($pid);
    }
    return;
}
