about summary refs log tree commit
path: root/t/nntp_deflate.t
diff options
context:
space:
mode:
Diffstat (limited to 't/nntp_deflate.t')
-rw-r--r--t/nntp_deflate.t109
1 files changed, 109 insertions, 0 deletions
diff --git a/t/nntp_deflate.t b/t/nntp_deflate.t
new file mode 100644
index 0000000..6a031f8
--- /dev/null
+++ b/t/nntp_deflate.t
@@ -0,0 +1,109 @@
+#!perl
+# unit test for internal Net::NNTP::Deflate class
+# This exercises some rare code paths which may not be exercised
+# in normal use and reaches into internal data structures to test them.
+use 5.008001;
+use strict;
+use warnings;
+use Test::More;
+use IO::Handle;
+if (!eval { require Net::NNTP::Deflate }) {
+  plan skip_all => 'no DEFLATE support';
+}
+Compress::Raw::Zlib->import(qw(Z_OK Z_PARTIAL_FLUSH Z_FINISH));
+plan tests => 21;
+
+my ($r, $w);
+
+# we don't want Net::NNTP::DESTROY triggering and blocking on select()
+# because it waits on the ->quit response;
+END {
+  $w->close if $w;
+  $r->close if $r;
+}
+
+# easy stuff, first
+pipe($r, $w) or die;
+Net::NNTP::Deflate->wrap($r);
+Net::NNTP::Deflate->wrap($w);
+is(12, $w->syswrite("HELLO HELLO\n"), 'syswrite OK');
+my $buf = '';
+my $n = $r->sysread($buf, 4096, 0);
+is($n, 12, 'read expected number of bytes');
+is($buf, "HELLO HELLO\n", 'got expected output');
+
+is(4, $w->syswrite("s'more!!1", 4, 2), 'wrote "more" using offset/length');
+$buf = '';
+is($r->sysread($buf, 4096, 0), 4, 'reader read data');
+is($buf, 'more', 'syswrite respected length and offset');
+
+# reach into internal state to simulate the real-world blocks
+# being split in non-optimal ways for inflate:
+{
+  my $deflate = ${*$w}{net_nntp_deflate};
+  my $zout = $deflate->[0];
+  open my $fh, '<', __FILE__ or die;
+  my $orig = do { local $/; <$fh> };
+  my $status = $zout->deflate($orig, $deflate->[1]);
+  $status == Z_OK() or die "->deflate failed: $status";
+
+  $status = $zout->flush($deflate->[1], Z_PARTIAL_FLUSH());
+  $status == Z_OK() or die "->flush failed: $status";
+
+  # start with a single-byte incomplete write, zlib has no chance of
+  # making sense of one byte of input:
+  my $len = length($deflate->[1]);
+  my $olen = 1;
+  my $remain = $len - $olen;
+  my $wrote = syswrite($w, $deflate->[1], $olen, $deflate->[2]);
+  is($wrote, $olen, 'wrote one byte');
+  $deflate->[2] += $olen;
+  $r->blocking(0);
+  $buf = '';
+  $n = $r->sysread($buf, 4096, 0);
+  is($n, undef, 'sysread can return undef');
+  ok($!{EAGAIN} || $!{EWOULDBLOCK}, 'EAGAIN/EWOULDBLOCK set');
+
+  # write the first half of the input, minus one byte we just wrote
+  $olen = int($len / 2) - 1;
+  my $wr1 = syswrite($w, $deflate->[1], $olen, $deflate->[2]);
+  ok($wr1, 'wrote some more');
+  $remain -= $wr1;
+  $deflate->[2] += $wr1;
+  $wrote += $wr1;
+
+  # and we should be able to read something, now
+  $n = $r->sysread($buf, 4096, 0);
+  ok($n, 'read something inflatable');
+  is($buf, substr($orig, 0, $n), 'able to read partial buffer');
+
+  # finish the write and make sure the reader sees it
+  my $wr2 = syswrite($w, $deflate->[1], $remain, $deflate->[2]);
+  is($wr2 + $wrote, $len, 'totally written');
+  $n = $r->sysread(my $rest = '', 4096, 0);
+  is($buf . $rest, $orig, 'completely read after incomplete read');
+
+  # trigger Z_STREAM_END in reader:
+  $deflate->[1] = '';
+  $deflate->[2] = 0;
+  $zout->flush($deflate->[1], Z_FINISH());
+  my $last = syswrite($w, $deflate->[1]);
+  is($r->sysread(my $end = '', 4096, 0), 0, 'got EOF on Z_STREAM_END');
+  ok($w->close, 'closed writer');
+  ok($r->close, 'closed reader');
+}
+
+# make sure ->sysread detects undecodable input
+{
+  $r = $w = undef;
+  pipe($r, $w) or die;
+  Net::NNTP::Deflate->wrap($r);
+  my @warn;
+
+  is(syswrite($w, "random junk\0"), 12, 'wrote random junk (not deflated)');
+  local $SIG{__WARN__} = sub { push @warn, @_ };
+  is($r->sysread(my $junk = '', 4096, 0), undef,
+    'refused to decode random junk');
+  ok($!{ECONNRESET}, 'got ECONNRESET on sysread failure');
+  like(join('', @warn), qr/Error:/, 'warned on inflate error');
+}