• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.lang3;
18 
19 import static java.lang.annotation.ElementType.FIELD;
20 import static java.lang.annotation.RetentionPolicy.RUNTIME;
21 import static org.apache.commons.lang3.AnnotationUtilsTest.Stooge.CURLY;
22 import static org.apache.commons.lang3.AnnotationUtilsTest.Stooge.LARRY;
23 import static org.apache.commons.lang3.AnnotationUtilsTest.Stooge.MOE;
24 import static org.apache.commons.lang3.AnnotationUtilsTest.Stooge.SHEMP;
25 import static org.junit.jupiter.api.Assertions.assertEquals;
26 import static org.junit.jupiter.api.Assertions.assertFalse;
27 import static org.junit.jupiter.api.Assertions.assertNotEquals;
28 import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
29 import static org.junit.jupiter.api.Assertions.assertTrue;
30 
31 import java.lang.annotation.ElementType;
32 import java.lang.annotation.Retention;
33 import java.lang.annotation.RetentionPolicy;
34 import java.lang.annotation.Target;
35 import java.lang.reflect.Array;
36 import java.lang.reflect.Field;
37 import java.lang.reflect.InvocationHandler;
38 import java.lang.reflect.Proxy;
39 import java.time.Duration;
40 import java.util.Collection;
41 import java.util.Map;
42 
43 import org.junit.jupiter.api.BeforeEach;
44 import org.junit.jupiter.api.Test;
45 
46 /**
47  */
48 public class AnnotationUtilsTest extends AbstractLangTest {
49     @TestAnnotation(
50             booleanValue = false,
51             booleanValues = { false },
52             byteValue = 0,
53             byteValues = { 0 },
54             charValue = 0,
55             charValues = { 0 },
56             doubleValue = 0,
57             doubleValues = { 0 },
58             floatValue = 0,
59             floatValues = { 0 },
60             intValue = 0,
61             intValues = { 0 },
62             longValue = 0,
63             longValues = { 0 },
64             nest = @NestAnnotation(
65                     booleanValue = false,
66                     booleanValues = { false },
67                     byteValue = 0,
68                     byteValues = { 0 },
69                     charValue = 0,
70                     charValues = { 0 },
71                     doubleValue = 0,
72                     doubleValues = { 0 },
73                     floatValue = 0,
74                     floatValues = { 0 },
75                     intValue = 0,
76                     intValues = { 0 },
77                     longValue = 0,
78                     longValues = { 0 },
79                     shortValue = 0,
80                     shortValues = { 0 },
81                     stooge = CURLY,
82                     stooges = { MOE, LARRY, SHEMP },
83                     string = "",
84                     strings = { "" },
85                     type = Object.class,
86                     types = { Object.class }
87             ),
88             nests = {
89                 @NestAnnotation(
90                         booleanValue = false,
91                         booleanValues = { false },
92                         byteValue = 0,
93                         byteValues = { 0 },
94                         charValue = 0,
95                         charValues = { 0 },
96                         doubleValue = 0,
97                         doubleValues = { 0 },
98                         floatValue = 0,
99                         floatValues = { 0 },
100                         intValue = 0,
101                         intValues = { 0 },
102                         longValue = 0,
103                         longValues = { 0 },
104                         shortValue = 0,
105                         shortValues = { 0 },
106                         stooge = CURLY,
107                         stooges = { MOE, LARRY, SHEMP },
108                         string = "",
109                         strings = { "" },
110                         type = Object[].class,
111                         types = { Object[].class }
112                 )
113             },
114             shortValue = 0,
115             shortValues = { 0 },
116             stooge = SHEMP,
117             stooges = { MOE, LARRY, CURLY },
118             string = "",
119             strings = { "" },
120             type = Object.class,
121             types = { Object.class }
122     )
123     public Object dummy1;
124 
125     @TestAnnotation(
126             booleanValue = false,
127             booleanValues = { false },
128             byteValue = 0,
129             byteValues = { 0 },
130             charValue = 0,
131             charValues = { 0 },
132             doubleValue = 0,
133             doubleValues = { 0 },
134             floatValue = 0,
135             floatValues = { 0 },
136             intValue = 0,
137             intValues = { 0 },
138             longValue = 0,
139             longValues = { 0 },
140             nest = @NestAnnotation(
141                     booleanValue = false,
142                     booleanValues = { false },
143                     byteValue = 0,
144                     byteValues = { 0 },
145                     charValue = 0,
146                     charValues = { 0 },
147                     doubleValue = 0,
148                     doubleValues = { 0 },
149                     floatValue = 0,
150                     floatValues = { 0 },
151                     intValue = 0,
152                     intValues = { 0 },
153                     longValue = 0,
154                     longValues = { 0 },
155                     shortValue = 0,
156                     shortValues = { 0 },
157                     stooge = CURLY,
158                     stooges = { MOE, LARRY, SHEMP },
159                     string = "",
160                     strings = { "" },
161                     type = Object.class,
162                     types = { Object.class }
163             ),
164             nests = {
165                 @NestAnnotation(
166                         booleanValue = false,
167                         booleanValues = { false },
168                         byteValue = 0,
169                         byteValues = { 0 },
170                         charValue = 0,
171                         charValues = { 0 },
172                         doubleValue = 0,
173                         doubleValues = { 0 },
174                         floatValue = 0,
175                         floatValues = { 0 },
176                         intValue = 0,
177                         intValues = { 0 },
178                         longValue = 0,
179                         longValues = { 0 },
180                         shortValue = 0,
181                         shortValues = { 0 },
182                         stooge = CURLY,
183                         stooges = { MOE, LARRY, SHEMP },
184                         string = "",
185                         strings = { "" },
186                         type = Object[].class,
187                         types = { Object[].class }
188                 )
189             },
190             shortValue = 0,
191             shortValues = { 0 },
192             stooge = SHEMP,
193             stooges = { MOE, LARRY, CURLY },
194             string = "",
195             strings = { "" },
196             type = Object.class,
197             types = { Object.class }
198     )
199     public Object dummy2;
200 
201     @TestAnnotation(
202             booleanValue = false,
203             booleanValues = { false },
204             byteValue = 0,
205             byteValues = { 0 },
206             charValue = 0,
207             charValues = { 0 },
208             doubleValue = 0,
209             doubleValues = { 0 },
210             floatValue = 0,
211             floatValues = { 0 },
212             intValue = 0,
213             intValues = { 0 },
214             longValue = 0,
215             longValues = { 0 },
216             nest = @NestAnnotation(
217                     booleanValue = false,
218                     booleanValues = { false },
219                     byteValue = 0,
220                     byteValues = { 0 },
221                     charValue = 0,
222                     charValues = { 0 },
223                     doubleValue = 0,
224                     doubleValues = { 0 },
225                     floatValue = 0,
226                     floatValues = { 0 },
227                     intValue = 0,
228                     intValues = { 0 },
229                     longValue = 0,
230                     longValues = { 0 },
231                     shortValue = 0,
232                     shortValues = { 0 },
233                     stooge = CURLY,
234                     stooges = { MOE, LARRY, SHEMP },
235                     string = "",
236                     strings = { "" },
237                     type = Object.class,
238                     types = { Object.class }
239             ),
240             nests = {
241                 @NestAnnotation(
242                         booleanValue = false,
243                         booleanValues = { false },
244                         byteValue = 0,
245                         byteValues = { 0 },
246                         charValue = 0,
247                         charValues = { 0 },
248                         doubleValue = 0,
249                         doubleValues = { 0 },
250                         floatValue = 0,
251                         floatValues = { 0 },
252                         intValue = 0,
253                         intValues = { 0 },
254                         longValue = 0,
255                         longValues = { 0 },
256                         shortValue = 0,
257                         shortValues = { 0 },
258                         stooge = CURLY,
259                         stooges = { MOE, LARRY, SHEMP },
260                         string = "",
261                         strings = { "" },
262                         type = Object[].class,
263                         types = { Object[].class }
264                 ),
265                 //add a second NestAnnotation to break equality:
266                 @NestAnnotation(
267                         booleanValue = false,
268                         booleanValues = { false },
269                         byteValue = 0,
270                         byteValues = { 0 },
271                         charValue = 0,
272                         charValues = { 0 },
273                         doubleValue = 0,
274                         doubleValues = { 0 },
275                         floatValue = 0,
276                         floatValues = { 0 },
277                         intValue = 0,
278                         intValues = { 0 },
279                         longValue = 0,
280                         longValues = { 0 },
281                         shortValue = 0,
282                         shortValues = { 0 },
283                         stooge = CURLY,
284                         stooges = { MOE, LARRY, SHEMP },
285                         string = "",
286                         strings = { "" },
287                         type = Object[].class,
288                         types = { Object[].class }
289                 )
290             },
291             shortValue = 0,
292             shortValues = { 0 },
293             stooge = SHEMP,
294             stooges = { MOE, LARRY, CURLY },
295             string = "",
296             strings = { "" },
297             type = Object.class,
298             types = { Object.class }
299     )
300     public Object dummy3;
301 
302     @NestAnnotation(
303             booleanValue = false,
304             booleanValues = { false },
305             byteValue = 0,
306             byteValues = { 0 },
307             charValue = 0,
308             charValues = { 0 },
309             doubleValue = 0,
310             doubleValues = { 0 },
311             floatValue = 0,
312             floatValues = { 0 },
313             intValue = 0,
314             intValues = { 0 },
315             longValue = 0,
316             longValues = { 0 },
317             shortValue = 0,
318             shortValues = { 0 },
319             stooge = CURLY,
320             stooges = { MOE, LARRY, SHEMP },
321             string = "",
322             strings = { "" },
323             type = Object[].class,
324             types = { Object[].class }
325     )
326     public Object dummy4;
327 
328     @Target(FIELD)
329     @Retention(RUNTIME)
330     public @interface TestAnnotation {
string()331         String string();
strings()332         String[] strings();
type()333         Class<?> type();
types()334         Class<?>[] types();
byteValue()335         byte byteValue();
byteValues()336         byte[] byteValues();
shortValue()337         short shortValue();
shortValues()338         short[] shortValues();
intValue()339         int intValue();
intValues()340         int[] intValues();
charValue()341         char charValue();
charValues()342         char[] charValues();
longValue()343         long longValue();
longValues()344         long[] longValues();
floatValue()345         float floatValue();
floatValues()346         float[] floatValues();
doubleValue()347         double doubleValue();
doubleValues()348         double[] doubleValues();
booleanValue()349         boolean booleanValue();
booleanValues()350         boolean[] booleanValues();
stooge()351         Stooge stooge();
stooges()352         Stooge[] stooges();
nest()353         NestAnnotation nest();
nests()354         NestAnnotation[] nests();
355     }
356 
357     @Retention(RUNTIME)
358     public @interface NestAnnotation {
string()359         String string();
strings()360         String[] strings();
type()361         Class<?> type();
types()362         Class<?>[] types();
byteValue()363         byte byteValue();
byteValues()364         byte[] byteValues();
shortValue()365         short shortValue();
shortValues()366         short[] shortValues();
intValue()367         int intValue();
intValues()368         int[] intValues();
charValue()369         char charValue();
charValues()370         char[] charValues();
longValue()371         long longValue();
longValues()372         long[] longValues();
floatValue()373         float floatValue();
floatValues()374         float[] floatValues();
doubleValue()375         double doubleValue();
doubleValues()376         double[] doubleValues();
booleanValue()377         boolean booleanValue();
booleanValues()378         boolean[] booleanValues();
stooge()379         Stooge stooge();
stooges()380         Stooge[] stooges();
381     }
382 
383     @Retention(RetentionPolicy.RUNTIME)
384     @Target({ElementType.METHOD})
385     public @interface TestMethodAnnotation {
expected()386         Class<? extends Throwable> expected() default None.class;
387 
timeout()388         long timeout() default 0L;
389 
390         class None extends Throwable {
391 
392             private static final long serialVersionUID = 1L;
393         }
394     }
395 
396     public enum Stooge {
397         MOE, LARRY, CURLY, JOE, SHEMP
398     }
399 
400     private Field field1;
401     private Field field2;
402     private Field field3;
403     private Field field4;
404 
405     @BeforeEach
setup()406     public void setup() throws Exception {
407         field1 = getClass().getDeclaredField("dummy1");
408         field2 = getClass().getDeclaredField("dummy2");
409         field3 = getClass().getDeclaredField("dummy3");
410         field4 = getClass().getDeclaredField("dummy4");
411     }
412 
413     @Test
testEquivalence()414     public void testEquivalence() {
415         assertTrue(AnnotationUtils.equals(field1.getAnnotation(TestAnnotation.class), field2.getAnnotation(TestAnnotation.class)));
416         assertTrue(AnnotationUtils.equals(field2.getAnnotation(TestAnnotation.class), field1.getAnnotation(TestAnnotation.class)));
417     }
418 
419     @Test
testSameInstance()420     public void testSameInstance() {
421         assertTrue(AnnotationUtils.equals(field1.getAnnotation(TestAnnotation.class), field1.getAnnotation(TestAnnotation.class)));
422     }
423 
424     @Test
testNonEquivalentAnnotationsOfSameType()425     public void testNonEquivalentAnnotationsOfSameType() {
426         assertFalse(AnnotationUtils.equals(field1.getAnnotation(TestAnnotation.class), field3.getAnnotation(TestAnnotation.class)));
427         assertFalse(AnnotationUtils.equals(field3.getAnnotation(TestAnnotation.class), field1.getAnnotation(TestAnnotation.class)));
428     }
429 
430     @Test
testAnnotationsOfDifferingTypes()431     public void testAnnotationsOfDifferingTypes() {
432         assertFalse(AnnotationUtils.equals(field1.getAnnotation(TestAnnotation.class), field4.getAnnotation(NestAnnotation.class)));
433         assertFalse(AnnotationUtils.equals(field4.getAnnotation(NestAnnotation.class), field1.getAnnotation(TestAnnotation.class)));
434     }
435 
436     @Test
testOneArgNull()437     public void testOneArgNull() {
438         assertFalse(AnnotationUtils.equals(field1.getAnnotation(TestAnnotation.class), null));
439         assertFalse(AnnotationUtils.equals(null, field1.getAnnotation(TestAnnotation.class)));
440     }
441 
442     @Test
testBothArgsNull()443     public void testBothArgsNull() {
444         assertTrue(AnnotationUtils.equals(null, null));
445     }
446 
447     @Test
testIsValidAnnotationMemberType()448     public void testIsValidAnnotationMemberType() {
449         for (final Class<?> type : new Class[] { byte.class, short.class, int.class, char.class,
450                 long.class, float.class, double.class, boolean.class, String.class, Class.class,
451                 NestAnnotation.class, TestAnnotation.class, Stooge.class, ElementType.class }) {
452             assertTrue(AnnotationUtils.isValidAnnotationMemberType(type));
453             assertTrue(AnnotationUtils.isValidAnnotationMemberType(Array.newInstance(type, 0)
454                     .getClass()));
455         }
456         for (final Class<?> type : new Class[] { Object.class, Map.class, Collection.class }) {
457             assertFalse(AnnotationUtils.isValidAnnotationMemberType(type));
458             assertFalse(AnnotationUtils.isValidAnnotationMemberType(Array.newInstance(type, 0)
459                     .getClass()));
460         }
461     }
462 
463     @Test
testGeneratedAnnotationEquivalentToRealAnnotation()464     public void testGeneratedAnnotationEquivalentToRealAnnotation() {
465         assertTimeoutPreemptively(Duration.ofSeconds(666L), () -> {
466             final Test real = getClass().getDeclaredMethod(
467                     "testGeneratedAnnotationEquivalentToRealAnnotation").getAnnotation(Test.class);
468 
469             final InvocationHandler generatedTestInvocationHandler = (proxy, method, args) -> {
470                 if ("equals".equals(method.getName()) && method.getParameterTypes().length == 1) {
471                     return Boolean.valueOf(proxy == args[0]);
472                 }
473                 if ("hashCode".equals(method.getName()) && method.getParameterTypes().length == 0) {
474                     return Integer.valueOf(System.identityHashCode(proxy));
475                 }
476                 if ("toString".equals(method.getName()) && method.getParameterTypes().length == 0) {
477                     return "Test proxy";
478                 }
479                 return method.invoke(real, args);
480             };
481 
482             final Test generated = (Test) Proxy.newProxyInstance(Thread.currentThread()
483                             .getContextClassLoader(), new Class[]{Test.class},
484                     generatedTestInvocationHandler);
485             assertEquals(real, generated);
486             assertNotEquals(generated, real);
487             assertTrue(AnnotationUtils.equals(generated, real));
488             assertTrue(AnnotationUtils.equals(real, generated));
489 
490             final Test generated2 = (Test) Proxy.newProxyInstance(Thread.currentThread()
491                             .getContextClassLoader(), new Class[]{Test.class},
492                     generatedTestInvocationHandler);
493             assertNotEquals(generated, generated2);
494             assertNotEquals(generated2, generated);
495             assertTrue(AnnotationUtils.equals(generated, generated2));
496             assertTrue(AnnotationUtils.equals(generated2, generated));
497         });
498     }
499 
500     @Test
testHashCode()501     public void testHashCode() {
502         assertTimeoutPreemptively(Duration.ofSeconds(666L), () -> {
503             final Test test = getClass().getDeclaredMethod("testHashCode").getAnnotation(Test.class);
504             assertEquals(test.hashCode(), AnnotationUtils.hashCode(test));
505             final TestAnnotation testAnnotation1 = field1.getAnnotation(TestAnnotation.class);
506             assertEquals(testAnnotation1.hashCode(), AnnotationUtils.hashCode(testAnnotation1));
507             final TestAnnotation testAnnotation3 = field3.getAnnotation(TestAnnotation.class);
508             assertEquals(testAnnotation3.hashCode(), AnnotationUtils.hashCode(testAnnotation3));
509         });
510     }
511 
512     @Test
513     @TestMethodAnnotation(timeout = 666000)
testToString()514     public void testToString() {
515         assertTimeoutPreemptively(Duration.ofSeconds(666L), () -> {
516             final TestMethodAnnotation testAnnotation =
517                     getClass().getDeclaredMethod("testToString").getAnnotation(TestMethodAnnotation.class);
518 
519             final String annotationString = AnnotationUtils.toString(testAnnotation);
520             assertTrue(annotationString.startsWith("@org.apache.commons.lang3.AnnotationUtilsTest$TestMethodAnnotation("));
521             assertTrue(annotationString.endsWith(")"));
522             assertTrue(annotationString.contains("expected=class org.apache.commons.lang3.AnnotationUtilsTest$TestMethodAnnotation$None"));
523             assertTrue(annotationString.contains("timeout=666000"));
524             assertTrue(annotationString.contains(", "));
525         });
526     }
527 
528 }
529