Add more tests for the builtin source filter implementation, and fix
Nicholas Clark [Sat, 15 Apr 2006 13:45:13 +0000 (13:45 +0000)]
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

pp_ctl.c
t/op/incfilter.t

index 7beea6a..364a1d5 100644 (file)
--- 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;
 }
 
index 9e273c3..4dbf7e9 100644 (file)
@@ -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;