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;
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.assertNotNull;
24  import static org.junit.jupiter.api.Assertions.assertThrows;
25  import static org.junit.jupiter.api.Assertions.fail;
26  
27  import java.io.BufferedInputStream;
28  import java.io.ByteArrayInputStream;
29  import java.io.ByteArrayOutputStream;
30  import java.io.File;
31  import java.io.IOException;
32  import java.io.InputStream;
33  import java.io.OutputStream;
34  import java.nio.file.Files;
35  import java.util.ArrayList;
36  import java.util.List;
37  import java.util.Objects;
38  import java.util.concurrent.ExecutionException;
39  import java.util.concurrent.ExecutorService;
40  import java.util.concurrent.Executors;
41  import java.util.concurrent.Future;
42  import java.util.stream.Collectors;
43  import java.util.stream.IntStream;
44  import java.util.zip.Deflater;
45  import java.util.zip.GZIPInputStream;
46  
47  import org.apache.commons.compress.AbstractTest;
48  import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
49  import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
50  import org.apache.commons.compress.archivers.tar.TarConstants;
51  import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
52  import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
53  import org.apache.commons.compress.compressors.gzip.GzipParameters;
54  import org.apache.commons.io.IOUtils;
55  import org.junit.jupiter.api.Test;
56  import org.junit.jupiter.params.ParameterizedTest;
57  import org.junit.jupiter.params.provider.ValueSource;
58  
59  public final class GZipTest extends AbstractTest {
60  
61      private void testCompress666(final int factor, final boolean bufferInputStream, final String localPath) throws ExecutionException, InterruptedException {
62          final ExecutorService executorService = Executors.newFixedThreadPool(10);
63          try {
64              final List<Future<?>> tasks = IntStream.range(0, 200).mapToObj(index -> executorService.submit(() -> {
65                  TarArchiveEntry tarEntry = null;
66                  try (InputStream inputStream = getClass().getResourceAsStream(localPath);
67                          TarArchiveInputStream tarInputStream = new TarArchiveInputStream(
68                                  bufferInputStream ? new BufferedInputStream(new GZIPInputStream(inputStream)) : new GZIPInputStream(inputStream),
69                                  TarConstants.DEFAULT_RCDSIZE * factor, TarConstants.DEFAULT_RCDSIZE)) {
70                      while ((tarEntry = tarInputStream.getNextEntry()) != null) {
71                          assertNotNull(tarEntry);
72                      }
73                  } catch (final IOException e) {
74                      fail(Objects.toString(tarEntry), e);
75                  }
76              })).collect(Collectors.toList());
77              final List<Exception> list = new ArrayList<>();
78              for (final Future<?> future : tasks) {
79                  try {
80                      future.get();
81                  } catch (final Exception e) {
82                      list.add(e);
83                  }
84              }
85              // check:
86              if (!list.isEmpty()) {
87                  fail(list.get(0));
88              }
89              // or:
90              // assertTrue(list.isEmpty(), () -> list.size() + " exceptions: " + list.toString());
91          } finally {
92              executorService.shutdownNow();
93          }
94      }
95  
96      /**
97       * Tests https://issues.apache.org/jira/browse/COMPRESS-666
98       *
99       * A factor of 20 is the default.
100      */
101     @ParameterizedTest
102     @ValueSource(ints = { 1, 2, 4, 8, 16, 20, 32, 64, 128 })
103     public void testCompress666Buffered(final int factor) throws ExecutionException, InterruptedException {
104         testCompress666(factor, true, "/COMPRESS-666/compress-666.tar.gz");
105     }
106 
107     /**
108      * Tests https://issues.apache.org/jira/browse/COMPRESS-666
109      *
110      * A factor of 20 is the default.
111      */
112     @ParameterizedTest
113     @ValueSource(ints = { 1, 2, 4, 8, 16, 20, 32, 64, 128 })
114     public void testCompress666Unbuffered(final int factor) throws ExecutionException, InterruptedException {
115         testCompress666(factor, false, "/COMPRESS-666/compress-666.tar.gz");
116     }
117 
118     @Test
119     public void testConcatenatedStreamsReadFirstOnly() throws Exception {
120         final File input = getFile("multiple.gz");
121         try (InputStream is = Files.newInputStream(input.toPath())) {
122             try (CompressorInputStream in = new CompressorStreamFactory().createCompressorInputStream("gz", is)) {
123                 assertEquals('a', in.read());
124                 assertEquals(-1, in.read());
125             }
126         }
127     }
128 
129     @Test
130     public void testConcatenatedStreamsReadFully() throws Exception {
131         final File input = getFile("multiple.gz");
132         try (InputStream is = Files.newInputStream(input.toPath())) {
133             try (CompressorInputStream in = new GzipCompressorInputStream(is, true)) {
134                 assertEquals('a', in.read());
135                 assertEquals('b', in.read());
136                 assertEquals(0, in.available());
137                 assertEquals(-1, in.read());
138             }
139         }
140     }
141 
142     /**
143      * @see "https://issues.apache.org/jira/browse/COMPRESS-84"
144      */
145     @Test
146     public void testCorruptedInput() throws Exception {
147         final byte[] data = readAllBytes("bla.tgz");
148         try (InputStream in = new ByteArrayInputStream(data, 0, data.length - 1);
149                 CompressorInputStream cin = new CompressorStreamFactory().createCompressorInputStream("gz", in);
150                 OutputStream out = new ByteArrayOutputStream()) {
151             assertThrows(IOException.class, () -> IOUtils.copy(cin, out), "Expected an exception");
152         }
153     }
154 
155     private void testExtraFlags(final int compressionLevel, final int flag, final int bufferSize) throws Exception {
156         final byte[] content = readAllBytes("test3.xml");
157 
158         final ByteArrayOutputStream bout = new ByteArrayOutputStream();
159 
160         final GzipParameters parameters = new GzipParameters();
161         parameters.setCompressionLevel(compressionLevel);
162         parameters.setBufferSize(bufferSize);
163         try (GzipCompressorOutputStream out = new GzipCompressorOutputStream(bout, parameters)) {
164             IOUtils.copy(new ByteArrayInputStream(content), out);
165             out.flush();
166         }
167 
168         assertEquals(flag, bout.toByteArray()[8], "extra flags (XFL)");
169     }
170 
171     @Test
172     public void testExtraFlagsBestCompression() throws Exception {
173         testExtraFlags(Deflater.BEST_COMPRESSION, 2, 1024);
174     }
175 
176     @Test
177     public void testExtraFlagsDefaultCompression() throws Exception {
178         testExtraFlags(Deflater.DEFAULT_COMPRESSION, 0, 4096);
179     }
180 
181     @Test
182     public void testExtraFlagsFastestCompression() throws Exception {
183         testExtraFlags(Deflater.BEST_SPEED, 4, 128);
184     }
185 
186     @Test
187     public void testGzipCreation() throws Exception {
188         final File input = getFile("test1.xml");
189         final File output = newTempFile("test1.xml.gz");
190         try (OutputStream out = Files.newOutputStream(output.toPath())) {
191             try (CompressorOutputStream cos = new CompressorStreamFactory().createCompressorOutputStream("gz", out)) {
192                 Files.copy(input.toPath(), cos);
193             }
194         }
195     }
196 
197     @Test
198     public void testGzipUnarchive() throws Exception {
199         final File input = getFile("bla.tgz");
200         final File output = newTempFile("bla.tar");
201         try (InputStream is = Files.newInputStream(input.toPath())) {
202             try (CompressorInputStream in = new CompressorStreamFactory().createCompressorInputStream("gz", is);) {
203                 Files.copy(in, output.toPath());
204             }
205         }
206     }
207 
208     @Test
209     public void testInteroperabilityWithGzipCompressorInputStream() throws Exception {
210         final byte[] content = readAllBytes("test3.xml");
211         final ByteArrayOutputStream bout = new ByteArrayOutputStream();
212         final GzipParameters parameters = new GzipParameters();
213         parameters.setCompressionLevel(Deflater.BEST_COMPRESSION);
214         parameters.setOperatingSystem(3);
215         parameters.setFilename("test3.xml");
216         assertEquals(parameters.getFilename(), parameters.getFileName());
217         parameters.setFileName("test3.xml");
218         assertEquals(parameters.getFilename(), parameters.getFileName());
219         parameters.setComment("Test file");
220         parameters.setModificationTime(System.currentTimeMillis());
221         try (GzipCompressorOutputStream out = new GzipCompressorOutputStream(bout, parameters)) {
222             out.write(content);
223             out.flush();
224         }
225         try (GzipCompressorInputStream in = new GzipCompressorInputStream(new ByteArrayInputStream(bout.toByteArray()))) {
226             final byte[] content2 = IOUtils.toByteArray(in);
227             assertArrayEquals(content, content2, "uncompressed content");
228         }
229     }
230 
231     @Test
232     public void testInteroperabilityWithGZIPInputStream() throws Exception {
233         final byte[] content = readAllBytes("test3.xml");
234         final ByteArrayOutputStream bout = new ByteArrayOutputStream();
235         final GzipParameters parameters = new GzipParameters();
236         parameters.setCompressionLevel(Deflater.BEST_COMPRESSION);
237         parameters.setOperatingSystem(3);
238         parameters.setFilename("test3.xml");
239         assertEquals(parameters.getFilename(), parameters.getFileName());
240         parameters.setFileName("test3.xml");
241         assertEquals(parameters.getFilename(), parameters.getFileName());
242         parameters.setComment("Test file");
243         parameters.setModificationTime(System.currentTimeMillis());
244         try (GzipCompressorOutputStream out = new GzipCompressorOutputStream(bout, parameters)) {
245             out.write(content);
246             out.flush();
247         }
248         final GZIPInputStream in = new GZIPInputStream(new ByteArrayInputStream(bout.toByteArray()));
249         final byte[] content2 = IOUtils.toByteArray(in);
250         assertArrayEquals(content, content2, "uncompressed content");
251     }
252 
253     @ParameterizedTest
254     @ValueSource(ints = { 0, -1 })
255     public void testInvalidBufferSize(final int bufferSize) {
256         final GzipParameters parameters = new GzipParameters();
257         assertThrows(IllegalArgumentException.class, () -> parameters.setBufferSize(bufferSize), "IllegalArgumentException not thrown");
258     }
259 
260     @ParameterizedTest
261     @ValueSource(ints = { 10, -5 })
262     public void testInvalidCompressionLevel(final int compressionLevel) {
263         final GzipParameters parameters = new GzipParameters();
264         assertThrows(IllegalArgumentException.class, () -> parameters.setCompressionLevel(compressionLevel), "IllegalArgumentException not thrown");
265     }
266 
267     @Test
268     public void testMetadataRoundTrip() throws Exception {
269         final ByteArrayOutputStream bout = new ByteArrayOutputStream();
270 
271         final GzipParameters parameters = new GzipParameters();
272         parameters.setCompressionLevel(Deflater.BEST_COMPRESSION);
273         parameters.setModificationTime(123456000);
274         parameters.setOperatingSystem(13);
275         parameters.setFilename("test3.xml");
276         assertEquals(parameters.getFilename(), parameters.getFileName());
277         parameters.setFileName("test3.xml");
278         assertEquals(parameters.getFilename(), parameters.getFileName());
279         parameters.setComment("Umlaute möglich?");
280         try (GzipCompressorOutputStream out = new GzipCompressorOutputStream(bout, parameters)) {
281             Files.copy(getFile("test3" + ".xml").toPath(), out);
282         }
283 
284         final GzipCompressorInputStream input = new GzipCompressorInputStream(new ByteArrayInputStream(bout.toByteArray()));
285         input.close();
286         final GzipParameters readParams = input.getMetaData();
287         assertEquals(Deflater.BEST_COMPRESSION, readParams.getCompressionLevel());
288         assertEquals(123456000, readParams.getModificationTime());
289         assertEquals(13, readParams.getOperatingSystem());
290         assertEquals("test3.xml", readParams.getFileName());
291         assertEquals("test3.xml", readParams.getFilename());
292         assertEquals("Umlaute möglich?", readParams.getComment());
293     }
294 
295     @Test
296     public void testMultiByteReadConsistentlyReturnsMinusOneAtEof() throws IOException {
297         final File input = getFile("bla.tgz");
298         final byte[] buf = new byte[2];
299         try (InputStream is = Files.newInputStream(input.toPath());
300                 GzipCompressorInputStream in = new GzipCompressorInputStream(is)) {
301             IOUtils.toByteArray(in);
302             assertEquals(-1, in.read(buf));
303             assertEquals(-1, in.read(buf));
304         }
305     }
306 
307     @Test
308     public void testOverWrite() throws Exception {
309         final GzipCompressorOutputStream out = new GzipCompressorOutputStream(new ByteArrayOutputStream());
310         out.close();
311         assertThrows(IOException.class, () -> out.write(0), "IOException expected");
312     }
313 
314     @Test
315     public void testSingleByteReadConsistentlyReturnsMinusOneAtEof() throws IOException {
316         final File input = getFile("bla.tgz");
317         try (InputStream is = Files.newInputStream(input.toPath());
318                 GzipCompressorInputStream in = new GzipCompressorInputStream(is)) {
319             IOUtils.toByteArray(in);
320             assertEquals(-1, in.read());
321             assertEquals(-1, in.read());
322         }
323     }
324 }