From 45ef21f900baa4dcad269100553818d4839b8cfb Mon Sep 17 00:00:00 2001 From: Scott Frederick Date: Tue, 7 Jun 2022 16:51:17 -0700 Subject: [PATCH] Add support for annotation processors with TestCompiler Closes gh-28582 --- .../test/generator/compile/TestCompiler.java | 51 ++++++++++++++++--- .../generator/compile/TestCompilerTests.java | 50 +++++++++++++++++- 2 files changed, 92 insertions(+), 9 deletions(-) diff --git a/spring-core-test/src/main/java/org/springframework/aot/test/generator/compile/TestCompiler.java b/spring-core-test/src/main/java/org/springframework/aot/test/generator/compile/TestCompiler.java index d3dc16d393..ea59603fa0 100644 --- a/spring-core-test/src/main/java/org/springframework/aot/test/generator/compile/TestCompiler.java +++ b/spring-core-test/src/main/java/org/springframework/aot/test/generator/compile/TestCompiler.java @@ -18,10 +18,13 @@ package org.springframework.aot.test.generator.compile; import java.io.PrintStream; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.function.Consumer; +import javax.annotation.processing.Processor; import javax.tools.Diagnostic; import javax.tools.DiagnosticListener; import javax.tools.JavaCompiler; @@ -43,6 +46,7 @@ import org.springframework.lang.Nullable; * Utility that can be used to dynamically compile and test Java source code. * * @author Phillip Webb + * @author Scott Frederick * @since 6.0 * @see #forSystem() */ @@ -57,13 +61,18 @@ public final class TestCompiler { private final ResourceFiles resourceFiles; + private final List processors; + private TestCompiler(@Nullable ClassLoader classLoader, JavaCompiler compiler, - SourceFiles sourceFiles, ResourceFiles resourceFiles) { + SourceFiles sourceFiles, ResourceFiles resourceFiles, + List processors) { + this.classLoader = classLoader; this.compiler = compiler; this.sourceFiles = sourceFiles; this.resourceFiles = resourceFiles; + this.processors = processors; } @@ -83,7 +92,7 @@ public final class TestCompiler { */ public static TestCompiler forCompiler(JavaCompiler javaCompiler) { return new TestCompiler(null, javaCompiler, SourceFiles.none(), - ResourceFiles.none()); + ResourceFiles.none(), Collections.emptyList()); } /** @@ -110,7 +119,7 @@ public final class TestCompiler { */ public TestCompiler withSources(SourceFile... sourceFiles) { return new TestCompiler(this.classLoader, this.compiler, - this.sourceFiles.and(sourceFiles), this.resourceFiles); + this.sourceFiles.and(sourceFiles), this.resourceFiles, this.processors); } /** @@ -120,7 +129,7 @@ public final class TestCompiler { */ public TestCompiler withSources(Iterable sourceFiles) { return new TestCompiler(this.classLoader, this.compiler, - this.sourceFiles.and(sourceFiles), this.resourceFiles); + this.sourceFiles.and(sourceFiles), this.resourceFiles, this.processors); } /** @@ -130,7 +139,7 @@ public final class TestCompiler { */ public TestCompiler withSources(SourceFiles sourceFiles) { return new TestCompiler(this.classLoader, this.compiler, - this.sourceFiles.and(sourceFiles), this.resourceFiles); + this.sourceFiles.and(sourceFiles), this.resourceFiles, this.processors); } /** @@ -140,7 +149,7 @@ public final class TestCompiler { */ public TestCompiler withResources(ResourceFile... resourceFiles) { return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles, - this.resourceFiles.and(resourceFiles)); + this.resourceFiles.and(resourceFiles), this.processors); } /** @@ -150,7 +159,7 @@ public final class TestCompiler { */ public TestCompiler withResources(Iterable resourceFiles) { return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles, - this.resourceFiles.and(resourceFiles)); + this.resourceFiles.and(resourceFiles), this.processors); } /** @@ -160,9 +169,32 @@ public final class TestCompiler { */ public TestCompiler withResources(ResourceFiles resourceFiles) { return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles, - this.resourceFiles.and(resourceFiles)); + this.resourceFiles.and(resourceFiles), this.processors); } + /** + * Return a new {@link TestCompiler} instance with additional annotation processors. + * @param processors the additional annotation processors + * @return a new {@link TestCompiler} instance + */ + public TestCompiler withProcessors(Processor... processors) { + List mergedProcessors = new ArrayList<>(this.processors); + mergedProcessors.addAll(Arrays.asList(processors)); + return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles, + this.resourceFiles, mergedProcessors); + } + + /** + * Return a new {@link TestCompiler} instance with additional annotation processors. + * @param processors the additional annotation processors + * @return a new {@link TestCompiler} instance + */ + public TestCompiler withProcessors(Iterable processors) { + List mergedProcessors = new ArrayList<>(this.processors); + processors.forEach(mergedProcessors::add); + return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles, + this.resourceFiles, mergedProcessors); + } /** * Compile content from this instance along with the additional provided @@ -244,6 +276,9 @@ public final class TestCompiler { Errors errors = new Errors(); CompilationTask task = this.compiler.getTask(null, fileManager, errors, null, null, compilationUnits); + if (!this.processors.isEmpty()) { + task.setProcessors(this.processors); + } boolean result = task.call(); if (!result || errors.hasReportedErrors()) { throw new CompilationException(errors.toString(), this.sourceFiles, this.resourceFiles); diff --git a/spring-core-test/src/test/java/org/springframework/aot/test/generator/compile/TestCompilerTests.java b/spring-core-test/src/test/java/org/springframework/aot/test/generator/compile/TestCompilerTests.java index 0c2667644b..f808e8b4ee 100644 --- a/spring-core-test/src/test/java/org/springframework/aot/test/generator/compile/TestCompilerTests.java +++ b/spring-core-test/src/test/java/org/springframework/aot/test/generator/compile/TestCompilerTests.java @@ -16,8 +16,17 @@ package org.springframework.aot.test.generator.compile; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; import java.util.function.Supplier; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.Processor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.element.TypeElement; + import com.example.PublicInterface; import org.junit.jupiter.api.Test; @@ -33,9 +42,9 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; /** * Tests for {@link TestCompiler}. - * * @author Phillip Webb * @author Andy Wilkinson + * @author Scott Frederick */ class TestCompilerTests { @@ -140,6 +149,29 @@ class TestCompilerTests { this::assertHasResource); } + @Test + void withProcessorsArrayAddsProcessors() { + SourceFile sourceFile = SourceFile.of(HELLO_WORLD); + TestProcessor processor = new TestProcessor(); + TestCompiler.forSystem().withSources(sourceFile).withProcessors(processor).compile((compiled -> { + assertThat(processor.getProcessedAnnotations()).isNotEmpty(); + assertThat(processor.getProcessedAnnotations()).satisfiesExactly(element -> + assertThat(element.getQualifiedName().toString()).isEqualTo("java.lang.Deprecated")); + })); + } + + @Test + void withProcessorsAddsProcessors() { + SourceFile sourceFile = SourceFile.of(HELLO_WORLD); + TestProcessor processor = new TestProcessor(); + List processors = List.of(processor); + TestCompiler.forSystem().withSources(sourceFile).withProcessors(processors).compile((compiled -> { + assertThat(processor.getProcessedAnnotations()).isNotEmpty(); + assertThat(processor.getProcessedAnnotations()).satisfiesExactly(element -> + assertThat(element.getQualifiedName().toString()).isEqualTo("java.lang.Deprecated")); + })); + } + @Test void compileWithWritableContent() { WritableContent content = appendable -> appendable.append(HELLO_WORLD); @@ -217,4 +249,20 @@ class TestCompilerTests { "META-INF/myfile")).hasContent("test"); } + @SupportedAnnotationTypes("java.lang.Deprecated") + static class TestProcessor extends AbstractProcessor { + + private final List processedAnnotations = new ArrayList<>(); + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + this.processedAnnotations.addAll(annotations); + return true; + } + + public List getProcessedAnnotations() { + return this.processedAnnotations; + } + } + }