File Coverage

blib/lib/Test/Instance/DNS.pm
Criterion Covered Total %
statement 49 51 96.0
branch 10 14 71.4
condition n/a
subroutine 13 14 92.8
pod 0 3 0.0
total 72 82 87.8


line stmt bran cond sub pod time code
1             package Test::Instance::DNS;
2              
3 1     1   705260 use Moo;
  1         14162  
  1         6  
4 1     1   5192 use IPC::System::Simple qw/ system /;
  1         4751  
  1         68  
5 1     1   535 use Net::EmptyPort qw/ empty_port /;
  1         3774  
  1         71  
6 1     1   713 use File::Temp;
  1         10122  
  1         729  
7              
8             our $VERSION = '0.001';
9              
10             =head1 NAME
11              
12             Test::Instance::DNS - Mock DNS server for testing
13              
14             =head1 SYNOPSIS
15              
16             use Test::More;
17             use Test::DNS;
18             use Test::Instance::DNS;
19              
20             my $t_i_dns = Test::Instance::DNS->new(
21             listen_addr => '127.0.0.1',
22             zone_file => 't/etc/db.example.com',
23             );
24              
25             $t_i_dns->run;
26              
27             my $dns = Test::DNS->new(nameservers => ['127.0.0.1']);
28             $dns->object->port($t_i_dns->listen_port);
29              
30             $dns->is_a('example.com' => '192.0.2.1');
31              
32             done_testing;
33              
34             =head1 DESCRIPTION
35              
36             Provides a local mock DNS server usable for testing.
37              
38             =cut
39              
40             has listen_port => (
41             is => 'lazy',
42             builder => sub {
43 1     1   14 return empty_port;
44             },
45             );
46              
47             has listen_addr => (
48             is => 'lazy',
49             coerce => sub {
50             ref( $_[0] ) eq 'ARRAY' ? $_[0] : [ $_[0] ];
51             },
52             builder => sub {
53 0     0   0 return ['::1', '127.0.0.1' ],
54             },
55             );
56              
57             has zone_file => (
58             is => 'ro',
59             required => 1,
60             );
61              
62             has nameserver => (
63             is => 'lazy',
64             builder => sub {
65 1     1   11 my $self = shift;
66 1         3 my $module = __PACKAGE__ . '::Server';
67 1         10 s/::/\//g, s/$/.pm/ for $module;
68 1 50       483 if ( require $module ) {
69 1         33 return $INC{$module};
70             }
71 0         0 die "Couldnt find $module";
72             },
73             );
74              
75             has pid => ( is => 'rwp' );
76              
77             has _temp_dir => (
78             is => 'lazy',
79             builder => sub {
80 1     1   17 return File::Temp->newdir;
81             },
82             );
83              
84             has pid_file_path => (
85             is => 'lazy',
86             builder => sub {
87 1     1   2039 my $self = shift;
88 1         20 return File::Spec->catfile( $self->_temp_dir->dirname, 'server.pid' );
89             },
90             );
91              
92             sub _nameserver_cmd {
93 1     1   3 my $self = shift;
94              
95 1         19 return join ( ' ',
96             'perl', $self->nameserver,
97             'run',
98             '--listen_port', $self->listen_port,
99             '--zone', $self->zone_file,
100             '--pid', $self->pid_file_path,
101             '&',
102             );
103             }
104              
105             sub run {
106 1     1 0 19 my $self = shift;
107              
108 1         4 system( $self->_nameserver_cmd );
109              
110 1         5750 for (1 .. 10) {
111 2         56 $self->_set_pid( $self->get_pid );
112 2 100       31 last if defined $self->pid;
113 1         1000195 sleep 1;
114             }
115             }
116              
117             sub get_pid {
118 2     2 0 21 my $self = shift;
119              
120 2         13 my $pid = undef;
121 2 100       225 if ( -f $self->pid_file_path ) {
122 1         159 open( my $fh, '<', $self->pid_file_path );
123 1         189 $pid = <$fh>; # read first line
124 1         10 chomp $pid;
125 1         27 close $fh;
126             }
127 2         113 return $pid;
128             }
129              
130             sub DEMOLISH {
131 1     1 0 46284 my $self = shift;
132            
133 1 50       52 if ( my $pid = $self->pid ) {
134             # print "Killing nameserver with pid " . $pid . "\n";
135 1         14 for my $signal ( qw/ TERM TERM INT KILL / ) {
136 1         7 $self->_kill_pid($signal);
137 1         6 for ( 1..10 ) {
138 2 100       22 last unless $self->_kill_pid( 0 );
139 1         1000158 sleep 1;
140             }
141 1 50       8 last unless $self->_kill_pid( 0 );
142             }
143             }
144             }
145            
146             sub _kill_pid {
147 4     4   17 my ( $self, $signal ) = @_;
148            
149             #print "Signal [" . $signal . "]\n";
150             #print "Pid [" . $self->pid . "]\n";
151 4 50       30 return unless $self->pid;
152 4         253 my $ret = kill $signal, $self->pid;
153             #print "Kill Return code: [" . $ret . "]\n";
154 4         97 return $ret;
155             }
156              
157             1;
158              
159             =head1 AUTHOR
160            
161             Tom Bloor E<lt>t.bloor@shadowcat.co.ukE<gt>
162            
163             =head1 COPYRIGHT
164            
165             Copyright 2018- Tom Bloor
166            
167             =head1 LICENSE
168            
169             This library is free software; you can redistribute it and/or modify
170             it under the same terms as Perl itself.
171            
172             =head1 SEE ALSO
173            
174             L<Test::DNS> L<Net::DNS>
175            
176             =cut