use lib '.','./t','./lib','../lib';
# can run from here or distribution base

use Test::More;
### use Data::Dumper;
eval "use DefaultPort;";
if ($@) {
    plan skip_all => 'No serial port selected for use with testing';
}
else {
    plan tests => 146;
}
cmp_ok($Win32::SerialPort::VERSION, '>=', 0.20, 'VERSION check');

# USB and virtual ports can't test output timing, first fail will set this
my $BUFFEROUT=0;

use Win32::SerialPort qw( :STAT 0.20 );

use strict;
use warnings;

# tests start using file created by test1.pl

use Win32;

my $file = "COM1";
if ($SerialJunk::Makefile_Test_Port) {
    $file = $SerialJunk::Makefile_Test_Port;
}
if (exists $ENV{Makefile_Test_Port}) {
    $file = $ENV{Makefile_Test_Port};
}

if (@ARGV) {
    $file = shift @ARGV;
}
my $cfgfile = $file."_test.cfg";

my $fault = 0;
my $ob;
my $pass;
my $fail;
my $in;
my $in2;
my @opts;
my $out;
my $blk;
my $err;
my $e;
my $tick;
my $tock;
my $s="testing is a wonderful thing - this is a 60 byte long string";
#      123456789012345678901234567890123456789012345678901234567890
my $line = $s.$s.$s;		# about 185 MS at 9600 baud
my @necessary_param = Win32::SerialPort->set_test_mode_active(1);

sub is_bad {
    local $Test::Builder::Level = $Test::Builder::Level + 1;
    return ok(!shift, shift);
}

# 2: Constructor

ok($ob = Win32::SerialPort->start ($cfgfile), "start $cfgfile");
die unless ($ob);    # next tests would die at runtime

#### 3 - 24: Check Port Capabilities Match Save

is($ob->xon_char, 0x11, 'xon_char');
is($ob->xoff_char, 0x13, 'xoff_char');
is($ob->eof_char, 0, 'eof_char');
is($ob->event_char, 0, 'event_char');
is($ob->error_char, 0, 'error_char');
is($ob->baudrate, 9600, 'baudrate');
is($ob->parity, "none", 'parity');
is($ob->databits, 8, 'databits');
is($ob->stopbits, 1, 'stopbits');
is($ob->handshake, "none", 'handshake');
is($ob->read_interval, 0xffffffff, 'read_interval');
is($ob->read_const_time, 0, 'read_const_time');
is($ob->read_char_time, 0, 'read_char_time');
is($ob->write_const_time, 200, 'write_const_time');
is($ob->write_char_time, 10, 'write_char_time');

($in, $out)= $ob->buffers;
is(4096, $in, 'buffer in');
is(4096, $out, 'buffer out');

is($ob->alias, 'TestPort', 'alias');

is($ob->binary, 1, 'binary');

is($ob->parity_enable, 0, 'parity_enable');
is($ob->xoff_limit, 200, 'xoff_limit');
is($ob->xon_limit, 100, 'xon_limit');


## 25 - 30: Status

ok(scalar $ob->purge_all, 'purge_all');
$ob->reset_error;
is(scalar (@opts = $ob->status), 4, 'status array');

# for an unconnected port, should be $in=0, $out=0, $blk=0, $err=0
($blk, $in, $out, $err)=@opts;

# A test to check $BUFFEROUT
$tick=$ob->get_tick_count;
is($ob->write($line), 180, 'write 180 characters');
$tock=$ob->get_tick_count;

my $delay=$tock - $tick;
if ($delay < 120) {
	$BUFFEROUT = 1;	# USB and virtual ports can't test output timing
}
if ($BUFFEROUT) {
	# USB and virtual ports can be different, but stil 4 elements
	ok(defined $blk, 'blocking byte');
	ok(defined $in, 'input count');
	ok(defined $out, 'output count');
	ok(defined $err, 'error byte');
	is_bad ($delay > 300, 'skip write timing');
} else {
	is($blk, 0, 'blocking bits');
	is($in, 0, 'input count');
	is($out, 0, 'output count');
	is($err, 0, 'error bits');
	is_bad (($delay < 120) or ($delay > 300), 'write timing');
}
print "<185> elapsed time=$delay\n";

# 31 - 33: "Instant" return for read_interval=0xffffffff

SKIP: {
    skip "Can't rely on timing and status details", 99 if $BUFFEROUT;

    $tick=$ob->get_tick_count;
    ($in, $in2) = $ob->read(10);
    $tock=$ob->get_tick_count;

    is($in, 0, 'character count');
    is_bad ($in2, 'no input');
    $out=$tock - $tick;
    ok ($out < 100, 'instant return from read');
    print "<0> elapsed time=$out\n";

    print "Beginning Timed Tests at 2-5 Seconds per Set\n";

    is($ob->read_const_time(2000), 2000, 'read_const_time');
    is($ob->read_interval(0), 0, 'read_interval');
    $tick=$ob->get_tick_count;
    ($in, $in2) = $ob->read(10);
    $tock=$ob->get_tick_count;
    $out=$tock - $tick;
    is($in, 0, 'two second constant timeout');
    is_bad($in2,'no data returned');
    is_bad (($out < 1800) or ($out > 2400), 'read timeout');
    print "<2000> elapsed time=$out\n";

    # 4 Second Timeout Constant+Character

    is($ob->read_char_time(100), 100, 'read_char_time');
    $tick=$ob->get_tick_count;
    ($in, $in2) = $ob->read(20);
    $tock=$ob->get_tick_count;
    $out=$tock - $tick;
    is($in, 0, 'four second constant+character timeout');
    is_bad($in2,'no data returned');
    is_bad (($out < 3800) or ($out > 4400), 'read timeout');
    print "<4000> elapsed time=$out\n";
#12
    # 3 Second Character Timeout

    is($ob->read_const_time(0), 0, 'read_const_time');
    $tick=$ob->get_tick_count;
    ($in, $in2) = $ob->read(30);
    $tock=$ob->get_tick_count;
    $out=$tock - $tick;
    is($in, 0, 'three second character timeout');
    is_bad($in2,'no data returned');
    is_bad (($out < 2800) or ($out > 3400), 'read timeout');
    print "<3000> elapsed time=$out\n";

    # 2 Second Constant Write Timeout

    is($ob->read_char_time(0), 0, 'read_char_time');
    is($ob->read_interval, 0, 'read_interval');
    is($ob->write_const_time(2000), 2000, 'write_const_time');
    is($ob->write_char_time(0), 0, 'write_char_time');
    is($ob->handshake("rts"), "rts", 'handshake rts should block');
#21
    $e="12345678901234567890";

    $tick=$ob->get_tick_count;
    is($ob->write($e), 0, 'write no characters');
    $tock=$ob->get_tick_count;
    $out=$tock - $tick;
    is_bad (($out < 1800) or ($out > 2400), 'write_timeout');
    print "<2000> elapsed time=$out\n";

    # 3.5 Second Timeout Constant+Character
    is($ob->write_char_time(75), 75, 'write_char_time');
    $tick=$ob->get_tick_count;
    is($ob->write($e), 0, 'write no characters');
    $tock=$ob->get_tick_count;
    $out=$tock - $tick;
    is_bad (($out < 3300) or ($out > 3900), 'write_timeout');
    print "<3500> elapsed time=$out\n";

    # 2.5 Second Read Constant Timeout
    is($ob->read_const_time(2500), 2500, 'read_const_time');
    is($ob->read_interval(0), 0, 'read_interval');
    ok(scalar $ob->purge_all, 'purge_all');
    $tick=$ob->get_tick_count;
    $in = $ob->read_bg(10);
    $tock=$ob->get_tick_count;
    $out=$tock - $tick;
    is($in, 0, '2.5 second constant read timeout');
    ok($out < 100, 'starts in background');
    print "<0> elapsed time=$out\n";
#31
    ($pass, $in, $in2) = $ob->read_done(0);
    $tock=$ob->get_tick_count;

    is($pass, 0, 'read_done(0)');
    is($in, 0, 'read_bg count');
    is($in2, "", 'read_bg data');
    $out=$tock - $tick;
    ok ($out < 100, 'not blocked');	


print "A Series of 1 Second Groups with Background I/O\n";

    sleep 1;
    ($pass, $in, $in2) = $ob->read_done(0);
    is($pass, 0, 'read_done(0)');
    is($in, 0, 'read_bg count');
    is($in2, "", 'read_bg data');
    is($ob->write_bg($e), 0, 'write_bg');
    ($pass, $out) = $ob->write_done(0);
    is($pass, 0, 'write_done(0)');
    is($out, 0, 'write_bg count');
#41
    sleep 1;
    ($pass, $in, $in2) = $ob->read_done(0);
    is($pass, 0, 'read_done(0)');
    ($pass, $out) = $ob->write_done(0);
    is($pass, 0, 'write_done(0)');

    ($blk, $in, $out, $err)=$ob->status;
    is($in, 0, 'read char count');
    is($out, 20, 'write char count');
    is($blk, 1, 'blocking bits');
    is($err, 0, 'error bits char count');

    sleep 1;
    ($pass, $in, $in2) = $ob->read_done(0);
    is($pass, 1, 'read_done(0)');
    is($in, 0, 'read_bg count');
    is($in2, "", 'read_bg data');
    $tock=$ob->get_tick_count;
    $out=$tock - $tick;
    is_bad (($out < 2800) or ($out > 3400), 'read_done timing');
    print "<3000> elapsed time=$out\n";
    ($pass, $out) = $ob->write_done(0);
    is($pass, 0, 'write_done(0)');
#52
    sleep 1;
    ($pass, $in, $in2) = $ob->read_done(0);		# double check ok?
    is($pass, 1, 'read_done(0)');
    is($in, 0, 'read_done count');
    is($in2, "", 'read_done data');
    ($pass, $out) = $ob->write_done(0);
    is($pass, 0, 'write_done(0)');

    sleep 1;
    ($pass, $out) = $ob->write_done(0);
    is($pass, 1, 'write_done(0)');
    is($out, 0, 'write_done count');
    $tock=$ob->get_tick_count;			# expect about 5 seconds
    $out=$tock - $tick;
    is_bad (($out < 4800) or ($out > 5400), 'write_done timing');
    print "<5000> elapsed time=$out\n";
#59
    $tick=$ob->get_tick_count;			# new timebase
    $in = $ob->read_bg(10);
    is($in, 0, 'read_bg count');
    ($pass, $in, $in2) = $ob->read_done(0);
    is($pass, 0, 'read_done(0)');
    is($in, 0, 'read_done count');
    is($in2, "", 'read_done data');

    sleep 1;
    ($pass, $in, $in2) = $ob->read_done(0);
    is($pass, 0, 'read_done(0)');
    ## print "testing fail message:\n";
    $in = $ob->read_bg(10);
    is_bad (defined $in, 'already reading');

    ($pass, $in, $in2) = $ob->read_done(1);
    is($pass, 1, 'read_done(1)');
    is($in, 0, 'read_done count');
    is($in2, "", 'read_done data');
    $tock=$ob->get_tick_count;			# expect 2.5 seconds
    $out=$tock - $tick;
    is_bad (($out < 2300) or ($out > 2800), 'read_done(1) timing');
    print "<2500> elapsed time=$out\n";
#69
    $tick=$ob->get_tick_count;			# new timebase
    $in = $ob->read_bg(10);
    is($in, 0, 'read_bg count');
    ($pass, $in, $in2) = $ob->read_done(0);
    is($pass, 0, 'read_done(0)');
    is($in, 0, 'read_done count');
    is($in2, "", 'read_done data');

    sleep 1;
    ($pass, $in, $in2) = $ob->read_done(0);
    is($pass, 0, 'read_done(0)');
    ok(scalar $ob->purge_rx, 'purge_rx');
    ($pass, $in, $in2) = $ob->read_done(1);
    ok(scalar $ob->purge_rx, 'purge_rx');
    if (Win32::IsWinNT()) {
        is($pass, 0, 'read_done(1) after purge');
    } else {
        is($pass, 1, 'read_done(1) after purge');
    }
    is($in, 0, 'read_done count');
    is($in2, "", 'read_done data');
    $tock=$ob->get_tick_count;			# expect 1 second
    $out=$tock - $tick;
    is_bad (($out < 900) or ($out > 1200), 'purge_rx timing');
    print "<1000> elapsed time=$out\n";
#80
    is($ob->write_bg($e), 0, 'write_bg');
    ($pass, $out) = $ob->write_done(0);
    is($pass, 0, 'write_done(0)');

    sleep 1;
    ($pass, $out) = $ob->write_done(0);
    is($pass, 0, 'write_done(0)');
    ok(scalar $ob->purge_tx, 'purge_tx');
    ($pass, $out) = $ob->write_done(1);
    ok(scalar $ob->purge_tx, 'purge_tx');
    if (Win32::IsWinNT()) {
        is($pass, 0, 'write_done(1)');
    } else {
        is($pass, 1, 'write_done(1)');
    }
    $tock=$ob->get_tick_count;			# expect 2 seconds
    $out=$tock - $tick;
    is_bad (($out < 1900) or ($out > 2200), 'write_done(1) timing');
    print "<2000> elapsed time=$out\n";
#87
    $tick=$ob->get_tick_count;			# new timebase
    $in = $ob->read_bg(10);
    is($in, 0, 'read_bg count');
    ($pass, $in, $in2) = $ob->read_done(0);
    is($pass, 0, 'read_done(0)');
    is($ob->write_bg($e), 0, 'write_bg');
    ($pass, $out) = $ob->write_done(0);
    is($pass, 0, 'write_done(0)');

    sleep 1;
    ($pass, $out) = $ob->write_done(0);
    is($pass, 0, 'write_done(0)');

    ($pass, $in, $in2) = $ob->read_done(1);
    is($pass, 1, 'read_done(1)');
    is($in, 0, 'read_done count');
    is($in2, "", 'read_done data');
    ($pass, $out) = $ob->write_done(0);
    is($pass, 0, 'write_done(0)');
    $tock=$ob->get_tick_count;			# expect 2.5 seconds
    $out=$tock - $tick;
    is_bad (($out < 2300) or ($out > 2800), 'write_done(1) timing');
    print "<2500> elapsed time=$out\n";

    ($pass, $out) = $ob->write_done(1);
    is($pass, 1, 'write_done(1)');
    $tock=$ob->get_tick_count;			# expect 3.5 seconds
    $out=$tock - $tick;
    is_bad (($out < 3300) or ($out > 3800), 'write_done(1) timing');
    print "<3500> elapsed time=$out\n";
#99
}

is($ob->user_msg, 1, 'user_msg');
is($ob->user_msg(0), 0, 'user_msg(0)');
is($ob->user_msg(1), 1, 'user_msg(1)');
is($ob->error_msg, 1, 'error_msg');
is($ob->error_msg(0), 0, 'error_msg(0)');
is($ob->error_msg(1), 1, 'error_msg(1)');

#### 137 - 143: Application Parameter Defaults

is($ob->devicetype, 'none', 'devicetype');
is($ob->hostname, 'localhost', 'hostname');
is($ob->hostaddr, 0, 'hostaddr');
is($ob->datatype, 'raw', 'datatype');
is($ob->cfg_param_1, 'none', 'cfg_param_1');
is($ob->cfg_param_2, 'none', 'cfg_param_2');
is($ob->cfg_param_3, 'none', 'cfg_param_3');

undef $ob;

# 144 - 145: Reopen tests (unconfirmed) $ob->close via undef

sleep 1;
ok($ob = Win32::SerialPort->start ($cfgfile), "start $cfgfile");
die unless ($ob);    # next tests would die at runtime
ok($ob->close, 'close');
undef $ob;
