View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.commons.compress.compressors.snappy;
20  
21  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
22  import static org.junit.jupiter.api.Assertions.assertEquals;
23  import static org.junit.jupiter.api.Assertions.assertFalse;
24  import static org.junit.jupiter.api.Assertions.assertThrows;
25  import static org.junit.jupiter.api.Assertions.assertTrue;
26  
27  import java.io.ByteArrayInputStream;
28  import java.io.ByteArrayOutputStream;
29  import java.io.File;
30  import java.io.IOException;
31  import java.io.InputStream;
32  import java.nio.file.Files;
33  import java.nio.file.Path;
34  
35  import org.apache.commons.compress.AbstractTest;
36  import org.apache.commons.compress.archivers.zip.ZipFile;
37  import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
38  import org.apache.commons.io.IOUtils;
39  import org.junit.jupiter.api.Test;
40  
41  public final class FramedSnappyCompressorInputStreamTest extends AbstractTest {
42  
43      private static byte[] generateTestData(final int inputSize) {
44          final byte[] arr = new byte[inputSize];
45          for (int i = 0; i < arr.length; i++) {
46            arr[i] = (byte) (65 + i % 10);
47          }
48  
49          return arr;
50      }
51  
52      private long mask(final long x) {
53          return (x >>> 15 | x << 17) + FramedSnappyCompressorInputStream.MASK_OFFSET & 0xffffFFFFL;
54      }
55  
56      @Test
57      public void testAvailable() throws Exception {
58          try (InputStream isSz = newInputStream("mixed.txt.sz");
59                  FramedSnappyCompressorInputStream in = new FramedSnappyCompressorInputStream(isSz)) {
60              assertEquals(0, in.available()); // no chunk read so far
61              assertEquals('1', in.read());
62              assertEquals(3, in.available()); // remainder of first uncompressed block
63              assertEquals(3, in.read(new byte[5], 0, 3));
64              assertEquals('5', in.read());
65              assertEquals(0, in.available()); // end of chunk, must read next one
66              assertEquals(4, in.read(new byte[5], 0, 4));
67              assertEquals('5', in.read());
68          }
69      }
70  
71      @Test
72      public void testChecksumUnmasking() {
73          testChecksumUnmasking(0xc757L);
74          testChecksumUnmasking(0xffffc757L);
75      }
76  
77      private void testChecksumUnmasking(final long x) {
78          assertEquals(Long.toHexString(x), Long.toHexString(FramedSnappyCompressorInputStream.unmask(mask(x))));
79      }
80  
81      @Test
82      public void testFinishWithNoWrite() throws IOException {
83          final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
84          try (FramedSnappyCompressorOutputStream compressor = new FramedSnappyCompressorOutputStream(buffer)) {
85              // do nothing here. this will test that flush on close doesn't throw any exceptions if no data is written.
86          }
87          assertTrue(buffer.size() == 10, "Only the signature gets written.");
88      }
89  
90      /**
91       * Something big enough to make buffers slide.
92       */
93      @Test
94      public void testLoremIpsum() throws Exception {
95          final Path outputSz = newTempPath("lorem-ipsum.1");
96          final Path outputGz = newTempPath("lorem-ipsum.2");
97          try (InputStream isSz = newInputStream("lorem-ipsum.txt.sz")) {
98              try (InputStream in = new FramedSnappyCompressorInputStream(isSz)) {
99                  Files.copy(in, outputSz);
100             }
101             try (InputStream isGz = newInputStream("lorem-ipsum.txt.gz");
102                     InputStream in = new GzipCompressorInputStream(isGz)) {
103                 Files.copy(in, outputGz);
104             }
105         }
106 
107         assertArrayEquals(Files.readAllBytes(outputSz), Files.readAllBytes(outputGz));
108     }
109 
110     @Test
111     public void testMatches() throws IOException {
112         assertFalse(FramedSnappyCompressorInputStream.matches(new byte[10], 10));
113         final byte[] expected = readAllBytes("bla.tar.sz");
114         assertFalse(FramedSnappyCompressorInputStream.matches(expected, 9));
115         assertTrue(FramedSnappyCompressorInputStream.matches(expected, 10));
116         assertTrue(FramedSnappyCompressorInputStream.matches(expected, 12));
117     }
118 
119     @Test
120     public void testMultiByteReadConsistentlyReturnsMinusOneAtEof() throws IOException {
121         final File input = getFile("bla.tar.sz");
122         final byte[] buf = new byte[2];
123         try (InputStream is = Files.newInputStream(input.toPath());
124                 FramedSnappyCompressorInputStream in = new FramedSnappyCompressorInputStream(is);) {
125             IOUtils.toByteArray(in);
126             assertEquals(-1, in.read(buf));
127             assertEquals(-1, in.read(buf));
128         }
129     }
130 
131     @Test
132     public void testReadIWAFile() throws Exception {
133         try (ZipFile zip = ZipFile.builder().setFile(getFile("testNumbersNew.numbers")).get()) {
134             try (InputStream is = zip.getInputStream(zip.getEntry("Index/Document.iwa"))) {
135                 try (FramedSnappyCompressorInputStream in = new FramedSnappyCompressorInputStream(is, FramedSnappyDialect.IWORK_ARCHIVE)) {
136                     Files.copy(in, newTempFile("snappyIWATest.raw").toPath());
137                 }
138             }
139         }
140     }
141 
142     /**
143      * @see "https://issues.apache.org/jira/browse/COMPRESS-358"
144      */
145     @Test
146     public void testReadIWAFileWithBiggerOffset() throws Exception {
147         final File o = newTempFile("COMPRESS-358.raw");
148         try (InputStream is = newInputStream("COMPRESS-358.iwa");
149                 FramedSnappyCompressorInputStream in = new FramedSnappyCompressorInputStream(is, 1 << 16, FramedSnappyDialect.IWORK_ARCHIVE);) {
150             Files.copy(in, o.toPath());
151         }
152         try (InputStream a = Files.newInputStream(o.toPath());
153                 InputStream e = newInputStream("COMPRESS-358.uncompressed")) {
154             final byte[] expected = IOUtils.toByteArray(e);
155             final byte[] actual = IOUtils.toByteArray(a);
156             assertArrayEquals(expected, actual);
157         }
158     }
159 
160     @Test
161     public void testRemainingChunkTypes() throws Exception {
162         try (ByteArrayOutputStream out = new ByteArrayOutputStream();
163                 InputStream isSz = newInputStream("mixed.txt.sz");
164                 FramedSnappyCompressorInputStream in = new FramedSnappyCompressorInputStream(isSz);) {
165             IOUtils.copy(in, out);
166             assertArrayEquals(new byte[] { '1', '2', '3', '4', '5', '6', '7', '8', '9', '5', '6', '7', '8', '9', '5', '6', '7', '8', '9', '5', '6', '7', '8',
167                     '9', '5', '6', '7', '8', '9', 10, '1', '2', '3', '4', '1', '2', '3', '4', }, out.toByteArray());
168         }
169     }
170 
171     @Test
172     public void testSingleByteReadConsistentlyReturnsMinusOneAtEof() throws IOException {
173         final File input = getFile("bla.tar.sz");
174         try (InputStream is = Files.newInputStream(input.toPath());
175                 FramedSnappyCompressorInputStream in = new FramedSnappyCompressorInputStream(is);) {
176             IOUtils.toByteArray(in);
177             assertEquals(-1, in.read());
178             assertEquals(-1, in.read());
179         }
180     }
181 
182     @Test
183     public void testUnskippableChunk() {
184         final byte[] input = { (byte) 0xff, 6, 0, 0, 's', 'N', 'a', 'P', 'p', 'Y', 2, 2, 0, 0, 1, 1 };
185         try (FramedSnappyCompressorInputStream in = new FramedSnappyCompressorInputStream(new ByteArrayInputStream(input))) {
186             final IOException exception = assertThrows(IOException.class, () -> in.read());
187             assertTrue(exception.getMessage().contains("Unskippable chunk"));
188         } catch (final IOException ex) {
189         }
190     }
191 
192     @Test
193     public void testWriteByteArrayVsWriteByte() throws IOException {
194         ByteArrayOutputStream buffer = new ByteArrayOutputStream();
195         final byte[] bytes = "abcdefghijklmnop".getBytes();
196         try (FramedSnappyCompressorOutputStream compressor = new FramedSnappyCompressorOutputStream(buffer)) {
197             compressor.write(bytes);
198             compressor.finish();
199         }
200         final byte[] bulkOutput = buffer.toByteArray();
201         buffer = new ByteArrayOutputStream();
202         try (FramedSnappyCompressorOutputStream compressor = new FramedSnappyCompressorOutputStream(buffer)) {
203             for (final byte element : bytes) {
204                 compressor.write(element);
205             }
206             compressor.finish();
207         }
208         assertArrayEquals(bulkOutput, buffer.toByteArray());
209     }
210 
211     @Test
212     public void testWriteDataLargerThanBufferOneCall() throws IOException {
213         final int inputSize = 500_000;
214         final byte[] data = generateTestData(inputSize);
215         final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
216         try (FramedSnappyCompressorOutputStream compressor = new FramedSnappyCompressorOutputStream(outputStream)) {
217             compressor.write(data, 0, data.length);
218             compressor.finish();
219         }
220         final byte[] compressed = outputStream.toByteArray();
221 
222         byte[] decompressed = {};
223         try (ByteArrayInputStream bytesIn = new ByteArrayInputStream(compressed, 0, compressed.length);
224                  FramedSnappyCompressorInputStream decompressor = new FramedSnappyCompressorInputStream(bytesIn)) {
225             int i;
226             final ByteArrayOutputStream decompressedOutputStream = new ByteArrayOutputStream();
227             while (-1 != (i = decompressor.read())) {
228                 decompressedOutputStream.write(i);
229             }
230             decompressed = decompressedOutputStream.toByteArray();
231         }
232         assertArrayEquals(data, decompressed);
233     }
234 
235 }