File Coverage

blib/lib/Test/CGI/External.pm
Criterion Covered Total %
statement 373 577 64.6
branch 122 264 46.2
condition 9 39 23.0
subroutine 42 53 79.2
pod 16 42 38.1
total 562 975 57.6


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