3131import org .apache .commons .logging .LogFactory ;
3232import org .springframework .context .ApplicationContextInitializer ;
3333import org .springframework .context .ConfigurableApplicationContext ;
34+ import org .springframework .core .annotation .AnnotationAttributes ;
3435import org .springframework .core .annotation .AnnotationUtils ;
3536import org .springframework .test .context .MetaAnnotationUtils .AnnotationDescriptor ;
3637import org .springframework .test .context .MetaAnnotationUtils .UntypedAnnotationDescriptor ;
@@ -192,9 +193,9 @@ static Class<? extends ContextLoader> resolveContextLoaderClass(Class<?> testCla
192193 }
193194
194195 /**
195- * Convenience method for creating a {@link ContextConfigurationAttributes} instance
196- * from the supplied {@link ContextConfiguration} and declaring class and then adding
197- * the attributes to the supplied list.
196+ * Convenience method for creating a {@link ContextConfigurationAttributes}
197+ * instance from the supplied {@link ContextConfiguration} annotation and
198+ * declaring class and then adding the attributes to the supplied list.
198199 */
199200 private static void convertContextConfigToConfigAttributesAndAddToList (ContextConfiguration contextConfiguration ,
200201 Class <?> declaringClass , final List <ContextConfigurationAttributes > attributesList ) {
@@ -211,6 +212,27 @@ private static void convertContextConfigToConfigAttributesAndAddToList(ContextCo
211212 attributesList .add (attributes );
212213 }
213214
215+ /**
216+ * Convenience method for creating a {@link ContextConfigurationAttributes}
217+ * instance from the supplied {@link AnnotationAttributes} and declaring
218+ * class and then adding the attributes to the supplied list.
219+ *
220+ * @since 4.0
221+ */
222+ private static void convertAnnotationAttributesToConfigAttributesAndAddToList (AnnotationAttributes annAttrs ,
223+ Class <?> declaringClass , final List <ContextConfigurationAttributes > attributesList ) {
224+ if (logger .isTraceEnabled ()) {
225+ logger .trace (String .format ("Retrieved @ContextConfiguration attributes [%s] for declaring class [%s]." ,
226+ annAttrs , declaringClass .getName ()));
227+ }
228+
229+ ContextConfigurationAttributes attributes = new ContextConfigurationAttributes (declaringClass , annAttrs );
230+ if (logger .isTraceEnabled ()) {
231+ logger .trace ("Resolved context configuration attributes: " + attributes );
232+ }
233+ attributesList .add (attributes );
234+ }
235+
214236 /**
215237 * Resolve the list of lists of {@linkplain ContextConfigurationAttributes context
216238 * configuration attributes} for the supplied {@linkplain Class test class} and its
@@ -243,6 +265,8 @@ private static void convertContextConfigToConfigAttributesAndAddToList(ContextCo
243265 * <em>present</em> on the supplied class; or if a given class in the class hierarchy
244266 * declares both {@code @ContextConfiguration} and {@code @ContextHierarchy} as
245267 * top-level annotations.
268+ * @throws IllegalStateException if no class in the class hierarchy declares
269+ * {@code @ContextHierarchy}.
246270 *
247271 * @since 3.2.2
248272 * @see #buildContextHierarchyMap(Class)
@@ -251,6 +275,7 @@ private static void convertContextConfigToConfigAttributesAndAddToList(ContextCo
251275 @ SuppressWarnings ("unchecked" )
252276 static List <List <ContextConfigurationAttributes >> resolveContextHierarchyAttributes (Class <?> testClass ) {
253277 Assert .notNull (testClass , "Class must not be null" );
278+ Assert .state (findAnnotation (testClass , ContextHierarchy .class ) != null , "@ContextHierarchy must be present" );
254279
255280 final Class <ContextConfiguration > contextConfigType = ContextConfiguration .class ;
256281 final Class <ContextHierarchy > contextHierarchyType = ContextHierarchy .class ;
@@ -263,27 +288,25 @@ static List<List<ContextConfigurationAttributes>> resolveContextHierarchyAttribu
263288 contextConfigType .getName (), contextHierarchyType .getName (), testClass .getName ()));
264289
265290 while (descriptor != null ) {
266- Class <?> rootDeclaringClass = descriptor .getDeclaringClass ();
267- Class <?> declaringClass = (descriptor .getStereotype () != null ) ? descriptor .getStereotypeType ()
268- : rootDeclaringClass ;
291+ Class <?> rootDeclaringClass = descriptor .getRootDeclaringClass ();
292+ Class <?> declaringClass = descriptor .getDeclaringClass ();
269293
270294 boolean contextConfigDeclaredLocally = isAnnotationDeclaredLocally (contextConfigType , declaringClass );
271295 boolean contextHierarchyDeclaredLocally = isAnnotationDeclaredLocally (contextHierarchyType , declaringClass );
272296
273297 if (contextConfigDeclaredLocally && contextHierarchyDeclaredLocally ) {
274- String msg = String .format ("Test class [%s] has been configured with both @ContextConfiguration "
298+ String msg = String .format ("Class [%s] has been configured with both @ContextConfiguration "
275299 + "and @ContextHierarchy. Only one of these annotations may be declared on a test class "
276- + "or custom stereotype annotation." , rootDeclaringClass .getName ());
300+ + "or custom stereotype annotation." , declaringClass .getName ());
277301 logger .error (msg );
278302 throw new IllegalStateException (msg );
279303 }
280304
281305 final List <ContextConfigurationAttributes > configAttributesList = new ArrayList <ContextConfigurationAttributes >();
282306
283307 if (contextConfigDeclaredLocally ) {
284- ContextConfiguration contextConfiguration = getAnnotation (declaringClass , contextConfigType );
285- convertContextConfigToConfigAttributesAndAddToList (contextConfiguration , declaringClass ,
286- configAttributesList );
308+ convertAnnotationAttributesToConfigAttributesAndAddToList (descriptor .getAnnotationAttributes (),
309+ declaringClass , configAttributesList );
287310 }
288311 else if (contextHierarchyDeclaredLocally ) {
289312 ContextHierarchy contextHierarchy = getAnnotation (declaringClass , contextHierarchyType );
@@ -293,7 +316,7 @@ else if (contextHierarchyDeclaredLocally) {
293316 }
294317 }
295318 else {
296- // This should theoretically actually never happen...
319+ // This should theoretically never happen...
297320 String msg = String .format ("Test class [%s] has been configured with neither @ContextConfiguration "
298321 + "nor @ContextHierarchy as a class-level annotation." , rootDeclaringClass .getName ());
299322 logger .error (msg );
@@ -405,13 +428,9 @@ static List<ContextConfigurationAttributes> resolveContextConfigurationAttribute
405428 annotationType .getName (), testClass .getName ()));
406429
407430 while (descriptor != null ) {
408- Class <?> rootDeclaringClass = descriptor .getDeclaringClass ();
409- Class <?> declaringClass = (descriptor .getStereotype () != null ) ? descriptor .getStereotypeType ()
410- : rootDeclaringClass ;
411-
412- convertContextConfigToConfigAttributesAndAddToList (descriptor .getAnnotation (), declaringClass ,
413- attributesList );
414- descriptor = findAnnotationDescriptor (rootDeclaringClass .getSuperclass (), annotationType );
431+ convertAnnotationAttributesToConfigAttributesAndAddToList (descriptor .getAnnotationAttributes (),
432+ descriptor .getDeclaringClass (), attributesList );
433+ descriptor = findAnnotationDescriptor (descriptor .getRootDeclaringClass ().getSuperclass (), annotationType );
415434 }
416435
417436 return attributesList ;
@@ -489,20 +508,18 @@ static String[] resolveActiveProfiles(Class<?> testClass) {
489508 final Set <String > activeProfiles = new HashSet <String >();
490509
491510 while (descriptor != null ) {
492- Class <?> rootDeclaringClass = descriptor .getDeclaringClass ();
493- Class <?> declaringClass = (descriptor .getStereotype () != null ) ? descriptor .getStereotypeType ()
494- : rootDeclaringClass ;
511+ Class <?> declaringClass = descriptor .getDeclaringClass ();
495512
496- ActiveProfiles annotation = descriptor .getAnnotation ();
513+ AnnotationAttributes annAttrs = descriptor .getAnnotationAttributes ();
497514 if (logger .isTraceEnabled ()) {
498- logger .trace (String .format ("Retrieved @ActiveProfiles [%s] for declaring class [%s]." , annotation ,
499- declaringClass .getName ()));
515+ logger .trace (String .format ("Retrieved @ActiveProfiles attributes [%s] for declaring class [%s]." ,
516+ annAttrs , declaringClass .getName ()));
500517 }
501- validateActiveProfilesConfiguration (declaringClass , annotation );
518+ validateActiveProfilesConfiguration (declaringClass , annAttrs );
502519
503- String [] profiles = annotation . profiles ( );
504- String [] valueProfiles = annotation . value ( );
505- Class <? extends ActiveProfilesResolver > resolverClass = annotation . resolver ( );
520+ String [] profiles = annAttrs . getStringArray ( "profiles" );
521+ String [] valueProfiles = annAttrs . getStringArray ( "value" );
522+ Class <? extends ActiveProfilesResolver > resolverClass = annAttrs . getClass ( "resolver" );
506523
507524 boolean resolverDeclared = !ActiveProfilesResolver .class .equals (resolverClass );
508525 boolean valueDeclared = !ObjectUtils .isEmpty (valueProfiles );
@@ -538,17 +555,17 @@ else if (valueDeclared) {
538555 }
539556 }
540557
541- descriptor = annotation . inheritProfiles ( ) ? findAnnotationDescriptor (rootDeclaringClass . getSuperclass (),
542- annotationType ) : null ;
558+ descriptor = annAttrs . getBoolean ( "inheritProfiles" ) ? findAnnotationDescriptor (
559+ descriptor . getRootDeclaringClass (). getSuperclass (), annotationType ) : null ;
543560 }
544561
545562 return StringUtils .toStringArray (activeProfiles );
546563 }
547564
548- private static void validateActiveProfilesConfiguration (Class <?> declaringClass , ActiveProfiles annotation ) {
549- String [] valueProfiles = annotation . value ( );
550- String [] profiles = annotation . profiles ( );
551- Class <? extends ActiveProfilesResolver > resolverClass = annotation . resolver ( );
565+ private static void validateActiveProfilesConfiguration (Class <?> declaringClass , AnnotationAttributes annAttrs ) {
566+ String [] valueProfiles = annAttrs . getStringArray ( "value" );
567+ String [] profiles = annAttrs . getStringArray ( "profiles" );
568+ Class <? extends ActiveProfilesResolver > resolverClass = annAttrs . getClass ( "resolver" );
552569 boolean valueDeclared = !ObjectUtils .isEmpty (valueProfiles );
553570 boolean profilesDeclared = !ObjectUtils .isEmpty (profiles );
554571 boolean resolverDeclared = !ActiveProfilesResolver .class .equals (resolverClass );
0 commit comments