File Coverage

blib/lib/Test/Mojo/IRC.pm
Criterion Covered Total %
statement 72 73 98.6
branch 22 26 84.6
condition 2 4 50.0
subroutine 12 13 92.3
pod 3 3 100.0
total 111 119 93.2


line stmt bran cond sub pod time code
1             package Test::Mojo::IRC;
2 13     13   10461 use Mojo::Base -base;
  13         1570383  
  13         107  
3              
4 13     13   8653 use Mojo::File;
  13         268846  
  13         602  
5 13     13   5534 use Mojo::IOLoop::Server;
  13         1251437  
  13         102  
6 13     13   6475 use Mojo::IRC;
  13         163  
  13         136  
7              
8             $ENV{TEST_MOJO_IRC_SERVER_TIMEOUT} ||= $ENV{TEST_MOJO_IRC_SERVER} ? 10 : 4;
9              
10             has server => '';
11              
12             has welcome_message => <<'HERE';
13             :hybrid8.local NOTICE AUTH :*** Looking up your hostname...
14             :hybrid8.local NOTICE AUTH :*** Checking Ident
15             :hybrid8.local NOTICE AUTH :*** Found your hostname
16             :hybrid8.local NOTICE AUTH :*** No Ident response
17             HERE
18              
19             sub on {
20 2     2 1 17 my ($self, $irc, $event, $cb) = @_;
21 2         3 push @{$self->{subscriptions}}, $irc, $event, $irc->on($event => $cb);
  2         7  
22 2         12 $self;
23             }
24              
25             sub run {
26 22     22 1 4637 my ($self, $reply_on, $cb) = @_;
27 22     0   122 my $guard = Mojo::IOLoop->timer($ENV{TEST_MOJO_IRC_SERVER_TIMEOUT}, sub { Mojo::IOLoop->stop });
  0         0  
28 22         1182 my @subscriptions;
29              
30 22         57 local $self->{from_client} = '';
31 22         50 local $self->{reply_on} = $reply_on;
32 22         58 local $self->{subscriptions} = \@subscriptions;
33              
34 22         71 $self->$cb;
35 22         23911 Mojo::IOLoop->remove($guard);
36              
37 22         731 while (@subscriptions) {
38 2         21 my ($irc, $event, $cb) = splice @subscriptions, 0, 3, ();
39 2         9 $irc->unsubscribe($event => $cb);
40             }
41              
42 22         102 $self;
43             }
44              
45             sub start_server {
46 14     14 1 1359 my $self = shift;
47              
48 14 100       71 return $self->new->tap('start_server') unless ref $self;
49 13 50       54 return $self->server if $self->server;
50 13 50       179 return $ENV{TEST_MOJO_IRC_SERVER} if $ENV{TEST_MOJO_IRC_SERVER};
51              
52 13         187 my $port = Mojo::IOLoop::Server->generate_port;
53 13         13660 my $write;
54              
55             $write = sub {
56 577 100   577   3816 return unless length $self->{server_buf};
57 539         3195 return shift->write(substr($self->{server_buf}, 0, int(10 + rand 20), ''), sub { shift->$write });
  536         55707  
58 13         104 };
59              
60             $self->{server_id} = Mojo::IOLoop->server(
61             {address => '127.0.0.1', port => $port},
62             sub {
63 12     12   9162 my ($ioloop, $stream) = @_;
64              
65             $stream->on(
66             read => sub {
67 29         3727 my ($stream, $buf) = @_;
68 29         84 $self->{from_client} .= $buf;
69              
70 29         178 while ($buf =~ /[\015\012]/g) {
71 51 100       85 last unless @{$self->{reply_on} || []};
  51 100       189  
72 26 100       242 last unless $self->{from_client} =~ $self->{reply_on}[0];
73 22         81 $self->_concat_server_buf($self->{reply_on}[1]);
74 22         425 splice @{$self->{reply_on}}, 0, 2, ();
  22         115  
75             }
76              
77 29         79 $stream->$write;
78             }
79 12         138 );
80              
81 12         108 $self->_concat_server_buf($self->welcome_message);
82 12         43 $stream->$write;
83             }
84 13         189 );
85              
86 13         10795 $self->{server_buf} = '';
87 13         64 $self->server("127.0.0.1:$port")->server;
88             }
89              
90             sub _concat_server_buf {
91 34     34   145 my ($self, $buf) = @_;
92              
93 34 100       142 if (ref $buf eq 'ARRAY') {
    100          
94 20 100       160 $buf = Mojo::Loader::data_section(@$buf == 1 ? ('main', @$buf) : @$buf);
95             }
96             elsif (ref $buf) {
97 2         23 $buf = Mojo::File::path(split '/', $$buf)->slurp;
98             }
99              
100 34         2553 $buf =~ s/[\015\012]/\015\012/g;
101 34         246 $self->{server_buf} .= $buf;
102             }
103              
104             sub import {
105 13     13   134 my $class = shift;
106 13   50     51 my $arg = shift // '';
107 13         27 my $caller = caller;
108              
109 13 50       65 return unless $arg =~ /^(?:-basic|-ua)$/;
110 13         191 $_->import for qw(strict warnings utf8);
111 13         799 feature->import(':5.10');
112 13 100 50     772 eval "require Mojo::IRC::UA;1" or die $@ if $arg eq '-ua';
113 13 50   13   7537 eval "package $caller; use Test::More; 1" or die $@;
  13         746755  
  13         109  
  13         890  
114             }
115              
116             1;
117              
118             =encoding utf8
119              
120             =head1 NAME
121              
122             Test::Mojo::IRC - Module for testing Mojo::IRC
123              
124             =head1 SYNOPSIS
125              
126             use Test::Mojo::IRC -basic;
127              
128             my $t = Test::Mojo::IRC->start_server;
129             my $irc = Mojo::IRC->new(server => $t->server);
130              
131             # simulate server/client communication
132             $t->run(
133             [
134             # Send "welcome.irc" from the DATA section when client sends "NICK"
135             qr{\bNICK\b} => [qw(main motd.irc)],
136             ],
137             sub {
138             my $err;
139             my $motd = 0;
140             $t->on($irc, irc_rpl_motd => sub { $motd++ });
141             $t->on($irc, irc_rpl_endofmotd => sub { Mojo::IOLoop->stop; }); # need to manually stop the IOLoop
142             $irc->connect(sub { $err = $_[1]; });
143             Mojo::IOLoop->start; # need to manually start the IOLoop
144             is $err, "", "connected";
145             is $motd, 3, "message of the day";
146             },
147             );
148              
149             done_testing;
150              
151             __DATA__
152             @@ motd.irc
153             :spectral.shadowcat.co.uk 375 test123 :- spectral.shadowcat.co.uk Message of the Day -
154             :spectral.shadowcat.co.uk 372 test123 :- We scan all connecting clients for open proxies and other
155             :spectral.shadowcat.co.uk 372 test123 :- exploitable nasties. If you don't wish to be scanned,
156             :spectral.shadowcat.co.uk 372 test123 :- don't connect again, and sorry for scanning you this time.
157             :spectral.shadowcat.co.uk 376 test123 :End of /MOTD command.
158              
159             =head1 DESCRIPTION
160              
161             L is a module for making it easier to test L
162             applications.
163              
164             =head1 ENVIRONMENT VARIABLES
165              
166             =head2 TEST_MOJO_IRC_SERVER
167              
168             C can be set to point to a live server. If the variable
169             is set, L will simply return L instead
170             of setting up a server.
171              
172             =head1 ATTRIBUTES
173              
174             =head2 server
175              
176             $str = $self->server;
177              
178             Returns the server address, "host:port", that L set up.
179              
180             =head2 welcome_message
181              
182             $str = $self->welcome_message;
183             $self = $self->welcome_message($str);
184              
185             Holds a message which will be sent to the client on connect.
186              
187             =head1 METHODS
188              
189             =head2 on
190              
191             $self->on($irc, $event, $cb);
192              
193             Will attach events to the L<$irc|Mojo::IRC> object which is removed
194             after L has completed. See L for example code.
195              
196             =head2 run
197              
198             $self->run($reply_on, sub { my $self = shift });
199              
200             Used to simulate communication between IRC server and client. The way this
201             works is that the C<$cb> will initiate L or
202             L to the server and the server will then respond
203             with the data from either L or C<$reply_on> on these
204             events.
205              
206             C<$reply_on> is an array-ref of regex/buffer pairs. Each time a message
207             from the client match the first regex in the C<$reply_on> array the
208             buffer will be sent back to the client and the regex/buffer will be removed.
209             This means that the order of the pairs are important. The buffer can be...
210              
211             =over 4
212              
213             =item * Scalar
214              
215             Plain text.
216              
217             =item * Scalar ref
218              
219             Path to file on disk.
220              
221             =item * Array ref
222              
223             The module name and file passed on to L. The default
224             package is "main", meaning the two examples below is the same:
225              
226             $self->run([qr{JOIN}, ["join-reply.irc"]], sub { my $self = shift });
227             $self->run([qr{JOIN}, ["main", "join-reply.irc"]], sub { my $self = shift });
228              
229             =back
230              
231             Note that starting and stopping the L is up to you, but
232             there is also a master timeout which will stop the IOLoop if running for too
233             long.
234              
235             See L for example.
236              
237             =head2 start_server
238              
239             $server = $self->start_server;
240             $self = Test::Mojo::IRC->start_server;
241              
242             Will start a test server and return L. It can also be called as
243             a class method which will return a new object.
244              
245             =head2 import
246              
247             use Test::Mojo::IRC -basic;
248              
249             Loading this module with "-basic" will import L, L, L,
250             L and 5.10 features into the caller namespace.
251              
252             =head1 COPYRIGHT AND LICENSE
253              
254             Copyright (C) 2014, Jan Henning Thorsen
255              
256             This program is free software, you can redistribute it and/or modify it under
257             the terms of the Artistic License version 2.0.
258              
259             =head1 AUTHOR
260              
261             Jan Henning Thorsen - C
262              
263             =cut