Replace Spring Boot TestCompiler with Spring Framework's version

See gh-31266
pull/32569/head
Scott Frederick 2 years ago
parent 8b2fd6a05a
commit d25a99692f

@ -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")
}

@ -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 void compile(Class<?> type, Consumer<Properties> consumer) {
process(type, (writtenFile) -> consumer.accept(getWrittenProperties(writtenFile)));
}
private void process(Class<?> type, Consumer<InputStream> 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 compile(Class<?>... types) throws IOException {
return process(types).getWrittenProperties();
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 TestAutoConfigureAnnotationProcessor process(Class<?>... types) {
TestAutoConfigureAnnotationProcessor processor = new TestAutoConfigureAnnotationProcessor(
this.compiler.getOutputLocation());
this.compiler.getTask(types).call(processor);
return processor;
private String getFileContents(InputStream inputStream) {
try {
return new String(inputStream.readAllBytes());
}
catch (IOException ex) {
fail("Error reading contents of properties file", ex);
}
return null;
}
}

@ -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);
}
}

@ -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")

@ -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 {

@ -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 static final String ADDITIONAL_METADATA_FILE = "META-INF/additional-spring-configuration-metadata.json";
protected ConfigurationMetadata compile(Class<?>... types) {
TestCompiler compiler = TestCompiler.forSystem().withSources(sourceFilesOf(types));
return compile(compiler);
}
private TestCompiler compiler;
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);
}
@BeforeEach
void createCompiler() throws IOException {
this.compiler = new TestCompiler(this.tempDir);
protected ConfigurationMetadata compile(String... source) {
TestCompiler compiler = TestCompiler.forSystem().withSources(sourceFilesOf(source));
return compile(compiler);
}
protected TestCompiler getCompiler() {
return this.compiler;
private ConfigurationMetadata compile(TestCompiler compiler) {
TestConfigurationMetadataAnnotationProcessor processor = new TestConfigurationMetadataAnnotationProcessor();
compiler = compiler.withProcessors(processor);
AtomicReference<ConfigurationMetadata> configurationMetadata = new AtomicReference<>();
compiler.compile((compiled) -> configurationMetadata.set(CompiledMetadataReader.getMetadata(compiled)));
return configurationMetadata.get();
}
protected ConfigurationMetadata compile(Class<?>... types) {
TestConfigurationMetadataAnnotationProcessor processor = new TestConfigurationMetadataAnnotationProcessor(
this.compiler.getOutputLocation());
this.compiler.getTask(types).call(processor);
return processor.getMetadata();
private List<SourceFile> sourceFilesOf(Class<?>... types) {
return Arrays.stream(types).map(SourceFile::forTestClass).toList();
}
protected ConfigurationMetadata compile(File... sources) {
TestConfigurationMetadataAnnotationProcessor processor = new TestConfigurationMetadataAnnotationProcessor(
this.compiler.getOutputLocation());
this.compiler.getTask(Arrays.asList(sources)).call(processor);
return processor.getMetadata();
private List<SourceFile> sourceFilesOf(String... content) {
return Arrays.stream(content).map(SourceFile::of).toList();
}
}

@ -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"));
}

@ -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"));

@ -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());
}

@ -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<ItemMetadata> 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 {

@ -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

@ -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<Class<?>> additionalClasses,
BiConsumer<TypeElement, MetadataGenerationEnvironment> consumer) throws IOException {
BiConsumer<TypeElement, MetadataGenerationEnvironment> consumer) {
BiConsumer<RoundEnvironmentTester, MetadataGenerationEnvironment> internalConsumer = (roundEnv,
metadataEnv) -> {
TypeElement element = roundEnv.getRootElement(target);
@ -189,11 +186,12 @@ class PropertyDescriptorResolverTests {
};
TestableAnnotationProcessor<MetadataGenerationEnvironment> processor = new TestableAnnotationProcessor<>(
internalConsumer, new MetadataGenerationEnvironmentFactory());
TestCompiler compiler = new TestCompiler(this.tempDir);
ArrayList<Class<?>> allClasses = new ArrayList<>();
allClasses.add(target);
allClasses.addAll(additionalClasses);
compiler.getTask(allClasses.toArray(new Class<?>[0])).call(processor);
SourceFile targetSource = SourceFile.forTestClass(target);
List<SourceFile> additionalSource = additionalClasses.stream().map(SourceFile::forTestClass).toList();
TestCompiler compiler = TestCompiler.forSystem().withProcessors(processor).withSources(targetSource)
.withSources(additionalSource);
compiler.compile((compiled) -> {
});
}
}

@ -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<RoundEnvironmentTester, MetadataGenerationEnvironment> consumer)
throws IOException {
protected void process(Class<?> target,
BiConsumer<RoundEnvironmentTester, MetadataGenerationEnvironment> consumer) {
TestableAnnotationProcessor<MetadataGenerationEnvironment> 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) -> {
});
}
}

@ -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 TestCompiler compiler;
private Set<File> 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<Class<?>> contents = new HashSet<>(Arrays.asList(classes));
contents.addAll(Arrays.asList(ALWAYS_INCLUDE));
copySources(contents);
}
private void copySources(Set<Class<?>> 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();
}
private SourceFiles sources;
public ConfigurationMetadata incrementalBuild(Class<?>... toRecompile) {
TestConfigurationMetadataAnnotationProcessor processor = new TestConfigurationMetadataAnnotationProcessor(
this.compiler.getOutputLocation());
TestCompilationTask task = this.compiler.getTask(toRecompile);
task.call(processor);
return processor.getMetadata();
public TestProject(Class<?>... classes) {
this.sources = SourceFiles.none().and(sourceFilesOf(ALWAYS_INCLUDE)).and(sourceFilesOf(classes));
}
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> 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 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);
}
/**
* 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));
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));
}
private static void putContents(File targetFile, String contents) throws IOException {
FileCopyUtils.copy(new StringReader(contents), new FileWriter(targetFile));
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));
}
private static String getContents(File file) throws Exception {
return FileCopyUtils.copyToString(new FileReader(file));
private List<SourceFile> sourceFilesOf(Class<?>... types) {
return Arrays.stream(types).map(SourceFile::forTestClass).toList();
}
}

@ -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<RoundEnvironmentTester, TypeUtils> consumer) throws IOException {
private void process(Class<?> target, BiConsumer<RoundEnvironmentTester, TypeUtils> consumer) {
TestableAnnotationProcessor<TypeUtils> 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) -> {
});
}
}

@ -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<String, Object> values = processor.getValues();
assertThat(values.get("string")).isEqualTo("1");
assertThat(values.get("stringNone")).isNull();

@ -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);
}
}
}

@ -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;
}
}

@ -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")

@ -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();

@ -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")

@ -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 {
/**

@ -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")

@ -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) {

Loading…
Cancel
Save