File Coverage

inc/Test/TCP.pm
Criterion Covered Total %
statement 35 82 42.6
branch 3 28 10.7
condition 2 11 18.1
subroutine 11 16 68.7
pod 2 3 66.6
total 53 140 37.8


line stmt bran cond sub pod time code
1             #line 1
2 6     6   4132 package Test::TCP;
  6         12  
  6         302  
3 6     6   28 use strict;
  6         12  
  6         164  
4 6     6   170 use warnings;
  6         22  
  6         510  
5             use 5.00800;
6 6     6   46 our $VERSION = '0.16';
  6         24  
  6         674  
7 6     6   188 use base qw/Exporter/;
  6         14  
  6         78  
8 6     6   9902 use IO::Socket::INET;
  6         7480  
  6         84  
9 6     6   66 use Test::SharedFork;
  6         10  
  6         104  
10 6     6   36 use Test::More ();
  6         8  
  6         294  
11 6     6   42 use Config;
  6         12  
  6         56  
12 6     6   16666 use POSIX;
  6         10  
  6         5550  
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 6   50 6 0 18068 sub empty_port {
21 6 50 33     74 my $port = shift || 10000;
22             $port = 19000 unless $port =~ /^[0-9]+$/ && $port < 19000;
23 6         32  
24 6 50       350 while ( $port++ < 20000 ) {
25             my $sock = IO::Socket::INET->new(
26             Listen => 5,
27             LocalAddr => '127.0.0.1',
28             LocalPort => $port,
29             Proto => 'tcp',
30             (($^O eq 'MSWin32') ? () : (ReuseAddr => 1)),
31 6 50       2962 );
32             return $port if $sock;
33 0           }
34             die "empty port not found";
35             }
36              
37 0     0 1   sub test_tcp {
38 0           my %args = @_;
39 0 0         for my $k (qw/client server/) {
40             die "missing madatory parameter $k" unless exists $args{$k};
41 0   0       }
42             my $port = $args{port} || empty_port();
43 0 0          
    0          
44             if ( my $pid = Test::SharedFork->fork() ) {
45 0           # parent.
46             wait_port($port);
47 0            
48             my $sig;
49             my $err;
50 0     0     {
  0            
  0            
  0            
51 0     0     local $SIG{INT} = sub { $sig = "INT"; die "SIGINT received\n" };
  0            
  0            
52 0           local $SIG{PIPE} = sub { $sig = "PIPE"; die "SIGPIPE received\n" };
53 0           eval {
54             $args{client}->($port, $pid);
55 0           };
56             $err = $@;
57              
58 0           # cleanup
59 0           kill $TERMSIG => $pid;
60 0           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;
72             }
73             }
74             }
75 0 0          
76 0           if ($sig) {
77             kill $sig, $$; # rethrow signal after cleanup
78 0 0         }
79 0           if ($err) {
80             die $err; # rethrow exception after cleanup.
81             }
82             }
83             elsif ( $pid == 0 ) {
84 0           # child
85 0           $args{server}->($port);
86             exit;
87             }
88 0           else {
89             die "fork failed: $!";
90             }
91             }
92              
93 0     0     sub _check_port {
94             my ($port) = @_;
95 0            
96             my $remote = IO::Socket::INET->new(
97             Proto => 'tcp',
98             PeerAddr => '127.0.0.1',
99             PeerPort => $port,
100 0 0         );
101 0           if ($remote) {
102 0           close $remote;
103             return 1;
104             }
105 0           else {
106             return 0;
107             }
108             }
109              
110 0     0 1   sub wait_port {
111             my $port = shift;
112 0            
113 0           my $retry = 100;
114 0 0         while ( $retry-- ) {
115 0           return if _check_port($port);
116             Time::HiRes::sleep(0.1);
117 0           }
118             die "cannot open port: $port";
119             }
120              
121             1;
122             __END__