Support meta-annotation attr overrides in the TCF
Prior to this commit, the Spring TestContext Framework (TCF) supported the use of test-related annotations as meta-annotations for composing custom test stereotype annotations; however, attributes in custom stereotypes could not be used to override meta-annotation attributes. This commit addresses this by allowing attributes from the following annotations (when used as meta-annotations) to be overridden in custom stereotypes. - @ContextConfiguration - @ActiveProfiles - @DirtiesContext - @TransactionConfiguration - @Timed - @TestExecutionListeners This support depends on functionality provided by AnnotatedElementUtils. See the 'Notes' below for further details and ramifications. Notes: - AnnotatedElementUtils does not support overrides for the 'value' attribute of an annotation. It is therefore not possible or not feasible to support meta-annotation attribute overrides for some test-related annotations. - @ContextHierarchy, @WebAppConfiguration, @Rollback, @Repeat, and @ProfileValueSourceConfiguration define single 'value' attributes which cannot be overridden via Spring's meta-annotation attribute support. - Although @IfProfileValue has 'values' and 'name' attributes, the typical usage scenario involves the 'value' attribute which is not supported for meta-annotation attribute overrides. Furthermore, 'name' and 'values' are so generic that it is deemed unfeasible to provide meta-annotation attribute override support for these. - @BeforeTransaction and @AfterTransaction do not define any attributes that can be overridden. - Support for meta-annotation attribute overrides for @Transactional is provided indirectly via SpringTransactionAnnotationParser. Implementation Details: - MetaAnnotationUtils.AnnotationDescriptor now provides access to the AnnotationAttributes for the described annotation. - MetaAnnotationUtils.AnnotationDescriptor now provides access to the root declaring class as well as the declaring class. - ContextLoaderUtils now retrieves AnnotationAttributes from AnnotationDescriptor to look up annotation attributes for @ContextConfiguration and @ActiveProfiles. - ContextConfigurationAttributes now provides a constructor to have its attributes sourced from an instance of AnnotationAttributes. - ContextLoaderUtils.resolveContextHierarchyAttributes() now throws an IllegalStateException if no class in the class hierarchy declares @ContextHierarchy. - TransactionalTestExecutionListener now uses AnnotatedElementUtils to look up annotation attributes for @TransactionConfiguration. - Implemented missing unit tests for @Rollback resolution in TransactionalTestExecutionListener. - SpringJUnit4ClassRunner now uses AnnotatedElementUtils to look up annotation attributes for @Timed. - TestContextManager now retrieves AnnotationAttributes from AnnotationDescriptor to look up annotation attributes for @TestExecutionListeners. - DirtiesContextTestExecutionListener now uses AnnotatedElementUtils to look up annotation attributes for @DirtiesContext. Issue: SPR-11038
This commit is contained in:
		
							parent
							
								
									c5779e2ed6
								
							
						
					
					
						commit
						64f593db8f
					
				|  | @ -20,9 +20,9 @@ import java.util.Arrays; | ||||||
| 
 | 
 | ||||||
| import org.apache.commons.logging.Log; | import org.apache.commons.logging.Log; | ||||||
| import org.apache.commons.logging.LogFactory; | import org.apache.commons.logging.LogFactory; | ||||||
| 
 |  | ||||||
| import org.springframework.context.ApplicationContextInitializer; | import org.springframework.context.ApplicationContextInitializer; | ||||||
| import org.springframework.context.ConfigurableApplicationContext; | import org.springframework.context.ConfigurableApplicationContext; | ||||||
|  | import org.springframework.core.annotation.AnnotationAttributes; | ||||||
| import org.springframework.core.style.ToStringCreator; | import org.springframework.core.style.ToStringCreator; | ||||||
| import org.springframework.util.Assert; | import org.springframework.util.Assert; | ||||||
| import org.springframework.util.ObjectUtils; | import org.springframework.util.ObjectUtils; | ||||||
|  | @ -68,21 +68,29 @@ public class ContextConfigurationAttributes { | ||||||
| 	 * @throws IllegalStateException if both the locations and value attributes have been declared | 	 * @throws IllegalStateException if both the locations and value attributes have been declared | ||||||
| 	 */ | 	 */ | ||||||
| 	private static String[] resolveLocations(Class<?> declaringClass, ContextConfiguration contextConfiguration) { | 	private static String[] resolveLocations(Class<?> declaringClass, ContextConfiguration contextConfiguration) { | ||||||
|  | 		return resolveLocations(declaringClass, contextConfiguration.locations(), contextConfiguration.value()); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Resolve resource locations from the supplied {@code locations} and | ||||||
|  | 	 * {@code value} arrays, which correspond to attributes of the same names in | ||||||
|  | 	 * the {@link ContextConfiguration} annotation. | ||||||
|  | 	 * | ||||||
|  | 	 * @throws IllegalStateException if both the locations and value attributes have been declared | ||||||
|  | 	 */ | ||||||
|  | 	private static String[] resolveLocations(Class<?> declaringClass, String[] locations, String[] value) { | ||||||
| 		Assert.notNull(declaringClass, "declaringClass must not be null"); | 		Assert.notNull(declaringClass, "declaringClass must not be null"); | ||||||
| 
 | 
 | ||||||
| 		String[] locations = contextConfiguration.locations(); | 		if (!ObjectUtils.isEmpty(value) && !ObjectUtils.isEmpty(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 " | 			String msg = String.format("Test class [%s] has been configured with @ContextConfiguration's 'value' %s " | ||||||
| 					+ "and 'locations' %s attributes. Only one declaration of resource " | 					+ "and 'locations' %s attributes. Only one declaration of resource " | ||||||
| 					+ "locations is permitted per @ContextConfiguration annotation.", declaringClass.getName(), | 					+ "locations is permitted per @ContextConfiguration annotation.", declaringClass.getName(), | ||||||
| 				ObjectUtils.nullSafeToString(valueLocations), ObjectUtils.nullSafeToString(locations)); | 				ObjectUtils.nullSafeToString(value), ObjectUtils.nullSafeToString(locations)); | ||||||
| 			logger.error(msg); | 			logger.error(msg); | ||||||
| 			throw new IllegalStateException(msg); | 			throw new IllegalStateException(msg); | ||||||
| 		} | 		} | ||||||
| 		else if (!ObjectUtils.isEmpty(valueLocations)) { | 		else if (!ObjectUtils.isEmpty(value)) { | ||||||
| 			locations = valueLocations; | 			locations = value; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		return locations; | 		return locations; | ||||||
|  | @ -101,6 +109,25 @@ public class ContextConfigurationAttributes { | ||||||
| 			contextConfiguration.inheritInitializers(), contextConfiguration.name(), contextConfiguration.loader()); | 			contextConfiguration.inheritInitializers(), contextConfiguration.name(), contextConfiguration.loader()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Construct a new {@link ContextConfigurationAttributes} instance for the | ||||||
|  | 	 * supplied {@link ContextConfiguration @ContextConfiguration} annotation and | ||||||
|  | 	 * the {@linkplain Class test class} that declared it. | ||||||
|  | 	 * @param declaringClass the test class that declared {@code @ContextConfiguration} | ||||||
|  | 	 * @param annAttrs the annotation attributes from which to retrieve the attributes | ||||||
|  | 	 */ | ||||||
|  | 	@SuppressWarnings("unchecked") | ||||||
|  | 	public ContextConfigurationAttributes(Class<?> declaringClass, AnnotationAttributes annAttrs) { | ||||||
|  | 		this( | ||||||
|  | 			declaringClass, | ||||||
|  | 			resolveLocations(declaringClass, annAttrs.getStringArray("locations"), annAttrs.getStringArray("value")), | ||||||
|  | 			annAttrs.getClassArray("classes"), | ||||||
|  | 			annAttrs.getBoolean("inheritLocations"), | ||||||
|  | 			(Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>[]) annAttrs.getClassArray("initializers"), | ||||||
|  | 			annAttrs.getBoolean("inheritInitializers"), annAttrs.getString("name"), | ||||||
|  | 			(Class<? extends ContextLoader>) annAttrs.getClass("loader")); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * Construct a new {@link ContextConfigurationAttributes} instance for the | 	 * Construct a new {@link ContextConfigurationAttributes} instance for the | ||||||
| 	 * {@linkplain Class test class} that declared the | 	 * {@linkplain Class test class} that declared the | ||||||
|  |  | ||||||
|  | @ -31,6 +31,7 @@ import org.apache.commons.logging.Log; | ||||||
| import org.apache.commons.logging.LogFactory; | import org.apache.commons.logging.LogFactory; | ||||||
| import org.springframework.context.ApplicationContextInitializer; | import org.springframework.context.ApplicationContextInitializer; | ||||||
| import org.springframework.context.ConfigurableApplicationContext; | import org.springframework.context.ConfigurableApplicationContext; | ||||||
|  | import org.springframework.core.annotation.AnnotationAttributes; | ||||||
| import org.springframework.core.annotation.AnnotationUtils; | import org.springframework.core.annotation.AnnotationUtils; | ||||||
| import org.springframework.test.context.MetaAnnotationUtils.AnnotationDescriptor; | import org.springframework.test.context.MetaAnnotationUtils.AnnotationDescriptor; | ||||||
| import org.springframework.test.context.MetaAnnotationUtils.UntypedAnnotationDescriptor; | import org.springframework.test.context.MetaAnnotationUtils.UntypedAnnotationDescriptor; | ||||||
|  | @ -192,9 +193,9 @@ abstract class ContextLoaderUtils { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * Convenience method for creating a {@link ContextConfigurationAttributes} instance | 	 * Convenience method for creating a {@link ContextConfigurationAttributes} | ||||||
| 	 * from the supplied {@link ContextConfiguration} and declaring class and then adding | 	 * instance from the supplied {@link ContextConfiguration} annotation and | ||||||
| 	 * the attributes to the supplied list. | 	 * declaring class and then adding the attributes to the supplied list. | ||||||
| 	 */ | 	 */ | ||||||
| 	private static void convertContextConfigToConfigAttributesAndAddToList(ContextConfiguration contextConfiguration, | 	private static void convertContextConfigToConfigAttributesAndAddToList(ContextConfiguration contextConfiguration, | ||||||
| 			Class<?> declaringClass, final List<ContextConfigurationAttributes> attributesList) { | 			Class<?> declaringClass, final List<ContextConfigurationAttributes> attributesList) { | ||||||
|  | @ -211,6 +212,27 @@ abstract class ContextLoaderUtils { | ||||||
| 		attributesList.add(attributes); | 		attributesList.add(attributes); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Convenience method for creating a {@link ContextConfigurationAttributes} | ||||||
|  | 	 * instance from the supplied {@link AnnotationAttributes} and declaring | ||||||
|  | 	 * class and then adding the attributes to the supplied list. | ||||||
|  | 	 * | ||||||
|  | 	 * @since 4.0 | ||||||
|  | 	 */ | ||||||
|  | 	private static void convertAnnotationAttributesToConfigAttributesAndAddToList(AnnotationAttributes annAttrs, | ||||||
|  | 			Class<?> declaringClass, final List<ContextConfigurationAttributes> attributesList) { | ||||||
|  | 		if (logger.isTraceEnabled()) { | ||||||
|  | 			logger.trace(String.format("Retrieved @ContextConfiguration attributes [%s] for declaring class [%s].", | ||||||
|  | 				annAttrs, declaringClass.getName())); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		ContextConfigurationAttributes attributes = new ContextConfigurationAttributes(declaringClass, annAttrs); | ||||||
|  | 		if (logger.isTraceEnabled()) { | ||||||
|  | 			logger.trace("Resolved context configuration attributes: " + attributes); | ||||||
|  | 		} | ||||||
|  | 		attributesList.add(attributes); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * Resolve the list of lists of {@linkplain ContextConfigurationAttributes context | 	 * Resolve the list of lists of {@linkplain ContextConfigurationAttributes context | ||||||
| 	 * configuration attributes} for the supplied {@linkplain Class test class} and its | 	 * configuration attributes} for the supplied {@linkplain Class test class} and its | ||||||
|  | @ -243,6 +265,8 @@ abstract class ContextLoaderUtils { | ||||||
| 	 * <em>present</em> on the supplied class; or if a given class in the class hierarchy | 	 * <em>present</em> on the supplied class; or if a given class in the class hierarchy | ||||||
| 	 * declares both {@code @ContextConfiguration} and {@code @ContextHierarchy} as | 	 * declares both {@code @ContextConfiguration} and {@code @ContextHierarchy} as | ||||||
| 	 * top-level annotations. | 	 * top-level annotations. | ||||||
|  | 	 * @throws IllegalStateException if no class in the class hierarchy declares | ||||||
|  | 	 * {@code @ContextHierarchy}. | ||||||
| 	 * | 	 * | ||||||
| 	 * @since 3.2.2 | 	 * @since 3.2.2 | ||||||
| 	 * @see #buildContextHierarchyMap(Class) | 	 * @see #buildContextHierarchyMap(Class) | ||||||
|  | @ -251,6 +275,7 @@ abstract class ContextLoaderUtils { | ||||||
| 	@SuppressWarnings("unchecked") | 	@SuppressWarnings("unchecked") | ||||||
| 	static List<List<ContextConfigurationAttributes>> resolveContextHierarchyAttributes(Class<?> testClass) { | 	static List<List<ContextConfigurationAttributes>> resolveContextHierarchyAttributes(Class<?> testClass) { | ||||||
| 		Assert.notNull(testClass, "Class must not be null"); | 		Assert.notNull(testClass, "Class must not be null"); | ||||||
|  | 		Assert.state(findAnnotation(testClass, ContextHierarchy.class) != null, "@ContextHierarchy must be present"); | ||||||
| 
 | 
 | ||||||
| 		final Class<ContextConfiguration> contextConfigType = ContextConfiguration.class; | 		final Class<ContextConfiguration> contextConfigType = ContextConfiguration.class; | ||||||
| 		final Class<ContextHierarchy> contextHierarchyType = ContextHierarchy.class; | 		final Class<ContextHierarchy> contextHierarchyType = ContextHierarchy.class; | ||||||
|  | @ -263,17 +288,16 @@ abstract class ContextLoaderUtils { | ||||||
| 			contextConfigType.getName(), contextHierarchyType.getName(), testClass.getName())); | 			contextConfigType.getName(), contextHierarchyType.getName(), testClass.getName())); | ||||||
| 
 | 
 | ||||||
| 		while (descriptor != null) { | 		while (descriptor != null) { | ||||||
| 			Class<?> rootDeclaringClass = descriptor.getDeclaringClass(); | 			Class<?> rootDeclaringClass = descriptor.getRootDeclaringClass(); | ||||||
| 			Class<?> declaringClass = (descriptor.getStereotype() != null) ? descriptor.getStereotypeType() | 			Class<?> declaringClass = descriptor.getDeclaringClass(); | ||||||
| 					: rootDeclaringClass; |  | ||||||
| 
 | 
 | ||||||
| 			boolean contextConfigDeclaredLocally = isAnnotationDeclaredLocally(contextConfigType, declaringClass); | 			boolean contextConfigDeclaredLocally = isAnnotationDeclaredLocally(contextConfigType, declaringClass); | ||||||
| 			boolean contextHierarchyDeclaredLocally = isAnnotationDeclaredLocally(contextHierarchyType, declaringClass); | 			boolean contextHierarchyDeclaredLocally = isAnnotationDeclaredLocally(contextHierarchyType, declaringClass); | ||||||
| 
 | 
 | ||||||
| 			if (contextConfigDeclaredLocally && contextHierarchyDeclaredLocally) { | 			if (contextConfigDeclaredLocally && contextHierarchyDeclaredLocally) { | ||||||
| 				String msg = String.format("Test class [%s] has been configured with both @ContextConfiguration " | 				String msg = String.format("Class [%s] has been configured with both @ContextConfiguration " | ||||||
| 						+ "and @ContextHierarchy. Only one of these annotations may be declared on a test class " | 						+ "and @ContextHierarchy. Only one of these annotations may be declared on a test class " | ||||||
| 						+ "or custom stereotype annotation.", rootDeclaringClass.getName()); | 						+ "or custom stereotype annotation.", declaringClass.getName()); | ||||||
| 				logger.error(msg); | 				logger.error(msg); | ||||||
| 				throw new IllegalStateException(msg); | 				throw new IllegalStateException(msg); | ||||||
| 			} | 			} | ||||||
|  | @ -281,9 +305,8 @@ abstract class ContextLoaderUtils { | ||||||
| 			final List<ContextConfigurationAttributes> configAttributesList = new ArrayList<ContextConfigurationAttributes>(); | 			final List<ContextConfigurationAttributes> configAttributesList = new ArrayList<ContextConfigurationAttributes>(); | ||||||
| 
 | 
 | ||||||
| 			if (contextConfigDeclaredLocally) { | 			if (contextConfigDeclaredLocally) { | ||||||
| 				ContextConfiguration contextConfiguration = getAnnotation(declaringClass, contextConfigType); | 				convertAnnotationAttributesToConfigAttributesAndAddToList(descriptor.getAnnotationAttributes(), | ||||||
| 				convertContextConfigToConfigAttributesAndAddToList(contextConfiguration, declaringClass, | 					declaringClass, configAttributesList); | ||||||
| 					configAttributesList); |  | ||||||
| 			} | 			} | ||||||
| 			else if (contextHierarchyDeclaredLocally) { | 			else if (contextHierarchyDeclaredLocally) { | ||||||
| 				ContextHierarchy contextHierarchy = getAnnotation(declaringClass, contextHierarchyType); | 				ContextHierarchy contextHierarchy = getAnnotation(declaringClass, contextHierarchyType); | ||||||
|  | @ -293,7 +316,7 @@ abstract class ContextLoaderUtils { | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			else { | 			else { | ||||||
| 				// This should theoretically actually never happen... | 				// This should theoretically never happen... | ||||||
| 				String msg = String.format("Test class [%s] has been configured with neither @ContextConfiguration " | 				String msg = String.format("Test class [%s] has been configured with neither @ContextConfiguration " | ||||||
| 						+ "nor @ContextHierarchy as a class-level annotation.", rootDeclaringClass.getName()); | 						+ "nor @ContextHierarchy as a class-level annotation.", rootDeclaringClass.getName()); | ||||||
| 				logger.error(msg); | 				logger.error(msg); | ||||||
|  | @ -405,13 +428,9 @@ abstract class ContextLoaderUtils { | ||||||
| 			annotationType.getName(), testClass.getName())); | 			annotationType.getName(), testClass.getName())); | ||||||
| 
 | 
 | ||||||
| 		while (descriptor != null) { | 		while (descriptor != null) { | ||||||
| 			Class<?> rootDeclaringClass = descriptor.getDeclaringClass(); | 			convertAnnotationAttributesToConfigAttributesAndAddToList(descriptor.getAnnotationAttributes(), | ||||||
| 			Class<?> declaringClass = (descriptor.getStereotype() != null) ? descriptor.getStereotypeType() | 				descriptor.getDeclaringClass(), attributesList); | ||||||
| 					: rootDeclaringClass; | 			descriptor = findAnnotationDescriptor(descriptor.getRootDeclaringClass().getSuperclass(), annotationType); | ||||||
| 
 |  | ||||||
| 			convertContextConfigToConfigAttributesAndAddToList(descriptor.getAnnotation(), declaringClass, |  | ||||||
| 				attributesList); |  | ||||||
| 			descriptor = findAnnotationDescriptor(rootDeclaringClass.getSuperclass(), annotationType); |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		return attributesList; | 		return attributesList; | ||||||
|  | @ -489,20 +508,18 @@ abstract class ContextLoaderUtils { | ||||||
| 		final Set<String> activeProfiles = new HashSet<String>(); | 		final Set<String> activeProfiles = new HashSet<String>(); | ||||||
| 
 | 
 | ||||||
| 		while (descriptor != null) { | 		while (descriptor != null) { | ||||||
| 			Class<?> rootDeclaringClass = descriptor.getDeclaringClass(); | 			Class<?> declaringClass = descriptor.getDeclaringClass(); | ||||||
| 			Class<?> declaringClass = (descriptor.getStereotype() != null) ? descriptor.getStereotypeType() |  | ||||||
| 					: rootDeclaringClass; |  | ||||||
| 
 | 
 | ||||||
| 			ActiveProfiles annotation = descriptor.getAnnotation(); | 			AnnotationAttributes annAttrs = descriptor.getAnnotationAttributes(); | ||||||
| 			if (logger.isTraceEnabled()) { | 			if (logger.isTraceEnabled()) { | ||||||
| 				logger.trace(String.format("Retrieved @ActiveProfiles [%s] for declaring class [%s].", annotation, | 				logger.trace(String.format("Retrieved @ActiveProfiles attributes [%s] for declaring class [%s].", | ||||||
| 					declaringClass.getName())); | 					annAttrs, declaringClass.getName())); | ||||||
| 			} | 			} | ||||||
| 			validateActiveProfilesConfiguration(declaringClass, annotation); | 			validateActiveProfilesConfiguration(declaringClass, annAttrs); | ||||||
| 
 | 
 | ||||||
| 			String[] profiles = annotation.profiles(); | 			String[] profiles = annAttrs.getStringArray("profiles"); | ||||||
| 			String[] valueProfiles = annotation.value(); | 			String[] valueProfiles = annAttrs.getStringArray("value"); | ||||||
| 			Class<? extends ActiveProfilesResolver> resolverClass = annotation.resolver(); | 			Class<? extends ActiveProfilesResolver> resolverClass = annAttrs.getClass("resolver"); | ||||||
| 
 | 
 | ||||||
| 			boolean resolverDeclared = !ActiveProfilesResolver.class.equals(resolverClass); | 			boolean resolverDeclared = !ActiveProfilesResolver.class.equals(resolverClass); | ||||||
| 			boolean valueDeclared = !ObjectUtils.isEmpty(valueProfiles); | 			boolean valueDeclared = !ObjectUtils.isEmpty(valueProfiles); | ||||||
|  | @ -538,17 +555,17 @@ abstract class ContextLoaderUtils { | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			descriptor = annotation.inheritProfiles() ? findAnnotationDescriptor(rootDeclaringClass.getSuperclass(), | 			descriptor = annAttrs.getBoolean("inheritProfiles") ? findAnnotationDescriptor( | ||||||
| 				annotationType) : null; | 				descriptor.getRootDeclaringClass().getSuperclass(), annotationType) : null; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		return StringUtils.toStringArray(activeProfiles); | 		return StringUtils.toStringArray(activeProfiles); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	private static void validateActiveProfilesConfiguration(Class<?> declaringClass, ActiveProfiles annotation) { | 	private static void validateActiveProfilesConfiguration(Class<?> declaringClass, AnnotationAttributes annAttrs) { | ||||||
| 		String[] valueProfiles = annotation.value(); | 		String[] valueProfiles = annAttrs.getStringArray("value"); | ||||||
| 		String[] profiles = annotation.profiles(); | 		String[] profiles = annAttrs.getStringArray("profiles"); | ||||||
| 		Class<? extends ActiveProfilesResolver> resolverClass = annotation.resolver(); | 		Class<? extends ActiveProfilesResolver> resolverClass = annAttrs.getClass("resolver"); | ||||||
| 		boolean valueDeclared = !ObjectUtils.isEmpty(valueProfiles); | 		boolean valueDeclared = !ObjectUtils.isEmpty(valueProfiles); | ||||||
| 		boolean profilesDeclared = !ObjectUtils.isEmpty(profiles); | 		boolean profilesDeclared = !ObjectUtils.isEmpty(profiles); | ||||||
| 		boolean resolverDeclared = !ActiveProfilesResolver.class.equals(resolverClass); | 		boolean resolverDeclared = !ActiveProfilesResolver.class.equals(resolverClass); | ||||||
|  |  | ||||||
|  | @ -18,6 +18,8 @@ package org.springframework.test.context; | ||||||
| 
 | 
 | ||||||
| import java.lang.annotation.Annotation; | import java.lang.annotation.Annotation; | ||||||
| 
 | 
 | ||||||
|  | import org.springframework.core.annotation.AnnotatedElementUtils; | ||||||
|  | import org.springframework.core.annotation.AnnotationAttributes; | ||||||
| import org.springframework.core.style.ToStringCreator; | import org.springframework.core.style.ToStringCreator; | ||||||
| import org.springframework.util.Assert; | import org.springframework.util.Assert; | ||||||
| import org.springframework.util.ObjectUtils; | import org.springframework.util.ObjectUtils; | ||||||
|  | @ -124,7 +126,7 @@ abstract class MetaAnnotationUtils { | ||||||
| 	 * <p> | 	 * <p> | ||||||
| 	 * If the annotation is used as a meta-annotation, the descriptor also includes | 	 * If the annotation is used as a meta-annotation, the descriptor also includes | ||||||
| 	 * the {@linkplain #getStereotype() stereotype} on which the annotation is | 	 * the {@linkplain #getStereotype() stereotype} on which the annotation is | ||||||
| 	 * present. In such cases, the <em>declaring class</em> is not directly | 	 * present. In such cases, the <em>root declaring class</em> is not directly | ||||||
| 	 * annotated with the annotation but rather indirectly via the stereotype. | 	 * annotated with the annotation but rather indirectly via the stereotype. | ||||||
| 	 * | 	 * | ||||||
| 	 * <p> | 	 * <p> | ||||||
|  | @ -133,6 +135,7 @@ abstract class MetaAnnotationUtils { | ||||||
| 	 * properties of the {@code AnnotationDescriptor} would be as follows. | 	 * properties of the {@code AnnotationDescriptor} would be as follows. | ||||||
| 	 * | 	 * | ||||||
| 	 * <ul> | 	 * <ul> | ||||||
|  | 	 * <li>rootDeclaringClass: {@code TransactionalTests} class object</li> | ||||||
| 	 * <li>declaringClass: {@code TransactionalTests} class object</li> | 	 * <li>declaringClass: {@code TransactionalTests} class object</li> | ||||||
| 	 * <li>stereotype: {@code null}</li> | 	 * <li>stereotype: {@code null}</li> | ||||||
| 	 * <li>annotation: instance of the {@code Transactional} annotation</li> | 	 * <li>annotation: instance of the {@code Transactional} annotation</li> | ||||||
|  | @ -150,7 +153,8 @@ abstract class MetaAnnotationUtils { | ||||||
| 	 * properties of the {@code AnnotationDescriptor} would be as follows. | 	 * properties of the {@code AnnotationDescriptor} would be as follows. | ||||||
| 	 * | 	 * | ||||||
| 	 * <ul> | 	 * <ul> | ||||||
| 	 * <li>declaringClass: {@code UserRepositoryTests} class object</li> | 	 * <li>rootDeclaringClass: {@code UserRepositoryTests} class object</li> | ||||||
|  | 	 * <li>declaringClass: {@code RepositoryTests} class object</li> | ||||||
| 	 * <li>stereotype: instance of the {@code RepositoryTests} annotation</li> | 	 * <li>stereotype: instance of the {@code RepositoryTests} annotation</li> | ||||||
| 	 * <li>annotation: instance of the {@code Transactional} annotation</li> | 	 * <li>annotation: instance of the {@code Transactional} annotation</li> | ||||||
| 	 * </ul> | 	 * </ul> | ||||||
|  | @ -170,22 +174,31 @@ abstract class MetaAnnotationUtils { | ||||||
| 	 */ | 	 */ | ||||||
| 	public static class AnnotationDescriptor<T extends Annotation> { | 	public static class AnnotationDescriptor<T extends Annotation> { | ||||||
| 
 | 
 | ||||||
|  | 		private final Class<?> rootDeclaringClass; | ||||||
| 		private final Class<?> declaringClass; | 		private final Class<?> declaringClass; | ||||||
| 		private final Annotation stereotype; | 		private final Annotation stereotype; | ||||||
| 		private final T annotation; | 		private final T annotation; | ||||||
|  | 		private final AnnotationAttributes annotationAttributes; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 		public AnnotationDescriptor(Class<?> declaringClass, T annotation) { | 		public AnnotationDescriptor(Class<?> rootDeclaringClass, T annotation) { | ||||||
| 			this(declaringClass, null, annotation); | 			this(rootDeclaringClass, null, annotation); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		public AnnotationDescriptor(Class<?> declaringClass, Annotation stereotype, T annotation) { | 		public AnnotationDescriptor(Class<?> rootDeclaringClass, Annotation stereotype, T annotation) { | ||||||
| 			Assert.notNull(declaringClass, "declaringClass must not be null"); | 			Assert.notNull(rootDeclaringClass, "rootDeclaringClass must not be null"); | ||||||
| 			Assert.notNull(annotation, "annotation must not be null"); | 			Assert.notNull(annotation, "annotation must not be null"); | ||||||
| 
 | 
 | ||||||
| 			this.declaringClass = declaringClass; | 			this.rootDeclaringClass = rootDeclaringClass; | ||||||
|  | 			this.declaringClass = (stereotype != null) ? stereotype.annotationType() : rootDeclaringClass; | ||||||
| 			this.stereotype = stereotype; | 			this.stereotype = stereotype; | ||||||
| 			this.annotation = annotation; | 			this.annotation = annotation; | ||||||
|  | 			this.annotationAttributes = AnnotatedElementUtils.getAnnotationAttributes(rootDeclaringClass, | ||||||
|  | 				annotation.annotationType().getName()); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public Class<?> getRootDeclaringClass() { | ||||||
|  | 			return this.rootDeclaringClass; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		public Class<?> getDeclaringClass() { | 		public Class<?> getDeclaringClass() { | ||||||
|  | @ -200,6 +213,10 @@ abstract class MetaAnnotationUtils { | ||||||
| 			return this.annotation.annotationType(); | 			return this.annotation.annotationType(); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		public AnnotationAttributes getAnnotationAttributes() { | ||||||
|  | 			return this.annotationAttributes; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		public Annotation getStereotype() { | 		public Annotation getStereotype() { | ||||||
| 			return this.stereotype; | 			return this.stereotype; | ||||||
| 		} | 		} | ||||||
|  | @ -214,6 +231,7 @@ abstract class MetaAnnotationUtils { | ||||||
| 		@Override | 		@Override | ||||||
| 		public String toString() { | 		public String toString() { | ||||||
| 			return new ToStringCreator(this)// | 			return new ToStringCreator(this)// | ||||||
|  | 			.append("rootDeclaringClass", rootDeclaringClass)// | ||||||
| 			.append("declaringClass", declaringClass)// | 			.append("declaringClass", declaringClass)// | ||||||
| 			.append("stereotype", stereotype)// | 			.append("stereotype", stereotype)// | ||||||
| 			.append("annotation", annotation)// | 			.append("annotation", annotation)// | ||||||
|  |  | ||||||
|  | @ -28,6 +28,7 @@ import org.apache.commons.logging.Log; | ||||||
| import org.apache.commons.logging.LogFactory; | import org.apache.commons.logging.LogFactory; | ||||||
| import org.springframework.beans.BeanUtils; | import org.springframework.beans.BeanUtils; | ||||||
| import org.springframework.context.ApplicationContext; | import org.springframework.context.ApplicationContext; | ||||||
|  | import org.springframework.core.annotation.AnnotationAttributes; | ||||||
| import org.springframework.test.context.MetaAnnotationUtils.AnnotationDescriptor; | import org.springframework.test.context.MetaAnnotationUtils.AnnotationDescriptor; | ||||||
| import org.springframework.util.Assert; | import org.springframework.util.Assert; | ||||||
| import org.springframework.util.ObjectUtils; | import org.springframework.util.ObjectUtils; | ||||||
|  | @ -172,13 +173,13 @@ public class TestContextManager { | ||||||
| 	 * @param clazz the test class for which the listeners should be retrieved | 	 * @param clazz the test class for which the listeners should be retrieved | ||||||
| 	 * @return an array of TestExecutionListeners for the specified class | 	 * @return an array of TestExecutionListeners for the specified class | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@SuppressWarnings("unchecked") | ||||||
| 	private TestExecutionListener[] retrieveTestExecutionListeners(Class<?> clazz) { | 	private TestExecutionListener[] retrieveTestExecutionListeners(Class<?> clazz) { | ||||||
| 		Assert.notNull(clazz, "Class must not be null"); | 		Assert.notNull(clazz, "Class must not be null"); | ||||||
| 		Class<TestExecutionListeners> annotationType = TestExecutionListeners.class; | 		Class<TestExecutionListeners> annotationType = TestExecutionListeners.class; | ||||||
| 		List<Class<? extends TestExecutionListener>> classesList = new ArrayList<Class<? extends TestExecutionListener>>(); | 		List<Class<? extends TestExecutionListener>> classesList = new ArrayList<Class<? extends TestExecutionListener>>(); | ||||||
| 
 | 
 | ||||||
| 		AnnotationDescriptor<TestExecutionListeners> descriptor = findAnnotationDescriptor(clazz, annotationType); | 		AnnotationDescriptor<TestExecutionListeners> descriptor = findAnnotationDescriptor(clazz, annotationType); | ||||||
| 
 |  | ||||||
| 		boolean defaultListeners = false; | 		boolean defaultListeners = false; | ||||||
| 
 | 
 | ||||||
| 		// Use defaults? | 		// Use defaults? | ||||||
|  | @ -192,21 +193,20 @@ public class TestContextManager { | ||||||
| 		else { | 		else { | ||||||
| 			// Traverse the class hierarchy... | 			// Traverse the class hierarchy... | ||||||
| 			while (descriptor != null) { | 			while (descriptor != null) { | ||||||
| 				Class<?> rootDeclaringClass = descriptor.getDeclaringClass(); | 				Class<?> declaringClass = descriptor.getDeclaringClass(); | ||||||
| 				Class<?> declaringClass = (descriptor.getStereotype() != null) ? descriptor.getStereotypeType() |  | ||||||
| 						: rootDeclaringClass; |  | ||||||
| 
 | 
 | ||||||
| 				TestExecutionListeners testExecutionListeners = declaringClass.getAnnotation(annotationType); | 				AnnotationAttributes annAttrs = descriptor.getAnnotationAttributes(); | ||||||
| 				if (logger.isTraceEnabled()) { | 				if (logger.isTraceEnabled()) { | ||||||
| 					logger.trace("Retrieved @TestExecutionListeners [" + testExecutionListeners | 					logger.trace(String.format( | ||||||
| 							+ "] for declaring class [" + declaringClass + "]."); | 						"Retrieved @TestExecutionListeners attributes [%s] for declaring class [%s].", annAttrs, | ||||||
|  | 						declaringClass)); | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				Class<? extends TestExecutionListener>[] valueListenerClasses = testExecutionListeners.value(); | 				Class<? extends TestExecutionListener>[] valueListenerClasses = (Class<? extends TestExecutionListener>[]) annAttrs.getClassArray("value"); | ||||||
| 				Class<? extends TestExecutionListener>[] listenerClasses = testExecutionListeners.listeners(); | 				Class<? extends TestExecutionListener>[] listenerClasses = (Class<? extends TestExecutionListener>[]) annAttrs.getClassArray("listeners"); | ||||||
| 				if (!ObjectUtils.isEmpty(valueListenerClasses) && !ObjectUtils.isEmpty(listenerClasses)) { | 				if (!ObjectUtils.isEmpty(valueListenerClasses) && !ObjectUtils.isEmpty(listenerClasses)) { | ||||||
| 					String msg = String.format( | 					String msg = String.format( | ||||||
| 						"Test class [%s] has been configured with @TestExecutionListeners' 'value' [%s] " | 						"Class [%s] has been configured with @TestExecutionListeners' 'value' [%s] " | ||||||
| 								+ "and 'listeners' [%s] attributes. Use one or the other, but not both.", | 								+ "and 'listeners' [%s] attributes. Use one or the other, but not both.", | ||||||
| 						declaringClass, ObjectUtils.nullSafeToString(valueListenerClasses), | 						declaringClass, ObjectUtils.nullSafeToString(valueListenerClasses), | ||||||
| 						ObjectUtils.nullSafeToString(listenerClasses)); | 						ObjectUtils.nullSafeToString(listenerClasses)); | ||||||
|  | @ -221,8 +221,8 @@ public class TestContextManager { | ||||||
| 					classesList.addAll(0, Arrays.<Class<? extends TestExecutionListener>> asList(listenerClasses)); | 					classesList.addAll(0, Arrays.<Class<? extends TestExecutionListener>> asList(listenerClasses)); | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				descriptor = (testExecutionListeners.inheritListeners() ? findAnnotationDescriptor( | 				descriptor = (annAttrs.getBoolean("inheritListeners") ? findAnnotationDescriptor( | ||||||
| 					rootDeclaringClass.getSuperclass(), annotationType) : null); | 					descriptor.getRootDeclaringClass().getSuperclass(), annotationType) : null); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -34,6 +34,8 @@ import org.junit.runners.BlockJUnit4ClassRunner; | ||||||
| import org.junit.runners.model.FrameworkMethod; | import org.junit.runners.model.FrameworkMethod; | ||||||
| import org.junit.runners.model.InitializationError; | import org.junit.runners.model.InitializationError; | ||||||
| import org.junit.runners.model.Statement; | import org.junit.runners.model.Statement; | ||||||
|  | import org.springframework.core.annotation.AnnotatedElementUtils; | ||||||
|  | import org.springframework.core.annotation.AnnotationAttributes; | ||||||
| import org.springframework.core.annotation.AnnotationUtils; | import org.springframework.core.annotation.AnnotationUtils; | ||||||
| import org.springframework.test.annotation.ProfileValueUtils; | import org.springframework.test.annotation.ProfileValueUtils; | ||||||
| import org.springframework.test.annotation.Repeat; | import org.springframework.test.annotation.Repeat; | ||||||
|  | @ -411,8 +413,15 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner { | ||||||
| 	 * @return the timeout, or {@code 0} if none was specified. | 	 * @return the timeout, or {@code 0} if none was specified. | ||||||
| 	 */ | 	 */ | ||||||
| 	protected long getSpringTimeout(FrameworkMethod frameworkMethod) { | 	protected long getSpringTimeout(FrameworkMethod frameworkMethod) { | ||||||
| 		Timed timedAnnotation = AnnotationUtils.getAnnotation(frameworkMethod.getMethod(), Timed.class); | 		AnnotationAttributes annAttrs = AnnotatedElementUtils.getAnnotationAttributes(frameworkMethod.getMethod(), | ||||||
| 		return (timedAnnotation != null && timedAnnotation.millis() > 0 ? timedAnnotation.millis() : 0); | 			Timed.class.getName()); | ||||||
|  | 		if (annAttrs == null) { | ||||||
|  | 			return 0; | ||||||
|  | 		} | ||||||
|  | 		else { | ||||||
|  | 			long millis = annAttrs.<Long> getNumber("millis").longValue(); | ||||||
|  | 			return millis > 0 ? millis : 0; | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
|  |  | ||||||
|  | @ -21,13 +21,15 @@ import java.lang.reflect.Method; | ||||||
| import org.apache.commons.logging.Log; | import org.apache.commons.logging.Log; | ||||||
| import org.apache.commons.logging.LogFactory; | import org.apache.commons.logging.LogFactory; | ||||||
| import org.springframework.context.ApplicationContext; | import org.springframework.context.ApplicationContext; | ||||||
|  | import org.springframework.core.annotation.AnnotatedElementUtils; | ||||||
|  | import org.springframework.core.annotation.AnnotationAttributes; | ||||||
| import org.springframework.test.annotation.DirtiesContext; | import org.springframework.test.annotation.DirtiesContext; | ||||||
| import org.springframework.test.annotation.DirtiesContext.ClassMode; | import org.springframework.test.annotation.DirtiesContext.ClassMode; | ||||||
| import org.springframework.test.annotation.DirtiesContext.HierarchyMode; | import org.springframework.test.annotation.DirtiesContext.HierarchyMode; | ||||||
| import org.springframework.test.context.TestContext; | import org.springframework.test.context.TestContext; | ||||||
| import org.springframework.util.Assert; | import org.springframework.util.Assert; | ||||||
| 
 | 
 | ||||||
| import static org.springframework.core.annotation.AnnotationUtils.*; | import static org.springframework.test.annotation.DirtiesContext.ClassMode.*; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * {@code TestExecutionListener} which provides support for marking the |  * {@code TestExecutionListener} which provides support for marking the | ||||||
|  | @ -81,24 +83,22 @@ public class DirtiesContextTestExecutionListener extends AbstractTestExecutionLi | ||||||
| 		Method testMethod = testContext.getTestMethod(); | 		Method testMethod = testContext.getTestMethod(); | ||||||
| 		Assert.notNull(testMethod, "The test method of the supplied TestContext must not be null"); | 		Assert.notNull(testMethod, "The test method of the supplied TestContext must not be null"); | ||||||
| 
 | 
 | ||||||
| 		final Class<DirtiesContext> annotationType = DirtiesContext.class; | 		final String annotationType = DirtiesContext.class.getName(); | ||||||
| 
 | 		AnnotationAttributes methodAnnAttrs = AnnotatedElementUtils.getAnnotationAttributes(testMethod, annotationType); | ||||||
| 		DirtiesContext methodDirtiesContextAnnotation = findAnnotation(testMethod, annotationType); | 		AnnotationAttributes classAnnAttrs = AnnotatedElementUtils.getAnnotationAttributes(testClass, annotationType); | ||||||
| 		boolean methodDirtiesContext = methodDirtiesContextAnnotation != null; | 		boolean methodDirtiesContext = methodAnnAttrs != null; | ||||||
| 
 | 		boolean classDirtiesContext = classAnnAttrs != null; | ||||||
| 		DirtiesContext classDirtiesContextAnnotation = findAnnotation(testClass, annotationType); | 		ClassMode classMode = classDirtiesContext ? classAnnAttrs.<ClassMode> getEnum("classMode") : null; | ||||||
| 		boolean classDirtiesContext = classDirtiesContextAnnotation != null; |  | ||||||
| 		ClassMode classMode = classDirtiesContext ? classDirtiesContextAnnotation.classMode() : null; |  | ||||||
| 
 | 
 | ||||||
| 		if (logger.isDebugEnabled()) { | 		if (logger.isDebugEnabled()) { | ||||||
| 			logger.debug("After test method: context [" + testContext + "], class dirties context [" | 			logger.debug(String.format( | ||||||
| 					+ classDirtiesContext + "], class mode [" + classMode + "], method dirties context [" | 				"After test method: context %s, class dirties context [%s], class mode [%s], method dirties context [%s].", | ||||||
| 					+ methodDirtiesContext + "]."); | 				testContext, classDirtiesContext, classMode, methodDirtiesContext)); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (methodDirtiesContext || (classMode == ClassMode.AFTER_EACH_TEST_METHOD)) { | 		if (methodDirtiesContext || (classMode == AFTER_EACH_TEST_METHOD)) { | ||||||
| 			HierarchyMode hierarchyMode = methodDirtiesContext ? methodDirtiesContextAnnotation.hierarchyMode() | 			HierarchyMode hierarchyMode = methodDirtiesContext ? methodAnnAttrs.<HierarchyMode> getEnum("hierarchyMode") | ||||||
| 					: classDirtiesContextAnnotation.hierarchyMode(); | 					: classAnnAttrs.<HierarchyMode> getEnum("hierarchyMode"); | ||||||
| 			dirtyContext(testContext, hierarchyMode); | 			dirtyContext(testContext, hierarchyMode); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | @ -107,7 +107,7 @@ public class DirtiesContextTestExecutionListener extends AbstractTestExecutionLi | ||||||
| 	 * If the test class of the supplied {@linkplain TestContext test context} is | 	 * If the test class of the supplied {@linkplain TestContext test context} is | ||||||
| 	 * annotated with {@link DirtiesContext @DirtiesContext}, the | 	 * annotated with {@link DirtiesContext @DirtiesContext}, the | ||||||
| 	 * {@linkplain ApplicationContext application context} of the test context will | 	 * {@linkplain ApplicationContext application context} of the test context will | ||||||
| 	 * be {@linkplain TestContext#markApplicationContextDirty() marked as dirty} , | 	 * be {@linkplain TestContext#markApplicationContextDirty() marked as dirty}, | ||||||
| 	 * and the | 	 * and the | ||||||
| 	 * {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE | 	 * {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE | ||||||
| 	 * REINJECT_DEPENDENCIES_ATTRIBUTE} in the test context will be set to | 	 * REINJECT_DEPENDENCIES_ATTRIBUTE} in the test context will be set to | ||||||
|  | @ -118,15 +118,16 @@ public class DirtiesContextTestExecutionListener extends AbstractTestExecutionLi | ||||||
| 		Class<?> testClass = testContext.getTestClass(); | 		Class<?> testClass = testContext.getTestClass(); | ||||||
| 		Assert.notNull(testClass, "The test class of the supplied TestContext must not be null"); | 		Assert.notNull(testClass, "The test class of the supplied TestContext must not be null"); | ||||||
| 
 | 
 | ||||||
| 		final Class<DirtiesContext> annotationType = DirtiesContext.class; | 		final String annotationType = DirtiesContext.class.getName(); | ||||||
|  | 		AnnotationAttributes annAttrs = AnnotatedElementUtils.getAnnotationAttributes(testClass, annotationType); | ||||||
|  | 		boolean dirtiesContext = annAttrs != null; | ||||||
| 
 | 
 | ||||||
| 		DirtiesContext dirtiesContextAnnotation = findAnnotation(testClass, annotationType); |  | ||||||
| 		boolean dirtiesContext = dirtiesContextAnnotation != null; |  | ||||||
| 		if (logger.isDebugEnabled()) { | 		if (logger.isDebugEnabled()) { | ||||||
| 			logger.debug("After test class: context [" + testContext + "], dirtiesContext [" + dirtiesContext + "]."); | 			logger.debug(String.format("After test class: context %s, dirtiesContext [%s].", testContext, | ||||||
|  | 				dirtiesContext)); | ||||||
| 		} | 		} | ||||||
| 		if (dirtiesContext) { | 		if (dirtiesContext) { | ||||||
| 			HierarchyMode hierarchyMode = dirtiesContextAnnotation.hierarchyMode(); | 			HierarchyMode hierarchyMode = annAttrs.<HierarchyMode> getEnum("hierarchyMode"); | ||||||
| 			dirtyContext(testContext, hierarchyMode); | 			dirtyContext(testContext, hierarchyMode); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -33,6 +33,8 @@ import org.springframework.beans.factory.BeanFactoryUtils; | ||||||
| import org.springframework.beans.factory.ListableBeanFactory; | import org.springframework.beans.factory.ListableBeanFactory; | ||||||
| import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; | import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; | ||||||
| import org.springframework.context.ApplicationContext; | import org.springframework.context.ApplicationContext; | ||||||
|  | import org.springframework.core.annotation.AnnotatedElementUtils; | ||||||
|  | import org.springframework.core.annotation.AnnotationAttributes; | ||||||
| import org.springframework.test.annotation.Rollback; | import org.springframework.test.annotation.Rollback; | ||||||
| import org.springframework.test.context.TestContext; | import org.springframework.test.context.TestContext; | ||||||
| import org.springframework.test.context.support.AbstractTestExecutionListener; | import org.springframework.test.context.support.AbstractTestExecutionListener; | ||||||
|  | @ -414,15 +416,17 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis | ||||||
| 		if (rollbackAnnotation != null) { | 		if (rollbackAnnotation != null) { | ||||||
| 			boolean rollbackOverride = rollbackAnnotation.value(); | 			boolean rollbackOverride = rollbackAnnotation.value(); | ||||||
| 			if (logger.isDebugEnabled()) { | 			if (logger.isDebugEnabled()) { | ||||||
| 				logger.debug("Method-level @Rollback(" + rollbackOverride + ") overrides default rollback [" + rollback | 				logger.debug(String.format( | ||||||
| 						+ "] for test context " + testContext); | 					"Method-level @Rollback(%s) overrides default rollback [%s] for test context %s.", | ||||||
|  | 					rollbackOverride, rollback, testContext)); | ||||||
| 			} | 			} | ||||||
| 			rollback = rollbackOverride; | 			rollback = rollbackOverride; | ||||||
| 		} | 		} | ||||||
| 		else { | 		else { | ||||||
| 			if (logger.isDebugEnabled()) { | 			if (logger.isDebugEnabled()) { | ||||||
| 				logger.debug("No method-level @Rollback override: using default rollback [" + rollback | 				logger.debug(String.format( | ||||||
| 						+ "] for test context " + testContext); | 					"No method-level @Rollback override: using default rollback [%s] for test context %s.", rollback, | ||||||
|  | 					testContext)); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		return rollback; | 		return rollback; | ||||||
|  | @ -524,21 +528,25 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis | ||||||
| 	 * {@code @TransactionConfiguration} will be used instead. | 	 * {@code @TransactionConfiguration} will be used instead. | ||||||
| 	 * @param testContext the test context for which the configuration | 	 * @param testContext the test context for which the configuration | ||||||
| 	 * attributes should be retrieved | 	 * attributes should be retrieved | ||||||
| 	 * @return a new TransactionConfigurationAttributes instance | 	 * @return the TransactionConfigurationAttributes instance for this listener, | ||||||
|  | 	 * potentially cached | ||||||
| 	 */ | 	 */ | ||||||
| 	private TransactionConfigurationAttributes retrieveConfigurationAttributes(TestContext testContext) { | 	TransactionConfigurationAttributes retrieveConfigurationAttributes(TestContext testContext) { | ||||||
| 		if (this.configurationAttributes == null) { | 		if (this.configurationAttributes == null) { | ||||||
| 			Class<?> clazz = testContext.getTestClass(); | 			Class<?> clazz = testContext.getTestClass(); | ||||||
| 			TransactionConfiguration config = findAnnotation(clazz, TransactionConfiguration.class); | 
 | ||||||
|  | 			AnnotationAttributes annAttrs = AnnotatedElementUtils.getAnnotationAttributes(clazz, | ||||||
|  | 				TransactionConfiguration.class.getName()); | ||||||
| 			if (logger.isDebugEnabled()) { | 			if (logger.isDebugEnabled()) { | ||||||
| 				logger.debug("Retrieved @TransactionConfiguration [" + config + "] for test class [" + clazz + "]"); | 				logger.debug(String.format("Retrieved @TransactionConfiguration attributes [%s] for test class [%s].", | ||||||
|  | 					annAttrs, clazz)); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			String transactionManagerName; | 			String transactionManagerName; | ||||||
| 			boolean defaultRollback; | 			boolean defaultRollback; | ||||||
| 			if (config != null) { | 			if (annAttrs != null) { | ||||||
| 				transactionManagerName = config.transactionManager(); | 				transactionManagerName = annAttrs.getString("transactionManager"); | ||||||
| 				defaultRollback = config.defaultRollback(); | 				defaultRollback = annAttrs.getBoolean("defaultRollback"); | ||||||
| 			} | 			} | ||||||
| 			else { | 			else { | ||||||
| 				transactionManagerName = DEFAULT_TRANSACTION_MANAGER_NAME; | 				transactionManagerName = DEFAULT_TRANSACTION_MANAGER_NAME; | ||||||
|  | @ -548,8 +556,8 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis | ||||||
| 			TransactionConfigurationAttributes configAttributes = new TransactionConfigurationAttributes( | 			TransactionConfigurationAttributes configAttributes = new TransactionConfigurationAttributes( | ||||||
| 				transactionManagerName, defaultRollback); | 				transactionManagerName, defaultRollback); | ||||||
| 			if (logger.isDebugEnabled()) { | 			if (logger.isDebugEnabled()) { | ||||||
| 				logger.debug("Retrieved TransactionConfigurationAttributes " + configAttributes + " for class [" | 				logger.debug(String.format("Retrieved TransactionConfigurationAttributes %s for class [%s].", | ||||||
| 						+ clazz + "]"); | 					configAttributes, clazz)); | ||||||
| 			} | 			} | ||||||
| 			this.configurationAttributes = configAttributes; | 			this.configurationAttributes = configAttributes; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -110,6 +110,17 @@ abstract class AbstractContextLoaderUtilsTests { | ||||||
| 	public static @interface MetaLocationsFooConfig { | 	public static @interface MetaLocationsFooConfig { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@ContextConfiguration | ||||||
|  | 	@ActiveProfiles | ||||||
|  | 	@Retention(RetentionPolicy.RUNTIME) | ||||||
|  | 	@Target(ElementType.TYPE) | ||||||
|  | 	public static @interface MetaLocationsFooConfigWithOverrides { | ||||||
|  | 
 | ||||||
|  | 		String[] locations() default "/foo.xml"; | ||||||
|  | 
 | ||||||
|  | 		String[] profiles() default "foo"; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@ContextConfiguration("/bar.xml") | 	@ContextConfiguration("/bar.xml") | ||||||
| 	@ActiveProfiles(profiles = "bar") | 	@ActiveProfiles(profiles = "bar") | ||||||
| 	@Retention(RetentionPolicy.RUNTIME) | 	@Retention(RetentionPolicy.RUNTIME) | ||||||
|  | @ -125,6 +136,14 @@ abstract class AbstractContextLoaderUtilsTests { | ||||||
| 	static class MetaLocationsBar extends MetaLocationsFoo { | 	static class MetaLocationsBar extends MetaLocationsFoo { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@MetaLocationsFooConfigWithOverrides | ||||||
|  | 	static class MetaLocationsFooWithOverrides { | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@MetaLocationsFooConfigWithOverrides(locations = { "foo1.xml", "foo2.xml" }, profiles = { "foo1", "foo2" }) | ||||||
|  | 	static class MetaLocationsFooWithOverriddenAttributes { | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@ContextConfiguration(locations = "/foo.xml", inheritLocations = false) | 	@ContextConfiguration(locations = "/foo.xml", inheritLocations = false) | ||||||
| 	@ActiveProfiles(profiles = "foo") | 	@ActiveProfiles(profiles = "foo") | ||||||
| 	static class LocationsFoo { | 	static class LocationsFoo { | ||||||
|  |  | ||||||
|  | @ -121,6 +121,26 @@ public class ContextLoaderUtilsActiveProfilesTests extends AbstractContextLoader | ||||||
| 		assertArrayEquals(new String[] { "foo" }, profiles); | 		assertArrayEquals(new String[] { "foo" }, profiles); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * @since 4.0 | ||||||
|  | 	 */ | ||||||
|  | 	@Test | ||||||
|  | 	public void resolveActiveProfilesWithMetaAnnotationAndOverrides() { | ||||||
|  | 		String[] profiles = resolveActiveProfiles(MetaLocationsFooWithOverrides.class); | ||||||
|  | 		assertNotNull(profiles); | ||||||
|  | 		assertArrayEquals(new String[] { "foo" }, profiles); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * @since 4.0 | ||||||
|  | 	 */ | ||||||
|  | 	@Test | ||||||
|  | 	public void resolveActiveProfilesWithMetaAnnotationAndOverriddenAttributes() { | ||||||
|  | 		String[] profiles = resolveActiveProfiles(MetaLocationsFooWithOverriddenAttributes.class); | ||||||
|  | 		assertNotNull(profiles); | ||||||
|  | 		assertArrayEquals(new String[] { "foo1", "foo2" }, profiles); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * @since 4.0 | 	 * @since 4.0 | ||||||
| 	 */ | 	 */ | ||||||
|  | @ -256,6 +276,18 @@ public class ContextLoaderUtilsActiveProfilesTests extends AbstractContextLoader | ||||||
| 	private static @interface MetaAnimalsConfig { | 	private static @interface MetaAnimalsConfig { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@ActiveProfiles | ||||||
|  | 	@Retention(RetentionPolicy.RUNTIME) | ||||||
|  | 	@Target(ElementType.TYPE) | ||||||
|  | 	private static @interface MetaProfilesWithOverrides { | ||||||
|  | 
 | ||||||
|  | 		String[] profiles() default { "dog", "cat" }; | ||||||
|  | 
 | ||||||
|  | 		Class<? extends ActiveProfilesResolver> resolver() default ActiveProfilesResolver.class; | ||||||
|  | 
 | ||||||
|  | 		boolean inheritProfiles() default false; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@MetaAnimalsConfig | 	@MetaAnimalsConfig | ||||||
| 	private static class MetaAnimals extends MetaLocationsBar { | 	private static class MetaAnimals extends MetaLocationsBar { | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -83,6 +83,24 @@ public class ContextLoaderUtilsConfigurationAttributesTests extends AbstractCont | ||||||
| 			EMPTY_CLASS_ARRAY, ContextLoader.class, true); | 			EMPTY_CLASS_ARRAY, ContextLoader.class, true); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void resolveConfigAttributesWithMetaAnnotationAndLocationsAndOverrides() { | ||||||
|  | 		List<ContextConfigurationAttributes> attributesList = resolveContextConfigurationAttributes(MetaLocationsFooWithOverrides.class); | ||||||
|  | 		assertNotNull(attributesList); | ||||||
|  | 		assertEquals(1, attributesList.size()); | ||||||
|  | 		assertAttributes(attributesList.get(0), MetaLocationsFooConfigWithOverrides.class, new String[] { "/foo.xml" }, | ||||||
|  | 			EMPTY_CLASS_ARRAY, ContextLoader.class, true); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void resolveConfigAttributesWithMetaAnnotationAndLocationsAndOverriddenAttributes() { | ||||||
|  | 		List<ContextConfigurationAttributes> attributesList = resolveContextConfigurationAttributes(MetaLocationsFooWithOverriddenAttributes.class); | ||||||
|  | 		assertNotNull(attributesList); | ||||||
|  | 		assertEquals(1, attributesList.size()); | ||||||
|  | 		assertAttributes(attributesList.get(0), MetaLocationsFooConfigWithOverrides.class, new String[] { "foo1.xml", | ||||||
|  | 			"foo2.xml" }, EMPTY_CLASS_ARRAY, ContextLoader.class, true); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@Test | 	@Test | ||||||
| 	public void resolveConfigAttributesWithMetaAnnotationAndLocationsInClassHierarchy() { | 	public void resolveConfigAttributesWithMetaAnnotationAndLocationsInClassHierarchy() { | ||||||
| 		List<ContextConfigurationAttributes> attributesList = resolveContextConfigurationAttributes(MetaLocationsBar.class); | 		List<ContextConfigurationAttributes> attributesList = resolveContextConfigurationAttributes(MetaLocationsBar.class); | ||||||
|  |  | ||||||
|  | @ -55,13 +55,9 @@ public class ContextLoaderUtilsContextHierarchyTests extends AbstractContextLoad | ||||||
| 		resolveContextHierarchyAttributes(SingleTestClassWithContextConfigurationAndContextHierarchyOnSingleMetaAnnotation.class); | 		resolveContextHierarchyAttributes(SingleTestClassWithContextConfigurationAndContextHierarchyOnSingleMetaAnnotation.class); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Test | 	@Test(expected = IllegalStateException.class) | ||||||
| 	public void resolveContextHierarchyAttributesForSingleTestClassWithImplicitSingleLevelContextHierarchy() { | 	public void resolveContextHierarchyAttributesForSingleTestClassWithImplicitSingleLevelContextHierarchy() { | ||||||
| 		List<List<ContextConfigurationAttributes>> hierarchyAttributes = resolveContextHierarchyAttributes(BareAnnotations.class); | 		resolveContextHierarchyAttributes(BareAnnotations.class); | ||||||
| 		assertEquals(1, hierarchyAttributes.size()); |  | ||||||
| 		List<ContextConfigurationAttributes> configAttributesList = hierarchyAttributes.get(0); |  | ||||||
| 		assertEquals(1, configAttributesList.size()); |  | ||||||
| 		debugConfigAttributes(configAttributesList); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Test | 	@Test | ||||||
|  | @ -98,8 +94,8 @@ public class ContextLoaderUtilsContextHierarchyTests extends AbstractContextLoad | ||||||
| 		debugConfigAttributes(configAttributesList); | 		debugConfigAttributes(configAttributesList); | ||||||
| 		assertAttributes(configAttributesList.get(0), testClass, new String[] { "A.xml" }, EMPTY_CLASS_ARRAY, | 		assertAttributes(configAttributesList.get(0), testClass, new String[] { "A.xml" }, EMPTY_CLASS_ARRAY, | ||||||
| 			ContextLoader.class, true); | 			ContextLoader.class, true); | ||||||
| 		assertAttributes(configAttributesList.get(1), testClass, new String[] { "B.xml" }, | 		assertAttributes(configAttributesList.get(1), testClass, new String[] { "B.xml" }, EMPTY_CLASS_ARRAY, | ||||||
| 			EMPTY_CLASS_ARRAY, ContextLoader.class, true); | 			ContextLoader.class, true); | ||||||
| 		assertAttributes(configAttributesList.get(2), testClass, new String[] { "C.xml" }, EMPTY_CLASS_ARRAY, | 		assertAttributes(configAttributesList.get(2), testClass, new String[] { "C.xml" }, EMPTY_CLASS_ARRAY, | ||||||
| 			ContextLoader.class, true); | 			ContextLoader.class, true); | ||||||
| 	} | 	} | ||||||
|  | @ -154,36 +150,39 @@ public class ContextLoaderUtilsContextHierarchyTests extends AbstractContextLoad | ||||||
| 			EMPTY_CLASS_ARRAY, ContextLoader.class, true); | 			EMPTY_CLASS_ARRAY, ContextLoader.class, true); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Test | 	private void assertOneTwo(List<List<ContextConfigurationAttributes>> hierarchyAttributes) { | ||||||
| 	public void resolveContextHierarchyAttributesForTestClassHierarchyWithBareContextConfigurationInSubclass() { |  | ||||||
| 		List<List<ContextConfigurationAttributes>> hierarchyAttributes = resolveContextHierarchyAttributes(TestClass2WithBareContextConfigurationInSubclass.class); |  | ||||||
| 		assertEquals(2, hierarchyAttributes.size()); | 		assertEquals(2, hierarchyAttributes.size()); | ||||||
| 
 | 
 | ||||||
| 		List<ContextConfigurationAttributes> configAttributesListClassLevel1 = hierarchyAttributes.get(0); | 		List<ContextConfigurationAttributes> configAttributesListClassLevel1 = hierarchyAttributes.get(0); | ||||||
|  | 		List<ContextConfigurationAttributes> configAttributesListClassLevel2 = hierarchyAttributes.get(1); | ||||||
| 		debugConfigAttributes(configAttributesListClassLevel1); | 		debugConfigAttributes(configAttributesListClassLevel1); | ||||||
|  | 		debugConfigAttributes(configAttributesListClassLevel2); | ||||||
|  | 
 | ||||||
| 		assertEquals(1, configAttributesListClassLevel1.size()); | 		assertEquals(1, configAttributesListClassLevel1.size()); | ||||||
| 		assertThat(configAttributesListClassLevel1.get(0).getLocations()[0], equalTo("one.xml")); | 		assertThat(configAttributesListClassLevel1.get(0).getLocations()[0], equalTo("one.xml")); | ||||||
| 
 | 
 | ||||||
| 		List<ContextConfigurationAttributes> configAttributesListClassLevel2 = hierarchyAttributes.get(1); |  | ||||||
| 		debugConfigAttributes(configAttributesListClassLevel2); |  | ||||||
| 		assertEquals(1, configAttributesListClassLevel2.size()); | 		assertEquals(1, configAttributesListClassLevel2.size()); | ||||||
| 		assertThat(configAttributesListClassLevel2.get(0).getLocations()[0], equalTo("two.xml")); | 		assertThat(configAttributesListClassLevel2.get(0).getLocations()[0], equalTo("two.xml")); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Test | 	@Test | ||||||
| 	public void resolveContextHierarchyAttributesForTestClassHierarchyWithBareContextConfigurationInSuperclass() { | 	public void resolveContextHierarchyAttributesForTestClassHierarchyWithBareContextConfigurationInSuperclass() { | ||||||
| 		List<List<ContextConfigurationAttributes>> hierarchyAttributes = resolveContextHierarchyAttributes(TestClass2WithBareContextConfigurationInSuperclass.class); | 		assertOneTwo(resolveContextHierarchyAttributes(TestClass2WithBareContextConfigurationInSuperclass.class)); | ||||||
| 		assertEquals(2, hierarchyAttributes.size()); | 	} | ||||||
| 
 | 
 | ||||||
| 		List<ContextConfigurationAttributes> configAttributesListClassLevel1 = hierarchyAttributes.get(0); | 	@Test | ||||||
| 		debugConfigAttributes(configAttributesListClassLevel1); | 	public void resolveContextHierarchyAttributesForTestClassHierarchyWithBareContextConfigurationInSubclass() { | ||||||
| 		assertEquals(1, configAttributesListClassLevel1.size()); | 		assertOneTwo(resolveContextHierarchyAttributes(TestClass2WithBareContextConfigurationInSubclass.class)); | ||||||
| 		assertThat(configAttributesListClassLevel1.get(0).getLocations()[0], equalTo("one.xml")); | 	} | ||||||
| 
 | 
 | ||||||
| 		List<ContextConfigurationAttributes> configAttributesListClassLevel2 = hierarchyAttributes.get(1); | 	@Test | ||||||
| 		debugConfigAttributes(configAttributesListClassLevel2); | 	public void resolveContextHierarchyAttributesForTestClassHierarchyWithBareMetaContextConfigWithOverridesInSuperclass() { | ||||||
| 		assertEquals(1, configAttributesListClassLevel2.size()); | 		assertOneTwo(resolveContextHierarchyAttributes(TestClass2WithBareMetaContextConfigWithOverridesInSuperclass.class)); | ||||||
| 		assertThat(configAttributesListClassLevel2.get(0).getLocations()[0], equalTo("two.xml")); | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void resolveContextHierarchyAttributesForTestClassHierarchyWithBareMetaContextConfigWithOverridesInSubclass() { | ||||||
|  | 		assertOneTwo(resolveContextHierarchyAttributes(TestClass2WithBareMetaContextConfigWithOverridesInSubclass.class)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Test | 	@Test | ||||||
|  | @ -408,7 +407,7 @@ public class ContextLoaderUtilsContextHierarchyTests extends AbstractContextLoad | ||||||
| 
 | 
 | ||||||
| 	@ContextConfiguration("two.xml") | 	@ContextConfiguration("two.xml") | ||||||
| 	private static class TestClass2WithBareContextConfigurationInSubclass extends | 	private static class TestClass2WithBareContextConfigurationInSubclass extends | ||||||
| 			TestClass1WithBareContextConfigurationInSuperclass { | 			TestClass1WithBareContextConfigurationInSubclass { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@ContextHierarchy({// | 	@ContextHierarchy({// | ||||||
|  | @ -569,4 +568,31 @@ public class ContextLoaderUtilsContextHierarchyTests extends AbstractContextLoad | ||||||
| 			TestClass2WithSingleLevelContextHierarchyFromMetaAnnotation { | 			TestClass2WithSingleLevelContextHierarchyFromMetaAnnotation { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// ------------------------------------------------------------------------- | ||||||
|  | 
 | ||||||
|  | 	@ContextConfiguration | ||||||
|  | 	@Retention(RetentionPolicy.RUNTIME) | ||||||
|  | 	private static @interface ContextConfigWithOverrides { | ||||||
|  | 
 | ||||||
|  | 		String[] locations() default "A.xml"; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@ContextConfigWithOverrides(locations = "one.xml") | ||||||
|  | 	private static class TestClass1WithBareMetaContextConfigWithOverridesInSuperclass { | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@ContextHierarchy(@ContextConfiguration(locations = "two.xml")) | ||||||
|  | 	private static class TestClass2WithBareMetaContextConfigWithOverridesInSuperclass extends | ||||||
|  | 			TestClass1WithBareMetaContextConfigWithOverridesInSuperclass { | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@ContextHierarchy(@ContextConfiguration(locations = "one.xml")) | ||||||
|  | 	private static class TestClass1WithBareMetaContextConfigWithOverridesInSubclass { | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@ContextConfigWithOverrides(locations = "two.xml") | ||||||
|  | 	private static class TestClass2WithBareMetaContextConfigWithOverridesInSubclass extends | ||||||
|  | 			TestClass1WithBareMetaContextConfigWithOverridesInSubclass { | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -43,7 +43,7 @@ public class MetaAnnotationUtilsTests { | ||||||
| 			Class<? extends Annotation> stereotypeType) { | 			Class<? extends Annotation> stereotypeType) { | ||||||
| 		AnnotationDescriptor<Component> descriptor = findAnnotationDescriptor(startClass, Component.class); | 		AnnotationDescriptor<Component> descriptor = findAnnotationDescriptor(startClass, Component.class); | ||||||
| 		assertNotNull(descriptor); | 		assertNotNull(descriptor); | ||||||
| 		assertEquals(declaringClass, descriptor.getDeclaringClass()); | 		assertEquals(declaringClass, descriptor.getRootDeclaringClass()); | ||||||
| 		assertEquals(Component.class, descriptor.getAnnotationType()); | 		assertEquals(Component.class, descriptor.getAnnotationType()); | ||||||
| 		assertEquals(name, descriptor.getAnnotation().value()); | 		assertEquals(name, descriptor.getAnnotation().value()); | ||||||
| 		assertNotNull(descriptor.getStereotype()); | 		assertNotNull(descriptor.getStereotype()); | ||||||
|  | @ -57,7 +57,7 @@ public class MetaAnnotationUtilsTests { | ||||||
| 		UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes(startClass, Service.class, | 		UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes(startClass, Service.class, | ||||||
| 			annotationType, Order.class, Transactional.class); | 			annotationType, Order.class, Transactional.class); | ||||||
| 		assertNotNull(descriptor); | 		assertNotNull(descriptor); | ||||||
| 		assertEquals(declaringClass, descriptor.getDeclaringClass()); | 		assertEquals(declaringClass, descriptor.getRootDeclaringClass()); | ||||||
| 		assertEquals(annotationType, descriptor.getAnnotationType()); | 		assertEquals(annotationType, descriptor.getAnnotationType()); | ||||||
| 		assertEquals(name, ((Component) descriptor.getAnnotation()).value()); | 		assertEquals(name, ((Component) descriptor.getAnnotation()).value()); | ||||||
| 		assertNotNull(descriptor.getStereotype()); | 		assertNotNull(descriptor.getStereotype()); | ||||||
|  | @ -74,16 +74,16 @@ public class MetaAnnotationUtilsTests { | ||||||
| 	public void findAnnotationDescriptorWithInheritedAnnotationOnClass() throws Exception { | 	public void findAnnotationDescriptorWithInheritedAnnotationOnClass() throws Exception { | ||||||
| 		// Note: @Transactional is inherited | 		// Note: @Transactional is inherited | ||||||
| 		assertEquals(InheritedAnnotationClass.class, | 		assertEquals(InheritedAnnotationClass.class, | ||||||
| 			findAnnotationDescriptor(InheritedAnnotationClass.class, Transactional.class).getDeclaringClass()); | 			findAnnotationDescriptor(InheritedAnnotationClass.class, Transactional.class).getRootDeclaringClass()); | ||||||
| 		assertEquals(InheritedAnnotationClass.class, | 		assertEquals(InheritedAnnotationClass.class, | ||||||
| 			findAnnotationDescriptor(SubInheritedAnnotationClass.class, Transactional.class).getDeclaringClass()); | 			findAnnotationDescriptor(SubInheritedAnnotationClass.class, Transactional.class).getRootDeclaringClass()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Test | 	@Test | ||||||
| 	public void findAnnotationDescriptorWithInheritedAnnotationOnInterface() throws Exception { | 	public void findAnnotationDescriptorWithInheritedAnnotationOnInterface() throws Exception { | ||||||
| 		// Note: @Transactional is inherited | 		// Note: @Transactional is inherited | ||||||
| 		assertEquals(InheritedAnnotationInterface.class, | 		assertEquals(InheritedAnnotationInterface.class, | ||||||
| 			findAnnotationDescriptor(InheritedAnnotationInterface.class, Transactional.class).getDeclaringClass()); | 			findAnnotationDescriptor(InheritedAnnotationInterface.class, Transactional.class).getRootDeclaringClass()); | ||||||
| 		assertNull(findAnnotationDescriptor(SubInheritedAnnotationInterface.class, Transactional.class)); | 		assertNull(findAnnotationDescriptor(SubInheritedAnnotationInterface.class, Transactional.class)); | ||||||
| 		assertNull(findAnnotationDescriptor(SubSubInheritedAnnotationInterface.class, Transactional.class)); | 		assertNull(findAnnotationDescriptor(SubSubInheritedAnnotationInterface.class, Transactional.class)); | ||||||
| 	} | 	} | ||||||
|  | @ -92,16 +92,16 @@ public class MetaAnnotationUtilsTests { | ||||||
| 	public void findAnnotationDescriptorForNonInheritedAnnotationOnClass() throws Exception { | 	public void findAnnotationDescriptorForNonInheritedAnnotationOnClass() throws Exception { | ||||||
| 		// Note: @Order is not inherited. | 		// Note: @Order is not inherited. | ||||||
| 		assertEquals(NonInheritedAnnotationClass.class, | 		assertEquals(NonInheritedAnnotationClass.class, | ||||||
| 			findAnnotationDescriptor(NonInheritedAnnotationClass.class, Order.class).getDeclaringClass()); | 			findAnnotationDescriptor(NonInheritedAnnotationClass.class, Order.class).getRootDeclaringClass()); | ||||||
| 		assertEquals(NonInheritedAnnotationClass.class, | 		assertEquals(NonInheritedAnnotationClass.class, | ||||||
| 			findAnnotationDescriptor(SubNonInheritedAnnotationClass.class, Order.class).getDeclaringClass()); | 			findAnnotationDescriptor(SubNonInheritedAnnotationClass.class, Order.class).getRootDeclaringClass()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Test | 	@Test | ||||||
| 	public void findAnnotationDescriptorForNonInheritedAnnotationOnInterface() throws Exception { | 	public void findAnnotationDescriptorForNonInheritedAnnotationOnInterface() throws Exception { | ||||||
| 		// Note: @Order is not inherited. | 		// Note: @Order is not inherited. | ||||||
| 		assertEquals(NonInheritedAnnotationInterface.class, | 		assertEquals(NonInheritedAnnotationInterface.class, | ||||||
| 			findAnnotationDescriptor(NonInheritedAnnotationInterface.class, Order.class).getDeclaringClass()); | 			findAnnotationDescriptor(NonInheritedAnnotationInterface.class, Order.class).getRootDeclaringClass()); | ||||||
| 		assertNull(findAnnotationDescriptor(SubNonInheritedAnnotationInterface.class, Order.class)); | 		assertNull(findAnnotationDescriptor(SubNonInheritedAnnotationInterface.class, Order.class)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -116,7 +116,7 @@ public class MetaAnnotationUtilsTests { | ||||||
| 		Class<Component> annotationType = Component.class; | 		Class<Component> annotationType = Component.class; | ||||||
| 		AnnotationDescriptor<Component> descriptor = findAnnotationDescriptor(HasLocalAndMetaComponentAnnotation.class, | 		AnnotationDescriptor<Component> descriptor = findAnnotationDescriptor(HasLocalAndMetaComponentAnnotation.class, | ||||||
| 			annotationType); | 			annotationType); | ||||||
| 		assertEquals(HasLocalAndMetaComponentAnnotation.class, descriptor.getDeclaringClass()); | 		assertEquals(HasLocalAndMetaComponentAnnotation.class, descriptor.getRootDeclaringClass()); | ||||||
| 		assertEquals(annotationType, descriptor.getAnnotationType()); | 		assertEquals(annotationType, descriptor.getAnnotationType()); | ||||||
| 		assertNull(descriptor.getStereotype()); | 		assertNull(descriptor.getStereotype()); | ||||||
| 		assertNull(descriptor.getStereotypeType()); | 		assertNull(descriptor.getStereotypeType()); | ||||||
|  | @ -159,10 +159,10 @@ public class MetaAnnotationUtilsTests { | ||||||
| 	public void findAnnotationDescriptorForTypesWithInheritedAnnotationOnClass() throws Exception { | 	public void findAnnotationDescriptorForTypesWithInheritedAnnotationOnClass() throws Exception { | ||||||
| 		// Note: @Transactional is inherited | 		// Note: @Transactional is inherited | ||||||
| 		assertEquals(InheritedAnnotationClass.class, | 		assertEquals(InheritedAnnotationClass.class, | ||||||
| 			findAnnotationDescriptorForTypes(InheritedAnnotationClass.class, Transactional.class).getDeclaringClass()); | 			findAnnotationDescriptorForTypes(InheritedAnnotationClass.class, Transactional.class).getRootDeclaringClass()); | ||||||
| 		assertEquals( | 		assertEquals( | ||||||
| 			InheritedAnnotationClass.class, | 			InheritedAnnotationClass.class, | ||||||
| 			findAnnotationDescriptorForTypes(SubInheritedAnnotationClass.class, Transactional.class).getDeclaringClass()); | 			findAnnotationDescriptorForTypes(SubInheritedAnnotationClass.class, Transactional.class).getRootDeclaringClass()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Test | 	@Test | ||||||
|  | @ -171,7 +171,7 @@ public class MetaAnnotationUtilsTests { | ||||||
| 		// Note: @Transactional is inherited | 		// Note: @Transactional is inherited | ||||||
| 		assertEquals( | 		assertEquals( | ||||||
| 			InheritedAnnotationInterface.class, | 			InheritedAnnotationInterface.class, | ||||||
| 			findAnnotationDescriptorForTypes(InheritedAnnotationInterface.class, Transactional.class).getDeclaringClass()); | 			findAnnotationDescriptorForTypes(InheritedAnnotationInterface.class, Transactional.class).getRootDeclaringClass()); | ||||||
| 		assertNull(findAnnotationDescriptorForTypes(SubInheritedAnnotationInterface.class, Transactional.class)); | 		assertNull(findAnnotationDescriptorForTypes(SubInheritedAnnotationInterface.class, Transactional.class)); | ||||||
| 		assertNull(findAnnotationDescriptorForTypes(SubSubInheritedAnnotationInterface.class, Transactional.class)); | 		assertNull(findAnnotationDescriptorForTypes(SubSubInheritedAnnotationInterface.class, Transactional.class)); | ||||||
| 	} | 	} | ||||||
|  | @ -181,9 +181,9 @@ public class MetaAnnotationUtilsTests { | ||||||
| 	public void findAnnotationDescriptorForTypesForNonInheritedAnnotationOnClass() throws Exception { | 	public void findAnnotationDescriptorForTypesForNonInheritedAnnotationOnClass() throws Exception { | ||||||
| 		// Note: @Order is not inherited. | 		// Note: @Order is not inherited. | ||||||
| 		assertEquals(NonInheritedAnnotationClass.class, | 		assertEquals(NonInheritedAnnotationClass.class, | ||||||
| 			findAnnotationDescriptorForTypes(NonInheritedAnnotationClass.class, Order.class).getDeclaringClass()); | 			findAnnotationDescriptorForTypes(NonInheritedAnnotationClass.class, Order.class).getRootDeclaringClass()); | ||||||
| 		assertEquals(NonInheritedAnnotationClass.class, | 		assertEquals(NonInheritedAnnotationClass.class, | ||||||
| 			findAnnotationDescriptorForTypes(SubNonInheritedAnnotationClass.class, Order.class).getDeclaringClass()); | 			findAnnotationDescriptorForTypes(SubNonInheritedAnnotationClass.class, Order.class).getRootDeclaringClass()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Test | 	@Test | ||||||
|  | @ -191,7 +191,7 @@ public class MetaAnnotationUtilsTests { | ||||||
| 	public void findAnnotationDescriptorForTypesForNonInheritedAnnotationOnInterface() throws Exception { | 	public void findAnnotationDescriptorForTypesForNonInheritedAnnotationOnInterface() throws Exception { | ||||||
| 		// Note: @Order is not inherited. | 		// Note: @Order is not inherited. | ||||||
| 		assertEquals(NonInheritedAnnotationInterface.class, | 		assertEquals(NonInheritedAnnotationInterface.class, | ||||||
| 			findAnnotationDescriptorForTypes(NonInheritedAnnotationInterface.class, Order.class).getDeclaringClass()); | 			findAnnotationDescriptorForTypes(NonInheritedAnnotationInterface.class, Order.class).getRootDeclaringClass()); | ||||||
| 		assertNull(findAnnotationDescriptorForTypes(SubNonInheritedAnnotationInterface.class, Order.class)); | 		assertNull(findAnnotationDescriptorForTypes(SubNonInheritedAnnotationInterface.class, Order.class)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -201,7 +201,7 @@ public class MetaAnnotationUtilsTests { | ||||||
| 		Class<Component> annotationType = Component.class; | 		Class<Component> annotationType = Component.class; | ||||||
| 		UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes( | 		UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes( | ||||||
| 			HasLocalAndMetaComponentAnnotation.class, Transactional.class, annotationType, Order.class); | 			HasLocalAndMetaComponentAnnotation.class, Transactional.class, annotationType, Order.class); | ||||||
| 		assertEquals(HasLocalAndMetaComponentAnnotation.class, descriptor.getDeclaringClass()); | 		assertEquals(HasLocalAndMetaComponentAnnotation.class, descriptor.getRootDeclaringClass()); | ||||||
| 		assertEquals(annotationType, descriptor.getAnnotationType()); | 		assertEquals(annotationType, descriptor.getAnnotationType()); | ||||||
| 		assertNull(descriptor.getStereotype()); | 		assertNull(descriptor.getStereotype()); | ||||||
| 		assertNull(descriptor.getStereotypeType()); | 		assertNull(descriptor.getStereotypeType()); | ||||||
|  | @ -213,6 +213,44 @@ public class MetaAnnotationUtilsTests { | ||||||
| 		assertComponentOnStereotypeForMultipleCandidateTypes(startClass, startClass, "meta1", Meta1.class); | 		assertComponentOnStereotypeForMultipleCandidateTypes(startClass, startClass, "meta1", Meta1.class); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@Test | ||||||
|  | 	@SuppressWarnings("unchecked") | ||||||
|  | 	public void findAnnotationDescriptorForTypesWithMetaAnnotationWithDefaultAttributes() throws Exception { | ||||||
|  | 		Class<?> startClass = MetaConfigWithDefaultAttributesTestCase.class; | ||||||
|  | 		Class<ContextConfiguration> annotationType = ContextConfiguration.class; | ||||||
|  | 
 | ||||||
|  | 		UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes(startClass, Service.class, | ||||||
|  | 			ContextConfiguration.class, Order.class, Transactional.class); | ||||||
|  | 
 | ||||||
|  | 		assertNotNull(descriptor); | ||||||
|  | 		assertEquals(startClass, descriptor.getRootDeclaringClass()); | ||||||
|  | 		assertEquals(annotationType, descriptor.getAnnotationType()); | ||||||
|  | 		assertArrayEquals(new Class[] {}, ((ContextConfiguration) descriptor.getAnnotation()).value()); | ||||||
|  | 		assertArrayEquals(new Class[] { MetaConfig.DevConfig.class, MetaConfig.ProductionConfig.class }, | ||||||
|  | 			descriptor.getAnnotationAttributes().getClassArray("classes")); | ||||||
|  | 		assertNotNull(descriptor.getStereotype()); | ||||||
|  | 		assertEquals(MetaConfig.class, descriptor.getStereotypeType()); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	@SuppressWarnings("unchecked") | ||||||
|  | 	public void findAnnotationDescriptorForTypesWithMetaAnnotationWithOverriddenAttributes() throws Exception { | ||||||
|  | 		Class<?> startClass = MetaConfigWithOverriddenAttributesTestCase.class; | ||||||
|  | 		Class<ContextConfiguration> annotationType = ContextConfiguration.class; | ||||||
|  | 
 | ||||||
|  | 		UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes(startClass, Service.class, | ||||||
|  | 			ContextConfiguration.class, Order.class, Transactional.class); | ||||||
|  | 
 | ||||||
|  | 		assertNotNull(descriptor); | ||||||
|  | 		assertEquals(startClass, descriptor.getRootDeclaringClass()); | ||||||
|  | 		assertEquals(annotationType, descriptor.getAnnotationType()); | ||||||
|  | 		assertArrayEquals(new Class[] {}, ((ContextConfiguration) descriptor.getAnnotation()).value()); | ||||||
|  | 		assertArrayEquals(new Class[] { MetaAnnotationUtilsTests.class }, | ||||||
|  | 			descriptor.getAnnotationAttributes().getClassArray("classes")); | ||||||
|  | 		assertNotNull(descriptor.getStereotype()); | ||||||
|  | 		assertEquals(MetaConfig.class, descriptor.getStereotypeType()); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@Test | 	@Test | ||||||
| 	public void findAnnotationDescriptorForTypesForInterfaceWithMetaAnnotation() { | 	public void findAnnotationDescriptorForTypesForInterfaceWithMetaAnnotation() { | ||||||
| 		Class<InterfaceWithMetaAnnotation> startClass = InterfaceWithMetaAnnotation.class; | 		Class<InterfaceWithMetaAnnotation> startClass = InterfaceWithMetaAnnotation.class; | ||||||
|  | @ -279,6 +317,28 @@ public class MetaAnnotationUtilsTests { | ||||||
| 			ClassWithLocalMetaAnnotationAndMetaAnnotatedInterface { | 			ClassWithLocalMetaAnnotationAndMetaAnnotatedInterface { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@ContextConfiguration | ||||||
|  | 	@Retention(RetentionPolicy.RUNTIME) | ||||||
|  | 	static @interface MetaConfig { | ||||||
|  | 
 | ||||||
|  | 		static class DevConfig { | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		static class ProductionConfig { | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 		Class<?>[] classes() default { DevConfig.class, ProductionConfig.class }; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@MetaConfig | ||||||
|  | 	public class MetaConfigWithDefaultAttributesTestCase { | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@MetaConfig(classes = MetaAnnotationUtilsTests.class) | ||||||
|  | 	public class MetaConfigWithOverriddenAttributesTestCase { | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	// ------------------------------------------------------------------------- | 	// ------------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| 	@Transactional | 	@Transactional | ||||||
|  |  | ||||||
|  | @ -0,0 +1,155 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright 2002-2013 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 | ||||||
|  |  * | ||||||
|  |  * http://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; | ||||||
|  | 
 | ||||||
|  | import java.lang.annotation.Retention; | ||||||
|  | import java.lang.annotation.RetentionPolicy; | ||||||
|  | 
 | ||||||
|  | import org.junit.Test; | ||||||
|  | import org.springframework.core.annotation.AnnotationAttributes; | ||||||
|  | import org.springframework.test.context.MetaAnnotationUtils.AnnotationDescriptor; | ||||||
|  | 
 | ||||||
|  | import static org.junit.Assert.*; | ||||||
|  | import static org.springframework.test.context.MetaAnnotationUtils.*; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Unit tests for {@link MetaAnnotationUtils} that verify support for overridden | ||||||
|  |  * meta-annotation attributes. | ||||||
|  |  * | ||||||
|  |  * <p>See <a href="https://jira.springsource.org/browse/SPR-10181">SPR-10181</a>. | ||||||
|  |  * | ||||||
|  |  * @author Sam Brannen | ||||||
|  |  * @since 4.0 | ||||||
|  |  */ | ||||||
|  | public class OverriddenMetaAnnotationAttributesTests { | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void contextConfigurationValue() throws Exception { | ||||||
|  | 		Class<MetaValueConfigTest> declaringClass = MetaValueConfigTest.class; | ||||||
|  | 		AnnotationDescriptor<ContextConfiguration> descriptor = findAnnotationDescriptor(declaringClass, | ||||||
|  | 			ContextConfiguration.class); | ||||||
|  | 		assertNotNull(descriptor); | ||||||
|  | 		assertEquals(declaringClass, descriptor.getRootDeclaringClass()); | ||||||
|  | 		assertEquals(MetaValueConfig.class, descriptor.getStereotypeType()); | ||||||
|  | 		assertEquals(ContextConfiguration.class, descriptor.getAnnotationType()); | ||||||
|  | 		assertNotNull(descriptor.getStereotype()); | ||||||
|  | 		assertEquals(MetaValueConfig.class, descriptor.getStereotypeType()); | ||||||
|  | 
 | ||||||
|  | 		// direct access to annotation value: | ||||||
|  | 		assertArrayEquals(new String[] { "foo.xml" }, descriptor.getAnnotation().value()); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void overriddenContextConfigurationValue() throws Exception { | ||||||
|  | 		Class<?> declaringClass = OverriddenMetaValueConfigTest.class; | ||||||
|  | 		AnnotationDescriptor<ContextConfiguration> descriptor = findAnnotationDescriptor(declaringClass, | ||||||
|  | 			ContextConfiguration.class); | ||||||
|  | 		assertNotNull(descriptor); | ||||||
|  | 		assertEquals(declaringClass, descriptor.getRootDeclaringClass()); | ||||||
|  | 		assertEquals(MetaValueConfig.class, descriptor.getStereotypeType()); | ||||||
|  | 		assertEquals(ContextConfiguration.class, descriptor.getAnnotationType()); | ||||||
|  | 		assertNotNull(descriptor.getStereotype()); | ||||||
|  | 		assertEquals(MetaValueConfig.class, descriptor.getStereotypeType()); | ||||||
|  | 
 | ||||||
|  | 		// direct access to annotation value: | ||||||
|  | 		assertArrayEquals(new String[] { "foo.xml" }, descriptor.getAnnotation().value()); | ||||||
|  | 
 | ||||||
|  | 		// overridden attribute: | ||||||
|  | 		AnnotationAttributes attributes = descriptor.getAnnotationAttributes(); | ||||||
|  | 
 | ||||||
|  | 		// NOTE: we would like to be able to override the 'value' attribute; however, | ||||||
|  | 		// Spring currently does not allow overrides for the 'value' attribute. | ||||||
|  | 		// TODO Determine if Spring should allow overrides for the 'value' attribute in | ||||||
|  | 		// custom annotations. | ||||||
|  | 		assertArrayEquals(new String[] { "foo.xml" }, attributes.getStringArray("value")); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void contextConfigurationLocationsAndInheritLocations() throws Exception { | ||||||
|  | 		Class<MetaLocationsConfigTest> declaringClass = MetaLocationsConfigTest.class; | ||||||
|  | 		AnnotationDescriptor<ContextConfiguration> descriptor = findAnnotationDescriptor(declaringClass, | ||||||
|  | 			ContextConfiguration.class); | ||||||
|  | 		assertNotNull(descriptor); | ||||||
|  | 		assertEquals(declaringClass, descriptor.getRootDeclaringClass()); | ||||||
|  | 		assertEquals(MetaLocationsConfig.class, descriptor.getStereotypeType()); | ||||||
|  | 		assertEquals(ContextConfiguration.class, descriptor.getAnnotationType()); | ||||||
|  | 		assertNotNull(descriptor.getStereotype()); | ||||||
|  | 		assertEquals(MetaLocationsConfig.class, descriptor.getStereotypeType()); | ||||||
|  | 
 | ||||||
|  | 		// direct access to annotation attributes: | ||||||
|  | 		assertArrayEquals(new String[] { "foo.xml" }, descriptor.getAnnotation().locations()); | ||||||
|  | 		assertFalse(descriptor.getAnnotation().inheritLocations()); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void overriddenContextConfigurationLocationsAndInheritLocations() throws Exception { | ||||||
|  | 		Class<?> declaringClass = OverriddenMetaLocationsConfigTest.class; | ||||||
|  | 		AnnotationDescriptor<ContextConfiguration> descriptor = findAnnotationDescriptor(declaringClass, | ||||||
|  | 			ContextConfiguration.class); | ||||||
|  | 		assertNotNull(descriptor); | ||||||
|  | 		assertEquals(declaringClass, descriptor.getRootDeclaringClass()); | ||||||
|  | 		assertEquals(MetaLocationsConfig.class, descriptor.getStereotypeType()); | ||||||
|  | 		assertEquals(ContextConfiguration.class, descriptor.getAnnotationType()); | ||||||
|  | 		assertNotNull(descriptor.getStereotype()); | ||||||
|  | 		assertEquals(MetaLocationsConfig.class, descriptor.getStereotypeType()); | ||||||
|  | 
 | ||||||
|  | 		// direct access to annotation attributes: | ||||||
|  | 		assertArrayEquals(new String[] { "foo.xml" }, descriptor.getAnnotation().locations()); | ||||||
|  | 		assertFalse(descriptor.getAnnotation().inheritLocations()); | ||||||
|  | 
 | ||||||
|  | 		// overridden attributes: | ||||||
|  | 		AnnotationAttributes attributes = descriptor.getAnnotationAttributes(); | ||||||
|  | 		assertArrayEquals(new String[] { "bar.xml" }, attributes.getStringArray("locations")); | ||||||
|  | 		assertTrue(attributes.getBoolean("inheritLocations")); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	// ------------------------------------------------------------------------- | ||||||
|  | 
 | ||||||
|  | 	@ContextConfiguration("foo.xml") | ||||||
|  | 	@Retention(RetentionPolicy.RUNTIME) | ||||||
|  | 	static @interface MetaValueConfig { | ||||||
|  | 
 | ||||||
|  | 		String[] value() default {}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@MetaValueConfig | ||||||
|  | 	public static class MetaValueConfigTest { | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@MetaValueConfig("bar.xml") | ||||||
|  | 	public static class OverriddenMetaValueConfigTest { | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@ContextConfiguration(locations = "foo.xml", inheritLocations = false) | ||||||
|  | 	@Retention(RetentionPolicy.RUNTIME) | ||||||
|  | 	static @interface MetaLocationsConfig { | ||||||
|  | 
 | ||||||
|  | 		String[] locations() default {}; | ||||||
|  | 
 | ||||||
|  | 		boolean inheritLocations(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@MetaLocationsConfig(inheritLocations = true) | ||||||
|  | 	static class MetaLocationsConfigTest { | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@MetaLocationsConfig(locations = "bar.xml", inheritLocations = true) | ||||||
|  | 	static class OverriddenMetaLocationsConfigTest { | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -16,14 +16,14 @@ | ||||||
| 
 | 
 | ||||||
| package org.springframework.test.context; | package org.springframework.test.context; | ||||||
| 
 | 
 | ||||||
| import static org.junit.Assert.assertEquals; |  | ||||||
| 
 |  | ||||||
| import java.lang.annotation.Retention; | import java.lang.annotation.Retention; | ||||||
| import java.lang.annotation.RetentionPolicy; | import java.lang.annotation.RetentionPolicy; | ||||||
| 
 | 
 | ||||||
| import org.junit.Test; | import org.junit.Test; | ||||||
| import org.springframework.test.context.support.AbstractTestExecutionListener; | import org.springframework.test.context.support.AbstractTestExecutionListener; | ||||||
| 
 | 
 | ||||||
|  | import static org.junit.Assert.*; | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * <p> |  * <p> | ||||||
|  * JUnit 4 based unit test for the {@link TestExecutionListeners |  * JUnit 4 based unit test for the {@link TestExecutionListeners | ||||||
|  | @ -114,6 +114,29 @@ public class TestExecutionListenersTests { | ||||||
| 			testContextManager.getTestExecutionListeners().size()); | 			testContextManager.getTestExecutionListeners().size()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void verifyNumListenersRegisteredViaMetaAnnotationWithOverrides() throws Exception { | ||||||
|  | 		TestContextManager testContextManager = new TestContextManager(MetaWithOverridesExampleTestCase.class); | ||||||
|  | 		assertEquals("Num registered TELs for MetaWithOverridesExampleTestCase.", 3, | ||||||
|  | 			testContextManager.getTestExecutionListeners().size()); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void verifyNumListenersRegisteredViaMetaAnnotationWithInheritedListenersWithOverrides() throws Exception { | ||||||
|  | 		TestContextManager testContextManager = new TestContextManager( | ||||||
|  | 			MetaInheritedListenersWithOverridesExampleTestCase.class); | ||||||
|  | 		assertEquals("Num registered TELs for MetaInheritedListenersWithOverridesExampleTestCase.", 5, | ||||||
|  | 			testContextManager.getTestExecutionListeners().size()); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void verifyNumListenersRegisteredViaMetaAnnotationWithNonInheritedListenersWithOverrides() throws Exception { | ||||||
|  | 		TestContextManager testContextManager = new TestContextManager( | ||||||
|  | 			MetaNonInheritedListenersWithOverridesExampleTestCase.class); | ||||||
|  | 		assertEquals("Num registered TELs for MetaNonInheritedListenersWithOverridesExampleTestCase.", 8, | ||||||
|  | 			testContextManager.getTestExecutionListeners().size()); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@Test(expected = IllegalStateException.class) | 	@Test(expected = IllegalStateException.class) | ||||||
| 	public void verifyDuplicateListenersConfigThrowsException() throws Exception { | 	public void verifyDuplicateListenersConfigThrowsException() throws Exception { | ||||||
| 		new TestContextManager(DuplicateListenersConfigExampleTestCase.class); | 		new TestContextManager(DuplicateListenersConfigExampleTestCase.class); | ||||||
|  | @ -174,6 +197,32 @@ public class TestExecutionListenersTests { | ||||||
| 	static @interface MetaNonInheritedListeners { | 	static @interface MetaNonInheritedListeners { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@TestExecutionListeners | ||||||
|  | 	@Retention(RetentionPolicy.RUNTIME) | ||||||
|  | 	static @interface MetaListenersWithOverrides { | ||||||
|  | 
 | ||||||
|  | 		Class<? extends TestExecutionListener>[] listeners() default { FooTestExecutionListener.class, | ||||||
|  | 			BarTestExecutionListener.class }; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@TestExecutionListeners | ||||||
|  | 	@Retention(RetentionPolicy.RUNTIME) | ||||||
|  | 	static @interface MetaInheritedListenersWithOverrides { | ||||||
|  | 
 | ||||||
|  | 		Class<? extends TestExecutionListener>[] listeners() default QuuxTestExecutionListener.class; | ||||||
|  | 
 | ||||||
|  | 		boolean inheritListeners() default true; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@TestExecutionListeners | ||||||
|  | 	@Retention(RetentionPolicy.RUNTIME) | ||||||
|  | 	static @interface MetaNonInheritedListenersWithOverrides { | ||||||
|  | 
 | ||||||
|  | 		Class<? extends TestExecutionListener>[] listeners() default QuuxTestExecutionListener.class; | ||||||
|  | 
 | ||||||
|  | 		boolean inheritListeners() default false; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@MetaListeners | 	@MetaListeners | ||||||
| 	static class MetaExampleTestCase { | 	static class MetaExampleTestCase { | ||||||
| 	} | 	} | ||||||
|  | @ -186,6 +235,28 @@ public class TestExecutionListenersTests { | ||||||
| 	static class MetaNonInheritedListenersExampleTestCase extends MetaInheritedListenersExampleTestCase { | 	static class MetaNonInheritedListenersExampleTestCase extends MetaInheritedListenersExampleTestCase { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@MetaListenersWithOverrides(listeners = {// | ||||||
|  | 	FooTestExecutionListener.class,// | ||||||
|  | 		BarTestExecutionListener.class,// | ||||||
|  | 		BazTestExecutionListener.class // | ||||||
|  | 	}) | ||||||
|  | 	static class MetaWithOverridesExampleTestCase { | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@MetaInheritedListenersWithOverrides(listeners = { FooTestExecutionListener.class, BarTestExecutionListener.class }) | ||||||
|  | 	static class MetaInheritedListenersWithOverridesExampleTestCase extends MetaWithOverridesExampleTestCase { | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@MetaNonInheritedListenersWithOverrides(listeners = {// | ||||||
|  | 	FooTestExecutionListener.class,// | ||||||
|  | 		BarTestExecutionListener.class,// | ||||||
|  | 		BazTestExecutionListener.class // | ||||||
|  | 	},// | ||||||
|  | 	inheritListeners = true) | ||||||
|  | 	static class MetaNonInheritedListenersWithOverridesExampleTestCase extends | ||||||
|  | 			MetaInheritedListenersWithOverridesExampleTestCase { | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	static class FooTestExecutionListener extends AbstractTestExecutionListener { | 	static class FooTestExecutionListener extends AbstractTestExecutionListener { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| /* | /* | ||||||
|  * Copyright 2002-2007 the original author or authors. |  * Copyright 2002-2013 the original author or authors. | ||||||
|  * |  * | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  * you may not use this file except in compliance with the License. |  * you may not use this file except in compliance with the License. | ||||||
|  | @ -16,13 +16,21 @@ | ||||||
| 
 | 
 | ||||||
| package org.springframework.test.context.junit4; | package org.springframework.test.context.junit4; | ||||||
| 
 | 
 | ||||||
| import org.junit.Test; | import java.lang.annotation.Retention; | ||||||
|  | import java.lang.annotation.RetentionPolicy; | ||||||
| 
 | 
 | ||||||
|  | import org.junit.Test; | ||||||
|  | import org.junit.runners.model.FrameworkMethod; | ||||||
|  | import org.springframework.test.annotation.Timed; | ||||||
| import org.springframework.test.context.TestContextManager; | import org.springframework.test.context.TestContextManager; | ||||||
| 
 | 
 | ||||||
|  | import static org.junit.Assert.*; | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * @author Rick Evans |  * Unit tests for {@link SpringJUnit4ClassRunner}. | ||||||
|  |  * | ||||||
|  * @author Sam Brannen |  * @author Sam Brannen | ||||||
|  |  * @author Rick Evans | ||||||
|  * @since 2.5 |  * @since 2.5 | ||||||
|  */ |  */ | ||||||
| public class SpringJUnit4ClassRunnerTests { | public class SpringJUnit4ClassRunnerTests { | ||||||
|  | @ -37,7 +45,8 @@ public class SpringJUnit4ClassRunnerTests { | ||||||
| 
 | 
 | ||||||
| 					@Override | 					@Override | ||||||
| 					public void prepareTestInstance(Object testInstance) { | 					public void prepareTestInstance(Object testInstance) { | ||||||
| 						throw new RuntimeException("This RuntimeException should be caught and wrapped in an Exception."); | 						throw new RuntimeException( | ||||||
|  | 							"This RuntimeException should be caught and wrapped in an Exception."); | ||||||
| 					} | 					} | ||||||
| 				}; | 				}; | ||||||
| 			} | 			} | ||||||
|  | @ -45,4 +54,45 @@ public class SpringJUnit4ClassRunnerTests { | ||||||
| 		runner.createTest(); | 		runner.createTest(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void getSpringTimeoutViaMetaAnnotation() throws Exception { | ||||||
|  | 		SpringJUnit4ClassRunner runner = new SpringJUnit4ClassRunner(getClass()); | ||||||
|  | 		long timeout = runner.getSpringTimeout(new FrameworkMethod(getClass().getDeclaredMethod( | ||||||
|  | 			"springTimeoutWithMetaAnnotation"))); | ||||||
|  | 		assertEquals(10, timeout); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void getSpringTimeoutViaMetaAnnotationWithOverride() throws Exception { | ||||||
|  | 		SpringJUnit4ClassRunner runner = new SpringJUnit4ClassRunner(getClass()); | ||||||
|  | 		long timeout = runner.getSpringTimeout(new FrameworkMethod(getClass().getDeclaredMethod( | ||||||
|  | 			"springTimeoutWithMetaAnnotationAndOverride"))); | ||||||
|  | 		assertEquals(42, timeout); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// ------------------------------------------------------------------------- | ||||||
|  | 
 | ||||||
|  | 	@MetaTimed | ||||||
|  | 	void springTimeoutWithMetaAnnotation() { | ||||||
|  | 		/* no-op */ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@MetaTimedWithOverride(millis = 42) | ||||||
|  | 	void springTimeoutWithMetaAnnotationAndOverride() { | ||||||
|  | 		/* no-op */ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	@Timed(millis = 10) | ||||||
|  | 	@Retention(RetentionPolicy.RUNTIME) | ||||||
|  | 	private static @interface MetaTimed { | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Timed(millis = 1000) | ||||||
|  | 	@Retention(RetentionPolicy.RUNTIME) | ||||||
|  | 	private static @interface MetaTimedWithOverride { | ||||||
|  | 
 | ||||||
|  | 		long millis() default 1000; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -16,8 +16,6 @@ | ||||||
| 
 | 
 | ||||||
| package org.springframework.test.context.junit4; | package org.springframework.test.context.junit4; | ||||||
| 
 | 
 | ||||||
| import static org.junit.Assert.*; |  | ||||||
| 
 |  | ||||||
| import java.lang.annotation.Retention; | import java.lang.annotation.Retention; | ||||||
| import java.lang.annotation.RetentionPolicy; | import java.lang.annotation.RetentionPolicy; | ||||||
| 
 | 
 | ||||||
|  | @ -31,6 +29,8 @@ import org.springframework.test.context.TestExecutionListeners; | ||||||
| import org.springframework.tests.Assume; | import org.springframework.tests.Assume; | ||||||
| import org.springframework.tests.TestGroup; | import org.springframework.tests.TestGroup; | ||||||
| 
 | 
 | ||||||
|  | import static org.junit.Assert.*; | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * Verifies proper handling of the following in conjunction with the |  * Verifies proper handling of the following in conjunction with the | ||||||
|  * {@link SpringJUnit4ClassRunner}: |  * {@link SpringJUnit4ClassRunner}: | ||||||
|  | @ -54,11 +54,11 @@ public class TimedSpringRunnerTests { | ||||||
| 		notifier.addListener(listener); | 		notifier.addListener(listener); | ||||||
| 
 | 
 | ||||||
| 		new SpringJUnit4ClassRunner(testClass).run(notifier); | 		new SpringJUnit4ClassRunner(testClass).run(notifier); | ||||||
| 		assertEquals("Verifying number of tests started for test class [" + testClass + "].", 6, | 		assertEquals("Verifying number of tests started for test class [" + testClass + "].", 7, | ||||||
| 			listener.getTestStartedCount()); | 			listener.getTestStartedCount()); | ||||||
| 		assertEquals("Verifying number of failures for test class [" + testClass + "].", 4, | 		assertEquals("Verifying number of failures for test class [" + testClass + "].", 5, | ||||||
| 			listener.getTestFailureCount()); | 			listener.getTestFailureCount()); | ||||||
| 		assertEquals("Verifying number of tests finished for test class [" + testClass + "].", 6, | 		assertEquals("Verifying number of tests finished for test class [" + testClass + "].", 7, | ||||||
| 			listener.getTestFinishedCount()); | 			listener.getTestFinishedCount()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -101,6 +101,13 @@ public class TimedSpringRunnerTests { | ||||||
| 			Thread.sleep(20); | 			Thread.sleep(20); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		// Should Fail due to timeout. | ||||||
|  | 		@Test | ||||||
|  | 		@MetaTimedWithOverride(millis = 10) | ||||||
|  | 		public void springTimeoutWithSleepAndMetaAnnotationAndOverride() throws Exception { | ||||||
|  | 			Thread.sleep(20); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		// Should Fail due to duplicate configuration. | 		// Should Fail due to duplicate configuration. | ||||||
| 		@Test(timeout = 200) | 		@Test(timeout = 200) | ||||||
| 		@Timed(millis = 200) | 		@Timed(millis = 200) | ||||||
|  | @ -114,4 +121,11 @@ public class TimedSpringRunnerTests { | ||||||
| 	private static @interface MetaTimed { | 	private static @interface MetaTimed { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@Timed(millis = 1000) | ||||||
|  | 	@Retention(RetentionPolicy.RUNTIME) | ||||||
|  | 	private static @interface MetaTimedWithOverride { | ||||||
|  | 
 | ||||||
|  | 		long millis() default 1000; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,68 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright 2002-2013 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 | ||||||
|  |  * | ||||||
|  |  * http://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.junit4.annotation.meta; | ||||||
|  | 
 | ||||||
|  | import java.lang.annotation.ElementType; | ||||||
|  | import java.lang.annotation.Retention; | ||||||
|  | import java.lang.annotation.RetentionPolicy; | ||||||
|  | import java.lang.annotation.Target; | ||||||
|  | 
 | ||||||
|  | import org.springframework.context.annotation.Bean; | ||||||
|  | import org.springframework.context.annotation.Configuration; | ||||||
|  | import org.springframework.context.annotation.Profile; | ||||||
|  | import org.springframework.test.context.ActiveProfiles; | ||||||
|  | import org.springframework.test.context.ContextConfiguration; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Custom configuration annotation with meta-annotation attribute overrides for | ||||||
|  |  * {@link ContextConfiguration#classes} and {@link ActiveProfiles#profiles}. | ||||||
|  |  * | ||||||
|  |  * @author Sam Brannen | ||||||
|  |  * @since 4.0 | ||||||
|  |  */ | ||||||
|  | @ContextConfiguration | ||||||
|  | @ActiveProfiles | ||||||
|  | @Retention(RetentionPolicy.RUNTIME) | ||||||
|  | @Target(ElementType.TYPE) | ||||||
|  | public @interface MetaConfig { | ||||||
|  | 
 | ||||||
|  | 	@Configuration | ||||||
|  | 	@Profile("dev") | ||||||
|  | 	static class DevConfig { | ||||||
|  | 
 | ||||||
|  | 		@Bean | ||||||
|  | 		public String foo() { | ||||||
|  | 			return "Dev Foo"; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Configuration | ||||||
|  | 	@Profile("prod") | ||||||
|  | 	static class ProductionConfig { | ||||||
|  | 
 | ||||||
|  | 		@Bean | ||||||
|  | 		public String foo() { | ||||||
|  | 			return "Production Foo"; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	Class<?>[] classes() default { DevConfig.class, ProductionConfig.class }; | ||||||
|  | 
 | ||||||
|  | 	String[] profiles() default "dev"; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,45 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright 2002-2013 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 | ||||||
|  |  * | ||||||
|  |  * http://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.junit4.annotation.meta; | ||||||
|  | 
 | ||||||
|  | import org.junit.Test; | ||||||
|  | import org.junit.runner.RunWith; | ||||||
|  | import org.springframework.beans.factory.annotation.Autowired; | ||||||
|  | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; | ||||||
|  | 
 | ||||||
|  | import static org.junit.Assert.*; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Integration tests for meta-annotation attribute override support, relying on | ||||||
|  |  * default attribute values defined in {@link MetaConfig}. | ||||||
|  |  * | ||||||
|  |  * @author Sam Brannen | ||||||
|  |  * @since 4.0 | ||||||
|  |  */ | ||||||
|  | @RunWith(SpringJUnit4ClassRunner.class) | ||||||
|  | @MetaConfig | ||||||
|  | public class MetaConfigDefaultsTests { | ||||||
|  | 
 | ||||||
|  | 	@Autowired | ||||||
|  | 	private String foo; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void foo() { | ||||||
|  | 		assertEquals("Dev Foo", foo); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -0,0 +1,66 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright 2002-2013 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 | ||||||
|  |  * | ||||||
|  |  * http://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.junit4.annotation.meta; | ||||||
|  | 
 | ||||||
|  | import org.junit.Test; | ||||||
|  | import org.junit.runner.RunWith; | ||||||
|  | import org.springframework.beans.factory.annotation.Autowired; | ||||||
|  | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; | ||||||
|  | import org.springframework.test.context.junit4.annotation.PojoAndStringConfig; | ||||||
|  | import org.springframework.tests.sample.beans.Employee; | ||||||
|  | import org.springframework.tests.sample.beans.Pet; | ||||||
|  | 
 | ||||||
|  | import static org.junit.Assert.*; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Integration tests for meta-annotation attribute override support, overriding | ||||||
|  |  * default attribute values defined in {@link MetaConfig}. | ||||||
|  |  * | ||||||
|  |  * @author Sam Brannen | ||||||
|  |  * @since 4.0 | ||||||
|  |  */ | ||||||
|  | @RunWith(SpringJUnit4ClassRunner.class) | ||||||
|  | @MetaConfig(classes = { PojoAndStringConfig.class, MetaConfig.ProductionConfig.class }, profiles = "prod") | ||||||
|  | public class MetaConfigOverrideTests { | ||||||
|  | 
 | ||||||
|  | 	@Autowired | ||||||
|  | 	private String foo; | ||||||
|  | 
 | ||||||
|  | 	@Autowired | ||||||
|  | 	private Pet pet; | ||||||
|  | 
 | ||||||
|  | 	@Autowired | ||||||
|  | 	protected Employee employee; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void verifyEmployee() { | ||||||
|  | 		assertNotNull("The employee should have been autowired.", this.employee); | ||||||
|  | 		assertEquals("John Smith", this.employee.getName()); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void verifyPet() { | ||||||
|  | 		assertNotNull("The pet should have been autowired.", this.pet); | ||||||
|  | 		assertEquals("Fido", this.pet.getName()); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void verifyFoo() { | ||||||
|  | 		assertEquals("Production Foo", this.foo); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -26,8 +26,8 @@ import org.springframework.test.annotation.DirtiesContext.ClassMode; | ||||||
| import org.springframework.test.annotation.DirtiesContext.HierarchyMode; | import org.springframework.test.annotation.DirtiesContext.HierarchyMode; | ||||||
| import org.springframework.test.context.TestContext; | import org.springframework.test.context.TestContext; | ||||||
| 
 | 
 | ||||||
| import static org.mockito.Matchers.*; |  | ||||||
| import static org.mockito.Mockito.*; | import static org.mockito.Mockito.*; | ||||||
|  | import static org.springframework.test.annotation.DirtiesContext.ClassMode.*; | ||||||
| import static org.springframework.test.annotation.DirtiesContext.HierarchyMode.*; | import static org.springframework.test.annotation.DirtiesContext.HierarchyMode.*; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -84,7 +84,7 @@ public class DirtiesContextTestExecutionListenerTests { | ||||||
| 		Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz); | 		Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz); | ||||||
| 		when(testContext.getTestMethod()).thenReturn(clazz.getDeclaredMethod("clean")); | 		when(testContext.getTestMethod()).thenReturn(clazz.getDeclaredMethod("clean")); | ||||||
| 		listener.afterTestMethod(testContext); | 		listener.afterTestMethod(testContext); | ||||||
| 		verify(testContext, times(0)).markApplicationContextDirty(any(HierarchyMode.class)); | 		verify(testContext, times(0)).markApplicationContextDirty(EXHAUSTIVE); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Test | 	@Test | ||||||
|  | @ -93,7 +93,16 @@ public class DirtiesContextTestExecutionListenerTests { | ||||||
| 		Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz); | 		Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz); | ||||||
| 		when(testContext.getTestMethod()).thenReturn(clazz.getDeclaredMethod("clean")); | 		when(testContext.getTestMethod()).thenReturn(clazz.getDeclaredMethod("clean")); | ||||||
| 		listener.afterTestMethod(testContext); | 		listener.afterTestMethod(testContext); | ||||||
| 		verify(testContext, times(0)).markApplicationContextDirty(any(HierarchyMode.class)); | 		verify(testContext, times(0)).markApplicationContextDirty(EXHAUSTIVE); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void afterTestMethodForDirtiesContextViaMetaAnnotationWithOverrides() throws Exception { | ||||||
|  | 		Class<?> clazz = DirtiesContextViaMetaAnnotationWithOverrides.class; | ||||||
|  | 		Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz); | ||||||
|  | 		when(testContext.getTestMethod()).thenReturn(clazz.getDeclaredMethod("clean")); | ||||||
|  | 		listener.afterTestMethod(testContext); | ||||||
|  | 		verify(testContext, times(1)).markApplicationContextDirty(CURRENT_LEVEL); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// ------------------------------------------------------------------------- | 	// ------------------------------------------------------------------------- | ||||||
|  | @ -103,7 +112,7 @@ public class DirtiesContextTestExecutionListenerTests { | ||||||
| 		Class<?> clazz = getClass(); | 		Class<?> clazz = getClass(); | ||||||
| 		Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz); | 		Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz); | ||||||
| 		listener.afterTestClass(testContext); | 		listener.afterTestClass(testContext); | ||||||
| 		verify(testContext, times(0)).markApplicationContextDirty(any(HierarchyMode.class)); | 		verify(testContext, times(0)).markApplicationContextDirty(EXHAUSTIVE); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Test | 	@Test | ||||||
|  | @ -127,7 +136,7 @@ public class DirtiesContextTestExecutionListenerTests { | ||||||
| 		Class<?> clazz = DirtiesContextDeclaredLocallyAfterClass.class; | 		Class<?> clazz = DirtiesContextDeclaredLocallyAfterClass.class; | ||||||
| 		Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz); | 		Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz); | ||||||
| 		listener.afterTestClass(testContext); | 		listener.afterTestClass(testContext); | ||||||
| 		verify(testContext, times(1)).markApplicationContextDirty(any(HierarchyMode.class)); | 		verify(testContext, times(1)).markApplicationContextDirty(EXHAUSTIVE); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Test | 	@Test | ||||||
|  | @ -135,7 +144,23 @@ public class DirtiesContextTestExecutionListenerTests { | ||||||
| 		Class<?> clazz = DirtiesContextDeclaredViaMetaAnnotationAfterClass.class; | 		Class<?> clazz = DirtiesContextDeclaredViaMetaAnnotationAfterClass.class; | ||||||
| 		Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz); | 		Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz); | ||||||
| 		listener.afterTestClass(testContext); | 		listener.afterTestClass(testContext); | ||||||
| 		verify(testContext, times(1)).markApplicationContextDirty(any(HierarchyMode.class)); | 		verify(testContext, times(1)).markApplicationContextDirty(EXHAUSTIVE); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void afterTestClassForDirtiesContextDeclaredViaMetaAnnotationWithOverrides() throws Exception { | ||||||
|  | 		Class<?> clazz = DirtiesContextViaMetaAnnotationWithOverrides.class; | ||||||
|  | 		Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz); | ||||||
|  | 		listener.afterTestClass(testContext); | ||||||
|  | 		verify(testContext, times(1)).markApplicationContextDirty(CURRENT_LEVEL); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void afterTestClassForDirtiesContextDeclaredViaMetaAnnotationWithOverridenAttributes() throws Exception { | ||||||
|  | 		Class<?> clazz = DirtiesContextViaMetaAnnotationWithOverridenAttributes.class; | ||||||
|  | 		Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz); | ||||||
|  | 		listener.afterTestClass(testContext); | ||||||
|  | 		verify(testContext, times(1)).markApplicationContextDirty(EXHAUSTIVE); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// ------------------------------------------------------------------------- | 	// ------------------------------------------------------------------------- | ||||||
|  | @ -156,17 +181,17 @@ public class DirtiesContextTestExecutionListenerTests { | ||||||
| 	static @interface MetaDirty { | 	static @interface MetaDirty { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) | 	@DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) | ||||||
| 	@Retention(RetentionPolicy.RUNTIME) | 	@Retention(RetentionPolicy.RUNTIME) | ||||||
| 	static @interface MetaDirtyAfterEachTestMethod { | 	static @interface MetaDirtyAfterEachTestMethod { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@DirtiesContext(classMode = ClassMode.AFTER_CLASS) | 	@DirtiesContext(classMode = AFTER_CLASS) | ||||||
| 	@Retention(RetentionPolicy.RUNTIME) | 	@Retention(RetentionPolicy.RUNTIME) | ||||||
| 	static @interface MetaDirtyAfterClass { | 	static @interface MetaDirtyAfterClass { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) | 	@DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) | ||||||
| 	static class DirtiesContextDeclaredLocallyAfterEachTestMethod { | 	static class DirtiesContextDeclaredLocallyAfterEachTestMethod { | ||||||
| 
 | 
 | ||||||
| 		void clean() { | 		void clean() { | ||||||
|  | @ -174,6 +199,15 @@ public class DirtiesContextTestExecutionListenerTests { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@DirtiesContext | ||||||
|  | 	@Retention(RetentionPolicy.RUNTIME) | ||||||
|  | 	static @interface MetaDirtyWithOverrides { | ||||||
|  | 
 | ||||||
|  | 		ClassMode classMode() default AFTER_EACH_TEST_METHOD; | ||||||
|  | 
 | ||||||
|  | 		HierarchyMode hierarchyMode() default HierarchyMode.CURRENT_LEVEL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@MetaDirtyAfterEachTestMethod | 	@MetaDirtyAfterEachTestMethod | ||||||
| 	static class DirtiesContextDeclaredViaMetaAnnotationAfterEachTestMethod { | 	static class DirtiesContextDeclaredViaMetaAnnotationAfterEachTestMethod { | ||||||
| 
 | 
 | ||||||
|  | @ -182,7 +216,7 @@ public class DirtiesContextTestExecutionListenerTests { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@DirtiesContext(classMode = ClassMode.AFTER_CLASS) | 	@DirtiesContext(classMode = AFTER_CLASS) | ||||||
| 	static class DirtiesContextDeclaredLocallyAfterClass { | 	static class DirtiesContextDeclaredLocallyAfterClass { | ||||||
| 
 | 
 | ||||||
| 		void clean() { | 		void clean() { | ||||||
|  | @ -198,4 +232,20 @@ public class DirtiesContextTestExecutionListenerTests { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@MetaDirtyWithOverrides | ||||||
|  | 	static class DirtiesContextViaMetaAnnotationWithOverrides { | ||||||
|  | 
 | ||||||
|  | 		void clean() { | ||||||
|  | 			/* no-op */ | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@MetaDirtyWithOverrides(classMode = AFTER_CLASS, hierarchyMode = EXHAUSTIVE) | ||||||
|  | 	static class DirtiesContextViaMetaAnnotationWithOverridenAttributes { | ||||||
|  | 
 | ||||||
|  | 		void clean() { | ||||||
|  | 			/* no-op */ | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -21,14 +21,17 @@ import java.lang.annotation.RetentionPolicy; | ||||||
| 
 | 
 | ||||||
| import org.junit.Test; | import org.junit.Test; | ||||||
| import org.mockito.Mockito; | import org.mockito.Mockito; | ||||||
|  | import org.springframework.test.annotation.Rollback; | ||||||
| import org.springframework.test.context.TestContext; | import org.springframework.test.context.TestContext; | ||||||
| import org.springframework.transaction.PlatformTransactionManager; | import org.springframework.transaction.PlatformTransactionManager; | ||||||
| import org.springframework.transaction.TransactionDefinition; | import org.springframework.transaction.TransactionDefinition; | ||||||
|  | import org.springframework.transaction.annotation.Propagation; | ||||||
| import org.springframework.transaction.annotation.Transactional; | import org.springframework.transaction.annotation.Transactional; | ||||||
| import org.springframework.transaction.support.SimpleTransactionStatus; | import org.springframework.transaction.support.SimpleTransactionStatus; | ||||||
| 
 | 
 | ||||||
| import static org.junit.Assert.*; | import static org.junit.Assert.*; | ||||||
| import static org.mockito.Mockito.*; | import static org.mockito.Mockito.*; | ||||||
|  | import static org.springframework.transaction.annotation.Propagation.*; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Unit tests for {@link TransactionalTestExecutionListener}. |  * Unit tests for {@link TransactionalTestExecutionListener}. | ||||||
|  | @ -56,6 +59,11 @@ public class TransactionalTestExecutionListenerTests { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	private void assertBeforeTestMethodWithTransactionalTestMethod(Class<? extends Invocable> clazz) throws Exception { | 	private void assertBeforeTestMethodWithTransactionalTestMethod(Class<? extends Invocable> clazz) throws Exception { | ||||||
|  | 		assertBeforeTestMethodWithTransactionalTestMethod(clazz, true); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	private void assertBeforeTestMethodWithTransactionalTestMethod(Class<? extends Invocable> clazz, boolean invokedInTx) | ||||||
|  | 			throws Exception { | ||||||
| 		Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz); | 		Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz); | ||||||
| 		Invocable instance = clazz.newInstance(); | 		Invocable instance = clazz.newInstance(); | ||||||
| 		when(testContext.getTestInstance()).thenReturn(instance); | 		when(testContext.getTestInstance()).thenReturn(instance); | ||||||
|  | @ -63,7 +71,7 @@ public class TransactionalTestExecutionListenerTests { | ||||||
| 
 | 
 | ||||||
| 		assertFalse(instance.invoked); | 		assertFalse(instance.invoked); | ||||||
| 		listener.beforeTestMethod(testContext); | 		listener.beforeTestMethod(testContext); | ||||||
| 		assertTrue(instance.invoked); | 		assertEquals(invokedInTx, instance.invoked); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	private void assertBeforeTestMethodWithNonTransactionalTestMethod(Class<? extends Invocable> clazz) | 	private void assertBeforeTestMethodWithNonTransactionalTestMethod(Class<? extends Invocable> clazz) | ||||||
|  | @ -109,6 +117,22 @@ public class TransactionalTestExecutionListenerTests { | ||||||
| 		assertFalse(instance.invoked); | 		assertFalse(instance.invoked); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	private void assertTransactionConfigurationAttributes(Class<?> clazz, String transactionManagerName, | ||||||
|  | 			boolean defaultRollback) { | ||||||
|  | 		Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz); | ||||||
|  | 
 | ||||||
|  | 		TransactionConfigurationAttributes attributes = listener.retrieveConfigurationAttributes(testContext); | ||||||
|  | 		assertNotNull(attributes); | ||||||
|  | 		assertEquals(transactionManagerName, attributes.getTransactionManagerName()); | ||||||
|  | 		assertEquals(defaultRollback, attributes.isDefaultRollback()); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	private void assertIsRollback(Class<?> clazz, boolean rollback) throws NoSuchMethodException, Exception { | ||||||
|  | 		Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz); | ||||||
|  | 		when(testContext.getTestMethod()).thenReturn(clazz.getDeclaredMethod("test")); | ||||||
|  | 		assertEquals(rollback, listener.isRollback(testContext)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@Test | 	@Test | ||||||
| 	public void beforeTestMethodWithTransactionalDeclaredOnClassLocally() throws Exception { | 	public void beforeTestMethodWithTransactionalDeclaredOnClassLocally() throws Exception { | ||||||
| 		assertBeforeTestMethodWithTransactionalTestMethod(TransactionalDeclaredOnClassLocallyTestCase.class); | 		assertBeforeTestMethodWithTransactionalTestMethod(TransactionalDeclaredOnClassLocallyTestCase.class); | ||||||
|  | @ -119,6 +143,23 @@ public class TransactionalTestExecutionListenerTests { | ||||||
| 		assertBeforeTestMethodWithTransactionalTestMethod(TransactionalDeclaredOnClassViaMetaAnnotationTestCase.class); | 		assertBeforeTestMethodWithTransactionalTestMethod(TransactionalDeclaredOnClassViaMetaAnnotationTestCase.class); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void beforeTestMethodWithTransactionalDeclaredOnClassViaMetaAnnotationWithOverride() throws Exception { | ||||||
|  | 		// Note: not actually invoked within a transaction since the test class is | ||||||
|  | 		// annotated with @MetaTxWithOverride(propagation = NOT_SUPPORTED) | ||||||
|  | 		assertBeforeTestMethodWithTransactionalTestMethod( | ||||||
|  | 			TransactionalDeclaredOnClassViaMetaAnnotationWithOverrideTestCase.class, false); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void beforeTestMethodWithTransactionalDeclaredOnMethodViaMetaAnnotationWithOverride() throws Exception { | ||||||
|  | 		// Note: not actually invoked within a transaction since the method is | ||||||
|  | 		// annotated with @MetaTxWithOverride(propagation = NOT_SUPPORTED) | ||||||
|  | 		assertBeforeTestMethodWithTransactionalTestMethod( | ||||||
|  | 			TransactionalDeclaredOnMethodViaMetaAnnotationWithOverrideTestCase.class, false); | ||||||
|  | 		assertBeforeTestMethodWithNonTransactionalTestMethod(TransactionalDeclaredOnMethodViaMetaAnnotationWithOverrideTestCase.class); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@Test | 	@Test | ||||||
| 	public void beforeTestMethodWithTransactionalDeclaredOnMethodLocally() throws Exception { | 	public void beforeTestMethodWithTransactionalDeclaredOnMethodLocally() throws Exception { | ||||||
| 		assertBeforeTestMethod(TransactionalDeclaredOnMethodLocallyTestCase.class); | 		assertBeforeTestMethod(TransactionalDeclaredOnMethodLocallyTestCase.class); | ||||||
|  | @ -149,6 +190,55 @@ public class TransactionalTestExecutionListenerTests { | ||||||
| 		assertAfterTestMethod(AfterTransactionDeclaredViaMetaAnnotationTestCase.class); | 		assertAfterTestMethod(AfterTransactionDeclaredViaMetaAnnotationTestCase.class); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void retrieveConfigurationAttributesWithMissingTransactionConfiguration() throws Exception { | ||||||
|  | 		assertTransactionConfigurationAttributes(MissingTransactionConfigurationTestCase.class, "transactionManager", | ||||||
|  | 			true); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void retrieveConfigurationAttributesWithEmptyTransactionConfiguration() throws Exception { | ||||||
|  | 		assertTransactionConfigurationAttributes(EmptyTransactionConfigurationTestCase.class, "transactionManager", | ||||||
|  | 			true); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void retrieveConfigurationAttributesWithExplicitValues() throws Exception { | ||||||
|  | 		assertTransactionConfigurationAttributes(TransactionConfigurationWithExplicitValuesTestCase.class, "tm", false); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void retrieveConfigurationAttributesViaMetaAnnotation() throws Exception { | ||||||
|  | 		assertTransactionConfigurationAttributes(TransactionConfigurationViaMetaAnnotationTestCase.class, "metaTxMgr", | ||||||
|  | 			true); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void retrieveConfigurationAttributesViaMetaAnnotationWithOverride() throws Exception { | ||||||
|  | 		assertTransactionConfigurationAttributes(TransactionConfigurationViaMetaAnnotationWithOverrideTestCase.class, | ||||||
|  | 			"overriddenTxMgr", true); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void isRollbackWithMissingRollback() throws Exception { | ||||||
|  | 		assertIsRollback(MissingRollbackTestCase.class, true); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void isRollbackWithEmptyRollback() throws Exception { | ||||||
|  | 		assertIsRollback(EmptyRollbackTestCase.class, true); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void isRollbackWithExplicitValue() throws Exception { | ||||||
|  | 		assertIsRollback(RollbackWithExplicitValueTestCase.class, false); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void isRollbackViaMetaAnnotation() throws Exception { | ||||||
|  | 		assertIsRollback(RollbackViaMetaAnnotationTestCase.class, false); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| 	// ------------------------------------------------------------------------- | 	// ------------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
|  | @ -157,6 +247,13 @@ public class TransactionalTestExecutionListenerTests { | ||||||
| 	private static @interface MetaTransactional { | 	private static @interface MetaTransactional { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@Transactional | ||||||
|  | 	@Retention(RetentionPolicy.RUNTIME) | ||||||
|  | 	private static @interface MetaTxWithOverride { | ||||||
|  | 
 | ||||||
|  | 		Propagation propagation() default REQUIRED; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@BeforeTransaction | 	@BeforeTransaction | ||||||
| 	@Retention(RetentionPolicy.RUNTIME) | 	@Retention(RetentionPolicy.RUNTIME) | ||||||
| 	private static @interface MetaBeforeTransaction { | 	private static @interface MetaBeforeTransaction { | ||||||
|  | @ -167,6 +264,18 @@ public class TransactionalTestExecutionListenerTests { | ||||||
| 	private static @interface MetaAfterTransaction { | 	private static @interface MetaAfterTransaction { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@TransactionConfiguration | ||||||
|  | 	@Retention(RetentionPolicy.RUNTIME) | ||||||
|  | 	private static @interface MetaTxConfig { | ||||||
|  | 
 | ||||||
|  | 		String transactionManager() default "metaTxMgr"; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Rollback(false) | ||||||
|  | 	@Retention(RetentionPolicy.RUNTIME) | ||||||
|  | 	private static @interface Commit { | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	private static abstract class Invocable { | 	private static abstract class Invocable { | ||||||
| 
 | 
 | ||||||
| 		boolean invoked = false; | 		boolean invoked = false; | ||||||
|  | @ -213,10 +322,6 @@ public class TransactionalTestExecutionListenerTests { | ||||||
| 		public void transactionalTest() { | 		public void transactionalTest() { | ||||||
| 			/* no-op */ | 			/* no-op */ | ||||||
| 		} | 		} | ||||||
| 
 |  | ||||||
| 		public void nonTransactionalTest() { |  | ||||||
| 			/* no-op */ |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	static class TransactionalDeclaredOnMethodViaMetaAnnotationTestCase extends Invocable { | 	static class TransactionalDeclaredOnMethodViaMetaAnnotationTestCase extends Invocable { | ||||||
|  | @ -236,6 +341,36 @@ public class TransactionalTestExecutionListenerTests { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@MetaTxWithOverride(propagation = NOT_SUPPORTED) | ||||||
|  | 	static class TransactionalDeclaredOnClassViaMetaAnnotationWithOverrideTestCase extends Invocable { | ||||||
|  | 
 | ||||||
|  | 		@BeforeTransaction | ||||||
|  | 		public void beforeTransaction() { | ||||||
|  | 			invoked = true; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public void transactionalTest() { | ||||||
|  | 			/* no-op */ | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	static class TransactionalDeclaredOnMethodViaMetaAnnotationWithOverrideTestCase extends Invocable { | ||||||
|  | 
 | ||||||
|  | 		@BeforeTransaction | ||||||
|  | 		public void beforeTransaction() { | ||||||
|  | 			invoked = true; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		@MetaTxWithOverride(propagation = NOT_SUPPORTED) | ||||||
|  | 		public void transactionalTest() { | ||||||
|  | 			/* no-op */ | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public void nonTransactionalTest() { | ||||||
|  | 			/* no-op */ | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	static class BeforeTransactionDeclaredLocallyTestCase extends Invocable { | 	static class BeforeTransactionDeclaredLocallyTestCase extends Invocable { | ||||||
| 
 | 
 | ||||||
| 		@BeforeTransaction | 		@BeforeTransaction | ||||||
|  | @ -304,4 +439,50 @@ public class TransactionalTestExecutionListenerTests { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	static class MissingTransactionConfigurationTestCase { | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@TransactionConfiguration | ||||||
|  | 	static class EmptyTransactionConfigurationTestCase { | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@TransactionConfiguration(transactionManager = "tm", defaultRollback = false) | ||||||
|  | 	static class TransactionConfigurationWithExplicitValuesTestCase { | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@MetaTxConfig | ||||||
|  | 	static class TransactionConfigurationViaMetaAnnotationTestCase { | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@MetaTxConfig(transactionManager = "overriddenTxMgr") | ||||||
|  | 	static class TransactionConfigurationViaMetaAnnotationWithOverrideTestCase { | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	static class MissingRollbackTestCase { | ||||||
|  | 
 | ||||||
|  | 		public void test() { | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	static class EmptyRollbackTestCase { | ||||||
|  | 
 | ||||||
|  | 		@Rollback | ||||||
|  | 		public void test() { | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	static class RollbackWithExplicitValueTestCase { | ||||||
|  | 
 | ||||||
|  | 		@Rollback(false) | ||||||
|  | 		public void test() { | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	static class RollbackViaMetaAnnotationTestCase { | ||||||
|  | 
 | ||||||
|  | 		@Commit | ||||||
|  | 		public void test() { | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue