From c61673cfe885d2764f1a25757fa2a07f0b121fcf Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 20 Nov 2013 19:44:55 +0100 Subject: [PATCH 01/12] Support meta-annotation attribute overrides in TCF Issue: SPR-11038 --- .../test/context/MetaAnnotationUtils.java | 9 + ...erriddenMetaAnnotationAttributesTests.java | 155 ++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 spring-test/src/test/java/org/springframework/test/context/OverriddenMetaAnnotationAttributesTests.java diff --git a/spring-test/src/main/java/org/springframework/test/context/MetaAnnotationUtils.java b/spring-test/src/main/java/org/springframework/test/context/MetaAnnotationUtils.java index de842020d0af..b520a8323e68 100644 --- a/spring-test/src/main/java/org/springframework/test/context/MetaAnnotationUtils.java +++ b/spring-test/src/main/java/org/springframework/test/context/MetaAnnotationUtils.java @@ -18,6 +18,8 @@ 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.util.Assert; import org.springframework.util.ObjectUtils; @@ -173,6 +175,7 @@ public static class AnnotationDescriptor { private final Class declaringClass; private final Annotation stereotype; private final T annotation; + private final AnnotationAttributes annotationAttributes; public AnnotationDescriptor(Class declaringClass, T annotation) { @@ -186,6 +189,8 @@ public AnnotationDescriptor(Class declaringClass, Annotation stereotype, T an this.declaringClass = declaringClass; this.stereotype = stereotype; this.annotation = annotation; + this.annotationAttributes = AnnotatedElementUtils.getAnnotationAttributes(declaringClass, + annotation.annotationType().getName()); } public Class getDeclaringClass() { @@ -200,6 +205,10 @@ public Class getAnnotationType() { return this.annotation.annotationType(); } + public AnnotationAttributes getAnnotationAttributes() { + return this.annotationAttributes; + } + public Annotation getStereotype() { return this.stereotype; } diff --git a/spring-test/src/test/java/org/springframework/test/context/OverriddenMetaAnnotationAttributesTests.java b/spring-test/src/test/java/org/springframework/test/context/OverriddenMetaAnnotationAttributesTests.java new file mode 100644 index 000000000000..4e7867c4744a --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/OverriddenMetaAnnotationAttributesTests.java @@ -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. + * + *

See SPR-10181. + * + * @author Sam Brannen + * @since 4.0 + */ +public class OverriddenMetaAnnotationAttributesTests { + + @Test + public void contextConfigurationValue() throws Exception { + Class declaringClass = MetaValueConfigTest.class; + AnnotationDescriptor descriptor = findAnnotationDescriptor(declaringClass, + ContextConfiguration.class); + assertNotNull(descriptor); + assertEquals(declaringClass, descriptor.getDeclaringClass()); + 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 descriptor = findAnnotationDescriptor(declaringClass, + ContextConfiguration.class); + assertNotNull(descriptor); + assertEquals(declaringClass, descriptor.getDeclaringClass()); + 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 declaringClass = MetaLocationsConfigTest.class; + AnnotationDescriptor descriptor = findAnnotationDescriptor(declaringClass, + ContextConfiguration.class); + assertNotNull(descriptor); + assertEquals(declaringClass, descriptor.getDeclaringClass()); + 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 descriptor = findAnnotationDescriptor(declaringClass, + ContextConfiguration.class); + assertNotNull(descriptor); + assertEquals(declaringClass, descriptor.getDeclaringClass()); + 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 { + } + +} From 963ef4fa7bb9be5ebd8478a87cde0ec31f913fb4 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Sat, 23 Nov 2013 18:24:17 +0100 Subject: [PATCH 02/12] @TransactionConfiguration: meta-annotation attribute overrides --- .../TransactionalTestExecutionListener.java | 20 ++- ...ansactionalTestExecutionListenerTests.java | 128 +++++++++++++++++- 2 files changed, 138 insertions(+), 10 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java index 428d178f8aec..9a0c9c8959cf 100644 --- a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java +++ b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java @@ -33,6 +33,8 @@ import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; 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.context.TestContext; import org.springframework.test.context.support.AbstractTestExecutionListener; @@ -524,21 +526,25 @@ private boolean isShadowed(Method current, Method previous) { * {@code @TransactionConfiguration} will be used instead. * @param testContext the test context for which the configuration * 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) { Class clazz = testContext.getTestClass(); - TransactionConfiguration config = findAnnotation(clazz, TransactionConfiguration.class); + + AnnotationAttributes ann = AnnotatedElementUtils.getAnnotationAttributes(clazz, + TransactionConfiguration.class.getName()); if (logger.isDebugEnabled()) { - logger.debug("Retrieved @TransactionConfiguration [" + config + "] for test class [" + clazz + "]"); + logger.debug("Retrieved @TransactionConfiguration attributes [" + ann + "] for test class [" + clazz + + "]"); } String transactionManagerName; boolean defaultRollback; - if (config != null) { - transactionManagerName = config.transactionManager(); - defaultRollback = config.defaultRollback(); + if (ann != null) { + transactionManagerName = ann.getString("transactionManager"); + defaultRollback = ann.getBoolean("defaultRollback"); } else { transactionManagerName = DEFAULT_TRANSACTION_MANAGER_NAME; diff --git a/spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java b/spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java index 83568850cb58..ec40c9a877ee 100644 --- a/spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java @@ -24,11 +24,13 @@ import org.springframework.test.context.TestContext; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.SimpleTransactionStatus; import static org.junit.Assert.*; import static org.mockito.Mockito.*; +import static org.springframework.transaction.annotation.Propagation.*; /** * Unit tests for {@link TransactionalTestExecutionListener}. @@ -56,6 +58,11 @@ private void assertBeforeTestMethod(Class clazz) throws Exc } private void assertBeforeTestMethodWithTransactionalTestMethod(Class clazz) throws Exception { + assertBeforeTestMethodWithTransactionalTestMethod(clazz, true); + } + + private void assertBeforeTestMethodWithTransactionalTestMethod(Class clazz, boolean invokedInTx) + throws Exception { Mockito.> when(testContext.getTestClass()).thenReturn(clazz); Invocable instance = clazz.newInstance(); when(testContext.getTestInstance()).thenReturn(instance); @@ -63,7 +70,7 @@ private void assertBeforeTestMethodWithTransactionalTestMethod(Class clazz) @@ -109,6 +116,16 @@ private void assertAfterTestMethodWithNonTransactionalTestMethod(Class clazz, String transactionManagerName, + boolean defaultRollback) { + Mockito.> when(testContext.getTestClass()).thenReturn(clazz); + + TransactionConfigurationAttributes attributes = listener.retrieveConfigurationAttributes(testContext); + assertNotNull(attributes); + assertEquals(transactionManagerName, attributes.getTransactionManagerName()); + assertEquals(defaultRollback, attributes.isDefaultRollback()); + } + @Test public void beforeTestMethodWithTransactionalDeclaredOnClassLocally() throws Exception { assertBeforeTestMethodWithTransactionalTestMethod(TransactionalDeclaredOnClassLocallyTestCase.class); @@ -119,6 +136,23 @@ public void beforeTestMethodWithTransactionalDeclaredOnClassViaMetaAnnotation() 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 public void beforeTestMethodWithTransactionalDeclaredOnMethodLocally() throws Exception { assertBeforeTestMethod(TransactionalDeclaredOnMethodLocallyTestCase.class); @@ -149,6 +183,35 @@ public void afterTestMethodWithAfterTransactionDeclaredViaMetaAnnotation() throw 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); + } + // ------------------------------------------------------------------------- @@ -157,6 +220,13 @@ public void afterTestMethodWithAfterTransactionDeclaredViaMetaAnnotation() throw private static @interface MetaTransactional { } + @Transactional + @Retention(RetentionPolicy.RUNTIME) + private static @interface MetaTxWithOverride { + + Propagation propagation() default REQUIRED; + } + @BeforeTransaction @Retention(RetentionPolicy.RUNTIME) private static @interface MetaBeforeTransaction { @@ -167,6 +237,13 @@ public void afterTestMethodWithAfterTransactionDeclaredViaMetaAnnotation() throw private static @interface MetaAfterTransaction { } + @TransactionConfiguration + @Retention(RetentionPolicy.RUNTIME) + private static @interface MetaTxConfig { + + String transactionManager() default "metaTxMgr"; + } + private static abstract class Invocable { boolean invoked = false; @@ -213,20 +290,46 @@ public void beforeTransaction() { public void transactionalTest() { /* no-op */ } + } + + static class TransactionalDeclaredOnMethodViaMetaAnnotationTestCase extends Invocable { + + @BeforeTransaction + public void beforeTransaction() { + invoked = true; + } + + @MetaTransactional + public void transactionalTest() { + /* no-op */ + } public void nonTransactionalTest() { /* no-op */ } } - static class TransactionalDeclaredOnMethodViaMetaAnnotationTestCase extends Invocable { + @MetaTxWithOverride(propagation = NOT_SUPPORTED) + static class TransactionalDeclaredOnClassViaMetaAnnotationWithOverrideTestCase extends Invocable { @BeforeTransaction public void beforeTransaction() { invoked = true; } - @MetaTransactional + 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 */ } @@ -304,4 +407,23 @@ public void nonTransactionalTest() { } } + 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 { + } + } From f6bf87e99fc7a0ef753cb80553a4fb59ed7d4a33 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Mon, 25 Nov 2013 20:14:37 -0500 Subject: [PATCH 03/12] Added unit tests for @Rollback resolution. --- .../TransactionalTestExecutionListener.java | 11 ++-- ...ansactionalTestExecutionListenerTests.java | 59 +++++++++++++++++++ 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java index 9a0c9c8959cf..283d2c96b914 100644 --- a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java +++ b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java @@ -416,15 +416,18 @@ protected final boolean isRollback(TestContext testContext) throws Exception { if (rollbackAnnotation != null) { boolean rollbackOverride = rollbackAnnotation.value(); if (logger.isDebugEnabled()) { - logger.debug("Method-level @Rollback(" + rollbackOverride + ") overrides default rollback [" + rollback - + "] for test context " + testContext); + logger.debug(String.format( + "Method-level @Rollback(%s) overrides default rollback [%s] for test context %s.", + rollbackOverride, + rollback, testContext)); } rollback = rollbackOverride; } else { if (logger.isDebugEnabled()) { - logger.debug("No method-level @Rollback override: using default rollback [" + rollback - + "] for test context " + testContext); + logger.debug(String.format( + "No method-level @Rollback override: using default rollback [%s] for test context %s.", rollback, + testContext)); } } return rollback; diff --git a/spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java b/spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java index ec40c9a877ee..fd6fd9c66c6d 100644 --- a/spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java @@ -21,6 +21,7 @@ import org.junit.Test; import org.mockito.Mockito; +import org.springframework.test.annotation.Rollback; import org.springframework.test.context.TestContext; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; @@ -126,6 +127,12 @@ private void assertTransactionConfigurationAttributes(Class clazz, String tra assertEquals(defaultRollback, attributes.isDefaultRollback()); } + private void assertIsRollback(Class clazz, boolean rollback) throws NoSuchMethodException, Exception { + Mockito.> when(testContext.getTestClass()).thenReturn(clazz); + when(testContext.getTestMethod()).thenReturn(clazz.getDeclaredMethod("test")); + assertEquals(rollback, listener.isRollback(testContext)); + } + @Test public void beforeTestMethodWithTransactionalDeclaredOnClassLocally() throws Exception { assertBeforeTestMethodWithTransactionalTestMethod(TransactionalDeclaredOnClassLocallyTestCase.class); @@ -212,6 +219,26 @@ public void retrieveConfigurationAttributesViaMetaAnnotationWithOverride() throw "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); + } + // ------------------------------------------------------------------------- @@ -244,6 +271,11 @@ public void retrieveConfigurationAttributesViaMetaAnnotationWithOverride() throw String transactionManager() default "metaTxMgr"; } + @Rollback(false) + @Retention(RetentionPolicy.RUNTIME) + private static @interface Commit { + } + private static abstract class Invocable { boolean invoked = false; @@ -426,4 +458,31 @@ static class TransactionConfigurationViaMetaAnnotationTestCase { 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() { + } + } + } From c9e95163eb039ae1f0172e9cf1a4945ffa5a4d2c Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Mon, 25 Nov 2013 21:07:24 -0500 Subject: [PATCH 04/12] @Timed: meta-annotation attribute overrides --- .../junit4/SpringJUnit4ClassRunner.java | 7 ++- .../junit4/SpringJUnit4ClassRunnerTests.java | 58 +++++++++++++++++-- .../junit4/TimedSpringRunnerTests.java | 28 ++++++--- 3 files changed, 79 insertions(+), 14 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java b/spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java index 79c577b764f5..99b2368b79d5 100644 --- a/spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java +++ b/spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java @@ -34,6 +34,8 @@ import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.InitializationError; 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.test.annotation.ProfileValueUtils; import org.springframework.test.annotation.Repeat; @@ -411,8 +413,9 @@ protected long getJUnitTimeout(FrameworkMethod frameworkMethod) { * @return the timeout, or {@code 0} if none was specified. */ protected long getSpringTimeout(FrameworkMethod frameworkMethod) { - Timed timedAnnotation = AnnotationUtils.getAnnotation(frameworkMethod.getMethod(), Timed.class); - return (timedAnnotation != null && timedAnnotation.millis() > 0 ? timedAnnotation.millis() : 0); + AnnotationAttributes ann = AnnotatedElementUtils.getAnnotationAttributes(frameworkMethod.getMethod(), + Timed.class.getName()); + return (ann != null && ann.getNumber("millis").intValue() > 0 ? ann.getNumber("millis").intValue() : 0); } /** diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunnerTests.java index aedd5ad46668..3d73e6b11d24 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunnerTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunnerTests.java @@ -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"); * you may not use this file except in compliance with the License. @@ -16,13 +16,21 @@ 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 static org.junit.Assert.*; + /** - * @author Rick Evans + * Unit tests for {@link SpringJUnit4ClassRunner}. + * * @author Sam Brannen + * @author Rick Evans * @since 2.5 */ public class SpringJUnit4ClassRunnerTests { @@ -37,7 +45,8 @@ protected TestContextManager createTestContextManager(Class clazz) { @Override 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 void prepareTestInstance(Object testInstance) { 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; + } + } diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java index fe3f25b2ab8b..3a7b84f6a1ca 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java @@ -16,8 +16,6 @@ package org.springframework.test.context.junit4; -import static org.junit.Assert.*; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -28,8 +26,8 @@ import org.junit.runners.JUnit4; import org.springframework.test.annotation.Timed; import org.springframework.test.context.TestExecutionListeners; -import org.springframework.tests.Assume; -import org.springframework.tests.TestGroup; + +import static org.junit.Assert.*; /** * Verifies proper handling of the following in conjunction with the @@ -47,18 +45,18 @@ public class TimedSpringRunnerTests { @Test public void timedTests() throws Exception { - Assume.group(TestGroup.PERFORMANCE); + // Assume.group(TestGroup.PERFORMANCE); Class testClass = TimedSpringRunnerTestCase.class; TrackingRunListener listener = new TrackingRunListener(); RunNotifier notifier = new RunNotifier(); notifier.addListener(listener); 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()); - assertEquals("Verifying number of failures for test class [" + testClass + "].", 4, + assertEquals("Verifying number of failures for test class [" + testClass + "].", 5, 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()); } @@ -101,6 +99,13 @@ public void springTimeoutWithSleepAndMetaAnnotation() throws Exception { 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. @Test(timeout = 200) @Timed(millis = 200) @@ -114,4 +119,11 @@ public void springAndJUnitTimeouts() { private static @interface MetaTimed { } + @Timed(millis = 1000) + @Retention(RetentionPolicy.RUNTIME) + private static @interface MetaTimedWithOverride { + + long millis() default 1000; + } + } From 729690da25cd7b843ec568f822d6abab36467625 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Mon, 25 Nov 2013 23:32:26 -0500 Subject: [PATCH 05/12] @TestExecutionListeners: meta-annotation attribute overrides --- .../test/context/TestContextManager.java | 18 +++-- .../junit4/SpringJUnit4ClassRunner.java | 10 ++- .../TransactionalTestExecutionListener.java | 19 +++-- .../context/TestExecutionListenersTests.java | 75 ++++++++++++++++++- 4 files changed, 102 insertions(+), 20 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/context/TestContextManager.java b/spring-test/src/main/java/org/springframework/test/context/TestContextManager.java index 22c6919bb356..6b1c4b3f10e4 100644 --- a/spring-test/src/main/java/org/springframework/test/context/TestContextManager.java +++ b/spring-test/src/main/java/org/springframework/test/context/TestContextManager.java @@ -28,6 +28,8 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeanUtils; import org.springframework.context.ApplicationContext; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.test.context.MetaAnnotationUtils.AnnotationDescriptor; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -172,6 +174,7 @@ private List getReversedTestExecutionListeners() { * @param clazz the test class for which the listeners should be retrieved * @return an array of TestExecutionListeners for the specified class */ + @SuppressWarnings("unchecked") private TestExecutionListener[] retrieveTestExecutionListeners(Class clazz) { Assert.notNull(clazz, "Class must not be null"); Class annotationType = TestExecutionListeners.class; @@ -196,14 +199,17 @@ private TestExecutionListener[] retrieveTestExecutionListeners(Class clazz) { Class declaringClass = (descriptor.getStereotype() != null) ? descriptor.getStereotypeType() : rootDeclaringClass; - TestExecutionListeners testExecutionListeners = declaringClass.getAnnotation(annotationType); + AnnotationAttributes annAttrs = AnnotatedElementUtils.getAnnotationAttributes(rootDeclaringClass, + TestExecutionListeners.class.getName()); + if (logger.isTraceEnabled()) { - logger.trace("Retrieved @TestExecutionListeners [" + testExecutionListeners - + "] for declaring class [" + declaringClass + "]."); + logger.trace(String.format( + "Retrieved @TestExecutionListeners attributes [%s] for declaring class [%s].", annAttrs, + declaringClass)); } - Class[] valueListenerClasses = testExecutionListeners.value(); - Class[] listenerClasses = testExecutionListeners.listeners(); + Class[] valueListenerClasses = (Class[]) annAttrs.getClassArray("value"); + Class[] listenerClasses = (Class[]) annAttrs.getClassArray("listeners"); if (!ObjectUtils.isEmpty(valueListenerClasses) && !ObjectUtils.isEmpty(listenerClasses)) { String msg = String.format( "Test class [%s] has been configured with @TestExecutionListeners' 'value' [%s] " @@ -221,7 +227,7 @@ else if (!ObjectUtils.isEmpty(valueListenerClasses)) { classesList.addAll(0, Arrays.> asList(listenerClasses)); } - descriptor = (testExecutionListeners.inheritListeners() ? findAnnotationDescriptor( + descriptor = (annAttrs.getBoolean("inheritListeners") ? findAnnotationDescriptor( rootDeclaringClass.getSuperclass(), annotationType) : null); } } diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java b/spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java index 99b2368b79d5..aa0fd1d0bc7c 100644 --- a/spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java +++ b/spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java @@ -413,9 +413,15 @@ protected long getJUnitTimeout(FrameworkMethod frameworkMethod) { * @return the timeout, or {@code 0} if none was specified. */ protected long getSpringTimeout(FrameworkMethod frameworkMethod) { - AnnotationAttributes ann = AnnotatedElementUtils.getAnnotationAttributes(frameworkMethod.getMethod(), + AnnotationAttributes annAttrs = AnnotatedElementUtils.getAnnotationAttributes(frameworkMethod.getMethod(), Timed.class.getName()); - return (ann != null && ann.getNumber("millis").intValue() > 0 ? ann.getNumber("millis").intValue() : 0); + if (annAttrs == null) { + return 0; + } + else { + long millis = annAttrs. getNumber("millis").longValue(); + return millis > 0 ? millis : 0; + } } /** diff --git a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java index 283d2c96b914..7223e3ff8f9f 100644 --- a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java +++ b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java @@ -418,8 +418,7 @@ protected final boolean isRollback(TestContext testContext) throws Exception { if (logger.isDebugEnabled()) { logger.debug(String.format( "Method-level @Rollback(%s) overrides default rollback [%s] for test context %s.", - rollbackOverride, - rollback, testContext)); + rollbackOverride, rollback, testContext)); } rollback = rollbackOverride; } @@ -536,18 +535,18 @@ TransactionConfigurationAttributes retrieveConfigurationAttributes(TestContext t if (this.configurationAttributes == null) { Class clazz = testContext.getTestClass(); - AnnotationAttributes ann = AnnotatedElementUtils.getAnnotationAttributes(clazz, + AnnotationAttributes annAttrs = AnnotatedElementUtils.getAnnotationAttributes(clazz, TransactionConfiguration.class.getName()); if (logger.isDebugEnabled()) { - logger.debug("Retrieved @TransactionConfiguration attributes [" + ann + "] for test class [" + clazz - + "]"); + logger.debug(String.format("Retrieved @TransactionConfiguration attributes [%s] for test class [%s].", + annAttrs, clazz)); } String transactionManagerName; boolean defaultRollback; - if (ann != null) { - transactionManagerName = ann.getString("transactionManager"); - defaultRollback = ann.getBoolean("defaultRollback"); + if (annAttrs != null) { + transactionManagerName = annAttrs.getString("transactionManager"); + defaultRollback = annAttrs.getBoolean("defaultRollback"); } else { transactionManagerName = DEFAULT_TRANSACTION_MANAGER_NAME; @@ -557,8 +556,8 @@ TransactionConfigurationAttributes retrieveConfigurationAttributes(TestContext t TransactionConfigurationAttributes configAttributes = new TransactionConfigurationAttributes( transactionManagerName, defaultRollback); if (logger.isDebugEnabled()) { - logger.debug("Retrieved TransactionConfigurationAttributes " + configAttributes + " for class [" - + clazz + "]"); + logger.debug(String.format("Retrieved TransactionConfigurationAttributes %s for class [%s].", + configAttributes, clazz)); } this.configurationAttributes = configAttributes; } diff --git a/spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java b/spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java index 6fd55d2b8e67..32483a078cda 100644 --- a/spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java @@ -16,14 +16,14 @@ package org.springframework.test.context; -import static org.junit.Assert.assertEquals; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import org.junit.Test; import org.springframework.test.context.support.AbstractTestExecutionListener; +import static org.junit.Assert.*; + /** *

* JUnit 4 based unit test for the {@link TestExecutionListeners @@ -114,6 +114,29 @@ public void verifyNumInheritedListenersRegisteredViaMetaAnnotation() throws Exce 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) public void verifyDuplicateListenersConfigThrowsException() throws Exception { new TestContextManager(DuplicateListenersConfigExampleTestCase.class); @@ -174,6 +197,32 @@ static class DuplicateListenersConfigExampleTestCase { static @interface MetaNonInheritedListeners { } + @TestExecutionListeners + @Retention(RetentionPolicy.RUNTIME) + static @interface MetaListenersWithOverrides { + + Class[] listeners() default { FooTestExecutionListener.class, + BarTestExecutionListener.class }; + } + + @TestExecutionListeners + @Retention(RetentionPolicy.RUNTIME) + static @interface MetaInheritedListenersWithOverrides { + + Class[] listeners() default QuuxTestExecutionListener.class; + + boolean inheritListeners() default true; + } + + @TestExecutionListeners + @Retention(RetentionPolicy.RUNTIME) + static @interface MetaNonInheritedListenersWithOverrides { + + Class[] listeners() default QuuxTestExecutionListener.class; + + boolean inheritListeners() default false; + } + @MetaListeners static class MetaExampleTestCase { } @@ -186,6 +235,28 @@ static class MetaInheritedListenersExampleTestCase extends MetaExampleTestCase { 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 { } From 655469854f9ed9f21c94ee34aa25b12bbe765e89 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Tue, 26 Nov 2013 00:22:40 -0500 Subject: [PATCH 06/12] @DirtiesContext: meta-annotation attribute overrides --- .../DirtiesContextTestExecutionListener.java | 43 ++++++------ ...tiesContextTestExecutionListenerTests.java | 70 ++++++++++++++++--- 2 files changed, 82 insertions(+), 31 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/context/support/DirtiesContextTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/support/DirtiesContextTestExecutionListener.java index 50bb5aa323c6..22b57df58943 100644 --- a/spring-test/src/main/java/org/springframework/test/context/support/DirtiesContextTestExecutionListener.java +++ b/spring-test/src/main/java/org/springframework/test/context/support/DirtiesContextTestExecutionListener.java @@ -21,13 +21,15 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; 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.ClassMode; import org.springframework.test.annotation.DirtiesContext.HierarchyMode; import org.springframework.test.context.TestContext; 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 @@ -81,24 +83,22 @@ public void afterTestMethod(TestContext testContext) throws Exception { Method testMethod = testContext.getTestMethod(); Assert.notNull(testMethod, "The test method of the supplied TestContext must not be null"); - final Class annotationType = DirtiesContext.class; - - DirtiesContext methodDirtiesContextAnnotation = findAnnotation(testMethod, annotationType); - boolean methodDirtiesContext = methodDirtiesContextAnnotation != null; - - DirtiesContext classDirtiesContextAnnotation = findAnnotation(testClass, annotationType); - boolean classDirtiesContext = classDirtiesContextAnnotation != null; - ClassMode classMode = classDirtiesContext ? classDirtiesContextAnnotation.classMode() : null; + final String annotationType = DirtiesContext.class.getName(); + AnnotationAttributes methodAnnAttrs = AnnotatedElementUtils.getAnnotationAttributes(testMethod, annotationType); + AnnotationAttributes classAnnAttrs = AnnotatedElementUtils.getAnnotationAttributes(testClass, annotationType); + boolean methodDirtiesContext = methodAnnAttrs != null; + boolean classDirtiesContext = classAnnAttrs != null; + ClassMode classMode = classDirtiesContext ? classAnnAttrs. getEnum("classMode") : null; if (logger.isDebugEnabled()) { - logger.debug("After test method: context [" + testContext + "], class dirties context [" - + classDirtiesContext + "], class mode [" + classMode + "], method dirties context [" - + methodDirtiesContext + "]."); + logger.debug(String.format( + "After test method: context %s, class dirties context [%s], class mode [%s], method dirties context [%s].", + testContext, classDirtiesContext, classMode, methodDirtiesContext)); } - if (methodDirtiesContext || (classMode == ClassMode.AFTER_EACH_TEST_METHOD)) { - HierarchyMode hierarchyMode = methodDirtiesContext ? methodDirtiesContextAnnotation.hierarchyMode() - : classDirtiesContextAnnotation.hierarchyMode(); + if (methodDirtiesContext || (classMode == AFTER_EACH_TEST_METHOD)) { + HierarchyMode hierarchyMode = methodDirtiesContext ? methodAnnAttrs. getEnum("hierarchyMode") + : classAnnAttrs. getEnum("hierarchyMode"); dirtyContext(testContext, hierarchyMode); } } @@ -107,7 +107,7 @@ public void afterTestMethod(TestContext testContext) throws Exception { * If the test class of the supplied {@linkplain TestContext test context} is * annotated with {@link DirtiesContext @DirtiesContext}, the * {@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 * {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE * REINJECT_DEPENDENCIES_ATTRIBUTE} in the test context will be set to @@ -118,15 +118,16 @@ public void afterTestClass(TestContext testContext) throws Exception { Class testClass = testContext.getTestClass(); Assert.notNull(testClass, "The test class of the supplied TestContext must not be null"); - final Class 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()) { - logger.debug("After test class: context [" + testContext + "], dirtiesContext [" + dirtiesContext + "]."); + logger.debug(String.format("After test class: context %s, dirtiesContext [%s].", testContext, + dirtiesContext)); } if (dirtiesContext) { - HierarchyMode hierarchyMode = dirtiesContextAnnotation.hierarchyMode(); + HierarchyMode hierarchyMode = annAttrs. getEnum("hierarchyMode"); dirtyContext(testContext, hierarchyMode); } } diff --git a/spring-test/src/test/java/org/springframework/test/context/support/DirtiesContextTestExecutionListenerTests.java b/spring-test/src/test/java/org/springframework/test/context/support/DirtiesContextTestExecutionListenerTests.java index 10d0f2cec530..b94049842db9 100644 --- a/spring-test/src/test/java/org/springframework/test/context/support/DirtiesContextTestExecutionListenerTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/support/DirtiesContextTestExecutionListenerTests.java @@ -26,8 +26,8 @@ import org.springframework.test.annotation.DirtiesContext.HierarchyMode; import org.springframework.test.context.TestContext; -import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; +import static org.springframework.test.annotation.DirtiesContext.ClassMode.*; import static org.springframework.test.annotation.DirtiesContext.HierarchyMode.*; /** @@ -84,7 +84,7 @@ public void afterTestMethodForDirtiesContextDeclaredLocallyOnClassAfterClass() t Mockito.> when(testContext.getTestClass()).thenReturn(clazz); when(testContext.getTestMethod()).thenReturn(clazz.getDeclaredMethod("clean")); listener.afterTestMethod(testContext); - verify(testContext, times(0)).markApplicationContextDirty(any(HierarchyMode.class)); + verify(testContext, times(0)).markApplicationContextDirty(EXHAUSTIVE); } @Test @@ -93,7 +93,16 @@ public void afterTestMethodForDirtiesContextDeclaredViaMetaAnnotationOnClassAfte Mockito.> when(testContext.getTestClass()).thenReturn(clazz); when(testContext.getTestMethod()).thenReturn(clazz.getDeclaredMethod("clean")); 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.> 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 void afterTestClassForDirtiesContextDeclaredLocallyOnMethod() throws Exce Class clazz = getClass(); Mockito.> when(testContext.getTestClass()).thenReturn(clazz); listener.afterTestClass(testContext); - verify(testContext, times(0)).markApplicationContextDirty(any(HierarchyMode.class)); + verify(testContext, times(0)).markApplicationContextDirty(EXHAUSTIVE); } @Test @@ -127,7 +136,7 @@ public void afterTestClassForDirtiesContextDeclaredLocallyOnClassAfterClass() th Class clazz = DirtiesContextDeclaredLocallyAfterClass.class; Mockito.> when(testContext.getTestClass()).thenReturn(clazz); listener.afterTestClass(testContext); - verify(testContext, times(1)).markApplicationContextDirty(any(HierarchyMode.class)); + verify(testContext, times(1)).markApplicationContextDirty(EXHAUSTIVE); } @Test @@ -135,7 +144,23 @@ public void afterTestClassForDirtiesContextDeclaredViaMetaAnnotationOnClassAfter Class clazz = DirtiesContextDeclaredViaMetaAnnotationAfterClass.class; Mockito.> when(testContext.getTestClass()).thenReturn(clazz); 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.> 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.> when(testContext.getTestClass()).thenReturn(clazz); + listener.afterTestClass(testContext); + verify(testContext, times(1)).markApplicationContextDirty(EXHAUSTIVE); } // ------------------------------------------------------------------------- @@ -156,17 +181,17 @@ void dirtiesContextDeclaredViaMetaAnnotation() { static @interface MetaDirty { } - @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) + @DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) @Retention(RetentionPolicy.RUNTIME) static @interface MetaDirtyAfterEachTestMethod { } - @DirtiesContext(classMode = ClassMode.AFTER_CLASS) + @DirtiesContext(classMode = AFTER_CLASS) @Retention(RetentionPolicy.RUNTIME) static @interface MetaDirtyAfterClass { } - @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) + @DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) static class DirtiesContextDeclaredLocallyAfterEachTestMethod { void clean() { @@ -174,6 +199,15 @@ void clean() { } } + @DirtiesContext + @Retention(RetentionPolicy.RUNTIME) + static @interface MetaDirtyWithOverrides { + + ClassMode classMode() default AFTER_EACH_TEST_METHOD; + + HierarchyMode hierarchyMode() default HierarchyMode.CURRENT_LEVEL; + } + @MetaDirtyAfterEachTestMethod static class DirtiesContextDeclaredViaMetaAnnotationAfterEachTestMethod { @@ -182,7 +216,7 @@ void clean() { } } - @DirtiesContext(classMode = ClassMode.AFTER_CLASS) + @DirtiesContext(classMode = AFTER_CLASS) static class DirtiesContextDeclaredLocallyAfterClass { void clean() { @@ -198,4 +232,20 @@ void clean() { } } + @MetaDirtyWithOverrides + static class DirtiesContextViaMetaAnnotationWithOverrides { + + void clean() { + /* no-op */ + } + } + + @MetaDirtyWithOverrides(classMode = AFTER_CLASS, hierarchyMode = EXHAUSTIVE) + static class DirtiesContextViaMetaAnnotationWithOverridenAttributes { + + void clean() { + /* no-op */ + } + } + } From 9b9fb42dbec40ba79572d7f9e5d9bcc3a3375dcb Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Tue, 26 Nov 2013 01:01:31 -0500 Subject: [PATCH 07/12] @ActiveProfiles: meta-annotation attribute overrides --- .../test/context/ContextLoaderUtils.java | 29 +++++++++-------- .../AbstractContextLoaderUtilsTests.java | 19 +++++++++++ ...ContextLoaderUtilsActiveProfilesTests.java | 32 +++++++++++++++++++ 3 files changed, 67 insertions(+), 13 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java b/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java index 30a279949428..b39e0025094d 100644 --- a/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java +++ b/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java @@ -31,6 +31,8 @@ import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.test.context.MetaAnnotationUtils.AnnotationDescriptor; import org.springframework.test.context.MetaAnnotationUtils.UntypedAnnotationDescriptor; @@ -493,16 +495,17 @@ static String[] resolveActiveProfiles(Class testClass) { Class declaringClass = (descriptor.getStereotype() != null) ? descriptor.getStereotypeType() : rootDeclaringClass; - ActiveProfiles annotation = descriptor.getAnnotation(); + AnnotationAttributes annAttrs = AnnotatedElementUtils.getAnnotationAttributes(rootDeclaringClass, + annotationType.getName()); if (logger.isTraceEnabled()) { - logger.trace(String.format("Retrieved @ActiveProfiles [%s] for declaring class [%s].", annotation, - declaringClass.getName())); + logger.trace(String.format("Retrieved @ActiveProfiles attributes [%s] for declaring class [%s].", + annAttrs, declaringClass.getName())); } - validateActiveProfilesConfiguration(declaringClass, annotation); + validateActiveProfilesConfiguration(declaringClass, annAttrs); - String[] profiles = annotation.profiles(); - String[] valueProfiles = annotation.value(); - Class resolverClass = annotation.resolver(); + String[] profiles = annAttrs.getStringArray("profiles"); + String[] valueProfiles = annAttrs.getStringArray("value"); + Class resolverClass = annAttrs.getClass("resolver"); boolean resolverDeclared = !ActiveProfilesResolver.class.equals(resolverClass); boolean valueDeclared = !ObjectUtils.isEmpty(valueProfiles); @@ -538,17 +541,17 @@ else if (valueDeclared) { } } - descriptor = annotation.inheritProfiles() ? findAnnotationDescriptor(rootDeclaringClass.getSuperclass(), - annotationType) : null; + descriptor = annAttrs.getBoolean("inheritProfiles") ? findAnnotationDescriptor( + rootDeclaringClass.getSuperclass(), annotationType) : null; } return StringUtils.toStringArray(activeProfiles); } - private static void validateActiveProfilesConfiguration(Class declaringClass, ActiveProfiles annotation) { - String[] valueProfiles = annotation.value(); - String[] profiles = annotation.profiles(); - Class resolverClass = annotation.resolver(); + private static void validateActiveProfilesConfiguration(Class declaringClass, AnnotationAttributes annAttrs) { + String[] valueProfiles = annAttrs.getStringArray("value"); + String[] profiles = annAttrs.getStringArray("profiles"); + Class resolverClass = annAttrs.getClass("resolver"); boolean valueDeclared = !ObjectUtils.isEmpty(valueProfiles); boolean profilesDeclared = !ObjectUtils.isEmpty(profiles); boolean resolverDeclared = !ActiveProfilesResolver.class.equals(resolverClass); diff --git a/spring-test/src/test/java/org/springframework/test/context/AbstractContextLoaderUtilsTests.java b/spring-test/src/test/java/org/springframework/test/context/AbstractContextLoaderUtilsTests.java index e2a6fe305ec2..4dbd94bbbc67 100644 --- a/spring-test/src/test/java/org/springframework/test/context/AbstractContextLoaderUtilsTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/AbstractContextLoaderUtilsTests.java @@ -110,6 +110,17 @@ static class BarConfig { 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") @ActiveProfiles(profiles = "bar") @Retention(RetentionPolicy.RUNTIME) @@ -125,6 +136,14 @@ static class 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) @ActiveProfiles(profiles = "foo") static class LocationsFoo { diff --git a/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsActiveProfilesTests.java b/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsActiveProfilesTests.java index d1c49ad27833..74ad3c2e19ed 100644 --- a/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsActiveProfilesTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsActiveProfilesTests.java @@ -121,6 +121,26 @@ public void resolveActiveProfilesWithMetaAnnotation() { 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 */ @@ -256,6 +276,18 @@ private static class Animals extends LocationsBar { private static @interface MetaAnimalsConfig { } + @ActiveProfiles + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + private static @interface MetaProfilesWithOverrides { + + String[] profiles() default { "dog", "cat" }; + + Class resolver() default ActiveProfilesResolver.class; + + boolean inheritProfiles() default false; + } + @MetaAnimalsConfig private static class MetaAnimals extends MetaLocationsBar { } From 362a2ea967cc5b6a0abbb81b25f11e23069c94d8 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Tue, 26 Nov 2013 01:31:40 -0500 Subject: [PATCH 08/12] @ContextConfiguration: meta-annotation attribute overrides --- .../ContextConfigurationAttributes.java | 43 +++++++++++++++---- .../test/context/ContextLoaderUtils.java | 25 ++++++++++- ...aderUtilsConfigurationAttributesTests.java | 18 ++++++++ 3 files changed, 76 insertions(+), 10 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextConfigurationAttributes.java b/spring-test/src/main/java/org/springframework/test/context/ContextConfigurationAttributes.java index 035483a71e0a..fcf1ab05b847 100644 --- a/spring-test/src/main/java/org/springframework/test/context/ContextConfigurationAttributes.java +++ b/spring-test/src/main/java/org/springframework/test/context/ContextConfigurationAttributes.java @@ -20,9 +20,9 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.style.ToStringCreator; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -68,21 +68,29 @@ public class ContextConfigurationAttributes { * @throws IllegalStateException if both the locations and value attributes have been declared */ private static String[] resolveLocations(Class declaringClass, ContextConfiguration contextConfiguration) { - Assert.notNull(declaringClass, "declaringClass must not be null"); + return resolveLocations(declaringClass, contextConfiguration.locations(), contextConfiguration.value()); + } - String[] locations = contextConfiguration.locations(); - String[] valueLocations = 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"); - if (!ObjectUtils.isEmpty(valueLocations) && !ObjectUtils.isEmpty(locations)) { + if (!ObjectUtils.isEmpty(value) && !ObjectUtils.isEmpty(locations)) { String msg = String.format("Test class [%s] has been configured with @ContextConfiguration's 'value' %s " + "and 'locations' %s attributes. Only one declaration of resource " + "locations is permitted per @ContextConfiguration annotation.", declaringClass.getName(), - ObjectUtils.nullSafeToString(valueLocations), ObjectUtils.nullSafeToString(locations)); + ObjectUtils.nullSafeToString(value), ObjectUtils.nullSafeToString(locations)); logger.error(msg); throw new IllegalStateException(msg); } - else if (!ObjectUtils.isEmpty(valueLocations)) { - locations = valueLocations; + else if (!ObjectUtils.isEmpty(value)) { + locations = value; } return locations; @@ -101,6 +109,25 @@ public ContextConfigurationAttributes(Class declaringClass, ContextConfigurat 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>[]) annAttrs.getClassArray("initializers"), + annAttrs.getBoolean("inheritInitializers"), annAttrs.getString("name"), + (Class) annAttrs.getClass("loader")); + } + /** * Construct a new {@link ContextConfigurationAttributes} instance for the * {@linkplain Class test class} that declared the diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java b/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java index b39e0025094d..a2b6abb90e5e 100644 --- a/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java +++ b/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java @@ -213,6 +213,25 @@ private static void convertContextConfigToConfigAttributesAndAddToList(ContextCo attributesList.add(attributes); } + /** + * Convenience method for creating a {@link ContextConfigurationAttributes} + * instance from the supplied {@link ContextConfiguration} attributes and + * declaring class and then adding the attributes to the supplied list. + */ + private static void convertContextConfigToConfigAttributesAndAddToList(AnnotationAttributes annAttrs, + Class declaringClass, final List 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 * configuration attributes} for the supplied {@linkplain Class test class} and its @@ -411,8 +430,10 @@ static List resolveContextConfigurationAttribute Class declaringClass = (descriptor.getStereotype() != null) ? descriptor.getStereotypeType() : rootDeclaringClass; - convertContextConfigToConfigAttributesAndAddToList(descriptor.getAnnotation(), declaringClass, - attributesList); + AnnotationAttributes annAttrs = AnnotatedElementUtils.getAnnotationAttributes(rootDeclaringClass, + annotationType.getName()); + + convertContextConfigToConfigAttributesAndAddToList(annAttrs, declaringClass, attributesList); descriptor = findAnnotationDescriptor(rootDeclaringClass.getSuperclass(), annotationType); } diff --git a/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsConfigurationAttributesTests.java b/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsConfigurationAttributesTests.java index 471243eb42a3..d41708a120b1 100644 --- a/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsConfigurationAttributesTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsConfigurationAttributesTests.java @@ -83,6 +83,24 @@ public void resolveConfigAttributesWithMetaAnnotationAndLocations() { EMPTY_CLASS_ARRAY, ContextLoader.class, true); } + @Test + public void resolveConfigAttributesWithMetaAnnotationAndLocationsAndOverrides() { + List 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 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 public void resolveConfigAttributesWithMetaAnnotationAndLocationsInClassHierarchy() { List attributesList = resolveContextConfigurationAttributes(MetaLocationsBar.class); From 735f6f053fcbb2580c6a9c2708dfb14e9ae5ec52 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Tue, 26 Nov 2013 12:57:16 -0500 Subject: [PATCH 09/12] @ContextConfiguration: meta-annotation attribute overrides --- .../test/context/ContextLoaderUtils.java | 27 ++++--- ...ntextLoaderUtilsContextHierarchyTests.java | 74 +++++++++++++------ 2 files changed, 66 insertions(+), 35 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java b/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java index a2b6abb90e5e..765f5164ecc6 100644 --- a/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java +++ b/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java @@ -194,9 +194,9 @@ static Class resolveContextLoaderClass(Class testCla } /** - * Convenience method for creating a {@link ContextConfigurationAttributes} instance - * from the supplied {@link ContextConfiguration} and declaring class and then adding - * the attributes to the supplied list. + * Convenience method for creating a {@link ContextConfigurationAttributes} + * instance from the supplied {@link ContextConfiguration} annotation and + * declaring class and then adding the attributes to the supplied list. */ private static void convertContextConfigToConfigAttributesAndAddToList(ContextConfiguration contextConfiguration, Class declaringClass, final List attributesList) { @@ -215,10 +215,12 @@ private static void convertContextConfigToConfigAttributesAndAddToList(ContextCo /** * Convenience method for creating a {@link ContextConfigurationAttributes} - * instance from the supplied {@link ContextConfiguration} attributes and - * declaring class and then adding the attributes to the supplied list. + * instance from the supplied {@link AnnotationAttributes} and declaring + * class and then adding the attributes to the supplied list. + * + * @since 4.0 */ - private static void convertContextConfigToConfigAttributesAndAddToList(AnnotationAttributes annAttrs, + private static void convertAnnotationAttributesToConfigAttributesAndAddToList(AnnotationAttributes annAttrs, Class declaringClass, final List attributesList) { if (logger.isTraceEnabled()) { logger.trace(String.format("Retrieved @ContextConfiguration attributes [%s] for declaring class [%s].", @@ -264,6 +266,8 @@ private static void convertContextConfigToConfigAttributesAndAddToList(Annotatio * present on the supplied class; or if a given class in the class hierarchy * declares both {@code @ContextConfiguration} and {@code @ContextHierarchy} as * top-level annotations. + * @throws IllegalStateException if no class in the class hierarchy declares + * {@code @ContextHierarchy}. * * @since 3.2.2 * @see #buildContextHierarchyMap(Class) @@ -272,6 +276,7 @@ private static void convertContextConfigToConfigAttributesAndAddToList(Annotatio @SuppressWarnings("unchecked") static List> resolveContextHierarchyAttributes(Class testClass) { Assert.notNull(testClass, "Class must not be null"); + Assert.state(findAnnotation(testClass, ContextHierarchy.class) != null, "@ContextHierarchy must be present"); final Class contextConfigType = ContextConfiguration.class; final Class contextHierarchyType = ContextHierarchy.class; @@ -302,8 +307,9 @@ static List> resolveContextHierarchyAttribu final List configAttributesList = new ArrayList(); if (contextConfigDeclaredLocally) { - ContextConfiguration contextConfiguration = getAnnotation(declaringClass, contextConfigType); - convertContextConfigToConfigAttributesAndAddToList(contextConfiguration, declaringClass, + AnnotationAttributes annAttrs = AnnotatedElementUtils.getAnnotationAttributes(rootDeclaringClass, + contextConfigType.getName()); + convertAnnotationAttributesToConfigAttributesAndAddToList(annAttrs, declaringClass, configAttributesList); } else if (contextHierarchyDeclaredLocally) { @@ -314,7 +320,7 @@ else if (contextHierarchyDeclaredLocally) { } } 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 " + "nor @ContextHierarchy as a class-level annotation.", rootDeclaringClass.getName()); logger.error(msg); @@ -432,8 +438,7 @@ static List resolveContextConfigurationAttribute AnnotationAttributes annAttrs = AnnotatedElementUtils.getAnnotationAttributes(rootDeclaringClass, annotationType.getName()); - - convertContextConfigToConfigAttributesAndAddToList(annAttrs, declaringClass, attributesList); + convertAnnotationAttributesToConfigAttributesAndAddToList(annAttrs, declaringClass, attributesList); descriptor = findAnnotationDescriptor(rootDeclaringClass.getSuperclass(), annotationType); } diff --git a/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsContextHierarchyTests.java b/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsContextHierarchyTests.java index 2b839bbbca49..2a0b24820cb5 100644 --- a/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsContextHierarchyTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsContextHierarchyTests.java @@ -55,13 +55,9 @@ public void resolveContextHierarchyAttributesForSingleTestClassWithContextConfig resolveContextHierarchyAttributes(SingleTestClassWithContextConfigurationAndContextHierarchyOnSingleMetaAnnotation.class); } - @Test + @Test(expected = IllegalStateException.class) public void resolveContextHierarchyAttributesForSingleTestClassWithImplicitSingleLevelContextHierarchy() { - List> hierarchyAttributes = resolveContextHierarchyAttributes(BareAnnotations.class); - assertEquals(1, hierarchyAttributes.size()); - List configAttributesList = hierarchyAttributes.get(0); - assertEquals(1, configAttributesList.size()); - debugConfigAttributes(configAttributesList); + resolveContextHierarchyAttributes(BareAnnotations.class); } @Test @@ -98,8 +94,8 @@ public void resolveContextHierarchyAttributesForSingleTestClassWithTripleLevelCo debugConfigAttributes(configAttributesList); assertAttributes(configAttributesList.get(0), testClass, new String[] { "A.xml" }, EMPTY_CLASS_ARRAY, ContextLoader.class, true); - assertAttributes(configAttributesList.get(1), testClass, new String[] { "B.xml" }, - EMPTY_CLASS_ARRAY, ContextLoader.class, true); + assertAttributes(configAttributesList.get(1), testClass, new String[] { "B.xml" }, EMPTY_CLASS_ARRAY, + ContextLoader.class, true); assertAttributes(configAttributesList.get(2), testClass, new String[] { "C.xml" }, EMPTY_CLASS_ARRAY, ContextLoader.class, true); } @@ -154,36 +150,39 @@ public void resolveContextHierarchyAttributesForTestClassHierarchyWithSingleLeve EMPTY_CLASS_ARRAY, ContextLoader.class, true); } - @Test - public void resolveContextHierarchyAttributesForTestClassHierarchyWithBareContextConfigurationInSubclass() { - List> hierarchyAttributes = resolveContextHierarchyAttributes(TestClass2WithBareContextConfigurationInSubclass.class); + private void assertOneTwo(List> hierarchyAttributes) { assertEquals(2, hierarchyAttributes.size()); List configAttributesListClassLevel1 = hierarchyAttributes.get(0); + List configAttributesListClassLevel2 = hierarchyAttributes.get(1); debugConfigAttributes(configAttributesListClassLevel1); + debugConfigAttributes(configAttributesListClassLevel2); + assertEquals(1, configAttributesListClassLevel1.size()); assertThat(configAttributesListClassLevel1.get(0).getLocations()[0], equalTo("one.xml")); - List configAttributesListClassLevel2 = hierarchyAttributes.get(1); - debugConfigAttributes(configAttributesListClassLevel2); assertEquals(1, configAttributesListClassLevel2.size()); assertThat(configAttributesListClassLevel2.get(0).getLocations()[0], equalTo("two.xml")); } @Test public void resolveContextHierarchyAttributesForTestClassHierarchyWithBareContextConfigurationInSuperclass() { - List> hierarchyAttributes = resolveContextHierarchyAttributes(TestClass2WithBareContextConfigurationInSuperclass.class); - assertEquals(2, hierarchyAttributes.size()); + assertOneTwo(resolveContextHierarchyAttributes(TestClass2WithBareContextConfigurationInSuperclass.class)); + } - List configAttributesListClassLevel1 = hierarchyAttributes.get(0); - debugConfigAttributes(configAttributesListClassLevel1); - assertEquals(1, configAttributesListClassLevel1.size()); - assertThat(configAttributesListClassLevel1.get(0).getLocations()[0], equalTo("one.xml")); + @Test + public void resolveContextHierarchyAttributesForTestClassHierarchyWithBareContextConfigurationInSubclass() { + assertOneTwo(resolveContextHierarchyAttributes(TestClass2WithBareContextConfigurationInSubclass.class)); + } - List configAttributesListClassLevel2 = hierarchyAttributes.get(1); - debugConfigAttributes(configAttributesListClassLevel2); - assertEquals(1, configAttributesListClassLevel2.size()); - assertThat(configAttributesListClassLevel2.get(0).getLocations()[0], equalTo("two.xml")); + @Test + public void resolveContextHierarchyAttributesForTestClassHierarchyWithBareMetaContextConfigWithOverridesInSuperclass() { + assertOneTwo(resolveContextHierarchyAttributes(TestClass2WithBareMetaContextConfigWithOverridesInSuperclass.class)); + } + + @Test + public void resolveContextHierarchyAttributesForTestClassHierarchyWithBareMetaContextConfigWithOverridesInSubclass() { + assertOneTwo(resolveContextHierarchyAttributes(TestClass2WithBareMetaContextConfigWithOverridesInSubclass.class)); } @Test @@ -408,7 +407,7 @@ private static class TestClass1WithBareContextConfigurationInSubclass { @ContextConfiguration("two.xml") private static class TestClass2WithBareContextConfigurationInSubclass extends - TestClass1WithBareContextConfigurationInSuperclass { + TestClass1WithBareContextConfigurationInSubclass { } @ContextHierarchy({// @@ -569,4 +568,31 @@ private static class TestClass3WithSingleLevelContextHierarchyFromMetaAnnotation 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 { + } + } From dcdeea22edc8c35b476b0e25e269a0a5416394bb Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Tue, 26 Nov 2013 14:08:21 -0500 Subject: [PATCH 10/12] Introduced integration tests for meta-annotation attribute override support. --- .../context/MetaAnnotationUtilsTests.java | 60 ++++++++++++++++ .../junit4/annotation/meta/MetaConfig.java | 68 +++++++++++++++++++ .../meta/MetaConfigDefaultsTests.java | 45 ++++++++++++ .../meta/MetaConfigOverrideTests.java | 66 ++++++++++++++++++ 4 files changed, 239 insertions(+) create mode 100644 spring-test/src/test/java/org/springframework/test/context/junit4/annotation/meta/MetaConfig.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/junit4/annotation/meta/MetaConfigDefaultsTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/junit4/annotation/meta/MetaConfigOverrideTests.java diff --git a/spring-test/src/test/java/org/springframework/test/context/MetaAnnotationUtilsTests.java b/spring-test/src/test/java/org/springframework/test/context/MetaAnnotationUtilsTests.java index 1b5b7c521dbe..4ff51fc83a92 100644 --- a/spring-test/src/test/java/org/springframework/test/context/MetaAnnotationUtilsTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/MetaAnnotationUtilsTests.java @@ -213,6 +213,44 @@ public void findAnnotationDescriptorForTypesWithMetaComponentAnnotation() throws assertComponentOnStereotypeForMultipleCandidateTypes(startClass, startClass, "meta1", Meta1.class); } + @Test + @SuppressWarnings("unchecked") + public void findAnnotationDescriptorForTypesWithMetaAnnotationWithDefaultAttributes() throws Exception { + Class startClass = MetaConfigWithDefaultAttributesTestCase.class; + Class annotationType = ContextConfiguration.class; + + UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes(startClass, Service.class, + ContextConfiguration.class, Order.class, Transactional.class); + + assertNotNull(descriptor); + assertEquals(startClass, descriptor.getDeclaringClass()); + 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 annotationType = ContextConfiguration.class; + + UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes(startClass, Service.class, + ContextConfiguration.class, Order.class, Transactional.class); + + assertNotNull(descriptor); + assertEquals(startClass, descriptor.getDeclaringClass()); + 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 public void findAnnotationDescriptorForTypesForInterfaceWithMetaAnnotation() { Class startClass = InterfaceWithMetaAnnotation.class; @@ -279,6 +317,28 @@ static class SubClassWithLocalMetaAnnotationAndMetaAnnotatedInterface extends 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 diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/annotation/meta/MetaConfig.java b/spring-test/src/test/java/org/springframework/test/context/junit4/annotation/meta/MetaConfig.java new file mode 100644 index 000000000000..51d2ec70a988 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/annotation/meta/MetaConfig.java @@ -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"; + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/annotation/meta/MetaConfigDefaultsTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/annotation/meta/MetaConfigDefaultsTests.java new file mode 100644 index 000000000000..8349aa846a7b --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/annotation/meta/MetaConfigDefaultsTests.java @@ -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); + } +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/annotation/meta/MetaConfigOverrideTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/annotation/meta/MetaConfigOverrideTests.java new file mode 100644 index 000000000000..3567d28bdd61 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/annotation/meta/MetaConfigOverrideTests.java @@ -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); + } +} From 01b5da44c1455f8cf4e4b9794df06b170ea5c296 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Tue, 26 Nov 2013 14:57:42 -0500 Subject: [PATCH 11/12] ContextLoaderUtils now retrieves AnnotationAttributes from AnnotationDescriptor to look up annotation attributes for @ContextConfiguration and @ActiveProfiles. --- .../test/context/ContextLoaderUtils.java | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java b/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java index 765f5164ecc6..2ed03fccff8d 100644 --- a/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java +++ b/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java @@ -31,7 +31,6 @@ import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.test.context.MetaAnnotationUtils.AnnotationDescriptor; @@ -307,10 +306,8 @@ static List> resolveContextHierarchyAttribu final List configAttributesList = new ArrayList(); if (contextConfigDeclaredLocally) { - AnnotationAttributes annAttrs = AnnotatedElementUtils.getAnnotationAttributes(rootDeclaringClass, - contextConfigType.getName()); - convertAnnotationAttributesToConfigAttributesAndAddToList(annAttrs, declaringClass, - configAttributesList); + convertAnnotationAttributesToConfigAttributesAndAddToList(descriptor.getAnnotationAttributes(), + declaringClass, configAttributesList); } else if (contextHierarchyDeclaredLocally) { ContextHierarchy contextHierarchy = getAnnotation(declaringClass, contextHierarchyType); @@ -436,9 +433,8 @@ static List resolveContextConfigurationAttribute Class declaringClass = (descriptor.getStereotype() != null) ? descriptor.getStereotypeType() : rootDeclaringClass; - AnnotationAttributes annAttrs = AnnotatedElementUtils.getAnnotationAttributes(rootDeclaringClass, - annotationType.getName()); - convertAnnotationAttributesToConfigAttributesAndAddToList(annAttrs, declaringClass, attributesList); + convertAnnotationAttributesToConfigAttributesAndAddToList(descriptor.getAnnotationAttributes(), + declaringClass, attributesList); descriptor = findAnnotationDescriptor(rootDeclaringClass.getSuperclass(), annotationType); } @@ -521,8 +517,7 @@ static String[] resolveActiveProfiles(Class testClass) { Class declaringClass = (descriptor.getStereotype() != null) ? descriptor.getStereotypeType() : rootDeclaringClass; - AnnotationAttributes annAttrs = AnnotatedElementUtils.getAnnotationAttributes(rootDeclaringClass, - annotationType.getName()); + AnnotationAttributes annAttrs = descriptor.getAnnotationAttributes(); if (logger.isTraceEnabled()) { logger.trace(String.format("Retrieved @ActiveProfiles attributes [%s] for declaring class [%s].", annAttrs, declaringClass.getName())); From da99bca99b257858652141d586daecc5a6105be4 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Tue, 26 Nov 2013 15:36:19 -0500 Subject: [PATCH 12/12] - 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. - TestContextManager now retrieves AnnotationAttributes from AnnotationDescriptor to look up annotation attributes for @TestExecutionListeners. --- .../test/context/ContextLoaderUtils.java | 23 +++++------- .../test/context/MetaAnnotationUtils.java | 25 ++++++++----- .../test/context/TestContextManager.java | 14 +++----- .../context/MetaAnnotationUtilsTests.java | 36 +++++++++---------- ...erriddenMetaAnnotationAttributesTests.java | 8 ++--- 5 files changed, 51 insertions(+), 55 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java b/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java index 2ed03fccff8d..078563060451 100644 --- a/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java +++ b/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java @@ -288,17 +288,16 @@ static List> resolveContextHierarchyAttribu contextConfigType.getName(), contextHierarchyType.getName(), testClass.getName())); while (descriptor != null) { - Class rootDeclaringClass = descriptor.getDeclaringClass(); - Class declaringClass = (descriptor.getStereotype() != null) ? descriptor.getStereotypeType() - : rootDeclaringClass; + Class rootDeclaringClass = descriptor.getRootDeclaringClass(); + Class declaringClass = descriptor.getDeclaringClass(); boolean contextConfigDeclaredLocally = isAnnotationDeclaredLocally(contextConfigType, declaringClass); boolean contextHierarchyDeclaredLocally = isAnnotationDeclaredLocally(contextHierarchyType, declaringClass); 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 " - + "or custom stereotype annotation.", rootDeclaringClass.getName()); + + "or custom stereotype annotation.", declaringClass.getName()); logger.error(msg); throw new IllegalStateException(msg); } @@ -429,13 +428,9 @@ static List resolveContextConfigurationAttribute annotationType.getName(), testClass.getName())); while (descriptor != null) { - Class rootDeclaringClass = descriptor.getDeclaringClass(); - Class declaringClass = (descriptor.getStereotype() != null) ? descriptor.getStereotypeType() - : rootDeclaringClass; - convertAnnotationAttributesToConfigAttributesAndAddToList(descriptor.getAnnotationAttributes(), - declaringClass, attributesList); - descriptor = findAnnotationDescriptor(rootDeclaringClass.getSuperclass(), annotationType); + descriptor.getDeclaringClass(), attributesList); + descriptor = findAnnotationDescriptor(descriptor.getRootDeclaringClass().getSuperclass(), annotationType); } return attributesList; @@ -513,9 +508,7 @@ static String[] resolveActiveProfiles(Class testClass) { final Set activeProfiles = new HashSet(); while (descriptor != null) { - Class rootDeclaringClass = descriptor.getDeclaringClass(); - Class declaringClass = (descriptor.getStereotype() != null) ? descriptor.getStereotypeType() - : rootDeclaringClass; + Class declaringClass = descriptor.getDeclaringClass(); AnnotationAttributes annAttrs = descriptor.getAnnotationAttributes(); if (logger.isTraceEnabled()) { @@ -563,7 +556,7 @@ else if (valueDeclared) { } descriptor = annAttrs.getBoolean("inheritProfiles") ? findAnnotationDescriptor( - rootDeclaringClass.getSuperclass(), annotationType) : null; + descriptor.getRootDeclaringClass().getSuperclass(), annotationType) : null; } return StringUtils.toStringArray(activeProfiles); diff --git a/spring-test/src/main/java/org/springframework/test/context/MetaAnnotationUtils.java b/spring-test/src/main/java/org/springframework/test/context/MetaAnnotationUtils.java index b520a8323e68..06b3e8d9633e 100644 --- a/spring-test/src/main/java/org/springframework/test/context/MetaAnnotationUtils.java +++ b/spring-test/src/main/java/org/springframework/test/context/MetaAnnotationUtils.java @@ -126,7 +126,7 @@ public static UntypedAnnotationDescriptor findAnnotationDescriptorForTypes(Class *

* If the annotation is used as a meta-annotation, the descriptor also includes * the {@linkplain #getStereotype() stereotype} on which the annotation is - * present. In such cases, the declaring class is not directly + * present. In such cases, the root declaring class is not directly * annotated with the annotation but rather indirectly via the stereotype. * *

@@ -135,6 +135,7 @@ public static UntypedAnnotationDescriptor findAnnotationDescriptorForTypes(Class * properties of the {@code AnnotationDescriptor} would be as follows. * *