From 669bfebe136647efe3fe4339cd708033b3d5ac45 Mon Sep 17 00:00:00 2001 From: lutovich Date: Tue, 3 May 2016 01:17:53 +0200 Subject: [PATCH] Improved test coverage for AnnotationUtils --- .../gen5/commons/util/AnnotationUtils.java | 28 +-- .../commons/util/AnnotationUtilsTests.java | 164 ++++++++++++++++++ 2 files changed, 180 insertions(+), 12 deletions(-) diff --git a/junit-commons/src/main/java/org/junit/gen5/commons/util/AnnotationUtils.java b/junit-commons/src/main/java/org/junit/gen5/commons/util/AnnotationUtils.java index 095d93b48aa..3251df0770f 100644 --- a/junit-commons/src/main/java/org/junit/gen5/commons/util/AnnotationUtils.java +++ b/junit-commons/src/main/java/org/junit/gen5/commons/util/AnnotationUtils.java @@ -71,7 +71,7 @@ public static boolean isAnnotated(AnnotatedElement element, Class Optional findAnnotation(AnnotatedElement element, Class annotationType) { - return findAnnotation(element, annotationType, new HashSet()); + return findAnnotation(element, annotationType, new HashSet<>()); } @SuppressWarnings("unchecked") @@ -99,15 +99,10 @@ private static Optional findAnnotation(AnnotatedElemen } // Meta-present on directly present annotations? - for (Annotation candidateAnnotation : element.getDeclaredAnnotations()) { - if (!isInJavaLangAnnotationPackage(candidateAnnotation) && visited.add(candidateAnnotation)) { - Optional metaAnnotation = findAnnotation(candidateAnnotation.annotationType(), annotationType, - visited); - if (metaAnnotation.isPresent()) { - annotationCache.put(key, metaAnnotation.get()); - return metaAnnotation; - } - } + Optional directMetaAnnotation = findMetaAnnotation(annotationType, element.getDeclaredAnnotations(), key, + visited); + if (directMetaAnnotation.isPresent()) { + return directMetaAnnotation; } // Indirectly present? @@ -118,7 +113,17 @@ private static Optional findAnnotation(AnnotatedElemen } // Meta-present on indirectly present annotations? - for (Annotation candidateAnnotation : element.getAnnotations()) { + Optional indirectMetaAnnotation = findMetaAnnotation(annotationType, element.getAnnotations(), key, visited); + if (indirectMetaAnnotation.isPresent()) { + return indirectMetaAnnotation; + } + + return Optional.empty(); + } + + private static Optional findMetaAnnotation(Class annotationType, + Annotation[] candidates, AnnotationCacheKey key, Set visited) { + for (Annotation candidateAnnotation : candidates) { if (!isInJavaLangAnnotationPackage(candidateAnnotation) && visited.add(candidateAnnotation)) { Optional metaAnnotation = findAnnotation(candidateAnnotation.annotationType(), annotationType, visited); @@ -128,7 +133,6 @@ private static Optional findAnnotation(AnnotatedElemen } } } - return Optional.empty(); } diff --git a/junit-tests/src/test/java/org/junit/gen5/commons/util/AnnotationUtilsTests.java b/junit-tests/src/test/java/org/junit/gen5/commons/util/AnnotationUtilsTests.java index 9d3ccfe1a2f..118f39dc56b 100644 --- a/junit-tests/src/test/java/org/junit/gen5/commons/util/AnnotationUtilsTests.java +++ b/junit-tests/src/test/java/org/junit/gen5/commons/util/AnnotationUtilsTests.java @@ -12,12 +12,18 @@ import static java.util.Arrays.asList; import static java.util.stream.Collectors.toList; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.gen5.api.Assertions.assertEquals; import static org.junit.gen5.api.Assertions.assertFalse; import static org.junit.gen5.api.Assertions.assertNotNull; +import static org.junit.gen5.api.Assertions.assertThrows; import static org.junit.gen5.api.Assertions.assertTrue; +import static org.junit.gen5.commons.util.AnnotationUtils.findAnnotatedMethods; import static org.junit.gen5.commons.util.AnnotationUtils.findAnnotation; import static org.junit.gen5.commons.util.AnnotationUtils.findRepeatableAnnotations; +import static org.junit.gen5.commons.util.AnnotationUtils.isAnnotated; +import static org.junit.gen5.commons.util.ReflectionUtils.MethodSortOrder.HierarchyDown; +import static org.junit.gen5.commons.util.ReflectionUtils.MethodSortOrder.HierarchyUp; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; @@ -25,6 +31,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.lang.reflect.Method; +import java.util.List; import java.util.Optional; import org.junit.gen5.api.Test; @@ -36,6 +44,11 @@ */ public final class AnnotationUtilsTests { + @Test + public void findAnnotationForNullAnnotatedElement() { + assertThat(findAnnotation(null, Annotation1.class)).isEmpty(); + } + @Test public void findAnnotationOnClassWithoutAnnotation() { Optional optionalAnnotation = findAnnotation(Annotation1Class.class, Annotation2.class); @@ -65,6 +78,17 @@ public void findAnnotationMetaPresentOnClass() { assertTrue(optionalAnnotation.isPresent()); } + /** + * Note: there is no findAnnotationIndirectlyMetaPresentOnMethod counterpart because {@link Inherited} + * annotation has no effect if the annotated type is used to annotate anything other than a class. + * + * @see Inherited + */ + @Test + public void findAnnotationIndirectlyMetaPresentOnClass() { + assertThat(findAnnotation(InheritedComposedAnnotationSubClass.class, Annotation1.class)).isPresent(); + } + @Test public void findAnnotationDirectlyPresentOnMethod() throws Exception { Optional optionalAnnotation = findAnnotation(Annotation2Class.class.getDeclaredMethod("method"), @@ -81,6 +105,41 @@ public void findAnnotationMetaPresentOnMethod() throws Exception { assertTrue(optionalAnnotation.isPresent()); } + @Test + public void isAnnotatedWhenClassWithoutAnnotation() { + assertFalse(isAnnotated(Annotation1Class.class, Annotation2.class)); + } + + @Test + public void isAnnotatedWhenIndirectlyPresentOnClass() { + assertTrue(isAnnotated(SubInheritedAnnotationClass.class, InheritedAnnotation.class)); + } + + @Test + public void isAnnotatedWhenDirectlyPresentOnClass() { + assertTrue(isAnnotated(Annotation1Class.class, Annotation1.class)); + } + + @Test + public void isAnnotatedWhenMetaPresentOnClass() { + assertTrue(isAnnotated(ComposedAnnotationClass.class, Annotation1.class)); + } + + @Test + public void isAnnotatedWhenDirectlyPresentOnMethod() throws Exception { + assertTrue(isAnnotated(Annotation2Class.class.getDeclaredMethod("method"), Annotation1.class)); + } + + @Test + public void isAnnotatedWhenMetaPresentOnMethod() throws Exception { + assertTrue(isAnnotated(ComposedAnnotationClass.class.getDeclaredMethod("method"), Annotation1.class)); + } + + @Test + public void findRepeatableAnnotationsForNullAnnotatedElement() { + assertThat(findRepeatableAnnotations(null, Tag.class)).isEmpty(); + } + @Test public void findRepeatableAnnotationsWithSingleTag() throws Exception { assertTagsFound(SingleTaggedClass.class, "a"); @@ -178,6 +237,58 @@ private void assertExtensionsFound(Class clazz, String... tags) throws Except () -> "Extensions found for class " + clazz.getName()); } + @Test + public void findAnnotatedMethodsForNullClass() { + assertThrows(PreconditionViolationException.class, + () -> findAnnotatedMethods(null, Annotation1.class, HierarchyDown)); + } + + @Test + public void findAnnotatedMethodsForNullAnnotationType() { + assertThrows(PreconditionViolationException.class, + () -> findAnnotatedMethods(ClassWithAnnotatedMethods.class, null, HierarchyDown)); + } + + @Test + public void findAnnotatedMethodsForUnusedAnnotation() { + assertThat(findAnnotatedMethods(ClassWithAnnotatedMethods.class, Fast.class, HierarchyDown)).isEmpty(); + } + + @Test + public void findAnnotatedMethodsForAnnotationUsedInClassOnly() throws Exception { + List methods = findAnnotatedMethods(ClassWithAnnotatedMethods.class, Annotation2.class, HierarchyDown); + + assertThat(methods).containsExactly(ClassWithAnnotatedMethods.class.getMethod("method2"), + ClassWithAnnotatedMethods.class.getMethod("method3")); + } + + @Test + public void findAnnotatedMethodsForAnnotationUsedInClassAndSuperClassHierarchyUp() throws Exception { + List methods = findAnnotatedMethods(ClassWithAnnotatedMethods.class, Annotation1.class, HierarchyUp); + + assertEquals(3, methods.size()); + assertThat(methods.subList(0, 2)).containsOnly(ClassWithAnnotatedMethods.class.getMethod("method1"), + ClassWithAnnotatedMethods.class.getMethod("method3")); + assertEquals(ClassWithAnnotatedMethods.class.getMethod("superMethod"), methods.get(2)); + } + + @Test + public void findAnnotatedMethodsForAnnotationUsedInClassAndSuperClassHierarchyDown() throws Exception { + List methods = findAnnotatedMethods(ClassWithAnnotatedMethods.class, Annotation1.class, HierarchyDown); + + assertEquals(3, methods.size()); + assertEquals(ClassWithAnnotatedMethods.class.getMethod("superMethod"), methods.get(0)); + assertThat(methods.subList(1, 3)).containsOnly(ClassWithAnnotatedMethods.class.getMethod("method1"), + ClassWithAnnotatedMethods.class.getMethod("method3")); + } + + @Test + public void findAnnotatedMethodsForAnnotationUsedInInterface() throws Exception { + List methods = findAnnotatedMethods(ClassWithAnnotatedMethods.class, Annotation3.class, HierarchyUp); + + assertThat(methods).containsExactly(ClassWithAnnotatedMethods.class.getMethod("interfaceMethod")); + } + @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @interface Annotation1 { @@ -188,6 +299,11 @@ private void assertExtensionsFound(Class clazz, String... tags) throws Except @interface Annotation2 { } + @Target({ ElementType.TYPE, ElementType.METHOD }) + @Retention(RetentionPolicy.RUNTIME) + @interface Annotation3 { + } + @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Inherited @@ -200,6 +316,13 @@ private void assertExtensionsFound(Class clazz, String... tags) throws Except @interface ComposedAnnotation { } + @Target({ ElementType.TYPE, ElementType.METHOD }) + @Retention(RetentionPolicy.RUNTIME) + @Annotation1 + @Inherited + @interface InheritedComposedAnnotation { + } + @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @interface Tags { @@ -289,6 +412,17 @@ void method() { } } + @InheritedComposedAnnotation + static class InheritedComposedAnnotationClass { + + @InheritedComposedAnnotation + public void method() { + } + } + + static class InheritedComposedAnnotationSubClass extends InheritedComposedAnnotationClass { + } + @Tag("a") static class SingleTaggedClass { } @@ -382,4 +516,34 @@ static class SubMultiComposedExtensionClass extends MultiComposedExtensionClass static class ContainerPlusSubMultiComposedExtensionClass extends MultiComposedExtensionClass { } + interface InterfaceWithAnnotatedMethod { + + @Annotation3 + default void interfaceMethod() { + } + } + + static class SuperClassWithAnnotatedMethod { + + @Annotation1 + public void superMethod() { + } + } + + static class ClassWithAnnotatedMethods extends SuperClassWithAnnotatedMethod + implements InterfaceWithAnnotatedMethod { + + @Annotation1 + public void method1() { + } + + @Annotation2 + public void method2() { + } + + @Annotation1 + @Annotation2 + public void method3() { + } + } }