Introduce StandardTestRuntimeHints in the TestContext framework
This commit introduces StandardTestRuntimeHints and migrates existing
hint registration from TestContextAotGenerator to this new class.
In addition, this commit removes a package cycle between context.aot and
context.web that was introduced in commit dc7c7ac22a.
See gh-29026, gh-29069
			
			
This commit is contained in:
		
							parent
							
								
									bf0ac84a05
								
							
						
					
					
						commit
						cbb30153fb
					
				| 
						 | 
					@ -16,7 +16,6 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package org.springframework.test.context.aot;
 | 
					package org.springframework.test.context.aot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.Arrays;
 | 
					 | 
				
			||||||
import java.util.Collections;
 | 
					import java.util.Collections;
 | 
				
			||||||
import java.util.concurrent.atomic.AtomicInteger;
 | 
					import java.util.concurrent.atomic.AtomicInteger;
 | 
				
			||||||
import java.util.stream.Stream;
 | 
					import java.util.stream.Stream;
 | 
				
			||||||
| 
						 | 
					@ -44,14 +43,12 @@ import org.springframework.test.context.ContextLoader;
 | 
				
			||||||
import org.springframework.test.context.MergedContextConfiguration;
 | 
					import org.springframework.test.context.MergedContextConfiguration;
 | 
				
			||||||
import org.springframework.test.context.SmartContextLoader;
 | 
					import org.springframework.test.context.SmartContextLoader;
 | 
				
			||||||
import org.springframework.test.context.TestContextBootstrapper;
 | 
					import org.springframework.test.context.TestContextBootstrapper;
 | 
				
			||||||
import org.springframework.test.context.web.WebMergedContextConfiguration;
 | 
					 | 
				
			||||||
import org.springframework.util.Assert;
 | 
					import org.springframework.util.Assert;
 | 
				
			||||||
import org.springframework.util.LinkedMultiValueMap;
 | 
					import org.springframework.util.LinkedMultiValueMap;
 | 
				
			||||||
import org.springframework.util.MultiValueMap;
 | 
					import org.springframework.util.MultiValueMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static org.springframework.aot.hint.MemberCategory.INVOKE_DECLARED_CONSTRUCTORS;
 | 
					import static org.springframework.aot.hint.MemberCategory.INVOKE_DECLARED_CONSTRUCTORS;
 | 
				
			||||||
import static org.springframework.aot.hint.MemberCategory.INVOKE_PUBLIC_METHODS;
 | 
					import static org.springframework.aot.hint.MemberCategory.INVOKE_PUBLIC_METHODS;
 | 
				
			||||||
import static org.springframework.util.ResourceUtils.CLASSPATH_URL_PREFIX;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * {@code TestContextAotGenerator} generates AOT artifacts for integration tests
 | 
					 * {@code TestContextAotGenerator} generates AOT artifacts for integration tests
 | 
				
			||||||
| 
						 | 
					@ -63,8 +60,6 @@ import static org.springframework.util.ResourceUtils.CLASSPATH_URL_PREFIX;
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class TestContextAotGenerator {
 | 
					public class TestContextAotGenerator {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private static final String SLASH = "/";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	private static final Log logger = LogFactory.getLog(TestContextAotGenerator.class);
 | 
						private static final Log logger = LogFactory.getLog(TestContextAotGenerator.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private final ApplicationContextAotGenerator aotGenerator = new ApplicationContextAotGenerator();
 | 
						private final ApplicationContextAotGenerator aotGenerator = new ApplicationContextAotGenerator();
 | 
				
			||||||
| 
						 | 
					@ -124,7 +119,6 @@ public class TestContextAotGenerator {
 | 
				
			||||||
		mergedConfigMappings.forEach((mergedConfig, testClasses) -> {
 | 
							mergedConfigMappings.forEach((mergedConfig, testClasses) -> {
 | 
				
			||||||
			logger.debug(LogMessage.format("Generating AOT artifacts for test classes %s",
 | 
								logger.debug(LogMessage.format("Generating AOT artifacts for test classes %s",
 | 
				
			||||||
					testClasses.stream().map(Class::getName).toList()));
 | 
										testClasses.stream().map(Class::getName).toList()));
 | 
				
			||||||
			registerHintsForMergedConfig(mergedConfig);
 | 
					 | 
				
			||||||
			try {
 | 
								try {
 | 
				
			||||||
				this.testRuntimeHintsRegistrars.forEach(registrar -> registrar.registerHints(this.runtimeHints,
 | 
									this.testRuntimeHintsRegistrars.forEach(registrar -> registrar.registerHints(this.runtimeHints,
 | 
				
			||||||
						mergedConfig, Collections.unmodifiableList(testClasses), getClass().getClassLoader()));
 | 
											mergedConfig, Collections.unmodifiableList(testClasses), getClass().getClassLoader()));
 | 
				
			||||||
| 
						 | 
					@ -247,51 +241,6 @@ public class TestContextAotGenerator {
 | 
				
			||||||
				.registerType(TypeReference.of(className), INVOKE_PUBLIC_METHODS);
 | 
									.registerType(TypeReference.of(className), INVOKE_PUBLIC_METHODS);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private void registerHintsForMergedConfig(MergedContextConfiguration mergedConfig) {
 | 
					 | 
				
			||||||
		// @ContextConfiguration(loader = ...)
 | 
					 | 
				
			||||||
		ContextLoader contextLoader = mergedConfig.getContextLoader();
 | 
					 | 
				
			||||||
		if (contextLoader != null) {
 | 
					 | 
				
			||||||
			registerDeclaredConstructors(contextLoader.getClass());
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// @ContextConfiguration(initializers = ...)
 | 
					 | 
				
			||||||
		mergedConfig.getContextInitializerClasses().forEach(this::registerDeclaredConstructors);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// @ContextConfiguration(locations = ...)
 | 
					 | 
				
			||||||
		registerHintsForClasspathResources(mergedConfig.getLocations());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// @TestPropertySource(locations = ... )
 | 
					 | 
				
			||||||
		registerHintsForClasspathResources(mergedConfig.getPropertySourceLocations());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (mergedConfig instanceof WebMergedContextConfiguration webMergedConfig) {
 | 
					 | 
				
			||||||
			String resourceBasePath = webMergedConfig.getResourceBasePath();
 | 
					 | 
				
			||||||
			if (resourceBasePath.startsWith(CLASSPATH_URL_PREFIX)) {
 | 
					 | 
				
			||||||
				String pattern = resourceBasePath.substring(CLASSPATH_URL_PREFIX.length());
 | 
					 | 
				
			||||||
				if (!pattern.startsWith(SLASH)) {
 | 
					 | 
				
			||||||
					pattern = SLASH + pattern;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				if (!pattern.endsWith(SLASH)) {
 | 
					 | 
				
			||||||
					pattern += SLASH;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				pattern += "*";
 | 
					 | 
				
			||||||
				this.runtimeHints.resources().registerPattern(pattern);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	private void registerHintsForClasspathResources(String... locations) {
 | 
					 | 
				
			||||||
		Arrays.stream(locations)
 | 
					 | 
				
			||||||
				.filter(location -> location.startsWith(CLASSPATH_URL_PREFIX))
 | 
					 | 
				
			||||||
				.map(location -> {
 | 
					 | 
				
			||||||
					location = location.substring(CLASSPATH_URL_PREFIX.length());
 | 
					 | 
				
			||||||
					if (!location.startsWith(SLASH)) {
 | 
					 | 
				
			||||||
						location = SLASH + location;
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					return location;
 | 
					 | 
				
			||||||
				})
 | 
					 | 
				
			||||||
				.forEach(this.runtimeHints.resources()::registerPattern);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	private void registerDeclaredConstructors(Class<?> type) {
 | 
						private void registerDeclaredConstructors(Class<?> type) {
 | 
				
			||||||
		ReflectionHints reflectionHints = this.runtimeHints.reflection();
 | 
							ReflectionHints reflectionHints = this.runtimeHints.reflection();
 | 
				
			||||||
		reflectionHints.registerType(type, INVOKE_DECLARED_CONSTRUCTORS);
 | 
							reflectionHints.registerType(type, INVOKE_DECLARED_CONSTRUCTORS);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,106 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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.hint;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.springframework.aot.hint.RuntimeHints;
 | 
				
			||||||
 | 
					import org.springframework.test.context.ContextLoader;
 | 
				
			||||||
 | 
					import org.springframework.test.context.MergedContextConfiguration;
 | 
				
			||||||
 | 
					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.util.ResourceUtils.CLASSPATH_URL_PREFIX;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * {@link TestRuntimeHintsRegistrar} implementation that registers run-time hints
 | 
				
			||||||
 | 
					 * for standard functionality in the <em>Spring TestContext Framework</em>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Sam Brannen
 | 
				
			||||||
 | 
					 * @since 6.0
 | 
				
			||||||
 | 
					 * @see TestContextRuntimeHints
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class StandardTestRuntimeHints implements TestRuntimeHintsRegistrar {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static final String SLASH = "/";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public void registerHints(RuntimeHints runtimeHints, MergedContextConfiguration mergedConfig,
 | 
				
			||||||
 | 
								List<Class<?>> testClasses, ClassLoader classLoader) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							registerHintsForMergedContextConfiguration(runtimeHints, mergedConfig);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private void registerHintsForMergedContextConfiguration(
 | 
				
			||||||
 | 
								RuntimeHints runtimeHints, MergedContextConfiguration mergedConfig) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// @ContextConfiguration(loader = ...)
 | 
				
			||||||
 | 
							ContextLoader contextLoader = mergedConfig.getContextLoader();
 | 
				
			||||||
 | 
							if (contextLoader != null) {
 | 
				
			||||||
 | 
								registerDeclaredConstructors(runtimeHints, contextLoader.getClass());
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// @ContextConfiguration(initializers = ...)
 | 
				
			||||||
 | 
							mergedConfig.getContextInitializerClasses()
 | 
				
			||||||
 | 
									.forEach(clazz -> registerDeclaredConstructors(runtimeHints, clazz));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// @ContextConfiguration(locations = ...)
 | 
				
			||||||
 | 
							registerClasspathResources(runtimeHints, mergedConfig.getLocations());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// @TestPropertySource(locations = ... )
 | 
				
			||||||
 | 
							registerClasspathResources(runtimeHints, mergedConfig.getPropertySourceLocations());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// @WebAppConfiguration(value = ...)
 | 
				
			||||||
 | 
							if (mergedConfig instanceof WebMergedContextConfiguration webConfig) {
 | 
				
			||||||
 | 
								registerClasspathResourceDirectoryStructure(runtimeHints, webConfig.getResourceBasePath());
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private void registerDeclaredConstructors(RuntimeHints runtimeHints, Class<?> type) {
 | 
				
			||||||
 | 
							runtimeHints.reflection().registerType(type, INVOKE_DECLARED_CONSTRUCTORS);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private void registerClasspathResources(RuntimeHints runtimeHints, String... locations) {
 | 
				
			||||||
 | 
							Arrays.stream(locations)
 | 
				
			||||||
 | 
									.filter(location -> location.startsWith(CLASSPATH_URL_PREFIX))
 | 
				
			||||||
 | 
									.map(this::cleanClasspathResource)
 | 
				
			||||||
 | 
									.forEach(runtimeHints.resources()::registerPattern);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private void registerClasspathResourceDirectoryStructure(RuntimeHints runtimeHints, String directory) {
 | 
				
			||||||
 | 
							if (directory.startsWith(CLASSPATH_URL_PREFIX)) {
 | 
				
			||||||
 | 
								String pattern = cleanClasspathResource(directory);
 | 
				
			||||||
 | 
								if (!pattern.endsWith(SLASH)) {
 | 
				
			||||||
 | 
									pattern += SLASH;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								pattern += "*";
 | 
				
			||||||
 | 
								runtimeHints.resources().registerPattern(pattern);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private String cleanClasspathResource(String location) {
 | 
				
			||||||
 | 
							location = location.substring(CLASSPATH_URL_PREFIX.length());
 | 
				
			||||||
 | 
							if (!location.startsWith(SLASH)) {
 | 
				
			||||||
 | 
								location = SLASH + location;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return location;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -34,6 +34,7 @@ import org.springframework.util.ClassUtils;
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @author Sam Brannen
 | 
					 * @author Sam Brannen
 | 
				
			||||||
 * @since 6.0
 | 
					 * @since 6.0
 | 
				
			||||||
 | 
					 * @see StandardTestRuntimeHints
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class TestContextRuntimeHints implements RuntimeHintsRegistrar {
 | 
					class TestContextRuntimeHints implements RuntimeHintsRegistrar {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,2 +1,5 @@
 | 
				
			||||||
org.springframework.aot.hint.RuntimeHintsRegistrar=\
 | 
					org.springframework.aot.hint.RuntimeHintsRegistrar=\
 | 
				
			||||||
org.springframework.test.context.aot.hint.TestContextRuntimeHints
 | 
					org.springframework.test.context.aot.hint.TestContextRuntimeHints
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					org.springframework.test.context.aot.TestRuntimeHintsRegistrar=\
 | 
				
			||||||
 | 
					org.springframework.test.context.aot.hint.StandardTestRuntimeHints
 | 
				
			||||||
		Loading…
	
		Reference in New Issue