From a68d4ae25ca3225c1c236718330964fe116dddf6 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Sun, 4 Sep 2022 18:59:12 +0200 Subject: [PATCH] Register runtime hints for ActiveProfilesResolvers This commit introduces automatic registration of runtime hints for custom ActiveProfilesResolver implementations configured via the `resolver` attribute in @ActiveProfiles. Closes gh-29022 --- .../aot/hint/StandardTestRuntimeHints.java | 17 ++++++++++ .../aot/TestContextAotGeneratorTests.java | 16 +++++++--- .../basic/BasicSpringJupiterTests.java | 4 ++- .../samples/basic/BasicTestConfiguration.java | 11 ++++++- .../basic/SpanishActiveProfilesResolver.java | 32 +++++++++++++++++++ .../samples/common/SpanishMessageService.java | 30 +++++++++++++++++ 6 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 spring-test/src/test/java/org/springframework/test/context/aot/samples/basic/SpanishActiveProfilesResolver.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/aot/samples/common/SpanishMessageService.java diff --git a/spring-test/src/main/java/org/springframework/test/context/aot/hint/StandardTestRuntimeHints.java b/spring-test/src/main/java/org/springframework/test/context/aot/hint/StandardTestRuntimeHints.java index 4f93602ed3..4d5f01e55c 100644 --- a/spring-test/src/main/java/org/springframework/test/context/aot/hint/StandardTestRuntimeHints.java +++ b/spring-test/src/main/java/org/springframework/test/context/aot/hint/StandardTestRuntimeHints.java @@ -20,12 +20,17 @@ import java.util.Arrays; import java.util.List; import org.springframework.aot.hint.RuntimeHints; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.ActiveProfilesResolver; import org.springframework.test.context.ContextLoader; import org.springframework.test.context.MergedContextConfiguration; +import org.springframework.test.context.TestContextAnnotationUtils; import org.springframework.test.context.aot.TestRuntimeHintsRegistrar; import org.springframework.test.context.web.WebMergedContextConfiguration; import static org.springframework.aot.hint.MemberCategory.INVOKE_DECLARED_CONSTRUCTORS; +import static org.springframework.core.annotation.MergedAnnotations.SearchStrategy.TYPE_HIERARCHY; import static org.springframework.util.ResourceUtils.CLASSPATH_URL_PREFIX; /** @@ -46,6 +51,7 @@ class StandardTestRuntimeHints implements TestRuntimeHintsRegistrar { List> testClasses, ClassLoader classLoader) { registerHintsForMergedContextConfiguration(runtimeHints, mergedConfig); + testClasses.forEach(testClass -> registerHintsForActiveProfilesResolvers(runtimeHints, testClass)); } private void registerHintsForMergedContextConfiguration( @@ -73,6 +79,17 @@ class StandardTestRuntimeHints implements TestRuntimeHintsRegistrar { } } + private void registerHintsForActiveProfilesResolvers(RuntimeHints runtimeHints, Class testClass) { + // @ActiveProfiles(resolver = ...) + MergedAnnotations.search(TYPE_HIERARCHY) + .withEnclosingClasses(TestContextAnnotationUtils::searchEnclosingClass) + .from(testClass) + .stream(ActiveProfiles.class) + .map(mergedAnnotation -> mergedAnnotation.getClass("resolver")) + .filter(type -> type != ActiveProfilesResolver.class) + .forEach(resolverClass -> registerDeclaredConstructors(runtimeHints, resolverClass)); + } + private void registerDeclaredConstructors(RuntimeHints runtimeHints, Class type) { runtimeHints.reflection().registerType(type, INVOKE_DECLARED_CONSTRUCTORS); } diff --git a/spring-test/src/test/java/org/springframework/test/context/aot/TestContextAotGeneratorTests.java b/spring-test/src/test/java/org/springframework/test/context/aot/TestContextAotGeneratorTests.java index 114aa62581..b443098e9d 100644 --- a/spring-test/src/test/java/org/springframework/test/context/aot/TestContextAotGeneratorTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/aot/TestContextAotGeneratorTests.java @@ -35,6 +35,7 @@ import org.springframework.aot.test.generator.compile.TestCompiler; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.Profiles; import org.springframework.javapoet.ClassName; import org.springframework.test.context.MergedContextConfiguration; import org.springframework.test.context.aot.samples.basic.BasicSpringJupiterSharedConfigTests; @@ -162,13 +163,15 @@ class TestContextAotGeneratorTests extends AbstractAotTests { Stream.of( // @BootstrapWith org.springframework.test.context.aot.samples.basic.BasicSpringVintageTests.CustomXmlBootstrapper.class, - // @ContextConfiguration(initializers=...) + // @ContextConfiguration(initializers = ...) org.springframework.test.context.aot.samples.basic.BasicSpringTestNGTests.CustomInitializer.class, - // @ContextConfiguration(loader=...) - org.springframework.test.context.support.AnnotationConfigContextLoader.class + // @ContextConfiguration(loader = ...) + org.springframework.test.context.support.AnnotationConfigContextLoader.class, + // @ActiveProfiles(resolver = ...) + org.springframework.test.context.aot.samples.basic.SpanishActiveProfilesResolver.class ).forEach(type -> assertReflectionRegistered(runtimeHints, type, INVOKE_DECLARED_CONSTRUCTORS)); - // @ContextConfiguration(locations=...) + // @ContextConfiguration(locations = ...) assertThat(resource().forResource("/org/springframework/test/context/aot/samples/xml/test-config.xml")) .accepts(runtimeHints); @@ -216,7 +219,10 @@ class TestContextAotGeneratorTests extends AbstractAotTests { assertThat(context.getEnvironment().getProperty("test.engine")).as("Environment").isNotNull(); MessageService messageService = context.getBean(MessageService.class); - assertThat(messageService.generateMessage()).isEqualTo("Hello, AOT!"); + ConfigurableApplicationContext cac = (ConfigurableApplicationContext) context; + String expectedMessage = cac.getEnvironment().acceptsProfiles(Profiles.of("spanish")) ? + "¡Hola, AOT!" : "Hello, AOT!"; + assertThat(messageService.generateMessage()).isEqualTo(expectedMessage); } private void assertContextForBasicWebTests(WebApplicationContext wac) throws Exception { diff --git a/spring-test/src/test/java/org/springframework/test/context/aot/samples/basic/BasicSpringJupiterTests.java b/spring-test/src/test/java/org/springframework/test/context/aot/samples/basic/BasicSpringJupiterTests.java index 9d27c1f89d..ea09bd610a 100644 --- a/spring-test/src/test/java/org/springframework/test/context/aot/samples/basic/BasicSpringJupiterTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/aot/samples/basic/BasicSpringJupiterTests.java @@ -23,6 +23,7 @@ import org.junit.jupiter.api.extension.Extension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationContext; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.aot.samples.basic.BasicSpringJupiterTests.DummyExtension; @@ -57,12 +58,13 @@ public class BasicSpringJupiterTests { @Nested @TestPropertySource(properties = "foo=bar") + @ActiveProfiles(resolver = SpanishActiveProfilesResolver.class) public class NestedTests { @org.junit.jupiter.api.Test void test(@Autowired ApplicationContext context, @Autowired MessageService messageService, @Value("${test.engine}") String testEngine, @Value("${foo}") String foo) { - assertThat(messageService.generateMessage()).isEqualTo("Hello, AOT!"); + assertThat(messageService.generateMessage()).isEqualTo("¡Hola, AOT!"); assertThat(foo).isEqualTo("bar"); assertThat(testEngine).isEqualTo("jupiter"); assertThat(context.getEnvironment().getProperty("test.engine")) diff --git a/spring-test/src/test/java/org/springframework/test/context/aot/samples/basic/BasicTestConfiguration.java b/spring-test/src/test/java/org/springframework/test/context/aot/samples/basic/BasicTestConfiguration.java index 886c1e19bc..2bacfa98c6 100644 --- a/spring-test/src/test/java/org/springframework/test/context/aot/samples/basic/BasicTestConfiguration.java +++ b/spring-test/src/test/java/org/springframework/test/context/aot/samples/basic/BasicTestConfiguration.java @@ -18,8 +18,10 @@ package org.springframework.test.context.aot.samples.basic; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; import org.springframework.test.context.aot.samples.common.DefaultMessageService; import org.springframework.test.context.aot.samples.common.MessageService; +import org.springframework.test.context.aot.samples.common.SpanishMessageService; /** * @author Sam Brannen @@ -29,8 +31,15 @@ import org.springframework.test.context.aot.samples.common.MessageService; class BasicTestConfiguration { @Bean - MessageService messageService() { + @Profile("default") + MessageService defaultMessageService() { return new DefaultMessageService(); } + @Bean + @Profile("spanish") + MessageService spanishMessageService() { + return new SpanishMessageService(); + } + } diff --git a/spring-test/src/test/java/org/springframework/test/context/aot/samples/basic/SpanishActiveProfilesResolver.java b/spring-test/src/test/java/org/springframework/test/context/aot/samples/basic/SpanishActiveProfilesResolver.java new file mode 100644 index 0000000000..40726e0893 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/aot/samples/basic/SpanishActiveProfilesResolver.java @@ -0,0 +1,32 @@ +/* + * Copyright 2002-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.test.context.aot.samples.basic; + +import org.springframework.test.context.ActiveProfilesResolver; + +/** + * @author Sam Brannen + * @since 6.0 + */ +public class SpanishActiveProfilesResolver implements ActiveProfilesResolver { + + @Override + public String[] resolve(Class testClass) { + return new String[] { "spanish" }; + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/aot/samples/common/SpanishMessageService.java b/spring-test/src/test/java/org/springframework/test/context/aot/samples/common/SpanishMessageService.java new file mode 100644 index 0000000000..4d866892f0 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/aot/samples/common/SpanishMessageService.java @@ -0,0 +1,30 @@ +/* + * Copyright 2002-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.test.context.aot.samples.common; + +/** + * @author Sam Brannen + * @since 6.0 + */ +public class SpanishMessageService implements MessageService { + + @Override + public String generateMessage() { + return "¡Hola, AOT!"; + } + +}