Skip to content

Commit

Permalink
Replace Spring Boot TestCompiler with Spring Framework's version
Browse files Browse the repository at this point in the history
  • Loading branch information
scottfrederick committed Sep 30, 2022
1 parent 8b2fd6a commit d25a996
Show file tree
Hide file tree
Showing 24 changed files with 460 additions and 517 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,16 @@

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;

/**
* Version of {@link AutoConfigureAnnotationProcessor} used for testing.
*
* @author Madhura Bhave
* @author Scott Frederick
*/
@SupportedAnnotationTypes({ "org.springframework.boot.autoconfigureprocessor.TestConditionalOnClass",
"org.springframework.boot.autoconfigureprocessor.TestConditionalOnBean",
Expand All @@ -40,10 +37,7 @@
"org.springframework.boot.autoconfigureprocessor.TestAutoConfiguration" })
public class TestAutoConfigureAnnotationProcessor extends AutoConfigureAnnotationProcessor {

private final File outputLocation;

public TestAutoConfigureAnnotationProcessor(File outputLocation) {
this.outputLocation = outputLocation;
public TestAutoConfigureAnnotationProcessor() {
}

@Override
Expand All @@ -69,20 +63,4 @@ protected List<PropertyGenerator> getPropertyGenerators() {
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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -36,6 +36,7 @@
* 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 {
Expand Down Expand Up @@ -76,7 +77,7 @@ public ConfigurationMetadata readAdditionalMetadata() throws IOException {
}

private ConfigurationMetadata readMetadata(InputStream in) throws IOException {
try {
try (in) {
return new JsonMarshaller().read(in);
}
catch (IOException ex) {
Expand All @@ -87,9 +88,6 @@ private ConfigurationMetadata readMetadata(InputStream in) throws IOException {
"Invalid additional meta-data in '" + METADATA_PATH + "': " + ex.getMessage(),
Diagnostic.Kind.ERROR);
}
finally {
in.close();
}
}

private FileObject getMetadataResource() throws IOException {
Expand All @@ -104,8 +102,26 @@ private InputStream getAdditionalMetadataStream() throws IOException {
// 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 {
Expand Down

0 comments on commit d25a996

Please sign in to comment.