The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# Before `make install' is performed this script should be runnable with
# `make test'. After `make install' it should work as `perl test.pl'

######################### We start with some black magic to print on failure.
# Change 1..1 below to 1..last_test_to_print .
# (It may become useful if the test is moved to ./t subdirectory.)

BEGIN { $| = 1; print "1..14\n"; }
END {print "not ok 1\n" unless $loaded;}

#use diagnostics;
use Net::NBsocket qw(
	open_udpNB
	AF_INET6
	havesock6
	isupport6
	in6addr_loopback
	in6addr_any
	pack_sockaddr_in6
);
use POSIX qw(EWOULDBLOCK);

$loaded = 1;
print "ok 1\n";
######################### End of black magic.

# Insert your test code below (better if it prints "ok 13"
# (correspondingly "not ok 13") depending on the success of chunk 13
# of the test code):

$test = 2;

my($L,$R);

sub ok {
  my $rv = shift;
  unless ($rv) {
    print "ok $test\n";
    ++$test;
    return;
  }
  close $R if $R;
  close $L if $L;
  while ($test < 15) {
    print "ok $test	# skipped, $rv\n";
    $rv = 'no IPv6 support';
    $test++;
  }
  exit;
}

sub next_sec {
  my ($then) = @_;
  $then = time unless $then;
  my $now;
# wait for epoch
  do { select(undef,undef,undef,0.1); $now = time }
        while ( $then >= $now );
  $now;
}

ok "Socket6 not installed"
	unless havesock6();

ok "host has no IPv6 support"
	unless isupport6();

## test 2	open a listening socket
$L = open_udpNB(AF_INET6());;
ok $L ? '' : 'could not open local unbound socket';

## test 3	bind a listner for testing
my $port;
foreach(10000..10100) {		# find a port to bind to
  if (bind($L,pack_sockaddr_in6($_,in6addr_any()))) {
    $port = $_;
    last;
  } else {
    $port = 0;
  }
}

ok $port ? '' : 'could not bind a port for testing';

## test 4	open a sending socket
$R = open_udpNB(AF_INET6());
ok $R ? '' : 'could not open unbound send socket';

## test 5	check non-blocking status
my $inbuf;
my $err = eval {
	local $SIG{ALRM} = sub {die "blocked, timeout"};
	alarm 2;
	if (recv $L,$inbuf,512,0) {
	  die "received something";
	} else {
	  alarm 0;
	  $!;
	}
};
ok $@ ? $@ : '';

## test 6	show any unexpected errors
if ($err && $err != EWOULDBLOCK()) {
  ok "unexpected error $err"
} else {
  &ok;
}

## test 7	send message, should not block
my $R_sin = pack_sockaddr_in6($port,in6addr_loopback());
my $message = 'expected message';
my $now = &next_sec;	# sync actions
$err = eval {
	local $SIG{ALRM} = sub {die "blocked, timeout"};
	my $rv;
	alarm 2;
	if (($rv = send($R,$message,0,$R_sin)) && $rv == length($message)) {
	  alarm 0;
	  undef;
	} else {
	  die "sent $rv bytes";
	}
};
ok $@ ? $@ : '';

## test 8	read message back on listner
$now = &next_sec($now);
my $L_sin;
$err = eval {
	local $SIG{ALRM} = sub {die "blocked, timeout"};
	alarm 2;
	$L_sin = recv $L,$inbuf,512,0;
	alarm 0;
	$!;
};
ok $@ ? $@ : '';

## test 9	report unexpected errors
ok $L_sin ? '' : "unexpected error $err";

#### ok so IPv6 transport works or we've already failed

## test 10	check message value
print "got: $inbuf, exp: $message\nnot "
	unless $inbuf && $inbuf eq $message;
&ok;

## test 11	send reply
$message = 'reply message';
$now = &next_sec;	# sync actions
$err = eval {
	local $SIG{ALRM} = sub {die "blocked, timeout"};
	my $rv;
	alarm 2;
	if (($rv = send($L,$message,0,$L_sin)) && $rv == length($message)) {
	  alarm 0;
	  undef;
	} else {
	  die "sent $rv bytes";
	}
};
print $@, "\nnot "
	if $@;
&ok;

## test 12	read message back on listner
$now = &next_sec($now);
$err = eval {
	local $SIG{ALRM} = sub {die "blocked, timeout"};
	alarm 2;
	$R_sin = recv $R,$inbuf,512,0;
	alarm 0;
	$!;
};
print $@, "\nnot "
	if $@;
&ok;

## test 13	report unexpected errors
print "unexpected error $err\nnot "
	unless $R_sin;
&ok;

## test 14	check message value
print "got: $inbuf, exp: $message\nnot "
	unless $inbuf && $inbuf eq $message;
&ok;

close $L;
close $R;