File Coverage

blib/lib/Net/Daemon/Test.pm
Criterion Covered Total %
statement 37 97 38.1
branch 9 34 26.4
condition 6 35 17.1
subroutine 6 9 66.6
pod 3 3 100.0
total 61 178 34.2


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