File Coverage

blib/lib/Test/ttserver.pm
Criterion Covered Total %
statement 42 109 38.5
branch 6 52 11.5
condition 1 16 6.2
subroutine 13 24 54.1
pod 12 12 100.0
total 74 213 34.7


line stmt bran cond sub pod time code
1             package Test::ttserver;
2              
3 7     7   344490 use strict;
  7         20  
  7         342  
4 7     7   40 use warnings;
  7         12  
  7         296  
5 7     7   38 use Cwd;
  7         19  
  7         605  
6 7     7   10786 use File::Temp;
  7         251228  
  7         746  
7 7     7   66 use File::Path;
  7         15  
  7         357  
8 7     7   7645 use IO::File;
  7         9170  
  7         1450  
9 7     7   8084 use POSIX qw/SIGTERM WNOHANG :sys_wait_h/;
  7         69058  
  7         82  
10 7     7   20047 use Time::HiRes 'sleep';
  7         19075  
  7         55  
11 7     7   18880 use Test::TCP;
  7         393014  
  7         29032  
12              
13             our $VERSION = '0.003';
14             $VERSION = eval $VERSION;
15              
16             our $errstr;
17             our @SearchPaths = qw(/usr/bin /usr/local/bin);
18             our @BooleanArgs = qw(dmn kl ld le uas rcc);
19             our %Defaults = (
20             debug => 0,
21             auto_start => 1,
22             base_dir => undef,
23             bin => undef,
24             );
25              
26             sub new {
27 4     4 1 2174 my $class = shift;
28 4   50     33 my $dbname = shift || '';
29 0         0 my $self = bless +{
30             %Defaults,
31 4 50       53 @_ == 1 ? %{$_[0]} : @_,
32             }, $class;
33              
34 4 50       24 $self->{'bin'} = $self->_find_bin or return;
35              
36 0 0 0     0 if ( defined $self->{'base_dir'} && $self->{'base_dir'} !~ m{^/} ) {
37 0         0 $self->{'base_dir'} = getcwd . '/' . $self->{'base_dir'}
38             } else {
39 0 0       0 $self->{'base_dir'} = File::Temp::tempdir(
40             CLEANUP => $ENV{TEST_TTSERVE_PRESERVE} ? undef : 1,
41             );
42             }
43              
44 0         0 my %args;
45 0         0 for (keys %$self) {
46 0 0       0 $args{$_} = delete $self->{$_} unless exists $Defaults{$_};
47             }
48 0         0 $args{'pid'} = $self->{'base_dir'} . '/tmp/pid';
49 0   0     0 $args{'host'} ||= $self->host;
50 0   0     0 $args{'port'} ||= $self->port;
51 0         0 $self->{'args'} = \%args;
52              
53 0 0       0 $self->{'dbname'} = $dbname ? $self->{'base_dir'} .'/tmp/'. $dbname : '';
54              
55 0 0       0 if ( $self->{'auto_start'} ) {
56 0         0 $self->setup;
57 0         0 $self->start;
58             }
59              
60 0         0 return $self;
61             }
62              
63             sub DESTROY {
64 4     4   11 my $self = shift;
65 4 50       22 $self->stop if defined $self->pid;
66             }
67              
68             sub setup {
69 0     0 1 0 my $self = shift;
70 0         0 mkpath( $self->{'base_dir'} . '/tmp', { verbose => $self->{'debug'} });
71             }
72              
73             sub start {
74 0     0 1 0 my $self = shift;
75 0 0       0 return if defined $self->pid;
76              
77 0         0 my $log_file = $self->{'base_dir'} . '/tmp/ttserver.log';
78 0 0       0 my $log_fh = IO::File->new($log_file, O_WRONLY|O_APPEND|O_CREAT)
79             or die qq/failed to create log file: $! "$log_file"/;
80              
81 0         0 my $pid = fork;
82 0 0       0 die qq/failed to fork: $!/ unless defined $pid;
83              
84 0 0       0 if ( $pid == 0 ) {
85 0         0 my $bin = $self->{'bin'};
86 0         0 my $args = $self->{'args'};
87 0         0 my %bool; @bool{@BooleanArgs} = (1) x @BooleanArgs;
  0         0  
88 0 0       0 my @command = (
89             $self->{'bin'},
90             ( map {
91 0         0 $bool{$_} ? "-$_" : ("-$_" => $args->{$_})
92             } (keys %$args) ),
93             $self->{'dbname'},
94             );
95 0 0 0     0 pop @command unless @command and defined $command[-1];
96 0 0       0 warn "@command\n" if $self->{'debug'};
97 0 0       0 open STDOUT, '>&', $log_fh or die qq/failed to dup: $! "stdout"/;
98 0 0       0 open STDERR, '>&', $log_fh or die qq/failed to dup: $! "stderr"/;
99 0         0 exec @command;
100 0         0 die qq/failed to launch ttserver: $? "$bin"/;
101             }
102              
103 0         0 $log_fh->close;
104 0         0 while (! -e $self->{'args'}{'pid'}) {
105 0 0       0 if ( 0 < waitpid $pid, WNOHANG ) {
106 0         0 die qq/*** failed to launch ttserver ***\n/ . $self->_get_log( $log_file );
107             }
108 0         0 sleep 0.1;
109             }
110              
111 0         0 $self->{'child'} = $pid;
112             }
113              
114             sub stop {
115 0     0 1 0 my ( $self, $sig ) = @_;
116 0 0       0 return unless defined $self->pid;
117 0   0     0 $sig ||= SIGTERM;
118 0         0 kill $sig, $self->pid;
119 0         0 1 while ( 0 >= waitpid $self->pid, 0 );
120 0         0 my $is_exited = WIFEXITED( $? );
121 0         0 delete $self->{'child'};
122             # might remain for example when sending SIGKILL
123 0         0 unlink $self->{'args'}{'pid'};
124 0         0 return $is_exited;
125             }
126              
127 0     0 1 0 sub socket { ($_[0]->host, $_[0]->port) }
128 0 0   0 1 0 sub host { shift->{'args'}{'host'} || '127.0.0.1' }
129 0 0   0 1 0 sub port { shift->{'args'}{'port'} || empty_port }
130 4     4 1 37 sub pid { shift->{'child'} }
131 0 0   0 1 0 sub is_up { shift->{'child'} ? 1 : 0 }
132 0 0   0 1 0 sub is_down { shift->is_up ? 0 : 1 }
133 0     0 1 0 sub pid_file { shift->{'args'}{'pid'} }
134 0     0 1 0 sub args { shift->{'args'} }
135              
136             sub _find_bin {
137 4     4   12 my $self = shift;
138              
139 4         17 my @paths = @SearchPaths;
140 4 100       49 push @paths, split ':', $ENV{'PATH'} if defined $ENV{'PATH'};
141              
142 4         13 for my $path (@paths) {
143 27         55 my $bin = $path . '/ttserver';
144 27 50       549 return $bin if -x $bin;
145             }
146              
147 4         12 $errstr = "could not find ttserver, please set appropriate PATH";
148 4         67 return;
149             }
150              
151             sub _get_log {
152 0     0     my ( $self, $log_file ) = @_;
153 0           my $log = '';
154 0 0         if ( my $log_fh = IO::File->new($log_file, O_RDONLY) ) {
155 0           $log = do { local $/; <$log_fh> };
  0            
  0            
156 0           $log_fh->close;
157             }
158 0           return $log;
159             }
160              
161             1;
162              
163             __END__