diff options
author | cgruber <cgruber@google.com> | 2015-01-08 17:26:12 -0800 |
---|---|---|
committer | Christian Edward Gruber <cgruber@google.com> | 2015-01-21 13:43:13 -0800 |
commit | d1f1f9a1c13a9ab76db20380ad6479da2af88bc6 (patch) | |
tree | 0cdc34af9f36843179e4518da412668af3a584fd /common/src/test/java/com | |
parent | e8fb2085e8686100b205e1698e4a249eadea69d9 (diff) | |
download | auto-d1f1f9a1c13a9ab76db20380ad6479da2af88bc6.tar.gz |
Add AnnotationMirrors, AnnotationValues, and a BasicAnnotationProcessor which performs early validation of elements, and stores un-processable elements and re-tries them on subsequent processing rounds (in case the processor depends on code which will become valid in future rounds). Also, move some stray tests from dagger to google/auto common.
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=83567228
Diffstat (limited to 'common/src/test/java/com')
4 files changed, 431 insertions, 17 deletions
diff --git a/common/src/test/java/com/google/auto/common/AnnotationMirrorsTest.java b/common/src/test/java/com/google/auto/common/AnnotationMirrorsTest.java new file mode 100644 index 00000000..2f7fef14 --- /dev/null +++ b/common/src/test/java/com/google/auto/common/AnnotationMirrorsTest.java @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.auto.common; + +import static com.google.auto.common.AnnotationMirrorsTest.SimpleEnum.BLAH; +import static com.google.auto.common.AnnotationMirrorsTest.SimpleEnum.FOO; +import static com.google.common.collect.Iterables.getOnlyElement; +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; + +import com.google.common.testing.EquivalenceTester; +import com.google.testing.compile.CompilationRule; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.Map; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.util.Elements; +import javax.lang.model.util.SimpleAnnotationValueVisitor6; + +/** + * Tests {@link AnnotationMirrors}. + */ +@RunWith(JUnit4.class) +public class AnnotationMirrorsTest { + @Rule public CompilationRule compilationRule = new CompilationRule(); + + private Elements elements; + + @Before public void setUp() { + this.elements = compilationRule.getElements(); + } + + @interface SimpleAnnotation {} + + @SimpleAnnotation class SimplyAnnotated {} + @SimpleAnnotation class AlsoSimplyAnnotated {} + + enum SimpleEnum { + BLAH, FOO + } + + @interface Outer { + SimpleEnum value(); + } + + @Outer(BLAH) static class TestClassBlah {} + @Outer(BLAH) static class TestClassBlah2 {} + @Outer(FOO) static class TestClassFoo {} + + @interface DefaultingOuter { + SimpleEnum value() default SimpleEnum.BLAH; + } + + @DefaultingOuter class TestWithDefaultingOuterDefault {} + @DefaultingOuter(BLAH) class TestWithDefaultingOuterBlah {} + @DefaultingOuter(FOO) class TestWithDefaultingOuterFoo {} + + @interface AnnotatedOuter { + DefaultingOuter value(); + } + + @AnnotatedOuter(@DefaultingOuter) class TestDefaultNestedAnnotated {} + @AnnotatedOuter(@DefaultingOuter(BLAH)) class TestBlahNestedAnnotated {} + @AnnotatedOuter(@DefaultingOuter(FOO)) class TestFooNestedAnnotated {} + + @interface OuterWithValueArray { + DefaultingOuter[] value() default {}; + } + + @OuterWithValueArray class TestValueArrayWithDefault {} + @OuterWithValueArray({}) class TestValueArrayWithEmpty {} + + @OuterWithValueArray({@DefaultingOuter}) class TestValueArrayWithOneDefault {} + @OuterWithValueArray(@DefaultingOuter(BLAH)) class TestValueArrayWithOneBlah {} + @OuterWithValueArray(@DefaultingOuter(FOO)) class TestValueArrayWithOneFoo {} + + @OuterWithValueArray({@DefaultingOuter(FOO), @DefaultingOuter}) + class TestValueArrayWithFooAndDefaultBlah {} + @OuterWithValueArray({@DefaultingOuter(FOO), @DefaultingOuter(BLAH)}) + class TestValueArrayWithFooBlah {} + @OuterWithValueArray({@DefaultingOuter(FOO), @DefaultingOuter(BLAH)}) + class TestValueArrayWithFooBlah2 {} // Different instances than on TestValueArrayWithFooBlah. + @OuterWithValueArray({@DefaultingOuter(BLAH), @DefaultingOuter(FOO)}) + class TestValueArrayWithBlahFoo {} + + @Test public void testEquivalences() { + EquivalenceTester<AnnotationMirror> tester = + EquivalenceTester.of(AnnotationMirrors.equivalence()); + + tester.addEquivalenceGroup( + annotationOn(SimplyAnnotated.class), + annotationOn(AlsoSimplyAnnotated.class)); + + tester.addEquivalenceGroup( + annotationOn(TestClassBlah.class), + annotationOn(TestClassBlah2.class)); + + tester.addEquivalenceGroup( + annotationOn(TestClassFoo.class)); + + tester.addEquivalenceGroup( + annotationOn(TestWithDefaultingOuterDefault.class), + annotationOn(TestWithDefaultingOuterBlah.class)); + + tester.addEquivalenceGroup( + annotationOn(TestWithDefaultingOuterFoo.class)); + + tester.addEquivalenceGroup( + annotationOn(TestDefaultNestedAnnotated.class), + annotationOn(TestBlahNestedAnnotated.class)); + + tester.addEquivalenceGroup( + annotationOn(TestFooNestedAnnotated.class)); + + tester.addEquivalenceGroup( + annotationOn(TestValueArrayWithDefault.class), + annotationOn(TestValueArrayWithEmpty.class)); + + tester.addEquivalenceGroup( + annotationOn(TestValueArrayWithOneDefault.class), + annotationOn(TestValueArrayWithOneBlah.class)); + + tester.addEquivalenceGroup( + annotationOn(TestValueArrayWithOneFoo.class)); + + tester.addEquivalenceGroup( + annotationOn(TestValueArrayWithFooAndDefaultBlah.class), + annotationOn(TestValueArrayWithFooBlah.class), + annotationOn(TestValueArrayWithFooBlah2.class)); + + tester.addEquivalenceGroup( + annotationOn(TestValueArrayWithBlahFoo.class)); + + tester.test(); + } + + @interface Stringy { + String value() default "default"; + } + + @Stringy class StringyUnset {} + @Stringy("foo") class StringySet {} + + @Test public void testGetDefaultValuesUnset() { + assertThat(annotationOn(StringyUnset.class).getElementValues()).isEmpty(); + Iterable<AnnotationValue> values = AnnotationMirrors.getAnnotationValuesWithDefaults( + annotationOn(StringyUnset.class)).values(); + String value = getOnlyElement(values).accept(new SimpleAnnotationValueVisitor6<String, Void>() { + @Override public String visitString(String value, Void ignored) { + return value; + } + }, null); + assertThat(value).isEqualTo("default"); + } + + @Test public void testGetDefaultValuesSet() { + Iterable<AnnotationValue> values = AnnotationMirrors.getAnnotationValuesWithDefaults( + annotationOn(StringySet.class)).values(); + String value = getOnlyElement(values).accept(new SimpleAnnotationValueVisitor6<String, Void>() { + @Override public String visitString(String value, Void ignored) { + return value; + } + }, null); + assertThat(value).isEqualTo("foo"); + } + + @Test public void testGetValueEntry() { + Map.Entry<ExecutableElement, AnnotationValue> elementValue = + AnnotationMirrors.getAnnotationElementAndValue( + annotationOn(TestClassBlah.class), "value"); + assertThat(elementValue.getKey().getSimpleName().toString()).isEqualTo("value"); + assertThat(elementValue.getValue().getValue()).isInstanceOf(VariableElement.class); + AnnotationValue value = AnnotationMirrors.getAnnotationValue( + annotationOn(TestClassBlah.class), "value"); + assertThat(value.getValue()).isInstanceOf(VariableElement.class); + } + + @Test public void testGetValueEntryFailure() { + try { + AnnotationMirrors.getAnnotationValue(annotationOn(TestClassBlah.class), "a"); + } catch (IllegalArgumentException e) { + assertThat(e).hasMessage( + "@com.google.auto.common.AnnotationMirrorsTest.Outer does not define an element a()"); + return; + } + fail("Should have thrown."); + } + + private AnnotationMirror annotationOn(Class<?> clazz) { + return getOnlyElement(elements.getTypeElement(clazz.getCanonicalName()).getAnnotationMirrors()); + } +} diff --git a/common/src/test/java/com/google/auto/common/BasicAnnotationProcessorTest.java b/common/src/test/java/com/google/auto/common/BasicAnnotationProcessorTest.java new file mode 100644 index 00000000..0fd41c82 --- /dev/null +++ b/common/src/test/java/com/google/auto/common/BasicAnnotationProcessorTest.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.auto.common; + +import static com.google.common.truth.Truth.assertAbout; +import static com.google.common.truth.Truth.assertThat; +import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.SetMultimap; +import com.google.testing.compile.JavaFileObjects; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.annotation.Annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Set; + +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.tools.JavaFileObject; +import javax.tools.StandardLocation; + +@RunWith(JUnit4.class) +public class BasicAnnotationProcessorTest { + @Retention(RetentionPolicy.SOURCE) + public @interface RequiresGeneratedCode {} + + /** Asserts that the code generated by {@link GeneratesCode} and its processor is present. */ + public static class RequiresGeneratedCodeProcessor extends BasicAnnotationProcessor { + boolean processed = false; + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latestSupported(); + } + + @Override + protected Iterable<? extends ProcessingStep> initSteps() { + return ImmutableSet.of(new ProcessingStep() { + @Override + public void process( + SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) { + processed = true; + try { + processingEnv.getFiler() + .getResource(StandardLocation.SOURCE_OUTPUT, "test", "SomeGeneratedClass"); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + @Override + public Set<? extends Class<? extends Annotation>> annotations() { + return ImmutableSet.of(RequiresGeneratedCode.class); + } + }); + } + } + + @Retention(RetentionPolicy.SOURCE) + public @interface GeneratesCode {} + + /** Generates a class called {@code test.SomeGeneratedClass}. */ + public static class GeneratesCodeProcessor extends BasicAnnotationProcessor { + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latestSupported(); + } + + @Override + protected Iterable<? extends ProcessingStep> initSteps() { + return ImmutableSet.of(new ProcessingStep() { + @Override + public void process( + SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) { + for (Element element : elementsByAnnotation.values()) { + try { + generateClass(element); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + @Override + public Set<? extends Class<? extends Annotation>> annotations() { + return ImmutableSet.of(GeneratesCode.class); + } + }); + } + + private void generateClass(Element sourceType) throws IOException { + JavaFileObject source = + processingEnv.getFiler().createSourceFile("test.SomeGeneratedClass", sourceType); + PrintWriter writer = new PrintWriter(source.openWriter()); + writer.println("package test;"); + writer.println("public class SomeGeneratedClass {}"); + writer.close(); + } + } + + @Test public void properlyDefersProcessing_typeElement() { + JavaFileObject classAFileObject = JavaFileObjects.forSourceLines("test.ClassA", + "package test;", + "", + "@" + RequiresGeneratedCode.class.getCanonicalName(), + "public class ClassA {", + " SomeGeneratedClass sgc;", + "}"); + JavaFileObject classBFileObject = JavaFileObjects.forSourceLines("test.ClassB", + "package test;", + "", + "@" + GeneratesCode.class.getCanonicalName(), + "public class ClassB {}"); + RequiresGeneratedCodeProcessor requiresGeneratedCodeProcessor = + new RequiresGeneratedCodeProcessor(); + assertAbout(javaSources()) + .that(ImmutableList.of(classAFileObject, classBFileObject)) + .processedWith(requiresGeneratedCodeProcessor, new GeneratesCodeProcessor()) + .compilesWithoutError(); + assertThat(requiresGeneratedCodeProcessor.processed).isTrue(); + } + + @Retention(RetentionPolicy.SOURCE) + public @interface ReferencesAClass { + Class<?> value(); + } + + @Test public void properlyDefersProcessing_packageElement() { + JavaFileObject classAFileObject = JavaFileObjects.forSourceLines("test.ClassA", + "package test;", + "", + "@" + GeneratesCode.class.getCanonicalName(), + "public class ClassA {", + "}"); + JavaFileObject packageFileObject = JavaFileObjects.forSourceLines("test.package-info", + "@" + RequiresGeneratedCode.class.getCanonicalName(), + "@" + ReferencesAClass.class.getCanonicalName() + "(SomeGeneratedClass.class)", + "package test;"); + RequiresGeneratedCodeProcessor requiresGeneratedCodeProcessor = + new RequiresGeneratedCodeProcessor(); + assertAbout(javaSources()) + .that(ImmutableList.of(classAFileObject, packageFileObject)) + .processedWith(requiresGeneratedCodeProcessor, new GeneratesCodeProcessor()) + .compilesWithoutError(); + assertThat(requiresGeneratedCodeProcessor.processed).isTrue(); + } + + @Test public void reportsMissingType() { + JavaFileObject classAFileObject = JavaFileObjects.forSourceLines("test.ClassA", + "package test;", + "", + "@" + RequiresGeneratedCode.class.getCanonicalName(), + "public class ClassA {", + " SomeGeneratedClass bar;", + "}"); + assertAbout(javaSources()) + .that(ImmutableList.of(classAFileObject)) + .processedWith(new RequiresGeneratedCodeProcessor()) + .failsToCompile() + .withErrorContaining(RequiresGeneratedCodeProcessor.class.getCanonicalName()) + .in(classAFileObject).onLine(4); + } +} diff --git a/common/src/test/java/com/google/auto/common/MoreElementsTest.java b/common/src/test/java/com/google/auto/common/MoreElementsTest.java index dffa89e2..ba1fc172 100644 --- a/common/src/test/java/com/google/auto/common/MoreElementsTest.java +++ b/common/src/test/java/com/google/auto/common/MoreElementsTest.java @@ -15,7 +15,7 @@ */ package com.google.auto.common; -import static com.google.common.truth.Truth.assert_; +import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; import com.google.common.base.Optional; @@ -34,6 +34,7 @@ import java.lang.annotation.RetentionPolicy; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.util.ElementFilter; @@ -56,16 +57,16 @@ public class MoreElementsTest { @Test public void getPackage() { - assert_().that(javaLangPackageElement).isEqualTo(javaLangPackageElement); - assert_().that(MoreElements.getPackage(stringElement)).isEqualTo(javaLangPackageElement); + assertThat(javaLangPackageElement).isEqualTo(javaLangPackageElement); + assertThat(MoreElements.getPackage(stringElement)).isEqualTo(javaLangPackageElement); for (Element childElement : stringElement.getEnclosedElements()) { - assert_().that(MoreElements.getPackage(childElement)).isEqualTo(javaLangPackageElement); + assertThat(MoreElements.getPackage(childElement)).isEqualTo(javaLangPackageElement); } } @Test public void asPackage() { - assert_().that(MoreElements.asPackage(javaLangPackageElement)) + assertThat(MoreElements.asPackage(javaLangPackageElement)) .isEqualTo(javaLangPackageElement); } @@ -77,9 +78,27 @@ public class MoreElementsTest { } catch (IllegalArgumentException expected) {} } + @Test public void asTypeElement() { + Element typeElement = + compilation.getElements().getTypeElement(String.class.getCanonicalName()); + assertThat(MoreElements.asType(typeElement)).isEqualTo(typeElement); + } + + @Test public void asTypeElement_notATypeElement() { + TypeElement typeElement = + compilation.getElements().getTypeElement(String.class.getCanonicalName()); + for (ExecutableElement e : ElementFilter.methodsIn(typeElement.getEnclosedElements())) { + try { + MoreElements.asType(e); + fail(); + } catch (IllegalArgumentException expected) { + } + } + } + @Test public void asType() { - assert_().that(MoreElements.asType(stringElement)).isEqualTo(stringElement); + assertThat(MoreElements.asType(stringElement)).isEqualTo(stringElement); } @Test @@ -93,7 +112,7 @@ public class MoreElementsTest { @Test public void asVariable() { for (Element variableElement : ElementFilter.fieldsIn(stringElement.getEnclosedElements())) { - assert_().that(MoreElements.asVariable(variableElement)).isEqualTo(variableElement); + assertThat(MoreElements.asVariable(variableElement)).isEqualTo(variableElement); } } @@ -108,11 +127,11 @@ public class MoreElementsTest { @Test public void asExecutable() { for (Element methodElement : ElementFilter.methodsIn(stringElement.getEnclosedElements())) { - assert_().that(MoreElements.asExecutable(methodElement)).isEqualTo(methodElement); + assertThat(MoreElements.asExecutable(methodElement)).isEqualTo(methodElement); } for (Element methodElement : ElementFilter.constructorsIn(stringElement.getEnclosedElements())) { - assert_().that(MoreElements.asExecutable(methodElement)).isEqualTo(methodElement); + assertThat(MoreElements.asExecutable(methodElement)).isEqualTo(methodElement); } } @@ -135,14 +154,11 @@ public class MoreElementsTest { public void isAnnotationPresent() { TypeElement annotatedAnnotationElement = compilation.getElements().getTypeElement(AnnotatedAnnotation.class.getCanonicalName()); - assert_() - .that(MoreElements.isAnnotationPresent(annotatedAnnotationElement, Documented.class)) + assertThat(MoreElements.isAnnotationPresent(annotatedAnnotationElement, Documented.class)) .isTrue(); - assert_() - .that(MoreElements.isAnnotationPresent(annotatedAnnotationElement, InnerAnnotation.class)) + assertThat(MoreElements.isAnnotationPresent(annotatedAnnotationElement, InnerAnnotation.class)) .isTrue(); - assert_() - .that(MoreElements.isAnnotationPresent(annotatedAnnotationElement, SuppressWarnings.class)) + assertThat(MoreElements.isAnnotationPresent(annotatedAnnotationElement, SuppressWarnings.class)) .isFalse(); } diff --git a/common/src/test/java/com/google/auto/common/MoreTypesIsTypeOfTest.java b/common/src/test/java/com/google/auto/common/MoreTypesIsTypeOfTest.java index b067f1e7..380b50a7 100644 --- a/common/src/test/java/com/google/auto/common/MoreTypesIsTypeOfTest.java +++ b/common/src/test/java/com/google/auto/common/MoreTypesIsTypeOfTest.java @@ -17,7 +17,7 @@ package com.google.auto.common; import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.truth.Truth.assertThat; -import static com.google.common.truth.Truth.assert_; +import static org.junit.Assert.fail; import com.google.common.collect.Iterables; import com.google.testing.compile.CompilationRule; @@ -164,7 +164,7 @@ public class MoreTypesIsTypeOfTest { getOnlyElement(typeElementFor(DeclaredVoid.class).getEnclosedElements()).asType(); try { MoreTypes.isTypeOf(String.class, method); - assert_().fail(); + fail(); } catch (IllegalArgumentException expected) {} } |