From d25a99692fbeeb77b28663483f6446b472e3fff1 Mon Sep 17 00:00:00 2001 From: Scott Frederick Date: Fri, 30 Sep 2022 15:58:11 -0500 Subject: [PATCH] Replace Spring Boot TestCompiler with Spring Framework's version See gh-31266 --- .../build.gradle | 1 + ...AutoConfigureAnnotationProcessorTests.java | 212 ++++++++++-------- .../TestAutoConfigureAnnotationProcessor.java | 26 +-- .../build.gradle | 1 + .../configurationprocessor/MetadataStore.java | 30 ++- .../AbstractMetadataGenerationTests.java | 64 +++--- ...ationMetadataAnnotationProcessorTests.java | 93 ++++---- .../EndpointMetadataGenerationTests.java | 21 +- ...crementalBuildMetadataGenerationTests.java | 34 +-- .../MergeMetadataGenerationTests.java | 115 ++++------ .../MethodBasedMetadataGenerationTests.java | 5 +- .../PropertyDescriptorResolverTests.java | 24 +- .../PropertyDescriptorTests.java | 23 +- .../configurationprocessor/TestProject.java | 155 ++++--------- .../TypeUtilsTests.java | 19 +- .../AbstractFieldValuesProcessorTests.java | 14 +- .../test/CompiledMetadataReader.java | 53 +++++ ...figurationMetadataAnnotationProcessor.java | 38 +--- .../spring-boot-loader/build.gradle | 1 + .../boot/loader/JarLauncherTests.java | 3 +- .../spring-boot-test-support/build.gradle | 1 + .../testsupport/compiler/TestCompiler.java | 5 +- spring-boot-project/spring-boot/build.gradle | 1 + .../bind/ValueObjectBinderTests.java | 46 ++-- 24 files changed, 464 insertions(+), 521 deletions(-) create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/test/CompiledMetadataReader.java diff --git a/spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/build.gradle b/spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/build.gradle index c131957b745..d9d8630ee5b 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/build.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/build.gradle @@ -12,5 +12,6 @@ dependencies { testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support")) testImplementation("org.assertj:assertj-core") testImplementation("org.springframework:spring-core") + testImplementation("org.springframework:spring-core-test") testImplementation("org.junit.jupiter:junit-jupiter") } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/AutoConfigureAnnotationProcessorTests.java b/spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/AutoConfigureAnnotationProcessorTests.java index 593bf2bbb1f..748de73df71 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/AutoConfigureAnnotationProcessorTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/AutoConfigureAnnotationProcessorTests.java @@ -16,145 +16,171 @@ package org.springframework.boot.autoconfigureprocessor; -import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.util.Properties; +import java.util.function.Consumer; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; -import org.springframework.boot.testsupport.compiler.TestCompiler; -import org.springframework.util.FileCopyUtils; +import org.springframework.core.test.tools.SourceFile; +import org.springframework.core.test.tools.TestCompiler; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; /** * Tests for {@link AutoConfigureAnnotationProcessor}. * * @author Madhura Bhave * @author Moritz Halbritter + * @author Scott Frederick */ class AutoConfigureAnnotationProcessorTests { - @TempDir - File tempDir; - - private TestCompiler compiler; - - @BeforeEach - void createCompiler() throws IOException { - this.compiler = new TestCompiler(this.tempDir); - } - @Test void annotatedClass() throws Exception { - Properties properties = compile(TestClassConfiguration.class); - assertThat(properties).hasSize(7); - assertThat(properties).containsEntry( - "org.springframework.boot.autoconfigureprocessor.TestClassConfiguration.ConditionalOnClass", - "java.io.InputStream,org.springframework.boot.autoconfigureprocessor." - + "TestClassConfiguration$Nested,org.springframework.foo"); - assertThat(properties).containsKey("org.springframework.boot.autoconfigureprocessor.TestClassConfiguration"); - assertThat(properties) - .containsKey("org.springframework.boot.autoconfigureprocessor.TestClassConfiguration$Nested"); - assertThat(properties).containsEntry( - "org.springframework.boot.autoconfigureprocessor.TestClassConfiguration.ConditionalOnBean", - "java.io.OutputStream"); - assertThat(properties).containsEntry("org.springframework.boot.autoconfigureprocessor." - + "TestClassConfiguration.ConditionalOnSingleCandidate", "java.io.OutputStream"); - assertThat(properties).containsEntry("org.springframework.boot.autoconfigureprocessor." - + "TestClassConfiguration.ConditionalOnWebApplication", "SERVLET"); + compile(TestClassConfiguration.class, (properties) -> { + assertThat(properties).hasSize(7); + assertThat(properties).containsEntry( + "org.springframework.boot.autoconfigureprocessor.TestClassConfiguration.ConditionalOnClass", + "java.io.InputStream,org.springframework.boot.autoconfigureprocessor." + + "TestClassConfiguration$Nested,org.springframework.foo"); + assertThat(properties) + .containsKey("org.springframework.boot.autoconfigureprocessor.TestClassConfiguration"); + assertThat(properties) + .containsKey("org.springframework.boot.autoconfigureprocessor.TestClassConfiguration$Nested"); + assertThat(properties).containsEntry( + "org.springframework.boot.autoconfigureprocessor.TestClassConfiguration.ConditionalOnBean", + "java.io.OutputStream"); + assertThat(properties).containsEntry("org.springframework.boot.autoconfigureprocessor." + + "TestClassConfiguration.ConditionalOnSingleCandidate", "java.io.OutputStream"); + assertThat(properties).containsEntry("org.springframework.boot.autoconfigureprocessor." + + "TestClassConfiguration.ConditionalOnWebApplication", "SERVLET"); + }); } @Test - void annotatedClassWithOnlyAutoConfiguration() throws Exception { - Properties properties = compile(TestAutoConfigurationOnlyConfiguration.class); - assertThat(properties).containsEntry( - "org.springframework.boot.autoconfigureprocessor.TestAutoConfigurationOnlyConfiguration", ""); - assertThat(properties).doesNotContainEntry( - "org.springframework.boot.autoconfigureprocessor.TestAutoConfigurationOnlyConfiguration.AutoConfigureAfter", - ""); - assertThat(properties).doesNotContainEntry( - "org.springframework.boot.autoconfigureprocessor.TestAutoConfigurationOnlyConfiguration.AutoConfigureBefore", - ""); + void annotatedClassWithOnlyAutoConfiguration() { + compile(TestAutoConfigurationOnlyConfiguration.class, (properties) -> { + assertThat(properties).containsEntry( + "org.springframework.boot.autoconfigureprocessor.TestAutoConfigurationOnlyConfiguration", ""); + assertThat(properties).doesNotContainEntry( + "org.springframework.boot.autoconfigureprocessor.TestAutoConfigurationOnlyConfiguration.AutoConfigureAfter", + ""); + assertThat(properties).doesNotContainEntry( + "org.springframework.boot.autoconfigureprocessor.TestAutoConfigurationOnlyConfiguration.AutoConfigureBefore", + ""); + }); } @Test - void annotatedClassWithOnBeanThatHasName() throws Exception { - Properties properties = compile(TestOnBeanWithNameClassConfiguration.class); - assertThat(properties).hasSize(2); - assertThat(properties).containsEntry( - "org.springframework.boot.autoconfigureprocessor.TestOnBeanWithNameClassConfiguration.ConditionalOnBean", - ""); + void annotatedClassWithOnBeanThatHasName() { + compile(TestOnBeanWithNameClassConfiguration.class, (properties) -> { + assertThat(properties).hasSize(2); + assertThat(properties).containsEntry( + "org.springframework.boot.autoconfigureprocessor.TestOnBeanWithNameClassConfiguration.ConditionalOnBean", + ""); + }); } @Test - void annotatedMethod() throws Exception { - Properties properties = compile(TestMethodConfiguration.class); - assertThat(properties).isNull(); + void annotatedMethod() { + process(TestMethodConfiguration.class, (properties) -> assertThat(properties).isNull()); } @Test - void annotatedClassWithOrder() throws Exception { - Properties properties = compile(TestOrderedClassConfiguration.class); - assertThat(properties).containsEntry( - "org.springframework.boot.autoconfigureprocessor.TestOrderedClassConfiguration.ConditionalOnClass", - "java.io.InputStream,java.io.OutputStream"); - assertThat(properties).containsEntry("org.springframework.boot.autoconfigureprocessor." - + "TestOrderedClassConfiguration.AutoConfigureBefore", "test.before1,test.before2"); - assertThat(properties).containsEntry( - "org.springframework.boot.autoconfigureprocessor.TestOrderedClassConfiguration.AutoConfigureAfter", - "java.io.ObjectInputStream"); - assertThat(properties).containsEntry( - "org.springframework.boot.autoconfigureprocessor.TestOrderedClassConfiguration.AutoConfigureOrder", - "123"); + void annotatedClassWithOrder() { + compile(TestOrderedClassConfiguration.class, (properties) -> { + assertThat(properties).containsEntry( + "org.springframework.boot.autoconfigureprocessor.TestOrderedClassConfiguration.ConditionalOnClass", + "java.io.InputStream,java.io.OutputStream"); + assertThat(properties).containsEntry("org.springframework.boot.autoconfigureprocessor." + + "TestOrderedClassConfiguration.AutoConfigureBefore", "test.before1,test.before2"); + assertThat(properties).containsEntry( + "org.springframework.boot.autoconfigureprocessor.TestOrderedClassConfiguration.AutoConfigureAfter", + "java.io.ObjectInputStream"); + assertThat(properties).containsEntry( + "org.springframework.boot.autoconfigureprocessor.TestOrderedClassConfiguration.AutoConfigureOrder", + "123"); + }); + } @Test void annotatedClassWithAutoConfiguration() throws Exception { - Properties properties = compile(TestAutoConfigurationConfiguration.class); - assertThat(properties).containsEntry( - "org.springframework.boot.autoconfigureprocessor.TestAutoConfigurationConfiguration", ""); - assertThat(properties).containsEntry( - "org.springframework.boot.autoconfigureprocessor.TestAutoConfigurationConfiguration.AutoConfigureBefore", - "java.io.InputStream,test.before1,test.before2"); - assertThat(properties).containsEntry( - "org.springframework.boot.autoconfigureprocessor.TestAutoConfigurationConfiguration.AutoConfigureAfter", - "java.io.OutputStream,test.after1,test.after2"); + compile(TestAutoConfigurationConfiguration.class, (properties) -> { + assertThat(properties).containsEntry( + "org.springframework.boot.autoconfigureprocessor.TestAutoConfigurationConfiguration", ""); + assertThat(properties).containsEntry( + "org.springframework.boot.autoconfigureprocessor.TestAutoConfigurationConfiguration.AutoConfigureBefore", + "java.io.InputStream,test.before1,test.before2"); + assertThat(properties).containsEntry( + "org.springframework.boot.autoconfigureprocessor.TestAutoConfigurationConfiguration.AutoConfigureAfter", + "java.io.OutputStream,test.after1,test.after2"); + }); } @Test void annotatedClassWithAutoConfigurationMerged() throws Exception { - Properties properties = compile(TestMergedAutoConfigurationConfiguration.class); - assertThat(properties).containsEntry( - "org.springframework.boot.autoconfigureprocessor.TestMergedAutoConfigurationConfiguration", ""); - assertThat(properties).containsEntry( - "org.springframework.boot.autoconfigureprocessor.TestMergedAutoConfigurationConfiguration.AutoConfigureBefore", - "java.io.InputStream,test.before1,test.before2,java.io.ObjectInputStream,test.before3,test.before4"); - assertThat(properties).containsEntry( - "org.springframework.boot.autoconfigureprocessor.TestMergedAutoConfigurationConfiguration.AutoConfigureAfter", - "java.io.OutputStream,test.after1,test.after2,java.io.ObjectOutputStream,test.after3,test.after4"); + compile(TestMergedAutoConfigurationConfiguration.class, (properties) -> { + assertThat(properties).containsEntry( + "org.springframework.boot.autoconfigureprocessor.TestMergedAutoConfigurationConfiguration", ""); + assertThat(properties).containsEntry( + "org.springframework.boot.autoconfigureprocessor.TestMergedAutoConfigurationConfiguration.AutoConfigureBefore", + "java.io.InputStream,test.before1,test.before2,java.io.ObjectInputStream,test.before3,test.before4"); + assertThat(properties).containsEntry( + "org.springframework.boot.autoconfigureprocessor.TestMergedAutoConfigurationConfiguration.AutoConfigureAfter", + "java.io.OutputStream,test.after1,test.after2,java.io.ObjectOutputStream,test.after3,test.after4"); + }); } @Test // gh-19370 void propertiesAreFullRepeatable() throws Exception { - String first = new String( - FileCopyUtils.copyToByteArray(process(TestOrderedClassConfiguration.class).getWrittenFile())); - String second = new String( - FileCopyUtils.copyToByteArray(process(TestOrderedClassConfiguration.class).getWrittenFile())); - assertThat(first).isEqualTo(second).doesNotContain("#"); + process(TestOrderedClassConfiguration.class, (firstFile) -> { + String first = getFileContents(firstFile); + process(TestOrderedClassConfiguration.class, (secondFile) -> { + String second = getFileContents(secondFile); + assertThat(first).isEqualTo(second).doesNotContain("#"); + }); + }); } - private Properties compile(Class... types) throws IOException { - return process(types).getWrittenProperties(); + private void compile(Class type, Consumer consumer) { + process(type, (writtenFile) -> consumer.accept(getWrittenProperties(writtenFile))); } - private TestAutoConfigureAnnotationProcessor process(Class... types) { - TestAutoConfigureAnnotationProcessor processor = new TestAutoConfigureAnnotationProcessor( - this.compiler.getOutputLocation()); - this.compiler.getTask(types).call(processor); - return processor; + private void process(Class type, Consumer consumer) { + TestAutoConfigureAnnotationProcessor processor = new TestAutoConfigureAnnotationProcessor(); + SourceFile sourceFile = SourceFile.forTestClass(type); + TestCompiler compiler = TestCompiler.forSystem().withProcessors(processor).withSources(sourceFile); + compiler.compile((compiled) -> { + InputStream propertiesFile = compiled.getClassLoader() + .getResourceAsStream(AutoConfigureAnnotationProcessor.PROPERTIES_PATH); + consumer.accept(propertiesFile); + }); + } + + private Properties getWrittenProperties(InputStream inputStream) { + try { + Properties properties = new Properties(); + properties.load(inputStream); + return properties; + } + catch (IOException ex) { + fail("Error reading properties", ex); + } + return null; + } + + private String getFileContents(InputStream inputStream) { + try { + return new String(inputStream.readAllBytes()); + } + catch (IOException ex) { + fail("Error reading contents of properties file", ex); + } + return null; } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestAutoConfigureAnnotationProcessor.java b/spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestAutoConfigureAnnotationProcessor.java index 36cfba8ab90..0214f8f74e0 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestAutoConfigureAnnotationProcessor.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestAutoConfigureAnnotationProcessor.java @@ -16,12 +16,8 @@ package org.springframework.boot.autoconfigureprocessor; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Properties; import javax.annotation.processing.SupportedAnnotationTypes; @@ -29,6 +25,7 @@ import javax.annotation.processing.SupportedAnnotationTypes; * Version of {@link AutoConfigureAnnotationProcessor} used for testing. * * @author Madhura Bhave + * @author Scott Frederick */ @SupportedAnnotationTypes({ "org.springframework.boot.autoconfigureprocessor.TestConditionalOnClass", "org.springframework.boot.autoconfigureprocessor.TestConditionalOnBean", @@ -40,10 +37,7 @@ import javax.annotation.processing.SupportedAnnotationTypes; "org.springframework.boot.autoconfigureprocessor.TestAutoConfiguration" }) public class TestAutoConfigureAnnotationProcessor extends AutoConfigureAnnotationProcessor { - private final File outputLocation; - - public TestAutoConfigureAnnotationProcessor(File outputLocation) { - this.outputLocation = outputLocation; + public TestAutoConfigureAnnotationProcessor() { } @Override @@ -69,20 +63,4 @@ public class TestAutoConfigureAnnotationProcessor extends AutoConfigureAnnotatio return generators; } - public Properties getWrittenProperties() throws IOException { - File file = getWrittenFile(); - if (!file.exists()) { - return null; - } - try (FileInputStream inputStream = new FileInputStream(file)) { - Properties properties = new Properties(); - properties.load(inputStream); - return properties; - } - } - - public File getWrittenFile() { - return new File(this.outputLocation, PROPERTIES_PATH); - } - } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/build.gradle b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/build.gradle index 684cae0a342..43a7ac73f2d 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/build.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/build.gradle @@ -19,6 +19,7 @@ dependencies { testCompileOnly("com.google.code.findbugs:jsr305:3.0.2") testImplementation(enforcedPlatform(project(":spring-boot-project:spring-boot-dependencies"))) testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support")) + testImplementation("org.springframework:spring-core-test") testImplementation("jakarta.validation:jakarta.validation-api") testImplementation("org.assertj:assertj-core") testImplementation("org.hamcrest:hamcrest-library") diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/MetadataStore.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/MetadataStore.java index c44724f9b11..73c4f3259b3 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/MetadataStore.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/MetadataStore.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,6 +36,7 @@ import org.springframework.boot.configurationprocessor.metadata.JsonMarshaller; * A {@code MetadataStore} is responsible for the storage of metadata on the filesystem. * * @author Andy Wilkinson + * @author Scott Frederick * @since 1.2.2 */ public class MetadataStore { @@ -76,7 +77,7 @@ public class MetadataStore { } private ConfigurationMetadata readMetadata(InputStream in) throws IOException { - try { + try (in) { return new JsonMarshaller().read(in); } catch (IOException ex) { @@ -87,9 +88,6 @@ public class MetadataStore { "Invalid additional meta-data in '" + METADATA_PATH + "': " + ex.getMessage(), Diagnostic.Kind.ERROR); } - finally { - in.close(); - } } private FileObject getMetadataResource() throws IOException { @@ -104,8 +102,26 @@ public class MetadataStore { // Most build systems will have copied the file to the class output location FileObject fileObject = this.environment.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", ADDITIONAL_METADATA_PATH); - File file = locateAdditionalMetadataFile(new File(fileObject.toUri())); - return (file.exists() ? new FileInputStream(file) : fileObject.toUri().toURL().openStream()); + InputStream inputStream = getMetadataStream(fileObject); + if (inputStream != null) { + return inputStream; + } + try { + File file = locateAdditionalMetadataFile(new File(fileObject.toUri())); + return (file.exists() ? new FileInputStream(file) : fileObject.toUri().toURL().openStream()); + } + catch (Exception ex) { + throw new FileNotFoundException(); + } + } + + private InputStream getMetadataStream(FileObject fileObject) { + try { + return fileObject.openInputStream(); + } + catch (IOException ex) { + return null; + } } File locateAdditionalMetadataFile(File standardLocation) throws IOException { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/AbstractMetadataGenerationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/AbstractMetadataGenerationTests.java index 8cd35bd6fa1..bfdb5bf382a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/AbstractMetadataGenerationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/AbstractMetadataGenerationTests.java @@ -16,50 +16,58 @@ package org.springframework.boot.configurationprocessor; -import java.io.File; -import java.io.IOException; import java.util.Arrays; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.io.TempDir; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata; +import org.springframework.boot.configurationprocessor.test.CompiledMetadataReader; import org.springframework.boot.configurationprocessor.test.TestConfigurationMetadataAnnotationProcessor; -import org.springframework.boot.testsupport.compiler.TestCompiler; +import org.springframework.core.test.tools.ResourceFile; +import org.springframework.core.test.tools.SourceFile; +import org.springframework.core.test.tools.TestCompiler; /** * Base test infrastructure for metadata generation tests. * * @author Stephane Nicoll + * @author Scott Frederick */ public abstract class AbstractMetadataGenerationTests { - @TempDir - File tempDir; - - private TestCompiler compiler; - - @BeforeEach - void createCompiler() throws IOException { - this.compiler = new TestCompiler(this.tempDir); - } - - protected TestCompiler getCompiler() { - return this.compiler; - } + private static final String ADDITIONAL_METADATA_FILE = "META-INF/additional-spring-configuration-metadata.json"; protected ConfigurationMetadata compile(Class... types) { - TestConfigurationMetadataAnnotationProcessor processor = new TestConfigurationMetadataAnnotationProcessor( - this.compiler.getOutputLocation()); - this.compiler.getTask(types).call(processor); - return processor.getMetadata(); + TestCompiler compiler = TestCompiler.forSystem().withSources(sourceFilesOf(types)); + return compile(compiler); } - protected ConfigurationMetadata compile(File... sources) { - TestConfigurationMetadataAnnotationProcessor processor = new TestConfigurationMetadataAnnotationProcessor( - this.compiler.getOutputLocation()); - this.compiler.getTask(Arrays.asList(sources)).call(processor); - return processor.getMetadata(); + protected ConfigurationMetadata compile(String additionalMetadata, Class type, Class... types) { + TestCompiler compiler = TestCompiler.forSystem().withSources(sourceFilesOf(type)) + .withSources(sourceFilesOf(types)) + .withResources(ResourceFile.of(ADDITIONAL_METADATA_FILE, additionalMetadata)); + return compile(compiler); + } + + protected ConfigurationMetadata compile(String... source) { + TestCompiler compiler = TestCompiler.forSystem().withSources(sourceFilesOf(source)); + return compile(compiler); + } + + private ConfigurationMetadata compile(TestCompiler compiler) { + TestConfigurationMetadataAnnotationProcessor processor = new TestConfigurationMetadataAnnotationProcessor(); + compiler = compiler.withProcessors(processor); + AtomicReference configurationMetadata = new AtomicReference<>(); + compiler.compile((compiled) -> configurationMetadata.set(CompiledMetadataReader.getMetadata(compiled))); + return configurationMetadata.get(); + } + + private List sourceFilesOf(Class... types) { + return Arrays.stream(types).map(SourceFile::forTestClass).toList(); + } + + private List sourceFilesOf(String... content) { + return Arrays.stream(content).map(SourceFile::of).toList(); } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java index c6630e9527e..a140709db05 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java @@ -16,13 +16,9 @@ package org.springframework.boot.configurationprocessor; -import java.io.File; -import java.io.FileWriter; import java.io.IOException; -import java.io.PrintWriter; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata; import org.springframework.boot.configurationprocessor.metadata.ItemMetadata; @@ -60,9 +56,10 @@ import org.springframework.boot.configurationsample.specific.InvalidDefaultValue import org.springframework.boot.configurationsample.specific.InvalidDoubleRegistrationProperties; import org.springframework.boot.configurationsample.specific.SimplePojo; import org.springframework.boot.configurationsample.specific.StaticAccessor; +import org.springframework.core.test.tools.CompilationException; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; /** * Tests for {@link ConfigurationMetadataAnnotationProcessor}. @@ -73,6 +70,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException; * @author Kris De Volder * @author Jonas Keßler * @author Pavel Anisimov + * @author Scott Frederick */ class ConfigurationMetadataAnnotationProcessorTests extends AbstractMetadataGenerationTests { @@ -374,26 +372,30 @@ class ConfigurationMetadataAnnotationProcessorTests extends AbstractMetadataGene @Test void invalidDoubleRegistration() { - assertThatIllegalStateException().isThrownBy(() -> compile(InvalidDoubleRegistrationProperties.class)) - .withMessageContaining("Compilation failed"); + assertThatExceptionOfType(CompilationException.class) + .isThrownBy(() -> compile(InvalidDoubleRegistrationProperties.class)) + .withMessageContaining("Unable to compile source"); } @Test void constructorParameterPropertyWithInvalidDefaultValueOnNumber() { - assertThatIllegalStateException().isThrownBy(() -> compile(InvalidDefaultValueNumberProperties.class)) - .withMessageContaining("Compilation failed"); + assertThatExceptionOfType(CompilationException.class) + .isThrownBy(() -> compile(InvalidDefaultValueNumberProperties.class)) + .withMessageContaining("Unable to compile source"); } @Test void constructorParameterPropertyWithInvalidDefaultValueOnFloatingPoint() { - assertThatIllegalStateException().isThrownBy(() -> compile(InvalidDefaultValueFloatingPointProperties.class)) - .withMessageContaining("Compilation failed"); + assertThatExceptionOfType(CompilationException.class) + .isThrownBy(() -> compile(InvalidDefaultValueFloatingPointProperties.class)) + .withMessageContaining("Unable to compile source"); } @Test void constructorParameterPropertyWithInvalidDefaultValueOnCharacter() { - assertThatIllegalStateException().isThrownBy(() -> compile(InvalidDefaultValueCharacterProperties.class)) - .withMessageContaining("Compilation failed"); + assertThatExceptionOfType(CompilationException.class) + .isThrownBy(() -> compile(InvalidDefaultValueCharacterProperties.class)) + .withMessageContaining("Unable to compile source"); } @Test @@ -411,31 +413,27 @@ class ConfigurationMetadataAnnotationProcessorTests extends AbstractMetadataGene } @Test - void recordProperties(@TempDir File temp) throws IOException { - File exampleRecord = new File(temp, "ExampleRecord.java"); - try (PrintWriter writer = new PrintWriter(new FileWriter(exampleRecord))) { - writer.println("@org.springframework.boot.configurationsample.ConfigurationProperties(\"implicit\")"); - writer.println("public record ExampleRecord(String someString, Integer someInteger) {"); - writer.println("}"); - } - ConfigurationMetadata metadata = compile(exampleRecord); + void recordProperties() throws IOException { + String source = """ + @org.springframework.boot.configurationsample.ConfigurationProperties("implicit") + public record ExampleRecord(String someString, Integer someInteger) { + } + """; + ConfigurationMetadata metadata = compile(source); assertThat(metadata).has(Metadata.withProperty("implicit.some-string")); assertThat(metadata).has(Metadata.withProperty("implicit.some-integer")); } @Test - void recordPropertiesWithDefaultValues(@TempDir File temp) throws IOException { - File exampleRecord = new File(temp, "ExampleRecord.java"); - try (PrintWriter writer = new PrintWriter(new FileWriter(exampleRecord))) { - writer.println( - "@org.springframework.boot.configurationsample.ConfigurationProperties(\"record.defaults\")"); - writer.println("public record ExampleRecord("); - writer.println("@org.springframework.boot.configurationsample.DefaultValue(\"An1s9n\") String someString,"); - writer.println("@org.springframework.boot.configurationsample.DefaultValue(\"594\") Integer someInteger"); - writer.println(") {"); - writer.println("}"); - } - ConfigurationMetadata metadata = compile(exampleRecord); + void recordPropertiesWithDefaultValues() throws IOException { + String source = """ + @org.springframework.boot.configurationsample.ConfigurationProperties("record.defaults") + public record ExampleRecord( + @org.springframework.boot.configurationsample.DefaultValue("An1s9n") String someString, + @org.springframework.boot.configurationsample.DefaultValue("594") Integer someInteger) { + } + """; + ConfigurationMetadata metadata = compile(source); assertThat(metadata) .has(Metadata.withProperty("record.defaults.some-string", String.class).withDefaultValue("An1s9n")); assertThat(metadata) @@ -443,21 +441,20 @@ class ConfigurationMetadataAnnotationProcessorTests extends AbstractMetadataGene } @Test - void multiConstructorRecordProperties(@TempDir File temp) throws IOException { - File exampleRecord = new File(temp, "ExampleRecord.java"); - try (PrintWriter writer = new PrintWriter(new FileWriter(exampleRecord))) { - writer.println("@org.springframework.boot.configurationsample.ConfigurationProperties(\"multi\")"); - writer.println("public record ExampleRecord(String someString, Integer someInteger) {"); - writer.println(" @org.springframework.boot.configurationsample.ConstructorBinding"); - writer.println(" public ExampleRecord(String someString) {"); - writer.println(" this(someString, 42);"); - writer.println(" }"); - writer.println(" public ExampleRecord(Integer someInteger) {"); - writer.println(" this(\"someString\", someInteger);"); - writer.println(" }"); - writer.println("}"); - } - ConfigurationMetadata metadata = compile(exampleRecord); + void multiConstructorRecordProperties() throws IOException { + String source = """ + @org.springframework.boot.configurationsample.ConfigurationProperties("multi") + public record ExampleRecord(String someString, Integer someInteger) { + @org.springframework.boot.configurationsample.ConstructorBinding + public ExampleRecord(String someString) { + this(someString, 42); + } + public ExampleRecord(Integer someInteger) { + this("someString", someInteger); + } + } + """; + ConfigurationMetadata metadata = compile(source); assertThat(metadata).has(Metadata.withProperty("multi.some-string")); assertThat(metadata).doesNotHave(Metadata.withProperty("multi.some-integer")); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/EndpointMetadataGenerationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/EndpointMetadataGenerationTests.java index 09c5f03d8fa..86e810497e7 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/EndpointMetadataGenerationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/EndpointMetadataGenerationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,6 +36,7 @@ import static org.assertj.core.api.Assertions.assertThat; * Metadata generation tests for Actuator endpoints. * * @author Stephane Nicoll + * @author Scott Frederick */ class EndpointMetadataGenerationTests extends AbstractMetadataGenerationTests { @@ -96,8 +97,8 @@ class EndpointMetadataGenerationTests extends AbstractMetadataGenerationTests { @Test void incrementalEndpointBuildChangeGeneralEnabledFlag() throws Exception { - TestProject project = new TestProject(this.tempDir, IncrementalEndpoint.class); - ConfigurationMetadata metadata = project.fullBuild(); + TestProject project = new TestProject(IncrementalEndpoint.class); + ConfigurationMetadata metadata = project.compile(); assertThat(metadata) .has(Metadata.withGroup("management.endpoint.incremental").fromSource(IncrementalEndpoint.class)); assertThat(metadata).has(enabledFlag("incremental", true)); @@ -105,7 +106,7 @@ class EndpointMetadataGenerationTests extends AbstractMetadataGenerationTests { assertThat(metadata.getItems()).hasSize(3); project.replaceText(IncrementalEndpoint.class, "id = \"incremental\"", "id = \"incremental\", enableByDefault = false"); - metadata = project.incrementalBuild(IncrementalEndpoint.class); + metadata = project.compile(); assertThat(metadata) .has(Metadata.withGroup("management.endpoint.incremental").fromSource(IncrementalEndpoint.class)); assertThat(metadata).has(enabledFlag("incremental", false)); @@ -115,15 +116,15 @@ class EndpointMetadataGenerationTests extends AbstractMetadataGenerationTests { @Test void incrementalEndpointBuildChangeCacheFlag() throws Exception { - TestProject project = new TestProject(this.tempDir, IncrementalEndpoint.class); - ConfigurationMetadata metadata = project.fullBuild(); + TestProject project = new TestProject(IncrementalEndpoint.class); + ConfigurationMetadata metadata = project.compile(); assertThat(metadata) .has(Metadata.withGroup("management.endpoint.incremental").fromSource(IncrementalEndpoint.class)); assertThat(metadata).has(enabledFlag("incremental", true)); assertThat(metadata).has(cacheTtl("incremental")); assertThat(metadata.getItems()).hasSize(3); project.replaceText(IncrementalEndpoint.class, "@Nullable String param", "String param"); - metadata = project.incrementalBuild(IncrementalEndpoint.class); + metadata = project.compile(); assertThat(metadata) .has(Metadata.withGroup("management.endpoint.incremental").fromSource(IncrementalEndpoint.class)); assertThat(metadata).has(enabledFlag("incremental", true)); @@ -132,14 +133,14 @@ class EndpointMetadataGenerationTests extends AbstractMetadataGenerationTests { @Test void incrementalEndpointBuildEnableSpecificEndpoint() throws Exception { - TestProject project = new TestProject(this.tempDir, SpecificEndpoint.class); - ConfigurationMetadata metadata = project.fullBuild(); + TestProject project = new TestProject(SpecificEndpoint.class); + ConfigurationMetadata metadata = project.compile(); assertThat(metadata).has(Metadata.withGroup("management.endpoint.specific").fromSource(SpecificEndpoint.class)); assertThat(metadata).has(enabledFlag("specific", true)); assertThat(metadata).has(cacheTtl("specific")); assertThat(metadata.getItems()).hasSize(3); project.replaceText(SpecificEndpoint.class, "enableByDefault = true", "enableByDefault = false"); - metadata = project.incrementalBuild(SpecificEndpoint.class); + metadata = project.compile(); assertThat(metadata).has(Metadata.withGroup("management.endpoint.specific").fromSource(SpecificEndpoint.class)); assertThat(metadata).has(enabledFlag("specific", false)); assertThat(metadata).has(cacheTtl("specific")); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/IncrementalBuildMetadataGenerationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/IncrementalBuildMetadataGenerationTests.java index 150e9b4813c..d47d0728410 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/IncrementalBuildMetadataGenerationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/IncrementalBuildMetadataGenerationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,31 +32,30 @@ import static org.assertj.core.api.Assertions.assertThat; * Metadata generation tests for incremental builds. * * @author Stephane Nicoll + * @author Scott Frederick */ class IncrementalBuildMetadataGenerationTests extends AbstractMetadataGenerationTests { @Test void incrementalBuild() throws Exception { - TestProject project = new TestProject(this.tempDir, FooProperties.class, BarProperties.class); - assertThat(project.getOutputFile(MetadataStore.METADATA_PATH).exists()).isFalse(); - ConfigurationMetadata metadata = project.fullBuild(); - assertThat(project.getOutputFile(MetadataStore.METADATA_PATH).exists()).isTrue(); + TestProject project = new TestProject(FooProperties.class, BarProperties.class); + ConfigurationMetadata metadata = project.compile(); assertThat(metadata) .has(Metadata.withProperty("foo.counter").fromSource(FooProperties.class).withDefaultValue(0)); assertThat(metadata) .has(Metadata.withProperty("bar.counter").fromSource(BarProperties.class).withDefaultValue(0)); - metadata = project.incrementalBuild(BarProperties.class); + metadata = project.compile(); assertThat(metadata) .has(Metadata.withProperty("foo.counter").fromSource(FooProperties.class).withDefaultValue(0)); assertThat(metadata) .has(Metadata.withProperty("bar.counter").fromSource(BarProperties.class).withDefaultValue(0)); project.addSourceCode(BarProperties.class, BarProperties.class.getResourceAsStream("BarProperties.snippet")); - metadata = project.incrementalBuild(BarProperties.class); + metadata = project.compile(); assertThat(metadata).has(Metadata.withProperty("bar.extra")); assertThat(metadata).has(Metadata.withProperty("foo.counter").withDefaultValue(0)); assertThat(metadata).has(Metadata.withProperty("bar.counter").withDefaultValue(0)); project.revert(BarProperties.class); - metadata = project.incrementalBuild(BarProperties.class); + metadata = project.compile(); assertThat(metadata).isNotEqualTo(Metadata.withProperty("bar.extra")); assertThat(metadata).has(Metadata.withProperty("foo.counter").withDefaultValue(0)); assertThat(metadata).has(Metadata.withProperty("bar.counter").withDefaultValue(0)); @@ -64,20 +63,21 @@ class IncrementalBuildMetadataGenerationTests extends AbstractMetadataGeneration @Test void incrementalBuildAnnotationRemoved() throws Exception { - TestProject project = new TestProject(this.tempDir, FooProperties.class, BarProperties.class); - ConfigurationMetadata metadata = project.fullBuild(); + TestProject project = new TestProject(FooProperties.class, BarProperties.class); + ConfigurationMetadata metadata = project.compile(); assertThat(metadata).has(Metadata.withProperty("foo.counter").withDefaultValue(0)); assertThat(metadata).has(Metadata.withProperty("bar.counter").withDefaultValue(0)); project.replaceText(BarProperties.class, "@ConfigurationProperties", "//@ConfigurationProperties"); - metadata = project.incrementalBuild(BarProperties.class); + project.replaceText(FooProperties.class, "@ConfigurationProperties", "//@ConfigurationProperties"); + metadata = project.compile(); assertThat(metadata).isNull(); } @Test @Disabled("gh-26271") void incrementalBuildTypeRenamed() throws Exception { - TestProject project = new TestProject(this.tempDir, FooProperties.class, BarProperties.class); - ConfigurationMetadata metadata = project.fullBuild(); + TestProject project = new TestProject(FooProperties.class, BarProperties.class); + ConfigurationMetadata metadata = project.compile(); assertThat(metadata) .has(Metadata.withProperty("foo.counter").fromSource(FooProperties.class).withDefaultValue(0)); assertThat(metadata) @@ -85,7 +85,7 @@ class IncrementalBuildMetadataGenerationTests extends AbstractMetadataGeneration assertThat(metadata).doesNotHave(Metadata.withProperty("bar.counter").fromSource(RenamedBarProperties.class)); project.delete(BarProperties.class); project.add(RenamedBarProperties.class); - metadata = project.incrementalBuild(RenamedBarProperties.class); + metadata = project.compile(); assertThat(metadata) .has(Metadata.withProperty("foo.counter").fromSource(FooProperties.class).withDefaultValue(0)); assertThat(metadata) @@ -96,9 +96,9 @@ class IncrementalBuildMetadataGenerationTests extends AbstractMetadataGeneration @Test void incrementalBuildDoesNotDeleteItems() throws Exception { - TestProject project = new TestProject(this.tempDir, ClassWithNestedProperties.class, FooProperties.class); - ConfigurationMetadata initialMetadata = project.fullBuild(); - ConfigurationMetadata updatedMetadata = project.incrementalBuild(FooProperties.class); + TestProject project = new TestProject(ClassWithNestedProperties.class, FooProperties.class); + ConfigurationMetadata initialMetadata = project.compile(); + ConfigurationMetadata updatedMetadata = project.compile(); assertThat(initialMetadata.getItems()).isEqualTo(updatedMetadata.getItems()); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/MergeMetadataGenerationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/MergeMetadataGenerationTests.java index 7b439437c80..573e8add84c 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/MergeMetadataGenerationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/MergeMetadataGenerationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,6 @@ package org.springframework.boot.configurationprocessor; -import java.io.File; -import java.io.FileWriter; import java.io.IOException; import java.util.Arrays; import java.util.Collections; @@ -37,15 +35,16 @@ import org.springframework.boot.configurationprocessor.metadata.TestJsonConverte import org.springframework.boot.configurationsample.simple.DeprecatedSingleProperty; import org.springframework.boot.configurationsample.simple.SimpleProperties; import org.springframework.boot.configurationsample.specific.SimpleConflictingProperties; -import org.springframework.util.FileCopyUtils; +import org.springframework.core.test.tools.CompilationException; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; /** * Metadata generation tests for merging additional metadata. * * @author Stephane Nicoll + * @author Scott Frederick */ class MergeMetadataGenerationTests extends AbstractMetadataGenerationTests { @@ -53,8 +52,8 @@ class MergeMetadataGenerationTests extends AbstractMetadataGenerationTests { void mergingOfAdditionalProperty() throws Exception { ItemMetadata property = ItemMetadata.newProperty(null, "foo", "java.lang.String", AdditionalMetadata.class.getName(), null, null, null, null); - writeAdditionalMetadata(property); - ConfigurationMetadata metadata = compile(SimpleProperties.class); + String additionalMetadata = buildAdditionalMetadata(property); + ConfigurationMetadata metadata = compile(additionalMetadata, SimpleProperties.class); assertThat(metadata).has(Metadata.withProperty("simple.comparator")); assertThat(metadata).has(Metadata.withProperty("foo", String.class).fromSource(AdditionalMetadata.class)); } @@ -63,8 +62,8 @@ class MergeMetadataGenerationTests extends AbstractMetadataGenerationTests { void mergingOfAdditionalPropertyMatchingGroup() throws Exception { ItemMetadata property = ItemMetadata.newProperty(null, "simple", "java.lang.String", null, null, null, null, null); - writeAdditionalMetadata(property); - ConfigurationMetadata metadata = compile(SimpleProperties.class); + String additionalMetadata = buildAdditionalMetadata(property); + ConfigurationMetadata metadata = compile(additionalMetadata, SimpleProperties.class); assertThat(metadata).has(Metadata.withGroup("simple").fromSource(SimpleProperties.class)); assertThat(metadata).has(Metadata.withProperty("simple", String.class)); } @@ -72,8 +71,8 @@ class MergeMetadataGenerationTests extends AbstractMetadataGenerationTests { @Test void mergeExistingPropertyDefaultValue() throws Exception { ItemMetadata property = ItemMetadata.newProperty("simple", "flag", null, null, null, null, true, null); - writeAdditionalMetadata(property); - ConfigurationMetadata metadata = compile(SimpleProperties.class); + String additionalMetadata = buildAdditionalMetadata(property); + ConfigurationMetadata metadata = compile(additionalMetadata, SimpleProperties.class); assertThat(metadata).has(Metadata.withProperty("simple.flag", Boolean.class).fromSource(SimpleProperties.class) .withDescription("A simple flag.").withDeprecation(null, null).withDefaultValue(true)); assertThat(metadata.getItems()).hasSize(4); @@ -83,8 +82,9 @@ class MergeMetadataGenerationTests extends AbstractMetadataGenerationTests { void mergeExistingPropertyWithSeveralCandidates() throws Exception { ItemMetadata property = ItemMetadata.newProperty("simple", "flag", Boolean.class.getName(), null, null, null, true, null); - writeAdditionalMetadata(property); - ConfigurationMetadata metadata = compile(SimpleProperties.class, SimpleConflictingProperties.class); + String additionalMetadata = buildAdditionalMetadata(property); + ConfigurationMetadata metadata = compile(additionalMetadata, SimpleProperties.class, + SimpleConflictingProperties.class); assertThat(metadata.getItems()).hasSize(6); List items = metadata.getItems().stream().filter((item) -> item.getName().equals("simple.flag")) .collect(Collectors.toList()); @@ -107,8 +107,8 @@ class MergeMetadataGenerationTests extends AbstractMetadataGenerationTests { void mergeExistingPropertyDescription() throws Exception { ItemMetadata property = ItemMetadata.newProperty("simple", "comparator", null, null, null, "A nice comparator.", null, null); - writeAdditionalMetadata(property); - ConfigurationMetadata metadata = compile(SimpleProperties.class); + String additionalMetadata = buildAdditionalMetadata(property); + ConfigurationMetadata metadata = compile(additionalMetadata, SimpleProperties.class); assertThat(metadata).has(Metadata.withProperty("simple.comparator", "java.util.Comparator") .fromSource(SimpleProperties.class).withDescription("A nice comparator.")); assertThat(metadata.getItems()).hasSize(4); @@ -118,8 +118,8 @@ class MergeMetadataGenerationTests extends AbstractMetadataGenerationTests { void mergeExistingPropertyDeprecation() throws Exception { ItemMetadata property = ItemMetadata.newProperty("simple", "comparator", null, null, null, null, null, new ItemDeprecation("Don't use this.", "simple.complex-comparator", "error")); - writeAdditionalMetadata(property); - ConfigurationMetadata metadata = compile(SimpleProperties.class); + String additionalMetadata = buildAdditionalMetadata(property); + ConfigurationMetadata metadata = compile(additionalMetadata, SimpleProperties.class); assertThat(metadata).has( Metadata.withProperty("simple.comparator", "java.util.Comparator").fromSource(SimpleProperties.class) .withDeprecation("Don't use this.", "simple.complex-comparator", "error")); @@ -130,8 +130,8 @@ class MergeMetadataGenerationTests extends AbstractMetadataGenerationTests { void mergeExistingPropertyDeprecationOverride() throws Exception { ItemMetadata property = ItemMetadata.newProperty("singledeprecated", "name", null, null, null, null, null, new ItemDeprecation("Don't use this.", "single.name")); - writeAdditionalMetadata(property); - ConfigurationMetadata metadata = compile(DeprecatedSingleProperty.class); + String additionalMetadata = buildAdditionalMetadata(property); + ConfigurationMetadata metadata = compile(additionalMetadata, DeprecatedSingleProperty.class); assertThat(metadata).has(Metadata.withProperty("singledeprecated.name", String.class.getName()) .fromSource(DeprecatedSingleProperty.class).withDeprecation("Don't use this.", "single.name")); assertThat(metadata.getItems()).hasSize(3); @@ -141,8 +141,8 @@ class MergeMetadataGenerationTests extends AbstractMetadataGenerationTests { void mergeExistingPropertyDeprecationOverrideLevel() throws Exception { ItemMetadata property = ItemMetadata.newProperty("singledeprecated", "name", null, null, null, null, null, new ItemDeprecation(null, null, "error")); - writeAdditionalMetadata(property); - ConfigurationMetadata metadata = compile(DeprecatedSingleProperty.class); + String additionalMetadata = buildAdditionalMetadata(property); + ConfigurationMetadata metadata = compile(additionalMetadata, DeprecatedSingleProperty.class); assertThat(metadata).has(Metadata.withProperty("singledeprecated.name", String.class.getName()) .fromSource(DeprecatedSingleProperty.class) .withDeprecation("renamed", "singledeprecated.new-name", "error")); @@ -151,17 +151,17 @@ class MergeMetadataGenerationTests extends AbstractMetadataGenerationTests { @Test void mergeOfInvalidAdditionalMetadata() throws IOException { - File additionalMetadataFile = createAdditionalMetadataFile(); - FileCopyUtils.copy("Hello World", new FileWriter(additionalMetadataFile)); - assertThatIllegalStateException().isThrownBy(() -> compile(SimpleProperties.class)) - .withMessage("Compilation failed"); + String metadata = "Hello World"; + assertThatExceptionOfType(CompilationException.class) + .isThrownBy(() -> compile(metadata, SimpleProperties.class)) + .withMessageContaining("Invalid additional meta-data"); } @Test void mergingOfSimpleHint() throws Exception { - writeAdditionalHints(ItemHint.newHint("simple.the-name", new ItemHint.ValueHint("boot", "Bla bla"), - new ItemHint.ValueHint("spring", null))); - ConfigurationMetadata metadata = compile(SimpleProperties.class); + String hints = buildAdditionalHints(ItemHint.newHint("simple.the-name", + new ItemHint.ValueHint("boot", "Bla bla"), new ItemHint.ValueHint("spring", null))); + ConfigurationMetadata metadata = compile(hints, SimpleProperties.class); assertThat(metadata).has(Metadata.withProperty("simple.the-name", String.class) .fromSource(SimpleProperties.class).withDescription("The name of this simple properties.") .withDefaultValue("boot").withDeprecation(null, null)); @@ -171,8 +171,9 @@ class MergeMetadataGenerationTests extends AbstractMetadataGenerationTests { @Test void mergingOfHintWithNonCanonicalName() throws Exception { - writeAdditionalHints(ItemHint.newHint("simple.theName", new ItemHint.ValueHint("boot", "Bla bla"))); - ConfigurationMetadata metadata = compile(SimpleProperties.class); + String hints = buildAdditionalHints( + ItemHint.newHint("simple.theName", new ItemHint.ValueHint("boot", "Bla bla"))); + ConfigurationMetadata metadata = compile(hints, SimpleProperties.class); assertThat(metadata).has(Metadata.withProperty("simple.the-name", String.class) .fromSource(SimpleProperties.class).withDescription("The name of this simple properties.") .withDefaultValue("boot").withDeprecation(null, null)); @@ -181,10 +182,10 @@ class MergeMetadataGenerationTests extends AbstractMetadataGenerationTests { @Test void mergingOfHintWithProvider() throws Exception { - writeAdditionalHints(new ItemHint("simple.theName", Collections.emptyList(), + String hints = buildAdditionalHints(new ItemHint("simple.theName", Collections.emptyList(), Arrays.asList(new ItemHint.ValueProvider("first", Collections.singletonMap("target", "org.foo")), new ItemHint.ValueProvider("second", null)))); - ConfigurationMetadata metadata = compile(SimpleProperties.class); + ConfigurationMetadata metadata = compile(hints, SimpleProperties.class); assertThat(metadata).has(Metadata.withProperty("simple.the-name", String.class) .fromSource(SimpleProperties.class).withDescription("The name of this simple properties.") .withDefaultValue("boot").withDeprecation(null, null)); @@ -194,58 +195,48 @@ class MergeMetadataGenerationTests extends AbstractMetadataGenerationTests { @Test void mergingOfAdditionalDeprecation() throws Exception { - writePropertyDeprecation(ItemMetadata.newProperty("simple", "wrongName", "java.lang.String", null, null, null, - null, new ItemDeprecation("Lame name.", "simple.the-name"))); - ConfigurationMetadata metadata = compile(SimpleProperties.class); + String deprecations = buildPropertyDeprecations(ItemMetadata.newProperty("simple", "wrongName", + "java.lang.String", null, null, null, null, new ItemDeprecation("Lame name.", "simple.the-name"))); + ConfigurationMetadata metadata = compile(deprecations, SimpleProperties.class); assertThat(metadata).has(Metadata.withProperty("simple.wrong-name", String.class).withDeprecation("Lame name.", "simple.the-name")); } @Test void mergingOfAdditionalMetadata() throws Exception { - File metaInfDirectory = new File(getCompiler().getOutputLocation(), "META-INF"); - metaInfDirectory.mkdirs(); - File additionalMetadataFile = new File(metaInfDirectory, "additional-spring-configuration-metadata.json"); - additionalMetadataFile.createNewFile(); JSONObject property = new JSONObject(); property.put("name", "foo"); property.put("type", "java.lang.String"); property.put("sourceType", AdditionalMetadata.class.getName()); JSONArray properties = new JSONArray(); properties.put(property); - JSONObject additionalMetadata = new JSONObject(); - additionalMetadata.put("properties", properties); - FileWriter writer = new FileWriter(additionalMetadataFile); - writer.append(additionalMetadata.toString(2)); - writer.flush(); - writer.close(); - ConfigurationMetadata metadata = compile(SimpleProperties.class); + JSONObject json = new JSONObject(); + json.put("properties", properties); + String additionalMetadata = json.toString(); + ConfigurationMetadata metadata = compile(additionalMetadata, SimpleProperties.class); assertThat(metadata).has(Metadata.withProperty("simple.comparator")); assertThat(metadata).has(Metadata.withProperty("foo", String.class).fromSource(AdditionalMetadata.class)); } - private void writeAdditionalMetadata(ItemMetadata... metadata) throws Exception { + private String buildAdditionalMetadata(ItemMetadata... metadata) throws Exception { TestJsonConverter converter = new TestJsonConverter(); - File additionalMetadataFile = createAdditionalMetadataFile(); JSONObject additionalMetadata = new JSONObject(); JSONArray properties = new JSONArray(); for (ItemMetadata itemMetadata : metadata) { properties.put(converter.toJsonObject(itemMetadata)); } additionalMetadata.put("properties", properties); - writeMetadata(additionalMetadataFile, additionalMetadata); + return additionalMetadata.toString(); } - private void writeAdditionalHints(ItemHint... hints) throws Exception { + private String buildAdditionalHints(ItemHint... hints) throws Exception { TestJsonConverter converter = new TestJsonConverter(); - File additionalMetadataFile = createAdditionalMetadataFile(); JSONObject additionalMetadata = new JSONObject(); additionalMetadata.put("hints", converter.toJsonArray(Arrays.asList(hints))); - writeMetadata(additionalMetadataFile, additionalMetadata); + return additionalMetadata.toString(); } - private void writePropertyDeprecation(ItemMetadata... items) throws Exception { - File additionalMetadataFile = createAdditionalMetadataFile(); + private String buildPropertyDeprecations(ItemMetadata... items) throws Exception { JSONArray propertiesArray = new JSONArray(); for (ItemMetadata item : items) { JSONObject jsonObject = new JSONObject(); @@ -269,21 +260,7 @@ class MergeMetadataGenerationTests extends AbstractMetadataGenerationTests { } JSONObject additionalMetadata = new JSONObject(); additionalMetadata.put("properties", propertiesArray); - writeMetadata(additionalMetadataFile, additionalMetadata); - } - - private File createAdditionalMetadataFile() throws IOException { - File metaInfDirectory = new File(getCompiler().getOutputLocation(), "META-INF"); - metaInfDirectory.mkdirs(); - File additionalMetadataFile = new File(metaInfDirectory, "additional-spring-configuration-metadata.json"); - additionalMetadataFile.createNewFile(); - return additionalMetadataFile; - } - - private void writeMetadata(File metadataFile, JSONObject metadata) throws Exception { - try (FileWriter writer = new FileWriter(metadataFile)) { - writer.append(metadata.toString(2)); - } + return additionalMetadata.toString(); } static class AdditionalMetadata { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/MethodBasedMetadataGenerationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/MethodBasedMetadataGenerationTests.java index 5aa5d34ae68..a1f25d3633d 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/MethodBasedMetadataGenerationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/MethodBasedMetadataGenerationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,6 +36,7 @@ import static org.assertj.core.api.Assertions.assertThat; * Metadata generation tests for types defined by {@code @Bean} methods. * * @author Stephane Nicoll + * @author Scott Frederick */ class MethodBasedMetadataGenerationTests extends AbstractMetadataGenerationTests { @@ -65,7 +66,7 @@ class MethodBasedMetadataGenerationTests extends AbstractMetadataGenerationTests @Test void privateMethodConfig() { ConfigurationMetadata metadata = compile(PrivateMethodConfig.class); - assertThat(metadata).doesNotHave(Metadata.withGroup("foo")); + assertThat(metadata).isNull(); } @Test diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/PropertyDescriptorResolverTests.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/PropertyDescriptorResolverTests.java index e8004b1ce1a..4383b100248 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/PropertyDescriptorResolverTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/PropertyDescriptorResolverTests.java @@ -16,12 +16,11 @@ package org.springframework.boot.configurationprocessor; -import java.io.File; import java.io.IOException; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.stream.Stream; @@ -29,7 +28,6 @@ import java.util.stream.Stream; import javax.lang.model.element.TypeElement; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; import org.springframework.boot.configurationprocessor.metadata.ItemMetadata; import org.springframework.boot.configurationprocessor.test.RoundEnvironmentTester; @@ -49,7 +47,8 @@ import org.springframework.boot.configurationsample.simple.HierarchicalPropertie import org.springframework.boot.configurationsample.simple.HierarchicalPropertiesParent; import org.springframework.boot.configurationsample.simple.SimpleProperties; import org.springframework.boot.configurationsample.specific.TwoConstructorsExample; -import org.springframework.boot.testsupport.compiler.TestCompiler; +import org.springframework.core.test.tools.SourceFile; +import org.springframework.core.test.tools.TestCompiler; import static org.assertj.core.api.Assertions.assertThat; @@ -57,12 +56,10 @@ import static org.assertj.core.api.Assertions.assertThat; * Tests for {@link PropertyDescriptorResolver}. * * @author Stephane Nicoll + * @author Scott Frederick */ class PropertyDescriptorResolverTests { - @TempDir - File tempDir; - @Test void propertiesWithJavaBeanProperties() throws IOException { process(SimpleProperties.class, @@ -181,7 +178,7 @@ class PropertyDescriptorResolverTests { } private void process(Class target, Collection> additionalClasses, - BiConsumer consumer) throws IOException { + BiConsumer consumer) { BiConsumer internalConsumer = (roundEnv, metadataEnv) -> { TypeElement element = roundEnv.getRootElement(target); @@ -189,11 +186,12 @@ class PropertyDescriptorResolverTests { }; TestableAnnotationProcessor processor = new TestableAnnotationProcessor<>( internalConsumer, new MetadataGenerationEnvironmentFactory()); - TestCompiler compiler = new TestCompiler(this.tempDir); - ArrayList> allClasses = new ArrayList<>(); - allClasses.add(target); - allClasses.addAll(additionalClasses); - compiler.getTask(allClasses.toArray(new Class[0])).call(processor); + SourceFile targetSource = SourceFile.forTestClass(target); + List additionalSource = additionalClasses.stream().map(SourceFile::forTestClass).toList(); + TestCompiler compiler = TestCompiler.forSystem().withProcessors(processor).withSources(targetSource) + .withSources(additionalSource); + compiler.compile((compiled) -> { + }); } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/PropertyDescriptorTests.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/PropertyDescriptorTests.java index e1e885297ba..cb6e8384992 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/PropertyDescriptorTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/PropertyDescriptorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,6 @@ package org.springframework.boot.configurationprocessor; -import java.io.File; -import java.io.IOException; import java.util.function.BiConsumer; import javax.lang.model.element.Element; @@ -26,23 +24,20 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.util.ElementFilter; -import org.junit.jupiter.api.io.TempDir; - import org.springframework.boot.configurationprocessor.test.ItemMetadataAssert; import org.springframework.boot.configurationprocessor.test.RoundEnvironmentTester; import org.springframework.boot.configurationprocessor.test.TestableAnnotationProcessor; -import org.springframework.boot.testsupport.compiler.TestCompiler; +import org.springframework.core.test.tools.SourceFile; +import org.springframework.core.test.tools.TestCompiler; /** * Base test infrastructure to test {@link PropertyDescriptor} implementations. * * @author Stephane Nicoll + * @author Scott Frederick */ public abstract class PropertyDescriptorTests { - @TempDir - File tempDir; - protected String createAccessorMethodName(String prefix, String name) { char[] chars = name.toCharArray(); chars[0] = Character.toUpperCase(chars[0]); @@ -66,12 +61,14 @@ public abstract class PropertyDescriptorTests { return new ItemMetadataAssert(property.resolveItemMetadata("test", metadataEnv)); } - protected void process(Class target, BiConsumer consumer) - throws IOException { + protected void process(Class target, + BiConsumer consumer) { TestableAnnotationProcessor processor = new TestableAnnotationProcessor<>( consumer, new MetadataGenerationEnvironmentFactory()); - TestCompiler compiler = new TestCompiler(this.tempDir); - compiler.getTask(target).call(processor); + TestCompiler compiler = TestCompiler.forSystem().withProcessors(processor) + .withSources(SourceFile.forTestClass(target)); + compiler.compile((compiled) -> { + }); } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/TestProject.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/TestProject.java index c8442e974ce..312ef288fb2 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/TestProject.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/TestProject.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,27 +16,22 @@ package org.springframework.boot.configurationprocessor; -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.StringReader; import java.util.Arrays; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Set; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata; +import org.springframework.boot.configurationprocessor.test.CompiledMetadataReader; import org.springframework.boot.configurationprocessor.test.TestConfigurationMetadataAnnotationProcessor; import org.springframework.boot.configurationsample.ConfigurationProperties; import org.springframework.boot.configurationsample.NestedConfigurationProperty; -import org.springframework.boot.testsupport.compiler.TestCompiler; -import org.springframework.boot.testsupport.compiler.TestCompiler.TestCompilationTask; +import org.springframework.core.test.tools.SourceFile; +import org.springframework.core.test.tools.SourceFiles; +import org.springframework.core.test.tools.TestCompiler; import org.springframework.util.Assert; import org.springframework.util.FileCopyUtils; -import org.springframework.util.FileSystemUtils; /** * A TestProject contains a copy of a subset of test sample code. @@ -46,85 +41,26 @@ import org.springframework.util.FileSystemUtils; * original content itself. * * @author Kris De Volder + * @author Scott Frederick */ public class TestProject { private static final Class[] ALWAYS_INCLUDE = { ConfigurationProperties.class, NestedConfigurationProperty.class }; - /** - * Contains copies of the original source so we can modify it safely to test - * incremental builds. - */ - private File sourceDirectory; + private SourceFiles sources; - private TestCompiler compiler; - - private Set sourceFiles = new LinkedHashSet<>(); - - public TestProject(File tempDirectory, Class... classes) throws IOException { - this.sourceDirectory = new File(tempDirectory, "src"); - this.compiler = new TestCompiler(new File(tempDirectory, "build")) { - - @Override - protected File getSourceDirectory() { - return TestProject.this.sourceDirectory; - } - - }; - Set> contents = new HashSet<>(Arrays.asList(classes)); - contents.addAll(Arrays.asList(ALWAYS_INCLUDE)); - copySources(contents); + public TestProject(Class... classes) { + this.sources = SourceFiles.none().and(sourceFilesOf(ALWAYS_INCLUDE)).and(sourceFilesOf(classes)); } - private void copySources(Set> contents) throws IOException { - for (Class type : contents) { - copySources(type); - } - } - - private void copySources(Class type) throws IOException { - File original = getOriginalSourceFile(type); - File target = getSourceFile(type); - target.getParentFile().mkdirs(); - FileCopyUtils.copy(original, target); - this.sourceFiles.add(target); - } - - public File getSourceFile(Class type) { - return new File(this.sourceDirectory, TestCompiler.sourcePathFor(type)); - } - - public ConfigurationMetadata fullBuild() { - TestConfigurationMetadataAnnotationProcessor processor = new TestConfigurationMetadataAnnotationProcessor( - this.compiler.getOutputLocation()); - TestCompilationTask task = this.compiler.getTask(this.sourceFiles); - deleteDirectoryContents(this.compiler.getOutputLocation()); - task.call(processor); - return processor.getMetadata(); - } - - public ConfigurationMetadata incrementalBuild(Class... toRecompile) { - TestConfigurationMetadataAnnotationProcessor processor = new TestConfigurationMetadataAnnotationProcessor( - this.compiler.getOutputLocation()); - TestCompilationTask task = this.compiler.getTask(toRecompile); - task.call(processor); - return processor.getMetadata(); - } - - private void deleteDirectoryContents(File outputDirectory) { - FileSystemUtils.deleteRecursively(outputDirectory); - outputDirectory.mkdirs(); - } - - /** - * Retrieve File relative to project's output directory. - * @param relativePath the relative path - * @return the output file - */ - public File getOutputFile(String relativePath) { - Assert.isTrue(!new File(relativePath).isAbsolute(), "'" + relativePath + "' was absolute"); - return new File(this.compiler.getOutputLocation(), relativePath); + public ConfigurationMetadata compile() { + TestConfigurationMetadataAnnotationProcessor processor = new TestConfigurationMetadataAnnotationProcessor(); + TestCompiler compiler = TestCompiler.forSystem().withProcessors(processor); + AtomicReference configurationMetadata = new AtomicReference<>(); + compiler.compile(this.sources, + (compiled) -> configurationMetadata.set(CompiledMetadataReader.getMetadata(compiled))); + return configurationMetadata.get(); } /** @@ -134,12 +70,12 @@ public class TestProject { * @throws Exception if the source cannot be added */ public void addSourceCode(Class target, InputStream snippetStream) throws Exception { - File targetFile = getSourceFile(target); - String contents = getContents(targetFile); + SourceFile sourceFile = SourceFile.forTestClass(target); + String contents = sourceFile.getContent(); int insertAt = contents.lastIndexOf('}'); String additionalSource = FileCopyUtils.copyToString(new InputStreamReader(snippetStream)); contents = contents.substring(0, insertAt) + additionalSource + contents.substring(insertAt); - putContents(targetFile, contents); + this.sources = this.sources.and(SourceFile.of(contents)); } /** @@ -147,53 +83,40 @@ public class TestProject { * @param type the class to delete */ public void delete(Class type) { - File target = getSourceFile(type); - target.delete(); - this.sourceFiles.remove(target); + SourceFile[] newSources = this.sources.stream() + .filter((sourceFile) -> !sourceFile.getPath().equals(SourceFile.forTestClass(type).getPath())) + .toArray(SourceFile[]::new); + this.sources = SourceFiles.of(newSources); } /** * Restore source code of given class to its original contents. * @param type the class to revert - * @throws IOException on IO error */ - public void revert(Class type) throws IOException { - Assert.isTrue(getSourceFile(type).exists(), "Source file for type '" + type + "' does not exist"); - copySources(type); + public void revert(Class type) { + Assert.isTrue(this.sources.stream().anyMatch((sourceFile) -> sourceFile.getClassName().equals(type.getName())), + "Source file for type '" + type + "' does not exist"); + this.sources = this.sources.and(SourceFile.forTestClass(type)); } /** * Add source code of given class to this project. * @param type the class to add - * @throws IOException on IO error */ - public void add(Class type) throws IOException { - Assert.isTrue(!getSourceFile(type).exists(), "Source file for type '" + type + "' already exists"); - copySources(type); + public void add(Class type) { + Assert.isTrue(this.sources.stream().noneMatch((sourceFile) -> sourceFile.getClassName().equals(type.getName())), + "Source file for type '" + type + "' already exists"); + this.sources = this.sources.and(SourceFile.forTestClass(type)); } - public void replaceText(Class type, String find, String replace) throws Exception { - File target = getSourceFile(type); - String contents = getContents(target); - contents = contents.replace(find, replace); - putContents(target, contents); + public void replaceText(Class type, String find, String replace) { + SourceFile sourceFile = SourceFile.forTestClass(type); + String contents = sourceFile.getContent().replace(find, replace); + this.sources = this.sources.and(SourceFile.of(contents)); } - /** - * Find the 'original' source code for given test class. Clients or subclasses should - * have no need to know about these. They should work only with the copied source - * code. - */ - private File getOriginalSourceFile(Class type) { - return new File(TestCompiler.SOURCE_DIRECTORY, TestCompiler.sourcePathFor(type)); - } - - private static void putContents(File targetFile, String contents) throws IOException { - FileCopyUtils.copy(new StringReader(contents), new FileWriter(targetFile)); - } - - private static String getContents(File file) throws Exception { - return FileCopyUtils.copyToString(new FileReader(file)); + private List sourceFilesOf(Class... types) { + return Arrays.stream(types).map(SourceFile::forTestClass).toList(); } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/TypeUtilsTests.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/TypeUtilsTests.java index 4bc54fef592..4686bfec26e 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/TypeUtilsTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/TypeUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +16,11 @@ package org.springframework.boot.configurationprocessor; -import java.io.File; import java.io.IOException; import java.time.Duration; import java.util.function.BiConsumer; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; import org.springframework.boot.configurationprocessor.TypeUtils.TypeDescriptor; import org.springframework.boot.configurationprocessor.test.RoundEnvironmentTester; @@ -30,7 +28,8 @@ import org.springframework.boot.configurationprocessor.test.TestableAnnotationPr import org.springframework.boot.configurationsample.generic.AbstractGenericProperties; import org.springframework.boot.configurationsample.generic.AbstractIntermediateGenericProperties; import org.springframework.boot.configurationsample.generic.SimpleGenericProperties; -import org.springframework.boot.testsupport.compiler.TestCompiler; +import org.springframework.core.test.tools.SourceFile; +import org.springframework.core.test.tools.TestCompiler; import static org.assertj.core.api.Assertions.assertThat; @@ -38,12 +37,10 @@ import static org.assertj.core.api.Assertions.assertThat; * Tests for {@link TypeUtils}. * * @author Stephane Nicoll + * @author Scott Frederick */ class TypeUtilsTests { - @TempDir - File tempDir; - @Test void resolveTypeDescriptorOnConcreteClass() throws IOException { process(SimpleGenericProperties.class, (roundEnv, typeUtils) -> { @@ -82,10 +79,12 @@ class TypeUtilsTests { }); } - private void process(Class target, BiConsumer consumer) throws IOException { + private void process(Class target, BiConsumer consumer) { TestableAnnotationProcessor processor = new TestableAnnotationProcessor<>(consumer, TypeUtils::new); - TestCompiler compiler = new TestCompiler(this.tempDir); - compiler.getTask(target).call(processor); + TestCompiler compiler = TestCompiler.forSystem().withProcessors(processor) + .withSources(SourceFile.forTestClass(target)); + compiler.compile((compiled) -> { + }); } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/fieldvalues/AbstractFieldValuesProcessorTests.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/fieldvalues/AbstractFieldValuesProcessorTests.java index 47938fee856..2c27e48c6e4 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/fieldvalues/AbstractFieldValuesProcessorTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/fieldvalues/AbstractFieldValuesProcessorTests.java @@ -16,7 +16,6 @@ package org.springframework.boot.configurationprocessor.fieldvalues; -import java.io.File; import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -31,10 +30,10 @@ import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; import org.springframework.boot.configurationsample.fieldvalues.FieldValues; -import org.springframework.boot.testsupport.compiler.TestCompiler; +import org.springframework.core.test.tools.SourceFile; +import org.springframework.core.test.tools.TestCompiler; import static org.assertj.core.api.Assertions.assertThat; @@ -46,16 +45,15 @@ import static org.assertj.core.api.Assertions.assertThat; */ public abstract class AbstractFieldValuesProcessorTests { - @TempDir - File tempDir; - protected abstract FieldValuesParser createProcessor(ProcessingEnvironment env); @Test void getFieldValues() throws Exception { TestProcessor processor = new TestProcessor(); - TestCompiler compiler = new TestCompiler(this.tempDir); - compiler.getTask(FieldValues.class).call(processor); + TestCompiler compiler = TestCompiler.forSystem().withProcessors(processor) + .withSources(SourceFile.forTestClass(FieldValues.class)); + compiler.compile((compiled) -> { + }); Map values = processor.getValues(); assertThat(values.get("string")).isEqualTo("1"); assertThat(values.get("stringNone")).isNull(); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/test/CompiledMetadataReader.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/test/CompiledMetadataReader.java new file mode 100644 index 00000000000..9091fac9e74 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/test/CompiledMetadataReader.java @@ -0,0 +1,53 @@ +/* + * Copyright 2012-2022 the original author or authors. + * + * 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 + * + * https://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 org.springframework.boot.configurationprocessor.test; + +import java.io.InputStream; + +import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata; +import org.springframework.boot.configurationprocessor.metadata.JsonMarshaller; +import org.springframework.core.test.tools.Compiled; +import org.springframework.core.test.tools.TestCompiler; + +/** + * Read the contents of metadata generated from the {@link TestCompiler}. + * + * @author Scott Frederick + */ +public final class CompiledMetadataReader { + + private static final String METADATA_FILE = "META-INF/spring-configuration-metadata.json"; + + private CompiledMetadataReader() { + } + + public static ConfigurationMetadata getMetadata(Compiled compiled) { + InputStream inputStream = compiled.getClassLoader().getResourceAsStream(METADATA_FILE); + try { + if (inputStream != null) { + return new JsonMarshaller().read(inputStream); + } + else { + return null; + } + } + catch (Exception ex) { + throw new RuntimeException("Failed to read metadata", ex); + } + } + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/test/TestConfigurationMetadataAnnotationProcessor.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/test/TestConfigurationMetadataAnnotationProcessor.java index 8480844bb55..cb0cbd48848 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/test/TestConfigurationMetadataAnnotationProcessor.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/test/TestConfigurationMetadataAnnotationProcessor.java @@ -16,10 +16,6 @@ package org.springframework.boot.configurationprocessor.test; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; import java.util.Arrays; import java.util.HashSet; import java.util.Set; @@ -29,8 +25,6 @@ import javax.annotation.processing.SupportedSourceVersion; import javax.lang.model.SourceVersion; import org.springframework.boot.configurationprocessor.ConfigurationMetadataAnnotationProcessor; -import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata; -import org.springframework.boot.configurationprocessor.metadata.JsonMarshaller; /** * Test {@link ConfigurationMetadataAnnotationProcessor}. @@ -39,6 +33,7 @@ import org.springframework.boot.configurationprocessor.metadata.JsonMarshaller; * @author Phillip Webb * @author Andy Wilkinson * @author Kris De Volder + * @author Scott Frederick */ @SupportedAnnotationTypes({ TestConfigurationMetadataAnnotationProcessor.CONFIGURATION_PROPERTIES_ANNOTATION, TestConfigurationMetadataAnnotationProcessor.CONTROLLER_ENDPOINT_ANNOTATION, @@ -79,12 +74,7 @@ public class TestConfigurationMetadataAnnotationProcessor extends ConfigurationM public static final String NAME_ANNOTATION = "org.springframework.boot.configurationsample.Name"; - private ConfigurationMetadata metadata; - - private final File outputLocation; - - public TestConfigurationMetadataAnnotationProcessor(File outputLocation) { - this.outputLocation = outputLocation; + public TestConfigurationMetadataAnnotationProcessor() { } @Override @@ -133,28 +123,4 @@ public class TestConfigurationMetadataAnnotationProcessor extends ConfigurationM return NAME_ANNOTATION; } - @Override - protected ConfigurationMetadata writeMetadata() throws Exception { - super.writeMetadata(); - try { - File metadataFile = new File(this.outputLocation, "META-INF/spring-configuration-metadata.json"); - if (metadataFile.isFile()) { - try (InputStream input = new FileInputStream(metadataFile)) { - this.metadata = new JsonMarshaller().read(input); - } - } - else { - this.metadata = new ConfigurationMetadata(); - } - return this.metadata; - } - catch (IOException ex) { - throw new RuntimeException("Failed to read metadata from disk", ex); - } - } - - public ConfigurationMetadata getMetadata() { - return this.metadata; - } - } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/build.gradle b/spring-boot-project/spring-boot-tools/spring-boot-loader/build.gradle index 2992e00afed..845fde0b610 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/build.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/build.gradle @@ -15,6 +15,7 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter") testImplementation("org.mockito:mockito-core") testImplementation("org.springframework:spring-test") + testImplementation("org.springframework:spring-core-test") testRuntimeOnly("ch.qos.logback:logback-classic") testRuntimeOnly("org.bouncycastle:bcprov-jdk18on:1.71") diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/JarLauncherTests.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/JarLauncherTests.java index 59cc53d2a25..b4e262637c0 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/JarLauncherTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/JarLauncherTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -101,6 +101,7 @@ class JarLauncherTests extends AbstractExecutableArchiveLauncherTests { } @Test + @SuppressWarnings("removal") void explodedJarDefinedPackagesIncludeManifestAttributes() throws Exception { Manifest manifest = new Manifest(); Attributes attributes = manifest.getMainAttributes(); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/build.gradle b/spring-boot-project/spring-boot-tools/spring-boot-test-support/build.gradle index 1a6ff9d08c7..528a9621a87 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support/build.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/build.gradle @@ -36,6 +36,7 @@ dependencies { implementation("org.hamcrest:hamcrest-library") implementation("org.springframework:spring-core") implementation("org.springframework:spring-test") + implementation("org.springframework:spring-core-test") testImplementation("jakarta.servlet:jakarta.servlet-api") testImplementation("org.junit.jupiter:junit-jupiter") diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/compiler/TestCompiler.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/compiler/TestCompiler.java index 532174dbdea..2889eac23fd 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/compiler/TestCompiler.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/compiler/TestCompiler.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,7 +37,10 @@ import javax.tools.ToolProvider; * @author Phillip Webb * @author Andy Wilkinson * @since 1.5.0 + * @deprecated since 3.0.0 in favor of + * {@link org.springframework.core.test.tools.TestCompiler} */ +@Deprecated(since = "3.0.0", forRemoval = true) public class TestCompiler { /** diff --git a/spring-boot-project/spring-boot/build.gradle b/spring-boot-project/spring-boot/build.gradle index 39c5fc85987..dc168dfd9f5 100644 --- a/spring-boot-project/spring-boot/build.gradle +++ b/spring-boot-project/spring-boot/build.gradle @@ -102,6 +102,7 @@ dependencies { optional("org.jetbrains.kotlin:kotlin-stdlib") testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support")) + testImplementation("org.springframework:spring-core-test") testImplementation("com.ibm.db2:jcc") testImplementation("com.jayway.jsonpath:json-path") testImplementation("com.microsoft.sqlserver:mssql-jdbc") diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java index d9a8387bcf4..bc7c974ef9d 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java @@ -16,36 +16,31 @@ package org.springframework.boot.context.properties.bind; -import java.io.File; -import java.io.FileWriter; import java.io.IOException; -import java.io.PrintWriter; import java.lang.reflect.Constructor; -import java.net.URL; -import java.net.URLClassLoader; import java.nio.file.Path; import java.nio.file.Paths; import java.time.LocalDate; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Objects; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; import org.springframework.boot.context.properties.source.ConfigurationPropertyName; import org.springframework.boot.context.properties.source.ConfigurationPropertySource; import org.springframework.boot.context.properties.source.MockConfigurationPropertySource; -import org.springframework.boot.testsupport.compiler.TestCompiler; import org.springframework.core.ResolvableType; import org.springframework.core.convert.ConversionService; +import org.springframework.core.test.tools.SourceFile; +import org.springframework.core.test.tools.TestCompiler; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.util.Assert; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.junit.jupiter.api.Assertions.fail; /** * Tests for {@link ValueObjectBinder}. @@ -368,26 +363,27 @@ class ValueObjectBinderTests { } @Test - void bindToRecordWithDefaultValue(@TempDir File tempDir) throws IOException, ClassNotFoundException { + void bindToRecordWithDefaultValue() throws IOException { MockConfigurationPropertySource source = new MockConfigurationPropertySource(); source.put("test.record.property1", "value-from-config-1"); this.sources.add(source); - File recordProperties = new File(tempDir, "RecordProperties.java"); - try (PrintWriter writer = new PrintWriter(new FileWriter(recordProperties))) { - writer.println("public record RecordProperties("); - writer.println( - "@org.springframework.boot.context.properties.bind.DefaultValue(\"default-value-1\") String property1,"); - writer.println( - "@org.springframework.boot.context.properties.bind.DefaultValue(\"default-value-2\") String property2"); - writer.println(") {"); - writer.println("}"); - } - TestCompiler compiler = new TestCompiler(tempDir); - compiler.getTask(Arrays.asList(recordProperties)).call(); - ClassLoader ucl = new URLClassLoader(new URL[] { tempDir.toURI().toURL() }); - Object bean = this.binder.bind("test.record", Class.forName("RecordProperties", true, ucl)).get(); - assertThat(bean).hasFieldOrPropertyWithValue("property1", "value-from-config-1") - .hasFieldOrPropertyWithValue("property2", "default-value-2"); + String recordProperties = """ + public record RecordProperties( + @org.springframework.boot.context.properties.bind.DefaultValue("default-value-1") String property1, + @org.springframework.boot.context.properties.bind.DefaultValue("default-value-2") String property2) { + } + """; + TestCompiler.forSystem().withSources(SourceFile.of(recordProperties)).compile((compiled) -> { + try { + ClassLoader cl = compiled.getClassLoader(); + Object bean = this.binder.bind("test.record", Class.forName("RecordProperties", true, cl)).get(); + assertThat(bean).hasFieldOrPropertyWithValue("property1", "value-from-config-1") + .hasFieldOrPropertyWithValue("property2", "default-value-2"); + } + catch (ClassNotFoundException ex) { + fail("Expected generated class 'RecordProperties' not found", ex); + } + }); } private void noConfigurationProperty(BindException ex) {