Skip to content

Commit 772d801

Browse files
committed
Test the ApplicationContextAotGenerator with the agent
This commit adds new tests for the `ApplicationContextAotGenerator`, this time leveraging the `RuntimeHintsAgent` that checks the need for runtime hints at runtime. See gh-27981
1 parent 52ec031 commit 772d801

File tree

2 files changed

+133
-0
lines changed

2 files changed

+133
-0
lines changed

spring-context/spring-context.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
plugins {
2+
id 'org.springframework.build.runtimehints-agent'
3+
}
4+
15
description = "Spring Context"
26

37
apply plugin: "kotlin"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.context.generator;
18+
19+
import java.util.function.BiConsumer;
20+
21+
import org.junit.jupiter.api.Test;
22+
23+
import org.springframework.aot.generate.DefaultGenerationContext;
24+
import org.springframework.aot.generate.InMemoryGeneratedFiles;
25+
import org.springframework.aot.hint.MemberCategory;
26+
import org.springframework.aot.hint.RuntimeHints;
27+
import org.springframework.aot.hint.RuntimeHintsRegistrar;
28+
import org.springframework.aot.hint.TypeReference;
29+
import org.springframework.aot.test.agent.EnabledIfRuntimeHintsAgent;
30+
import org.springframework.aot.test.agent.RuntimeHintsInvocations;
31+
import org.springframework.aot.test.agent.RuntimeHintsRecorder;
32+
import org.springframework.aot.test.generator.compile.TestCompiler;
33+
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
34+
import org.springframework.beans.factory.config.BeanDefinition;
35+
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
36+
import org.springframework.beans.factory.support.RootBeanDefinition;
37+
import org.springframework.context.ApplicationContextInitializer;
38+
import org.springframework.context.annotation.AnnotationConfigUtils;
39+
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
40+
import org.springframework.context.aot.ApplicationContextAotGenerator;
41+
import org.springframework.context.support.GenericApplicationContext;
42+
import org.springframework.context.testfixture.context.generator.SimpleComponent;
43+
import org.springframework.context.testfixture.context.generator.annotation.AutowiredComponent;
44+
import org.springframework.context.testfixture.context.generator.annotation.InitDestroyComponent;
45+
import org.springframework.core.testfixture.aot.generate.TestGenerationContext;
46+
47+
import static org.assertj.core.api.Assertions.assertThat;
48+
49+
/**
50+
* Tests the {@link org.springframework.aot.hint.RuntimeHints} generation in {@link ApplicationContextAotGenerator}.
51+
*
52+
* @author Brian Clozel
53+
* @author Stephane Nicoll
54+
*/
55+
@EnabledIfRuntimeHintsAgent
56+
class ApplicationContextAotGeneratorRuntimeHintsTests {
57+
58+
@Test
59+
void generateApplicationContextWithSimpleBean() {
60+
GenericApplicationContext context = new GenericApplicationContext();
61+
context.registerBeanDefinition("test", new RootBeanDefinition(SimpleComponent.class));
62+
compile(context, (hints, invocations) -> assertThat(invocations).match(hints));
63+
}
64+
65+
@Test
66+
void generateApplicationContextWithAutowiring() {
67+
GenericApplicationContext context = new GenericApplicationContext();
68+
context.registerBeanDefinition(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME,
69+
BeanDefinitionBuilder.rootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class)
70+
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
71+
context.registerBeanDefinition("autowiredComponent", new RootBeanDefinition(AutowiredComponent.class));
72+
context.registerBeanDefinition("number", BeanDefinitionBuilder.rootBeanDefinition(Integer.class, "valueOf")
73+
.addConstructorArgValue("42").getBeanDefinition());
74+
compile(context, (hints, invocations) -> assertThat(invocations).match(hints));
75+
}
76+
77+
@Test
78+
void generateApplicationContextWithInitDestroyMethods() {
79+
GenericApplicationContext context = new GenericApplicationContext();
80+
context.registerBeanDefinition(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME,
81+
BeanDefinitionBuilder.rootBeanDefinition(CommonAnnotationBeanPostProcessor.class)
82+
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
83+
context.registerBeanDefinition("initDestroyComponent", new RootBeanDefinition(InitDestroyComponent.class));
84+
compile(context, (hints, invocations) -> assertThat(invocations).withRegistrar(new InitDestroyIssueRegistrar()).match(hints));
85+
}
86+
87+
@Test
88+
void generateApplicationContextWithMultipleInitDestroyMethods() {
89+
GenericApplicationContext context = new GenericApplicationContext();
90+
context.registerBeanDefinition(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME,
91+
BeanDefinitionBuilder.rootBeanDefinition(CommonAnnotationBeanPostProcessor.class)
92+
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
93+
RootBeanDefinition beanDefinition = new RootBeanDefinition(InitDestroyComponent.class);
94+
beanDefinition.setInitMethodName("customInit");
95+
beanDefinition.setDestroyMethodName("customDestroy");
96+
context.registerBeanDefinition("initDestroyComponent", beanDefinition);
97+
compile(context, (hints, invocations) -> assertThat(invocations).withRegistrar(new InitDestroyIssueRegistrar()).match(hints));
98+
}
99+
100+
// TODO: Remove once https://github.com/spring-projects/spring-framework/issues/28215 is fixed
101+
static class InitDestroyIssueRegistrar implements RuntimeHintsRegistrar {
102+
@Override
103+
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
104+
hints.reflection()
105+
.registerType(TypeReference.of(InitDestroyComponent.class.getName()), typeHint ->
106+
typeHint.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS));
107+
}
108+
}
109+
110+
@SuppressWarnings({"rawtypes", "unchecked"})
111+
private void compile(GenericApplicationContext applicationContext, BiConsumer<RuntimeHints, RuntimeHintsInvocations> initializationResult) {
112+
ApplicationContextAotGenerator generator = new ApplicationContextAotGenerator();
113+
InMemoryGeneratedFiles generatedFiles = new InMemoryGeneratedFiles();
114+
DefaultGenerationContext generationContext = new TestGenerationContext(generatedFiles);
115+
generator.generateApplicationContext(applicationContext, generationContext);
116+
generationContext.writeGeneratedContent();
117+
TestCompiler.forSystem().withFiles(generatedFiles).compile(compiled -> {
118+
ApplicationContextInitializer instance = compiled.getInstance(ApplicationContextInitializer.class);
119+
GenericApplicationContext freshContext = new GenericApplicationContext();
120+
RuntimeHintsInvocations recordedInvocations = RuntimeHintsRecorder.record(() -> {
121+
instance.initialize(freshContext);
122+
freshContext.refresh();
123+
freshContext.close();
124+
});
125+
initializationResult.accept(generationContext.getRuntimeHints(), recordedInvocations);
126+
});
127+
}
128+
129+
}

0 commit comments

Comments
 (0)