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.archivers;
20  
21  import static org.apache.commons.compress.AbstractTestCase.getFile;
22  import static org.junit.Assert.assertEquals;
23  import static org.junit.Assert.assertNull;
24  import static org.junit.Assert.assertTrue;
25  import static org.junit.Assert.fail;
26  
27  import java.io.BufferedInputStream;
28  import java.io.ByteArrayInputStream;
29  import java.io.ByteArrayOutputStream;
30  import java.io.FileInputStream;
31  import java.io.IOException;
32  import java.io.InputStream;
33  import java.lang.reflect.Field;
34  
35  import org.apache.commons.compress.archivers.arj.ArjArchiveInputStream;
36  import org.apache.commons.compress.archivers.cpio.CpioArchiveInputStream;
37  import org.apache.commons.compress.archivers.dump.DumpArchiveInputStream;
38  import org.apache.commons.compress.archivers.jar.JarArchiveInputStream;
39  import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
40  import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
41  import org.junit.Test;
42  
43  public class ArchiveStreamFactoryTest {
44  
45      private static final String UNKNOWN = "??";
46  
47      /**
48       * see https://issues.apache.org/jira/browse/COMPRESS-171
49       */
50      @Test
51      public void shortTextFilesAreNoTARs() throws Exception {
52          try {
53              new ArchiveStreamFactory()
54                  .createArchiveInputStream(new ByteArrayInputStream("This certainly is not a tar archive, really, no kidding".getBytes()));
55              fail("created an input stream for a non-archive");
56          } catch (final ArchiveException ae) {
57              assertTrue(ae.getMessage().startsWith("No Archiver found"));
58          }
59      }
60  
61      /**
62       * see https://issues.apache.org/jira/browse/COMPRESS-191
63       */
64      @Test
65      public void aiffFilesAreNoTARs() throws Exception {
66          try (FileInputStream fis = new FileInputStream("src/test/resources/testAIFF.aif")) {
67              try (InputStream is = new BufferedInputStream(fis)) {
68                  new ArchiveStreamFactory().createArchiveInputStream(is);
69                  fail("created an input stream for a non-archive");
70              } catch (final ArchiveException ae) {
71                  assertTrue(ae.getMessage().startsWith("No Archiver found"));
72              }
73          }
74      }
75  
76      @Test
77      public void testCOMPRESS209() throws Exception {
78          try (FileInputStream fis = new FileInputStream("src/test/resources/testCompress209.doc")) {
79              try (InputStream bis = new BufferedInputStream(fis)) {
80                  new ArchiveStreamFactory().createArchiveInputStream(bis);
81                  fail("created an input stream for a non-archive");
82              } catch (final ArchiveException ae) {
83                  assertTrue(ae.getMessage().startsWith("No Archiver found"));
84              }
85          }
86      }
87  
88      @Test(expected = StreamingNotSupportedException.class)
89      public void cantRead7zFromStream() throws Exception {
90          new ArchiveStreamFactory()
91              .createArchiveInputStream(ArchiveStreamFactory.SEVEN_Z,
92                                        new ByteArrayInputStream(new byte[0]));
93      }
94  
95      @Test(expected = StreamingNotSupportedException.class)
96      public void cantWrite7zToStream() throws Exception {
97          new ArchiveStreamFactory()
98              .createArchiveOutputStream(ArchiveStreamFactory.SEVEN_Z,
99                                         new ByteArrayOutputStream());
100     }
101 
102     /**
103      * Test case for 
104      * <a href="https://issues.apache.org/jira/browse/COMPRESS-267"
105      * >COMPRESS-267</a>.
106      */
107     @Test
108     public void detectsAndThrowsFor7z() throws Exception {
109         try (FileInputStream fis = new FileInputStream("src/test/resources/bla.7z")) {
110             try (InputStream bis = new BufferedInputStream(fis)) {
111                 new ArchiveStreamFactory().createArchiveInputStream(bis);
112                 fail("Expected a StreamingNotSupportedException");
113             } catch (final StreamingNotSupportedException ex) {
114                 assertEquals(ArchiveStreamFactory.SEVEN_Z, ex.getFormat());
115             }
116         }
117     }
118 
119     /**
120      * Test case for 
121      * <a href="https://issues.apache.org/jira/browse/COMPRESS-208"
122      * >COMPRESS-208</a>.
123      */
124     @Test
125     public void skipsPK00Prefix() throws Exception {
126         try (FileInputStream fis = new FileInputStream("src/test/resources/COMPRESS-208.zip")) {
127             try (InputStream bis = new BufferedInputStream(fis)) {
128                 try (ArchiveInputStream ais = new ArchiveStreamFactory().createArchiveInputStream(bis)) {
129                     assertTrue(ais instanceof ZipArchiveInputStream);
130                 }
131             }
132         }
133     }
134     
135     @Test
136     public void testEncodingCtor() {
137         ArchiveStreamFactory fac = new ArchiveStreamFactory();
138         assertNull(fac.getEntryEncoding());
139         fac = new ArchiveStreamFactory(null);
140         assertNull(fac.getEntryEncoding());
141         fac = new ArchiveStreamFactory("UTF-8");
142         assertEquals("UTF-8", fac.getEntryEncoding());
143     }
144 
145     @Test
146     @SuppressWarnings("deprecation")
147     public void testEncodingDeprecated() {
148         ArchiveStreamFactory fac = new ArchiveStreamFactory();
149         assertNull(fac.getEntryEncoding());
150         fac.setEntryEncoding("UTF-8");
151         assertEquals("UTF-8", fac.getEntryEncoding());
152         fac.setEntryEncoding("US_ASCII");
153         assertEquals("US_ASCII", fac.getEntryEncoding());
154         fac = new ArchiveStreamFactory("UTF-8");
155         assertEquals("UTF-8", fac.getEntryEncoding());
156         try {
157             fac.setEntryEncoding("US_ASCII");
158             fail("Expected IllegalStateException");
159         } catch (final IllegalStateException ise) {
160             // expected
161         }
162     }
163 
164     static class TestData {
165         final String testFile;
166         final String expectedEncoding;
167         final ArchiveStreamFactory fac;
168         final String fieldName;
169         final String type;
170         final boolean hasOutputStream;
171         
172         TestData(final String testFile, final String type, final boolean hasOut, final String expectedEncoding, final ArchiveStreamFactory fac, final String fieldName) {
173             this.testFile = testFile;
174             this.expectedEncoding = expectedEncoding;
175             this.fac = fac;
176             this.fieldName = fieldName;
177             this.type = type;
178             this.hasOutputStream = hasOut;
179         }
180         
181         @Override
182         public String toString() {
183             return "TestData [testFile=" + testFile + ", expectedEncoding=" + expectedEncoding + ", fac=" + fac
184                     + ", fieldName=" + fieldName + ", type=" + type + ", hasOutputStream=" + hasOutputStream + "]";
185         }
186     }
187 
188     @SuppressWarnings("deprecation") // test of deprecated method
189     static ArchiveStreamFactory getFactory(final String entryEncoding) {
190         final ArchiveStreamFactory fac = new ArchiveStreamFactory();
191         fac.setEntryEncoding(entryEncoding);
192         return fac;
193     }
194     // The different factory types
195     private static final ArchiveStreamFactory FACTORY = new ArchiveStreamFactory();
196     private static final ArchiveStreamFactory FACTORY_UTF8 = new ArchiveStreamFactory("UTF-8");
197     private static final ArchiveStreamFactory FACTORY_ASCII = new ArchiveStreamFactory("ASCII");
198     private static final ArchiveStreamFactory FACTORY_SET_UTF8 = getFactory("UTF-8");
199     private static final ArchiveStreamFactory FACTORY_SET_ASCII = getFactory("ASCII");
200 
201     // Default encoding if none is provided (not even null)
202     // The test currently assumes that the output default is the same as the input default
203     private static final String ARJ_DEFAULT;
204     private static final String DUMP_DEFAULT;
205 
206     private static final String ZIP_DEFAULT = getField(new ZipArchiveInputStream(null),"encoding");
207     private static final String CPIO_DEFAULT = getField(new CpioArchiveInputStream(null),"encoding");
208     private static final String TAR_DEFAULT = getField(new TarArchiveInputStream(null),"encoding");
209     private static final String JAR_DEFAULT = getField(new JarArchiveInputStream(null),"encoding");
210 
211     static {
212         String dflt;
213         dflt = UNKNOWN;
214         try {
215             dflt = getField(new ArjArchiveInputStream(new FileInputStream(getFile("bla.arj"))), "charsetName");
216         } catch (final Exception e) {
217             e.printStackTrace();
218         }
219         ARJ_DEFAULT = dflt;
220         dflt = UNKNOWN;
221         try {
222             dflt = getField(new DumpArchiveInputStream(new FileInputStream(getFile("bla.dump"))), "encoding");
223         } catch (final Exception e) {
224             e.printStackTrace();
225         }
226         DUMP_DEFAULT = dflt;
227     }
228 
229     static final TestData[] TESTS = {
230         new TestData("bla.arj", ArchiveStreamFactory.ARJ, false, ARJ_DEFAULT, FACTORY, "charsetName"),
231         new TestData("bla.arj", ArchiveStreamFactory.ARJ, false, "UTF-8", FACTORY_UTF8, "charsetName"),
232         new TestData("bla.arj", ArchiveStreamFactory.ARJ, false, "ASCII", FACTORY_ASCII, "charsetName"),
233         new TestData("bla.arj", ArchiveStreamFactory.ARJ, false, "UTF-8", FACTORY_SET_UTF8, "charsetName"),
234         new TestData("bla.arj", ArchiveStreamFactory.ARJ, false, "ASCII", FACTORY_SET_ASCII, "charsetName"),
235 
236         new TestData("bla.cpio", ArchiveStreamFactory.CPIO, true, CPIO_DEFAULT, FACTORY, "encoding"),
237         new TestData("bla.cpio", ArchiveStreamFactory.CPIO, true, "UTF-8", FACTORY_UTF8, "encoding"),
238         new TestData("bla.cpio", ArchiveStreamFactory.CPIO, true, "ASCII", FACTORY_ASCII, "encoding"),
239         new TestData("bla.cpio", ArchiveStreamFactory.CPIO, true, "UTF-8", FACTORY_SET_UTF8, "encoding"),
240         new TestData("bla.cpio", ArchiveStreamFactory.CPIO, true, "ASCII", FACTORY_SET_ASCII, "encoding"),
241 
242         new TestData("bla.dump", ArchiveStreamFactory.DUMP, false, DUMP_DEFAULT, FACTORY, "encoding"),
243         new TestData("bla.dump", ArchiveStreamFactory.DUMP, false, "UTF-8", FACTORY_UTF8, "encoding"),
244         new TestData("bla.dump", ArchiveStreamFactory.DUMP, false, "ASCII", FACTORY_ASCII, "encoding"),
245         new TestData("bla.dump", ArchiveStreamFactory.DUMP, false, "UTF-8", FACTORY_SET_UTF8, "encoding"),
246         new TestData("bla.dump", ArchiveStreamFactory.DUMP, false, "ASCII", FACTORY_SET_ASCII, "encoding"),
247 
248         new TestData("bla.tar", ArchiveStreamFactory.TAR, true, TAR_DEFAULT, FACTORY, "encoding"),
249         new TestData("bla.tar", ArchiveStreamFactory.TAR, true, "UTF-8", FACTORY_UTF8, "encoding"),
250         new TestData("bla.tar", ArchiveStreamFactory.TAR, true, "ASCII", FACTORY_ASCII, "encoding"),
251         new TestData("bla.tar", ArchiveStreamFactory.TAR, true, "UTF-8", FACTORY_SET_UTF8, "encoding"),
252         new TestData("bla.tar", ArchiveStreamFactory.TAR, true, "ASCII", FACTORY_SET_ASCII, "encoding"),
253 
254         new TestData("bla.jar", ArchiveStreamFactory.JAR, true, JAR_DEFAULT, FACTORY, "encoding"),
255         new TestData("bla.jar", ArchiveStreamFactory.JAR, true, "UTF-8", FACTORY_UTF8, "encoding"),
256         new TestData("bla.jar", ArchiveStreamFactory.JAR, true, "ASCII", FACTORY_ASCII, "encoding"),
257         new TestData("bla.jar", ArchiveStreamFactory.JAR, true, "UTF-8", FACTORY_SET_UTF8, "encoding"),
258         new TestData("bla.jar", ArchiveStreamFactory.JAR, true, "ASCII", FACTORY_SET_ASCII, "encoding"),
259 
260         new TestData("bla.zip", ArchiveStreamFactory.ZIP, true, ZIP_DEFAULT, FACTORY, "encoding"),
261         new TestData("bla.zip", ArchiveStreamFactory.ZIP, true, "UTF-8", FACTORY_UTF8, "encoding"),
262         new TestData("bla.zip", ArchiveStreamFactory.ZIP, true, "ASCII", FACTORY_ASCII, "encoding"),
263         new TestData("bla.zip", ArchiveStreamFactory.ZIP, true, "UTF-8", FACTORY_SET_UTF8, "encoding"),
264         new TestData("bla.zip", ArchiveStreamFactory.ZIP, true, "ASCII", FACTORY_SET_ASCII, "encoding"),
265     };
266 
267     @Test
268     public void testEncodingInputStreamAutodetect() throws Exception {
269         int failed = 0;
270         for (int i = 1; i <= TESTS.length; i++) {
271             final TestData test = TESTS[i - 1];
272             try (final ArchiveInputStream ais = getInputStreamFor(test.testFile, test.fac)) {
273                 final String field = getField(ais, test.fieldName);
274                 if (!eq(test.expectedEncoding, field)) {
275                     System.out.println("Failed test " + i + ". expected: " + test.expectedEncoding + " actual: " + field
276                             + " type: " + test.type);
277                     failed++;
278                 }
279             }
280         }
281         if (failed > 0) {
282             fail("Tests failed: " + failed + " out of " + TESTS.length);
283         }
284     }
285 
286     @Test
287     public void testEncodingInputStream() throws Exception {
288         int failed = 0;
289         for (int i = 1; i <= TESTS.length; i++) {
290             final TestData test = TESTS[i - 1];
291             try (final ArchiveInputStream ais = getInputStreamFor(test.type, test.testFile, test.fac)) {
292                 final String field = getField(ais, test.fieldName);
293                 if (!eq(test.expectedEncoding, field)) {
294                     System.out.println("Failed test " + i + ". expected: " + test.expectedEncoding + " actual: " + field
295                             + " type: " + test.type);
296                     failed++;
297                 }
298             }
299         }
300         if (failed > 0) {
301             fail("Tests failed: " + failed + " out of " + TESTS.length);
302         }
303     }
304 
305     @Test
306     public void testEncodingOutputStream() throws Exception {
307         int failed = 0;
308         for(int i = 1; i <= TESTS.length; i++) {
309             final TestData test = TESTS[i-1];
310             if (test.hasOutputStream) {
311                 try (final ArchiveOutputStream ais = getOutputStreamFor(test.type, test.fac)) {
312                     final String field = getField(ais, test.fieldName);
313                     if (!eq(test.expectedEncoding, field)) {
314                         System.out.println("Failed test " + i + ". expected: " + test.expectedEncoding + " actual: "
315                                 + field + " type: " + test.type);
316                         failed++;
317                     }
318                 }
319             }
320         }
321         if (failed > 0) {
322             fail("Tests failed: " + failed + " out of " + TESTS.length);
323         }
324     }
325 
326     // equals allowing null
327     private static boolean eq(final String exp, final String act) {
328         if (exp == null) {
329             return act == null;
330         }
331         return exp.equals(act);
332     }
333 
334     private static String getField(final Object instance, final String name) {
335         final Class<?> cls = instance.getClass();
336         Field fld;
337         try {
338             fld = cls.getDeclaredField(name);
339         } catch (final NoSuchFieldException nsfe) {
340                 try {
341                     fld = cls.getSuperclass().getDeclaredField(name);
342                 } catch (final NoSuchFieldException e) {
343                     System.out.println("Cannot find " + name + " in class " + instance.getClass().getSimpleName());
344                     return UNKNOWN;
345                 }                
346         }
347         final boolean isAccessible = fld.isAccessible();
348         try {
349             if (!isAccessible) {
350                 fld.setAccessible(true);
351             }
352             final Object object = fld.get(instance);
353             if (object instanceof String || object == null) {
354                 return (String) object;
355             }
356             System.out.println("Wrong type: " + object.getClass().getCanonicalName() + " for " + name + " in class " + instance.getClass().getSimpleName());
357             return UNKNOWN;
358         } catch (final Exception e) {
359             e.printStackTrace();
360             return UNKNOWN;
361         } finally {
362             if (!isAccessible) {
363                 fld.setAccessible(isAccessible);
364             }
365         }
366     }
367 
368     private ArchiveInputStream getInputStreamFor(final String resource, final ArchiveStreamFactory factory)
369             throws IOException, ArchiveException {
370         return factory.createArchiveInputStream(
371                    new BufferedInputStream(new FileInputStream(
372                        getFile(resource))));
373     }
374 
375     private ArchiveInputStream getInputStreamFor(final String type, final String resource, final ArchiveStreamFactory factory)
376             throws IOException, ArchiveException {
377         return factory.createArchiveInputStream(
378                    type,
379                    new BufferedInputStream(new FileInputStream(
380                        getFile(resource))));
381     }
382 
383     private ArchiveOutputStream getOutputStreamFor(final String type, final ArchiveStreamFactory factory)
384             throws IOException, ArchiveException {
385         return factory.createArchiveOutputStream(type, new ByteArrayOutputStream());
386     }
387 }