Skip to content

Commit

Permalink
Improved test coverage for AnnotationUtils
Browse files Browse the repository at this point in the history
  • Loading branch information
lutovich authored and sbrannen committed May 6, 2016
1 parent 7626b32 commit 669bfeb
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 12 deletions.
Expand Up @@ -71,7 +71,7 @@ public static boolean isAnnotated(AnnotatedElement element, Class<? extends Anno
* the supplied {@code element}. * the supplied {@code element}.
*/ */
public static <A extends Annotation> Optional<A> findAnnotation(AnnotatedElement element, Class<A> annotationType) { public static <A extends Annotation> Optional<A> findAnnotation(AnnotatedElement element, Class<A> annotationType) {
return findAnnotation(element, annotationType, new HashSet<Annotation>()); return findAnnotation(element, annotationType, new HashSet<>());
} }


@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Expand Down Expand Up @@ -99,15 +99,10 @@ private static <A extends Annotation> Optional<A> findAnnotation(AnnotatedElemen
} }


// Meta-present on directly present annotations? // Meta-present on directly present annotations?
for (Annotation candidateAnnotation : element.getDeclaredAnnotations()) { Optional<A> directMetaAnnotation = findMetaAnnotation(annotationType, element.getDeclaredAnnotations(), key,
if (!isInJavaLangAnnotationPackage(candidateAnnotation) && visited.add(candidateAnnotation)) { visited);
Optional<A> metaAnnotation = findAnnotation(candidateAnnotation.annotationType(), annotationType, if (directMetaAnnotation.isPresent()) {
visited); return directMetaAnnotation;
if (metaAnnotation.isPresent()) {
annotationCache.put(key, metaAnnotation.get());
return metaAnnotation;
}
}
} }


// Indirectly present? // Indirectly present?
Expand All @@ -118,7 +113,17 @@ private static <A extends Annotation> Optional<A> findAnnotation(AnnotatedElemen
} }


// Meta-present on indirectly present annotations? // Meta-present on indirectly present annotations?
for (Annotation candidateAnnotation : element.getAnnotations()) { Optional<A> indirectMetaAnnotation = findMetaAnnotation(annotationType, element.getAnnotations(), key, visited);
if (indirectMetaAnnotation.isPresent()) {
return indirectMetaAnnotation;
}

return Optional.empty();
}

private static <A extends Annotation> Optional<A> findMetaAnnotation(Class<A> annotationType,
Annotation[] candidates, AnnotationCacheKey key, Set<Annotation> visited) {
for (Annotation candidateAnnotation : candidates) {
if (!isInJavaLangAnnotationPackage(candidateAnnotation) && visited.add(candidateAnnotation)) { if (!isInJavaLangAnnotationPackage(candidateAnnotation) && visited.add(candidateAnnotation)) {
Optional<A> metaAnnotation = findAnnotation(candidateAnnotation.annotationType(), annotationType, Optional<A> metaAnnotation = findAnnotation(candidateAnnotation.annotationType(), annotationType,
visited); visited);
Expand All @@ -128,7 +133,6 @@ private static <A extends Annotation> Optional<A> findAnnotation(AnnotatedElemen
} }
} }
} }

return Optional.empty(); return Optional.empty();
} }


Expand Down
Expand Up @@ -12,19 +12,27 @@


import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.stream.Collectors.toList; 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.assertEquals;
import static org.junit.gen5.api.Assertions.assertFalse; import static org.junit.gen5.api.Assertions.assertFalse;
import static org.junit.gen5.api.Assertions.assertNotNull; 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.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.findAnnotation;
import static org.junit.gen5.commons.util.AnnotationUtils.findRepeatableAnnotations; 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.ElementType;
import java.lang.annotation.Inherited; import java.lang.annotation.Inherited;
import java.lang.annotation.Repeatable; import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional; import java.util.Optional;


import org.junit.gen5.api.Test; import org.junit.gen5.api.Test;
Expand All @@ -36,6 +44,11 @@
*/ */
public final class AnnotationUtilsTests { public final class AnnotationUtilsTests {


@Test
public void findAnnotationForNullAnnotatedElement() {
assertThat(findAnnotation(null, Annotation1.class)).isEmpty();
}

@Test @Test
public void findAnnotationOnClassWithoutAnnotation() { public void findAnnotationOnClassWithoutAnnotation() {
Optional<Annotation2> optionalAnnotation = findAnnotation(Annotation1Class.class, Annotation2.class); Optional<Annotation2> optionalAnnotation = findAnnotation(Annotation1Class.class, Annotation2.class);
Expand Down Expand Up @@ -65,6 +78,17 @@ public void findAnnotationMetaPresentOnClass() {
assertTrue(optionalAnnotation.isPresent()); assertTrue(optionalAnnotation.isPresent());
} }


/**
* <b>Note:</b> 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 @Test
public void findAnnotationDirectlyPresentOnMethod() throws Exception { public void findAnnotationDirectlyPresentOnMethod() throws Exception {
Optional<Annotation1> optionalAnnotation = findAnnotation(Annotation2Class.class.getDeclaredMethod("method"), Optional<Annotation1> optionalAnnotation = findAnnotation(Annotation2Class.class.getDeclaredMethod("method"),
Expand All @@ -81,6 +105,41 @@ public void findAnnotationMetaPresentOnMethod() throws Exception {
assertTrue(optionalAnnotation.isPresent()); 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 @Test
public void findRepeatableAnnotationsWithSingleTag() throws Exception { public void findRepeatableAnnotationsWithSingleTag() throws Exception {
assertTagsFound(SingleTaggedClass.class, "a"); assertTagsFound(SingleTaggedClass.class, "a");
Expand Down Expand Up @@ -178,6 +237,58 @@ private void assertExtensionsFound(Class<?> clazz, String... tags) throws Except
() -> "Extensions found for class " + clazz.getName()); () -> "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<Method> 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<Method> 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<Method> 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<Method> methods = findAnnotatedMethods(ClassWithAnnotatedMethods.class, Annotation3.class, HierarchyUp);

assertThat(methods).containsExactly(ClassWithAnnotatedMethods.class.getMethod("interfaceMethod"));
}

@Target({ ElementType.TYPE, ElementType.METHOD }) @Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@interface Annotation1 { @interface Annotation1 {
Expand All @@ -188,6 +299,11 @@ private void assertExtensionsFound(Class<?> clazz, String... tags) throws Except
@interface Annotation2 { @interface Annotation2 {
} }


@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@interface Annotation3 {
}

@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Inherited @Inherited
Expand All @@ -200,6 +316,13 @@ private void assertExtensionsFound(Class<?> clazz, String... tags) throws Except
@interface ComposedAnnotation { @interface ComposedAnnotation {
} }


@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Annotation1
@Inherited
@interface InheritedComposedAnnotation {
}

@Target({ ElementType.TYPE, ElementType.METHOD }) @Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@interface Tags { @interface Tags {
Expand Down Expand Up @@ -289,6 +412,17 @@ void method() {
} }
} }


@InheritedComposedAnnotation
static class InheritedComposedAnnotationClass {

@InheritedComposedAnnotation
public void method() {
}
}

static class InheritedComposedAnnotationSubClass extends InheritedComposedAnnotationClass {
}

@Tag("a") @Tag("a")
static class SingleTaggedClass { static class SingleTaggedClass {
} }
Expand Down Expand Up @@ -382,4 +516,34 @@ static class SubMultiComposedExtensionClass extends MultiComposedExtensionClass
static class ContainerPlusSubMultiComposedExtensionClass 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() {
}
}
} }

0 comments on commit 669bfeb

Please sign in to comment.