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         };
135     }
136 
137     @ParameterizedTest
138     @MethodSource
139     void testAnalyzeJarVersion(String jarName, String expectedRevision) throws Exception {
140         JarClasses jclass = getJarClasses(jarName);
141 
142         assertEquals(expectedRevision, jclass.getJdkRevision());
143     }
144 
145     @Test
146     void testAnalyzeJarWithModuleInfoClass() throws Exception {
147         JarData jarData = getJarData("tomcat-jni-9.0.75.jar");
148         JarClasses jclass = jarData.getJarClasses();
149         assertEquals("1.8", jclass.getJdkRevision());
150     }
151 
152     @Test
153     void testAnalyzeJarWithOnlyModuleInfoClass() throws Exception {
154         JarData jarData = getJarData("module-info-only-test-0.0.1.jar");
155         assertEquals(10, jarData.getNumEntries());
156         // root level information
157         assertEquals(8, jarData.getNumRootEntries());
158         JarClasses jclass = jarData.getJarClasses();
159         assertTrue(jclass.getImports().isEmpty());
160         assertTrue(jclass.getPackages().isEmpty());
161         assertTrue(jclass.getClassNames().isEmpty());
162         assertTrue(jclass.getMethods().isEmpty());
163         assertNull(jclass.getJdkRevision());
164 
165         JarVersionedRuntimes jarVersionedRuntimes = jarData.getVersionedRuntimes();
166         assertNotNull(jarVersionedRuntimes);
167         Map<Integer, JarVersionedRuntime> jarVersionedRuntimeMap = jarVersionedRuntimes.getVersionedRuntimeMap();
168         assertNotNull(jarVersionedRuntimeMap);
169         assertEquals(1, jarVersionedRuntimeMap.size()); // 11
170 
171         JarVersionedRuntime jarVersionedRuntime11 = jarVersionedRuntimes.getJarVersionedRuntime(11);
172         JarClasses jarClasses11 = jarVersionedRuntime11.getJarClasses();
173         assertEquals("11", jarClasses11.getJdkRevision());
174         assertTrue(jarClasses11.getImports().isEmpty());
175         assertEquals(1, jarClasses11.getPackages().size());
176         assertEquals("", jarClasses11.getPackages().get(0));
177         assertEquals(1, jarClasses11.getClassNames().size());
178         assertTrue(jarClasses11.getMethods().isEmpty());
179         assertEquals(2, jarVersionedRuntime11.getNumEntries());
180         assertEntriesContains(jarVersionedRuntime11.getEntries(), "META-INF/versions/11/module-info.class");
181     }
182 
183     @Test
184     void testAnalyzeMultiReleaseJarVersion() throws Exception {
185         JarData jarData = getJarData("multi-release-test-0.0.1.jar");
186         assertEquals(37, jarData.getNumEntries());
187         // root level information
188         assertEquals(17, jarData.getNumRootEntries());
189         JarClasses jclass = jarData.getJarClasses();
190         assertEquals("1.8", jclass.getJdkRevision());
191         assertFalse(jclass.getImports().isEmpty());
192         assertEquals(1, jclass.getPackages().size());
193         assertEquals(1, jclass.getClassNames().size());
194         assertFalse(jclass.getMethods().isEmpty());
195         assertEntriesContains(jarData.getEntries(), "resource.txt");
196 
197         JarVersionedRuntimes jarVersionedRuntimes = jarData.getVersionedRuntimes();
198         assertNotNull(jarVersionedRuntimes);
199         Map<Integer, JarVersionedRuntime> jarVersionedRuntimeMap = jarVersionedRuntimes.getVersionedRuntimeMap();
200         assertNotNull(jarVersionedRuntimeMap);
201         assertEquals(2, jarVersionedRuntimeMap.size()); // 9 and 11
202 
203         JarVersionedRuntime jarVersionedRuntime9 = jarVersionedRuntimes.getJarVersionedRuntime(9);
204         JarClasses jarClasses9 = jarVersionedRuntime9.getJarClasses();
205         assertEquals("9", jarClasses9.getJdkRevision());
206         assertFalse(jarClasses9.getImports().isEmpty());
207         assertEquals(1, jarClasses9.getPackages().size());
208         assertEquals(1, jarClasses9.getClassNames().size());
209         assertFalse(jarClasses9.getMethods().isEmpty());
210         assertEquals(10, jarVersionedRuntime9.getNumEntries());
211         assertEntriesContains(jarVersionedRuntime9.getEntries(), "META-INF/versions/9/resource.txt");
212 
213         JarVersionedRuntime jarVersionedRuntime11 = jarVersionedRuntimes.getJarVersionedRuntime(11);
214         JarClasses jarClasses11 = jarVersionedRuntime11.getJarClasses();
215         assertEquals("11", jarClasses11.getJdkRevision());
216         assertFalse(jarClasses11.getImports().isEmpty());
217         assertEquals(1, jarClasses11.getPackages().size());
218         assertEquals(1, jarClasses11.getClassNames().size());
219         assertFalse(jarClasses11.getMethods().isEmpty());
220         assertEquals(10, jarVersionedRuntime11.getNumEntries());
221         assertEntriesContains(jarVersionedRuntime11.getEntries(), "META-INF/versions/11/resource.txt");
222 
223         // test ordering
224         assertEquals("[9, 11]", jarVersionedRuntimeMap.keySet().toString());
225 
226         // test best fit
227         try {
228             jarVersionedRuntimes.getBestFitJarVersionedRuntime(null);
229             fail("It should throw an NPE");
230         } catch (NullPointerException e) {
231             assertTrue(true);
232         }
233         assertNull(jarVersionedRuntimes.getBestFitJarVersionedRuntime(8)); // unreal value but good just for testing
234         assertEquals(
235                 "9",
236                 jarVersionedRuntimes
237                         .getBestFitJarVersionedRuntime(9)
238                         .getJarClasses()
239                         .getJdkRevision());
240         assertEquals(
241                 "9",
242                 jarVersionedRuntimes
243                         .getBestFitJarVersionedRuntime(10)
244                         .getJarClasses()
245                         .getJdkRevision());
246         assertEquals(
247                 "11",
248                 jarVersionedRuntimes
249                         .getBestFitJarVersionedRuntime(11)
250                         .getJarClasses()
251                         .getJdkRevision());
252         assertEquals(
253                 "11",
254                 jarVersionedRuntimes
255                         .getBestFitJarVersionedRuntime(20)
256                         .getJarClasses()
257                         .getJdkRevision());
258 
259         try {
260             jarVersionedRuntimes.getBestFitJarVersionedRuntimeBySystemProperty(null);
261             fail("It should throw an NPE");
262         } catch (NullPointerException e) {
263             assertTrue(true);
264         }
265 
266         try {
267             getBestFitReleaseBySystemProperty(jarVersionedRuntimes, null);
268             fail("It should throw an NPE");
269         } catch (NullPointerException e) {
270             assertTrue(true);
271         }
272 
273         try {
274             getBestFitReleaseBySystemProperty(jarVersionedRuntimes, "xxx");
275             fail("It should throw an ISE");
276         } catch (IllegalStateException e) {
277             assertTrue(true);
278         }
279 
280         assertNull(getBestFitReleaseBySystemProperty(jarVersionedRuntimes, "8"));
281         assertEquals(
282                 "9",
283                 getBestFitReleaseBySystemProperty(jarVersionedRuntimes, "9")
284                         .getJarClasses()
285                         .getJdkRevision());
286         assertEquals(
287                 "9",
288                 getBestFitReleaseBySystemProperty(jarVersionedRuntimes, "10")
289                         .getJarClasses()
290                         .getJdkRevision());
291         assertEquals(
292                 "11",
293                 getBestFitReleaseBySystemProperty(jarVersionedRuntimes, "11")
294                         .getJarClasses()
295                         .getJdkRevision());
296         assertEquals(
297                 "11",
298                 getBestFitReleaseBySystemProperty(jarVersionedRuntimes, "20")
299                         .getJarClasses()
300                         .getJdkRevision());
301     }
302 
303     /**
304      * Exposes issue MSHARED-1413
305      */
306     @Test
307     public void testAnalyzeMultiReleaseJarWithVersion11HasALowerJdkRevisionClass() {
308         try {
309             // Version 11 has one class compiled to target Java 1.8
310             JarData jarData = getJarData("multi-release-version-with-lower-jdk-revision-class-0.0.1.jar");
311             JarClasses jclass = jarData.getJarClasses();
312 
313             assertNull(jclass.getJdkRevision());
314 
315             JarVersionedRuntimes jarVersionedRuntimes = jarData.getVersionedRuntimes();
316             assertNotNull(jarVersionedRuntimes);
317             Map<Integer, JarVersionedRuntime> jarVersionedRuntimeMap = jarVersionedRuntimes.getVersionedRuntimeMap();
318             assertNotNull(jarVersionedRuntimeMap);
319             assertEquals(1, jarVersionedRuntimeMap.size()); // 11
320 
321             JarVersionedRuntime jarVersionedRuntime11 = jarVersionedRuntimes.getJarVersionedRuntime(11);
322             JarClasses jarClasses11 = jarVersionedRuntime11.getJarClasses();
323             assertEquals("1.8", jarClasses11.getJdkRevision());
324         } catch (Exception e) {
325             fail("It should not raise an exception", e);
326         }
327     }
328 
329     /**
330      * Ensures no exceptions are raised when versioned content does not contain classes (just resources)
331      */
332     @Test
333     public void testAnalyzeMultiReleaseJarResourcesOnly() {
334         try {
335             JarData jarData = getJarData("multi-release-resources-only-0.0.1.jar");
336             JarClasses jclass = jarData.getJarClasses();
337 
338             assertEquals("1.8", jclass.getJdkRevision());
339 
340             JarVersionedRuntimes jarVersionedRuntimes = jarData.getVersionedRuntimes();
341             assertNotNull(jarVersionedRuntimes);
342             Map<Integer, JarVersionedRuntime> jarVersionedRuntimeMap = jarVersionedRuntimes.getVersionedRuntimeMap();
343             assertNotNull(jarVersionedRuntimeMap);
344             assertEquals(2, jarVersionedRuntimeMap.size()); // 9 and 11
345 
346             JarVersionedRuntime jarVersionedRuntime9 = jarVersionedRuntimes.getJarVersionedRuntime(9);
347             JarClasses jarClasses9 = jarVersionedRuntime9.getJarClasses();
348             // no classes found
349             assertNull(jarClasses9.getJdkRevision());
350 
351             JarVersionedRuntime jarVersionedRuntime11 = jarVersionedRuntimes.getJarVersionedRuntime(11);
352             JarClasses jarClasses11 = jarVersionedRuntime11.getJarClasses();
353             // no classes found
354             assertNull(jarClasses11.getJdkRevision());
355         } catch (Exception e) {
356             fail("It should not raise an exception", e);
357         }
358     }
359 
360     private void assertEntriesContains(List<JarEntry> list, final String entryToFind) {
361         assertTrue(list.stream().anyMatch(entry -> entry.getName().equals(entryToFind)));
362     }
363 
364     private JarVersionedRuntime getBestFitReleaseBySystemProperty(
365             JarVersionedRuntimes jarVersionedRuntimes, String value) {
366         String key = "maven.shared.jar.test.vm";
367         System.setProperty(key, value);
368         return jarVersionedRuntimes.getBestFitJarVersionedRuntimeBySystemProperty(key);
369     }
370 
371     private JarData getJarData(String filename) throws Exception {
372         File file = getSampleJar(filename);
373 
374         JarAnalyzer jarAnalyzer = new JarAnalyzer(file);
375         JarClasses jclass = analyzer.analyze(jarAnalyzer);
376         JarData jarData = jarAnalyzer.getJarData();
377         assertNotNull(jclass, "JarClasses");
378         assertNotNull(jarData, "JarData");
379 
380         return jarData;
381     }
382 
383     private JarClasses getJarClasses(String filename) throws Exception {
384         File file = getSampleJar(filename);
385 
386         JarClasses jclass = analyzer.analyze(new JarAnalyzer(file));
387         assertNotNull(jclass, "JarClasses");
388 
389         return jclass;
390     }
391 }