The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
#!/usr/bin/perl

use strict;
use warnings;

use Test::More;

use Time::HiRes qw(); # Empty import, just there to let IO::Async and Net::Async::IRC use it

use IO::Async::Test;
use IO::Async::OS;
use IO::Async::Loop;
use IO::Async::Stream;

use Net::Async::IRC;

my $CRLF = "\x0d\x0a"; # because \r\n isn't portable

my $loop = IO::Async::Loop->new();
testing_loop( $loop );

my ( $S1, $S2 ) = IO::Async::OS->socketpair() or die "Cannot create socket pair - $!";

my $lag;
my $pingout;

my $irc = Net::Async::IRC->new(
   handle => $S1,
   on_message => sub { "IGNORE" },

   pingtime => 2,
   pongtime => 1,

   on_pong_reply   => sub { $lag = $_[1] },
   on_ping_timeout => sub { $pingout = 1 },
);

$loop->add( $irc );

# This is all tricky timing-related code. Pay attention

# First [the server] will send three messages, separated by 1sec, and assert
# that the client didn't send a PING

my $serverstream = "";

my $msgcount = 0;

sub tick {
   $msgcount++;
   $S2->syswrite( "HELLO client$CRLF" );

   $loop->enqueue_timer(
      delay => 1,
      code => \&tick
   ) if $msgcount < 3;
}

tick();

wait_for_stream { $msgcount == 3 } $S2 => $serverstream;

is( $serverstream, "", 'client quiet after server noise' );

# Now [the server] will be quiet and assert that the client sends a PING

wait_for_stream { $serverstream =~ m/$CRLF/ } $S2 => $serverstream;

like( $serverstream, qr/^PING .*$CRLF$/, 'client sent PING after server idle' );

# Now lets be a good server and reply to the PING
my ( $pingarg ) = $serverstream =~ m/^PING (.*)$CRLF$/;
$S2->syswrite( ":irc.example.com PONG $pingarg$CRLF" );

undef $lag;
wait_for { defined $lag };

ok( $lag >= 0 && $lag <= 1, 'client acknowledges PONG reply' );

# Now [the server] won't reply to a PING at all, and hope for an event to note
# that it failed

wait_for { defined $pingout };
ok( $pingout, 'client reports PING timeout' );

done_testing;