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