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   *   https://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  
20  package org.apache.commons.compress.compressors.gzip;
21  
22  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
23  import static org.junit.jupiter.api.Assertions.assertEquals;
24  
25  import java.io.IOException;
26  import java.io.OutputStream;
27  import java.nio.charset.StandardCharsets;
28  import java.nio.file.Files;
29  import java.nio.file.Path;
30  import java.nio.file.StandardOpenOption;
31  import java.util.ArrayList;
32  import java.util.HashSet;
33  import java.util.List;
34  import java.util.concurrent.atomic.AtomicLong;
35  
36  import org.apache.commons.io.IOUtils;
37  import org.apache.commons.io.file.PathUtils;
38  import org.apache.commons.io.function.IOStream;
39  import org.apache.commons.io.input.RandomAccessFileInputStream;
40  import org.junit.jupiter.api.Disabled;
41  import org.junit.jupiter.api.Test;
42  import org.junit.jupiter.api.io.TempDir;
43  
44  /**
45   * Tests {@link GzipCompressorInputStream}.
46   */
47  class GzipCompressorInputStreamTest {
48  
49      @TempDir
50      Path tempDir;
51  
52      /**
53       * Extracts members of a GZIP file to the temporary directory.
54       */
55      @SuppressWarnings("resource")
56      private List<Path> extractMembers(final String sourceGzipPath) throws IOException {
57          final List<GzipParameters> members = new ArrayList<>();
58          final Path tempFile = tempDir.resolve("temp.bin");
59          // Extract GZIP members in one temp file.
60          // Callbacks are invoked while reading with the member size known only after reading a member's trailer.
61          // @formatter:off
62          try (OutputStream fos = Files.newOutputStream(tempFile);
63                  GzipCompressorInputStream gis = GzipCompressorInputStream.builder()
64                  .setFile(sourceGzipPath)
65                  .setDecompressConcatenated(true)
66                  .setOnMemberEnd(in -> members.add(in.getMetaData()))
67                  .get()) {
68              // @formatter:on
69              IOUtils.copy(gis, fos);
70          }
71          final List<Path> resolved = new ArrayList<>(members.size());
72          final AtomicLong startPos = new AtomicLong();
73          // Read temp file and write each member file.
74          // @formatter:off
75          try (RandomAccessFileInputStream rafIs = RandomAccessFileInputStream.builder()
76                  .setPath(tempFile)
77                  .setCloseOnClose(true)
78                  .get()) {
79              // @formatter:on
80              IOStream.of(members).forEach(e -> {
81                  final Path member = tempDir.resolve(e.getFileName());
82                  resolved.add(member);
83                  try (OutputStream os = Files.newOutputStream(member, StandardOpenOption.CREATE_NEW, StandardOpenOption.TRUNCATE_EXISTING)) {
84                      startPos.addAndGet(rafIs.copy(startPos.get(), e.getTrailerISize(), os));
85                  }
86              });
87          }
88          return resolved;
89      }
90  
91      @Test
92      @Disabled
93      void testGzipParametersMembersIo() throws IOException {
94          final Path targetFile = tempDir.resolve("test.gz");
95          final String sourceFileName1 = "file1";
96          final String sourceFileName2 = "file2";
97          final Path tempSourceFile1 = tempDir.resolve(sourceFileName1);
98          final Path tempSourceFile2 = tempDir.resolve(sourceFileName2);
99          final byte[] bytes1 = "<text>Hello World 1!</text>".getBytes(StandardCharsets.UTF_8);
100         final byte[] bytes2 = "<text>Hello World 2!</text>".getBytes(StandardCharsets.UTF_8);
101         Files.write(tempSourceFile1, bytes1);
102         Files.write(tempSourceFile2, bytes2);
103         final GzipParameters parameters1 = new GzipParameters();
104         final GzipParameters parameters2 = new GzipParameters();
105         parameters1.setFileName(sourceFileName1);
106         parameters2.setFileName(sourceFileName2);
107         try (OutputStream fos = Files.newOutputStream(targetFile);
108                 GzipCompressorOutputStream gos = new GzipCompressorOutputStream(fos, parameters1)) {
109             gos.write(tempSourceFile1);
110             gos.finish();
111             gos.write(tempSourceFile2);
112             gos.finish();
113         }
114         try (GzipCompressorInputStream gis = GzipCompressorInputStream.builder().setPath(targetFile).setDecompressConcatenated(false).get()) {
115             assertEquals(parameters1, gis.getMetaData());
116             assertArrayEquals(bytes1, IOUtils.toByteArray(gis));
117         }
118         try (GzipCompressorInputStream gis = GzipCompressorInputStream.builder().setPath(targetFile).setDecompressConcatenated(true).get()) {
119             assertEquals(parameters1, gis.getMetaData());
120             // assertArrayEquals(ArrayUtils.addAll(bytes1, bytes2), IOUtils.toByteArray(gis));
121         }
122     }
123 
124     /**
125      * Tests file from gzip 1.13.
126      *
127      * <pre>{@code
128      * gzip --keep --name --best -c hello1.txt >members.gz
129      * gzip --keep --name --best -c hello2.txt >>members.gz
130      * }</pre>
131      *
132      * @throws IOException on test failure.
133      */
134     @Test
135     void testOnMemberFirstAll() throws IOException {
136         final List<GzipParameters> parametersStart = new ArrayList<>();
137         final List<GzipParameters> parametersEnd = new ArrayList<>();
138         // Concatenated members, same file
139         // @formatter:off
140         try (GzipCompressorInputStream gis = GzipCompressorInputStream.builder()
141                 .setFile("src/test/resources/org/apache/commons/compress/gzip/members.gz")
142                 .setDecompressConcatenated(true)
143                 .setOnMemberStart(in -> parametersStart.add(in.getMetaData()))
144                 .setOnMemberEnd(in -> parametersEnd.add(in.getMetaData()))
145                 .get()) {
146             // @formatter:on
147             assertEquals("hello1.txt", gis.getMetaData().getFileName());
148             assertEquals("Hello1\nHello2\n", IOUtils.toString(gis, StandardCharsets.ISO_8859_1));
149             assertEquals("hello2.txt", gis.getMetaData().getFileName());
150         }
151         assertEquals(2, parametersStart.size());
152         assertEquals(2, parametersEnd.size());
153         assertEquals(parametersStart, parametersEnd);
154         // Make sure we are not reusing GzipParameters anymore.
155         assertEquals(2, new HashSet<>(parametersStart).size());
156         assertEquals(2, new HashSet<>(parametersEnd).size());
157         // trailers
158         assertEquals(4202744527L, parametersEnd.get(0).getTrailerCrc());
159         assertEquals(7, parametersEnd.get(0).getTrailerISize());
160         assertEquals(3517815052L, parametersEnd.get(1).getTrailerCrc());
161         assertEquals(7, parametersEnd.get(1).getTrailerISize());
162     }
163 
164     /**
165      * Tests file from gzip 1.13.
166      *
167      * <pre>{@code
168      * gzip --keep --name --best -c hello1.txt >members.gz
169      * gzip --keep --name --best -c hello2.txt >>members.gz
170      * }</pre>
171      *
172      * @throws IOException on test failure.
173      */
174     @Test
175     void testOnMemberFirstOnly() throws IOException {
176         final List<GzipParameters> parametersStart = new ArrayList<>();
177         final List<GzipParameters> parametersEnd = new ArrayList<>();
178         // First member only
179         // @formatter:off
180         try (GzipCompressorInputStream gis = GzipCompressorInputStream.builder()
181                 .setFile("src/test/resources/org/apache/commons/compress/gzip/members.gz")
182                 .setDecompressConcatenated(false)
183                 .setOnMemberStart(in -> parametersStart.add(in.getMetaData()))
184                 .setOnMemberEnd(in -> parametersEnd.add(in.getMetaData()))
185                 .get()) {
186             // @formatter:on
187             assertEquals("hello1.txt", gis.getMetaData().getFileName());
188             assertEquals("Hello1\n", IOUtils.toString(gis, StandardCharsets.ISO_8859_1));
189             assertEquals("hello1.txt", gis.getMetaData().getFileName());
190         }
191         assertEquals(1, parametersStart.size());
192         assertEquals(1, parametersEnd.size());
193         assertEquals(parametersStart, parametersEnd);
194         // trailer
195         assertEquals(4202744527L, parametersEnd.get(0).getTrailerCrc());
196         assertEquals(7, parametersEnd.get(0).getTrailerISize());
197     }
198 
199     /**
200      * Tests file from gzip 1.13.
201      *
202      * <pre>{@code
203      * gzip --keep --name --best -c hello1.txt >members.gz
204      * gzip --keep --name --best -c hello2.txt >>members.gz
205      * }</pre>
206      *
207      * @throws IOException on test failure.
208      */
209     @Test
210     void testOnMemberSaveAsFiles() throws IOException {
211         final List<Path> resolved = extractMembers("src/test/resources/org/apache/commons/compress/gzip/members.gz");
212         assertEquals("Hello1\n", PathUtils.readString(resolved.get(0), StandardCharsets.ISO_8859_1));
213         assertEquals("Hello2\n", PathUtils.readString(resolved.get(1), StandardCharsets.ISO_8859_1));
214     }
215 
216     /**
217      * Tests file from gzip 1.13 for input files of size 0.
218      *
219      * <pre>{@code
220      * gzip --keep --name --best -c hello-size-0-a.txt >members-size-0.gz
221      * gzip --keep --name --best -c hello-size-0-b.txt >>members-size-0.gz
222      * gzip --keep --name --best -c hello-size-0-c.txt >>members-size-0.gz
223      * }</pre>
224      *
225      * @throws IOException on test failure.
226      */
227     @SuppressWarnings("resource")
228     @Test
229     void testOnMemberSaveAsSize0Files() throws IOException {
230         final List<Path> resolved = extractMembers("src/test/resources/org/apache/commons/compress/gzip/members-size-0.gz");
231         assertEquals(3, resolved.size());
232         IOStream.of(resolved).forEach(p -> {
233             assertEquals(0, Files.size(p));
234             assertEquals("", PathUtils.readString(p, StandardCharsets.ISO_8859_1));
235         });
236     }
237 
238     /**
239      * Tests file from gzip 1.13.
240      *
241      * <pre>{@code
242      * gzip --keep --name --best -c hello1.txt >members.gz
243      * gzip --keep --name --best -c hello2.txt >>members.gz
244      * }</pre>
245      *
246      * @throws IOException on test failure.
247      */
248     @Test
249     void testReadGzipFileCreatedByCli() throws IOException {
250         // First member only
251         try (GzipCompressorInputStream gis = GzipCompressorInputStream.builder().setFile("src/test/resources/org/apache/commons/compress/gzip/members.gz")
252                 .get()) {
253             assertEquals("hello1.txt", gis.getMetaData().getFileName());
254             assertEquals("Hello1\n", IOUtils.toString(gis, StandardCharsets.ISO_8859_1));
255             assertEquals("hello1.txt", gis.getMetaData().getFileName());
256         }
257         // Concatenated members, same file
258         try (GzipCompressorInputStream gis = GzipCompressorInputStream.builder().setFile("src/test/resources/org/apache/commons/compress/gzip/members.gz")
259                 .setDecompressConcatenated(true).get()) {
260             assertEquals("hello1.txt", gis.getMetaData().getFileName());
261             assertEquals("Hello1\nHello2\n", IOUtils.toString(gis, StandardCharsets.ISO_8859_1));
262             assertEquals("hello2.txt", gis.getMetaData().getFileName());
263         }
264     }
265 
266 }