Skip to content

Commit

Permalink
Support meta-annotations
Browse files Browse the repository at this point in the history
Resolves TNG#282

Signed-off-by: Daniel Shuy <[email protected]>
  • Loading branch information
daniel-shuy committed Feb 5, 2020
1 parent 16f3561 commit 4d6bbef
Show file tree
Hide file tree
Showing 14 changed files with 618 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

import org.junit.platform.commons.support.AnnotationSupport;
import org.junit.platform.engine.TestDescriptor;
import org.junit.platform.engine.TestSource;
import org.junit.platform.engine.TestTag;
Expand All @@ -39,15 +41,17 @@ abstract class AbstractArchUnitTestDescriptor extends AbstractTestDescriptor imp
super(uniqueId, displayName, source);
tags = Arrays.stream(elements).map(this::findTagsOn).flatMap(Collection::stream).collect(toSet());
skipResult = Arrays.stream(elements)
.filter(e -> e.isAnnotationPresent(ArchIgnore.class))
.map(e -> e.getAnnotation(ArchIgnore.class))
.map(e -> AnnotationSupport.findAnnotation(e, ArchIgnore.class))
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst()
.map(ignore -> SkipResult.skip(ignore.reason()))
.orElse(SkipResult.doNotSkip());
}

private Set<TestTag> findTagsOn(AnnotatedElement annotatedElement) {
return Arrays.stream(annotatedElement.getAnnotationsByType(ArchTag.class))
return AnnotationSupport.findRepeatableAnnotations(annotatedElement, ArchTag.class)
.stream()
.map(annotation -> TestTag.create(annotation.value()))
.collect(toSet());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,30 @@
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.junit.ArchUnitTestEngine.SharedCache;
import com.tngtech.archunit.junit.testexamples.ClassWithPrivateTests;
import com.tngtech.archunit.junit.testexamples.ComplexMetaTags;
import com.tngtech.archunit.junit.testexamples.ComplexRuleLibrary;
import com.tngtech.archunit.junit.testexamples.ComplexTags;
import com.tngtech.archunit.junit.testexamples.FullAnalyzeClassesSpec;
import com.tngtech.archunit.junit.testexamples.LibraryWithPrivateTests;
import com.tngtech.archunit.junit.testexamples.SimpleRuleLibrary;
import com.tngtech.archunit.junit.testexamples.TestClassWithMetaTag;
import com.tngtech.archunit.junit.testexamples.TestClassWithMetaTags;
import com.tngtech.archunit.junit.testexamples.TestClassWithTags;
import com.tngtech.archunit.junit.testexamples.TestFieldWithMetaTag;
import com.tngtech.archunit.junit.testexamples.TestFieldWithMetaTags;
import com.tngtech.archunit.junit.testexamples.TestFieldWithTags;
import com.tngtech.archunit.junit.testexamples.TestMethodWithMetaTag;
import com.tngtech.archunit.junit.testexamples.TestMethodWithMetaTags;
import com.tngtech.archunit.junit.testexamples.TestMethodWithTags;
import com.tngtech.archunit.junit.testexamples.UnwantedClass;
import com.tngtech.archunit.junit.testexamples.ignores.IgnoredClass;
import com.tngtech.archunit.junit.testexamples.ignores.IgnoredField;
import com.tngtech.archunit.junit.testexamples.ignores.IgnoredLibrary;
import com.tngtech.archunit.junit.testexamples.ignores.IgnoredMethod;
import com.tngtech.archunit.junit.testexamples.ignores.MetaIgnoredClass;
import com.tngtech.archunit.junit.testexamples.ignores.MetaIgnoredField;
import com.tngtech.archunit.junit.testexamples.ignores.MetaIgnoredLibrary;
import com.tngtech.archunit.junit.testexamples.ignores.MetaIgnoredMethod;
import com.tngtech.archunit.junit.testexamples.subone.SimpleRuleField;
import com.tngtech.archunit.junit.testexamples.subone.SimpleRuleMethod;
import com.tngtech.archunit.junit.testexamples.subtwo.SimpleRules;
Expand Down Expand Up @@ -59,7 +70,11 @@
import static com.tngtech.archunit.junit.ArchUnitTestDescriptor.FIELD_SEGMENT_TYPE;
import static com.tngtech.archunit.junit.ArchUnitTestDescriptor.METHOD_SEGMENT_TYPE;
import static com.tngtech.archunit.junit.EngineExecutionTestListener.onlyElement;
import static com.tngtech.archunit.junit.testexamples.TestFieldWithMetaTag.FIELD_WITH_META_TAG_NAME;
import static com.tngtech.archunit.junit.testexamples.TestFieldWithMetaTags.FIELD_WITH_META_TAGS_NAME;
import static com.tngtech.archunit.junit.testexamples.TestFieldWithTags.FIELD_WITH_TAG_NAME;
import static com.tngtech.archunit.junit.testexamples.TestMethodWithMetaTag.METHOD_WITH_META_TAG_NAME;
import static com.tngtech.archunit.junit.testexamples.TestMethodWithMetaTags.METHOD_WITH_META_TAGS_NAME;
import static com.tngtech.archunit.junit.testexamples.TestMethodWithTags.METHOD_WITH_TAG_NAME;
import static com.tngtech.archunit.junit.testexamples.subone.SimpleRuleField.SIMPLE_RULE_FIELD_NAME;
import static com.tngtech.archunit.junit.testexamples.subone.SimpleRuleMethod.SIMPLE_RULE_METHOD_NAME;
Expand Down Expand Up @@ -463,20 +478,80 @@ void tags_of_test_classes() {
);
}

@Test
void meta_tag_of_test_classes() {
EngineDiscoveryTestRequest discoveryRequest = new EngineDiscoveryTestRequest().withClass(TestClassWithMetaTag.class);

TestDescriptor descriptor = testEngine.discover(discoveryRequest, engineId);

TestDescriptor testClass = getOnlyElement(descriptor.getChildren());
assertThat(testClass.getTags()).containsOnly(TestTag.create("meta-tag-one"), TestTag.create("meta-tag-two"));

Set<? extends TestDescriptor> concreteRules = getAllLeafs(testClass);
assertThat(concreteRules).as("concrete rules").hasSize(3);
concreteRules.forEach(concreteRule ->
assertThat(concreteRule.getTags()).containsOnly(TestTag.create("meta-tag-one"), TestTag.create("meta-tag-two"))
);
}

@Test
void meta_tags_of_test_classes() {
EngineDiscoveryTestRequest discoveryRequest = new EngineDiscoveryTestRequest().withClass(TestClassWithMetaTags.class);

TestDescriptor descriptor = testEngine.discover(discoveryRequest, engineId);

TestDescriptor testClass = getOnlyElement(descriptor.getChildren());
assertThat(testClass.getTags()).containsOnly(TestTag.create("meta-tags-one"), TestTag.create("meta-tags-two"));

Set<? extends TestDescriptor> concreteRules = getAllLeafs(testClass);
assertThat(concreteRules).as("concrete rules").hasSize(3);
concreteRules.forEach(concreteRule ->
assertThat(concreteRule.getTags()).containsOnly(TestTag.create("meta-tags-one"), TestTag.create("meta-tags-two"))
);
}

@Test
void tags_of_rule_fields() {
TestDescriptor testField = getOnlyChildWithDescriptorContaining(FIELD_WITH_TAG_NAME, TestFieldWithTags.class);

assertThat(testField.getTags()).containsOnly(TestTag.create("field-tag-one"), TestTag.create("field-tag-two"));
}

@Test
void meta_tag_of_rule_fields() {
TestDescriptor testField = getOnlyChildWithDescriptorContaining(FIELD_WITH_META_TAG_NAME, TestFieldWithMetaTag.class);

assertThat(testField.getTags()).containsOnly(TestTag.create("field-meta-tag-one"), TestTag.create("field-meta-tag-two"));
}

@Test
void meta_tags_of_rule_fields() {
TestDescriptor testField = getOnlyChildWithDescriptorContaining(FIELD_WITH_META_TAGS_NAME, TestFieldWithMetaTags.class);

assertThat(testField.getTags()).containsOnly(TestTag.create("field-meta-tags-one"), TestTag.create("field-meta-tags-two"));
}

@Test
void tags_of_rule_methods() {
TestDescriptor testMethod = getOnlyChildWithDescriptorContaining(METHOD_WITH_TAG_NAME, TestMethodWithTags.class);

assertThat(testMethod.getTags()).containsOnly(TestTag.create("method-tag-one"), TestTag.create("method-tag-two"));
}

@Test
void meta_tag_of_rule_methods() {
TestDescriptor testMethod = getOnlyChildWithDescriptorContaining(METHOD_WITH_META_TAG_NAME, TestMethodWithMetaTag.class);

assertThat(testMethod.getTags()).containsOnly(TestTag.create("method-meta-tag-one"), TestTag.create("method-meta-tag-two"));
}

@Test
void meta_tags_of_rule_methods() {
TestDescriptor testMethod = getOnlyChildWithDescriptorContaining(METHOD_WITH_META_TAGS_NAME, TestMethodWithMetaTags.class);

assertThat(testMethod.getTags()).containsOnly(TestTag.create("method-meta-tags-one"), TestTag.create("method-meta-tags-two"));
}

@Test
void complex_tags() {
EngineDiscoveryTestRequest discoveryRequest = new EngineDiscoveryTestRequest().withClass(ComplexTags.class);
Expand Down Expand Up @@ -514,6 +589,57 @@ void complex_tags() {
TestTag.create("method-tag"));
}

@Test
void complex_meta_tags() {
EngineDiscoveryTestRequest discoveryRequest = new EngineDiscoveryTestRequest().withClass(ComplexMetaTags.class);

TestDescriptor descriptor = testEngine.discover(discoveryRequest, engineId);

Map<UniqueId, Set<TestTag>> tagsById = new HashMap<>();
descriptor.accept(d -> tagsById.put(d.getUniqueId(), d.getTags()));

assertThat(getTagsForIdEndingIn(ComplexMetaTags.class.getSimpleName(), tagsById))
.containsOnly(TestTag.create("library-meta-tag"));

assertThat(getTagsForIdEndingIn(TestClassWithMetaTag.class.getSimpleName(), tagsById))
.containsOnly(
TestTag.create("library-meta-tag"),
TestTag.create("rules-meta-tag"),
TestTag.create("meta-tag-one"),
TestTag.create("meta-tag-two"));

assertThat(getTagsForIdEndingIn(TestClassWithMetaTags.class.getSimpleName(), tagsById))
.containsOnly(
TestTag.create("library-meta-tag"),
TestTag.create("rules-meta-tag"),
TestTag.create("meta-tags-one"),
TestTag.create("meta-tags-two"));

assertThat(getTagsForIdEndingIn(TestClassWithMetaTag.FIELD_RULE_NAME, tagsById))
.containsOnly(
TestTag.create("library-meta-tag"),
TestTag.create("rules-meta-tag"),
TestTag.create("meta-tag-one"),
TestTag.create("meta-tag-two"));

assertThat(getTagsForIdEndingIn(TestClassWithMetaTags.FIELD_RULE_NAME, tagsById))
.containsOnly(
TestTag.create("library-meta-tag"),
TestTag.create("rules-meta-tag"),
TestTag.create("meta-tags-one"),
TestTag.create("meta-tags-two"));

assertThat(getTagsForIdEndingIn(ComplexMetaTags.FIELD_RULE_NAME, tagsById))
.containsOnly(
TestTag.create("library-meta-tag"),
TestTag.create("field-meta-tag"));

assertThat(getTagsForIdEndingIn(ComplexMetaTags.METHOD_RULE_NAME, tagsById))
.containsOnly(
TestTag.create("library-meta-tag"),
TestTag.create("method-meta-tag"));
}

@Test
void filtering_excluded_class_names() {
EngineDiscoveryTestRequest discoveryRequest = new EngineDiscoveryTestRequest()
Expand Down Expand Up @@ -885,6 +1011,105 @@ void with_reason() {
}
}

@Nested
class MetaIgnores {
@Test
void fields() {
simulateCachedClassesForTest(MetaIgnoredField.class, UnwantedClass.CLASS_VIOLATING_RULES);

EngineExecutionTestListener testListener = execute(engineId, MetaIgnoredField.class);

testListener.verifySkipped(engineId
.append(CLASS_SEGMENT_TYPE, MetaIgnoredField.class.getName())
.append(FIELD_SEGMENT_TYPE, MetaIgnoredField.IGNORED_RULE_FIELD));

testListener.verifyViolation(engineId
.append(CLASS_SEGMENT_TYPE, MetaIgnoredField.class.getName())
.append(FIELD_SEGMENT_TYPE, MetaIgnoredField.UNIGNORED_RULE_FIELD));
}

@Test
void methods() {
simulateCachedClassesForTest(MetaIgnoredMethod.class, UnwantedClass.CLASS_VIOLATING_RULES);

EngineExecutionTestListener testListener = execute(engineId, MetaIgnoredMethod.class);

testListener.verifySkipped(engineId
.append(CLASS_SEGMENT_TYPE, MetaIgnoredMethod.class.getName())
.append(METHOD_SEGMENT_TYPE, MetaIgnoredMethod.IGNORED_RULE_METHOD));

testListener.verifyViolation(engineId
.append(CLASS_SEGMENT_TYPE, MetaIgnoredMethod.class.getName())
.append(METHOD_SEGMENT_TYPE, MetaIgnoredMethod.UNIGNORED_RULE_METHOD));
}

@Test
void classes() {
simulateCachedClassesForTest(MetaIgnoredClass.class, UnwantedClass.CLASS_VIOLATING_RULES);

EngineExecutionTestListener testListener = execute(engineId, MetaIgnoredClass.class);

testListener.verifySkipped(engineId
.append(CLASS_SEGMENT_TYPE, MetaIgnoredClass.class.getName()));

testListener.verifyNoOtherStartExceptHierarchyOf(engineId);
}

@Test
void libraries() {
simulateCachedClassesForTest(MetaIgnoredLibrary.class, UnwantedClass.CLASS_VIOLATING_RULES);

EngineExecutionTestListener testListener = execute(engineId, MetaIgnoredLibrary.class);

testListener.verifySkipped(engineId
.append(CLASS_SEGMENT_TYPE, MetaIgnoredLibrary.class.getName())
.append(FIELD_SEGMENT_TYPE, MetaIgnoredLibrary.IGNORED_LIB_FIELD)
.append(CLASS_SEGMENT_TYPE, SimpleRules.class.getName()));
}

@Test
void library_referenced_classes() {
simulateCachedClassesForTest(MetaIgnoredLibrary.class, UnwantedClass.CLASS_VIOLATING_RULES);

EngineExecutionTestListener testListener = execute(engineId, MetaIgnoredLibrary.class);

testListener.verifySkipped(engineId
.append(CLASS_SEGMENT_TYPE, MetaIgnoredLibrary.class.getName())
.append(FIELD_SEGMENT_TYPE, MetaIgnoredLibrary.UNIGNORED_LIB_ONE_FIELD)
.append(CLASS_SEGMENT_TYPE, MetaIgnoredClass.class.getName()));
}

@Test
void library_sub_rules() {
simulateCachedClassesForTest(MetaIgnoredLibrary.class, UnwantedClass.CLASS_VIOLATING_RULES);

EngineExecutionTestListener testListener = execute(engineId, MetaIgnoredLibrary.class);

UniqueId classWithIgnoredMethod = engineId
.append(CLASS_SEGMENT_TYPE, MetaIgnoredLibrary.class.getName())
.append(FIELD_SEGMENT_TYPE, MetaIgnoredLibrary.UNIGNORED_LIB_TWO_FIELD)
.append(CLASS_SEGMENT_TYPE, MetaIgnoredMethod.class.getName());

testListener.verifySkipped(classWithIgnoredMethod
.append(METHOD_SEGMENT_TYPE, MetaIgnoredMethod.IGNORED_RULE_METHOD));

testListener.verifyViolation(classWithIgnoredMethod
.append(METHOD_SEGMENT_TYPE, MetaIgnoredMethod.UNIGNORED_RULE_METHOD));
}

@Test
void with_reason() {
simulateCachedClassesForTest(MetaIgnoredField.class, UnwantedClass.CLASS_VIOLATING_RULES);

EngineExecutionTestListener testListener = execute(engineId, MetaIgnoredField.class);

UniqueId ignoredId = engineId
.append(CLASS_SEGMENT_TYPE, MetaIgnoredField.class.getName())
.append(FIELD_SEGMENT_TYPE, MetaIgnoredField.IGNORED_RULE_FIELD);
testListener.verifySkipped(ignoredId, "some example description");
}
}

private UniqueId createEngineId() {
return UniqueId.forEngine(ArchUnitTestEngine.UNIQUE_ID);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.tngtech.archunit.junit.testexamples;

import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchRules;
import com.tngtech.archunit.junit.ArchTag;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.lang.ArchRule;

import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@ComplexMetaTags.LibraryTag
@AnalyzeClasses
public class ComplexMetaTags {
public static final String FIELD_RULE_NAME = "field_rule";
public static final String METHOD_RULE_NAME = "method_rule";

@RulesTag
@ArchTest
static final ArchRules classWithMetaTag = ArchRules.in(TestClassWithMetaTag.class);

@RulesTag
@ArchTest
static final ArchRules classWithMetaTags = ArchRules.in(TestClassWithMetaTags.class);

@FieldTag
@ArchTest
static final ArchRule field_rule = RuleThatFails.on(UnwantedClass.class);

@MethodTag
@ArchTest
static void method_rule(JavaClasses classes) {
}

@Inherited
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD})
@ArchTag("library-meta-tag")
@interface LibraryTag {
}

@Inherited
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD})
@ArchTag("rules-meta-tag")
@interface RulesTag {
}

@Inherited
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD})
@ArchTag("field-meta-tag")
@interface FieldTag {
}

@Inherited
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD})
@ArchTag("method-meta-tag")
@interface MethodTag {
}
}
Loading

0 comments on commit 4d6bbef

Please sign in to comment.