[SPR-6184] Implemented recursive search for configuration classes; introduced LocationsResolver strategy in ContextLoaderUtils with ResourcePathLocationsResolver and ClassNameLocationsResolver implementations.
This commit is contained in:
parent
f3d125626d
commit
4dcc79d6eb
|
|
@ -32,7 +32,7 @@ import org.springframework.util.StringUtils;
|
||||||
/**
|
/**
|
||||||
* Utility methods for working with {@link ContextLoader ContextLoaders}.
|
* Utility methods for working with {@link ContextLoader ContextLoaders}.
|
||||||
*
|
*
|
||||||
* <p>TODO: Consider refactoring into a stateful ContextLoaderResolver.
|
* <p>TODO Consider refactoring into a stateful ContextLoaderResolver.
|
||||||
*
|
*
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
|
|
@ -44,6 +44,9 @@ public abstract class ContextLoaderUtils {
|
||||||
|
|
||||||
private static final String STANDARD_DEFAULT_CONTEXT_LOADER_CLASS_NAME = "org.springframework.test.context.support.GenericXmlContextLoader";
|
private static final String STANDARD_DEFAULT_CONTEXT_LOADER_CLASS_NAME = "org.springframework.test.context.support.GenericXmlContextLoader";
|
||||||
|
|
||||||
|
private static final ClassNameLocationsResolver classNameLocationsResolver = new ClassNameLocationsResolver();
|
||||||
|
private static final ResourcePathLocationsResolver resourcePathLocationsResolver = new ResourcePathLocationsResolver();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO Document resolveContextLoader().
|
* TODO Document resolveContextLoader().
|
||||||
|
|
@ -164,53 +167,29 @@ public abstract class ContextLoaderUtils {
|
||||||
|
|
||||||
Class<ContextConfiguration> annotationType = ContextConfiguration.class;
|
Class<ContextConfiguration> annotationType = ContextConfiguration.class;
|
||||||
Class<?> declaringClass = AnnotationUtils.findAnnotationDeclaringClass(annotationType, clazz);
|
Class<?> declaringClass = AnnotationUtils.findAnnotationDeclaringClass(annotationType, clazz);
|
||||||
Assert.notNull(declaringClass, "Could not find an 'annotation declaring class' for annotation type ["
|
Assert.notNull(declaringClass, String.format(
|
||||||
+ annotationType + "] and class [" + clazz + "]");
|
"Could not find an 'annotation declaring class' for annotation type [%s] and class [%s]", annotationType,
|
||||||
|
clazz));
|
||||||
|
|
||||||
boolean processConfigurationClasses = (contextLoader instanceof ResourceTypeAwareContextLoader)
|
boolean processConfigurationClasses = (contextLoader instanceof ResourceTypeAwareContextLoader)
|
||||||
&& ((ResourceTypeAwareContextLoader) contextLoader).supportsClassResources();
|
&& ((ResourceTypeAwareContextLoader) contextLoader).supportsClassResources();
|
||||||
|
LocationsResolver locationsResolver = processConfigurationClasses ? classNameLocationsResolver
|
||||||
return processConfigurationClasses ? //
|
: resourcePathLocationsResolver;
|
||||||
resolveConfigurationClassNames(contextLoader, annotationType, declaringClass)
|
|
||||||
: resolveStringLocations(contextLoader, annotationType, declaringClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO Document resolveStringLocations().
|
|
||||||
*
|
|
||||||
* @param contextLoader
|
|
||||||
* @param annotationType
|
|
||||||
* @param declaringClass
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private static String[] resolveStringLocations(ContextLoader contextLoader,
|
|
||||||
Class<ContextConfiguration> annotationType, Class<?> declaringClass) {
|
|
||||||
|
|
||||||
List<String> locationsList = new ArrayList<String>();
|
List<String> locationsList = new ArrayList<String>();
|
||||||
|
|
||||||
while (declaringClass != null) {
|
while (declaringClass != null) {
|
||||||
ContextConfiguration contextConfiguration = declaringClass.getAnnotation(annotationType);
|
ContextConfiguration contextConfiguration = declaringClass.getAnnotation(annotationType);
|
||||||
|
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace(String.format("Retrieved @ContextConfiguration [%s] for declaring class [%s].",
|
logger.trace(String.format("Retrieved @ContextConfiguration [%s] for declaring class [%s].",
|
||||||
contextConfiguration, declaringClass));
|
contextConfiguration, declaringClass));
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] valueLocations = contextConfiguration.value();
|
String[] resolvedLocations = locationsResolver.resolveLocations(contextConfiguration, declaringClass);
|
||||||
String[] locations = contextConfiguration.locations();
|
String[] processedLocations = contextLoader.processLocations(declaringClass, resolvedLocations);
|
||||||
if (!ObjectUtils.isEmpty(valueLocations) && !ObjectUtils.isEmpty(locations)) {
|
locationsList.addAll(0, Arrays.<String> asList(processedLocations));
|
||||||
String msg = String.format(
|
|
||||||
"Test class [%s] has been configured with @ContextConfiguration's 'value' [%s] and 'locations' [%s] attributes. Only one declaration of resource locations is permitted per @ContextConfiguration annotation.",
|
|
||||||
declaringClass, ObjectUtils.nullSafeToString(valueLocations),
|
|
||||||
ObjectUtils.nullSafeToString(locations));
|
|
||||||
logger.error(msg);
|
|
||||||
throw new IllegalStateException(msg);
|
|
||||||
}
|
|
||||||
else if (!ObjectUtils.isEmpty(valueLocations)) {
|
|
||||||
locations = valueLocations;
|
|
||||||
}
|
|
||||||
|
|
||||||
locations = contextLoader.processLocations(declaringClass, locations);
|
|
||||||
locationsList.addAll(0, Arrays.<String> asList(locations));
|
|
||||||
declaringClass = contextConfiguration.inheritLocations() ? AnnotationUtils.findAnnotationDeclaringClass(
|
declaringClass = contextConfiguration.inheritLocations() ? AnnotationUtils.findAnnotationDeclaringClass(
|
||||||
annotationType, declaringClass.getSuperclass()) : null;
|
annotationType, declaringClass.getSuperclass()) : null;
|
||||||
}
|
}
|
||||||
|
|
@ -218,25 +197,39 @@ public abstract class ContextLoaderUtils {
|
||||||
return locationsList.toArray(new String[locationsList.size()]);
|
return locationsList.toArray(new String[locationsList.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO Document resolveConfigClassNames().
|
|
||||||
*
|
|
||||||
* @param contextLoader
|
|
||||||
* @param annotationType
|
|
||||||
* @param declaringClass
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private static String[] resolveConfigurationClassNames(ContextLoader contextLoader,
|
|
||||||
Class<ContextConfiguration> annotationType, Class<?> declaringClass) {
|
|
||||||
|
|
||||||
// TODO [SPR-6184] Implement recursive search for configuration classes.
|
private static interface LocationsResolver {
|
||||||
|
|
||||||
ContextConfiguration contextConfiguration = declaringClass.getAnnotation(annotationType);
|
String[] resolveLocations(ContextConfiguration contextConfiguration, Class<?> declaringClass);
|
||||||
if (logger.isTraceEnabled()) {
|
|
||||||
logger.trace(String.format("Retrieved @ContextConfiguration [%s] for declaring class [%s].",
|
|
||||||
contextConfiguration, declaringClass));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class ResourcePathLocationsResolver implements LocationsResolver {
|
||||||
|
|
||||||
|
public String[] resolveLocations(ContextConfiguration contextConfiguration, Class<?> declaringClass) {
|
||||||
|
|
||||||
|
String[] locations = contextConfiguration.locations();
|
||||||
|
String[] valueLocations = contextConfiguration.value();
|
||||||
|
|
||||||
|
if (!ObjectUtils.isEmpty(valueLocations) && !ObjectUtils.isEmpty(locations)) {
|
||||||
|
String msg = String.format(
|
||||||
|
"Test class [%s] has been configured with @ContextConfiguration's 'value' [%s] and 'locations' [%s] attributes. Only one declaration of resource locations is permitted per @ContextConfiguration annotation.",
|
||||||
|
declaringClass, ObjectUtils.nullSafeToString(valueLocations),
|
||||||
|
ObjectUtils.nullSafeToString(locations));
|
||||||
|
ContextLoaderUtils.logger.error(msg);
|
||||||
|
throw new IllegalStateException(msg);
|
||||||
|
}
|
||||||
|
else if (!ObjectUtils.isEmpty(valueLocations)) {
|
||||||
|
locations = valueLocations;
|
||||||
|
}
|
||||||
|
|
||||||
|
return locations;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ClassNameLocationsResolver implements LocationsResolver {
|
||||||
|
|
||||||
|
public String[] resolveLocations(ContextConfiguration contextConfiguration, Class<?> declaringClass) {
|
||||||
|
|
||||||
String[] classNames = null;
|
String[] classNames = null;
|
||||||
|
|
||||||
Class<?>[] configClasses = contextConfiguration.classes();
|
Class<?>[] configClasses = contextConfiguration.classes();
|
||||||
|
|
@ -248,7 +241,8 @@ public abstract class ContextLoaderUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return contextLoader.processLocations(declaringClass, classNames);
|
return classNames;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ import org.junit.runners.Suite.SuiteClasses;
|
||||||
import org.springframework.test.context.ClassLevelDirtiesContextTests;
|
import org.springframework.test.context.ClassLevelDirtiesContextTests;
|
||||||
import org.springframework.test.context.SpringRunnerContextCacheTests;
|
import org.springframework.test.context.SpringRunnerContextCacheTests;
|
||||||
import org.springframework.test.context.junit4.annotation.AnnotationConfigSpringJUnit4ClassRunnerAppCtxTests;
|
import org.springframework.test.context.junit4.annotation.AnnotationConfigSpringJUnit4ClassRunnerAppCtxTests;
|
||||||
|
import org.springframework.test.context.junit4.annotation.DefaultConfigClassBaseTests;
|
||||||
|
import org.springframework.test.context.junit4.annotation.DefaultConfigClassInheritedTests;
|
||||||
import org.springframework.test.context.junit4.orm.HibernateSessionFlushingTests;
|
import org.springframework.test.context.junit4.orm.HibernateSessionFlushingTests;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -50,6 +52,8 @@ StandardJUnit4FeaturesTests.class,//
|
||||||
StandardJUnit4FeaturesSpringRunnerTests.class,//
|
StandardJUnit4FeaturesSpringRunnerTests.class,//
|
||||||
SpringJUnit47ClassRunnerRuleTests.class,//
|
SpringJUnit47ClassRunnerRuleTests.class,//
|
||||||
AnnotationConfigSpringJUnit4ClassRunnerAppCtxTests.class,//
|
AnnotationConfigSpringJUnit4ClassRunnerAppCtxTests.class,//
|
||||||
|
DefaultConfigClassBaseTests.class,//
|
||||||
|
DefaultConfigClassInheritedTests.class,//
|
||||||
ExpectedExceptionSpringRunnerTests.class,//
|
ExpectedExceptionSpringRunnerTests.class,//
|
||||||
TimedSpringRunnerTests.class,//
|
TimedSpringRunnerTests.class,//
|
||||||
RepeatedSpringRunnerTests.class,//
|
RepeatedSpringRunnerTests.class,//
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
*/
|
*/
|
||||||
@ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = PojoAndStringConfig.class)
|
@ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = PojoAndStringConfig.class, inheritLocations = false)
|
||||||
public class AnnotationConfigSpringJUnit4ClassRunnerAppCtxTests extends SpringJUnit4ClassRunnerAppCtxTests {
|
public class AnnotationConfigSpringJUnit4ClassRunnerAppCtxTests extends SpringJUnit4ClassRunnerAppCtxTests {
|
||||||
/* all tests are in the parent class. */
|
/* all tests are in the parent class. */
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,14 +28,10 @@ import org.junit.runners.Suite.SuiteClasses;
|
||||||
*/
|
*/
|
||||||
@RunWith(Suite.class)
|
@RunWith(Suite.class)
|
||||||
// Note: the following 'multi-line' layout is for enhanced code readability.
|
// Note: the following 'multi-line' layout is for enhanced code readability.
|
||||||
@SuiteClasses({
|
@SuiteClasses({//
|
||||||
|
AnnotationConfigSpringJUnit4ClassRunnerAppCtxTests.class,//
|
||||||
AnnotationConfigSpringJUnit4ClassRunnerAppCtxTests.class,
|
DefaultConfigClassBaseTests.class,//
|
||||||
|
DefaultConfigClassInheritedTests.class //
|
||||||
DefaultConfigClassBaseTests.class,
|
|
||||||
|
|
||||||
DefaultConfigClassInheritedTests.class
|
|
||||||
|
|
||||||
})
|
})
|
||||||
public class AnnotationConfigSuiteTests {
|
public class AnnotationConfigSuiteTests {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ package org.springframework.test.context.junit4.annotation;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.beans.Pet;
|
import org.springframework.beans.Pet;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
@ -31,7 +30,6 @@ import org.springframework.test.context.ContextConfiguration;
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
*/
|
*/
|
||||||
@Ignore("[SPR-6184] Disabled until ContextLoaderUtils supports recursive search for configuration classes")
|
|
||||||
@ContextConfiguration
|
@ContextConfiguration
|
||||||
public class DefaultConfigClassInheritedTests extends DefaultConfigClassBaseTests {
|
public class DefaultConfigClassInheritedTests extends DefaultConfigClassBaseTests {
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue