File Coverage

inc/Test/TCP.pm
Criterion Covered Total %
statement 90 102 88.2
branch 24 46 52.1
condition 4 9 44.4
subroutine 21 21 100.0
pod 7 8 87.5
total 146 186 78.4


line stmt bran cond sub pod time code
1             #line 1
2 55     55   6627552 package Test::TCP;
  55         142  
  55         2228  
3 55     55   320 use strict;
  55         112  
  55         1575  
4 55     55   1292 use warnings;
  55         210  
  55         4342  
5             use 5.00800;
6 55     55   357 our $VERSION = '1.13';
  55         134  
  55         4815  
7 55     55   1758772 use base qw/Exporter/;
  55         2009628  
  55         488  
8 55     55   94709 use IO::Socket::INET;
  55         2056  
  55         753  
9 55     55   32537 use Test::SharedFork 0.12;
  55         151  
  55         1155  
10 55     55   339 use Test::More ();
  55         120  
  55         1994  
11 55     55   66285 use Config;
  55         451598  
  55         1126  
12 55     55   740384 use POSIX;
  55         137386  
  55         1743  
13 55     55   408 use Time::HiRes ();
  55         132  
  55         64866  
14             use Carp ();
15              
16             our @EXPORT = qw/ empty_port test_tcp wait_port /;
17              
18             # process does not die when received SIGTERM, on win32.
19             my $TERMSIG = $^O eq 'MSWin32' ? 'KILL' : 'TERM';
20              
21             # get a empty port on 49152 .. 65535
22             # http://www.iana.org/assignments/port-numbers
23 14     14 0 4626 sub empty_port {
24 14 50       64 my $port = do {
25 0         0 if (@_) {
26 0 0 0     0 my $p = $_[0];
27 0         0 $p = 49152 unless $p =~ /^[0-9]+$/ && $p < 49152;
28             $p;
29 14         88 } else {
30             50000 + int(rand()*1000);
31             }
32             };
33 14         92  
34 14 50       96 while ( $port++ < 60000 ) {
35 14 50       171 next if _check_port($port);
36             my $sock = IO::Socket::INET->new(
37             Listen => 5,
38             LocalAddr => '127.0.0.1',
39             LocalPort => $port,
40             Proto => 'tcp',
41             (($^O eq 'MSWin32') ? () : (ReuseAddr => 1)),
42 14 50       3968 );
43             return $port if $sock;
44 0         0 }
45             die "empty port not found";
46             }
47              
48 43     43 1 3000 sub test_tcp {
49 43         146 my %args = @_;
50 86 50       364 for my $k (qw/client server/) {
51             die "missing madatory parameter $k" unless exists $args{$k};
52 43   66     640 }
53             my $server = Test::TCP->new(
54             code => $args{server},
55             port => $args{port} || empty_port(),
56 43         548 );
57 43         2106 $args{client}->($server->port, $server->pid);
58             undef $server; # make sure
59             }
60              
61 102     102   659 sub _check_port {
62             my ($port) = @_;
63 102         5739  
64             my $remote = IO::Socket::INET->new(
65             Proto => 'tcp',
66             PeerAddr => '127.0.0.1',
67             PeerPort => $port,
68 102 100       132364 );
69 43         4103 if ($remote) {
70 43         825 close $remote;
71             return 1;
72             }
73 59         697 else {
74             return 0;
75             }
76             }
77              
78 43     43 1 296 sub wait_port {
79             my $port = shift;
80 43         480  
81 43         691 my $retry = 100;
82 88 100       1517 while ( $retry-- ) {
83 45         4516542 return if _check_port($port);
84             Time::HiRes::sleep(0.1);
85 0         0 }
86             die "cannot open port: $port";
87             }
88              
89             # -------------------------------------------------------------------------
90             # OO-ish interface
91              
92 43     43 1 122 sub new {
93 43 50       337 my $class = shift;
  0         0  
94 43 50       168 my %args = @_==1 ? %{$_[0]} : @_;
95 43         472 Carp::croak("missing mandatory parameter 'code'") unless exists $args{code};
96             my $self = bless {
97             auto_start => 1,
98             _my_pid => $$,
99             %args,
100 43 50       375 }, $class;
101 43 50       350 $self->{port} = Test::TCP::empty_port() unless exists $self->{port};
102             $self->start()
103 43         2015 if $self->{auto_start};
104             return $self;
105             }
106 43     43 1 1356  
107 86     86 1 2056 sub pid { $_[0]->{pid} }
108             sub port { $_[0]->{port} }
109              
110 43     43 1 95 sub start {
111 43 50       93614 my $self = shift;
    0          
112             if ( my $pid = fork() ) {
113 43         1808 # parent.
114 43         13650 $self->{pid} = $pid;
115 43         472 Test::TCP::wait_port($self->port);
116             return;
117             } elsif ($pid == 0) {
118 0         0 # child process
119             $self->{code}->($self->port);
120 0 0       0 # should not reach here
121 0         0 if (kill 0, $self->{_my_pid}) { # warn only parent process still exists
122             warn("[Test::TCP] Child process does not block(PID: $$, PPID: $self->{_my_pid})");
123 0         0 }
124             exit 0;
125 0         0 } else {
126             die "fork failed: $!";
127             }
128             }
129              
130 43     43 1 95 sub stop {
131             my $self = shift;
132 43 50       210  
133 43 50       857 return unless defined $self->{pid};
134             return unless $self->{_my_pid} == $$;
135              
136             # This is a workaround for win32 fork emulation's bug.
137             #
138             # kill is inherently unsafe for pseudo-processes in Windows
139             # and the process calling kill(9, $pid) may be destabilized
140             # The call to Sleep will decrease the frequency of this problems
141             #
142             # SEE ALSO:
143             # http://www.gossamer-threads.com/lists/perl/porters/261805
144 43 50       368 # https://rt.cpan.org/Ticket/Display.html?id=67292
145             Win32::Sleep(0) if $^O eq "MSWin32"; # will relinquish the remainder of its time slice
146 43         36585  
147             kill $TERMSIG => $self->{pid};
148 43 50       323  
149             Win32::Sleep(0) if $^O eq "MSWin32"; # will relinquish the remainder of its time slice
150              
151 43         491  
152 43         643 local $?; # waitpid modifies original $?.
153 86         207384 LOOP: while (1) {
154 86 50       753 my $kid = waitpid( $self->{pid}, 0 );
155 86 100       815 if ($^O ne 'MSWin32') { # i'm not in hell
156 43         18957 if (POSIX::WIFSIGNALED($?)) {
157 43 50       47168 my $signame = (split(' ', $Config{sig_name}))[POSIX::WTERMSIG($?)];
158 0         0 if ($signame =~ /^(ABRT|PIPE)$/) {
159             Test::More::diag("your server received SIG$signame");
160             }
161             }
162 86 100 66     849 }
163 43         154 if ($kid == 0 || $kid == -1) {
164             last LOOP;
165             }
166 43         3509 }
167             undef $self->{pid};
168             }
169              
170 43     43   146 sub DESTROY {
171 43         122 my $self = shift;
172 43         1258 local $@;
173             $self->stop();
174             }
175              
176             1;
177             __END__