1
2use lib 't';
3use strict;
4use warnings;
5use bytes;
6
7use Test::More ;
8use CompTestUtils;
9
10sub run
11{
12    my $CompressClass   = identify();
13    my $UncompressClass = getInverse($CompressClass);
14    my $Error           = getErrorRef($CompressClass);
15    my $UnError         = getErrorRef($UncompressClass);
16
17#    my $hello = <<EOM ;
18#hello world
19#this is a test
20#some more stuff on this line
21#and finally...
22#EOM
23
24    # ASCII hex equivalent of the text above. This makes the test
25    # harness behave identically on an EBCDIC platform.
26    my $hello =
27      "\x68\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64\x0a\x74\x68\x69\x73" .
28      "\x20\x69\x73\x20\x61\x20\x74\x65\x73\x74\x0a\x73\x6f\x6d\x65\x20" .
29      "\x6d\x6f\x72\x65\x20\x73\x74\x75\x66\x66\x20\x6f\x6e\x20\x74\x68" .
30      "\x69\x73\x20\x6c\x69\x6e\x65\x0a\x61\x6e\x64\x20\x66\x69\x6e\x61" .
31      "\x6c\x6c\x79\x2e\x2e\x2e\x0a" ;
32
33    my $blocksize = 10 ;
34
35
36    my ($info, $compressed) = mkComplete($CompressClass, $hello);
37
38    my $header_size  = $info->{HeaderLength};
39    my $trailer_size = $info->{TrailerLength};
40    my $fingerprint_size = $info->{FingerprintLength};
41    ok 1, "Compressed size is " . length($compressed) ;
42    ok 1, "Fingerprint size is $fingerprint_size" ;
43    ok 1, "Header size is $header_size" ;
44    ok 1, "Trailer size is $trailer_size" ;
45
46    foreach my $fb ( qw( filehandle buffer ) )
47    {
48        for my $trans ( 0 .. 1)
49        {
50            title "Truncating $CompressClass, Source $fb, Transparent $trans";
51
52
53            foreach my $i (1 .. $fingerprint_size-1)
54            {
55                my $lex = LexFile->new( my $name );
56                my $input;
57
58                title "Fingerprint Truncation - length $i, Transparent $trans";
59
60                my $part = substr($compressed, 0, $i);
61                if ($fb eq 'filehandle')
62                {
63                    writeFile($name, $part);
64                    $input = $name ;
65                }
66                else
67                {
68                    $input = \$part;
69                }
70
71                my $gz = $UncompressClass->can('new')->( $UncompressClass, $input,
72                                              -BlockSize   => $blocksize,
73                                              -Transparent => $trans );
74                if ($trans) {
75                    ok $gz;
76                    ok ! $gz->error() ;
77                    my $buff ;
78                    is $gz->read($buff, 5000), length($part) ;
79                    ok $buff eq $part ;
80                    ok $gz->eof() ;
81                    $gz->close();
82                }
83                else {
84                    ok !$gz;
85                }
86
87            }
88
89            #
90            # Any header corruption past the fingerprint is considered catastrophic
91            # so even if Transparent is set, it should still fail
92            #
93            foreach my $i ($fingerprint_size .. $header_size -1)
94            {
95                my $lex = LexFile->new( my $name );
96                my $input;
97
98                title "Header Truncation - length $i, Source $fb, Transparent $trans";
99
100                my $part = substr($compressed, 0, $i);
101                if ($fb eq 'filehandle')
102                {
103                    writeFile($name, $part);
104                    $input = $name ;
105                }
106                else
107                {
108                    $input = \$part;
109                }
110
111                ok ! defined $UncompressClass->can('new')->( $UncompressClass, $input,
112                                                  -BlockSize   => $blocksize,
113                                                  -Transparent => $trans );
114                #ok $gz->eof() ;
115            }
116
117            # Test corruption directly after the header
118            # In this case the uncompression object will have been created,
119            # so need to check that subsequent reads from the object fail
120            if ($header_size > 0)
121            {
122                for my $mode (qw(block line para record slurp))
123                {
124
125                    title "Corruption after header - Mode $mode, Source $fb, Transparent $trans";
126
127                    my $lex = LexFile->new( my $name );
128                    my $input;
129
130                    my $part = substr($compressed, 0, $header_size);
131                    # Append corrupt data
132                    $part .= "\xFF" x 100 ;
133                    if ($fb eq 'filehandle')
134                    {
135                        writeFile($name, $part);
136                        $input = $name ;
137                    }
138                    else
139                    {
140                        $input = \$part;
141                    }
142
143                    ok my $gz = $UncompressClass->can('new')->( $UncompressClass, $input,
144                                                     -Strict      => 1,
145                                                     -BlockSize   => $blocksize,
146                                                     -Transparent => $trans )
147                         or diag $$UnError;
148
149                    my $un ;
150                    my $status = 1;
151                    if ($mode eq 'block')
152                    {
153                        $status = $gz->read($un) ;
154                        is $status, -1, "got -1";
155                    }
156                    else
157                    {
158                        if ($mode eq 'line')
159                        {
160                            $status = <$gz>;
161                        }
162                        elsif ($mode eq 'para')
163                        {
164                            local $/ = "\n\n";
165                            $status = <$gz>;
166                        }
167                        elsif ($mode eq 'record')
168                        {
169                            local $/ = \ 4;
170                            $status = <$gz>;
171                        }
172                        elsif ($mode eq 'slurp')
173                        {
174                            local $/ ;
175                            $status = <$gz>;
176                        }
177
178                        is $status, undef, "got undef";
179                    }
180
181                    ok $gz->error() ;
182                    $gz->close();
183                }
184            }
185
186            # Back to truncation tests
187
188            foreach my $i ($header_size .. length($compressed) - 1 - $trailer_size)
189            {
190                next if $i == 0 ;
191
192                for my $mode (qw(block line))
193                {
194
195                    title "Compressed Data Truncation - length $i, MOde $mode, Source $fb, Transparent $trans";
196
197                    my $lex = LexFile->new( my $name );
198                    my $input;
199
200                    my $part = substr($compressed, 0, $i);
201                    if ($fb eq 'filehandle')
202                    {
203                        writeFile($name, $part);
204                        $input = $name ;
205                    }
206                    else
207                    {
208                        $input = \$part;
209                    }
210
211                    ok my $gz = $UncompressClass->can('new')->( $UncompressClass, $input,
212                                                     -Strict      => 1,
213                                                     -BlockSize   => $blocksize,
214                                                     -Transparent => $trans )
215                         or diag $$UnError;
216
217                    my $un ;
218                    if ($mode eq 'block')
219                    {
220                        my $status = 1 ;
221                        $status = $gz->read($un) while $status > 0 ;
222                        cmp_ok $status, "<", 0 ;
223                    }
224                    else
225                    {
226                        1 while <$gz> ;
227                    }
228                    ok $gz->error() ;
229                    cmp_ok $gz->errorNo(), '<', 0 ;
230                    # ok $gz->eof()
231                    #     or die "EOF";
232                    $gz->close();
233                }
234            }
235
236            # RawDeflate and Zstandard do not have a trailer
237            next if $CompressClass eq 'IO::Compress::RawDeflate' ;
238            next if $CompressClass eq 'IO::Compress::Zstd' ;
239
240            title "Compressed Trailer Truncation";
241            foreach my $i (length($compressed) - $trailer_size .. length($compressed) -1 )
242            {
243                foreach my $lax (0, 1)
244                {
245                    my $lex = LexFile->new( my $name );
246                    my $input;
247
248                    ok 1, "Compressed Trailer Truncation - Length $i, Lax $lax, Transparent $trans" ;
249                    my $part = substr($compressed, 0, $i);
250                    if ($fb eq 'filehandle')
251                    {
252                        writeFile($name, $part);
253                        $input = $name ;
254                    }
255                    else
256                    {
257                        $input = \$part;
258                    }
259
260                    ok my $gz = $UncompressClass->can('new')->( $UncompressClass, $input,
261                                                     -BlockSize   => $blocksize,
262                                                     -Strict      => !$lax,
263                                                     -Append      => 1,
264                                                     -Transparent => $trans );
265                    my $un = '';
266                    my $status = 1 ;
267                    $status = $gz->read($un) while $status > 0 ;
268
269                    if ($lax)
270                    {
271                        is $un, $hello;
272                        is $status, 0
273                            or diag "Status $status Error is " . $gz->error() ;
274                        ok $gz->eof()
275                            or diag "Status $status Error is " . $gz->error() ;
276                        ok ! $gz->error() ;
277                    }
278                    else
279                    {
280                        cmp_ok $status, "<", 0
281                            or diag "Status $status Error is " . $gz->error() ;
282                        ok $gz->eof()
283                            or diag "Status $status Error is " . $gz->error() ;
284                        ok $gz->error() ;
285                    }
286
287                    $gz->close();
288                }
289            }
290        }
291    }
292}
293
2941;
295