#!perl
use warnings;
use strict;
# TIMEOUT_MULT allows scaling all timing values for slow machines (default: 1)
use constant TIMEOUT_MULT => $ENV{PERL_TEST_TIME_OUT_FACTOR} || ($ENV{AUTOMATED_TESTING} ? 2 : 1);
use constant HARDER => $ENV{RELEASE_TESTING} ? 10 : 1;
use constant POST_CLIENTS => HARDER*1;
use constant GET_CLIENTS => HARDER*1;
use constant GOOD_CLIENTS => HARDER*1;
use Test::More tests =>
    19 + 2*POST_CLIENTS + 2*GET_CLIENTS + 4*GOOD_CLIENTS;
use Test::Fatal;
use lib 't'; use Utils;

BEGIN { use_ok('Feersum') };

my ($socket,$port) = get_listen_socket();
ok $socket, "made listen socket";
ok $socket->fileno, "has a fileno";

my $evh = Feersum->new();
is exception { $evh->use_socket($socket) }, undef,;
$evh->request_handler(sub {
    my $r = shift;
    my $env = $r->env();
    ok $env->{HTTP_X_GOOD_CLIENT}, "got a request from a good client";
    $r->send_response(200, ["Content-Type" => "text/plain"], "thx.");
});

my $default = $evh->read_timeout;
is $default, 5.0, "default timeout is 5 seconds";

like exception { $evh->read_timeout(-1.0) },
    qr/^must set a positive \(non-zero\) value for the timeout/,
    "can't set a negative number";
is $evh->read_timeout, 5.0;

like exception { $evh->read_timeout(0) },
    qr/^must set a positive \(non-zero\) value for the timeout/,
    "can't set a negative number";
is $evh->read_timeout, 5.0;

like exception {
    no warnings 'numeric';
    $evh->read_timeout("this isn't a number");
}, qr/^must set a positive \(non-zero\) value for the timeout/,
   "can't set a string as the timeout";
is $evh->read_timeout, 5.0;

is exception { $evh->read_timeout(6+1) }, undef, "IV is OK";
is $evh->read_timeout, 7.0, "new timeout set";

is exception { $evh->read_timeout("8.0") }, undef, "NV-as-string is OK";
is $evh->read_timeout, 8.0, "new timeout set";

is exception { $evh->read_timeout($default) }, undef, "NV is OK";
is $evh->read_timeout, $default, "reset to default";

# Use a shorter timeout for the actual client tests to keep the test fast
# and leave ample margin before the guard timer fires.
$evh->read_timeout(2.0);

my $cv = AE::cv;

sub timeout_get_client {
    my $n = shift;
    $cv->begin;
    my $ot; $ot = AE::timer rand(1), 0, sub {
        my $h; $h = simple_client GET => '/',
            name => "(get $n)",
            timeout => 8 * TIMEOUT_MULT,
            skip_head => 1,
        sub {
            my ($body,$headers) = @_;
            is $headers->{Status}, 408, "(get $n) got timeout";
            $cv->end;
            undef $h;
        };
        undef $ot;
    };
}

sub timeout_post_client {
    my $n = shift;
    $cv->begin;
    my $ot; $ot = AE::timer rand(1), 0, sub {
        my $h; $h = simple_client POST => '/',
            name => "(post $n)",
            timeout => 8 * TIMEOUT_MULT,
            headers => {
                # C-L with no body puts simple_client into stream mode
                'Content-Length' => 8,
                'Content-Type' => 'text/plain',
            },
        sub {
            my ($body,$headers) = @_;
            is $headers->{Status}, 408, "(post $n) got timeout";
            $cv->end;
            undef $h;
        };
        $h->push_write("o "); # 2 out of claimed 8 bytes
        my $t; $t = AE::timer rand(0.5),0,sub {
            $h->push_write("hai"); # 3 more out of claimed 8 bytes
            undef $t; # keep ref
        };
        undef $ot;
    };
}

sub good_client {
    my $n = "(good $_[0])";
    $cv->begin;
    my $ot; $ot = AE::timer rand(1),0,sub {
        my $h; $h = simple_client POST => "/rad", 
            name => $n,
            headers => {'X-Good-Client' => 1},
            body => 'Here it is!',
        sub {
            my ($body,$headers) = @_;
            is $headers->{Status}, 200, "$n got 200";
            is $body, "thx.", "$n got body";
            $cv->end;
            undef $h; # keep ref
        };
        undef $ot;
    };
}

my $t; $t = AE::timer 8 * TIMEOUT_MULT, 0, sub {
    $cv->croak("TOO LONG");
};

$cv->begin;
timeout_get_client($_) for (1 .. GET_CLIENTS);
timeout_post_client($_) for (1 .. POST_CLIENTS);
good_client($_) for (1 .. GOOD_CLIENTS);
$cv->end;

is exception { $cv->recv }, undef, "no client errors";
pass "all done";
