File Coverage

blib/lib/Test/UNIXSock.pm
Criterion Covered Total %
statement 88 98 89.8
branch 17 32 53.1
condition 4 8 50.0
subroutine 21 21 100.0
pod 5 7 71.4
total 135 166 81.3


line stmt bran cond sub pod time code
1             package Test::UNIXSock;
2 3     3   31619 use strict;
  3         8  
  3         76  
3 3     3   13 use warnings;
  3         7  
  3         67  
4 3     3   59 use 5.00800;
  3         10  
5             our $VERSION = '0.2';
6 3     3   15 use base qw/Exporter/;
  3         7  
  3         267  
7 3     3   1231 use IO::Socket::UNIX;
  3         52235  
  3         18  
8 3     3   2139 use Test::SharedFork 0.12;
  3         55648  
  3         37  
9 3     3   461 use Test::More ();
  3         6  
  3         40  
10 3     3   12 use Config;
  3         7  
  3         75  
11 3     3   1232 use POSIX;
  3         12999  
  3         35  
12 3     3   7742 use Time::HiRes ();
  3         2977  
  3         60  
13 3     3   18 use Carp ();
  3         6  
  3         48  
14 3     3   14 use File::Temp qw/ tempdir /;
  3         6  
  3         170  
15 3     3   1096 use Net::EmptyPort ();
  3         21239  
  3         1922  
16              
17             our @EXPORT = qw/ test_unix_sock wait_unix_sock /;
18              
19             my $TERMSIG = 'TERM';
20              
21             sub test_unix_sock {
22 1     1 0 1517 my %args = @_;
23 1         3 for my $k (qw/client server/) {
24 2 50       8 die "missing madatory parameter $k" unless exists $args{$k};
25             }
26 1         4 my $server_code = delete $args{server};
27 1         4 my $client_code = delete $args{client};
28              
29 1         9 my $server = Test::UNIXSock->new(
30             code => $server_code,
31             %args,
32             );
33 1         6 $client_code->($server->path, $server->pid);
34 1         16969 undef $server; # make sure
35             }
36              
37             sub wait_unix_sock {
38 2     2 1 107 my ($path, $max_wait);
39 2 50 33     54 if (@_ && ref $_[0] eq 'HASH') {
    0          
40 2         13 $path = $_[0]->{path};
41 2         5 $max_wait = $_[0]->{max_wait};
42             } elsif (@_ == 3) {
43             # backward compat
44 0         0 ($path, (my $sleep), (my $retry)) = @_;
45 0         0 $max_wait = $sleep * $retry;
46             } else {
47 0         0 ($path, $max_wait) = @_;
48             }
49 2   50     13 $max_wait ||= 10;
50 2         54 my $waiter = Net::EmptyPort::_make_waiter($max_wait);
51 2         79 while ( $waiter->() ) {
52 8 100       33245 IO::Socket::UNIX->new(
53             Type => SOCK_STREAM,
54             Peer => $path,
55             ) && return 1;
56             }
57 0         0 return 0;
58             }
59              
60             # -------------------------------------------------------------------------
61             # OO-ish interface
62              
63             sub new {
64 2     2 1 25 my $class = shift;
65 2 50       12 my %args = @_==1 ? %{$_[0]} : @_;
  0         0  
66 2 50       10 Carp::croak("missing mandatory parameter 'code'") unless exists $args{code};
67 2         15 my $self = bless {
68             auto_start => 1,
69             max_wait => 10,
70             _my_pid => $$,
71             %args,
72             }, $class;
73 2 50       24 unless (defined $self->{path}) {
74 2         15 $self->{tmpdir} = tempdir( CLEANUP => 1 );
75 2         888 $self->{path} = $self->{tmpdir} . "/test.sock";
76             }
77             $self->start()
78 2 50       18 if $self->{auto_start};
79 2         30 return $self;
80             }
81              
82 1     1 1 7 sub pid { $_[0]->{pid} }
83 14     14 0 11716 sub path { $_[0]->{path} }
84              
85             sub start {
86 2     2 1 4 my $self = shift;
87 2         1424 my $pid = fork();
88 2 50       80 die "fork() failed: $!" unless defined $pid;
89              
90 2 50       67 if ( $pid ) { # parent process.
91 2         27 $self->{pid} = $pid;
92 2         51 Test::UNIXSock::wait_unix_sock({ path => $self->path, max_wait => $self->{max_wait} });
93 2         515 return;
94             } else { # child process
95 0         0 $self->{code}->($self->path);
96             # should not reach here
97 0 0       0 if (kill 0, $self->{_my_pid}) { # warn only parent process still exists
98 0         0 warn("[Test::UNIXSocket] Child process does not block(PID: $$, PPID: $self->{_my_pid})");
99             }
100 0         0 exit 0;
101             }
102             }
103              
104             sub stop {
105 2     2 1 6 my $self = shift;
106              
107 2 50       12 return unless defined $self->{pid};
108 2 50       17 return unless $self->{_my_pid} == $$;
109              
110 2         1004 kill $TERMSIG => $self->{pid};
111              
112 2         19 local $?; # waitpid modifies original $?.
113 2         5 LOOP: while (1) {
114 4         1718 my $kid = waitpid( $self->{pid}, 0 );
115 4 100       44 if (POSIX::WIFSIGNALED($?)) {
116 2         259 my $signame = (split(' ', $Config{sig_name}))[POSIX::WTERMSIG($?)];
117 2 50       24 if ($signame =~ /^(ABRT|PIPE)$/) {
118 0         0 Test::More::diag("your server received SIG$signame");
119             }
120             }
121 4 100 66     33 if ($kid == 0 || $kid == -1) {
122 2         7 last LOOP;
123             }
124             }
125 2         78 undef $self->{pid};
126             }
127              
128             sub DESTROY {
129 2     2   4738 my $self = shift;
130 2         6 local $@;
131 2         19 $self->stop();
132             }
133              
134             1;
135             __END__