File Coverage

inc/Test/TCP.pm
Criterion Covered Total %
statement 70 82 85.3
branch 18 28 64.2
condition 7 11 63.6
subroutine 14 16 87.5
pod 2 3 66.6
total 111 140 79.2


line stmt bran cond sub pod time code
1             #line 1
2 4     4   1804 package Test::TCP;
  4         6  
  4         134  
3 4     4   14 use strict;
  4         4  
  4         76  
4 4     4   58 use warnings;
  4         10  
  4         158  
5             use 5.00800;
6 4     4   14 our $VERSION = '0.16';
  4         4  
  4         270  
7 4     4   2070 use base qw/Exporter/;
  4         83762  
  4         22  
8 4     4   3860 use IO::Socket::INET;
  4         118804  
  4         104  
9 4     4   3662 use Test::SharedFork;
  4         16520  
  4         98  
10 4     4   24 use Test::More ();
  4         6  
  4         136  
11 4     4   2344 use Config;
  4         19716  
  4         24  
12 4     4   11212 use POSIX;
  4         5250  
  4         2442  
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   100 6 0 38 sub empty_port {
21 6 100 66     64 my $port = shift || 10000;
22             $port = 19000 unless $port =~ /^[0-9]+$/ && $port < 19000;
23 6         24  
24 6 50       86 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       1750 );
32             return $port if $sock;
33 0         0 }
34             die "empty port not found";
35             }
36              
37 4     4 1 60 sub test_tcp {
38 4         12 my %args = @_;
39 8 50       28 for my $k (qw/client server/) {
40             die "missing madatory parameter $k" unless exists $args{$k};
41 4   33     26 }
42             my $port = $args{port} || empty_port();
43 4 100       38  
    50          
44             if ( my $pid = Test::SharedFork->fork() ) {
45 2         1808 # parent.
46             wait_port($port);
47 2         6  
48             my $sig;
49             my $err;
50 2     0   20 {
  2         116  
  0         0  
  0         0  
51 2     0   39 local $SIG{INT} = sub { $sig = "INT"; die "SIGINT received\n" };
  0         0  
  0         0  
52 2         6 local $SIG{PIPE} = sub { $sig = "PIPE"; die "SIGPIPE received\n" };
53 2         33 eval {
54             $args{client}->($port, $pid);
55 2         10609 };
56             $err = $@;
57              
58 2         75 # cleanup
59 2         4 kill $TERMSIG => $pid;
60 4         473026 while (1) {
61 4 50       43 my $kid = waitpid( $pid, 0 );
62 4 50       28 if ($^O ne 'MSWin32') { # i'm not in hell
63 0         0 if (WIFSIGNALED($?)) {
64 0 0       0 my $signame = (split(' ', $Config{sig_name}))[WTERMSIG($?)];
65 0         0 if ($signame =~ /^(ABRT|PIPE)$/) {
66             Test::More::diag("your server received SIG$signame");
67             }
68             }
69 4 100 66     39 }
70 2         52 if ($kid == 0 || $kid == -1) {
71             last;
72             }
73             }
74             }
75 2 50       8  
76 0         0 if ($sig) {
77             kill $sig, $$; # rethrow signal after cleanup
78 2 50       126 }
79 0         0 if ($err) {
80             die $err; # rethrow exception after cleanup.
81             }
82             }
83             elsif ( $pid == 0 ) {
84 2         2050 # child
85 2         7936107 $args{server}->($port);
86             exit;
87             }
88 0         0 else {
89             die "fork failed: $!";
90             }
91             }
92              
93 4     4   14 sub _check_port {
94             my ($port) = @_;
95 4         116  
96             my $remote = IO::Socket::INET->new(
97             Proto => 'tcp',
98             PeerAddr => '127.0.0.1',
99             PeerPort => $port,
100 4 100       2680 );
101 2         93 if ($remote) {
102 2         30 close $remote;
103             return 1;
104             }
105 2         15 else {
106             return 0;
107             }
108             }
109              
110 2     2 1 32 sub wait_port {
111             my $port = shift;
112 2         13  
113 2         66 my $retry = 100;
114 4 100       108 while ( $retry-- ) {
115 2         200304 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__