File Coverage

blib/lib/Test/CGI/External.pm
Criterion Covered Total %
statement 374 590 63.3
branch 122 268 45.5
condition 9 39 23.0
subroutine 42 54 77.7
pod 17 43 39.5
total 564 994 56.7


line stmt bran cond sub pod time code
1             package Test::CGI::External;
2 6     6   356461 use 5.006;
  6         61  
3 6     5   125 use warnings;
  5         9  
  5         289  
4 5     5   29 use strict;
  5         10  
  5         134  
5 5     5   1431 use utf8;
  5         48  
  5         29  
6              
7 5     5   144 use Carp;
  5         10  
  5         297  
8 5     5   1879 use Encode 'decode';
  5         48977  
  5         431  
9 5     5   2669 use File::Temp 'tempfile';
  5         98381  
  5         399  
10 5     5   272 use FindBin '$Bin';
  5         711  
  5         531  
11 5     5   40 use Test::Builder;
  5         15  
  5         34531  
12              
13             our $VERSION = '0.23';
14              
15             sub new
16             {
17 6     6 1 5008 my %tester;
18              
19 6         52 my $tb = Test::Builder->new ();
20 6         66 $tester{tb} = $tb;
21             # $tester{html_validator} = '/home/ben/bin/validate';
22              
23 6         30 return bless \%tester;
24             }
25              
26             sub note
27             {
28 126     126 0 435 my ($self, $note) = @_;
29 126         684 my (undef, $file, $line) = caller ();
30 126 100       572 if ($self->{verbose}) {
31 29         234 $self->{tb}->note ("$file:$line: $note");
32             }
33             }
34              
35             sub on_off_msg
36             {
37 4     4 0 22 my ($self, $switch, $type) = @_;
38 4 100       27 if ($self->{verbose}) {
39 1         3 my $msg = "You have asked me to turn ";
40 1 50       4 if ($switch) {
41 1         5 $msg .= "on";
42             }
43             else {
44 0         0 $msg .= "off";
45             }
46 1         4 $msg .= " testing of $type";
47 1         6 my (undef, $file, $line) = caller ();
48 1         10 $self->{tb}->note ("$file:$line: $msg");
49             }
50             }
51              
52             sub set_cgi_executable
53             {
54 17     17 1 41973 my ($self, $cgi_executable, @command_line_options) = @_;
55 17         122 $self->note ("I am setting the CGI executable to be tested to '$cgi_executable'.");
56 17         1047 $self->do_test (-f $cgi_executable, "found executable $cgi_executable");
57 17 50       8883 if ($^O eq 'MSWin32') {
58             # These tests don't do anything useful on Windows, see
59             # http://perldoc.perl.org/perlport.html#-X
60 0         0 $self->pass_test ('Invalid test for MS Windows');
61             }
62             else {
63 17         462 $self->do_test (-x $cgi_executable, "$cgi_executable is executable");
64             }
65 17         7356 $self->{cgi_executable} = $cgi_executable;
66 17 100       68 if (@command_line_options) {
67 10         83 $self->{command_line_options} = \@command_line_options;
68             }
69             else {
70 7         28 $self->{command_line_options} = [];
71             }
72             }
73              
74             sub do_compression_test
75             {
76 4     4 1 6448 my ($self, $switch) = @_;
77 4         23 $switch = !! $switch;
78 4         75 $self->on_off_msg ($switch, "compression");
79 4         363 $self->{comp_test} = $switch;
80 4 100 66     41 if ($switch && ! $self->{_use_io_uncompress_gunzip}) {
81 3         370 eval "use Gzip::Faster;";
82 3 50       33 if ($@) {
83 0         0 $self->{_use_io_uncompress_gunzip} = 1;
84 0 0       0 if (! $self->{no_warn}) {
85 0         0 carp "Gzip::Faster is not installed, using IO::Uncompress::Gunzip";
86             }
87             }
88             }
89             }
90              
91             sub do_caching_test
92             {
93 0     0 1 0 my ($self, $switch) = @_;
94 0         0 $switch = !! $switch;
95 0         0 $self->on_off_msg ($switch, "if-modified/last-modified response");
96 0         0 $self->{cache_test} = $switch;
97 0 0       0 if ($switch) {
98 0         0 eval "use HTTP::Date;";
99 0 0       0 if ($@) {
100 0 0       0 if (! $self->{no_warn}) {
101 0         0 carp "HTTP::Date is not installed, cannot do caching test";
102             }
103 0         0 $self->{cache_test} = undef;
104             }
105             }
106             }
107              
108             sub expect_charset
109             {
110 4     4 1 41 my ($self, $charset) = @_;
111 4         385 eval "use Unicode::UTF8 qw/decode_utf8 encode_utf8/";
112 4 50       27 if ($@) {
113 0         0 Encode->import (qw/decode_utf8 encode_utf8/);
114 0 0 0     0 if (! $self->{no_warn} && ! $self->{_warned_unicode_utf8}) {
115 0         0 carp "Unicode::UTF8 is not installed, using Encode";
116 0         0 $self->{_warned_unicode_utf8} = 1;
117             }
118             }
119 4         31 $self->note ("You have told me to expect a 'charset' value of '$charset'.");
120 4         319 $self->{expected_charset} = $charset;
121             }
122              
123             sub expect_mime_type
124             {
125 2     2 1 20 my ($self, $mime_type) = @_;
126 2 50       9 if ($mime_type) {
127 2         10 $self->note ("You have told me to expect a mime type of '$mime_type'.");
128             }
129             else {
130 0         0 $self->note ("You have deleted the mime type.");
131             }
132 2         10 $self->{mime_type} = $mime_type;
133             }
134              
135             sub set_verbosity
136             {
137 1     1 1 11 my ($self, $verbosity) = @_;
138 1         10 $self->{verbose} = !! $verbosity;
139 1         8 $self->note ("You have asked me to print messages as I work.");
140             }
141              
142             sub set_no_warnings
143             {
144 0     0 1 0 my ($self, $onoff) = @_;
145 0         0 $self->{no_warn} = !! $onoff;
146 0         0 $self->on_off_msg ($onoff, "warnings");
147             }
148              
149             sub test_if_modified_since
150             {
151 0     0 0 0 my ($self, $last_modified) = @_;
152 0 0       0 die unless defined $last_modified;
153 0         0 my $saved = $ENV{HTTP_IF_MODIFIED_SINCE};
154 0         0 $ENV{HTTP_IF_MODIFIED_SINCE} = $last_modified;
155 0         0 $self->note ("Testing response with last modified time $last_modified");
156 0         0 my $saved_no_check_content = $self->{no_check_content};
157 0         0 $self->{no_check_content} = 1;
158             # Copy the hash of options into a private copy, so that we can run
159             # the thing again without overwriting our precious stuff.
160 0         0 my $saved_run_options = $self->{run_options};
161 0         0 my %run_options = %$saved_run_options;
162 0 0       0 if ($run_options{expect_errors}) {
163 0 0       0 if (! $self->{no_warn}) {
164 0         0 carp "The expect_errors option is often incompatible with do_caching_test (1), suggest switching off testing of caching when expecting errors";
165             }
166             }
167 0         0 $self->{run_options} = \%run_options;
168 0         0 my $saved_no_warn = $self->{no_warn};
169 0         0 $self->{no_warn} = 1;
170 0         0 run_private ($self);
171 0         0 $self->check_headers_private ($self);
172 0         0 $self->test_status (304);
173 0         0 my $body = $run_options{body};
174 0   0     0 $self->do_test (! defined ($body) || length ($body) == 0,
175             "No body returned with 304 response");
176 0         0 $ENV{HTTP_IF_MODIFIED_SINCE} = $saved;
177             # Restore our precious stuff.
178 0         0 $self->{run_options} = $saved_run_options;
179 0         0 $self->{no_warn} = $saved_no_warn;
180 0         0 $self->{no_check_content} = $saved_no_check_content;
181             }
182              
183             sub check_caching_private
184             {
185 0     0 0 0 my ($self) = @_;
186 0         0 my $output = $self->{run_options};
187 0         0 my $headers = $output->{headers};
188 0 0       0 if (! $headers) {
189 0         0 die "There are no headers in object, did the tests really run?";
190             }
191 0         0 my $last_modified = $headers->{'last-modified'};
192 0         0 $self->do_test ($last_modified, "Has last modified header");
193             # for my $k (keys %$headers) {
194             # print "$k $headers->{$k}\n";
195             # }
196 0         0 my $time = str2time ($last_modified);
197 0         0 $self->do_test (defined $time, "Last modified time can be parsed by HTTP::Date");
198 0 0       0 if ($last_modified) {
199 0         0 $self->test_if_modified_since ($last_modified);
200             }
201             else {
202 0         0 $self->note ("Not doing last modified test due to no-header failure");
203             }
204             # Restore the headers because they were overwritten when we did
205             # the caching test.
206 0         0 $output->{headers} = $headers;
207             }
208              
209             my @request_method_list = qw/POST GET HEAD/;
210             my %valid_request_method = map {$_ => 1} @request_method_list;
211              
212             sub check_request_method
213             {
214 18     18 0 59 my ($self, $request_method) = @_;
215 18         61 my $default_request_method = 'GET';
216 18 50       65 if ($request_method) {
217 18 50 33     348 if ($request_method && ! $valid_request_method{$request_method}) {
218 0 0       0 if (! $self->{no_warn}) {
219 0         0 carp "You have set the request method to a value '$request_method' which is not one of the ones I know about, which are ", join (', ', @request_method_list), " so I am setting it to the default, '$default_request_method'";
220             }
221 0         0 $request_method = $default_request_method;
222             }
223             }
224             else {
225 0 0       0 if (! $self->{no_warn}) {
226 0         0 carp "You have not set the request method, so I am setting it to the default, '$default_request_method'";
227             }
228 0         0 $request_method = $default_request_method;
229             }
230 18         64 return $request_method;
231             }
232              
233             sub do_test
234             {
235 122     122 0 539 my ($self, $test, $message) = @_;
236 122         1125 $self->{tb}->ok ($test, $message);
237             }
238              
239             # Register a successful test (deprecated legacy from pre-Test::Builder days)
240              
241             sub pass_test
242             {
243 59     59 0 246 my ($self, $test) = @_;
244 59         473 $self->{tb}->ok (1, $test);
245             }
246              
247             # Fail a test and keep going (deprecated legacy from pre-Test::Builder days)
248              
249             sub fail_test
250             {
251 4     4 0 26 my ($self, $test) = @_;
252 4         64 $self->{tb}->ok (0, $test);
253             }
254              
255             # Print the TAP plan
256              
257             sub plan
258             {
259 0     0 1 0 my ($self) = @_;
260 0         0 $self->{tb}->done_testing ();
261             }
262              
263             # Fail a test which means that we cannot keep going.
264              
265             sub abort_test
266             {
267 0     0 0 0 my ($self, $test) = @_;
268 0         0 $self->{tb}->skip_all ($test);
269             }
270              
271             # Set an environment variable, with warning about collisions.
272              
273             sub setenv_private
274             {
275 49     49 0 132 my ($self, $name, $value) = @_;
276 49 100       143 if (! $self->{set_env}) {
277 20         70 $self->{set_env} = [$name];
278             }
279             else {
280 29         48 push @{$self->{set_env}}, $name;
  29         93  
281             }
282 49 50       202 if ($ENV{$name}) {
283 0 0       0 if (! $self->{no_warn}) {
284 0         0 carp "A variable '$name' is already set in the environment.\n";
285             }
286             }
287 49         288 $ENV{$name} = $value;
288             }
289              
290             sub encode_utf8_safe
291             {
292 1     1 0 2 my ($self) = @_;
293 1         3 my $input = $self->{input};
294 1         58 eval "use Unicode::UTF8;";
295 1 50       4 if ($@) {
296 0 0 0     0 if (! $self->{no_warn} && ! $self->{_warned_unicode_utf8}) {
297 0         0 carp "Unicode::UTF8 is not installed, using Encode";
298 0         0 $self->{_warned_unicode_utf8} = 1;
299             }
300             # Encode::encode_utf8 uses prototypes so we have to hassle this up.
301 0         0 return Encode::encode_utf8 ($input);
302             }
303 1         9 return Unicode::UTF8::encode_utf8 ($input);
304             }
305              
306             # Internal routine to run a CGI program.
307              
308             sub run_private
309             {
310 20     20 0 57 my ($self) = @_;
311              
312             # Pull everything out of the object and into normal variables.
313              
314 20         66 my $verbose = $self->{verbose};
315 20         42 my $options = $self->{run_options};
316 20         59 my $cgi_executable = $self->{cgi_executable};
317 20         50 my $comp_test = $self->{comp_test};
318              
319             # Hassle up the CGI inputs, including environment variables, from
320             # the options the user has given.
321              
322             # mwforum requires GATEWAY_INTERFACE to be set to CGI/1.1
323             # setenv_private ($o, 'GATEWAY_INTERFACE', 'CGI/1.1');
324              
325 20         48 my $query_string = $options->{QUERY_STRING};
326 20 50       64 if (defined $query_string) {
327 0         0 $self->note ("I am setting the query string to '$query_string'.");
328 0         0 setenv_private ($self, 'QUERY_STRING', $query_string);
329             }
330             else {
331 20         86 $self->note ("There is no query string.");
332 20         1532 setenv_private ($self, 'QUERY_STRING', "");
333             }
334              
335 20         42 my $request_method;
336 20 100       62 if ($options->{no_check_request_method}) {
337 2         4 $request_method = $options->{REQUEST_METHOD};
338             }
339             else {
340 18         103 $request_method = $self->check_request_method ($options->{REQUEST_METHOD});
341             }
342 20         111 $self->note ("The request method is '$request_method'.");
343 20         1197 setenv_private ($self, 'REQUEST_METHOD', $request_method);
344 20         49 my $content_type = $options->{CONTENT_TYPE};
345 20 50       61 if ($content_type) {
346 0         0 $self->note ("The content type is '$content_type'.");
347 0         0 setenv_private ($self, 'CONTENT_TYPE', $content_type);
348             }
349 20 50       73 if ($options->{HTTP_COOKIE}) {
350 0         0 setenv_private ($self, 'HTTP_COOKIE', $options->{HTTP_COOKIE});
351             }
352 20         53 my $remote_addr = $self->{run_options}->{REMOTE_ADDR};
353 20 50       64 if ($remote_addr) {
354 0         0 $self->note ("I am setting the remote address to '$remote_addr'.");
355 0         0 setenv_private ($self, 'REMOTE_ADDR', $remote_addr);
356             }
357 20 100       82 if (defined $options->{input}) {
358 3         18 $self->{input} = $options->{input};
359 3 100       19 if (utf8::is_utf8 ($self->{input})) {
360 1         3 $self->{input} = $self->encode_utf8_safe ();
361             }
362 3 50       12 if ($self->{bad_content_length}) {
363 0         0 setenv_private ($self, 'CONTENT_LENGTH', '0');
364             }
365             else {
366 3         10 my $content_length = length ($self->{input});
367 3         12 setenv_private ($self, 'CONTENT_LENGTH', $content_length);
368 3         19 $self->note ("I am setting the CGI program's standard input to a string of length $content_length taken from the input options.");
369 3         597 $options->{content_length} = $content_length;
370             }
371             }
372              
373 20 100       62 if ($comp_test) {
374 6 100       22 if ($verbose) {
375 4         25 $self->{tb}->note ("I am requesting gzip encoding from the CGI executable.\n");
376             }
377 6         1197 setenv_private ($self, 'HTTP_ACCEPT_ENCODING', 'gzip, fake');
378             }
379              
380             # Actually run the executable under the current circumstances.
381              
382 20         61 my @cmd = ($cgi_executable);
383 20 50       68 if ($self->{command_line_options}) {
384 20         39 push @cmd, @{$self->{command_line_options}};
  20         63  
385             }
386 20         141 $self->note ("I am running '@cmd'");
387 20         1234 $self->run3 (\@cmd);
388 20         224 $options->{output} = $self->{output};
389 20         95 $options->{error_output} = $self->{errors};
390 20         323 $options->{exit_code} = $?;
391 20         403 $self->note (sprintf ("The program has now finished running. There were %d bytes of output.", length ($self->{output})));
392 20 50       2698 if ($options->{expect_failure}) {
393             }
394             else {
395 20         179 $self->do_test ($options->{exit_code} == 0,
396             "The CGI executable exited with zero status");
397             }
398 20         16646 $self->do_test ($options->{output}, "The CGI executable produced some output");
399 20 50       9633 if ($options->{expect_errors}) {
400 0 0       0 if ($options->{error_output}) {
401 0         0 $self->pass_test ("The CGI executable produced some output on the error stream as follows:\n$self->{errors}\n");
402             }
403             else {
404 0         0 $self->fail_test ("Expecting errors, but the CGI executable did not produce any output on the error stream");
405             }
406             }
407             else {
408 20 50       143 if ($self->{errors}) {
409 0         0 $self->fail_test ("Not expecting errors, but the CGI executable produced some output on the error stream as follows:\n$self->{errors}\n");
410             }
411             else {
412 20         102 $self->pass_test ("The CGI executable did not produce any output on the error stream");
413             }
414             }
415              
416 20         9067 $self->tidy_files ();
417              
418 20         232 return;
419             }
420              
421              
422             # my %token_valid_chars;
423             # @token_valid_chars{0..127} = (1) x 128;
424             # my @ctls = (0..31,127);
425             # @token_valid_chars{@ctls} = (0) x @ctls;
426             # my @tspecials =
427             # ('(', ')', '<', '>', '@', ',', ';', ':', '\\', '"',
428             # '/', '[', ']', '?', '=', '{', '}', \x32, \x09 );
429             # @token_valid_chars{@tspecials} = (0) x @tspecials;
430              
431             # These regexes are for testing the validity of the HTTP headers
432             # produced by the CGI script.
433              
434             my $HTTP_CTL = qr/[\x{0}-\x{1F}\x{7f}]/;
435              
436             my $HTTP_TSPECIALS = qr/[\x{09}\x{20}\x{22}\x{28}\x{29}\x{2C}\x{2F}\x{3A}-\x{3F}\x{5B}-\x{5D}\x{7B}\x{7D}]/;
437              
438             my $HTTP_TOKEN = '[\x{21}\x{23}-\x{27}\x{2a}\x{2b}\x{2d}\x{2e}\x{30}-\x{39}\x{40}-\x{5a}\x{5e}-\x{7A}\x{7c}\x{7e}]';
439              
440             my $HTTP_TEXT = qr/[^\x{0}-\x{1F}\x{7f}]/;
441              
442             # This does not include [CRLF].
443              
444             my $HTTP_LWS = '[\x{09}\x{20}]';
445              
446             my $qd_text = qr/[^"\x{0}-\x{1f}\x{7f}]/;
447             my $quoted_string = qr/"$qd_text+"/;
448             my $field_content = qr/(?:$HTTP_TEXT)*|
449             (?:
450             $HTTP_TOKEN|
451             $HTTP_TSPECIALS|
452             $quoted_string
453             )*
454             /x;
455              
456             my $http_token = qr/(?:$HTTP_TOKEN+)/;
457              
458             # Check for a valid content type line.
459              
460             sub check_content_line_private
461             {
462 15     15 0 67 my ($self, $header, $verbose) = @_;
463              
464 15         65 my $expected_charset = $self->{expected_charset};
465              
466 15         85 $self->note ("I am checking to see if the output contains a valid content type line.");
467 15         1925 my $content_type_ok;
468 15         123 my $has_content_type = ($header =~ m!(Content-Type:\s*.*)!i);
469 15         62 my $content_type_line = $1;
470 15         79 $self->do_test ($has_content_type, "There is a Content-Type header");
471 15 100       9426 if (! $has_content_type) {
472 1         7 return;
473             }
474 14         645 my $lineok = ($content_type_line =~ m!^Content-Type:(?:$HTTP_LWS)+
475             ($http_token/$http_token)
476             !xi);
477 14         72 my $mime_type = $1;
478 14         68 $self->do_test ($lineok, "The Content-Type header is well-formed");
479 14 50       7423 if (! $lineok) {
480 0         0 return;
481             }
482 14 100       76 if ($self->{mime_type}) {
483             $self->do_test ($mime_type eq $self->{mime_type},
484 2         24 "Got expected mime type $mime_type = $self->{mime_type}");
485             }
486 14 100       1800 if ($expected_charset) {
487 9         284 my $has_charset = ($content_type_line =~ /charset
488             =
489             (
490             $http_token|
491             $quoted_string
492             )/xi);
493 9         53 my $charset = $1;
494 9         51 $self->do_test ($has_charset, "Specifies a charset");
495 9 100       5118 if ($has_charset) {
496 8         42 $charset =~ s/^"(.*)"$/$1/;
497 8         99 $self->do_test (lc $charset eq lc $expected_charset,
498             "Got expected charset $charset = $expected_charset");
499             }
500             }
501             }
502              
503             sub check_http_header_syntax_private
504             {
505 20     20 0 77 my ($self, $header, $verbose) = @_;
506 20 100       110 if ($verbose) {
507 4         17 $self->note ("Checking the HTTP header.");
508             }
509 20         1861 my @lines = split /\r?\n/, $header;
510 20         69 my $line_number = 0;
511 20         55 my $bad_headers = 0;
512 20         47 my %headers;
513 20         752 my $line_re = qr/($HTTP_TOKEN+):$HTTP_LWS+(.*)/;
514             # print "Line regex is $line_re\n";
515 20         114 for my $line (@lines) {
516 29 50       3318 if ($line =~ /^$/) {
517 0 0       0 if ($line_number == 0) {
518 0         0 $self->fail_test ("The output of the CGI executable has a blank line as its first line");
519             }
520             else {
521 0         0 $self->pass_test ("There are $line_number valid header lines");
522             }
523             # We have finished looking at the headers.
524 0         0 last;
525             }
526 29         77 $line_number += 1;
527 29 100       335 if ($line !~ $line_re) {
528 1         30 $self->fail_test ("The header on line $line_number, '$line', appears not to be a correctly-formed HTTP header");
529 1         383 $bad_headers++;
530             }
531             else {
532 28         215 my $key = lc $1;
533 28         185 my $value = $2;
534 28         132 $headers{$key} = $value;
535 28         196 $self->pass_test ("The header on line $line_number, '$line', appears to be a correctly-formed HTTP header");
536             }
537             }
538 20 100       11437 if ($verbose) {
539 4         41 print "# I have finished checking the HTTP header for consistency.\n";
540             }
541 20         202 $self->{run_options}{headers} = \%headers;
542             }
543              
544             # The output is required to have a blank line even if it has no body.
545              
546             sub check_blank_line
547             {
548 20     20 0 120 my ($self, $output) = @_;
549 20         1020 my $blank = ($output =~ /\r?\n\r?\n/);
550 20         199 $self->{tb}->ok ($blank, "Output contains a blank line");
551             }
552              
553             # Check whether the headers of the CGI output are well-formed.
554              
555             sub check_headers_private
556             {
557 20     20 0 70 my ($self) = @_;
558              
559             # Extract variables from the object
560              
561 20         138 my $verbose = $self->{verbose};
562 20         68 my $output = $self->{run_options}->{output};
563 20 50       76 if (! $output) {
564 0         0 $self->note ("No output, skipping header tests");
565 0         0 return;
566             }
567 20         116 check_blank_line ($self, $output);
568 20         10833 my ($header, $body) = split /\r?\n\r?\n/, $output, 2;
569 20         141 check_http_header_syntax_private ($self, $header, $verbose);
570 20 100       103 if (! $self->{no_check_content}) {
571 15         74 check_content_line_private ($self, $header, $verbose);
572             }
573              
574 20         4018 $self->{run_options}->{header} = $header;
575 20         113 $self->{run_options}->{body} = $body;
576             }
577              
578             # This is "safe" in the sense that it falls back to using
579             # IO::Uncompress::Gunzip if it can't find Gzip::Faster. However, it
580             # throws an exception if it fails, so it's not really "safe".
581              
582             sub gunzip_safe
583             {
584 5     5 0 17 my ($self, $content) = @_;
585 5         13 my $out;
586 5 50       24 if ($self->{_use_io_uncompress_gunzip}) {
587             # gunzip_safe is called within an eval block. It's possible
588             # that the require might fail, but trying to fix these kinds
589             # of problems goes beyond the scope of this module.
590 0         0 eval "use IO::Uncompress::Gunzip;";
591 0         0 my $status = IO::Uncompress::Gunzip::gunzip (\$content, \$out);
592 0 0       0 if (! $status) {
593 0         0 die "IO::Uncompress::Gunzip failed: $IO::Uncompress::Gunzip::GunzipError";
594             }
595             }
596             else {
597             # We have already loaded Gzip::Faster within
598             # do_compression_test.
599 5         457 $out = Gzip::Faster::gunzip ($content);
600             }
601 5         25 return $out;
602             }
603              
604             sub check_compression_private
605             {
606 6     6 0 20 my ($self) = @_;
607 6         23 my $body = $self->{run_options}->{body};
608 6         19 my $header = $self->{run_options}->{header};
609 6         28 my $verbose = $self->{verbose};
610 6 100       25 if ($verbose) {
611 4         49 print "# I am testing whether compression has been applied to the output.\n";
612             }
613 6 100       73 if ($header !~ /Content-Encoding:.*\bgzip\b/i) {
614 1         14 $self->fail_test ("Output does not have a header indicating compression");
615             }
616             else {
617 5         29 $self->pass_test ("The header claims that the output is compressed");
618 5         1866 my $uncompressed;
619             #printf "The length of the body is %d\n", length ($body);
620 5         16 eval {
621 5         24 $uncompressed = $self->gunzip_safe ($body);
622             };
623 5 50       21 if ($@) {
624 0         0 $self->fail_test ("Output claims to be in gzip format but gunzip on the output failed with the error '$@'");
625 0         0 my $failedfile = "$0.gunzip-failure.$$";
626 0 0       0 open my $temp, ">:bytes", $failedfile or die $!;
627 0         0 print $temp $body;
628 0 0       0 close $temp or die $!;
629 0         0 print "# Saved failed output to $failedfile.\n";
630             }
631             else {
632 5         17 my $uncomp_size = length $uncompressed;
633 5         98 my $percent_comp = sprintf ("%.1f%%", (100 * length ($body)) / $uncomp_size);
634 5         40 $self->pass_test ("The body of the CGI output was able to be decompressed using 'gunzip'. The uncompressed size is $uncomp_size. The compressed output is $percent_comp of the uncompressed size.");
635            
636 5         1945 $self->{run_options}->{body} = $uncompressed;
637             }
638             }
639 6 100       1088 if ($verbose) {
640 4         28 print "# I have finished testing the compression.\n";
641             }
642             }
643              
644             sub set_no_check_content
645             {
646 2     2 1 3330 my ($self, $value) = @_;
647 2         7 my $verbose = $self->{verbose};
648 2 50       13 if ($verbose) {
649 0         0 print "# I am setting no content check to $value.\n";
650             }
651 2         11 $self->{no_check_content} = $value;
652             }
653              
654             sub test_not_implemented
655             {
656 1     1 1 7 my ($self, $method) = @_;
657 1         23 my %options;
658 1 50       5 if ($method) {
659 0         0 $options{REQUEST_METHOD} = $method;
660             }
661             else {
662 1         3 $options{REQUEST_METHOD} = 'GOBBLEDIGOOK';
663             }
664 1         2 $options{no_check_request_method} = 1;
665 1         2 my $saved_no_check_content = $self->{no_check_content};
666 1         1 $self->{no_check_content} = 1;
667 1         2 $self->{run_options} = \%options;
668 1         5 run_private ($self);
669             #print $options{output}, "\n";
670 1         6 $self->check_headers_private ();
671 1         40 $self->test_status (501);
672 1         279 $self->{no_check_content} = $saved_no_check_content;
673 1         6 $self->clear_env ();
674             }
675              
676             sub test_status
677             {
678 3     3 1 1646 my ($self, $status) = @_;
679 3 100       33 if ($status !~ /^[0-9]{3}$/) {
680 1         186 carp "$status is not a valid HTTP status, use a number like 301 or 503";
681 1         93 return;
682             }
683 2         9 my $headers = $self->{run_options}{headers};
684 2 100       12 if (! $headers) {
685 1         285 carp "no headers in this object; have you run a test yet?";
686 1         75 return;
687             }
688 1         4 $self->{tb}->ok ($headers->{status}, "Got status header");
689 1         224 $self->{tb}->like ($headers->{status}, qr/$status/, "Got $status status");
690             }
691              
692              
693             sub test_method_not_allowed
694             {
695 1     1 1 13 my ($self, $bad_method) = @_;
696 1         4 my $tb = $self->{tb};
697 1         3 my %options;
698 1         3 $options{REQUEST_METHOD} = $bad_method;
699 1         4 $options{no_check_request_method} = 1;
700 1         3 my $saved_no_check_content = $self->{no_check_content};
701 1         3 $self->{no_check_content} = 1;
702 1         8 $self->{run_options} = \%options;
703 1         7 run_private ($self);
704 1         15 $self->check_headers_private ();
705 1         2 my $headers = $options{headers};
706 1         6 $tb->ok ($headers->{allow}, "Got Allow header");
707 1         227 $tb->like ($headers->{status}, qr/405/, "Got method not allowed status");
708 1         559 $self->clear_env ();
709 1 50       4 if ($headers->{allow}) {
710 1         7 my @allow = split /,\s*/, $headers->{allow};
711 1         4 my $saved_no_warn = $self->{no_warn};
712 1         4 $self->{no_warn} = 1;
713 1         5 for my $ok_method (@allow) {
714             # Run the program with each of the headers we were told were
715             # allowed, and see whether the program executes correctly.
716 2         5 my %op2;
717 2         8 $op2{REQUEST_METHOD} = $ok_method;
718 2 50       8 if ($ok_method eq 'POST') {
719 0         0 $op2{CONTENT_TYPE} = 'application/x-www-form-urlencoded';
720 0         0 $op2{input} = 'a=b';
721             # $op2{CONTENT_LENGTH} = length ($op2{input});
722             }
723 2         7 $self->{run_options} = \%op2;
724 2         21 run_private ($self);
725 2         28 $self->check_headers_private ();
726 2         7 my $headers2 = $op2{headers};
727             # Check that either there is no status line (defaults to 200),
728             # or that there is a status line, and it has status 200.
729 2   33     23 $tb->ok (! $headers2->{status} || $headers2->{status} =~ /200/,
730             "Method $ok_method specified by Allow: header was allowed");
731 2         761 $self->clear_env ();
732             }
733 1         9 $self->{no_warn} = $saved_no_warn;
734             }
735 1         13 $self->{no_check_content} = $saved_no_check_content;
736             }
737              
738             # Make a request with CONTENT_LENGTH set to zero and see if the
739             # executable produces a 411 status (content length required).
740              
741             sub test_411
742             {
743 0     0 1 0 my ($self, $options) = @_;
744 0 0       0 if (! $options) {
745 0         0 $options = {};
746             }
747 0         0 $self->{bad_content_length} = 1;
748 0         0 my $rm;
749 0 0 0     0 if ($options->{REQUEST_METHOD} && $options->{REQUEST_METHOD} ne 'POST') {
750 0         0 $rm = $options->{REQUEST_METHOD};
751 0 0       0 if (! $self->{no_warn}) {
752 0         0 carp "test_411 requires REQUEST_METHOD to be POST";
753             }
754             }
755 0         0 $options->{REQUEST_METHOD} = 'POST';
756 0 0       0 if (! $options->{CONTENT_TYPE}) {
757 0         0 $options->{CONTENT_TYPE} = 'application/x-www-form-urlencoded';
758             }
759 0 0       0 if (! $options->{input}) {
760 0         0 $options->{input} = 'this does not have a zero length';
761             }
762 0         0 my $saved_no_check_content = $self->{no_check_content};
763 0         0 $self->{no_check_content} = 1;
764 0         0 $self->{run_options} = $options;
765 0         0 $self->run_private ();
766             # This has to be run to parse the headers.
767 0         0 $self->check_headers_private ();
768 0         0 $self->test_status (411);
769             # Delete everything from $self so that it can be used again.
770 0         0 $self->{bad_content_length} = undef;
771 0         0 $self->{run_options} = undef;
772 0         0 $self->clear_env ();
773 0         0 $self->{no_check_content} = $saved_no_check_content;
774             # Put the user's %options back to how it was.
775 0         0 $options->{REQUEST_METHOD} = $rm;
776             }
777              
778             sub test_options
779             {
780 0     0 1 0 my ($self) = @_;
781 0         0 my %options = (
782             REQUEST_METHOD => 'OPTIONS',
783             QUERY_STRING => '',
784             );
785 0         0 $self->{run_options} = \%options;
786 0         0 $valid_request_method{OPTIONS} = 1;
787 0         0 $self->run_private ();
788 0         0 $self->check_headers_private ();
789 0         0 my $headers = $options{headers};
790 0         0 $self->do_test ($headers->{allow}, "Got allow header");
791 0         0 delete $valid_request_method{OPTIONS};
792             }
793              
794             # Send bullshit queries expecting a 400 response.
795              
796             sub test_broken_queries
797             {
798 0     0 0 0 my ($self, $options, $queries) = @_;
799 0         0 for my $query (@$queries) {
800 0         0 $ENV{QUERY_STRING} = $query;
801 0         0 $self->run ($options);
802             # test for 400 header
803 0         0 $self->test_status (400);
804             }
805             }
806              
807             # Clear all the environment variables we have set ourselves.
808              
809             sub clear_env
810             {
811 20     20 0 64 my ($self) = @_;
812 20         54 for my $e (@{$self->{set_env}}) {
  20         122  
813             # print "Deleting environment variable $e\n";
814 49         369 $ENV{$e} = undef;
815             }
816 20         232 $self->{set_env} = undef;
817             }
818              
819             sub run
820             {
821 16     16 1 2180 my ($self, $options) = @_;
822 16 50       89 if (ref $options ne 'HASH') {
823 0         0 carp "Use a hash reference as argument, \$tester->run (\\\%options);";
824 0         0 return;
825             }
826 16         67 my $verbose = $self->{verbose};
827 16         45 local $Test::Builder::Level = $Test::Builder::Level + 1;
828 16 50       61 if (! $self->{cgi_executable}) {
829 0         0 croak "You have requested me to run a CGI executable with 'run' without telling me what it is you want me to run. Please tell me the name of the CGI executable using the method 'set_cgi_executable'.";
830             }
831 16 50       129 if (! $options) {
832 0         0 $self->{run_options} = {};
833 0 0       0 if (! $self->{no_warn}) {
834 0         0 carp "You have requested me to run a CGI executable with 'run' without specifying a hash reference to store the input, output, and error output. I can only run basic tests of correctness";
835             }
836             }
837             else {
838 16         50 $self->{run_options} = $options;
839             }
840 16 100       58 if ($self->{verbose}) {
841 4         55 print "# I am commencing the testing of CGI executable '$self->{cgi_executable}'.\n";
842             }
843 16 50 33     184 if ($options->{html} && ! $self->{no_warn}) {
    100 66        
    50 33        
844 0 0       0 if ($self->{mime_type}) {
845 0 0       0 if ($self->{mime_type} ne 'text/html') {
846 0         0 carp "If you want to test for HTML output, you should also specify a mime type 'text/html', but you have specified '$self->{mime_type}'";
847             }
848             }
849             else {
850 0         0 carp "If you want to check for html validity, you should also check the mime type is 'text/html' using expect_mime_type";
851             }
852             }
853             elsif ($options->{json} && ! $self->{no_warn}) {
854 2         8 my $mime_type = $self->{mime_type};
855 2 50       7 if ($mime_type) {
856 2 50 33     19 if ($mime_type ne 'text/plain' && $mime_type ne 'application/json') {
857 0         0 carp "Your expected mime type of $mime_type is not valid for JSON";
858             }
859             }
860             else {
861 0         0 carp "There is no expected mime type, use expect_mime_type ('application/json') or expect_mime_type ('text/plain') for JSON output";
862             }
863             }
864             elsif ($options->{png} && ! $self->{no_warn}) {
865 0         0 my $mime_type = $self->{mime_type};
866 0 0       0 if ($mime_type) {
867 0 0       0 if ($mime_type ne 'image/png') {
868 0         0 carp "Your expected mime type of $mime_type is not valid for PNG";
869             }
870             }
871             else {
872 0         0 carp "There is no expected mime type, use image/png for PNG output";
873             }
874             }
875              
876 16 50       185 if ($options->{png}) {
    50          
877 0 0 0     0 if ($options->{html} || $options->{json}) {
878 0         0 carp "Contradictory options png and json/html";
879             }
880             }
881             elsif ($options->{html}) {
882 0 0       0 if ($options->{json}) {
883 0         0 carp "Contradictory options json and html";
884             }
885             }
886              
887             # eval {
888 16         93 run_private ($self);
889 16         93 my $output = $self->{run_options}->{output};
890             # Jump over the following tests if there is no output. This used
891             # to complain a lot about output and fail tests but this proved a
892             # huge nuisance when creating TODO tests, so just skip over the
893             # output tests if we have already failed the basic "did not
894             # produce output" issue.
895 16 50       75 if ($output) {
896 16         120 check_headers_private ($self);
897 16 100       78 if ($self->{comp_test}) {
898 6         40 check_compression_private ($self);
899             }
900 16         57 my $ecs = $self->{expected_charset};
901 16 100       59 if ($ecs) {
902 11 100       175 if ($ecs =~ /utf\-?8/i) {
903 10 100       47 if ($verbose) {
904 4         27 print ("# Expected charset '$ecs' looks like UTF-8, sending it to Unicode::UTF8.\n");
905             }
906 10         234 $options->{body} = decode_utf8 ($options->{body});
907             }
908             else {
909 1 50       9 if ($verbose) {
910 0         0 print ("# Expected charset '$ecs' doesn't look like UTF-8, sending it to Encode.\n");
911             }
912 1         4 eval {
913 1         114 $options->{body} = decode ($options->{body}, $ecs);
914             };
915 1 50       3627 if (! $@) {
916 0         0 $self->pass_test ("decoded from $ecs encoding");
917             }
918             else {
919 1         14 $self->fail_test ("decoded from $ecs encoding");
920             }
921             }
922             }
923 16 50       1009 if ($self->{cache_test}) {
924 0         0 $self->check_caching_private ();
925             }
926             }
927 16 50       76 if ($options->{html}) {
928 0         0 validate_html ($self);
929             }
930 16 100       80 if ($options->{json}) {
931 2         16 validate_json ($self);
932             }
933 16 50       1694 if ($options->{png}) {
934 0         0 validate_png ($self);
935             }
936 16         124 $self->clear_env ();
937             }
938              
939             sub tidy_files
940             {
941 20     20 0 65 my ($self) = @_;
942 20 100       166 if ($self->{infile}) {
943 3 50       336 unlink $self->{infile} or die $!;
944             }
945              
946             # Insert HTML test here?
947              
948 20 50       17361 unlink $self->{outfile} or die $!;
949 20 50       867 unlink $self->{errfile} or die $!;
950             }
951              
952             sub tfilename
953             {
954 3     3 0 12 my $dir = "/tmp";
955 3         51 my $file = "$dir/temp.$$-" . scalar(time ()) . "-" . int (rand (10000));
956 3         19 return $file;
957             }
958              
959             sub run3
960             {
961 20     20 0 61 my ($self, $exe) = @_;
962 20         159 my $cmd = "@$exe";
963 20 100       110 if (defined $self->{input}) {
964 3         13 $self->{infile} = tfilename ();
965 3 50       96673 open my $in, ">:raw", $self->{infile} or die $!;
966 3         58 print $in $self->{input};
967 3 50       155 close $in or die $!;
968 3         37 $cmd .= " < " . $self->{infile};
969             }
970             else {
971             # Make sure that the program does not hang waiting for STDIN
972             # to complete.
973 17         49 $cmd .= " < /dev/null ";
974             }
975 20         47 my $out;
976 20         176 ($out, $self->{outfile}) = tempfile ("/tmp/output-XXXXXX");
977 20 50       9631 close $out or die $!;
978 20         47 my $err;
979 20         79 ($err, $self->{errfile}) = tempfile ("/tmp/errors-XXXXXX");
980 20 50       5912 close $err or die $!;
981            
982 20         864621 my $status = system ("$cmd > $self->{outfile} 2> $self->{errfile}");
983              
984 20         473 $self->{output} = '';
985 20 50       742 if (-f $self->{outfile}) {
986 20 50       1384 open my $out, "<", $self->{outfile} or die $!;
987 20         727 while (<$out>) {
988 66         405 $self->{output} .= $_;
989             }
990 20 50       321 close $out or die $!;
991             }
992 20         135 $self->{errors} = '';
993 20 50       249 if (-f $self->{errfile}) {
994 20 50       488 open my $err, "<", $self->{errfile} or die $!;
995 20         333 while (<$err>) {
996 0         0 $self->{errors} .= $_;
997             }
998 20 50       201 close $err or die $!;
999             }
1000              
1001             # print "OUTPUT IS $self->{output}\n";
1002             # print "$$errors\n";
1003             # exit;
1004              
1005 20         432 return $status;
1006             }
1007              
1008             sub set_html_validator
1009             {
1010 0     0 1 0 my ($self, $hvc) = @_;
1011 0 0       0 if (! $hvc) {
1012 0 0       0 if (! $self->{no_warn}) {
1013 0         0 carp "Invalid value for validator";
1014             }
1015 0         0 return;
1016             }
1017 0 0       0 if (! -x $hvc) {
1018 0 0       0 if (! $self->{no_warn}) {
1019 0         0 carp "$hvc doesn't seem to be an executable program";
1020             }
1021             }
1022 0         0 $self->{html_validator} = $hvc;
1023             }
1024              
1025             sub validate_html
1026             {
1027 0     0 0 0 my ($self) = @_;
1028 0         0 my $html_validator = $self->{html_validator};
1029 0 0 0     0 if (! $html_validator || ! -x $html_validator) {
1030 0         0 warn "HTML validation could not be completed, set validator to executable program using \$tce->set_html_validator ('command')";
1031 0         0 return;
1032             }
1033 0         0 my $html_validate = "$Bin/html-validate-temp-out.$$";
1034 0         0 my $html_temp_file = "$Bin/html-validate-temp.$$.html";
1035 0 0       0 open my $htmltovalidate, ">:encoding(utf8)", $html_temp_file or die $!;
1036 0         0 print $htmltovalidate $self->{run_options}->{body};
1037 0 0       0 close $htmltovalidate or die $!;
1038 0         0 my $status = system ("$html_validator $html_temp_file > $html_validate");
1039            
1040 0         0 $self->do_test (! -s $html_validate, "HTML is valid");
1041 0 0       0 if (-s $html_validate) {
1042 0 0       0 open my $in, "<", $html_validate or die $!;
1043 0         0 while (<$in>) {
1044 0         0 print ("# $_");
1045             }
1046 0 0       0 close $in or die $!;
1047             }
1048 0 0       0 unlink $html_temp_file or die $!;
1049 0 0       0 if (-f $html_validate) {
1050 0 0       0 unlink $html_validate or die $!;
1051             }
1052             }
1053              
1054             sub validate_json
1055             {
1056 2     2 0 10 my ($self) = @_;
1057 2         10 my $json = $self->{run_options}->{body};
1058 2     4   498 eval "use JSON::Parse 'valid_json';";
  4     5   995  
  4         3565  
  4         212  
  5         1034  
  5         1863  
  5         379  
1059 2 50       16 if ($@) {
1060 0         0 croak "JSON::Parse is not installed, cannot validate JSON";
1061             }
1062 2         18 my $valid = valid_json ($json);
1063 2 100       72 if ($valid) {
1064 1         7 $self->pass_test ("Valid JSON");
1065             }
1066             else {
1067 1         9 $self->fail_test ("Valid JSON");
1068             }
1069             }
1070              
1071             sub validate_png
1072             {
1073 0     0 0 0 my ($self) = @_;
1074 0         0 eval "use Image::PNG::Libpng 'read_from_scalar';";
1075 0 0       0 if ($@) {
1076 0         0 croak "Image::PNG::Libpng is not installed, cannot validate PNG";
1077             }
1078 0         0 my $body = $self->{run_options}->{body};
1079 0         0 my $png;
1080 0         0 eval {
1081 0         0 $png = read_from_scalar ($body);
1082             };
1083 0         0 $self->{tb}->ok (!$@, "Could read PNG from body");
1084 0         0 $self->{tb}->ok ($png, "Got a valid value for PNG");
1085 0         0 $self->{run_options}{pngdata} = $png;
1086             }
1087              
1088             1;
1089