File Coverage

blib/lib/AxKit2/Test.pm
Criterion Covered Total %
statement 25 117 21.3
branch 0 38 0.0
condition 0 17 0.0
subroutine 9 22 40.9
pod n/a
total 34 194 17.5


line stmt bran cond sub pod time code
1             # Copyright 2001-2006 The Apache Software Foundation
2             #
3             # Licensed under the Apache License, Version 2.0 (the "License");
4             # you may not use this file except in compliance with the License.
5             # You may obtain a copy of the License at
6             #
7             # http://www.apache.org/licenses/LICENSE-2.0
8             #
9             # Unless required by applicable law or agreed to in writing, software
10             # distributed under the License is distributed on an "AS IS" BASIS,
11             # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12             # See the License for the specific language governing permissions and
13             # limitations under the License.
14             #
15              
16             package AxKit2::Test;
17              
18 9     9   6727 use strict;
  9         18  
  9         414  
19 9     9   46 use warnings;
  9         14  
  9         222  
20 9     9   11237 use Encode;
  9         142261  
  9         905  
21              
22 9     9   9237 use IO::Socket;
  9         266210  
  9         51  
23 9     9   16647 use LWP::UserAgent;
  9         524015  
  9         321  
24 9     9   97 use File::Spec;
  9         19  
  9         245  
25 9     9   52 use base 'Test::Builder::Module';
  9         19  
  9         9534  
26              
27             our @EXPORT = qw(start_server stop_server http_get
28             content_is content_matches content_doesnt_match
29             status_is is_redirect no_redirect header_is
30             skip plan);
31             our $VERSION = 0.01;
32              
33             # Module to assist with testing
34              
35             my $ua = LWP::UserAgent->new;
36             $ua->agent(__PACKAGE__."/".$VERSION);
37              
38             my $server_port = 54000;
39              
40             sub get_free_port {
41 0 0   0     die "No ports free" if $server_port == 65534;
42            
43 0           while (IO::Socket::INET->new(PeerAddr => "localhost:$server_port")) {
44 0           $server_port++;
45             }
46 0 0         if (IO::Socket::INET->new(PeerAddr => "localhost", PeerPort => $server_port+1)) {
47             # server port free, console port isn't
48 0           $server_port += 2;
49 0           return get_free_port();
50             }
51 0           return $server_port;
52             }
53              
54             my $server;
55              
56             =head2 start_server |
57              
58             This takes either a configuration file excerpt as a string (anything that goes inside a block),
59             or the document root, a list of plugins to load and a list of other configuration directives.
60              
61             =cut
62              
63             sub start_server {
64 0     0     my ($docroot, $plugins, $directives) = @_;
65            
66 0           my $port = get_free_port();
67            
68 0 0         if (defined $plugins) {
69 0   0       $directives ||= [];
70 0           $docroot = File::Spec->rel2abs($docroot);
71 0           $server = AxKit2::Test::Server->new($port,"DocumentRoot '$docroot'\n" .
72 0           join("\n",map { "Plugin $_" } @$plugins) . "\n" .
73             join("\n",@$directives) . "\n");
74             } else {
75 0           $server = AxKit2::Test::Server->new($port, $docroot);
76             }
77              
78 0           return $server;
79             }
80              
81             sub stop_server {
82 0     0     $server->shutdown();
83 0           undef $server;
84             }
85              
86             sub http_get {
87 0     0     my ($url) = @_;
88 0 0         $url = "http://localhost:$server_port$url" if $url !~ m/^[a-z0-9]{1,6}:/i;
89 0           my $req = new HTTP::Request(GET => $url);
90 0           return ($req, $ua->request($req));
91             }
92              
93             sub plan {
94 0     0     my $builder = __PACKAGE__->builder;
95 0           return $builder->plan(@_);
96             }
97              
98             sub skip {
99 0     0     my $builder = __PACKAGE__->builder;
100 0           return $builder->skip(@_);
101             }
102              
103             sub content_is {
104 0     0     my ($url, $content, $name, $ignore) = @_;
105 0           my $builder = __PACKAGE__->builder;
106 0           my $res = http_get($url);
107 0 0 0       if (!$ignore && !$res->is_success) {
108 0           $builder->ok(0,$name);
109 0           $builder->diag("Request for '${url}' failed with error code ".$res->status_line);
110 0           return 0;
111             }
112 0           my $got = $res->content;
113 0           $got =~ s/[\r\n]*$//;
114 0           $content =~ s/[\r\n]*$//;
115 0 0         $builder->is_eq($got, $content, $name) or $builder->diag("Request URL: ${url}");
116             }
117              
118             sub header_is {
119 0     0     my ($url, $header, $content, $name, $ignore) = @_;
120 0           my $builder = __PACKAGE__->builder;
121 0           my $res = http_get($url);
122 0 0 0       if (!$ignore && !$res->is_success) {
123 0           $builder->ok(0,$name);
124 0           $builder->diag("Request for '${url}' failed with error code ".$res->status_line);
125 0           return 0;
126             }
127 0           my $got = $res->header($header);
128 0 0         $builder->is_eq($got, $content, $name) or $builder->diag("Request URL: ${url}");
129             }
130              
131             sub content_matches {
132 0     0     my ($url, $regex, $name, $ignore) = @_;
133 0           my $builder = __PACKAGE__->builder;
134 0           my $res = http_get($url);
135 0 0 0       if (!$ignore && !$res->is_success) {
136 0           $builder->ok(0,$name);
137 0           $builder->diag("Request for '${url}' failed with error code ".$res->status_line);
138 0           return 0;
139             }
140 0           my $got = decode_utf8($res->content);
141 0           $got =~ s/[\r\n]*$//;
142 0 0         $regex = qr($regex) unless ref($regex);
143 0 0         $builder->like($got, $regex, $name) or $builder->diag("Request URL: ${url}");
144             }
145              
146             sub content_doesnt_match {
147 0     0     my ($url, $regex, $name, $ignore) = @_;
148 0           my $builder = __PACKAGE__->builder;
149 0           my $res = http_get($url);
150 0 0 0       if (!$ignore && !$res->is_success) {
151 0           $builder->ok(0,$name);
152 0           $builder->diag("Request for '${url}' failed with error code ".$res->status_line);
153 0           return 0;
154             }
155 0           my $got = decode_utf8($res->content);
156 0           $got =~ s/[\r\n]*$//;
157 0 0         $regex = qr($regex) unless ref($regex);
158 0 0         $builder->unlike($got, $regex, $name) or $builder->diag("Request URL: ${url}");
159             }
160              
161             sub is_redirect {
162 0     0     my ($url, $dest, $name) = @_;
163 0           my $builder = __PACKAGE__->builder;
164 0           $ua->max_redirect(0);
165 0           $dest = "http://localhost:$server_port$dest";
166 0           my $res = http_get($url);
167 0           $ua->max_redirect(7);
168 0           my $got = $res->code;
169 0           my $gotdest = $res->header('Location');
170 0 0 0       $builder->ok($res->is_redirect && $dest eq $gotdest, $name) or $builder->diag("Request for '${url}' failed:" .
    0          
    0          
171             ($res->is_redirect? "" : "\n got status: $got, expected a redirect") .
172             ($dest eq $gotdest? "" : "\n got destination: $gotdest\nexpected destination: $dest"));
173             }
174              
175             sub no_redirect {
176 0     0     my ($url, $name) = @_;
177 0           my $builder = __PACKAGE__->builder;
178 0           $ua->max_redirect(0);
179             #$dest = "http://localhost:$server_port$dest";
180 0           my $res = http_get($url);
181 0           $ua->max_redirect(7);
182 0           my $got = $res->code;
183 0           my $gotdest = $res->header('Location');
184 0 0         $builder->ok(!$res->is_redirect, $name) or $builder->diag("Request for '${url}' failed:
185             got status: $got -> $gotdest, expected non-redirect status");
186             }
187              
188             sub status_is {
189 0     0     my ($url, $status, $name) = @_;
190 0           my $builder = __PACKAGE__->builder;
191 0           my $res = http_get($url);
192 0           my $got = $res->code;
193 0 0         $builder->is_num($got, $status, $name) or $builder->diag("Request URL: ${url}");
194             }
195              
196             package AxKit2::Test::Server;
197              
198 9     9   158725 use File::Temp qw(tempfile);
  9         135844  
  9         716  
199 9     9   5113 use AxKit2;
  0            
  0            
200              
201             sub new {
202             my $class = shift;
203             my ($port, $config) = @_;
204            
205             my ($fh, $filename) = tempfile();
206            
207             my $self = bless {
208             port => $port,
209             console_port => $port + 1,
210             config_file => $filename,
211             }, $class;
212            
213             $self->setup_config($fh, $config);
214            
215             pipe(READER, WRITER) || die "cannot create pipe: $!";
216            
217             my $child = fork;
218             die "fork failed" unless defined $child;
219             if ($child) {
220             $self->{child_pid} = $child;
221             close WRITER;
222             my $line = ;
223             return $self;
224             }
225            
226             # child
227             close READER;
228             Danga::Socket->AddTimer(0, sub { print WRITER "READY\n"; close(WRITER); });
229             AxKit2->run($filename);
230             exit;
231             }
232              
233             sub setup_config {
234             my ($self, $fh, $config) = @_;
235            
236             my $port = $self->{port};
237             my $console = $self->{console_port};
238            
239             print $fh <
240             Plugin logging/file
241             LogFile test.log
242             LogLevel LOGDEBUG
243              
244             # setup console
245             ConsolePort $console
246             Plugin stats
247              
248             Plugin error_xml
249             ErrorStylesheet demo/error.xsl
250             StackTrace On
251              
252            
253             Port $port
254            
255             EOT
256             print $fh $config;
257            
258             print $fh <
259              
260            
261             EOT
262            
263             seek($fh, 0, 0);
264             }
265              
266             sub DESTROY {
267             my $self = shift;
268            
269             $self->shutdown;
270             }
271              
272             sub shutdown {
273             my $self = shift;
274            
275             return unless $self->{child_pid};
276            
277             unlink($self->{config_file});
278            
279             my $conf = IO::Socket::INET->new(
280             PeerAddr => "127.0.0.1",
281             PeerPort => $self->{console_port},
282             ) || die "Cannot connect to console port $self->{console_port} : $!";
283              
284             IO::Handle::blocking($conf, 0);
285            
286             $conf->print("shutdown\n");
287            
288             my $buf;
289             read($conf, $buf, 128 * 1024);
290            
291             use POSIX ":sys_wait_h";
292             my $kid;
293             do {
294             $kid = waitpid(-1, WNOHANG);
295             } until $kid > 0;
296            
297             delete $self->{child_pid};
298             }
299              
300             1;