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.juneau.commons.reflect;
18
19 import static java.lang.annotation.ElementType.*;
20 import static java.lang.annotation.RetentionPolicy.*;
21 import static org.junit.jupiter.api.Assertions.*;
22
23 import java.lang.annotation.*;
24 import java.net.*;
25
26 import org.apache.juneau.*;
27 import org.junit.jupiter.api.*;
28
29 class PackageInfo_Test extends TestBase {
30
31 @Target(PACKAGE)
32 @Retention(RUNTIME)
33 public static @interface TestPackageAnnotation {
34 String value() default "";
35 }
36
37 // Test classes in different packages
38 public static class TestClass1 {}
39 public static class TestClass2 {}
40
41 //====================================================================================================
42 // equals(Object)
43 //====================================================================================================
44 @Test
45 void a001_equals() {
46 var pi1 = PackageInfo.of(TestClass1.class);
47 var pi2 = PackageInfo.of(TestClass2.class);
48 var pi3 = PackageInfo.of(TestClass1.class);
49
50 // Same package should be equal
51 assertEquals(pi1, pi3);
52 assertEquals(pi1.hashCode(), pi3.hashCode());
53
54 // Different packages should not be equal
55 if (!pi1.getName().equals(pi2.getName())) {
56 assertNotEquals(pi1, pi2);
57 }
58
59 // Should equal the underlying Package
60 assertEquals(pi1, pi1.inner());
61 }
62
63 //====================================================================================================
64 // getAnnotatableType()
65 //====================================================================================================
66 @Test
67 void a002_getAnnotatableType() {
68 var pi = PackageInfo.of(TestClass1.class);
69 assertEquals(AnnotatableType.PACKAGE_TYPE, pi.getAnnotatableType());
70 }
71
72 //====================================================================================================
73 // getAnnotations()
74 //====================================================================================================
75 @Test
76 void a003_getAnnotations() {
77 var pi = PackageInfo.of(TestClass1.class);
78 var annotations = pi.getAnnotations();
79 assertNotNull(annotations);
80 // May or may not have annotations depending on package-info.java
81 }
82
83 //====================================================================================================
84 // getAnnotations(Class<A>)
85 //====================================================================================================
86 @Test
87 void a004_getAnnotations_typed() {
88 var pi = PackageInfo.of(TestClass1.class);
89 var annotations = pi.getAnnotations(TestPackageAnnotation.class);
90 assertNotNull(annotations);
91 // May or may not have annotations depending on package-info.java
92 var count = annotations.count();
93 assertTrue(count >= 0);
94 }
95
96 //====================================================================================================
97 // getImplementationTitle()
98 //====================================================================================================
99 @Test
100 void a005_getImplementationTitle() {
101 var pi = PackageInfo.of(String.class);
102 // May be null if not specified in manifest
103 // Just verify the method doesn't throw
104 assertDoesNotThrow(() -> pi.getImplementationTitle());
105 }
106
107 //====================================================================================================
108 // getImplementationVendor()
109 //====================================================================================================
110 @Test
111 void a006_getImplementationVendor() {
112 var pi = PackageInfo.of(String.class);
113 // May be null if not specified in manifest
114 // Just verify the method doesn't throw
115 assertDoesNotThrow(() -> pi.getImplementationVendor());
116 }
117
118 //====================================================================================================
119 // getImplementationVersion()
120 //====================================================================================================
121 @Test
122 void a007_getImplementationVersion() {
123 var pi = PackageInfo.of(String.class);
124 // May be null if not specified in manifest
125 // Just verify the method doesn't throw
126 assertDoesNotThrow(() -> pi.getImplementationVersion());
127 }
128
129 //====================================================================================================
130 // getLabel()
131 //====================================================================================================
132 @Test
133 void a008_getLabel() {
134 var pi = PackageInfo.of(TestClass1.class);
135 var label = pi.getLabel();
136 assertNotNull(label);
137 assertEquals(pi.getName(), label);
138 }
139
140 //====================================================================================================
141 // getName()
142 //====================================================================================================
143 @Test
144 void a009_getName() {
145 var pi = PackageInfo.of(TestClass1.class);
146 var name = pi.getName();
147 assertNotNull(name);
148 assertEquals("org.apache.juneau.commons.reflect", name);
149
150 // Test with String.class
151 var pi2 = PackageInfo.of(String.class);
152 assertEquals("java.lang", pi2.getName());
153 }
154
155 //====================================================================================================
156 // getSpecificationTitle()
157 //====================================================================================================
158 @Test
159 void a010_getSpecificationTitle() {
160 var pi = PackageInfo.of(String.class);
161 // May be null if not specified in manifest
162 // Just verify the method doesn't throw
163 assertDoesNotThrow(() -> pi.getSpecificationTitle());
164 }
165
166 //====================================================================================================
167 // getSpecificationVendor()
168 //====================================================================================================
169 @Test
170 void a011_getSpecificationVendor() {
171 var pi = PackageInfo.of(String.class);
172 // May be null if not specified in manifest
173 // Just verify the method doesn't throw
174 assertDoesNotThrow(() -> pi.getSpecificationVendor());
175 }
176
177 //====================================================================================================
178 // getSpecificationVersion()
179 //====================================================================================================
180 @Test
181 void a012_getSpecificationVersion() {
182 var pi = PackageInfo.of(String.class);
183 // May be null if not specified in manifest
184 // Just verify the method doesn't throw
185 assertDoesNotThrow(() -> pi.getSpecificationVersion());
186 }
187
188 //====================================================================================================
189 // hashCode()
190 //====================================================================================================
191 @Test
192 void a013_hashCode() {
193 var pi1 = PackageInfo.of(TestClass1.class);
194 var pi2 = PackageInfo.of(TestClass1.class);
195 var pi3 = PackageInfo.of(TestClass2.class);
196
197 // Same package should have same hash code
198 assertEquals(pi1.hashCode(), pi2.hashCode());
199
200 // Should match underlying Package hash code
201 assertEquals(pi1.hashCode(), pi1.inner().hashCode());
202
203 // Different packages may or may not have different hash codes
204 if (!pi1.getName().equals(pi3.getName())) {
205 // Hash codes might still be equal (collision), but usually different
206 }
207 }
208
209 //====================================================================================================
210 // inner()
211 //====================================================================================================
212 @Test
213 void a014_inner() {
214 var pi = PackageInfo.of(TestClass1.class);
215 var pkg = pi.inner();
216 assertNotNull(pkg);
217 assertEquals("org.apache.juneau.commons.reflect", pkg.getName());
218 }
219
220 //====================================================================================================
221 // isCompatibleWith(String)
222 //====================================================================================================
223 @Test
224 void a015_isCompatibleWith() {
225 var pi = PackageInfo.of(String.class);
226
227 // Test line 309: ensure inner.isCompatibleWith(desired) is called
228 // Package versions come from JAR manifest (Specification-Version header)
229 // Most test packages don't have versions, so isCompatibleWith throws NumberFormatException
230 // However, line 309 is still executed - the exception is thrown FROM that line
231
232 // Try to find a package with a version (e.g., from standard library or a dependency)
233 // If found, test the normal return path
234 var specVersion = pi.getSpecificationVersion();
235 if (specVersion != null && !specVersion.isEmpty()) {
236 // Line 309: normal return path when package has a version
237 var compatible = pi.isCompatibleWith("1.0");
238 assertNotNull(compatible);
239
240 // Test with another version
241 var compatible2 = pi.isCompatibleWith("2.0");
242 assertNotNull(compatible2);
243 } else {
244 // Package doesn't have a version - isCompatibleWith will throw NumberFormatException
245 // Line 309 is still executed, exception is thrown from inner.isCompatibleWith
246 assertThrows(NumberFormatException.class, () -> pi.isCompatibleWith("1.0"));
247 }
248
249 // Test with invalid version - should throw NumberFormatException
250 // Line 309 is executed, then exception is thrown from inner.isCompatibleWith
251 assertThrows(NumberFormatException.class, () -> pi.isCompatibleWith("invalid"));
252
253 // Test with null - Package.isCompatibleWith(null) throws NumberFormatException
254 // Line 309 is executed, then exception is thrown from inner.isCompatibleWith
255 assertThrows(NumberFormatException.class, () -> pi.isCompatibleWith(null));
256 }
257
258 //====================================================================================================
259 // isSealed()
260 //====================================================================================================
261 @Test
262 void a016_isSealed() {
263 var pi = PackageInfo.of(TestClass1.class);
264 var sealed = pi.isSealed();
265 // Most packages are not sealed
266 assertNotNull(sealed);
267 }
268
269 //====================================================================================================
270 // isSealed(URL)
271 //====================================================================================================
272 @Test
273 void a017_isSealed_withUrl() throws Exception {
274 var pi = PackageInfo.of(TestClass1.class);
275 var url = new URL("file:///test.jar");
276 var sealed = pi.isSealed(url);
277 // Most packages are not sealed with respect to a specific URL
278 assertNotNull(sealed);
279 }
280
281 //====================================================================================================
282 // of(Class<?>)
283 //====================================================================================================
284 @Test
285 void a018_of_withClass() {
286 var pi = PackageInfo.of(TestClass1.class);
287 assertNotNull(pi);
288 assertEquals("org.apache.juneau.commons.reflect", pi.getName());
289
290 // Test with null - should throw
291 assertThrows(NullPointerException.class, () -> PackageInfo.of((Class<?>)null));
292 }
293
294 //====================================================================================================
295 // of(ClassInfo)
296 //====================================================================================================
297 @Test
298 void a019_of_withClassInfo() {
299 var ci = ClassInfo.of(TestClass1.class);
300 var pi = PackageInfo.of(ci);
301 assertNotNull(pi);
302 assertEquals("org.apache.juneau.commons.reflect", pi.getName());
303 }
304
305 //====================================================================================================
306 // of(Package)
307 //====================================================================================================
308 @Test
309 void a020_of_withPackage() {
310 var pkg = TestClass1.class.getPackage();
311 var pi = PackageInfo.of(pkg);
312 assertNotNull(pi);
313 assertEquals("org.apache.juneau.commons.reflect", pi.getName());
314
315 // Test caching - should return same instance
316 var pi2 = PackageInfo.of(pkg);
317 assertSame(pi, pi2);
318
319 // Test with null - should throw
320 assertThrows(IllegalArgumentException.class, () -> PackageInfo.of((Package)null));
321 }
322
323 //====================================================================================================
324 // toString()
325 //====================================================================================================
326 @Test
327 void a021_toString() {
328 var pi = PackageInfo.of(TestClass1.class);
329 var str = pi.toString();
330 assertNotNull(str);
331 assertTrue(str.contains("package"));
332 assertTrue(str.contains("org.apache.juneau.commons.reflect"));
333
334 // Should match underlying Package toString
335 assertEquals(pi.inner().toString(), str);
336 }
337 }
338