Permalink
Browse files

Merge pull request #393 from sbrannen/SPR-7827

* SPR-7827:
  Provide meta-annotation support in the TCF
  • Loading branch information...
sbrannen committed Oct 28, 2013
2 parents 56dfcd1 + 5e7021f commit 2bd5a535e1e6dbae83b016376ffe114dc5cb1384
Showing with 1,852 additions and 205 deletions.
  1. +20 −5 spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java
  2. +19 −3 spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java
  3. +7 −5 spring-test/src/main/java/org/springframework/test/annotation/ProfileValueUtils.java
  4. +9 −7 spring-test/src/main/java/org/springframework/test/annotation/Repeat.java
  5. +12 −15 spring-test/src/main/java/org/springframework/test/annotation/Timed.java
  6. +74 −44 spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java
  7. +2 −23 spring-test/src/main/java/org/springframework/test/context/DefaultTestContext.java
  8. +248 −0 spring-test/src/main/java/org/springframework/test/context/MetaAnnotationUtils.java
  9. +15 −6 spring-test/src/main/java/org/springframework/test/context/TestContextManager.java
  10. +3 −2 spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java
  11. +12 −8 ...t/src/main/java/org/springframework/test/context/support/DirtiesContextTestExecutionListener.java
  12. +7 −5 spring-test/src/main/java/org/springframework/test/context/transaction/AfterTransaction.java
  13. +7 −5 spring-test/src/main/java/org/springframework/test/context/transaction/BeforeTransaction.java
  14. +37 −36 ...rc/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java
  15. +307 −0 ...in/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java
  16. +95 −4 spring-test/src/test/java/org/springframework/test/annotation/ProfileValueUtilsTests.java
  17. +43 −1 spring-test/src/test/java/org/springframework/test/context/AbstractContextLoaderUtilsTests.java
  18. +52 −0 ...ng-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsActiveProfilesTests.java
  19. +20 −10 ...rc/test/java/org/springframework/test/context/ContextLoaderUtilsConfigurationAttributesTests.java
  20. +103 −2 ...-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsContextHierarchyTests.java
  21. +14 −2 spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsMergedConfigTests.java
  22. +321 −0 spring-test/src/test/java/org/springframework/test/context/MetaAnnotationUtilsTests.java
  23. +69 −18 spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java
  24. +17 −0 spring-test/src/test/java/org/springframework/test/context/junit4/RepeatedSpringRunnerTests.java
  25. +19 −4 spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java
  26. +201 −0 .../test/java/org/springframework/test/context/support/DirtiesContextTestExecutionListenerTests.java
  27. +73 −0 spring-test/src/test/java/org/springframework/test/context/web/MetaAnnotationConfigWacTests.java
  28. +46 −0 spring-test/src/test/java/org/springframework/test/context/web/WebTests.java
@@ -285,9 +285,8 @@ private static boolean isInterfaceWithAnnotatedMethods(Class<?> iface) {
* <p>The standard {@link Class} API does not provide a mechanism for determining which class
* in an inheritance hierarchy actually declares an {@link Annotation}, so we need to handle
* this explicitly.
* @param annotationType the Class object corresponding to the annotation type
* @param clazz the Class object corresponding to the class on which to check for the annotation,
* or {@code null}
* @param annotationType the annotation class to look for, both locally and as a meta-annotation
* @param clazz the class on which to check for the annotation, or {@code null}
* @return the first {@link Class} in the inheritance hierarchy of the specified {@code clazz}
* which declares an annotation for the specified {@code annotationType}, or {@code null}
* if not found
@@ -301,8 +300,24 @@ private static boolean isInterfaceWithAnnotatedMethods(Class<?> iface) {
if (clazz == null || clazz.equals(Object.class)) {
return null;
}
return (isAnnotationDeclaredLocally(annotationType, clazz)) ? clazz : findAnnotationDeclaringClass(
annotationType, clazz.getSuperclass());
// Declared locally?
if (isAnnotationDeclaredLocally(annotationType, clazz)) {
return clazz;
}
// Declared on a stereotype annotation (i.e., as a meta-annotation)?
if (!Annotation.class.isAssignableFrom(clazz)) {
for (Annotation stereotype : clazz.getAnnotations()) {
Class<?> declaringClass = findAnnotationDeclaringClass(annotationType, stereotype.annotationType());
if (declaringClass != null) {
return declaringClass;
}
}
}
// Declared on a superclass?
return findAnnotationDeclaringClass(annotationType, clazz.getSuperclass());
}
/**
@@ -96,6 +96,18 @@ public void testFindMethodAnnotationOnBridgeMethod() throws Exception {
// assertNotNull(o);
// }
@Test
public void findAnnotationPrefersInteracesOverLocalMetaAnnotations() {
Component component = AnnotationUtils.findAnnotation(
ClassWithLocalMetaAnnotationAndMetaAnnotatedInterface.class, Component.class);
// By inspecting ClassWithLocalMetaAnnotationAndMetaAnnotatedInterface, one
// might expect that "meta2" should be found; however, with the current
// implementation "meta1" will be found.
assertNotNull(component);
assertEquals("meta1", component.value());
}
@Test
public void testFindAnnotationDeclaringClass() throws Exception {
// no class-level annotation
@@ -133,7 +145,7 @@ public void findAnnotationDeclaringClassForTypesWithSingleCandidateType() {
assertEquals(InheritedAnnotationInterface.class,
findAnnotationDeclaringClassForTypes(transactionalCandidateList, InheritedAnnotationInterface.class));
assertNull(findAnnotationDeclaringClassForTypes(transactionalCandidateList,
SubInheritedAnnotationInterface.class));
SubInheritedAnnotationInterface.class));
assertEquals(InheritedAnnotationClass.class,
findAnnotationDeclaringClassForTypes(transactionalCandidateList, InheritedAnnotationClass.class));
assertEquals(InheritedAnnotationClass.class,
@@ -288,19 +300,23 @@ public void testGetRepeatableFromMethod() throws Exception {
@Component(value = "meta1")
@Order
@Retention(RetentionPolicy.RUNTIME)
@interface Meta1 {
}
@Component(value = "meta2")
@Transactional
@Retention(RetentionPolicy.RUNTIME)
@interface Meta2 {
}
@Meta1
@Component(value = "local")
static interface InterfaceWithMetaAnnotation {
}
@Meta2
static class HasLocalAndMetaComponentAnnotation {
static class ClassWithLocalMetaAnnotationAndMetaAnnotatedInterface implements InterfaceWithMetaAnnotation {
}
public static interface AnnotatedInterface {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* 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.
@@ -25,6 +25,8 @@
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import static org.springframework.core.annotation.AnnotationUtils.*;
/**
* General utility methods for working with <em>profile values</em>.
*
@@ -63,7 +65,7 @@ public static ProfileValueSource retrieveProfileValueSource(Class<?> testClass)
Assert.notNull(testClass, "testClass must not be null");
Class<ProfileValueSourceConfiguration> annotationType = ProfileValueSourceConfiguration.class;
ProfileValueSourceConfiguration config = testClass.getAnnotation(annotationType);
ProfileValueSourceConfiguration config = findAnnotation(testClass, annotationType);
if (logger.isDebugEnabled()) {
logger.debug("Retrieved @ProfileValueSourceConfiguration [" + config + "] for test class ["
+ testClass.getName() + "]");
@@ -114,7 +116,7 @@ public static ProfileValueSource retrieveProfileValueSource(Class<?> testClass)
* environment
*/
public static boolean isTestEnabledInThisEnvironment(Class<?> testClass) {
IfProfileValue ifProfileValue = testClass.getAnnotation(IfProfileValue.class);
IfProfileValue ifProfileValue = findAnnotation(testClass, IfProfileValue.class);
return isTestEnabledInThisEnvironment(retrieveProfileValueSource(testClass), ifProfileValue);
}
@@ -157,11 +159,11 @@ public static boolean isTestEnabledInThisEnvironment(Method testMethod, Class<?>
public static boolean isTestEnabledInThisEnvironment(ProfileValueSource profileValueSource, Method testMethod,
Class<?> testClass) {
IfProfileValue ifProfileValue = testClass.getAnnotation(IfProfileValue.class);
IfProfileValue ifProfileValue = findAnnotation(testClass, IfProfileValue.class);
boolean classLevelEnabled = isTestEnabledInThisEnvironment(profileValueSource, ifProfileValue);
if (classLevelEnabled) {
ifProfileValue = testMethod.getAnnotation(IfProfileValue.class);
ifProfileValue = findAnnotation(testMethod, IfProfileValue.class);
return isTestEnabledInThisEnvironment(profileValueSource, ifProfileValue);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* 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.
@@ -17,25 +17,27 @@
package org.springframework.test.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
/**
* Test annotation to indicate that a test method should be invoked repeatedly.
* <p />
* Note that the scope of execution to be repeated includes execution of the
*
* <p>Note that the scope of execution to be repeated includes execution of the
* test method itself as well as any <em>set up</em> or <em>tear down</em> of
* the test fixture.
*
* @author Rod Johnson
* @author Sam Brannen
* @since 2.0
* @see Timed
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Retention(RUNTIME)
@Target({ METHOD, ANNOTATION_TYPE })
public @interface Repeat {
int value() default 1;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* 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.
@@ -17,34 +17,31 @@
package org.springframework.test.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
/**
* <p>
* Test-specific annotation to indicate that a test method has to finish
* execution in a {@link #millis() specified time period}.
* </p>
* <p>
* If the text execution takes longer than the specified time period, then the
* test is to be considered failed.
* </p>
* <p>
* Note that the time period includes execution of the test method itself, any
* {@link Repeat repetitions} of the test, and any <em>set up</em> or
*
* <p>If the text execution takes longer than the specified time period, then
* the test is to be considered failed.
*
* <p>Note that the time period includes execution of the test method itself,
* any {@link Repeat repetitions} of the test, and any <em>set up</em> or
* <em>tear down</em> of the test fixture.
* </p>
*
* @author Rod Johnson
* @author Sam Brannen
* @since 2.0
* @see Repeat
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Retention(RUNTIME)
@Target({ METHOD, ANNOTATION_TYPE })
public @interface Timed {
/**
Oops, something went wrong.

0 comments on commit 2bd5a53

Please sign in to comment.