From: Nicholas Clark Date: Sat, 15 Apr 2006 13:45:13 +0000 (+0000) Subject: Add more tests for the builtin source filter implementation, and fix X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=5675696b3881ef5bfde3012a829ca51ab1d42333;p=p5sagit%2Fp5-mst-13.2.git Add more tests for the builtin source filter implementation, and fix two bugs - it would loop infinitely if data were already in the read buffer, and it would process those data twice. p4raw-id: //depot/perl@27812 --- diff --git a/pp_ctl.c b/pp_ctl.c index 7beea6a..364a1d5 100644 --- a/pp_ctl.c +++ b/pp_ctl.c @@ -4538,14 +4538,23 @@ S_run_user_filter(pTHX_ int idx, SV *buf_sv, int maxlen) SV * const filter_state = (SV *)IoTOP_GV(datasv); SV * const filter_sub = (SV *)IoBOTTOM_GV(datasv); int len = 0; - + /* Filter API says that the filter appends to the contents of the buffer. + Usually the buffer is "", so the details don't matter. But if it's not, + then clearly what it contains is already filtered by this filter, so we + don't want to pass it in a second time. + I'm going to use a mortal in case the upstream filter croaks. */ + SV *const upstream + = ((SvOK(buf_sv) && sv_len(buf_sv)) || SvGMAGICAL(buf_sv)) + ? sv_newmortal() : buf_sv; + + SvUPGRADE(upstream, SVt_PV); /* I was having segfault trouble under Linux 2.2.5 after a parse error occured. (Had to hack around it with a test for PL_error_count == 0.) Solaris doesn't segfault -- not sure where the trouble is yet. XXX */ if (filter_has_file) { - len = FILTER_READ(idx+1, buf_sv, maxlen); + len = FILTER_READ(idx+1, upstream, maxlen); } if (filter_sub && len >= 0) { @@ -4557,7 +4566,7 @@ S_run_user_filter(pTHX_ int idx, SV *buf_sv, int maxlen) SAVETMPS; EXTEND(SP, 2); - DEFSV = buf_sv; + DEFSV = upstream; PUSHMARK(SP); PUSHs(sv_2mortal(newSViv(maxlen))); if (filter_state) { @@ -4596,6 +4605,9 @@ S_run_user_filter(pTHX_ int idx, SV *buf_sv, int maxlen) filter_del(S_run_user_filter); } + if (upstream != buf_sv) { + sv_catsv(buf_sv, upstream); + } return len; } diff --git a/t/op/incfilter.t b/t/op/incfilter.t index 9e273c3..4dbf7e9 100644 --- a/t/op/incfilter.t +++ b/t/op/incfilter.t @@ -12,8 +12,9 @@ BEGIN { require "test.pl"; } use strict; +use Filter::Util::Call; -plan(tests => 12); +plan(tests => 19); unshift @INC, sub { no warnings 'uninitialized'; @@ -23,7 +24,7 @@ unshift @INC, sub { my $fh; open $fh, "<", \'pass("Can return file handles from \@INC");'; -do $fh; +do $fh or die; my @origlines = ("# This is a blank line\n", "pass('Can return generators from \@INC');\n", @@ -37,11 +38,11 @@ sub generator { return defined $_ ? 1 : 0; }; -do \&generator; +do \&generator or die; @lines = @origlines; # Check that the array dereferencing works ready for the more complex tests: -do [\&generator]; +do [\&generator] or die; do [sub { my $param = $_[1]; @@ -50,13 +51,72 @@ do [sub { return defined $_ ? 1 : 0; }, ["pass('Can return generators which take state');\n", "pass('And return multiple lines');\n", - ]]; + ]] or die; open $fh, "<", \'fail("File handles and filters work from \@INC");'; -do [$fh, sub {s/fail/pass/}]; +do [$fh, sub {s/fail/pass/}] or die; open $fh, "<", \'fail("File handles and filters with state work from \@INC");'; -do [$fh, sub {s/$_[1]/pass/}, 'fail']; +do [$fh, sub {s/$_[1]/pass/}, 'fail'] or die; + +print "# 2 tests with pipes from subprocesses.\n"; + +open $fh, 'echo pass|' or die $!; + +do $fh or die; + +open $fh, 'echo fail|' or die $!; + +do [$fh, sub {s/$_[1]/pass/}, 'fail'] or die; + +sub rot13_filter { + filter_add(sub { + my $status = filter_read(); + tr/A-Za-z/N-ZA-Mn-za-m/; + $status; + }) +} + +open $fh, "<", \<<'EOC'; +BEGIN {rot13_filter}; +cnff("This will rot13'ed prepend"); +EOC + +do $fh or die; + +open $fh, "<", \<<'EOC'; +ORTVA {ebg13_svygre}; +pass("This will rot13'ed twice"); +EOC + +do [$fh, sub {tr/A-Za-z/N-ZA-Mn-za-m/;}] or die; + +my $count = 32; +sub prepend_rot13_filter { + filter_add(sub { + my $previous = defined $_ ? $_ : ''; + # Filters should append to any existing data in $_ + # But (logically) shouldn't filter it twice. + my $test = "fzrt!"; + $_ = $test; + my $status = filter_read(); + # Sadly, doing this inside the source filter causes an + # infinte loop + my $got = substr $_, 0, length $test, ''; + is $got, $test, "Upstream didn't alter existing data"; + tr/A-Za-z/N-ZA-Mn-za-m/; + $_ = $previous . $_; + die "Looping infinitely" unless $count--; + $status; + }) +} + +open $fh, "<", \<<'EOC'; +ORTVA {cercraq_ebg13_svygre}; +pass("This will rot13'ed twice"); +EOC + +do [$fh, sub {tr/A-Za-z/N-ZA-Mn-za-m/;}] or die;