File Coverage

blib/lib/AnyEvent/XMPP/TestClient.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package AnyEvent::XMPP::TestClient;
2 18     18   378334 use strict;
  18         43  
  18         747  
3 18     18   92 no warnings;
  18         64  
  18         536  
4 18     18   38901 use AnyEvent;
  18         131642  
  18         737  
5 18     18   17578 use AnyEvent::XMPP::Client;
  0            
  0            
6             use AnyEvent::XMPP::Util qw/stringprep_jid prep_bare_jid dump_twig_xml/;
7             use AnyEvent::XMPP::Namespaces qw/xmpp_ns/;
8             use Test::More;
9              
10             =head1 NAME
11              
12             AnyEvent::XMPP::TestClient - XMPP Test Client for tests
13              
14             =head1 SYNOPSIS
15              
16             =head1 DESCRIPTION
17              
18             This module is a helper module to ease the task of testing.
19             If you want to run the developer test suite you have to set the environment
20             variable C to something like this:
21              
22             NET_XMPP2_TEST="test_me@your_xmpp_server.tld:secret_password"
23              
24             Most tests will try to connect two accounts, so please take a server
25             that allows two connections from the same IP.
26              
27             If you also want to run the MUC tests (see L)
28             you also need to setup the environment variable C
29             to contain the domain of a MUC service:
30              
31             NET_XMPP2_TEST_MUC="conference.your_xmpp_server.tld"
32              
33             If you see some tests fail and want to know more about the protocol flow
34             you can enable the protocol debugging output by setting C
35             to '1':
36              
37             NET_XMPP2_TEST_DEBUG=1
38              
39             (NOTE: You will only see the output of this by running a single test)
40              
41             If one of the tests takes longer than the preconfigured 20 seconds default
42             timeout in your setup you can set C:
43              
44             NET_XMPP2_TEST_TIMEOUT=60 # for a 1 minute timeout
45              
46             =head1 CLEANING UP
47              
48             If the tests went wrong somewhere or you interrupted the tests you might
49             want to delete the accounts from the server manually, then run:
50              
51             perl t/z_*_unregister.t
52              
53             =head1 MANUAL TESTING
54              
55             If you just want to run a single test yourself, just execute the register
56             test before doing so:
57              
58             perl t/z_00_register.t
59              
60             And then you could eg. run:
61              
62             perl t/z_03_iq_auth.t
63              
64             =head1 METHODS
65              
66             =head2 new (%args)
67              
68             Following arguments can be passed in C<%args>:
69              
70             =over 4
71              
72             =back
73              
74             =cut
75              
76             sub new_or_exit {
77             my $this = shift;
78             my $class = ref($this) || $this;
79             my $self = {
80             timeout => 20,
81             finish_count => 1,
82             @_
83             };
84              
85             if ($ENV{NET_XMPP2_TEST_DEBUG}) {
86             $self->{debug} = 1;
87             }
88              
89             if ($ENV{NET_XMPP2_TEST_TIMEOUT}) {
90             $self->{timeout} = $ENV{NET_XMPP2_TEST_TIMEOUT};
91             }
92              
93             $self->{tests};
94              
95             if ($self->{muc_test} && not $ENV{NET_XMPP2_TEST_MUC}) {
96             plan skip_all => "environment var NET_XMPP2_TEST_MUC not set! Set it to a conference!";
97             exit;
98             }
99              
100             if ($ENV{NET_XMPP2_TEST}) {
101             plan tests => $self->{tests} + 1
102             } else {
103             plan skip_all => "environment var NET_XMPP2_TEST not set! (see also AnyEvent::XMPP::TestClient)!";
104             exit;
105             }
106              
107             bless $self, $class;
108             $self->init;
109             $self
110             }
111              
112             sub init {
113             my ($self) = @_;
114             $self->{condvar} = AnyEvent->condvar;
115             $self->{timeout} =
116             AnyEvent->timer (
117             after => $self->{timeout}, cb => sub {
118             $self->{error} .= "Error: Test Timeout\n";
119             $self->{condvar}->broadcast;
120             }
121             );
122              
123             my $cl = $self->{client} = AnyEvent::XMPP::Client->new (debug => $self->{debug} || 0);
124             my ($jid, $password) = split /:/, $ENV{NET_XMPP2_TEST}, 2;
125              
126             $self->{jid} = $jid;
127             $self->{jid2} = "2nd_" . $jid;
128             $self->{password} = $password;
129             $cl->add_account ($jid, $password, undef, undef, $self->{connection_args});
130              
131             if ($self->{two_accounts}) {
132             my $cnt = 0;
133             $cl->reg_cb (session_ready => sub {
134             my ($cl, $acc) = @_;
135              
136             if (++$cnt > 1) {
137             $self->{acc} = $cl->get_account ($self->{jid});
138             $self->{acc2} = $cl->get_account ($self->{jid2});
139             $cl->event ('two_accounts_ready', $acc);
140             $self->state_done ('two_accounts_ready');
141             }
142             });
143              
144             $cl->add_account ("2nd_".$jid, $password, undef, undef, $self->{connection_args});
145              
146             } else {
147             $cl->reg_cb (before_session_ready => sub {
148             my ($cl, $acc) = @_;
149             $self->{acc} = $acc;
150             $self->state_done ('one_account_ready');
151             });
152             }
153              
154             if ($self->{muc_test} && $ENV{NET_XMPP2_TEST_MUC}) {
155             $self->{muc_room} = "test_nxmpp2@" . $ENV{NET_XMPP2_TEST_MUC};
156              
157             my $disco = $self->{disco} = $self->instance_ext ('AnyEvent::XMPP::Ext::Disco');
158             my $muc = $self->{muc} = $self->instance_ext ('AnyEvent::XMPP::Ext::MUC', disco => $disco);
159              
160             $cl->reg_cb (
161             two_accounts_ready => sub {
162             my ($cl, $acc) = @_;
163             my $cnt = 0;
164             my ($room1, $room2);
165              
166             $muc->join_room ($self->{acc}->connection, $self->{muc_room}, "test1");
167             my $rid;
168             $rid = $muc->reg_cb (
169             join_error => sub {
170             my ($muc, $room, $error) = @_;
171             $self->{error} .= "Error: Couldn't join $self->{muc_room}: ".$error->string."\n";
172             $self->{condvar}->broadcast;
173             },
174             enter => sub {
175             my ($muc, $room, $user) = @_;
176              
177             if ($room->get_me->nick eq 'test1') {
178             $self->{user} = $user;
179             $self->{room} = $room;
180              
181             $muc->join_room ($self->{acc2}->connection, $self->{muc_room}, "test2");
182             } else {
183             $self->{user2} = $user;
184             $self->{room2} = $room;
185              
186             $muc->unreg_cb ($rid);
187             $cl->event ('two_rooms_joined', $acc);
188             $self->state_done ('two_rooms_joined');
189             }
190             }
191             );
192             }
193             );
194             }
195              
196             $cl->reg_cb (error => sub {
197             my ($cl, $acc, $error) = @_;
198              
199             $self->{error} .= "Error: " . $error->string . "\n";
200             $self->finish unless $self->{continue_on_error};
201             });
202              
203             $cl->start;
204             }
205              
206             sub checkpoint {
207             my ($self, $name, $cnt, $cb) = @_;
208             $self->{checkpoints}->{$name} = [$cnt, $cb];
209             }
210              
211             sub reached_checkpoint {
212             my ($self, $name) = @_;
213             my $chp = $self->{checkpoints}->{$name}
214             or die "no such checkpoint defined: $name";
215              
216             $chp->[0]--;
217             if ($chp->[0] <= 0) {
218             $chp->[1]->();
219             delete $self->{checkpoints}->{$name};
220             }
221             }
222              
223             sub main_account { ($_[0]->{jid}, $_[0]->{password}) }
224              
225             sub client { $_[0]->{client} }
226              
227             sub tests { $_[0]->{tests} }
228              
229             sub instance_ext {
230             my ($self, $ext, @args) = @_;
231              
232             eval "require $ext; 1";
233             if ($@) { die "Couldn't load '$ext': $@" }
234             my $eo = $ext->new (@args);
235             $self->{client}->add_extension ($eo);
236             $eo
237             }
238              
239             sub finish {
240             my ($self) = @_;
241              
242             $self->{_cur_finish_cnt}++;
243             if ($self->{finish_count} <= $self->{_cur_finish_cnt}) {
244             $self->{condvar}->broadcast;
245             }
246             }
247              
248             sub wait {
249             my ($self) = @_;
250             $self->{condvar}->wait;
251              
252             if ($self->error) {
253             fail ("error free");
254             diag ($self->error);
255             } else {
256             pass ("error free");
257             }
258             }
259              
260             sub error { $_[0]->{error} }
261              
262             my %STATE;
263              
264             sub state {
265             my $self = shift;
266             my $prec = [];
267             if (ref $_[0] eq 'ARRAY') {
268             $prec = shift;
269             }
270             my ($state, $args, $cond, $cb) = @_;
271             $STATE{$state} = { name => $state, args => $args, cond => $cond, cb => $cb, done => 0, prec => $prec };
272              
273             $self->state_check ();
274             }
275              
276             sub state_done {
277             my ($self, $state) = @_;
278             $STATE{$state} ||= {
279             name => $state, args => undef, cond => undef, cb => undef, done => 0
280             };
281             $STATE{$state}->{done} = 1;
282             if ($ENV{ANYEVENT_XMPP_MAINTAINER_TEST_DEBUG}) {
283             warn "STATE '$state' DONE\n";
284             }
285              
286             $self->state_check ();
287             }
288              
289             sub state_check {
290             my ($self, $state, $cb) = @_;
291             if (defined $state && $STATE{$state} && !$STATE{$state}->{done}) {
292             $cb->($STATE{$state}->{args});
293             }
294              
295             RESTART: {
296             for my $s (grep { !$_->{done} } values %STATE) {
297             if (@{$s->{prec} || []}
298             && grep { !$STATE{$_} || !$STATE{$_}->{done} } @{$s->{prec} || []}) {
299             next;
300             }
301              
302             if (!defined ($s->{cond}) || $s->{cond}->($s->{args})) {
303             if ($ENV{ANYEVENT_XMPP_MAINTAINER_TEST_DEBUG}) {
304             print "STATE '$s->{name}' OK (".join (',', @{$s->{prec} || []}).")\n";
305             }
306             $s->{cb}->($s->{args}) if defined $s->{cb};
307             $s->{done} = 1;
308             goto RESTART;
309             }
310             }
311             }
312              
313             if ($ENV{ANYEVENT_XMPP_MAINTAINER_TEST_DEBUG}) {
314             warn "STATE STATUS:\n";
315             for my $s (keys %STATE) {
316             warn "\t$s => $STATE{$s}->{done}\t"
317             . join (',', map {
318             "$_:$STATE{$s}->{args}->{$_}" } keys %{$STATE{$s}->{args}}
319             )."\n";
320             }
321             }
322             }
323              
324             =head1 AUTHOR
325              
326             Robin Redeker, C<< >>, JID: C<< >>
327              
328             =head1 COPYRIGHT & LICENSE
329              
330             Copyright 2007, 2008 Robin Redeker, all rights reserved.
331              
332             This program is free software; you can redistribute it and/or modify it
333             under the same terms as Perl itself.
334              
335             =cut
336              
337             1; # End of AnyEvent::XMPP::TestClient