| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package org.chromium.base.test.util; |
| |
| import static org.hamcrest.Matchers.contains; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertThat; |
| import static org.junit.Assert.fail; |
| import static org.junit.runner.Description.createTestDescription; |
| |
| import org.junit.Ignore; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.rules.TestRule; |
| import org.junit.runner.Description; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.BlockJUnit4ClassRunner; |
| import org.junit.runners.model.FrameworkMethod; |
| import org.junit.runners.model.InitializationError; |
| import org.junit.runners.model.Statement; |
| |
| import org.chromium.base.test.util.AnnotationProcessingUtils.AnnotationExtractor; |
| |
| import java.lang.annotation.Annotation; |
| import java.lang.annotation.ElementType; |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.lang.annotation.Target; |
| import java.util.Arrays; |
| import java.util.Comparator; |
| import java.util.List; |
| |
| /** Test for {@link AnnotationProcessingUtils}. */ |
| @RunWith(BlockJUnit4ClassRunner.class) |
| public class AnnotationProcessingUtilsTest { |
| @Test |
| public void testGetTargetAnnotation_NotOnClassNorMethod() { |
| TargetAnnotation retrievedAnnotation; |
| |
| retrievedAnnotation = AnnotationProcessingUtils.getAnnotation( |
| createTestDescription( |
| ClassWithoutTargetAnnotation.class, "methodWithoutAnnotation"), |
| TargetAnnotation.class); |
| assertNull(retrievedAnnotation); |
| } |
| |
| @Test |
| public void testGetTargetAnnotation_NotOnClassButOnMethod() { |
| TargetAnnotation retrievedAnnotation; |
| |
| retrievedAnnotation = AnnotationProcessingUtils.getAnnotation( |
| getTest(ClassWithoutTargetAnnotation.class, "methodWithTargetAnnotation"), |
| TargetAnnotation.class); |
| assertNotNull(retrievedAnnotation); |
| } |
| |
| @Test |
| public void testGetTargetAnnotation_NotOnClassDifferentOneOnMethod() { |
| TargetAnnotation retrievedAnnotation; |
| |
| retrievedAnnotation = AnnotationProcessingUtils.getAnnotation( |
| getTest(ClassWithoutTargetAnnotation.class, "methodWithAnnotatedAnnotation"), |
| TargetAnnotation.class); |
| assertNull(retrievedAnnotation); |
| } |
| |
| @Test |
| public void testGetTargetAnnotation_OnClassButNotOnMethod() { |
| TargetAnnotation retrievedAnnotation; |
| |
| retrievedAnnotation = AnnotationProcessingUtils.getAnnotation( |
| getTest(ClassWithAnnotation.class, "methodWithoutAnnotation"), |
| TargetAnnotation.class); |
| assertNotNull(retrievedAnnotation); |
| assertEquals(Location.Class, retrievedAnnotation.value()); |
| } |
| |
| @Test |
| public void testGetTargetAnnotation_OnClassAndMethod() { |
| TargetAnnotation retrievedAnnotation; |
| |
| retrievedAnnotation = AnnotationProcessingUtils.getAnnotation( |
| getTest(ClassWithAnnotation.class, "methodWithTargetAnnotation"), |
| TargetAnnotation.class); |
| assertNotNull(retrievedAnnotation); |
| assertEquals(Location.Method, retrievedAnnotation.value()); |
| } |
| |
| @Test |
| @Ignore("Rules not supported yet.") |
| public void testGetTargetAnnotation_OnRuleButNotOnMethod() { |
| TargetAnnotation retrievedAnnotation; |
| |
| retrievedAnnotation = AnnotationProcessingUtils.getAnnotation( |
| getTest(ClassWithRule.class, "methodWithoutAnnotation"), TargetAnnotation.class); |
| assertNotNull(retrievedAnnotation); |
| assertEquals(Location.Rule, retrievedAnnotation.value()); |
| } |
| |
| @Test |
| @Ignore("Rules not supported yet.") |
| public void testGetTargetAnnotation_OnRuleAndMethod() { |
| TargetAnnotation retrievedAnnotation; |
| |
| retrievedAnnotation = AnnotationProcessingUtils.getAnnotation( |
| getTest(ClassWithRule.class, "methodWithTargetAnnotation"), TargetAnnotation.class); |
| assertNotNull(retrievedAnnotation); |
| assertEquals(Location.Method, retrievedAnnotation.value()); |
| } |
| |
| @Test |
| public void testGetMetaAnnotation_Indirectly() { |
| MetaAnnotation retrievedAnnotation; |
| |
| retrievedAnnotation = AnnotationProcessingUtils.getAnnotation( |
| getTest(ClassWithoutTargetAnnotation.class, "methodWithAnnotatedAnnotation"), |
| MetaAnnotation.class); |
| assertNotNull(retrievedAnnotation); |
| } |
| |
| @Test |
| public void testGetAllTargetAnnotations() { |
| List<TargetAnnotation> retrievedAnnotations; |
| |
| retrievedAnnotations = AnnotationProcessingUtils.getAnnotations( |
| getTest(ClassWithAnnotation.class, "methodWithTargetAnnotation"), |
| TargetAnnotation.class); |
| assertEquals(2, retrievedAnnotations.size()); |
| assertEquals(Location.Class, retrievedAnnotations.get(0).value()); |
| assertEquals(Location.Method, retrievedAnnotations.get(1).value()); |
| } |
| |
| @Test |
| public void testGetAllTargetAnnotations_OnParentClass() { |
| List<TargetAnnotation> retrievedAnnotations; |
| |
| retrievedAnnotations = AnnotationProcessingUtils.getAnnotations( |
| getTest(DerivedClassWithoutAnnotation.class, "newMethodWithoutAnnotation"), |
| TargetAnnotation.class); |
| assertEquals(1, retrievedAnnotations.size()); |
| assertEquals(Location.Class, retrievedAnnotations.get(0).value()); |
| } |
| |
| @Test |
| public void testGetAllTargetAnnotations_OnDerivedMethodAndParentClass() { |
| List<TargetAnnotation> retrievedAnnotations; |
| |
| retrievedAnnotations = AnnotationProcessingUtils.getAnnotations( |
| getTest(DerivedClassWithoutAnnotation.class, "newMethodWithTargetAnnotation"), |
| TargetAnnotation.class); |
| assertEquals(2, retrievedAnnotations.size()); |
| assertEquals(Location.Class, retrievedAnnotations.get(0).value()); |
| assertEquals(Location.DerivedMethod, retrievedAnnotations.get(1).value()); |
| } |
| |
| @Test |
| public void testGetAllTargetAnnotations_OnDerivedMethodAndParentClassAndMethod() { |
| List<TargetAnnotation> retrievedAnnotations; |
| |
| retrievedAnnotations = AnnotationProcessingUtils.getAnnotations( |
| getTest(DerivedClassWithoutAnnotation.class, "methodWithTargetAnnotation"), |
| TargetAnnotation.class); |
| // We should not look at the base implementation of the method. Mostly it should not happen |
| // in the context of tests. |
| assertEquals(2, retrievedAnnotations.size()); |
| assertEquals(Location.Class, retrievedAnnotations.get(0).value()); |
| assertEquals(Location.DerivedMethod, retrievedAnnotations.get(1).value()); |
| } |
| |
| @Test |
| public void testGetAllTargetAnnotations_OnDerivedParentAndParentClass() { |
| List<TargetAnnotation> retrievedAnnotations; |
| |
| retrievedAnnotations = AnnotationProcessingUtils.getAnnotations( |
| getTest(DerivedClassWithAnnotation.class, "methodWithoutAnnotation"), |
| TargetAnnotation.class); |
| assertEquals(2, retrievedAnnotations.size()); |
| assertEquals(Location.Class, retrievedAnnotations.get(0).value()); |
| assertEquals(Location.DerivedClass, retrievedAnnotations.get(1).value()); |
| } |
| |
| @Test |
| public void testGetAllAnnotations() { |
| List<Annotation> annotations; |
| |
| AnnotationExtractor annotationExtractor = new AnnotationExtractor( |
| TargetAnnotation.class, MetaAnnotation.class, AnnotatedAnnotation.class); |
| annotations = annotationExtractor.getMatchingAnnotations( |
| getTest(DerivedClassWithAnnotation.class, "methodWithTwoAnnotations")); |
| assertEquals(5, annotations.size()); |
| |
| // Retrieved annotation order: |
| // On Parent Class |
| assertEquals(TargetAnnotation.class, annotations.get(0).annotationType()); |
| assertEquals(Location.Class, ((TargetAnnotation) annotations.get(0)).value()); |
| |
| // On Class |
| assertEquals(TargetAnnotation.class, annotations.get(1).annotationType()); |
| assertEquals(Location.DerivedClass, ((TargetAnnotation) annotations.get(1)).value()); |
| |
| // Meta-annotations from method |
| assertEquals(MetaAnnotation.class, annotations.get(2).annotationType()); |
| |
| // On Method |
| assertEquals(AnnotatedAnnotation.class, annotations.get(3).annotationType()); |
| assertEquals(TargetAnnotation.class, annotations.get(4).annotationType()); |
| assertEquals(Location.DerivedMethod, ((TargetAnnotation) annotations.get(4)).value()); |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Test |
| public void testAnnotationExtractorSortOrder_UnknownAnnotations() { |
| AnnotationExtractor annotationExtractor = new AnnotationExtractor(Target.class); |
| Comparator<Class<? extends Annotation>> comparator = |
| annotationExtractor.getTypeComparator(); |
| List<Class<? extends Annotation>> testList = |
| Arrays.asList(Rule.class, Test.class, Override.class, Target.class, Rule.class); |
| testList.sort(comparator); |
| assertThat("Unknown annotations should not be reordered and come before the known ones.", |
| testList, |
| contains(Rule.class, Test.class, Override.class, Rule.class, Target.class)); |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Test |
| public void testAnnotationExtractorSortOrder_KnownAnnotations() { |
| AnnotationExtractor annotationExtractor = |
| new AnnotationExtractor(Test.class, Target.class, Rule.class); |
| Comparator<Class<? extends Annotation>> comparator = |
| annotationExtractor.getTypeComparator(); |
| List<Class<? extends Annotation>> testList = |
| Arrays.asList(Rule.class, Test.class, Override.class, Target.class, Rule.class); |
| testList.sort(comparator); |
| assertThat( |
| "Known annotations should be sorted in the same order as provided to the extractor", |
| testList, |
| contains(Override.class, Test.class, Target.class, Rule.class, Rule.class)); |
| } |
| |
| private static Description getTest(Class<?> klass, String testName) { |
| Description description = null; |
| try { |
| description = new DummyTestRunner(klass).describe(testName); |
| } catch (InitializationError initializationError) { |
| initializationError.printStackTrace(); |
| fail("DummyTestRunner initialization failed:" + initializationError.getMessage()); |
| } |
| if (description == null) { |
| fail("Not test named '" + testName + "' in class" + klass.getSimpleName()); |
| } |
| return description; |
| } |
| |
| // region Test Data: Annotations and dummy test classes |
| private enum Location { Unspecified, Class, Method, Rule, DerivedClass, DerivedMethod } |
| |
| @Retention(RetentionPolicy.RUNTIME) |
| @Target({ElementType.TYPE, ElementType.METHOD}) |
| private @interface TargetAnnotation { |
| Location value() default Location.Unspecified; |
| } |
| |
| @Retention(RetentionPolicy.RUNTIME) |
| @Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.METHOD}) |
| private @interface MetaAnnotation {} |
| |
| @Retention(RetentionPolicy.RUNTIME) |
| @Target({ElementType.TYPE, ElementType.METHOD}) |
| @MetaAnnotation |
| private @interface AnnotatedAnnotation {} |
| |
| private @interface SimpleAnnotation {} |
| |
| @SimpleAnnotation |
| private static class ClassWithoutTargetAnnotation { |
| @Test |
| public void methodWithoutAnnotation() {} |
| |
| @Test |
| @TargetAnnotation |
| public void methodWithTargetAnnotation() {} |
| |
| @Test |
| @AnnotatedAnnotation |
| public void methodWithAnnotatedAnnotation() {} |
| } |
| |
| @TargetAnnotation(Location.Class) |
| private static class ClassWithAnnotation { |
| @Test |
| public void methodWithoutAnnotation() {} |
| |
| @Test |
| @TargetAnnotation(Location.Method) |
| public void methodWithTargetAnnotation() {} |
| |
| @Test |
| @MetaAnnotation |
| public void methodWithMetaAnnotation() {} |
| |
| @Test |
| @AnnotatedAnnotation |
| public void methodWithAnnotatedAnnotation() {} |
| } |
| |
| private static class DerivedClassWithoutAnnotation extends ClassWithAnnotation { |
| @Test |
| public void newMethodWithoutAnnotation() {} |
| |
| @Test |
| @TargetAnnotation(Location.DerivedMethod) |
| public void newMethodWithTargetAnnotation() {} |
| |
| @Test |
| @Override |
| @TargetAnnotation(Location.DerivedMethod) |
| public void methodWithTargetAnnotation() {} |
| } |
| |
| @TargetAnnotation(Location.DerivedClass) |
| private static class DerivedClassWithAnnotation extends ClassWithAnnotation { |
| @Test |
| public void newMethodWithoutAnnotation() {} |
| |
| @Test |
| @AnnotatedAnnotation |
| @TargetAnnotation(Location.DerivedMethod) |
| public void methodWithTwoAnnotations() {} |
| } |
| |
| private static class ClassWithRule { |
| @Rule |
| Rule1 mRule = new Rule1(); |
| |
| @Test |
| public void methodWithoutAnnotation() {} |
| |
| @Test |
| @TargetAnnotation |
| public void methodWithTargetAnnotation() {} |
| } |
| |
| @TargetAnnotation(Location.Rule) |
| @MetaAnnotation |
| private static class Rule1 implements TestRule { |
| @Override |
| public Statement apply(Statement statement, Description description) { |
| return null; |
| } |
| } |
| |
| private static class DummyTestRunner extends BlockJUnit4ClassRunner { |
| public DummyTestRunner(Class<?> klass) throws InitializationError { |
| super(klass); |
| } |
| |
| @Override |
| protected void collectInitializationErrors(List<Throwable> errors) { |
| // Do nothing. BlockJUnit4ClassRunner requires the class to be public, but we don't |
| // want/need it. |
| } |
| |
| public Description describe(String testName) { |
| List<FrameworkMethod> tests = getTestClass().getAnnotatedMethods(Test.class); |
| for (FrameworkMethod testMethod : tests) { |
| if (testMethod.getName().equals(testName)) return describeChild(testMethod); |
| } |
| return null; |
| } |
| } |
| |
| // endregion |
| } |