aboutsummaryrefslogtreecommitdiff
path: root/common/src/test/java/com/google/auto/common/BasicAnnotationProcessorTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'common/src/test/java/com/google/auto/common/BasicAnnotationProcessorTest.java')
-rw-r--r--common/src/test/java/com/google/auto/common/BasicAnnotationProcessorTest.java387
1 files changed, 269 insertions, 118 deletions
diff --git a/common/src/test/java/com/google/auto/common/BasicAnnotationProcessorTest.java b/common/src/test/java/com/google/auto/common/BasicAnnotationProcessorTest.java
index 59a135c4..f7534503 100644
--- a/common/src/test/java/com/google/auto/common/BasicAnnotationProcessorTest.java
+++ b/common/src/test/java/com/google/auto/common/BasicAnnotationProcessorTest.java
@@ -18,28 +18,35 @@ package com.google.auto.common;
import static com.google.common.collect.Multimaps.transformValues;
import static com.google.common.truth.Truth.assertAbout;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.Compiler.javac;
import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
import static javax.tools.Diagnostic.Kind.ERROR;
import static javax.tools.StandardLocation.SOURCE_OUTPUT;
+import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
+import com.google.auto.common.BasicAnnotationProcessor.Step;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.SetMultimap;
import com.google.common.truth.Correspondence;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.CompilationRule;
import com.google.testing.compile.JavaFileObjects;
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.annotation.processing.Filer;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.Elements;
import javax.tools.JavaFileObject;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -47,30 +54,36 @@ import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class BasicAnnotationProcessorTest {
+ private abstract static class BaseAnnotationProcessor extends BasicAnnotationProcessor {
+
+ static final String ENCLOSING_CLASS_NAME =
+ BasicAnnotationProcessorTest.class.getCanonicalName();
+
+ @Override
+ public final SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+ }
+
@Retention(RetentionPolicy.SOURCE)
public @interface RequiresGeneratedCode {}
/**
* Rejects elements unless the class generated by {@link GeneratesCode}'s processor is present.
*/
- private static final class RequiresGeneratedCodeProcessor extends BasicAnnotationProcessor {
+ private static class RequiresGeneratedCodeProcessor extends BaseAnnotationProcessor {
int rejectedRounds;
- final ImmutableList.Builder<ImmutableSetMultimap<Class<? extends Annotation>, Element>>
- processArguments = ImmutableList.builder();
+ final ImmutableList.Builder<ImmutableSetMultimap<String, Element>> processArguments =
+ ImmutableList.builder();
@Override
- public SourceVersion getSupportedSourceVersion() {
- return SourceVersion.latestSupported();
- }
-
- @Override
- protected Iterable<? extends ProcessingStep> initSteps() {
+ protected Iterable<? extends Step> steps() {
return ImmutableSet.of(
- new ProcessingStep() {
+ new Step() {
@Override
- public Set<Element> process(
- SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
+ public ImmutableSet<? extends Element> process(
+ ImmutableSetMultimap<String, Element> elementsByAnnotation) {
processArguments.add(ImmutableSetMultimap.copyOf(elementsByAnnotation));
TypeElement requiredClass =
processingEnv.getElementUtils().getTypeElement("test.SomeGeneratedClass");
@@ -83,25 +96,25 @@ public class BasicAnnotationProcessorTest {
}
@Override
- public Set<? extends Class<? extends Annotation>> annotations() {
- return ImmutableSet.of(RequiresGeneratedCode.class);
+ public ImmutableSet<String> annotations() {
+ return ImmutableSet.of(ENCLOSING_CLASS_NAME + ".RequiresGeneratedCode");
}
},
- new ProcessingStep() {
+ new Step() {
@Override
- public Set<? extends Class<? extends Annotation>> annotations() {
- return ImmutableSet.of(AnAnnotation.class);
+ public ImmutableSet<? extends Element> process(
+ ImmutableSetMultimap<String, Element> elementsByAnnotation) {
+ return ImmutableSet.of();
}
@Override
- public Set<? extends Element> process(
- SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
- return ImmutableSet.of();
+ public ImmutableSet<String> annotations() {
+ return ImmutableSet.of(ENCLOSING_CLASS_NAME + ".AnAnnotation");
}
});
}
- ImmutableList<ImmutableSetMultimap<Class<? extends Annotation>, Element>> processArguments() {
+ ImmutableList<ImmutableSetMultimap<String, Element>> processArguments() {
return processArguments.build();
}
}
@@ -110,27 +123,21 @@ public class BasicAnnotationProcessorTest {
public @interface GeneratesCode {}
/** Generates a class called {@code test.SomeGeneratedClass}. */
- public class GeneratesCodeProcessor extends BasicAnnotationProcessor {
-
+ public static class GeneratesCodeProcessor extends BaseAnnotationProcessor {
@Override
- public SourceVersion getSupportedSourceVersion() {
- return SourceVersion.latestSupported();
- }
-
- @Override
- protected Iterable<? extends ProcessingStep> initSteps() {
+ protected Iterable<? extends Step> steps() {
return ImmutableSet.of(
- new ProcessingStep() {
+ new Step() {
@Override
- public Set<Element> process(
- SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
+ public ImmutableSet<? extends Element> process(
+ ImmutableSetMultimap<String, Element> elementsByAnnotation) {
generateClass(processingEnv.getFiler(), "SomeGeneratedClass");
return ImmutableSet.of();
}
@Override
- public Set<? extends Class<? extends Annotation>> annotations() {
- return ImmutableSet.of(GeneratesCode.class);
+ public ImmutableSet<String> annotations() {
+ return ImmutableSet.of(ENCLOSING_CLASS_NAME + ".GeneratesCode");
}
});
}
@@ -139,20 +146,15 @@ public class BasicAnnotationProcessorTest {
public @interface AnAnnotation {}
/** When annotating a type {@code Foo}, generates a class called {@code FooXYZ}. */
- public class AnAnnotationProcessor extends BasicAnnotationProcessor {
+ public static class AnAnnotationProcessor extends BaseAnnotationProcessor {
@Override
- public SourceVersion getSupportedSourceVersion() {
- return SourceVersion.latestSupported();
- }
-
- @Override
- protected Iterable<? extends ProcessingStep> initSteps() {
+ protected Iterable<? extends Step> steps() {
return ImmutableSet.of(
- new ProcessingStep() {
+ new Step() {
@Override
- public Set<Element> process(
- SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
+ public ImmutableSet<Element> process(
+ ImmutableSetMultimap<String, Element> elementsByAnnotation) {
for (Element element : elementsByAnnotation.values()) {
generateClass(processingEnv.getFiler(), element.getSimpleName() + "XYZ");
}
@@ -160,8 +162,8 @@ public class BasicAnnotationProcessorTest {
}
@Override
- public Set<? extends Class<? extends Annotation>> annotations() {
- return ImmutableSet.of(AnAnnotation.class);
+ public ImmutableSet<String> annotations() {
+ return ImmutableSet.of(ENCLOSING_CLASS_NAME + ".AnAnnotation");
}
});
}
@@ -171,19 +173,15 @@ public class BasicAnnotationProcessorTest {
public @interface CauseError {}
/** Report an error for any class annotated. */
- public static class CauseErrorProcessor extends BasicAnnotationProcessor {
- @Override
- public SourceVersion getSupportedSourceVersion() {
- return SourceVersion.latestSupported();
- }
+ public static class CauseErrorProcessor extends BaseAnnotationProcessor {
@Override
- protected Iterable<? extends ProcessingStep> initSteps() {
+ protected Iterable<? extends Step> steps() {
return ImmutableSet.of(
- new ProcessingStep() {
+ new Step() {
@Override
- public Set<Element> process(
- SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
+ public ImmutableSet<Element> process(
+ ImmutableSetMultimap<String, Element> elementsByAnnotation) {
for (Element e : elementsByAnnotation.values()) {
processingEnv.getMessager().printMessage(ERROR, "purposeful error", e);
}
@@ -191,26 +189,91 @@ public class BasicAnnotationProcessorTest {
}
@Override
- public Set<? extends Class<? extends Annotation>> annotations() {
- return ImmutableSet.of(CauseError.class);
+ public ImmutableSet<String> annotations() {
+ return ImmutableSet.of(ENCLOSING_CLASS_NAME + ".CauseError");
+ }
+ });
+ }
+ }
+
+ public static class MissingAnnotationProcessor extends BaseAnnotationProcessor {
+
+ private ImmutableSetMultimap<String, Element> elementsByAnnotation;
+
+ @Override
+ protected Iterable<? extends Step> steps() {
+ return ImmutableSet.of(
+ new Step() {
+ @Override
+ public ImmutableSet<Element> process(
+ ImmutableSetMultimap<String, Element> elementsByAnnotation) {
+ MissingAnnotationProcessor.this.elementsByAnnotation = elementsByAnnotation;
+ for (Element element : elementsByAnnotation.values()) {
+ generateClass(processingEnv.getFiler(), element.getSimpleName() + "XYZ");
+ }
+ return ImmutableSet.of();
+ }
+
+ @Override
+ public ImmutableSet<String> annotations() {
+ return ImmutableSet.of(
+ "test.SomeNonExistentClass", ENCLOSING_CLASS_NAME + ".AnAnnotation");
}
});
}
+
+ ImmutableSetMultimap<String, Element> getElementsByAnnotation() {
+ return elementsByAnnotation;
+ }
}
- @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 {}");
+ @SuppressWarnings("deprecation") // Deprecated ProcessingStep is being explicitly tested.
+ static final class MultiAnnotationProcessingStep implements ProcessingStep {
+
+ private SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation;
+
+ @Override
+ public ImmutableSet<? extends Class<? extends Annotation>> annotations() {
+ return ImmutableSet.of(AnAnnotation.class, ReferencesAClass.class);
+ }
+
+ @Override
+ public ImmutableSet<? extends Element> process(
+ SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
+ this.elementsByAnnotation = elementsByAnnotation;
+ return ImmutableSet.of();
+ }
+
+ SetMultimap<Class<? extends Annotation>, Element> getElementsByAnnotation() {
+ return elementsByAnnotation;
+ }
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ReferencesAClass {
+ Class<?> value();
+ }
+
+ @Rule public CompilationRule compilation = new CompilationRule();
+
+ @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())
@@ -244,22 +307,22 @@ public class BasicAnnotationProcessorTest {
.generatesFileNamed(SOURCE_OUTPUT, "test", "ValidInRound2XYZ.java");
}
- @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;");
+ @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())
@@ -272,21 +335,28 @@ public class BasicAnnotationProcessorTest {
assertThat(requiresGeneratedCodeProcessor.rejectedRounds).isEqualTo(0);
}
- @Test public void properlyDefersProcessing_argumentElement() {
- JavaFileObject classAFileObject = JavaFileObjects.forSourceLines("test.ClassA",
- "package test;",
- "",
- "public class ClassA {",
- " SomeGeneratedClass sgc;",
- " public void myMethod(@" + RequiresGeneratedCode.class.getCanonicalName() + " int myInt)",
- " {}",
- "}");
- JavaFileObject classBFileObject = JavaFileObjects.forSourceLines("test.ClassB",
- "package test;",
- "",
- "public class ClassB {",
- " public void myMethod(@" + GeneratesCode.class.getCanonicalName() + " int myInt) {}",
- "}");
+ @Test
+ public void properlyDefersProcessing_argumentElement() {
+ JavaFileObject classAFileObject =
+ JavaFileObjects.forSourceLines(
+ "test.ClassA",
+ "package test;",
+ "",
+ "public class ClassA {",
+ " SomeGeneratedClass sgc;",
+ " public void myMethod(@"
+ + RequiresGeneratedCode.class.getCanonicalName()
+ + " int myInt)",
+ " {}",
+ "}");
+ JavaFileObject classBFileObject =
+ JavaFileObjects.forSourceLines(
+ "test.ClassB",
+ "package test;",
+ "",
+ "public class ClassB {",
+ " public void myMethod(@" + GeneratesCode.class.getCanonicalName() + " int myInt) {}",
+ "}");
RequiresGeneratedCodeProcessor requiresGeneratedCodeProcessor =
new RequiresGeneratedCodeProcessor();
assertAbout(javaSources())
@@ -311,11 +381,13 @@ public class BasicAnnotationProcessorTest {
" @" + AnAnnotation.class.getCanonicalName(),
" public void method() {}",
"}");
- JavaFileObject classBFileObject = JavaFileObjects.forSourceLines("test.ClassB",
- "package test;",
- "",
- "@" + GeneratesCode.class.getCanonicalName(),
- "public class ClassB {}");
+ JavaFileObject classBFileObject =
+ JavaFileObjects.forSourceLines(
+ "test.ClassB",
+ "package test;",
+ "",
+ "@" + GeneratesCode.class.getCanonicalName(),
+ "public class ClassB {}");
RequiresGeneratedCodeProcessor requiresGeneratedCodeProcessor =
new RequiresGeneratedCodeProcessor();
assertAbout(javaSources())
@@ -332,8 +404,8 @@ public class BasicAnnotationProcessorTest {
assertThat(requiresGeneratedCodeProcessor.processArguments())
.comparingElementsUsing(setMultimapValuesByString())
.containsExactly(
- ImmutableSetMultimap.of(RequiresGeneratedCode.class, "test.ClassA"),
- ImmutableSetMultimap.of(RequiresGeneratedCode.class, "test.ClassA"))
+ ImmutableSetMultimap.of(RequiresGeneratedCode.class.getCanonicalName(), "test.ClassA"),
+ ImmutableSetMultimap.of(RequiresGeneratedCode.class.getCanonicalName(), "test.ClassA"))
.inOrder();
}
@@ -345,20 +417,60 @@ public class BasicAnnotationProcessorTest {
"is equivalent comparing multimap values by `toString()` to");
}
- @Test public void reportsMissingType() {
- JavaFileObject classAFileObject = JavaFileObjects.forSourceLines("test.ClassA",
- "package test;",
- "",
- "@" + RequiresGeneratedCode.class.getCanonicalName(),
- "public class ClassA {",
- " SomeGeneratedClass bar;",
- "}");
+ @Test
+ public void properlySkipsMissingAnnotations_generatesClass() {
+ JavaFileObject source =
+ JavaFileObjects.forSourceLines(
+ "test.ValidInRound2",
+ "package test;",
+ "",
+ "@" + AnAnnotation.class.getCanonicalName(),
+ "public class ValidInRound2 {",
+ " ValidInRound1XYZ vir1xyz;",
+ " @" + AnAnnotation.class.getCanonicalName(),
+ " static class ValidInRound1 {}",
+ "}");
+ Compilation compilation =
+ javac().withProcessors(new MissingAnnotationProcessor()).compile(source);
+ assertThat(compilation).succeeded();
+ assertThat(compilation).generatedSourceFile("test.ValidInRound2XYZ");
+ }
+
+ @Test
+ public void properlySkipsMissingAnnotations_passesValidAnnotationsToProcess() {
+ JavaFileObject source =
+ JavaFileObjects.forSourceLines(
+ "test.ClassA",
+ "package test;",
+ "",
+ "@" + AnAnnotation.class.getCanonicalName(),
+ "public class ClassA {",
+ "}");
+ MissingAnnotationProcessor missingAnnotationProcessor = new MissingAnnotationProcessor();
+ assertThat(javac().withProcessors(missingAnnotationProcessor).compile(source)).succeeded();
+ assertThat(missingAnnotationProcessor.getElementsByAnnotation().keySet())
+ .containsExactly(AnAnnotation.class.getCanonicalName());
+ assertThat(missingAnnotationProcessor.getElementsByAnnotation().values()).hasSize(1);
+ }
+
+ @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);
+ .in(classAFileObject)
+ .onLine(4);
}
@Test
@@ -378,6 +490,45 @@ public class BasicAnnotationProcessorTest {
.withErrorContaining("purposeful");
}
+ @Test
+ public void processingStepAsStepAnnotationsNamesMatchClasses() {
+ Step step = BasicAnnotationProcessor.asStep(new MultiAnnotationProcessingStep());
+
+ assertThat(step.annotations())
+ .containsExactly(
+ AnAnnotation.class.getCanonicalName(), ReferencesAClass.class.getCanonicalName());
+ }
+
+ /**
+ * Tests that a {@link ProcessingStep} passed to {@link
+ * BasicAnnotationProcessor#asStep(ProcessingStep)} still gets passed the correct arguments to
+ * {@link Step#process(ImmutableSetMultimap)}.
+ */
+ @Test
+ public void processingStepAsStepProcessElementsMatchClasses() {
+ Elements elements = compilation.getElements();
+ String anAnnotationName = AnAnnotation.class.getCanonicalName();
+ String referencesAClassName = ReferencesAClass.class.getCanonicalName();
+ TypeElement anAnnotationElement = elements.getTypeElement(anAnnotationName);
+ TypeElement referencesAClassElement = elements.getTypeElement(referencesAClassName);
+ MultiAnnotationProcessingStep processingStep = new MultiAnnotationProcessingStep();
+
+ BasicAnnotationProcessor.asStep(processingStep)
+ .process(
+ ImmutableSetMultimap.of(
+ anAnnotationName,
+ anAnnotationElement,
+ referencesAClassName,
+ referencesAClassElement));
+
+ assertThat(processingStep.getElementsByAnnotation())
+ .containsExactly(
+ AnAnnotation.class,
+ anAnnotationElement,
+ ReferencesAClass.class,
+ referencesAClassElement);
+ }
+
private static void generateClass(Filer filer, String generatedClassName) {
PrintWriter writer = null;
try {