Merge branch '6.2.x'

This commit is contained in:
Sam Brannen
2025-03-03 17:40:39 +01:00
11 changed files with 399 additions and 20 deletions

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2002-2025 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;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.support.AbstractContextLoader;
/**
* @author Sam Brannen
* @since 6.2.4
*/
class AbstractAotContextLoader extends AbstractContextLoader implements AotContextLoader {
@Override
public final GenericApplicationContext loadContext(MergedContextConfiguration mergedConfig) {
return new StaticApplicationContext();
}
@Override
public final GenericApplicationContext loadContextForAotRuntime(MergedContextConfiguration mergedConfig,
ApplicationContextInitializer<ConfigurableApplicationContext> initializer) {
return loadContext(mergedConfig);
}
@Override
protected final String getResourceSuffix() {
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright 2002-2025 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;
import java.lang.reflect.Method;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.springframework.aot.generate.InMemoryGeneratedFiles;
import org.springframework.aot.hint.ExecutableMode;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.MergedContextConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.aot.hint.predicate.RuntimeHintsPredicates.reflection;
/**
* Tests for registering run-time hints within an {@link AotContextLoader}, tested
* via the {@link TestContextAotGenerator}.
*
* @author Sam Brannen
* @since 6.2.4
*/
class AotContextLoaderRuntimeHintsTests {
@Test // gh-34513
void aotContextLoaderCanRegisterRuntimeHints() {
RuntimeHints runtimeHints = new RuntimeHints();
TestContextAotGenerator generator = new TestContextAotGenerator(new InMemoryGeneratedFiles(), runtimeHints);
generator.processAheadOfTime(Stream.of(TestCase.class));
assertThat(reflection().onMethod(ConfigWithMain.class, "main").invoke()).accepts(runtimeHints);
}
@ContextConfiguration(classes = ConfigWithMain.class, loader = RuntimeHintsAwareAotContextLoader.class)
static class TestCase {
}
@Configuration(proxyBeanMethods = false)
static class ConfigWithMain {
public static void main(String[] args) {
// Mimics main() method for Spring Boot app
}
}
static class RuntimeHintsAwareAotContextLoader extends AbstractAotContextLoader {
@Override
public void processContextConfiguration(ContextConfigurationAttributes configAttributes) {
/* no-op */
}
@Override
public GenericApplicationContext loadContextForAotProcessing(MergedContextConfiguration mergedConfig,
RuntimeHints runtimeHints) throws Exception {
// Mimics SpringBootContextLoader
Method mainMethod = mergedConfig.getClasses()[0].getMethod("main", String[].class);
runtimeHints.reflection().registerMethod(mainMethod, ExecutableMode.INVOKE);
return loadContext(mergedConfig);
}
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright 2002-2025 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;
import java.lang.reflect.Method;
import org.junit.jupiter.api.Test;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.context.MergedContextConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.never;
import static org.mockito.BDDMockito.spy;
import static org.mockito.BDDMockito.then;
/**
* Unit tests for {@link AotContextLoader}.
*
* @author Sam Brannen
* @since 6.2.4
*/
class AotContextLoaderTests {
/**
* Verifies that a legacy {@link AotContextLoader} which only overrides
* {@link AotContextLoader#loadContextForAotProcessing(MergedContextConfiguration)
* is still supported.
*/
@Test // gh-34513
@SuppressWarnings("removal")
void legacyAotContextLoader() throws Exception {
// Prerequisites
assertDeclaringClasses(LegacyAotContextLoader.class, LegacyAotContextLoader.class, AotContextLoader.class);
AotContextLoader loader = spy(new LegacyAotContextLoader());
MergedContextConfiguration mergedConfig = new MergedContextConfiguration(getClass(), null, null, null, loader);
loader.loadContextForAotProcessing(mergedConfig, new RuntimeHints());
then(loader).should().loadContextForAotProcessing(mergedConfig);
}
/**
* Verifies that a modern {@link AotContextLoader} which only overrides
* {@link AotContextLoader#loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)
* is supported.
*/
@Test // gh-34513
@SuppressWarnings("removal")
void runtimeHintsAwareAotContextLoader() throws Exception {
// Prerequisites
assertDeclaringClasses(RuntimeHintsAwareAotContextLoader.class, AotContextLoader.class, RuntimeHintsAwareAotContextLoader.class);
AotContextLoader loader = spy(new RuntimeHintsAwareAotContextLoader());
MergedContextConfiguration mergedConfig = new MergedContextConfiguration(getClass(), null, null, null, loader);
loader.loadContextForAotProcessing(mergedConfig, new RuntimeHints());
then(loader).should(never()).loadContextForAotProcessing(mergedConfig);
}
private static void assertDeclaringClasses(Class<? extends AotContextLoader> loaderClass,
Class<?> declaringClassForLegacyMethod, Class<?> declaringClassForNewMethod) throws Exception {
Method legacyMethod = loaderClass.getMethod("loadContextForAotProcessing", MergedContextConfiguration.class);
Method newMethod = loaderClass.getMethod("loadContextForAotProcessing", MergedContextConfiguration.class, RuntimeHints.class);
assertThat(legacyMethod.getDeclaringClass()).isEqualTo(declaringClassForLegacyMethod);
assertThat(newMethod.getDeclaringClass()).isEqualTo(declaringClassForNewMethod);
}
private static class LegacyAotContextLoader extends AbstractAotContextLoader {
@Override
@SuppressWarnings("removal")
public GenericApplicationContext loadContextForAotProcessing(MergedContextConfiguration mergedConfig) {
return loadContext(mergedConfig);
}
}
private static class RuntimeHintsAwareAotContextLoader extends AbstractAotContextLoader {
@Override
public GenericApplicationContext loadContextForAotProcessing(MergedContextConfiguration mergedConfig,
RuntimeHints runtimeHints) {
return loadContext(mergedConfig);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2025 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.
@@ -38,7 +38,7 @@ import static org.assertj.core.api.Assertions.assertThatNoException;
* @author Sam Brannen
* @since 6.1
*/
class TestPropertySourceRuntimeHintsTests extends AbstractAotTests {
class TestPropertySourceRuntimeHintsTests {
private final RuntimeHints runtimeHints = new RuntimeHints();
@@ -93,7 +93,7 @@ class TestPropertySourceRuntimeHintsTests extends AbstractAotTests {
static class WildcardLocationPatternTestCase {
}
@Configuration
@Configuration(proxyBeanMethods = false)
static class Config {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@@ -20,6 +20,7 @@ import java.util.Arrays;
import org.junit.jupiter.api.Test;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.context.MergedContextConfiguration;
@@ -79,7 +80,7 @@ class AnnotationConfigContextLoaderTests {
AnnotatedFooConfigInnerClassTestCase.class, EMPTY_STRING_ARRAY,
new Class<?>[] {AnnotatedFooConfigInnerClassTestCase.FooConfig.class},
EMPTY_STRING_ARRAY, contextLoader);
ConfigurableApplicationContext context = contextLoader.loadContextForAotProcessing(mergedConfig);
ConfigurableApplicationContext context = contextLoader.loadContextForAotProcessing(mergedConfig, new RuntimeHints());
assertThat(context.isActive()).as("ApplicationContext is active").isFalse();
assertThat(Arrays.stream(context.getBeanDefinitionNames())).anyMatch(name -> name.contains("FooConfig"));
context.close();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@@ -21,6 +21,7 @@ import java.util.Arrays;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
@@ -181,7 +182,7 @@ class DelegatingSmartContextLoaderTests {
private void assertApplicationContextLoadsForAotProcessing(MergedContextConfiguration mergedConfig,
String expectedBeanDefName) throws Exception {
ApplicationContext context = loader.loadContextForAotProcessing(mergedConfig);
ApplicationContext context = loader.loadContextForAotProcessing(mergedConfig, new RuntimeHints());
assertThat(context).isInstanceOf(ConfigurableApplicationContext.class);
ConfigurableApplicationContext cac = (ConfigurableApplicationContext) context;
assertThat(cac.isActive()).as("ApplicationContext is active").isFalse();