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.zstandard;
21  
22  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
23  import static org.junit.jupiter.api.Assertions.assertTrue;
24  
25  import java.io.File;
26  import java.io.FileOutputStream;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.io.OutputStream;
30  import java.nio.file.Files;
31  import java.nio.file.Path;
32  
33  import org.apache.commons.compress.AbstractTest;
34  import org.apache.commons.compress.compressors.CompressorInputStream;
35  import org.apache.commons.compress.compressors.CompressorOutputStream;
36  import org.apache.commons.compress.compressors.CompressorStreamFactory;
37  import org.apache.commons.io.IOUtils;
38  import org.apache.commons.io.function.IOFunction;
39  import org.apache.commons.lang3.ArrayUtils;
40  import org.junit.jupiter.api.Disabled;
41  import org.junit.jupiter.api.Test;
42  import org.junitpioneer.jupiter.cartesian.CartesianTest;
43  import org.junitpioneer.jupiter.cartesian.CartesianTest.Values;
44  
45  import com.github.luben.zstd.ZstdOutputStream;
46  
47  /**
48   * Tests {@link ZstdCompressorOutputStream}.
49   */
50  class ZstdRoundtripTest extends AbstractTest {
51  
52      private interface OutputStreamCreator extends IOFunction<FileOutputStream, ZstdCompressorOutputStream> {
53          // empty
54      }
55  
56      private void roundtrip(final OutputStreamCreator oc) throws IOException {
57          final Path input = getPath("bla.tar");
58          final File output = newTempFile(input.getFileName() + ".zstd");
59          try (FileOutputStream os = new FileOutputStream(output);
60                  ZstdCompressorOutputStream zos = oc.apply(os)) {
61              zos.write(input);
62              zos.close();
63              assertTrue(zos.isClosed());
64          }
65          try (ZstdCompressorInputStream zis = new ZstdCompressorInputStream(Files.newInputStream(output.toPath()))) {
66              final byte[] expected = Files.readAllBytes(input);
67              final byte[] actual = IOUtils.toByteArray(zis);
68              assertArrayEquals(expected, actual);
69          }
70      }
71  
72      @Test
73      void testDirectRoundtrip() throws Exception {
74          roundtrip(ZstdCompressorOutputStream::new);
75      }
76  
77      @Test
78      void testFactoryRoundtrip() throws Exception {
79          final Path input = getPath("bla.tar");
80          final File output = newTempFile(input.getFileName() + ".zstd");
81          try (OutputStream os = Files.newOutputStream(output.toPath());
82                  CompressorOutputStream<?> zos = new CompressorStreamFactory().createCompressorOutputStream("zstd", os)) {
83              zos.write(input);
84          }
85          try (InputStream inputStream = Files.newInputStream(output.toPath());
86                  CompressorInputStream zis = new CompressorStreamFactory().createCompressorInputStream("zstd", inputStream)) {
87              final byte[] expected = Files.readAllBytes(input);
88              final byte[] actual = IOUtils.toByteArray(zis);
89              assertArrayEquals(expected, actual);
90          }
91      }
92  
93      @Test
94      void testRoundtripSetChainLogNonDefaultMax() throws Exception {
95          roundtrip(os -> ZstdCompressorOutputStream.builder().setOutputStream(os).setChainLog(ZstdConstants.ZSTD_CHAINLOG_MAX).get());
96      }
97  
98      @Test
99      void testRoundtripSetChainLogNonDefaultMin() throws Exception {
100         roundtrip(os -> ZstdCompressorOutputStream.builder().setOutputStream(os).setChainLog(ZstdConstants.ZSTD_CHAINLOG_MIN).get());
101     }
102 
103     @Test
104     void testRoundtripSetChecksumNonDefault() throws Exception {
105         roundtrip(os -> ZstdCompressorOutputStream.builder().setOutputStream(os).setChecksum(true).get());
106     }
107 
108     @Test
109     void testRoundtripSetCloseFrameOnFlushNonDefault() throws Exception {
110         roundtrip(os -> ZstdCompressorOutputStream.builder().setOutputStream(os).setCloseFrameOnFlush(true).get());
111     }
112 
113     @Test
114     void testRoundtripSetHashLogNonDefaultMax() throws Exception {
115         roundtrip(os -> ZstdCompressorOutputStream.builder().setOutputStream(os).setHashLog(ZstdConstants.ZSTD_HASHLOG_MAX).get());
116     }
117 
118     @Test
119     void testRoundtripSetHashLogNonDefaultMin() throws Exception {
120         roundtrip(os -> ZstdCompressorOutputStream.builder().setOutputStream(os).setHashLog(ZstdConstants.ZSTD_HASHLOG_MIN).get());
121     }
122 
123     @Test
124     void testRoundtripSetJobSizeNonDefault() throws Exception {
125         roundtrip(os -> ZstdCompressorOutputStream.builder().setOutputStream(os).setJobSize(1).get());
126     }
127 
128     @Test
129     void testRoundtripSetLevelNonDefault() throws Exception {
130         roundtrip(os -> ZstdCompressorOutputStream.builder().setOutputStream(os).setLevel(1).get());
131     }
132 
133     @Test
134     void testRoundtripSetMinMatchNonDefaultMax() throws Exception {
135         roundtrip(os -> ZstdCompressorOutputStream.builder().setOutputStream(os).setMinMatch(ZstdConstants.ZSTD_MINMATCH_MAX).get());
136     }
137 
138     @Test
139     void testRoundtripSetMinMatchNonDefaultMin() throws Exception {
140         roundtrip(os -> ZstdCompressorOutputStream.builder().setOutputStream(os).setMinMatch(ZstdConstants.ZSTD_MINMATCH_MIN).get());
141     }
142 
143     @Test
144     void testRoundtripSetOverlapLogNonDefault() throws Exception {
145         roundtrip(os -> ZstdCompressorOutputStream.builder().setOutputStream(os).setOverlapLog(1).get());
146     }
147 
148     @Test
149     void testRoundtripSetSearchLogNonDefault() throws Exception {
150         roundtrip(os -> ZstdCompressorOutputStream.builder().setOutputStream(os).setSearchLog(1).get());
151     }
152 
153     @Test
154     void testRoundtripSetStrategyNonDefault() throws Exception {
155         roundtrip(os -> ZstdCompressorOutputStream.builder().setOutputStream(os).setStrategy(1).get());
156     }
157 
158     @Test
159     @Disabled("com.github.luben.zstd.ZstdIOException: Frame requires too much memory for decoding")
160     void testRoundtripSetWindowLogNonDefaultMax() throws Exception {
161         roundtrip(os -> ZstdCompressorOutputStream.builder().setOutputStream(os).setWindowLog(ZstdConstants.ZSTD_WINDOWLOG_MAX).get());
162     }
163 
164     @Test
165     void testRoundtripSetWindowLogNonDefaultMin() throws Exception {
166         roundtrip(os -> ZstdCompressorOutputStream.builder().setOutputStream(os).setWindowLog(ZstdConstants.ZSTD_WINDOWLOG_MIN).get());
167     }
168 
169     @Test
170     void testRoundtripSetWorkersNonDefault() throws Exception {
171         roundtrip(os -> ZstdCompressorOutputStream.builder().setOutputStream(os).setWorkers(1).get());
172     }
173 
174     @Test
175     void testRoundtripSetZstdDict() throws Exception {
176         // Avoid JVM segmentation fault in zstd-jni 1.5.7-2
177         // TODO Remove ternary expression in the ctor if/when https://github.com/luben/zstd-jni/pull/356 is fixed.
178         roundtrip(os -> ZstdCompressorOutputStream.builder().setOutputStream(os).setDict(null).get());
179         roundtrip(os -> ZstdCompressorOutputStream.builder().setOutputStream(os).setDict(ArrayUtils.EMPTY_BYTE_ARRAY).get());
180         roundtrip(os -> ZstdCompressorOutputStream.builder().setOutputStream(os).setDict(new byte[512]).get());
181     }
182 
183     @CartesianTest
184     // @formatter:off
185     void testRoundtripWithAll(
186             @Values(ints = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }) final int level, // see zstd.h
187             @Values(booleans = { false, true }) final boolean checksum,
188             @Values(booleans = { false, true }) final boolean closeFrameOnFlush,
189             @Values(ints = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }) final int strategy, // see zstd.h
190             @Values(ints = { 0, 6, 9 }) final int overlapLog  // see zstd.h
191         ) throws Exception {
192         roundtrip(os -> ZstdCompressorOutputStream.builder()
193                 .setChainLog(0)
194                 .setChecksum(checksum)
195                 .setCloseFrameOnFlush(closeFrameOnFlush)
196                 .setDict(null)
197                 .setHashLog(0)
198                 .setJobSize(0)
199                 .setLevel(level)
200                 .setMinMatch(0)
201                 .setOutputStream(os)
202                 .setOverlapLog(overlapLog)
203                 .setSearchLog(0)
204                 .setStrategy(strategy)
205                 .setWindowLog(0)
206                 .setWorkers(0)
207                 .get());
208     }
209     // @formatter:on
210 
211     @Test
212     void testRoundtripWithChecksum() throws Exception {
213         roundtrip(os -> new ZstdCompressorOutputStream(os, 3, false, true));
214         roundtrip(os -> ZstdCompressorOutputStream.builder().setOutputStream(os).setLevel(3).setCloseFrameOnFlush(true).setChecksum(true).get());
215     }
216 
217     @Test
218     void testRoundtripWithCloseFrameOnFlush() throws Exception {
219         roundtrip(os -> new ZstdCompressorOutputStream(os, 3, true));
220         roundtrip(os -> ZstdCompressorOutputStream.builder().setOutputStream(os).setLevel(3).setCloseFrameOnFlush(true).get());
221     }
222 
223     @Test
224     void testRoundtripWithCustomLevel() throws Exception {
225         roundtrip(os -> new ZstdCompressorOutputStream(os, 1));
226         roundtrip(os -> ZstdCompressorOutputStream.builder().setOutputStream(os).setLevel(1).get());
227     }
228 
229     @Test
230     void testRoundtripWithZstdOutputStream() throws Exception {
231         roundtrip(os -> ZstdCompressorOutputStream.builder().setOutputStream(new ZstdOutputStream(os)).get());
232         roundtrip(os -> new ZstdCompressorOutputStream(new ZstdOutputStream(os)));
233     }
234 }