From eb60cf49886c193becb1c5c8b258f54a422cb95e Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 26 May 2023 09:33:12 +0100 Subject: [PATCH] Remove duplication of BindableRuntimeHintsRegistrarTests There were several tests in CPBFIAPT that were duplicating tests in BindableRuntimeHintsRegistrarTests. To test the AOT processor, all that is really necessary is to assert that the expected types are found and passed to BindableRuntimeHintsRegistrar. The tests for BindableRuntimeHintsRegistrar are then responsible for asserting that the expected hints are generated for the various different types. Closes gh-35645 --- ...BeanFactoryInitializationAotProcessor.java | 9 +- ...actoryInitializationAotProcessorTests.java | 602 +----------------- 2 files changed, 26 insertions(+), 585 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanFactoryInitializationAotProcessor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanFactoryInitializationAotProcessor.java index d00b6daeecf..c7bf287643e 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanFactoryInitializationAotProcessor.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanFactoryInitializationAotProcessor.java @@ -40,7 +40,8 @@ import org.springframework.util.CollectionUtils; class ConfigurationPropertiesBeanFactoryInitializationAotProcessor implements BeanFactoryInitializationAotProcessor { @Override - public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) { + public ConfigurationPropertiesReflectionHintsContribution processAheadOfTime( + ConfigurableListableBeanFactory beanFactory) { String[] beanNames = beanFactory.getBeanNamesForAnnotation(ConfigurationProperties.class); List> types = new ArrayList<>(); for (String beanName : beanNames) { @@ -55,7 +56,7 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessor implements Be return null; } - private static final class ConfigurationPropertiesReflectionHintsContribution + static final class ConfigurationPropertiesReflectionHintsContribution implements BeanFactoryInitializationAotContribution { private final Iterable> types; @@ -70,6 +71,10 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessor implements Be BindableRuntimeHintsRegistrar.forTypes(this.types).registerHints(generationContext.getRuntimeHints()); } + Iterable> getTypes() { + return this.types; + } + } } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests.java index 6ef68b8bd62..1b45fc8e5d2 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests.java @@ -16,35 +16,21 @@ package org.springframework.boot.context.properties; -import java.lang.reflect.Constructor; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.springframework.aot.generate.GenerationContext; -import org.springframework.aot.hint.ExecutableHint; -import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.TypeHint; import org.springframework.aot.hint.TypeReference; -import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; import org.springframework.aot.test.generate.TestGenerationContext; import org.springframework.beans.factory.aot.AotServices; -import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution; import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor; import org.springframework.beans.factory.aot.BeanFactoryInitializationCode; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.boot.context.properties.bind.ConstructorBinding; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.EnvironmentAware; -import org.springframework.core.env.Environment; +import org.springframework.boot.context.properties.ConfigurationPropertiesBeanFactoryInitializationAotProcessor.ConfigurationPropertiesReflectionHintsContribution; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -68,209 +54,34 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests { @Test void processNoMatchesReturnsNullContribution() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerBeanDefinition("test", new RootBeanDefinition(String.class)); - assertThat(this.processor.processAheadOfTime(beanFactory)).isNull(); + assertThat(process(String.class)).isNull(); } @Test void processManuallyRegisteredSingleton() { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); beanFactory.registerSingleton("test", new SampleProperties()); - RuntimeHints runtimeHints = process(beanFactory); - assertThat(runtimeHints.reflection().getTypeHint(SampleProperties.class)) - .satisfies(javaBeanBinding(SampleProperties.class)); + ConfigurationPropertiesReflectionHintsContribution contribution = process(beanFactory); + assertThat(contribution.getTypes()).containsExactly(SampleProperties.class); + assertThat(typeHints(contribution).map(TypeHint::getType)) + .containsExactly(TypeReference.of(SampleProperties.class)); } @Test - void processJavaBeanConfigurationProperties() { - RuntimeHints runtimeHints = process(SampleProperties.class); - assertThat(runtimeHints.reflection().getTypeHint(SampleProperties.class)) - .satisfies(javaBeanBinding(SampleProperties.class)); + void processDefinedBean() { + ConfigurationPropertiesReflectionHintsContribution contribution = process(SampleProperties.class); + assertThat(contribution.getTypes()).containsExactly(SampleProperties.class); + assertThat(typeHints(contribution).map(TypeHint::getType)) + .containsExactly(TypeReference.of(SampleProperties.class)); } - @Test - void processJavaBeanConfigurationPropertiesWithSeveralConstructors() throws NoSuchMethodException { - RuntimeHints runtimeHints = process(SamplePropertiesWithSeveralConstructors.class); - assertThat(runtimeHints.reflection().getTypeHint(SamplePropertiesWithSeveralConstructors.class)) - .satisfies(javaBeanBinding(SamplePropertiesWithSeveralConstructors.class, - SamplePropertiesWithSeveralConstructors.class.getDeclaredConstructor())); - } - - @Test - void processJavaBeanConfigurationPropertiesWithMapOfPojo() { - RuntimeHints runtimeHints = process(SamplePropertiesWithMap.class); - List typeHints = runtimeHints.reflection().typeHints().toList(); - assertThat(typeHints).anySatisfy(javaBeanBinding(SamplePropertiesWithMap.class)); - assertThat(typeHints).anySatisfy(javaBeanBinding(Address.class)); - assertThat(typeHints).hasSize(2); - } - - @Test - void processJavaBeanConfigurationPropertiesWithListOfPojo() { - RuntimeHints runtimeHints = process(SamplePropertiesWithList.class); - List typeHints = runtimeHints.reflection().typeHints().toList(); - assertThat(typeHints).anySatisfy(javaBeanBinding(SamplePropertiesWithList.class)); - assertThat(typeHints).anySatisfy(javaBeanBinding(Address.class)); - assertThat(typeHints).hasSize(2); - } - - @Test - void processJavaBeanConfigurationPropertiesWitArrayOfPojo() { - RuntimeHints runtimeHints = process(SamplePropertiesWithArray.class); - List typeHints = runtimeHints.reflection().typeHints().toList(); - assertThat(typeHints).anySatisfy(javaBeanBinding(SamplePropertiesWithArray.class)); - assertThat(typeHints).anySatisfy(javaBeanBinding(Address.class)); - assertThat(typeHints).hasSize(2); - } - - @Test - void processJavaBeanConfigurationPropertiesWithListOfJavaType() { - RuntimeHints runtimeHints = process(SamplePropertiesWithSimpleList.class); - List typeHints = runtimeHints.reflection().typeHints().toList(); - assertThat(typeHints).anySatisfy(javaBeanBinding(SamplePropertiesWithSimpleList.class)); - assertThat(typeHints).hasSize(1); - } - - @Test - void processValueObjectConfigurationProperties() { - RuntimeHints runtimeHints = process(SampleImmutableProperties.class); - List typeHints = runtimeHints.reflection().typeHints().toList(); - assertThat(typeHints).anySatisfy(valueObjectBinding(SampleImmutableProperties.class, - SampleImmutableProperties.class.getDeclaredConstructors()[0])); - assertThat(typeHints).hasSize(1); - } - - @Test - void processValueObjectConfigurationPropertiesWithSpecificConstructor() throws NoSuchMethodException { - RuntimeHints runtimeHints = process(SampleImmutablePropertiesWithSeveralConstructors.class); - List typeHints = runtimeHints.reflection().typeHints().toList(); - assertThat(typeHints).anySatisfy(valueObjectBinding(SampleImmutablePropertiesWithSeveralConstructors.class, - SampleImmutablePropertiesWithSeveralConstructors.class.getDeclaredConstructor(String.class))); - assertThat(typeHints).hasSize(1); - } - - @Test - void processValueObjectConfigurationPropertiesWithSeveralLayersOfPojo() { - RuntimeHints runtimeHints = process(SampleImmutablePropertiesWithList.class); - List typeHints = runtimeHints.reflection().typeHints().toList(); - assertThat(typeHints).anySatisfy(valueObjectBinding(SampleImmutablePropertiesWithList.class, - SampleImmutablePropertiesWithList.class.getDeclaredConstructors()[0])); - assertThat(typeHints).anySatisfy(valueObjectBinding(Person.class, Person.class.getDeclaredConstructors()[0])); - assertThat(typeHints).anySatisfy(valueObjectBinding(Address.class, Address.class.getDeclaredConstructors()[0])); - assertThat(typeHints).hasSize(3); - } - - @Test - void processConfigurationPropertiesWithNestedTypeNotUsedIsIgnored() { - RuntimeHints runtimeHints = process(SamplePropertiesWithNested.class); - assertThat(runtimeHints.reflection().getTypeHint(SamplePropertiesWithNested.class)) - .satisfies(javaBeanBinding(SamplePropertiesWithNested.class)); - } - - @Test - void processConfigurationPropertiesWithNestedExternalType() { - RuntimeHints runtimeHints = process(SamplePropertiesWithExternalNested.class); - assertThat(runtimeHints.reflection().typeHints()) - .anySatisfy(javaBeanBinding(SamplePropertiesWithExternalNested.class)) - .anySatisfy(javaBeanBinding(SampleType.class)) - .anySatisfy(javaBeanBinding(SampleType.Nested.class)) - .hasSize(3); - } - - @Test - void processConfigurationPropertiesWithRecursiveType() { - RuntimeHints runtimeHints = process(SamplePropertiesWithRecursive.class); - assertThat(runtimeHints.reflection().typeHints()) - .anySatisfy(javaBeanBinding(SamplePropertiesWithRecursive.class)) - .anySatisfy(javaBeanBinding(Recursive.class)) - .hasSize(2); - } - - @Test - void processValueObjectConfigurationPropertiesWithRecursiveType() { - RuntimeHints runtimeHints = process(SampleImmutablePropertiesWithRecursive.class); - assertThat(runtimeHints.reflection().typeHints()) - .anySatisfy(valueObjectBinding(SampleImmutablePropertiesWithRecursive.class, - SampleImmutablePropertiesWithRecursive.class.getDeclaredConstructors()[0])) - .anySatisfy( - valueObjectBinding(ImmutableRecursive.class, ImmutableRecursive.class.getDeclaredConstructors()[0])) - .hasSize(2); - } - - @Test - void processConfigurationPropertiesWithWellKnownTypes() { - RuntimeHints runtimeHints = process(SamplePropertiesWithWellKnownTypes.class); - assertThat(runtimeHints.reflection().typeHints()) - .anySatisfy(javaBeanBinding(SamplePropertiesWithWellKnownTypes.class)) - .hasSize(1); - } - - @Test - void processConfigurationPropertiesWithCrossReference() { - RuntimeHints runtimeHints = process(SamplePropertiesWithCrossReference.class); - assertThat(runtimeHints.reflection().typeHints()) - .anySatisfy(javaBeanBinding(SamplePropertiesWithCrossReference.class)) - .anySatisfy(javaBeanBinding(CrossReferenceA.class)) - .anySatisfy(javaBeanBinding(CrossReferenceB.class)) - .hasSize(3); - } - - @Test - void processConfigurationPropertiesWithUnresolvedGeneric() { - RuntimeHints runtimeHints = process(SamplePropertiesWithGeneric.class); - assertThat(runtimeHints.reflection().typeHints()).anySatisfy(javaBeanBinding(SamplePropertiesWithGeneric.class)) - .anySatisfy(javaBeanBinding(GenericObject.class)); - } - - @Test - void processConfigurationPropertiesWithNestedGenerics() { - RuntimeHints runtimeHints = process(NestedGenerics.class); - assertThat(RuntimeHintsPredicates.reflection().onType(NestedGenerics.class)).accepts(runtimeHints); - assertThat(RuntimeHintsPredicates.reflection().onType(NestedGenerics.Nested.class)).accepts(runtimeHints); - } - - @Test - void processConfigurationPropertiesWithMultipleNestedClasses() { - RuntimeHints runtimeHints = process(TripleNested.class); - assertThat(RuntimeHintsPredicates.reflection().onType(TripleNested.class)).accepts(runtimeHints); - assertThat(RuntimeHintsPredicates.reflection().onType(TripleNested.DoubleNested.class)).accepts(runtimeHints); - assertThat(RuntimeHintsPredicates.reflection().onType(TripleNested.DoubleNested.Nested.class)) - .accepts(runtimeHints); - } - - private Consumer javaBeanBinding(Class type) { - return javaBeanBinding(type, type.getDeclaredConstructors()[0]); - } - - private Consumer javaBeanBinding(Class type, Constructor constructor) { - return (entry) -> { - assertThat(entry.getType()).isEqualTo(TypeReference.of(type)); - assertThat(entry.constructors()).singleElement().satisfies(match(constructor)); - assertThat(entry.getMemberCategories()).isEmpty(); - assertThat(entry.methods()).allMatch((t) -> t.getName().startsWith("set") || t.getName().startsWith("get") - || t.getName().startsWith("is")); - }; - } - - private Consumer valueObjectBinding(Class type, Constructor constructor) { - return (entry) -> { - assertThat(entry.getType()).isEqualTo(TypeReference.of(type)); - assertThat(entry.constructors()).singleElement().satisfies(match(constructor)); - assertThat(entry.getMemberCategories()).isEmpty(); - assertThat(entry.methods()).isEmpty(); - }; - } - - private Consumer match(Constructor constructor) { - return (executableHint) -> { - assertThat(executableHint.getName()).isEqualTo(""); - assertThat(Arrays.stream(constructor.getParameterTypes()).map(TypeReference::of).toList()) - .isEqualTo(executableHint.getParameterTypes()); - }; + Stream typeHints(ConfigurationPropertiesReflectionHintsContribution contribution) { + GenerationContext generationContext = new TestGenerationContext(); + contribution.applyTo(generationContext, mock(BeanFactoryInitializationCode.class)); + return generationContext.getRuntimeHints().reflection().typeHints(); } - private RuntimeHints process(Class... types) { + private ConfigurationPropertiesReflectionHintsContribution process(Class... types) { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); for (Class type : types) { beanFactory.registerBeanDefinition(type.getName(), new RootBeanDefinition(type)); @@ -278,12 +89,8 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests { return process(beanFactory); } - private RuntimeHints process(ConfigurableListableBeanFactory beanFactory) { - BeanFactoryInitializationAotContribution contribution = this.processor.processAheadOfTime(beanFactory); - assertThat(contribution).isNotNull(); - GenerationContext generationContext = new TestGenerationContext(); - contribution.applyTo(generationContext, mock(BeanFactoryInitializationCode.class)); - return generationContext.getRuntimeHints(); + private ConfigurationPropertiesReflectionHintsContribution process(ConfigurableListableBeanFactory beanFactory) { + return this.processor.processAheadOfTime(beanFactory); } @ConfigurationProperties("test") @@ -291,375 +98,4 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests { } - @ConfigurationProperties("test") - public static class SamplePropertiesWithSeveralConstructors { - - SamplePropertiesWithSeveralConstructors() { - } - - SamplePropertiesWithSeveralConstructors(String ignored) { - } - - } - - @ConfigurationProperties("test") - public static class SamplePropertiesWithMap { - - public Map getAddresses() { - return Collections.emptyMap(); - } - - } - - @ConfigurationProperties("test") - public static class SamplePropertiesWithList { - - public List
getAllAddresses() { - return Collections.emptyList(); - } - - } - - @ConfigurationProperties("test") - public static class SamplePropertiesWithSimpleList { - - public List getNames() { - return Collections.emptyList(); - } - - } - - @ConfigurationProperties("test") - public static class SamplePropertiesWithArray { - - public Address[] getAllAddresses() { - return new Address[0]; - } - - } - - @ConfigurationProperties - public static class SampleImmutableProperties { - - @SuppressWarnings("unused") - private final String name; - - SampleImmutableProperties(String name) { - this.name = name; - } - - } - - @ConfigurationProperties - public static class SampleImmutablePropertiesWithSeveralConstructors { - - @SuppressWarnings("unused") - private final String name; - - @ConstructorBinding - SampleImmutablePropertiesWithSeveralConstructors(String name) { - this.name = name; - } - - SampleImmutablePropertiesWithSeveralConstructors() { - this("test"); - } - - } - - @ConfigurationProperties - public static class SampleImmutablePropertiesWithList { - - @SuppressWarnings("unused") - private final List family; - - SampleImmutablePropertiesWithList(List family) { - this.family = family; - } - - } - - @ConfigurationProperties("nested") - public static class SamplePropertiesWithNested { - - static class OneLevelDown { - - } - - } - - @ConfigurationProperties("nested") - public static class SamplePropertiesWithExternalNested { - - private String name; - - @NestedConfigurationProperty - private SampleType sampleType; - - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - public SampleType getSampleType() { - return this.sampleType; - } - - public void setSampleType(SampleType sampleType) { - this.sampleType = sampleType; - } - - } - - @ConfigurationProperties("recursive") - public static class SamplePropertiesWithRecursive { - - @NestedConfigurationProperty - private Recursive recursive; - - public Recursive getRecursive() { - return this.recursive; - } - - public void setRecursive(Recursive recursive) { - this.recursive = recursive; - } - - } - - @ConfigurationProperties - public static class SampleImmutablePropertiesWithRecursive { - - @NestedConfigurationProperty - private final ImmutableRecursive recursive; - - SampleImmutablePropertiesWithRecursive(ImmutableRecursive recursive) { - this.recursive = recursive; - } - - } - - @ConfigurationProperties("wellKnownTypes") - public static class SamplePropertiesWithWellKnownTypes implements ApplicationContextAware, EnvironmentAware { - - private ApplicationContext applicationContext; - - private Environment environment; - - public ApplicationContext getApplicationContext() { - return this.applicationContext; - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) { - this.applicationContext = applicationContext; - } - - public Environment getEnvironment() { - return this.environment; - } - - @Override - public void setEnvironment(Environment environment) { - this.environment = environment; - } - - } - - public static class SampleType { - - private final Nested nested = new Nested(); - - public Nested getNested() { - return this.nested; - } - - static class Nested { - - } - - } - - public static class Address { - - } - - public static class Person { - - @SuppressWarnings("unused") - private final String firstName; - - @SuppressWarnings("unused") - private final String lastName; - - @NestedConfigurationProperty - private final Address address; - - Person(String firstName, String lastName, Address address) { - this.firstName = firstName; - this.lastName = lastName; - this.address = address; - } - - } - - public static class Recursive { - - private Recursive recursive; - - public Recursive getRecursive() { - return this.recursive; - } - - public void setRecursive(Recursive recursive) { - this.recursive = recursive; - } - - } - - public static class ImmutableRecursive { - - @SuppressWarnings("unused") - private final ImmutableRecursive recursive; - - ImmutableRecursive(ImmutableRecursive recursive) { - this.recursive = recursive; - } - - } - - @ConfigurationProperties("crossreference") - public static class SamplePropertiesWithCrossReference { - - @NestedConfigurationProperty - private CrossReferenceA crossReferenceA; - - public void setCrossReferenceA(CrossReferenceA crossReferenceA) { - this.crossReferenceA = crossReferenceA; - } - - public CrossReferenceA getCrossReferenceA() { - return this.crossReferenceA; - } - - } - - public static class CrossReferenceA { - - @NestedConfigurationProperty - private CrossReferenceB crossReferenceB; - - public void setCrossReferenceB(CrossReferenceB crossReferenceB) { - this.crossReferenceB = crossReferenceB; - } - - public CrossReferenceB getCrossReferenceB() { - return this.crossReferenceB; - } - - } - - public static class CrossReferenceB { - - private CrossReferenceA crossReferenceA; - - public void setCrossReferenceA(CrossReferenceA crossReferenceA) { - this.crossReferenceA = crossReferenceA; - } - - public CrossReferenceA getCrossReferenceA() { - return this.crossReferenceA; - } - - } - - @ConfigurationProperties(prefix = "generic") - public static class SamplePropertiesWithGeneric { - - @NestedConfigurationProperty - private GenericObject generic; - - public GenericObject getGeneric() { - return this.generic; - } - - } - - public static final class GenericObject { - - private final T value; - - GenericObject(T value) { - this.value = value; - } - - public T getValue() { - return this.value; - } - - } - - @ConfigurationProperties(prefix = "nested-generics") - public static class NestedGenerics { - - private final Map> nested = new HashMap<>(); - - public Map> getNested() { - return this.nested; - } - - public static class Nested { - - private String field; - - public String getField() { - return this.field; - } - - public void setField(String field) { - this.field = field; - } - - } - - } - - @ConfigurationProperties(prefix = "triple-nested") - public static class TripleNested { - - private final DoubleNested doubleNested = new DoubleNested(); - - public DoubleNested getDoubleNested() { - return this.doubleNested; - } - - public static class DoubleNested { - - private final Nested nested = new Nested(); - - public Nested getNested() { - return this.nested; - } - - public static class Nested { - - private String field; - - public String getField() { - return this.field; - } - - public void setField(String field) { - this.field = field; - } - - } - - } - - } - }