View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one or more
3    *  contributor license agreements.  See the NOTICE file distributed with
4    *  this work for additional information regarding copyright ownership.
5    *  The ASF licenses this file to You under the Apache License, Version 2.0
6    *  (the "License"); you may not use this file except in compliance with
7    *  the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  package org.apache.commons.compress.harmony.unpack200;
18  
19  import static org.junit.Assert.assertThrows;
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertFalse;
22  import static org.junit.jupiter.api.Assertions.assertNotNull;
23  import static org.junit.jupiter.api.Assertions.assertTrue;
24  
25  import java.io.BufferedInputStream;
26  import java.io.BufferedOutputStream;
27  import java.io.BufferedReader;
28  import java.io.File;
29  import java.io.FileInputStream;
30  import java.io.FileOutputStream;
31  import java.io.FileReader;
32  import java.io.IOException;
33  import java.io.InputStream;
34  import java.io.InputStreamReader;
35  import java.net.URL;
36  import java.util.Enumeration;
37  import java.util.jar.JarEntry;
38  import java.util.jar.JarFile;
39  import java.util.jar.JarOutputStream;
40  import java.util.zip.ZipEntry;
41  
42  import org.apache.commons.compress.AbstractTempDirTest;
43  import org.apache.commons.io.input.BoundedInputStream;
44  import org.apache.commons.io.output.NullOutputStream;
45  import org.junit.jupiter.api.AfterEach;
46  import org.junit.jupiter.api.Test;
47  import org.junit.jupiter.params.ParameterizedTest;
48  import org.junit.jupiter.params.provider.ValueSource;
49  
50  /**
51   * Tests for org.apache.commons.compress.harmony.unpack200.Archive, which is the main class for unpack200.
52   */
53  public class ArchiveTest extends AbstractTempDirTest {
54  
55      InputStream in;
56      JarOutputStream out;
57  
58      @AfterEach
59      public void tearDown() throws Exception {
60          if (in != null) {
61              try {
62                  in.close();
63              } catch (final IOException e) {
64                  e.printStackTrace();
65              }
66          }
67          try {
68              if (out != null) {
69                  out.close();
70              }
71          } catch (final IOException e) {
72              e.printStackTrace();
73          }
74      }
75  
76      @Test
77      public void testAlternativeConstructor() throws Exception {
78          final String inputFile = new File(Archive.class.getResource("/pack200/sql.pack.gz").toURI()).getPath();
79          final File file = createTempFile("sql", ".jar");
80          final String outputFile = file.getPath();
81          final Archive archive = new Archive(inputFile, outputFile);
82          archive.unpack();
83      }
84  
85      @Test
86      public void testDeflateHint() throws Exception {
87          in = Archive.class.getResourceAsStream("/pack200/sql.pack.gz");
88          File file = createTempFile("sql", ".jar");
89          out = new JarOutputStream(new FileOutputStream(file));
90          Archive archive = new Archive(in, out);
91          archive.setDeflateHint(true);
92          archive.unpack();
93          try (JarFile jarFile = new JarFile(file)) {
94              assertEquals(ZipEntry.DEFLATED, jarFile.getEntry("bin/test/org/apache/harmony/sql/tests/internal/rowset/CachedRowSetImplTest.class").getMethod());
95  
96              in = Archive.class.getResourceAsStream("/pack200/sql.pack.gz");
97              file = createTempFile("sql", ".jar");
98              out = new JarOutputStream(new FileOutputStream(file));
99              archive = new Archive(in, out);
100             archive.setDeflateHint(false);
101             archive.unpack();
102         }
103         try (JarFile jarFile = new JarFile(file)) {
104             assertEquals(ZipEntry.STORED, jarFile.getEntry("bin/test/org/apache/harmony/sql/tests/internal/rowset/CachedRowSetImplTest.class").getMethod());
105         }
106     }
107 
108     @Test
109     public void testJustResourcesGZip() throws Exception {
110         in = Archive.class.getResourceAsStream("/pack200/JustResources.pack.gz");
111         final File file = createTempFile("Just", "ResourcesGz.jar");
112         out = new JarOutputStream(new FileOutputStream(file));
113         final Archive archive = new Archive(in, out);
114         archive.unpack();
115     }
116 
117     // Test verbose, quiet and log file options.
118     @Test
119     public void testLoggingOptions() throws Exception {
120         // test default option, which is quiet (no output at all)
121         in = Archive.class.getResourceAsStream("/pack200/sql.pack.gz");
122         File file = createTempFile("logtest", ".jar");
123         out = new JarOutputStream(new FileOutputStream(file));
124         Archive archive = new Archive(in, out);
125         File logFile = createTempFile("logfile", ".txt");
126         archive.setLogFile(logFile.getPath());
127         archive.unpack();
128 
129         // log file should be empty
130         try (FileReader reader = new FileReader(logFile)) {
131             assertFalse(reader.ready());
132         }
133 
134         // test verbose
135         in = Archive.class.getResourceAsStream("/pack200/sql.pack.gz");
136         file = createTempFile("logtest", ".jar");
137         out = new JarOutputStream(new FileOutputStream(file));
138         archive = new Archive(in, out);
139         logFile = createTempFile("logfile", ".txt");
140         archive.setLogFile(logFile.getPath());
141         archive.setVerbose(true);
142         archive.unpack();
143 
144         // log file should not be empty
145         try (FileReader reader = new FileReader(logFile)) {
146             assertTrue(reader.ready());
147         }
148 
149         // test append option
150         final long length = logFile.length();
151         in = Archive.class.getResourceAsStream("/pack200/sql.pack.gz");
152         file = createTempFile("logtest", ".jar");
153         out = new JarOutputStream(new FileOutputStream(file));
154         archive = new Archive(in, out);
155         archive.setLogFile(logFile.getPath(), true);
156         archive.setVerbose(true);
157         archive.unpack();
158         assertTrue(logFile.length() > length);
159         in = Archive.class.getResourceAsStream("/pack200/sql.pack.gz");
160         file = createTempFile("logtest", ".jar");
161         out = new JarOutputStream(new FileOutputStream(file));
162         archive = new Archive(in, out);
163         archive.setLogFile(logFile.getPath(), false);
164         archive.setVerbose(true);
165         archive.unpack();
166         assertEquals(logFile.length(), length);
167 
168         // test setting quiet explicitly
169         in = Archive.class.getResourceAsStream("/pack200/sql.pack.gz");
170         file = createTempFile("logtest", ".jar");
171         out = new JarOutputStream(new FileOutputStream(file));
172         archive = new Archive(in, out);
173         logFile = createTempFile("logfile", ".txt");
174         archive.setLogFile(logFile.getPath());
175         archive.setQuiet(true);
176         archive.unpack();
177 
178         // log file should be empty
179         try (FileReader reader = new FileReader(logFile)) {
180             assertFalse(reader.ready());
181         }
182     }
183 
184     @ParameterizedTest
185     @ValueSource(strings = {
186     // @formatter:off
187             "bandint_oom.pack",
188             "cpfloat_oom.pack",
189             "cputf8_oom.pack",
190             "favoured_oom.pack",
191             "filebits_oom.pack",
192             "flags_oom.pack",
193             "references_oom.pack",
194             "segment_header_oom.pack",
195             "signatures_oom.pack"
196     // @formatter:on
197     })
198     // Tests of various files that can cause out of memory errors
199     public void testParsingOOMBounded(final String testFileName) throws Exception {
200         final URL url = Segment.class.getResource("/org/apache/commons/compress/pack/" + testFileName);
201         try (BoundedInputStream in = Pack200UnpackerAdapter.newBoundedInputStream(url);
202                 JarOutputStream out = new JarOutputStream(NullOutputStream.INSTANCE)) {
203             assertThrows(IOException.class, () -> new Archive(in, out).unpack());
204         }
205     }
206 
207     @ParameterizedTest
208     @ValueSource(strings = {
209     // @formatter:off
210             "bandint_oom.pack",
211             "cpfloat_oom.pack",
212             "cputf8_oom.pack",
213             "favoured_oom.pack",
214             "filebits_oom.pack",
215             "flags_oom.pack",
216             "references_oom.pack",
217             "segment_header_oom.pack",
218             "signatures_oom.pack"
219     // @formatter:on
220     })
221     // Tests of various files that can cause out of memory errors
222     public void testParsingOOMUnbounded(final String testFileName) throws Exception {
223         try (InputStream is = Segment.class.getResourceAsStream("/org/apache/commons/compress/pack/" + testFileName);
224                 JarOutputStream out = new JarOutputStream(NullOutputStream.INSTANCE)) {
225             assertThrows(IOException.class, () -> new Archive(is, out).unpack());
226         }
227     }
228 
229     @Test
230     public void testRemovePackFile() throws Exception {
231         final File original = new File(Archive.class.getResource("/pack200/sql.pack.gz").toURI());
232         final File copy = createTempFile("sqlcopy", ".pack.gz");
233         try (BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(original));
234                 BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(copy))) {
235             final byte[] bytes = new byte[256];
236             int read = inputStream.read(bytes);
237             while (read > 0) {
238                 outputStream.write(bytes, 0, read);
239                 read = inputStream.read(bytes);
240             }
241         }
242         final String inputFileName = copy.getPath();
243         final File file = createTempFile("sqlout", ".jar");
244         final String outputFileName = file.getPath();
245         final Archive archive = new Archive(inputFileName, outputFileName);
246         archive.setRemovePackFile(true);
247         archive.unpack();
248         assertFalse(copy.exists());
249     }
250 
251     // Test with an archive containing Annotations
252     @Test
253     public void testWithAnnotations() throws Exception {
254         in = Archive.class.getResourceAsStream("/pack200/annotations.pack.gz");
255         final File file = createTempFile("annotations", ".jar");
256         out = new JarOutputStream(new FileOutputStream(file));
257         final Archive archive = new Archive(in, out);
258         archive.unpack();
259     }
260 
261     // Test with an archive packed with the -E0 option
262     @Test
263     public void testWithE0() throws Exception {
264         in = Archive.class.getResourceAsStream("/pack200/simple-E0.pack.gz");
265         final File file = createTempFile("simple-e0", ".jar");
266         out = new JarOutputStream(new FileOutputStream(file));
267         final Archive archive = new Archive(in, out);
268         archive.unpack();
269     }
270 
271     // Test with an archive containing Harmony's JNDI module
272     @Test
273     public void testWithJNDIE1() throws Exception {
274         in = Archive.class.getResourceAsStream("/pack200/jndi-e1.pack.gz");
275         final File file = createTempFile("jndi-e1", ".jar");
276         out = new JarOutputStream(new FileOutputStream(file));
277         final Archive archive = new Archive(in, out);
278         archive.unpack();
279     }
280 
281     // Test with a class containing lots of local variables (regression test for
282     // HARMONY-5470)
283     @Test
284     public void testWithLargeClass() throws Exception {
285         in = Archive.class.getResourceAsStream("/pack200/LargeClass.pack.gz");
286         final File file = createTempFile("largeClass", ".jar");
287         out = new JarOutputStream(new FileOutputStream(file));
288         final Archive archive = new Archive(in, out);
289         archive.unpack();
290     }
291 
292     // Test with an archive containing Harmony's Pack200 module
293     @Test
294     public void testWithPack200() throws Exception {
295         in = Archive.class.getResourceAsStream("/pack200/pack200.pack.gz");
296         final File file = createTempFile("p200", ".jar");
297         out = new JarOutputStream(new FileOutputStream(file));
298         final Archive archive = new Archive(in, out);
299         archive.unpack();
300     }
301 
302     // Test with an archive containing Harmony's Pack200 module, packed with -E1
303     @Test
304     public void testWithPack200E1() throws Exception {
305         in = Archive.class.getResourceAsStream("/pack200/pack200-e1.pack.gz");
306         final File file = createTempFile("p200-e1", ".jar");
307         out = new JarOutputStream(new FileOutputStream(file));
308         final Archive archive = new Archive(in, out);
309         archive.unpack();
310     }
311 
312     // Test with an archive containing Harmony's SQL module
313     @Test
314     public void testWithSql() throws Exception {
315         in = Archive.class.getResourceAsStream("/pack200/sql.pack.gz");
316         final File file = createTempFile("sql", ".jar");
317         out = new JarOutputStream(new FileOutputStream(file));
318         final Archive archive = new Archive(in, out);
319         archive.unpack();
320         final File compareFile = new File(Archive.class.getResource("/pack200/sqlUnpacked.jar").toURI());
321         try (JarFile jarFile = new JarFile(file);
322                 JarFile jarFile2 = new JarFile(compareFile)) {
323 
324             final long differenceInJarSizes = Math.abs(compareFile.length() - file.length());
325             assertTrue(differenceInJarSizes < 100, "Expected jar files to be a similar size, difference was " + differenceInJarSizes + " bytes");
326 
327             final Enumeration<JarEntry> entries = jarFile.entries();
328             final Enumeration<JarEntry> entries2 = jarFile2.entries();
329             while (entries.hasMoreElements() && entries2.hasMoreElements()) {
330 
331                 final JarEntry entry = entries.nextElement();
332                 assertNotNull(entry);
333                 final String name = entry.getName();
334 
335                 final JarEntry entry2 = entries2.nextElement();
336                 assertNotNull(entry2);
337                 final String name2 = entry2.getName();
338 
339                 assertEquals(name, name2);
340 
341                 try (InputStream ours = jarFile.getInputStream(entry);
342                         InputStream expected = jarFile2.getInputStream(entry2);
343                         BufferedReader reader1 = new BufferedReader(new InputStreamReader(ours));
344                         BufferedReader reader2 = new BufferedReader(new InputStreamReader(expected))) {
345                     String line1 = reader1.readLine();
346                     String line2 = reader2.readLine();
347                     int i = 1;
348                     while (line1 != null || line2 != null) {
349                         assertEquals(line2, line1, "Unpacked class files differ for " + name);
350                         line1 = reader1.readLine();
351                         line2 = reader2.readLine();
352                         i++;
353                     }
354                     assertTrue(i > 0);
355                 }
356             }
357         }
358     }
359 
360     // Test with an archive containing Harmony's SQL module, packed with -E1
361     @Test
362     public void testWithSqlE1() throws Exception {
363         in = Archive.class.getResourceAsStream("/pack200/sql-e1.pack.gz");
364         final File file = createTempFile("sql-e1", ".jar");
365         out = new JarOutputStream(new FileOutputStream(file));
366         final Archive archive = new Archive(in, out);
367         archive.unpack();
368     }
369 
370 }