# $Id$
# -*-perl-*-
#
# This test tests one client with good time, who grabs a job for 5 seconds.  But while he's
# working on it, another client comes along with a clock set to the future, and grabs the job
# but getting it, since for it, 5 seconds has passed.
#
# This tests that the library doesn't rely on the client's time, but the server's time.
#

use strict;
use warnings;

# make time() be overridable in the future at runtime, rather than be an opcode:
BEGIN {
    *CORE::GLOBAL::time = sub { time() };
}
no warnings 'redefine';

require './t/lib/db-common.pl';

use TheSchwartz;
use Test::More tests => 2;

# how we keep track of if job was done twice:  signal from children back up to us
my $got_job  = 0;
my $got_done = 0;
$SIG{USR1} = sub { $got_job++; };
$SIG{USR2} = sub { $got_done++; };

# kill children on exit
my %children;    # pid -> 1
my $parent = $$;

END {
    if ( $$ == $parent ) {
        my @pids = keys %children;
        kill 9, @pids if @pids;
    }
}

run_tests_innodb(
    2,
    sub {

        # put one job into database
        my $client = test_client( dbs => ['ts1'] );
        $client->insert( "Worker::Addition", { numbers => [ 1, 2 ] } )
            or die;

        # two children to race.  this one with normal time:
        work();

        # let first dude get started first
        select( undef, undef, undef, 1.5 );

# make this worker 60 seconds in the future:  (well past the grabbed until time)
        work(60);

        # hang out waiting for children to finish or timeout
        my $now = time();
        while ( $got_done < 2 && time() < $now + 7 ) {
            sleep 1;
        }

        is( $got_done, 2, "two children finished" );
        is( $got_job,  1, "only did one job" );

        teardown_dbs('ts1');
    }
);

sub work {
    my $future = shift;

    # parent:
    if ( my $childpid = fork() ) {
        $children{$childpid} = 1;
        return;
    }

    if ($future) {
        *CORE::GLOBAL::time = sub { CORE::time() + $future };
    }

    my $client = test_client(
        dbs  => ['ts1'],
        init => 0
    );

    # child:
    while ( my $job = Worker::Addition->grab_job($client) ) {
        eval { Worker::Addition->work($job); };
    }

    kill 'USR2', getppid();
    exit 0;
}

############################################################################
package Worker::Addition;
use base 'TheSchwartz::Worker';

sub work {
    my ( $class, $job ) = @_;
    sleep 3;
    kill 'USR1', getppid();
    $job->completed;
}

# tell framework to set 'grabbed_until' to time() + 60.  because if
# we can't  add some numbers in 30 seconds, our process probably
# failed and work should be reassigned.
sub grab_for {5}

