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.apache.commons.compress.AbstractTestCase.getFile; 22 import static org.junit.Assert.assertEquals; 23 import static org.junit.Assert.assertFalse; 24 import static org.junit.Assert.assertNotNull; 25 import static org.junit.Assert.assertTrue; 26 import static org.junit.Assert.fail; 27 28 import java.io.BufferedInputStream; 29 import java.io.ByteArrayInputStream; 30 import java.io.FileInputStream; 31 import java.io.IOException; 32 import java.io.InputStream; 33 34 import org.apache.commons.compress.MemoryLimitException; 35 import org.apache.commons.compress.MockEvilInputStream; 36 import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream; 37 import org.apache.commons.compress.compressors.deflate.DeflateCompressorInputStream; 38 import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; 39 import org.apache.commons.compress.compressors.pack200.Pack200CompressorInputStream; 40 import org.apache.commons.compress.compressors.xz.XZCompressorInputStream; 41 import org.apache.commons.compress.compressors.zstandard.ZstdCompressorInputStream; 42 import org.junit.Test; 43 44 @SuppressWarnings("deprecation") // deliberately tests setDecompressConcatenated 45 public final class DetectCompressorTestCase { 46 47 final CompressorStreamFactory factory = new CompressorStreamFactory(); 48 private static final CompressorStreamFactory factoryTrue = new CompressorStreamFactory(true); 49 private static final CompressorStreamFactory factoryFalse = new CompressorStreamFactory(false); 50 51 // Must be static to allow use in the TestData entries 52 private static final CompressorStreamFactory factorySetTrue; 53 private static final CompressorStreamFactory factorySetFalse; 54 55 static { 56 factorySetTrue = new CompressorStreamFactory(); 57 factorySetTrue.setDecompressConcatenated(true); 58 factorySetFalse = new CompressorStreamFactory(); 59 factorySetFalse.setDecompressConcatenated(false); 60 } 61 62 static class TestData { 63 final String fileName; // The multiple file name 64 final char[] entryNames; // expected entries ... 65 final CompressorStreamFactory factory; // ... when using this factory 66 final boolean concat; // expected value for decompressConcatenated TestData(final String name, final char[] names, final CompressorStreamFactory factory, final boolean concat)67 TestData(final String name, final char[] names, final CompressorStreamFactory factory, final boolean concat) { 68 this.fileName = name; 69 this.entryNames = names; 70 this.factory = factory; 71 this.concat = concat; 72 } 73 } 74 75 private final TestData[] tests = { 76 new TestData("multiple.bz2", new char[]{'a','b'}, factoryTrue, true), 77 new TestData("multiple.bz2", new char[]{'a','b'}, factorySetTrue, true), 78 new TestData("multiple.bz2", new char[]{'a'}, factoryFalse, false), 79 new TestData("multiple.bz2", new char[]{'a'}, factorySetFalse, false), 80 new TestData("multiple.bz2", new char[]{'a'}, factory, false), 81 82 new TestData("multiple.gz", new char[]{'a','b'}, factoryTrue, true), 83 new TestData("multiple.gz", new char[]{'a','b'}, factorySetTrue, true), 84 new TestData("multiple.gz", new char[]{'a'}, factoryFalse, false), 85 new TestData("multiple.gz", new char[]{'a'}, factorySetFalse, false), 86 new TestData("multiple.gz", new char[]{'a'}, factory, false), 87 88 new TestData("multiple.xz", new char[]{'a','b'}, factoryTrue, true), 89 new TestData("multiple.xz", new char[]{'a','b'}, factorySetTrue, true), 90 new TestData("multiple.xz", new char[]{'a'}, factoryFalse, false), 91 new TestData("multiple.xz", new char[]{'a'}, factorySetFalse, false), 92 new TestData("multiple.xz", new char[]{'a'}, factory, false), 93 }; 94 95 @Test testDetection()96 public void testDetection() throws Exception { 97 final CompressorInputStream bzip2 = getStreamFor("bla.txt.bz2"); 98 assertNotNull(bzip2); 99 assertTrue(bzip2 instanceof BZip2CompressorInputStream); 100 101 final CompressorInputStream gzip = getStreamFor("bla.tgz"); 102 assertNotNull(gzip); 103 assertTrue(gzip instanceof GzipCompressorInputStream); 104 105 final CompressorInputStream pack200 = getStreamFor("bla.pack"); 106 assertNotNull(pack200); 107 assertTrue(pack200 instanceof Pack200CompressorInputStream); 108 109 final CompressorInputStream xz = getStreamFor("bla.tar.xz"); 110 assertNotNull(xz); 111 assertTrue(xz instanceof XZCompressorInputStream); 112 113 final CompressorInputStream zlib = getStreamFor("bla.tar.deflatez"); 114 assertNotNull(zlib); 115 assertTrue(zlib instanceof DeflateCompressorInputStream); 116 117 final CompressorInputStream zstd = getStreamFor("bla.tar.zst"); 118 assertNotNull(zstd); 119 assertTrue(zstd instanceof ZstdCompressorInputStream); 120 121 try { 122 factory.createCompressorInputStream(new ByteArrayInputStream(new byte[0])); 123 fail("No exception thrown for an empty input stream"); 124 } catch (final CompressorException e) { 125 // expected 126 } 127 } 128 129 @Test testDetect()130 public void testDetect() throws Exception { 131 132 assertEquals(CompressorStreamFactory.BZIP2, detect("bla.txt.bz2")); 133 assertEquals(CompressorStreamFactory.GZIP, detect("bla.tgz")); 134 assertEquals(CompressorStreamFactory.PACK200, detect("bla.pack")); 135 assertEquals(CompressorStreamFactory.XZ, detect("bla.tar.xz")); 136 assertEquals(CompressorStreamFactory.DEFLATE, detect("bla.tar.deflatez")); 137 assertEquals(CompressorStreamFactory.LZ4_FRAMED, detect("bla.tar.lz4")); 138 assertEquals(CompressorStreamFactory.LZMA, detect("bla.tar.lzma")); 139 assertEquals(CompressorStreamFactory.SNAPPY_FRAMED, detect("bla.tar.sz")); 140 assertEquals(CompressorStreamFactory.Z, detect("bla.tar.Z")); 141 assertEquals(CompressorStreamFactory.ZSTANDARD, detect("bla.tar.zst")); 142 143 //make sure we don't oom on detect 144 assertEquals(CompressorStreamFactory.Z, detect("COMPRESS-386")); 145 assertEquals(CompressorStreamFactory.LZMA, detect("COMPRESS-382")); 146 147 try { 148 CompressorStreamFactory.detect(new BufferedInputStream(new ByteArrayInputStream(new byte[0]))); 149 fail("shouldn't be able to detect empty stream"); 150 } catch (CompressorException e) { 151 assertEquals("No Compressor found for the stream signature.", e.getMessage()); 152 } 153 154 try { 155 CompressorStreamFactory.detect(null); 156 fail("shouldn't be able to detect null stream"); 157 } catch (IllegalArgumentException e) { 158 assertEquals("Stream must not be null.", e.getMessage()); 159 } 160 161 try { 162 CompressorStreamFactory.detect(new BufferedInputStream(new MockEvilInputStream())); 163 fail("Expected IOException"); 164 } catch (CompressorException e) { 165 assertEquals("IOException while reading signature.", e.getMessage()); 166 } 167 168 169 } 170 detect(String testFileName)171 private String detect(String testFileName) throws IOException, CompressorException { 172 String name = null; 173 try (InputStream is = new BufferedInputStream( 174 new FileInputStream(getFile(testFileName)))) { 175 name = CompressorStreamFactory.detect(is); 176 } 177 return name; 178 } 179 180 @Test(expected = MemoryLimitException.class) testLZMAMemoryLimit()181 public void testLZMAMemoryLimit() throws Exception { 182 getStreamFor("COMPRESS-382", 100); 183 } 184 185 @Test(expected = MemoryLimitException.class) testZMemoryLimit()186 public void testZMemoryLimit() throws Exception { 187 getStreamFor("COMPRESS-386", 100); 188 } 189 190 @Test(expected = MemoryLimitException.class) testXZMemoryLimitOnRead()191 public void testXZMemoryLimitOnRead() throws Exception { 192 //Even though the file is very small, the memory limit 193 //has to be quite large (8296 KiB) because of the dictionary size 194 195 //This is triggered on read(); not during initialization. 196 //This test is here instead of the xz unit test to make sure 197 //that the parameter is properly passed via the CompressorStreamFactory 198 try (InputStream compressorIs = getStreamFor("bla.tar.xz", 100)) { 199 compressorIs.read(); 200 } 201 } 202 203 @Test(expected = MemoryLimitException.class) testXZMemoryLimitOnSkip()204 public void testXZMemoryLimitOnSkip() throws Exception { 205 try (InputStream compressorIs = getStreamFor("bla.tar.xz", 100)) { 206 compressorIs.skip(10); 207 } 208 } 209 getStreamFor(final String fileName, final int memoryLimitInKb)210 private InputStream getStreamFor(final String fileName, final int memoryLimitInKb) throws Exception { 211 CompressorStreamFactory fac = new CompressorStreamFactory(true, 212 memoryLimitInKb); 213 InputStream is = new BufferedInputStream( 214 new FileInputStream(getFile(fileName))); 215 try { 216 return fac.createCompressorInputStream(is); 217 } catch (CompressorException e) { 218 if (e.getCause() != null && e.getCause() instanceof Exception) { 219 //unwrap cause to reveal MemoryLimitException 220 throw (Exception)e.getCause(); 221 } else { 222 throw e; 223 } 224 } 225 226 } 227 228 229 @Test testOverride()230 public void testOverride() { 231 CompressorStreamFactory fac = new CompressorStreamFactory(); 232 assertFalse(fac.getDecompressConcatenated()); 233 fac.setDecompressConcatenated(true); 234 assertTrue(fac.getDecompressConcatenated()); 235 236 fac = new CompressorStreamFactory(false); 237 assertFalse(fac.getDecompressConcatenated()); 238 try { 239 fac.setDecompressConcatenated(true); 240 fail("Expected IllegalStateException"); 241 } catch (final IllegalStateException ise) { 242 // expected 243 } 244 245 fac = new CompressorStreamFactory(true); 246 assertTrue(fac.getDecompressConcatenated()); 247 try { 248 fac.setDecompressConcatenated(true); 249 fail("Expected IllegalStateException"); 250 } catch (final IllegalStateException ise) { 251 // expected 252 } 253 } 254 255 @Test testMutiples()256 public void testMutiples() throws Exception { 257 for(int i=0; i <tests.length; i++) { 258 final TestData test = tests[i]; 259 final CompressorStreamFactory fac = test.factory; 260 assertNotNull("Test entry "+i, fac); 261 assertEquals("Test entry "+i, test.concat, fac.getDecompressConcatenated()); 262 final CompressorInputStream in = getStreamFor(test.fileName, fac); 263 assertNotNull("Test entry "+i,in); 264 for (final char entry : test.entryNames) { 265 assertEquals("Test entry" + i, entry, in.read()); 266 } 267 assertEquals(0, in.available()); 268 assertEquals(-1, in.read()); 269 } 270 } 271 getStreamFor(final String resource)272 private CompressorInputStream getStreamFor(final String resource) 273 throws CompressorException, IOException { 274 return factory.createCompressorInputStream( 275 new BufferedInputStream(new FileInputStream( 276 getFile(resource)))); 277 } 278 getStreamFor(final String resource, final CompressorStreamFactory factory)279 private CompressorInputStream getStreamFor(final String resource, final CompressorStreamFactory factory) 280 throws CompressorException, IOException { 281 return factory.createCompressorInputStream( 282 new BufferedInputStream(new FileInputStream( 283 getFile(resource)))); 284 } 285 } 286