#!perl use strict; no warnings; use Test::More; use AnyEvent::Impl::Perl; use AnyEvent; use Net::IRC3::Connection; use Net::IRC3::Client::Connection; use Net::IRC3::Util qw/split_prefix prefix_nick/; use Socket; use IO::Handle; $Net::IRC3::Client::Connection::DEBUG = 1; our $WATCHDOG; sub prepare_cl_srv { my $srv = Net::IRC3::Connection->new; my $cl = Net::IRC3::Client::Connection->new; socketpair (my $s1, my $s2, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die "socketpair: $!"; $s1 or die "couldn't make socketpair: $!"; $srv->use_socket ('localhost', '65533', $s1); $cl->use_socket ('localhost', '6667', $s2); ($srv, $cl) } sub start_watchdog { my ($c, $checks) = @_; $WATCHDOG = AnyEvent->timer (after => 10, cb => sub { $c->broadcast; $checks->{watchdog} = 1; undef $WATCHDOG }); } sub uniq { my (@lst) = @_; my %h; $h{$_}++ for @lst; keys %h } plan tests => 20; { my $c = AnyEvent->condvar; my ($srv, $cl) = prepare_cl_srv; my ($nick, $user, $real) = (test => testbot => "I'm a Net::IRC3 test"); my @test_nicks = qw/@elmex @testor foobar/; # for channel $test_chan1 my $test_chan1 = '#test'; my $test_chan2 = '#tESt2'; my $test_chan_msg_srcnick = 'testbot'; my $ss = {}; # server state my $checks = $cl->heap; # for storing testing check results $checks->{stage} = 0; $srv->reg_cb ( irc_nick => sub { my ($srv, $p) = @_; $ss->{nick} = $p->{params}->[0]; if ($ss->{retry} < 2) { if ($ss->{retry} > 0) { $cl->set_nick_change_cb (undef); } $ss->{retry}++; $srv->send_msg ( 'localhost', '433' => "Nickname is already in use", $ss->{nick} ); } elsif ($checks->{stage} == 0) { $checks->{srv_nick} = $ss->{nick}; $ss->{cl_prefix} = sprintf "%s!%s@%s", $ss->{nick}, $ss->{user}, 'localhost'; if ($ss->{user}) { $srv->send_msg ( 'localhost', '001' => 'Welcome to the Net::IRC3 test', $ss->{nick} ); } } 1 }, irc_user => sub { my ($srv, $p) = @_; $ss->{user} = $p->{params}->[0]; $ss->{real} = $p->{params}->[3]; $checks->{srv_user} = $ss->{user}; $checks->{srv_real} = $ss->{real}; $checks->{srv_nick} = $ss->{nick}; 1 }, irc_pong => sub { my ($srv) = @_; $checks->{recv_pong} = 1; 1 }, irc_privmsg => sub { my ($srv, $m) = @_; if ($m->{trailing} =~ /TEST HELLO/) { $srv->send_msg ( $test_chan_msg_srcnick . '!' . $test_chan_msg_srcnick . '@localhost', PRIVMSG => "TEST REPLY", $m->{params}->[0] ); $checks->{recv_message} = $m->{trailing}; } }, irc_join => sub { my ($srv, $p) = @_; my $chan = $p->{params}->[0]; $srv->send_msg ($ss->{cl_prefix}, JOIN => $chan); if ($chan eq $test_chan1) { $srv->send_msg ( 'localhost', '353', (join ' ', @test_nicks, $ss->{nick}), $ss->{nick}, '=', $chan ); $srv->send_raw ("PING :fooobar"); $srv->send_msg ( 'localhost', '366', 'End of /NAMES list', $ss->{nick}, $chan ); $ss->{chan1} = [ @test_nicks, $ss->{nick} ]; } elsif ($chan eq $test_chan2) { $srv->send_msg ( 'localhost', '353', (join ' ', '@' . $ss->{nick}), $ss->{nick}, '=', $chan ); $srv->send_msg ( 'localhost', '366', 'End of /NAMES list', $ss->{nick}, $chan ); $ss->{chan2} = [ '@' . $ss->{nick} ]; } 1 }, disconnect => sub { my ($srv, $r) = @_; $checks->{srv_disconnect} = $r; $c->broadcast; 1 } ); $cl->reg_cb ( registered => sub { my ($cl) = @_; $checks->{connected} = $cl->is_connected; $checks->{registered} = $cl->nick; $checks->{stage} = 1; 1 }, irc_ping => sub { my ($cl) = @_; $checks->{recv_ping} = 1; 1 }, irc_join => sub { my ($cl, $p) = @_; if ($checks->{stage} == 1) { my ($n, $u, $h) = split_prefix ($p); $checks->{stage_1_prefix} = [$n, $u, $h]; } 1 }, publicmsg => sub { my ($cl, $chan, $msg) = @_; $checks->{public_reply} = [prefix_nick ($msg), $msg->{trailing}]; 1 }, channel_add => sub { my ($cl, $chan, @nicks) = @_; if ($chan eq $test_chan1 && $checks->{stage} == 1) { push @{$checks->{stage_1_nicks}}, @nicks; } elsif ($chan eq $test_chan2 && $checks->{stage} == 2) { push @{$checks->{stage_2_nicks}}, @nicks; } if ($checks->{stage} == 1 && scalar (@{$checks->{stage_1_nicks} || []}) == scalar (@test_nicks) + 1) { $checks->{stage} = 2; $cl->send_srv (JOIN => $test_chan2); } elsif ($checks->{stage} == 2 && scalar (@{$checks->{stage_2_nicks} || []}) == 1) { $checks->{stage} = 3; $checks->{stage_3_channel_list} = [ keys %{$cl->channel_list} ]; $cl->disconnect ("done"); } 1 }, disconnect => sub { my ($cl, $r) = @_; $checks->{cl_disconnect} = $r; 1 } ); $cl->set_nick_change_cb (sub { my ($n) = @_; "$n|" }); $cl->register ($nick, $user, $real); $cl->send_chan ($test_chan1, PRIVMSG => "TEST HELLO", $test_chan1); $cl->send_srv (JOIN => $test_chan1); start_watchdog ($c, $checks); $c->wait; $nick .= "|_"; # TEST CHECKS: $checks = $cl->heap; ok ($checks->{connected}, "client connected"); is ($checks->{registered}, $nick, "client registered with correct nick"); is ($checks->{srv_nick}, $nick, "server got nick after 2 collisions"); is ($checks->{srv_user}, $user, "server got user"); is ($checks->{srv_real}, $real, "server got real"); ok ($checks->{recv_ping}, "client got ping"); ok ($checks->{recv_pong}, "server got pong"); is ($checks->{recv_message}, 'TEST HELLO', "channel message received by server"); is ($checks->{public_reply}->[0], $test_chan_msg_srcnick, "channel message reply received by client from right nick"); is ($checks->{public_reply}->[1], 'TEST REPLY', "channel message reply received by client"); is ($checks->{stage_1_prefix}->[0], $nick, "stage 1: nick prefix ok"); is ($checks->{stage_1_prefix}->[1], $user, "stage 1: user prefix ok"); is ($checks->{stage_1_prefix}->[2], 'localhost', "stage 1: host prefix ok"); is ( (join ' ', sort @{$checks->{stage_1_nicks} || []}), (join ' ', sort map { s/^@//; $_ } @test_nicks, $nick), "stage 1: successfully joined channel with test users" ); is ( (join ' ', sort @{$checks->{stage_2_nicks} || []}), (join ' ', sort $nick), "stage 2: successfully joined second channel without" ); is ( (join ' ', sort @{$checks->{stage_3_channel_list} || []}), (join ' ', sort map lc $_, $test_chan1, $test_chan2), "stage 3: channel list ok" ); is ($checks->{stage}, 3, "last test stage reached"); is ($checks->{cl_disconnect}, 'done', "client disconnected"); like ($checks->{srv_disconnect}, qr/EOF/, "server disconnected"); ok (!$checks->{watchdog}, "watchdog didn't trigger"); }