diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/ActivateProfiles.java b/org.springframework.test/src/main/java/org/springframework/test/context/ActivateProfiles.java new file mode 100644 index 00000000000..63c839eed38 --- /dev/null +++ b/org.springframework.test/src/main/java/org/springframework/test/context/ActivateProfiles.java @@ -0,0 +1,68 @@ +/* + * Copyright 2002-2011 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.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * {@code ActivateProfiles} is a class-level annotation that is used to + * activate the bean definition profiles to use when loading an + * {@link org.springframework.context.ApplicationContext ApplicationContext} + * for test classes. + * + * @author Sam Brannen + * @since 3.1 + * @see ContextConfiguration + * @see ContextLoader + * @see org.springframework.context.ApplicationContext + * @see org.springframework.context.annotation.Profile + */ +@Documented +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface ActivateProfiles { + + /** + * Alias for {@link #profiles() profiles}. + * + *

This attribute may not be used in conjunction + * with {@link #profiles}, but it may be used instead of + * {@link #profiles}. + */ + String[] value() default {}; + + /** + * The list of bean definition profiles to activate. + * + *

This attribute may not be used in conjunction + * with {@link #value}, but it may be used instead of + * {@link #value}. + */ + String[] profiles() default {}; + + /** + * TODO Document inheritLocations. + */ + boolean inheritProfiles() default true; + +} diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java b/org.springframework.test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java index 0ad20cec081..2f414deff15 100644 --- a/org.springframework.test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java +++ b/org.springframework.test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java @@ -209,6 +209,58 @@ abstract class ContextLoaderUtils { return locationsList.toArray(new String[locationsList.size()]); } + /** + * TODO Document resolveActivatedProfiles(). + * + * @param clazz + * @return + */ + static String[] resolveActivatedProfiles(Class clazz) { + Assert.notNull(clazz, "Class must not be null"); + + Class annotationType = ActivateProfiles.class; + Class declaringClass = AnnotationUtils.findAnnotationDeclaringClass(annotationType, clazz); + + if (declaringClass == null && logger.isDebugEnabled()) { + logger.debug(String.format( + "Could not find an 'annotation declaring class' for annotation type [%s] and class [%s]", + annotationType, clazz)); + } + + List profilesList = new ArrayList(); + + while (declaringClass != null) { + ActivateProfiles activateProfiles = declaringClass.getAnnotation(annotationType); + + if (logger.isTraceEnabled()) { + logger.trace(String.format("Retrieved @ActivateProfiles [%s] for declaring class [%s].", + activateProfiles, declaringClass)); + } + + String[] profiles = activateProfiles.profiles(); + String[] valueProfiles = activateProfiles.value(); + + if (!ObjectUtils.isEmpty(valueProfiles) && !ObjectUtils.isEmpty(profiles)) { + String msg = String.format("Test class [%s] has been configured with @ActivateProfiles' 'value' [%s] " + + "and 'profiles' [%s] attributes. Only one declaration of bean " + + "definition profiles is permitted per @ActivateProfiles annotation.", declaringClass, + ObjectUtils.nullSafeToString(valueProfiles), ObjectUtils.nullSafeToString(profiles)); + logger.error(msg); + throw new IllegalStateException(msg); + } + else if (!ObjectUtils.isEmpty(valueProfiles)) { + profiles = valueProfiles; + } + + profilesList.addAll(0, Arrays. asList(profiles)); + + declaringClass = activateProfiles.inheritProfiles() ? AnnotationUtils.findAnnotationDeclaringClass( + annotationType, declaringClass.getSuperclass()) : null; + } + + return profilesList.toArray(new String[profilesList.size()]); + } + /** * Strategy interface for resolving application context resource locations. diff --git a/org.springframework.test/src/test/java/org/springframework/test/context/ContextLoaderUtilsTests.java b/org.springframework.test/src/test/java/org/springframework/test/context/ContextLoaderUtilsTests.java new file mode 100644 index 00000000000..0ed8ccb4927 --- /dev/null +++ b/org.springframework.test/src/test/java/org/springframework/test/context/ContextLoaderUtilsTests.java @@ -0,0 +1,92 @@ +/* + * Copyright 2002-2011 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; + +/** + * Unit tests for {@link ContextLoaderUtils}. + * + * @author Sam Brannen + * @since 3.1 + */ +public class ContextLoaderUtilsTests { + + @Test + public void resolveActivatedProfilesWithoutAnnotation() { + String[] profiles = ContextLoaderUtils.resolveActivatedProfiles(Enigma.class); + assertNotNull(profiles); + assertEquals(0, profiles.length); + } + + @Test + public void resolveActivatedProfilesWithLocalAnnotation() { + String[] profiles = ContextLoaderUtils.resolveActivatedProfiles(Foo.class); + assertNotNull(profiles); + assertEquals(1, profiles.length); + assertEquals("foo", profiles[0]); + } + + @Test + public void resolveActivatedProfilesWithInheritedAnnotation() { + String[] profiles = ContextLoaderUtils.resolveActivatedProfiles(InheritedFoo.class); + assertNotNull(profiles); + assertEquals(1, profiles.length); + assertEquals("foo", profiles[0]); + } + + @Test + public void resolveActivatedProfilesWithLocalAndInheritedAnnotations() { + String[] profiles = ContextLoaderUtils.resolveActivatedProfiles(Bar.class); + assertNotNull(profiles); + assertEquals(2, profiles.length); + assertEquals("foo", profiles[0]); + assertEquals("bar", profiles[1]); + } + + @Test + public void resolveActivatedProfilesWithOverriddenAnnotation() { + String[] profiles = ContextLoaderUtils.resolveActivatedProfiles(Animals.class); + assertNotNull(profiles); + assertEquals(2, profiles.length); + assertEquals("dog", profiles[0]); + assertEquals("cat", profiles[1]); + } + + + private static class Enigma { + } + + @ActivateProfiles(profiles = "foo") + private static class Foo { + } + + private static class InheritedFoo extends Foo { + } + + @ActivateProfiles("bar") + private static class Bar extends Foo { + } + + @ActivateProfiles(profiles = { "dog", "cat" }, inheritProfiles = false) + private static class Animals extends Bar { + } + +}