File Coverage

blib/lib/Net/Daemon/Test.pm
Criterion Covered Total %
statement 39 100 39.0
branch 8 34 23.5
condition 5 35 14.2
subroutine 7 10 70.0
pod 3 3 100.0
total 62 182 34.0


line stmt bran cond sub pod time code
1             ############################################################################
2             #
3             # Net::Daemon::Test
4             #
5             # Copyright (C) 1998, Jochen Wiedmann
6             # Am Eisteich 9
7             # 72555 Metzingen
8             # Germany
9             #
10             # Phone: +49 7123 14887
11             # Email: joe@ispsoft.de
12             #
13             #
14             # This module is free software; you can redistribute it and/or modify
15             # it under the terms of the GNU General Public License as published by
16             # the Free Software Foundation; either version 2 of the License, or
17             # (at your option) any later version.
18             #
19             # This module is distributed in the hope that it will be useful,
20             # but WITHOUT ANY WARRANTY; without even the implied warranty of
21             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22             # GNU General Public License for more details.
23             #
24             # You should have received a copy of the GNU General Public License
25             # along with this module; if not, write to the Free Software
26             # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27             #
28             ############################################################################
29              
30             package Net::Daemon::Test;
31              
32 33     33   824379 use strict;
  33         326  
  33         903  
33 33     33   152 use warnings;
  33         43  
  33         845  
34              
35 33     33   15446 use Net::Daemon ();
  33         95  
  33         802  
36 33     33   194 use Symbol ();
  33         62  
  33         442  
37 33     33   151 use File::Basename ();
  33         67  
  33         41650  
38              
39             our $VERSION = '0.49';
40             our @ISA = qw(Net::Daemon);
41              
42             =head1 NAME
43              
44             Net::Daemon::Test - support functions for testing Net::Daemon servers
45              
46              
47             =head1 SYNOPSIS
48              
49             # This is the server, stored in the file "servertask".
50             #
51             # Create a subclass of Net::Daemon::Test, which in turn is
52             # a subclass of Net::Daemon
53             use Net::Daemon::Test ();
54             package MyDaemon;
55             our @ISA = qw(Net::Daemon::Test);
56              
57             sub Run {
58             # Overwrite this and other methods, as you like.
59             }
60              
61             my $self = Net::Daemon->new(\%attr, \@options);
62             eval { $self->Bind() };
63             if ($@) {
64             die "Server cannot bind: $!";
65             }
66             eval { $self->Run() };
67             if ($@) {
68             die "Unexpected server termination: $@";
69             }
70              
71              
72             # This is the client, the real test script, note we call the
73             # "servertask" file below:
74             #
75             # Call the Child method to spawn a child. Don't forget to use
76             # the timeout option.
77             use Net::Daemon::Test ();
78              
79             my($handle, $port) = eval {
80             Net::Daemon::Test->Child(5, # Number of subtests
81             'servertask', '--timeout', '20')
82             };
83             if ($@) {
84             print "not ok 1 $@\n";
85             exit 0;
86             }
87             print "ok 1\n";
88              
89             # Real tests following here
90             ...
91              
92             # Terminate the server
93             $handle->Terminate();
94              
95              
96             =head1 DESCRIPTION
97              
98             This module is a frame for creating test scripts of Net::Daemon based
99             server packages, preferrably using Test::Harness, but that's your
100             choice.
101              
102             A test consists of two parts: The client part and the server part.
103             The test is executed by the child part which invokes the server part,
104             by spawning a child process and invoking an external Perl script.
105             (Of course we woultn't need this external file with fork(), but that's
106             the best possibility to make the test scripts portable to Windows
107             without requiring threads in the test script.)
108              
109             The server part is a usual Net::Daemon application, for example a script
110             like dbiproxy. The only difference is that it derives from
111             Net::Daemon::Test and not from Net::Daemon, the main difference is that
112             the B method attempts to allocate a port automatically. Once a
113             port is allocated, the number is stored in the file "ndtest.prt".
114              
115             After spawning the server process, the child will wait ten seconds
116             (hopefully sufficient) for the creation of ndtest.prt.
117              
118              
119             =head1 AVAILABLE METHODS
120              
121             =head2 Server part
122              
123             =over 8
124              
125             =item Options
126              
127             Adds an option B<--timeout> to Net::Daemon: The server's Run method
128             will die after at most 20 seconds.
129              
130             =cut
131              
132             sub Options ($) {
133 0     0 1 0 my $self = shift;
134 0         0 my $options = $self->SUPER::Options();
135 0         0 $options->{'timeout'} = {
136             'template' => 'timeout=i',
137             'description' => '--timeout ' . "The server will die if the test takes longer\n" . ' than this number of seconds.'
138             };
139 0         0 $options;
140             }
141              
142             =pod
143              
144             =item Bind
145              
146             (Instance method) This is mainly the default Bind method, but it attempts
147             to find and allocate a free port in two ways: First of all, it tries to
148             call Bind with port 0, most systems will automatically choose a port in
149             that case. If that seems to fail, ports 30000-30049 are tried. We
150             hope, one of these will succeed. :-)
151              
152             =cut
153              
154             sub Bind ($) {
155              
156             # First try: Pass unmodified options to Net::Daemon::Bind
157 0     0 1 0 my $self = shift;
158 0         0 my ( $port, $socket );
159 0 0 0     0 $self->{'proto'} ||= $self->{'localpath'} ? 'unix' : 'tcp';
160 0 0       0 if ( $self->{'proto'} eq 'unix' ) {
161 0   0     0 $port = $self->{'localpath'} || die "Missing option: localpath";
162 0         0 $socket = eval {
163             IO::Socket::UNIX->new(
164             'Local' => $port,
165 0   0     0 'Listen' => $self->{'listen'} || 10
166             );
167             }
168             }
169             else {
170             my @socket_args = (
171             'LocalAddr' => $self->{'localaddr'},
172             'LocalPort' => $self->{'localport'},
173             'Proto' => $self->{'proto'} || 'tcp',
174 0   0     0 'Listen' => $self->{'listen'} || 10,
      0        
175             'Reuse' => 1
176             );
177 0         0 $socket = eval { IO::Socket::INET->new(@socket_args) };
  0         0  
178 0 0       0 if ($socket) {
179 0         0 $port = $socket->sockport();
180             }
181             else {
182 0         0 $port = 30049;
183 0   0     0 while ( !$socket && $port++ < 30060 ) {
184 0         0 $socket = eval {
185 0         0 IO::Socket::INET->new(
186             @socket_args,
187             'LocalPort' => $port
188             );
189             };
190             }
191             }
192             }
193 0 0       0 if ( !$socket ) {
194 0   0     0 die "Cannot create socket: " . ( $@ || $! );
195             }
196              
197             # Create the "ndtest.prt" file so that the child knows to what
198             # port it may connect.
199 0         0 my $fh = Symbol::gensym();
200 0 0 0     0 if ( !open( $fh, ">ndtest.prt" )
      0        
201             || !( print $fh $port )
202             || !close($fh) ) {
203 0         0 die "Error while creating 'ndtest.prt': $!";
204             }
205 0         0 $self->Debug("Created ndtest.prt with port $port\n");
206 0         0 $self->{'socket'} = $socket;
207              
208 0 0       0 if ( my $timeout = $self->{'timeout'} ) {
209 0         0 eval { alarm $timeout };
  0         0  
210             }
211              
212 0         0 $self->SUPER::Bind();
213             }
214              
215             =pod
216              
217             =item Run
218              
219             (Instance method) Overwrites the Net::Daemon's method by adding a timeout.
220              
221             =back
222              
223             sub Run ($) {
224             my $self = shift;
225             $self->Run();
226             }
227              
228              
229             =head2 Client part
230              
231             =over 8
232              
233             =item Child
234              
235             (Class method) Attempts to spawn a server process. The server process is
236             expected to create the file 'ndtest.prt' with the port number.
237              
238             The method returns a process handle and a port number. The process handle
239             offers a method B that may later be used to stop the server
240             process.
241              
242             =back
243              
244             =cut
245              
246             sub Child ($$@) {
247 42     42 1 5148541 my $self = shift;
248 42         467 my $numTests = shift;
249 42         292 my ( $handle, $pid );
250              
251 42         465 my $args = join( " ", @_ );
252 42         1462 print "Starting server: $args\n";
253              
254 42         1512 unlink 'ndtest.prt';
255              
256 42 50 66     1663 if ( $args =~ /\-\-mode=(?:ithread|thread|single)/ && $^O =~ /mswin32/i ) {
257 0         0 require Win32;
258 0         0 require Win32::Process;
259 0         0 my $proc = $_[0];
260              
261             # Win32::Process seems to require an absolute path; this includes
262             # a program extension like ".exe"
263 0         0 my $path;
264             my @pdirs;
265              
266 0         0 File::Basename::fileparse_set_fstype("MSWin32");
267 0 0       0 if ( File::Basename::basename($proc) !~ /\./ ) {
268 0         0 $proc .= ".exe";
269             }
270 0 0 0     0 if ( $proc !~ /^\w\:\\/ && $proc !~ /^\\/ ) {
271              
272             # Doesn't look like an absolute path
273 0         0 foreach my $dir ( @pdirs = split( /;/, $ENV{'PATH'} ) ) {
274 0 0       0 if ( -x "$dir/$proc" ) {
275 0         0 $path = "$dir/$proc";
276 0         0 last;
277             }
278             }
279 0 0       0 if ( !$path ) {
280 0         0 print STDERR ( "Cannot find $proc in the following", " directories:\n" );
281 0         0 foreach my $dir (@pdirs) {
282 0         0 print STDERR " $dir\n";
283             }
284 0         0 print STDERR "Terminating.\n";
285 0         0 exit 1;
286             }
287             }
288             else {
289 0         0 $path = $proc;
290             }
291              
292 0         0 print "Starting process: proc = $path, args = ", join( " ", @_ ), "\n";
293 0 0       0 if (
294             !Win32::Process::Create(
295             $pid, $path,
296             join( " ", @_ ), 0,
297             Win32::Process::DETACHED_PROCESS(),
298             "."
299             )
300             ) {
301 0         0 die "Cannot create child process: " . Win32::FormatMessage( Win32::GetLastError() );
302             }
303 0         0 $handle = bless( \$pid, "Net::Daemon::Test::Win32" );
304             }
305             else {
306 42         145 $pid = eval { fork() };
  42         140154  
307 42 50       2892 if ( defined($pid) ) {
308              
309             # Aaaah, Unix! :-)
310 42 100       1156 if ( !$pid ) {
311              
312             # This is the child process, spawn the server.
313 11         0 exec @_;
314             }
315 31         1563 $handle = bless( \$pid, "Net::Daemon::Test::Fork" );
316             }
317             else {
318 0         0 print "1..0\n";
319 0         0 exit 0;
320             }
321             }
322              
323 31 100       1656 print "1..$numTests\n" if defined($numTests);
324 31   66     31009464 for ( my $secs = 20; $secs && !-s 'ndtest.prt'; $secs -= sleep 1 ) {
325             }
326 31 50       1227 if ( !-s 'ndtest.prt' ) {
327 0         0 die "Server process didn't create a file 'ndtest.prt'.";
328             }
329              
330             # Sleep another second in case the server is still creating the
331             # file with the port number ...
332 31         31004696 sleep 1;
333 31         1839 my $fh = Symbol::gensym();
334 31         3242 my $port;
335 31 50 33     6253 if ( !open( $fh, "
336             || !defined( $port = <$fh> ) ) {
337 0         0 die "Error while reading 'ndtest.prt': $!";
338             }
339 31         1592 ( $handle, $port );
340             }
341              
342             package Net::Daemon::Test::Fork;
343              
344             sub Terminate ($) {
345 21     21   41856644 my $self = shift;
346 21         126 my $pid = $$self;
347 21         1828 kill 'TERM', $pid;
348             }
349              
350             package Net::Daemon::Test::Win32;
351              
352             sub Terminate ($) {
353 0     0     my $self = shift;
354 0           my $pid = $$self;
355 0           $pid->Kill(0);
356             }
357              
358             1;
359              
360             =head1 AUTHOR AND COPYRIGHT
361              
362             Net::Daemon is Copyright (C) 1998, Jochen Wiedmann
363             Am Eisteich 9
364             72555 Metzingen
365             Germany
366              
367             Phone: +49 7123 14887
368             Email: joe@ispsoft.de
369              
370             All rights reserved.
371              
372             You may distribute under the terms of either the GNU General Public
373             License or the Artistic License, as specified in the Perl README file.
374              
375              
376             =head1 SEE ALSO
377              
378             L, L
379              
380             =cut