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