File Coverage

inc/Test/TCP.pm
Criterion Covered Total %
statement 30 80 37.5
branch 0 28 0.0
condition 0 9 0.0
subroutine 10 17 58.8
pod 2 3 66.6
total 42 137 30.6


line stmt bran cond sub pod time code
1             #line 1
2 1     1   1390 package Test::TCP;
  1         10  
  1         34  
3 1     1   5 use strict;
  1         2  
  1         27  
4 1     1   21 use warnings;
  1         3  
  1         57  
5             use 5.00800;
6 1     1   5 our $VERSION = '1.06';
  1         1  
  1         207  
7 1     1   2312 use base qw/Exporter/;
  1         45635  
  1         11  
8 1     1   1817 use IO::Socket::INET;
  1         64410  
  1         12  
9 1     1   152 use Test::SharedFork 0.12;
  1         1  
  1         14  
10 1     1   5 use Test::More ();
  1         3  
  1         67  
11 1     1   1084 use Config;
  1         6854  
  1         8  
12 1     1   5326 use POSIX;
  1         3008  
  1         1106  
13             use Time::HiRes ();
14              
15             # process does not die when received SIGTERM, on win32.
16             my $TERMSIG = $^O eq 'MSWin32' ? 'KILL' : 'TERM';
17              
18             our @EXPORT = qw/ empty_port test_tcp wait_port /;
19              
20 0     0 0   sub empty_port {
21 0 0         my $port = do {
22 0           if (@_) {
23 0 0 0       my $p = $_[0];
24 0           $p = 19000 unless $p =~ /^[0-9]+$/ && $p < 19000;
25             $p;
26 0           } else {
27             10000 + int(rand()*1000);
28             }
29             };
30 0            
31 0 0         while ( $port++ < 20000 ) {
32 0 0         next if _check_port($port);
33             my $sock = IO::Socket::INET->new(
34             Listen => 5,
35             LocalAddr => '127.0.0.1',
36             LocalPort => $port,
37             Proto => 'tcp',
38             (($^O eq 'MSWin32') ? () : (ReuseAddr => 1)),
39 0 0         );
40             return $port if $sock;
41 0           }
42             die "empty port not found";
43             }
44              
45 0     0 1   sub test_tcp {
46 0           my %args = @_;
47 0 0         for my $k (qw/client server/) {
48             die "missing madatory parameter $k" unless exists $args{$k};
49 0   0       }
50             my $port = $args{port} || empty_port();
51 0 0          
    0          
52             if ( my $pid = fork() ) {
53 0           # parent.
54             wait_port($port);
55              
56             my $guard = Test::TCP::Guard->new(code => sub {
57 0     0     # cleanup
58 0           kill $TERMSIG => $pid;
59 0           local $?; # waitpid modifies original $?.
60 0           LOOP: while (1) {
61 0 0         my $kid = waitpid( $pid, 0 );
62 0 0         if ($^O ne 'MSWin32') { # i'm not in hell
63 0           if (WIFSIGNALED($?)) {
64 0 0         my $signame = (split(' ', $Config{sig_name}))[WTERMSIG($?)];
65 0           if ($signame =~ /^(ABRT|PIPE)$/) {
66             Test::More::diag("your server received SIG$signame");
67             }
68             }
69 0 0 0       }
70 0           if ($kid == 0 || $kid == -1) {
71             last LOOP;
72             }
73 0           }
74             });
75 0            
76             $args{client}->($port, $pid);
77             }
78             elsif ( $pid == 0 ) {
79 0           # child
80 0           $args{server}->($port);
81             exit;
82             }
83 0           else {
84             die "fork failed: $!";
85             }
86             }
87              
88 0     0     sub _check_port {
89             my ($port) = @_;
90 0            
91             my $remote = IO::Socket::INET->new(
92             Proto => 'tcp',
93             PeerAddr => '127.0.0.1',
94             PeerPort => $port,
95 0 0         );
96 0           if ($remote) {
97 0           close $remote;
98             return 1;
99             }
100 0           else {
101             return 0;
102             }
103             }
104              
105 0     0 1   sub wait_port {
106             my $port = shift;
107 0            
108 0           my $retry = 100;
109 0 0         while ( $retry-- ) {
110 0           return if _check_port($port);
111             Time::HiRes::sleep(0.1);
112 0           }
113             die "cannot open port: $port";
114             }
115              
116             {
117             package # hide from pause
118             Test::TCP::Guard;
119 0     0     sub new {
120 0           my ($class, %args) = @_;
121             bless { %args }, $class;
122             }
123 0     0     sub DESTROY {
124 0           my ($self) = @_;
125 0           local $@;
126             $self->{code}->();
127             }
128             }
129              
130             1;
131             __END__