| # please insert nothing before this line: -*- mode: cperl; cperl-indent-level: 4; cperl-continued-statement-offset: 4; indent-tabs-mode: nil -*- |
| package TestAPR::perlio; |
| |
| # to see what happens inside the io layer, assuming that you built |
| # mod_perl with MP_TRACE=1, run: |
| # env MOD_PERL_TRACE=o t/TEST -v -trace=debug apr/perlio |
| |
| use strict; |
| use warnings FATAL => 'all'; |
| |
| use Apache::Test; |
| use Apache::TestUtil; |
| |
| use Fcntl (); |
| use File::Spec::Functions qw(catfile); |
| |
| use Apache2::Const -compile => qw(OK CRLF); |
| |
| #XXX: APR::LARGE_FILES_CONFLICT constant? |
| #XXX: you can set to zero if largefile support is not enabled in Perl |
| use constant LARGE_FILES_CONFLICT => 1; |
| |
| # apr_file_dup has a bug on win32, |
| # should be fixed in apr 0.9.4 / httpd-2.0.48 |
| require Apache2::Build; |
| use constant APR_WIN32_FILE_DUP_BUG => |
| Apache2::Build::WIN32() && !have_min_apache_version('2.0.48'); |
| |
| sub handler { |
| my $r = shift; |
| |
| my $tests = 22; |
| $tests += 3 unless LARGE_FILES_CONFLICT; |
| $tests += 1 unless APR_WIN32_FILE_DUP_BUG; |
| |
| require APR::PerlIO; |
| plan $r, tests => $tests, |
| need { "This Perl build doesn't support PerlIO layers" => |
| APR::PerlIO::PERLIO_LAYERS_ARE_ENABLED() }; |
| |
| my $vars = Apache::Test::config()->{vars}; |
| my $dir = catfile $vars->{documentroot}, "perlio"; |
| |
| t_mkdir($dir); |
| |
| my $sep = "-- sep --\n"; |
| my @lines = ("This is a test: $$\n", "test line --sep two\n"); |
| |
| my $expected = $lines[0]; |
| my $expected_all = join $sep, @lines; |
| |
| # write file |
| my $file = catfile $dir, "test"; |
| t_debug "open file $file for writing"; |
| my $foo = "bar"; |
| open my $fh, ">:APR", $file, $r->pool |
| or die "Cannot open $file for writing: $!"; |
| ok ref($fh) eq 'GLOB'; |
| |
| t_debug "write to a file:\n$expected\n"; |
| print $fh $expected_all; |
| close $fh; |
| |
| # open() failure test |
| { |
| # workaround for locale setups where the error message may be |
| # in a different language |
| open my $fh, "perlio_this_file_cannot_exist"; |
| my $errno_string = "$!"; |
| |
| # non-existent file |
| my $file = "/this/file/does/not/exist"; |
| if (open my $fh, "<:APR", $file, $r->pool) { |
| t_debug "must not be able to open $file!"; |
| ok 0; |
| close $fh; |
| } |
| else { |
| ok t_cmp("$!", |
| $errno_string, |
| "expected failure"); |
| } |
| } |
| |
| # seek/tell() tests |
| unless (LARGE_FILES_CONFLICT) { |
| open my $fh, "<:APR", $file, $r->pool |
| or die "Cannot open $file for reading: $!"; |
| |
| # read the whole file so we can test the buffer flushed |
| # correctly on seek. |
| my $dummy = join '', <$fh>; |
| |
| # Fcntl::SEEK_SET() |
| my $pos = 3; # rewinds after reading 6 chars above |
| seek $fh, $pos, Fcntl::SEEK_SET(); |
| my $got = tell($fh); |
| ok t_cmp($got, |
| $pos, |
| "seek/tell the file Fcntl::SEEK_SET"); |
| |
| # Fcntl::SEEK_CUR() |
| my $step = 10; |
| $pos = tell($fh) + $step; |
| seek $fh, $step, Fcntl::SEEK_CUR(); |
| $got = tell($fh); |
| ok t_cmp($got, |
| $pos, |
| "seek/tell the file Fcntl::SEEK_CUR"); |
| |
| # Fcntl::SEEK_END() |
| $pos = -s $file; |
| seek $fh, 0, Fcntl::SEEK_END(); |
| $got = tell($fh); |
| ok t_cmp($got, |
| $pos, |
| "seek/tell the file Fcntl::SEEK_END"); |
| |
| close $fh; |
| } |
| |
| # read() tests |
| { |
| open my $fh, "<:APR", $file, $r->pool |
| or die "Cannot open $file for reading: $!"; |
| |
| # basic open test |
| ok ref($fh) eq 'GLOB'; |
| |
| # basic single line read |
| ok t_cmp(scalar(<$fh>), |
| $expected, |
| "single line read"); |
| |
| # slurp mode |
| seek $fh, 0, Fcntl::SEEK_SET(); # rewind to the start |
| local $/; |
| |
| ok t_cmp(scalar(<$fh>), |
| $expected_all, |
| "slurp file"); |
| |
| # test ungetc (a long sep requires read ahead) |
| seek $fh, 0, Fcntl::SEEK_SET(); # rewind to the start |
| local $/ = $sep; |
| my @got_lines = <$fh>; |
| my @expect = ($lines[0] . $sep, $lines[1]); |
| ok t_cmp(\@got_lines, |
| \@expect, |
| "custom complex input record sep read"); |
| |
| close $fh; |
| } |
| |
| |
| # eof() tests |
| { |
| open my $fh, "<:APR", $file, $r->pool |
| or die "Cannot open $file for reading: $!"; |
| |
| ok t_cmp(0, |
| int eof($fh), # returns false, not 0 |
| "not end of file"); |
| # go to the end and read so eof will return 1 |
| seek $fh, 0, Fcntl::SEEK_END(); |
| my $received = <$fh>; |
| |
| t_debug($received); |
| |
| ok t_cmp(eof($fh), |
| 1, |
| "end of file"); |
| close $fh; |
| } |
| |
| # dup() test |
| { |
| open my $fh, "<:APR", $file, $r->pool |
| or die "Cannot open $file for reading: $!"; |
| |
| open my $dup_fh, "<&:APR", $fh |
| or die "Cannot dup $file for reading: $!"; |
| close $fh; |
| ok ref($dup_fh) eq 'GLOB'; |
| |
| my $received = <$dup_fh>; |
| |
| close $dup_fh; |
| unless (APR_WIN32_FILE_DUP_BUG) { |
| ok t_cmp($received, |
| $expected, |
| "read/write a dupped file"); |
| } |
| } |
| |
| # unbuffered write |
| { |
| open my $wfh, ">:APR", $file, $r->pool |
| or die "Cannot open $file for writing: $!"; |
| open my $rfh, "<:APR", $file, $r->pool |
| or die "Cannot open $file for reading: $!"; |
| |
| my $expected = "This is an un buffering write test"; |
| # unbuffer |
| my $oldfh = select($wfh); $| = 1; select($oldfh); |
| print $wfh $expected; # must be flushed to disk immediately |
| |
| ok t_cmp(scalar(<$rfh>), |
| $expected, |
| "file unbuffered write"); |
| |
| # buffer up |
| $oldfh = select($wfh); $| = 0; select($oldfh); |
| print $wfh $expected; # should be buffered up and not flushed |
| |
| ok t_cmp(scalar(<$rfh>), |
| undef, |
| "file buffered write"); |
| |
| close $wfh; |
| close $rfh; |
| |
| } |
| |
| # tests reading and writing text and binary files |
| { |
| for my $file ('MoonRise.jpeg', 'redrum.txt') { |
| my $in = catfile $dir, $file; |
| my $out = catfile $dir, "$file.out"; |
| my ($apr_content, $perl_content); |
| open my $rfh, "<:APR", $in, $r->pool |
| or die "Cannot open $in for reading: $!"; |
| { |
| local $/; |
| $apr_content = <$rfh>; |
| } |
| close $rfh; |
| open my $pfh, "<", $in |
| or die "Cannot open $in for reading: $!"; |
| binmode($pfh); |
| { |
| local $/; |
| $perl_content = <$pfh>; |
| } |
| close $pfh; |
| ok t_cmp(length $apr_content, |
| length $perl_content, |
| "testing data size of $file"); |
| |
| open my $wfh, ">:APR", $out, $r->pool |
| or die "Cannot open $out for writing: $!"; |
| print $wfh $apr_content; |
| close $wfh; |
| ok t_cmp(-s $out, |
| -s $in, |
| "testing file size of $file"); |
| unlink $out; |
| } |
| } |
| |
| # tests for various CRLF and utf-8 issues |
| { |
| my $scratch = catfile $dir, 'scratch.dat'; |
| my $text; |
| my $count = 2000; |
| open my $wfh, ">:crlf", $scratch |
| or die "Cannot open $scratch for writing: $!"; |
| print $wfh 'a' . ((('a' x 14) . "\n") x $count); |
| close $wfh; |
| open my $rfh, "<:APR", $scratch, $r->pool |
| or die "Cannot open $scratch for reading: $!"; |
| { |
| local $/; |
| $text = <$rfh>; |
| } |
| close $rfh; |
| ok t_cmp(count_chars($text, Apache2::Const::CRLF), |
| $count, |
| 'testing for presence of \015\012'); |
| ok t_cmp(count_chars($text, "\n"), |
| $count, |
| 'testing for presence of \n'); |
| |
| open $wfh, ">:APR", $scratch, $r->pool |
| or die "Cannot open $scratch for writing: $!"; |
| print $wfh 'a' . ((('a' x 14) . Apache2::Const::CRLF) x $count); |
| close $wfh; |
| open $rfh, "<:APR", $scratch, $r->pool |
| or die "Cannot open $scratch for reading: $!"; |
| { |
| local $/; |
| $text = <$rfh>; |
| } |
| close $rfh; |
| ok t_cmp(count_chars($text, Apache2::Const::CRLF), |
| $count, |
| 'testing for presence of \015\012'); |
| ok t_cmp(count_chars($text, "\n"), |
| $count, |
| 'testing for presence of \n'); |
| open $rfh, "<:crlf", $scratch |
| or die "Cannot open $scratch for reading: $!"; |
| { |
| local $/; |
| $text = <$rfh>; |
| } |
| close $rfh; |
| ok t_cmp(count_chars($text, Apache2::Const::CRLF), |
| 0, |
| 'testing for presence of \015\012'); |
| ok t_cmp(count_chars($text, "\n"), |
| $count, |
| 'testing for presence of \n'); |
| |
| my $utf8 = "\x{042F} \x{0432}\x{0430}\x{0441} \x{043B}\x{044E}"; |
| open $wfh, ">:APR", $scratch, $r->pool |
| or die "Cannot open $scratch for writing: $!"; |
| binmode($wfh, ':utf8'); |
| print $wfh $utf8; |
| close $wfh; |
| open $rfh, "<:APR", $scratch, $r->pool |
| or die "Cannot open $scratch for reading: $!"; |
| binmode($rfh, ':utf8'); |
| { |
| local $/; |
| $text = <$rfh>; |
| } |
| close $rfh; |
| ok t_cmp($text, |
| $utf8, |
| 'utf8 binmode test'); |
| unlink $scratch; |
| } |
| |
| # XXX: need tests |
| # - for stdin/out/err as they are handled specially |
| |
| # XXX: tmpfile is missing: |
| # consider to use 5.8's syntax: |
| # open $fh, "+>", undef; |
| |
| # cleanup: t_mkdir will remove the whole tree including the file |
| |
| Apache2::Const::OK; |
| } |
| |
| sub count_chars { |
| my ($text, $chars) = @_; |
| my $seen = 0; |
| $seen++ while $text =~ /$chars/g; |
| return $seen; |
| } |
| |
| 1; |