Browse Source
This commit introduces a new loadContextForAotProcessing(...) variant in AotContextLoader which accepts a RuntimeHints argument. This new method is an interface default method which delegates to the existing loadContextForAotProcessing(MergedContextConfiguration) variant for backward compatibility. In addition, the original loadContextForAotProcessing(...) variant is now deprecated and has been converted to an interface default method which throws an UnsupportedOperationException. Note, however, that the framework now only invokes the new loadContextForAotProcessing(...) variant within TestContextAotGenerator. Closes gh-34513pull/34656/head
10 changed files with 396 additions and 17 deletions
@ -0,0 +1,49 @@
@@ -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(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,87 @@
@@ -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); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,109 @@
@@ -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); |
||||
} |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue