File Coverage

blib/lib/Test/Instance/Apache.pm
Criterion Covered Total %
statement 62 100 62.0
branch 4 16 25.0
condition n/a
subroutine 22 28 78.5
pod 6 6 100.0
total 94 150 62.6


line stmt bran cond sub pod time code
1             package Test::Instance::Apache;
2              
3 4     4   276730 use Moo;
  4         35756  
  4         19  
4 4     4   6551 use File::Temp;
  4         43529  
  4         238  
5 4     4   21 use File::Spec;
  4         11  
  4         68  
6 4     4   1475 use File::Which qw/ which /;
  4         2488  
  4         181  
7 4     4   1816 use IPC::System::Simple qw/ capture /;
  4         33556  
  4         225  
8 4     4   1541 use Net::EmptyPort qw/ empty_port /;
  4         68094  
  4         205  
9 4     4   1665 use IO::All;
  4         29134  
  4         20  
10              
11 4     4   1626 use Test::Instance::Apache::Config;
  4         9  
  4         115  
12 4     4   1541 use Test::Instance::Apache::Modules;
  4         7  
  4         95  
13              
14 4     4   19 use namespace::clean;
  4         4  
  4         27  
15              
16             our $VERSION = '0.001';
17              
18             =head1 NAME
19              
20             Test::Instance::Apache - Create Apache instance for Testing
21              
22             =head1 SYNOPSIS
23              
24             use FindBin qw/ $Bin /;
25             use Test::Instance::Apache;
26              
27             my $instance = Test::Instance::Apache->new(
28             config => [
29             "VirtualHost *" => [
30             DocumentRoot => "$Bin/root",
31             ],
32             ],
33             modules => [ qw/ mpm_prefork authz_core mime / ],
34             );
35              
36             $instance->run;
37              
38             =head1 DESCRIPTION
39              
40             Test::Instance::Apache allows you to spin up a complete Apache instance for
41             testing. This is useful when developing various plugins for Apache, or if your
42             application is tightly integrated to the webserver.
43              
44             =head2 Attributes
45              
46             These are the attributes available on Test::Instance::Apache.
47              
48             =cut
49              
50             has _temp_dir => (
51             is => 'lazy',
52             builder => sub {
53 2     2   576 return File::Temp->newdir;
54             },
55             );
56              
57             =head3 server_root
58              
59             The root folder for creating the Apache instance. This folder is passed to
60             Apache during instantiation as the server root configuration, and normally
61             contains all the configuration files for Apache. If not set during object
62             creation, a new folder will be created using File::Temp.
63              
64             =cut
65              
66             has server_root => (
67             is => 'lazy',
68             builder => sub {
69 2     2   554 my $self = shift;
70 2         8 return $self->_temp_dir->dirname;
71             },
72             );
73              
74             =head3 conf_dir
75              
76             The directory for holding the configuration files. Defaults to
77             C<$server_root/conf>. If set during object creation, then you will need to
78             create the folder manually.
79              
80             =cut
81              
82             has conf_dir => (
83             is => 'lazy',
84             builder => sub {
85 2     2   539 my $self = shift;
86 2         5 return $self->make_server_dir( 'conf' );
87             },
88             );
89              
90             =head3 log_dir
91              
92             The directory for holding all the log files. Defaults to C<$server_root/logs>.
93             If set during object creation, then you will need to create the folder
94             manually.
95              
96             =cut
97              
98             has log_dir => (
99             is => 'lazy',
100             builder => sub {
101 0     0   0 my $self = shift;
102 0         0 return $self->make_server_dir( 'logs' );
103             },
104             );
105              
106             =head3 conf_file_path
107              
108             The path to the main configuration file. Defaults to C<$conf_dir/httpd.conf>.
109             This is then used by L to create the base
110             configuration file.
111              
112             =cut
113              
114             has conf_file_path => (
115             is => 'lazy',
116             builder => sub {
117 2     2   582 my $self = shift;
118 2         7 return File::Spec->catfile( $self->conf_dir, 'httpd.conf' );
119             },
120             );
121              
122             has _config_manager => (
123             is => 'lazy',
124             builder => sub {
125 2     2   639 my $self = shift;
126             return Test::Instance::Apache::Config->new(
127             filename => $self->conf_file_path,
128             config => [
129             PidFile => $self->pid_file_path,
130             Listen => $self->listen_port,
131 2         2516 @{$self->_module_manager->include_modules},
132 2         9 @{$self->config},
  2         22  
133             ]
134             );
135             },
136             );
137              
138             =head3 config
139              
140             Takes an arrayref of values to pass to L.
141              
142             =cut
143              
144             has config => (
145             is => 'ro',
146             default => sub { return [] },
147             );
148              
149             =head3 modules
150              
151             Takes an arrayref of modules to load into Apache. These are the same names as
152             they appear in C, so only the modules which are available on your
153             local machine can be used.
154              
155             =cut
156              
157             has modules => (
158             is => 'ro',
159             required => 1,
160             isa => sub { die "modules must be an array!\n" unless ref $_[0] eq 'ARRAY' },
161             );
162              
163             has _module_manager => (
164             is => 'lazy',
165             builder => sub {
166 2     2   631 my $self = shift;
167 2         47 return Test::Instance::Apache::Modules->new(
168             modules => $self->modules,
169             server_root => $self->server_root,
170             );
171             },
172             );
173              
174             =head3 pid_file_path
175              
176             Path to the pid file for Apache. Defaults to C<$server_root/httpd.pid>.
177              
178             =cut
179              
180             has pid_file_path => (
181             is => 'lazy',
182             builder => sub {
183 2     2   667 my $self = shift;
184 2         30 return File::Spec->catfile( $self->server_root, 'httpd.pid' );
185             },
186             );
187              
188             =head3 listen_port
189              
190             Port for Apache master process to listen on. If not set, will use
191             L to find an unused high-number port.
192              
193             =cut
194              
195             has listen_port => (
196             is => 'lazy',
197             builder => sub {
198 2     2   602 return empty_port;
199             },
200             );
201              
202             =head3 apache_httpd
203              
204             Path to the main Apache process. Uses L to determine the
205             path of the binary from your C<$PATH>.
206              
207             =cut
208              
209             has apache_httpd => (
210             is => 'lazy',
211             builder => sub {
212 2     2   360 my ($httpd) = do {
213             local $ENV{PATH} = join( ':',
214             map {
215 7         8 my $copy = $_;
216 7 100       42 ( $copy =~ s!/bin$!/sbin!
217             ? ( $copy,$_ )
218             : $_
219             )
220             } split ':', $ENV{PATH}
221 2         11 );
222 2         10 grep defined, map scalar( which $_ ), qw/ httpd apache apache2 /;
223             };
224 2 50       711 return $httpd if defined $httpd;
225 2         50 die "Apache server program not found - please check your path\n";
226             },
227             );
228              
229             =head3 pid
230              
231             Pid number for the main Apache process. Set during L and then used during
232             L to kill the correct process.
233              
234             =cut
235              
236             has pid => ( is => 'rwp' );
237              
238             sub _httpd_cmd {
239 0     0   0 my $self = shift;
240              
241 0         0 return join ( ' ', $self->apache_httpd,
242             '-d', $self->server_root,
243             '-f', $self->conf_file_path,
244             );
245             }
246              
247             =head2 Methods
248              
249             These are the various methods inside this module either for internal or basic
250             usage.
251              
252             =head3 run
253              
254             Sets up all the pre-required folders, writes the config files, loads the
255             required modules, and then starts Apache itself.
256              
257             =cut
258              
259             sub run {
260 2     2 1 23 my $self = shift;
261              
262 2         7 $self->_config_manager->write_config;
263 2         12023 $self->_module_manager->load_modules;
264 0         0 $self->log_dir;
265              
266             # capture will wait until the standard apache fork has finished
267 0         0 capture( $self->_httpd_cmd );
268              
269 0         0 for (1 .. 10) {
270 0         0 $self->_set_pid( $self->get_pid );
271 0 0       0 last if defined $self->pid;
272 0         0 sleep 1;
273             }
274             }
275              
276             =head3 make_server_dir
277              
278             Used internally to create folders under the server root. Will take an array of
279             directory names, which are then passed to File::Spec - so if you do the
280             following:
281              
282             $instance->make_server_dir( 'a', 'b', 'c' );
283              
284             Then a path of C<$server_root/a/b/c> will be created.
285              
286             =cut
287              
288             sub make_server_dir {
289 2     2 1 5 my ( $self, @dirnames ) = @_;
290 2         7 my $dir = File::Spec->catdir( $self->server_root, @dirnames );
291 2         1107 mkdir $dir;
292 2         31 return $dir;
293             }
294              
295             =head3 get_pid
296              
297             Returns the contents of the first line of the pid file. Used internally to set
298             the pid after startup.
299              
300             =cut
301              
302             sub get_pid {
303 0     0 1 0 my $self = shift;
304              
305 0         0 my $pid = undef;
306 0 0       0 if ( -f $self->pid_file_path ) {
307 0         0 open( my $fh, '<', $self->pid_file_path );
308 0         0 $pid = <$fh>; # read first line
309 0         0 chomp $pid;
310 0         0 close $fh;
311             }
312 0         0 return $pid;
313             }
314              
315             =head3 get_logs
316              
317             This will return all the items in the log directory as a hashref of filename
318             and content. This is useful either during test development, or if you are
319             testing exceptions on your application. Please note that it does not recurse
320             subdirectories in the logs folder.
321              
322             =cut
323              
324             sub get_logs {
325 0     0 1 0 my $self = shift;
326              
327 0         0 my $logs = {};
328 0         0 my @files = io->dir( $self->log_dir )->all;
329 0         0 for my $file ( @files ) {
330 0         0 $logs->{ $file->filename } = $file->slurp;
331             }
332              
333 0         0 return $logs;
334             }
335              
336             =head3 debug
337              
338             This is more for use during development of this module - prints out the path of
339             all the files and folders stored as attributes in this module.
340              
341             =cut
342              
343             sub debug {
344 0     0 1 0 my $self = shift;
345 0         0 for my $item ( qw/ server_root conf_dir conf_file_path apache_httpd / ) {
346 0         0 my $string = sprintf( "%16s | [%s]\n", $item, $self->$item );
347 0         0 print $string;
348             }
349             }
350              
351             =head3 DEMOLISH
352              
353             Kills the Apache instance started during run.
354              
355             =cut
356              
357             sub DEMOLISH {
358 4     4 1 7967 my $self = shift;
359              
360 4 50       78 if ( my $pid = $self->pid ) {
361             # print "Killing apache with pid " . $pid . "\n";
362 0           for my $signal ( qw/ TERM TERM INT KILL / ) {
363 0           $self->_kill_pid($signal);
364 0           for ( 1..10 ) {
365 0 0         last unless $self->_kill_pid( 0 );
366 0           sleep 1;
367             }
368 0 0         last unless $self->_kill_pid( 0 );
369             }
370             }
371             }
372              
373             sub _kill_pid {
374 0     0     my ( $self, $signal ) = @_;
375              
376             #print "Signal [" . $signal . "]\n";
377             #print "Pid [" . $self->pid . "]\n";
378 0 0         return undef unless $self->pid;
379 0           my $ret = kill $signal, $self->pid;
380             #print "Kill Return code: [" . $ret . "]\n";
381 0           return $ret;
382             }
383              
384             =head1 AUTHOR
385              
386             Tom Bloor Et.bloor@shadowcat.co.ukE
387              
388             Initial development sponsored by L
389              
390             =head1 COPYRIGHT
391              
392             Copyright 2016 Tom Bloor
393              
394             =head1 LICENCE
395              
396             This library is free software; you can redistribute it and/or modify
397             it under the same terms as Perl itself.
398              
399             =head1 SEE ALSO
400              
401             =over
402              
403             =item * L
404              
405             =item * L
406              
407             =item * L
408              
409             =back
410              
411             =cut
412              
413             1;