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.maven.shared.jar.classes;
20  
21  import javax.inject.Inject;
22  
23  import java.io.File;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.jar.JarEntry;
27  
28  import org.apache.maven.shared.jar.AbstractJarAnalyzerTestCase;
29  import org.apache.maven.shared.jar.JarAnalyzer;
30  import org.apache.maven.shared.jar.JarData;
31  import org.codehaus.plexus.testing.PlexusTest;
32  import org.junit.jupiter.api.Test;
33  import org.junit.jupiter.params.ParameterizedTest;
34  import org.junit.jupiter.params.provider.MethodSource;
35  
36  import static org.junit.jupiter.api.Assertions.assertEquals;
37  import static org.junit.jupiter.api.Assertions.assertFalse;
38  import static org.junit.jupiter.api.Assertions.assertNotNull;
39  import static org.junit.jupiter.api.Assertions.assertNull;
40  import static org.junit.jupiter.api.Assertions.assertTrue;
41  import static org.junit.jupiter.api.Assertions.fail;
42  
43  /**
44   * JarAnalyzer Classes Test Case
45   */
46  @PlexusTest
47  class JarClassesAnalyzerTest extends AbstractJarAnalyzerTestCase {
48  
49      @Inject
50      private JarClassesAnalysis analyzer;
51  
52      @Test
53      void testAnalyzeJXR() throws Exception {
54          JarClasses jclass = getJarClasses("jxr.jar");
55  
56          assertFalse(jclass.getImports().isEmpty(), "classes.imports.length > 0");
57          assertFalse(jclass.getPackages().isEmpty(), "classes.packages.length > 0");
58          assertFalse(jclass.getMethods().isEmpty(), "classes.methods.length > 0");
59  
60          assertNotContainsRegex("Import List", "[\\[\\)\\(\\;]", jclass.getImports());
61  
62          // TODO: test for classes, methods, etc.
63  
64          assertTrue(jclass.getImports().contains("org.apache.maven.jxr.JXR"), "classes.imports");
65          assertTrue(jclass.getImports().contains("org.apache.oro.text.perl.Perl5Util"), "classes.imports");
66          assertTrue(jclass.getPackages().contains("org.apache.maven.jxr.pacman"), "classes.packages");
67      }
68  
69      @Test
70      void testAnalyzeANT() throws Exception {
71          JarClasses jclass = getJarClasses("ant.jar");
72  
73          assertFalse(jclass.getImports().isEmpty(), "classes.imports.length > 0");
74          assertFalse(jclass.getPackages().isEmpty(), "classes.packages.length > 0");
75          assertFalse(jclass.getMethods().isEmpty(), "classes.methods.length > 0");
76  
77          assertNotContainsRegex("Import List", "[\\[\\)\\(\\;]", jclass.getImports());
78  
79          assertTrue(jclass.getImports().contains("java.util.zip.GZIPInputStream"), "classes.imports");
80          assertTrue(jclass.getImports().contains("org.apache.tools.ant.XmlLogger$TimedElement"), "classes.imports");
81          assertTrue(jclass.getImports().contains("org.apache.tools.mail.MailMessage"), "classes.imports");
82          assertTrue(jclass.getPackages().contains("org.apache.tools.ant"), "classes.packages");
83          assertTrue(jclass.getPackages().contains("org.apache.tools.bzip2"), "classes.packages");
84      }
85  
86      @Test
87      void testAnalyzeJarWithInvalidClassFile() throws Exception {
88          JarClasses jclass = getJarClasses("invalid-class-file.jar");
89  
90          // Doesn't fail, as exceptions are ignored.
91          assertTrue(jclass.getClassNames().isEmpty());
92          assertTrue(jclass.getPackages().isEmpty());
93          assertTrue(jclass.getImports().isEmpty());
94          assertNull(jclass.getJdkRevision());
95          assertTrue(jclass.getMethods().isEmpty());
96      }
97  
98      @Test
99      void testAnalyzeJarWithDebug() throws Exception {
100         JarClasses jclass = getJarClasses("helloworld-1.4-debug.jar");
101 
102         assertTrue(jclass.isDebugPresent(), "has debug");
103     }
104 
105     @Test
106     void testAnalyzeJarWithoutDebug() throws Exception {
107         JarClasses jclass = getJarClasses("helloworld-1.4.jar");
108 
109         assertFalse(jclass.isDebugPresent(), "no debug present");
110     }
111 
112     static String[][] testAnalyzeJarVersion() {
113         return new String[][] {
114             {"helloworld-1.1.jar", "1.1"},
115             {"helloworld-1.2.jar", "1.2"},
116             {"helloworld-1.3.jar", "1.3"},
117             {"helloworld-1.4.jar", "1.4"},
118             {"helloworld-1.5.jar", "1.5"},
119             {"helloworld-1.6.jar", "1.6"},
120             {"helloworld-1.7.jar", "1.7"},
121             {"helloworld-1.8.jar", "1.8"},
122             {"helloworld-9.jar", "9"},
123             {"helloworld-10.jar", "10"},
124             {"helloworld-11.jar", "11"},
125             {"helloworld-12.jar", "12"},
126             {"helloworld-13.jar", "13"},
127             {"helloworld-14.jar", "14"},
128             {"helloworld-15.jar", "15"},
129             {"helloworld-16.jar", "16"},
130             {"helloworld-17.jar", "17"},
131             {"helloworld-18.jar", "18"},
132             {"helloworld-19.jar", "19"},
133             {"helloworld-20.jar", "20"},
134             {"helloworld-21.jar", "21"},
135             {"helloworld-23.jar", "23"},
136             {"helloworld-22.jar", "22"},
137             {"helloworld-24.jar", "24"},
138             {"helloworld-25.jar", "25"}
139         };
140     }
141 
142     @ParameterizedTest
143     @MethodSource
144     void testAnalyzeJarVersion(String jarName, String expectedRevision) throws Exception {
145         JarClasses jclass = getJarClasses(jarName);
146 
147         assertEquals(expectedRevision, jclass.getJdkRevision());
148     }
149 
150     @Test
151     void testAnalyzeJarWithModuleInfoClass() throws Exception {
152         JarData jarData = getJarData("tomcat-jni-9.0.75.jar");
153         JarClasses jclass = jarData.getJarClasses();
154         assertEquals("1.8", jclass.getJdkRevision());
155     }
156 
157     @Test
158     void testAnalyzeJarWithOnlyModuleInfoClass() throws Exception {
159         JarData jarData = getJarData("module-info-only-test-0.0.1.jar");
160         assertEquals(10, jarData.getNumEntries());
161         // root level information
162         assertEquals(8, jarData.getNumRootEntries());
163         JarClasses jclass = jarData.getJarClasses();
164         assertTrue(jclass.getImports().isEmpty());
165         assertTrue(jclass.getPackages().isEmpty());
166         assertTrue(jclass.getClassNames().isEmpty());
167         assertTrue(jclass.getMethods().isEmpty());
168         assertNull(jclass.getJdkRevision());
169 
170         JarVersionedRuntimes jarVersionedRuntimes = jarData.getVersionedRuntimes();
171         assertNotNull(jarVersionedRuntimes);
172         Map<Integer, JarVersionedRuntime> jarVersionedRuntimeMap = jarVersionedRuntimes.getVersionedRuntimeMap();
173         assertNotNull(jarVersionedRuntimeMap);
174         assertEquals(1, jarVersionedRuntimeMap.size()); // 11
175 
176         JarVersionedRuntime jarVersionedRuntime11 = jarVersionedRuntimes.getJarVersionedRuntime(11);
177         JarClasses jarClasses11 = jarVersionedRuntime11.getJarClasses();
178         assertEquals("11", jarClasses11.getJdkRevision());
179         assertTrue(jarClasses11.getImports().isEmpty());
180         assertEquals(1, jarClasses11.getPackages().size());
181         assertEquals("", jarClasses11.getPackages().get(0));
182         assertEquals(1, jarClasses11.getClassNames().size());
183         assertTrue(jarClasses11.getMethods().isEmpty());
184         assertEquals(2, jarVersionedRuntime11.getNumEntries());
185         assertEntriesContains(jarVersionedRuntime11.getEntries(), "META-INF/versions/11/module-info.class");
186     }
187 
188     @Test
189     void testAnalyzeMultiReleaseJarVersion() throws Exception {
190         JarData jarData = getJarData("multi-release-test-0.0.1.jar");
191         assertEquals(37, jarData.getNumEntries());
192         // root level information
193         assertEquals(17, jarData.getNumRootEntries());
194         JarClasses jclass = jarData.getJarClasses();
195         assertEquals("1.8", jclass.getJdkRevision());
196         assertFalse(jclass.getImports().isEmpty());
197         assertEquals(1, jclass.getPackages().size());
198         assertEquals(1, jclass.getClassNames().size());
199         assertFalse(jclass.getMethods().isEmpty());
200         assertEntriesContains(jarData.getEntries(), "resource.txt");
201 
202         JarVersionedRuntimes jarVersionedRuntimes = jarData.getVersionedRuntimes();
203         assertNotNull(jarVersionedRuntimes);
204         Map<Integer, JarVersionedRuntime> jarVersionedRuntimeMap = jarVersionedRuntimes.getVersionedRuntimeMap();
205         assertNotNull(jarVersionedRuntimeMap);
206         assertEquals(2, jarVersionedRuntimeMap.size()); // 9 and 11
207 
208         JarVersionedRuntime jarVersionedRuntime9 = jarVersionedRuntimes.getJarVersionedRuntime(9);
209         JarClasses jarClasses9 = jarVersionedRuntime9.getJarClasses();
210         assertEquals("9", jarClasses9.getJdkRevision());
211         assertFalse(jarClasses9.getImports().isEmpty());
212         assertEquals(1, jarClasses9.getPackages().size());
213         assertEquals(1, jarClasses9.getClassNames().size());
214         assertFalse(jarClasses9.getMethods().isEmpty());
215         assertEquals(10, jarVersionedRuntime9.getNumEntries());
216         assertEntriesContains(jarVersionedRuntime9.getEntries(), "META-INF/versions/9/resource.txt");
217 
218         JarVersionedRuntime jarVersionedRuntime11 = jarVersionedRuntimes.getJarVersionedRuntime(11);
219         JarClasses jarClasses11 = jarVersionedRuntime11.getJarClasses();
220         assertEquals("11", jarClasses11.getJdkRevision());
221         assertFalse(jarClasses11.getImports().isEmpty());
222         assertEquals(1, jarClasses11.getPackages().size());
223         assertEquals(1, jarClasses11.getClassNames().size());
224         assertFalse(jarClasses11.getMethods().isEmpty());
225         assertEquals(10, jarVersionedRuntime11.getNumEntries());
226         assertEntriesContains(jarVersionedRuntime11.getEntries(), "META-INF/versions/11/resource.txt");
227 
228         // test ordering
229         assertEquals("[9, 11]", jarVersionedRuntimeMap.keySet().toString());
230 
231         // test best fit
232         try {
233             jarVersionedRuntimes.getBestFitJarVersionedRuntime(null);
234             fail("It should throw an NPE");
235         } catch (NullPointerException e) {
236             assertTrue(true);
237         }
238         assertNull(jarVersionedRuntimes.getBestFitJarVersionedRuntime(8)); // unreal value but good just for testing
239         assertEquals(
240                 "9",
241                 jarVersionedRuntimes
242                         .getBestFitJarVersionedRuntime(9)
243                         .getJarClasses()
244                         .getJdkRevision());
245         assertEquals(
246                 "9",
247                 jarVersionedRuntimes
248                         .getBestFitJarVersionedRuntime(10)
249                         .getJarClasses()
250                         .getJdkRevision());
251         assertEquals(
252                 "11",
253                 jarVersionedRuntimes
254                         .getBestFitJarVersionedRuntime(11)
255                         .getJarClasses()
256                         .getJdkRevision());
257         assertEquals(
258                 "11",
259                 jarVersionedRuntimes
260                         .getBestFitJarVersionedRuntime(20)
261                         .getJarClasses()
262                         .getJdkRevision());
263 
264         try {
265             jarVersionedRuntimes.getBestFitJarVersionedRuntimeBySystemProperty(null);
266             fail("It should throw an NPE");
267         } catch (NullPointerException e) {
268             assertTrue(true);
269         }
270 
271         try {
272             getBestFitReleaseBySystemProperty(jarVersionedRuntimes, null);
273             fail("It should throw an NPE");
274         } catch (NullPointerException e) {
275             assertTrue(true);
276         }
277 
278         try {
279             getBestFitReleaseBySystemProperty(jarVersionedRuntimes, "xxx");
280             fail("It should throw an ISE");
281         } catch (IllegalStateException e) {
282             assertTrue(true);
283         }
284 
285         assertNull(getBestFitReleaseBySystemProperty(jarVersionedRuntimes, "8"));
286         assertEquals(
287                 "9",
288                 getBestFitReleaseBySystemProperty(jarVersionedRuntimes, "9")
289                         .getJarClasses()
290                         .getJdkRevision());
291         assertEquals(
292                 "9",
293                 getBestFitReleaseBySystemProperty(jarVersionedRuntimes, "10")
294                         .getJarClasses()
295                         .getJdkRevision());
296         assertEquals(
297                 "11",
298                 getBestFitReleaseBySystemProperty(jarVersionedRuntimes, "11")
299                         .getJarClasses()
300                         .getJdkRevision());
301         assertEquals(
302                 "11",
303                 getBestFitReleaseBySystemProperty(jarVersionedRuntimes, "20")
304                         .getJarClasses()
305                         .getJdkRevision());
306     }
307 
308     /**
309      * Exposes issue MSHARED-1413
310      */
311     @Test
312     public void testAnalyzeMultiReleaseJarWithVersion11HasALowerJdkRevisionClass() {
313         try {
314             // Version 11 has one class compiled to target Java 1.8
315             JarData jarData = getJarData("multi-release-version-with-lower-jdk-revision-class-0.0.1.jar");
316             JarClasses jclass = jarData.getJarClasses();
317 
318             assertNull(jclass.getJdkRevision());
319 
320             JarVersionedRuntimes jarVersionedRuntimes = jarData.getVersionedRuntimes();
321             assertNotNull(jarVersionedRuntimes);
322             Map<Integer, JarVersionedRuntime> jarVersionedRuntimeMap = jarVersionedRuntimes.getVersionedRuntimeMap();
323             assertNotNull(jarVersionedRuntimeMap);
324             assertEquals(1, jarVersionedRuntimeMap.size()); // 11
325 
326             JarVersionedRuntime jarVersionedRuntime11 = jarVersionedRuntimes.getJarVersionedRuntime(11);
327             JarClasses jarClasses11 = jarVersionedRuntime11.getJarClasses();
328             assertEquals("1.8", jarClasses11.getJdkRevision());
329         } catch (Exception e) {
330             fail("It should not raise an exception", e);
331         }
332     }
333 
334     /**
335      * Ensures no exceptions are raised when versioned content does not contain classes (just resources)
336      */
337     @Test
338     public void testAnalyzeMultiReleaseJarResourcesOnly() {
339         try {
340             JarData jarData = getJarData("multi-release-resources-only-0.0.1.jar");
341             JarClasses jclass = jarData.getJarClasses();
342 
343             assertEquals("1.8", jclass.getJdkRevision());
344 
345             JarVersionedRuntimes jarVersionedRuntimes = jarData.getVersionedRuntimes();
346             assertNotNull(jarVersionedRuntimes);
347             Map<Integer, JarVersionedRuntime> jarVersionedRuntimeMap = jarVersionedRuntimes.getVersionedRuntimeMap();
348             assertNotNull(jarVersionedRuntimeMap);
349             assertEquals(2, jarVersionedRuntimeMap.size()); // 9 and 11
350 
351             JarVersionedRuntime jarVersionedRuntime9 = jarVersionedRuntimes.getJarVersionedRuntime(9);
352             JarClasses jarClasses9 = jarVersionedRuntime9.getJarClasses();
353             // no classes found
354             assertNull(jarClasses9.getJdkRevision());
355 
356             JarVersionedRuntime jarVersionedRuntime11 = jarVersionedRuntimes.getJarVersionedRuntime(11);
357             JarClasses jarClasses11 = jarVersionedRuntime11.getJarClasses();
358             // no classes found
359             assertNull(jarClasses11.getJdkRevision());
360         } catch (Exception e) {
361             fail("It should not raise an exception", e);
362         }
363     }
364 
365     private void assertEntriesContains(List<JarEntry> list, final String entryToFind) {
366         assertTrue(list.stream().anyMatch(entry -> entry.getName().equals(entryToFind)));
367     }
368 
369     private JarVersionedRuntime getBestFitReleaseBySystemProperty(
370             JarVersionedRuntimes jarVersionedRuntimes, String value) {
371         String key = "maven.shared.jar.test.vm";
372         System.setProperty(key, value);
373         return jarVersionedRuntimes.getBestFitJarVersionedRuntimeBySystemProperty(key);
374     }
375 
376     private JarData getJarData(String filename) throws Exception {
377         File file = getSampleJar(filename);
378 
379         JarAnalyzer jarAnalyzer = new JarAnalyzer(file);
380         JarClasses jclass = analyzer.analyze(jarAnalyzer);
381         JarData jarData = jarAnalyzer.getJarData();
382         assertNotNull(jclass, "JarClasses");
383         assertNotNull(jarData, "JarData");
384 
385         return jarData;
386     }
387 
388     private JarClasses getJarClasses(String filename) throws Exception {
389         File file = getSampleJar(filename);
390 
391         JarClasses jclass = analyzer.analyze(new JarAnalyzer(file));
392         assertNotNull(jclass, "JarClasses");
393 
394         return jclass;
395     }
396 }