File Coverage

blib/lib/IO/Async/Test.pm
Criterion Covered Total %
statement 37 41 90.2
branch 5 8 62.5
condition 3 5 60.0
subroutine 11 12 91.6
pod 4 4 100.0
total 60 70 85.7


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2007-2015 -- leonerd@leonerd.org.uk
5              
6             package IO::Async::Test;
7              
8 96     96   101969 use strict;
  96         491  
  96         2319  
9 96     96   414 use warnings;
  96         129  
  96         3231  
10              
11             our $VERSION = '0.802';
12              
13 96     96   431 use Exporter 'import';
  96         146  
  96         48745  
14             our @EXPORT = qw(
15             testing_loop
16             wait_for
17             wait_for_stream
18             wait_for_future
19             );
20              
21             =head1 NAME
22              
23             C - utility functions for use in test scripts
24              
25             =head1 SYNOPSIS
26              
27             use Test::More tests => 1;
28             use IO::Async::Test;
29              
30             use IO::Async::Loop;
31             my $loop = IO::Async::Loop->new;
32             testing_loop( $loop );
33              
34             my $result;
35              
36             $loop->do_something(
37             some => args,
38              
39             on_done => sub {
40             $result = the_outcome;
41             }
42             );
43              
44             wait_for { defined $result };
45              
46             is( $result, what_we_expected, 'The event happened' );
47              
48             ...
49              
50             my $buffer = "";
51             my $handle = IO::Handle-> ...
52              
53             wait_for_stream { length $buffer >= 10 } $handle => $buffer;
54              
55             is( substr( $buffer, 0, 10, "" ), "0123456789", 'Buffer was correct' );
56              
57             my $result = wait_for_future( $stream->read_until( "\n" ) )->get;
58              
59             =head1 DESCRIPTION
60              
61             This module provides utility functions that may be useful when writing test
62             scripts for code which uses L (as well as being used in the
63             L test scripts themselves).
64              
65             Test scripts are often synchronous by nature; they are a linear sequence of
66             actions to perform, interspersed with assertions which check for given
67             conditions. This goes against the very nature of L which, being an
68             asynchronisation framework, does not provide a linear stepped way of working.
69              
70             In order to write a test, the C function provides a way of
71             synchronising the code, so that a given condition is known to hold, which
72             would typically signify that some event has occurred, the outcome of which can
73             now be tested using the usual testing primitives.
74              
75             Because the primary purpose of L is to provide IO operations on
76             filehandles, a great many tests will likely be based around connected pipes or
77             socket handles. The C function provides a convenient way
78             to wait for some content to be written through such a connected stream.
79              
80             =cut
81              
82             my $loop;
83 68     68   23876 END { undef $loop }
84              
85             =head1 FUNCTIONS
86              
87             =cut
88              
89             =head2 testing_loop
90              
91             testing_loop( $loop )
92              
93             Set the L object which the C function will loop
94             on.
95              
96             =cut
97              
98             sub testing_loop
99             {
100 126     126 1 1199 $loop = shift;
101             }
102              
103             =head2 wait_for
104              
105             wait_for { COND } OPTS
106              
107             Repeatedly call the C method on the underlying loop (given to the
108             C function), until the given condition function callback
109             returns true.
110              
111             To guard against stalled scripts, if the loop indicates a timeout for (a
112             default of) 10 consequentive seconds, then an error is thrown.
113              
114             Takes the following named options:
115              
116             =over 4
117              
118             =item timeout => NUM
119              
120             The time in seconds to wait before giving up the test as being stalled.
121             Defaults to 10 seconds.
122              
123             =back
124              
125             =cut
126              
127             our $Level = 0;
128              
129             sub wait_for(&@)
130             {
131 490     490 1 58261 my ( $cond, %opts ) = @_;
132              
133 490         8499 my ( undef, $callerfile, $callerline ) = caller( $Level );
134              
135 490         1307 my $timedout = 0;
136             my $timerid = $loop->watch_time(
137             after => $opts{timeout} // 10,
138 0     0   0 code => sub { $timedout = 1 },
139 490   50     9946 );
140              
141 490   66     1841 $loop->loop_once( 1 ) while !$cond->() and !$timedout;
142              
143 488 50       2544 if( $timedout ) {
144 0         0 die "Nothing was ready after 10 second wait; called at $callerfile line $callerline\n";
145             }
146             else {
147 488         1936 $loop->unwatch_time( $timerid );
148             }
149             }
150              
151             =head2 wait_for_stream
152              
153             wait_for_stream { COND } $handle, $buffer
154              
155             As C, but will also watch the given IO handle for readability, and
156             whenever it is readable will read bytes in from it into the given buffer. The
157             buffer is NOT initialised when the function is entered, in case data remains
158             from a previous call.
159              
160             C<$buffer> can also be a CODE reference, in which case it will be invoked
161             being passed data read from the handle, whenever it is readable.
162              
163             =cut
164              
165             sub wait_for_stream(&$$)
166             {
167 7     7 1 730 my ( $cond, $handle, undef ) = @_;
168              
169 7         19 my $on_read;
170 7 100       39 if( ref $_[2] eq "CODE" ) {
171 1         3 $on_read = $_[2];
172             }
173             else {
174 6         17 my $varref = \$_[2];
175 6     6   51 $on_read = sub { $$varref .= $_[0] };
  6         26  
176             }
177              
178             $loop->watch_io(
179             handle => $handle,
180             on_read_ready => sub {
181 7     7   34 my $ret = $handle->sysread( my $buffer, 8192 );
182 7 50       186 if( !defined $ret ) {
    50          
183 0         0 die "Read failed on $handle - $!\n";
184             }
185             elsif( $ret == 0 ) {
186 0         0 die "Read returned EOF on $handle\n";
187             }
188 7         18 $on_read->( $buffer );
189             }
190 7         86 );
191              
192 7         109 local $Level = $Level + 1;
193             # Have to defeat the prototype... grr I hate these
194 7         39 &wait_for( $cond );
195              
196 7         233 $loop->unwatch_io(
197             handle => $handle,
198             on_read_ready => 1,
199             );
200             }
201              
202             =head2 wait_for_future
203              
204             $future = wait_for_future $future
205              
206             I
207              
208             A handy wrapper around using C to wait for a L to become
209             ready. The future instance itself is returned, allowing neater code.
210              
211             =cut
212              
213             sub wait_for_future
214             {
215 49     49 1 1380 my ( $future ) = @_;
216              
217 49         322 local $Level = $Level + 1;
218 49     136   808 wait_for { $future->is_ready };
  136         622  
219              
220 49         1985 return $future;
221             }
222              
223             =head1 AUTHOR
224              
225             Paul Evans
226              
227             =cut
228              
229             0x55AA;