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   42439 use strict;
  3         7  
  3         79  
3 3     3   14 use warnings;
  3         5  
  3         103  
4 3     3   80 use 5.00800;
  3         9  
5             our $VERSION = '0.1';
6 3     3   23 use base qw/Exporter/;
  3         10  
  3         292  
7 3     3   2405 use IO::Socket::UNIX;
  3         75674  
  3         18  
8 3     3   4226 use Test::SharedFork 0.12;
  3         94400  
  3         42  
9 3     3   516 use Test::More ();
  3         8  
  3         62  
10 3     3   19 use Config;
  3         6  
  3         117  
11 3     3   2943 use POSIX;
  3         24351  
  3         23  
12 3     3   12834 use Time::HiRes ();
  3         4371  
  3         77  
13 3     3   20 use Carp ();
  3         4  
  3         80  
14 3     3   16 use File::Temp qw/ tempdir /;
  3         5  
  3         214  
15 3     3   2323 use Net::EmptyPort ();
  3         29633  
  3         2777  
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 1111 my %args = @_;
23 1         5 for my $k (qw/client server/) {
24 2 50       8 die "missing madatory parameter $k" unless exists $args{$k};
25             }
26 1         3 my $server_code = delete $args{server};
27 1         3 my $client_code = delete $args{client};
28              
29 1         8 my $server = Test::UNIXSock->new(
30             code => $server_code,
31             %args,
32             );
33 1         10 $client_code->($server->path, $server->pid);
34 1         24292 undef $server; # make sure
35             }
36              
37             sub wait_unix_sock {
38 2     2 1 242 my ($path, $max_wait);
39 2 50 33     49 if (@_ && ref $_[0] eq 'HASH') {
    0          
40 2         25 $path = $_[0]->{path};
41 2         10 $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     7 $max_wait ||= 10;
50 2         77 my $waiter = Net::EmptyPort::_make_waiter($max_wait);
51 2         105 while ( $waiter->() ) {
52 8 100       36128 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 21 my $class = shift;
65 2 50       13 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       21 unless (defined $self->{path}) {
74 2         13 $self->{tmpdir} = tempdir( CLEANUP => 1 );
75 2         1081 $self->{path} = $self->{tmpdir} . "/test.sock";
76             }
77             $self->start()
78 2 50       17 if $self->{auto_start};
79 2         57 return $self;
80             }
81              
82 1     1 1 26 sub pid { $_[0]->{pid} }
83 14     14 0 18180 sub path { $_[0]->{path} }
84              
85             sub start {
86 2     2 1 4 my $self = shift;
87 2         2672 my $pid = fork();
88 2 50       90 die "fork() failed: $!" unless defined $pid;
89              
90 2 50       116 if ( $pid ) { # parent process.
91 2         57 $self->{pid} = $pid;
92 2         81 Test::UNIXSock::wait_unix_sock({ path => $self->path, max_wait => $self->{max_wait} });
93 2         621 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       17 return unless defined $self->{pid};
108 2 50       28 return unless $self->{_my_pid} == $$;
109              
110 2         1479 kill $TERMSIG => $self->{pid};
111              
112 2         55 local $?; # waitpid modifies original $?.
113 2         9 LOOP: while (1) {
114 4         2665 my $kid = waitpid( $self->{pid}, 0 );
115 4 100       82 if (POSIX::WIFSIGNALED($?)) {
116 2         440 my $signame = (split(' ', $Config{sig_name}))[POSIX::WTERMSIG($?)];
117 2 50       45 if ($signame =~ /^(ABRT|PIPE)$/) {
118 0         0 Test::More::diag("your server received SIG$signame");
119             }
120             }
121 4 100 66     47 if ($kid == 0 || $kid == -1) {
122 2         11 last LOOP;
123             }
124             }
125 2         140 undef $self->{pid};
126             }
127              
128             sub DESTROY {
129 2     2   4784 my $self = shift;
130 2         6 local $@;
131 2         25 $self->stop();
132             }
133              
134             1;
135             __END__