Skip to content

Commit

Permalink
make metaAnnotated predicates also match direct annotations
Browse files Browse the repository at this point in the history
So far the most common use case for checking rules for meta-annotations seems to be annotation composition, where users want to test that certain classes/members are either directly annotated with some annotation, or with some annotation that is meta-annotated with the respective annotation. Thus it makes sense to cover this use case directly with the `metaAnnotated(..)` syntax. For example

```
@interface A{}

@A
@interface B{}

class Foo {
    @A Object directlyAnnotated;
    @b Object indirectlyAnnotated;
}
```

Previously only `indirectlyAnnotated` would have counted as meta-annotated with `@A`. Now we count both those methods as meta-annotated with `@A`. The old behavior of really being annotated only through the hierarchy, but not directly can still be expressed as `metaAnnotatedWith(A.class).and(not(annotatedWith(A.class)))`, which seems to be good enough still to cover the old behavior. Besides that the only use case I could imagine is to enforce the use of a custom composite annotation, which could also be achieved by simply forbidding the original annotation altogether.

Signed-off-by: Peter Gafert <[email protected]>
  • Loading branch information
codecholeric committed Feb 21, 2021
1 parent d37e2d0 commit 5d68c45
Show file tree
Hide file tree
Showing 8 changed files with 87 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,12 @@ private static boolean isMetaAnnotatedWith(
return false;
}

if (predicate.apply(annotation)) {
return true;
}

for (JavaAnnotation<?> metaAnnotation : annotation.getRawType().getAnnotations()) {
if (predicate.apply(metaAnnotation) || isMetaAnnotatedWith(metaAnnotation, predicate, visitedAnnotations)) {
if (isMetaAnnotatedWith(metaAnnotation, predicate, visitedAnnotations)) {
return true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,13 @@ public void isMetaAnnotatedWith_type_on_resolved_target() {

assertThat(call.getTarget().isMetaAnnotatedWith(QueriedAnnotation.class))
.as("target is meta-annotated with @" + QueriedAnnotation.class.getSimpleName())
.isFalse();
.isTrue();
assertThat(call.getTarget().isMetaAnnotatedWith(Retention.class))
.as("target is meta-annotated with @" + Retention.class.getSimpleName())
.isTrue();
assertThat(call.getTarget().isMetaAnnotatedWith(Deprecated.class))
.as("target is meta-annotated with @" + Deprecated.class.getSimpleName())
.isFalse();
}

@Test
Expand All @@ -88,10 +91,13 @@ public void isMetaAnnotatedWith_typeName_on_resolved_target() {

assertThat(call.getTarget().isMetaAnnotatedWith(QueriedAnnotation.class.getName()))
.as("target is meta-annotated with @" + QueriedAnnotation.class.getSimpleName())
.isFalse();
.isTrue();
assertThat(call.getTarget().isMetaAnnotatedWith(Retention.class.getName()))
.as("target is meta-annotated with @" + Retention.class.getSimpleName())
.isTrue();
assertThat(call.getTarget().isMetaAnnotatedWith(Deprecated.class))
.as("target is meta-annotated with @" + Deprecated.class.getSimpleName())
.isFalse();
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,19 +320,23 @@ public void isMetaAnnotatedWith_type() {
JavaClass clazz = importClassesWithContext(Parent.class, SomeAnnotation.class).get(Parent.class);

assertThat(clazz.isMetaAnnotatedWith(SomeAnnotation.class))
.as("Parent is meta-annotated with @" + SomeAnnotation.class.getSimpleName()).isFalse();
.as("Parent is meta-annotated with @" + SomeAnnotation.class.getSimpleName()).isTrue();
assertThat(clazz.isMetaAnnotatedWith(Retention.class))
.as("Parent is meta-annotated with @" + Retention.class.getSimpleName()).isTrue();
assertThat(clazz.isMetaAnnotatedWith(Deprecated.class))
.as("Parent is meta-annotated with @" + Deprecated.class.getSimpleName()).isFalse();
}

@Test
public void isMetaAnnotatedWith_typeName() {
JavaClass clazz = importClassesWithContext(Parent.class, SomeAnnotation.class).get(Parent.class);

assertThat(clazz.isMetaAnnotatedWith(SomeAnnotation.class.getName()))
.as("Parent is meta-annotated with @" + SomeAnnotation.class.getSimpleName()).isFalse();
.as("Parent is meta-annotated with @" + SomeAnnotation.class.getSimpleName()).isTrue();
assertThat(clazz.isMetaAnnotatedWith(Retention.class.getName()))
.as("Parent is meta-annotated with @" + Retention.class.getSimpleName()).isTrue();
assertThat(clazz.isMetaAnnotatedWith(Deprecated.class.getName()))
.as("Parent is meta-annotated with @" + Deprecated.class.getSimpleName()).isFalse();
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,20 @@
import static com.tngtech.archunit.core.domain.TestUtils.importClassWithContext;
import static com.tngtech.archunit.core.domain.TestUtils.importClassesWithContext;
import static com.tngtech.archunit.testutil.Assertions.assertThat;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

public class JavaMemberTest {
@Test
public void isAnnotatedWith_type() {
assertThat(importField(SomeClass.class, "someField").isAnnotatedWith(Deprecated.class))
assertThat(importField(SomeClass.class, "someField").isAnnotatedWith(SomeAnnotation.class))
.as("field is annotated with @Deprecated").isTrue();
assertThat(importField(SomeClass.class, "someField").isAnnotatedWith(Retention.class))
.as("field is annotated with @Retention").isFalse();
}

@Test
public void isAnnotatedWith_typeName() {
assertThat(importField(SomeClass.class, "someField").isAnnotatedWith(Deprecated.class.getName()))
assertThat(importField(SomeClass.class, "someField").isAnnotatedWith(SomeAnnotation.class.getName()))
.as("field is annotated with @Deprecated").isTrue();
assertThat(importField(SomeClass.class, "someField").isAnnotatedWith(Retention.class.getName()))
.as("field is annotated with @Retention").isFalse();
Expand All @@ -42,20 +43,24 @@ public void isAnnotatedWith_predicate() {
public void isMetaAnnotatedWith_type() {
JavaClass clazz = importClassesWithContext(SomeClass.class, Deprecated.class).get(SomeClass.class);

assertThat(clazz.getField("someField").isMetaAnnotatedWith(Deprecated.class))
.as("field is meta-annotated with @Deprecated").isFalse();
assertThat(clazz.getField("someField").isMetaAnnotatedWith(SomeAnnotation.class))
.as("field is meta-annotated with @SomeAnnotation").isTrue();
assertThat(clazz.getField("someField").isMetaAnnotatedWith(Retention.class))
.as("field is meta-annotated with @Retention").isTrue();
assertThat(clazz.getField("someField").isMetaAnnotatedWith(Deprecated.class))
.as("field is meta-annotated with @Deprecated").isFalse();
}

@Test
public void isMetaAnnotatedWith_typeName() {
JavaClass clazz = importClassesWithContext(SomeClass.class, Deprecated.class).get(SomeClass.class);

assertThat(clazz.getField("someField").isMetaAnnotatedWith(Deprecated.class.getName()))
.as("field is meta-annotated with @Deprecated").isFalse();
assertThat(clazz.getField("someField").isMetaAnnotatedWith(SomeAnnotation.class.getName()))
.as("field is meta-annotated with @SomeAnnotation").isTrue();
assertThat(clazz.getField("someField").isMetaAnnotatedWith(Retention.class.getName()))
.as("field is meta-annotated with @Retention").isTrue();
assertThat(clazz.getField("someField").isMetaAnnotatedWith(Deprecated.class.getName()))
.as("field is meta-annotated with @Deprecated").isFalse();
}

@Test
Expand Down Expand Up @@ -93,8 +98,13 @@ private static JavaField importField(Class<?> owner, String name) {
return importClassWithContext(owner).getField(name);
}

@Retention(RUNTIME)
private @interface SomeAnnotation {
}

@SuppressWarnings("unused")
private static class SomeClass {
@Deprecated
@SomeAnnotation
private String someField;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -340,31 +340,31 @@ public void areMetaAnnotatedWith_type() {
List<JavaClass> classes = filterResultOf(classes().that().areMetaAnnotatedWith(SomeAnnotation.class))
.on(MetaAnnotatedClass.class, AnnotatedClass.class, SimpleClass.class, MetaAnnotatedAnnotation.class);

assertThatType(getOnlyElement(classes)).matches(MetaAnnotatedClass.class);
assertThatTypes(classes).matchInAnyOrder(MetaAnnotatedClass.class, AnnotatedClass.class, MetaAnnotatedAnnotation.class);
}

@Test
public void areNotMetaAnnotatedWith_type() {
List<JavaClass> classes = filterResultOf(classes().that().areNotMetaAnnotatedWith(SomeAnnotation.class))
.on(MetaAnnotatedClass.class, AnnotatedClass.class, SimpleClass.class, MetaAnnotatedAnnotation.class);

assertThatTypes(classes).matchInAnyOrder(AnnotatedClass.class, SimpleClass.class, MetaAnnotatedAnnotation.class);
assertThatTypes(classes).matchInAnyOrder(SimpleClass.class);
}

@Test
public void areMetaAnnotatedWith_typeName() {
List<JavaClass> classes = filterResultOf(classes().that().areMetaAnnotatedWith(SomeAnnotation.class.getName()))
.on(MetaAnnotatedClass.class, AnnotatedClass.class, SimpleClass.class, MetaAnnotatedAnnotation.class);

assertThatType(getOnlyElement(classes)).matches(MetaAnnotatedClass.class);
assertThatTypes(classes).matchInAnyOrder(MetaAnnotatedClass.class, AnnotatedClass.class, MetaAnnotatedAnnotation.class);
}

@Test
public void areNotMetaAnnotatedWith_typeName() {
List<JavaClass> classes = filterResultOf(classes().that().areNotMetaAnnotatedWith(SomeAnnotation.class.getName()))
.on(MetaAnnotatedClass.class, AnnotatedClass.class, SimpleClass.class, MetaAnnotatedAnnotation.class);

assertThatTypes(classes).matchInAnyOrder(AnnotatedClass.class, SimpleClass.class, MetaAnnotatedAnnotation.class);
assertThatTypes(classes).matchInAnyOrder(SimpleClass.class);
}

@Test
Expand All @@ -373,7 +373,7 @@ public void areMetaAnnotatedWith_predicate() {
List<JavaClass> classes = filterResultOf(classes().that().areMetaAnnotatedWith(hasNamePredicate))
.on(MetaAnnotatedClass.class, AnnotatedClass.class, SimpleClass.class, MetaAnnotatedAnnotation.class);

assertThatType(getOnlyElement(classes)).matches(MetaAnnotatedClass.class);
assertThatTypes(classes).matchInAnyOrder(MetaAnnotatedClass.class, AnnotatedClass.class, MetaAnnotatedAnnotation.class);
}

@Test
Expand All @@ -382,7 +382,7 @@ public void areNotMetaAnnotatedWith_predicate() {
List<JavaClass> classes = filterResultOf(classes().that().areNotMetaAnnotatedWith(hasNamePredicate))
.on(MetaAnnotatedClass.class, AnnotatedClass.class, SimpleClass.class, MetaAnnotatedAnnotation.class);

assertThatTypes(classes).matchInAnyOrder(AnnotatedClass.class, SimpleClass.class, MetaAnnotatedAnnotation.class);
assertThatTypes(classes).matchInAnyOrder(SimpleClass.class);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,31 +336,31 @@ public void areMetaAnnotatedWith_type() {
List<JavaMember> members = filterResultOf(members().that().areDeclaredInClassesThat().areMetaAnnotatedWith(SomeAnnotation.class))
.on(MetaAnnotatedClass.class, AnnotatedClass.class, SimpleClass.class, MetaAnnotatedAnnotation.class);

assertThatMembers(members).matchInAnyOrderMembersOf(MetaAnnotatedClass.class);
assertThatMembers(members).matchInAnyOrderMembersOf(MetaAnnotatedClass.class, AnnotatedClass.class);
}

@Test
public void areNotMetaAnnotatedWith_type() {
List<JavaMember> members = filterResultOf(members().that().areDeclaredInClassesThat().areNotMetaAnnotatedWith(SomeAnnotation.class))
.on(MetaAnnotatedClass.class, AnnotatedClass.class, SimpleClass.class, MetaAnnotatedAnnotation.class);

assertThatMembers(members).matchInAnyOrderMembersOf(AnnotatedClass.class, SimpleClass.class, MetaAnnotatedAnnotation.class);
assertThatMembers(members).matchInAnyOrderMembersOf(SimpleClass.class, MetaAnnotatedAnnotation.class);
}

@Test
public void areMetaAnnotatedWith_typeName() {
List<JavaMember> members = filterResultOf(members().that().areDeclaredInClassesThat().areMetaAnnotatedWith(SomeAnnotation.class.getName()))
.on(MetaAnnotatedClass.class, AnnotatedClass.class, SimpleClass.class, MetaAnnotatedAnnotation.class);

assertThatMembers(members).matchInAnyOrderMembersOf(MetaAnnotatedClass.class);
assertThatMembers(members).matchInAnyOrderMembersOf(MetaAnnotatedClass.class, AnnotatedClass.class);
}

@Test
public void areNotMetaAnnotatedWith_typeName() {
List<JavaMember> members = filterResultOf(members().that().areDeclaredInClassesThat().areNotMetaAnnotatedWith(SomeAnnotation.class.getName()))
.on(MetaAnnotatedClass.class, AnnotatedClass.class, SimpleClass.class, MetaAnnotatedAnnotation.class);

assertThatMembers(members).matchInAnyOrderMembersOf(AnnotatedClass.class, SimpleClass.class, MetaAnnotatedAnnotation.class);
assertThatMembers(members).matchInAnyOrderMembersOf(SimpleClass.class, MetaAnnotatedAnnotation.class);
}

@Test
Expand All @@ -369,7 +369,7 @@ public void areMetaAnnotatedWith_predicate() {
List<JavaMember> members = filterResultOf(members().that().areDeclaredInClassesThat().areMetaAnnotatedWith(hasNamePredicate))
.on(MetaAnnotatedClass.class, AnnotatedClass.class, SimpleClass.class, MetaAnnotatedAnnotation.class);

assertThatMembers(members).matchInAnyOrderMembersOf(MetaAnnotatedClass.class);
assertThatMembers(members).matchInAnyOrderMembersOf(MetaAnnotatedClass.class, AnnotatedClass.class);
}

@Test
Expand All @@ -378,7 +378,7 @@ public void areNotMetaAnnotatedWith_predicate() {
List<JavaMember> members = filterResultOf(members().that().areDeclaredInClassesThat().areNotMetaAnnotatedWith(hasNamePredicate))
.on(MetaAnnotatedClass.class, AnnotatedClass.class, SimpleClass.class, MetaAnnotatedAnnotation.class);

assertThatMembers(members).matchInAnyOrderMembersOf(AnnotatedClass.class, SimpleClass.class, MetaAnnotatedAnnotation.class);
assertThatMembers(members).matchInAnyOrderMembersOf(SimpleClass.class, MetaAnnotatedAnnotation.class);
}

@Test
Expand Down
Loading

0 comments on commit 5d68c45

Please sign in to comment.