diff --git a/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsRepeatableAnnotationTests.java b/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsRepeatableAnnotationTests.java index 40b1fdf5b8..ba611b23f4 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsRepeatableAnnotationTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsRepeatableAnnotationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2024 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. @@ -35,6 +35,8 @@ import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.springframework.core.annotation.MergedAnnotations.SearchStrategy.INHERITED_ANNOTATIONS; +import static org.springframework.core.annotation.MergedAnnotations.SearchStrategy.TYPE_HIERARCHY; /** * Tests for {@link MergedAnnotations} and {@link RepeatableContainers} that @@ -49,184 +51,168 @@ class MergedAnnotationsRepeatableAnnotationTests { @Test void inheritedAnnotationsWhenNonRepeatableThrowsException() { - assertThatIllegalArgumentException().isThrownBy(() -> - getAnnotations(null, NonRepeatable.class, SearchStrategy.INHERITED_ANNOTATIONS, getClass())) - .satisfies(this::nonRepeatableRequirements); + assertThatIllegalArgumentException() + .isThrownBy(() -> getAnnotations(null, NonRepeatable.class, INHERITED_ANNOTATIONS, getClass())) + .satisfies(this::nonRepeatableRequirements); } @Test void inheritedAnnotationsWhenContainerMissingValueAttributeThrowsException() { - assertThatAnnotationConfigurationException().isThrownBy(() -> - getAnnotations(ContainerMissingValueAttribute.class, InvalidRepeatable.class, - SearchStrategy.INHERITED_ANNOTATIONS, getClass())) - .satisfies(this::missingValueAttributeRequirements); + assertThatAnnotationConfigurationException() + .isThrownBy(() -> getAnnotations(ContainerMissingValueAttribute.class, InvalidRepeatable.class, + INHERITED_ANNOTATIONS, getClass())) + .satisfies(this::missingValueAttributeRequirements); } @Test void inheritedAnnotationsWhenWhenNonArrayValueAttributeThrowsException() { - assertThatAnnotationConfigurationException().isThrownBy(() -> - getAnnotations(ContainerWithNonArrayValueAttribute.class, InvalidRepeatable.class, - SearchStrategy.INHERITED_ANNOTATIONS, getClass())) - .satisfies(this::nonArrayValueAttributeRequirements); + assertThatAnnotationConfigurationException() + .isThrownBy(() -> getAnnotations(ContainerWithNonArrayValueAttribute.class, InvalidRepeatable.class, + INHERITED_ANNOTATIONS, getClass())) + .satisfies(this::nonArrayValueAttributeRequirements); } @Test void inheritedAnnotationsWhenWrongComponentTypeThrowsException() { - assertThatAnnotationConfigurationException().isThrownBy(() -> - getAnnotations(ContainerWithArrayValueAttributeButWrongComponentType.class, - InvalidRepeatable.class, SearchStrategy.INHERITED_ANNOTATIONS, getClass())) - .satisfies(this::wrongComponentTypeRequirements); + assertThatAnnotationConfigurationException() + .isThrownBy(() -> getAnnotations(ContainerWithArrayValueAttributeButWrongComponentType.class, + InvalidRepeatable.class, INHERITED_ANNOTATIONS, getClass())) + .satisfies(this::wrongComponentTypeRequirements); } @Test void inheritedAnnotationsWhenOnClassReturnsAnnotations() { Set annotations = getAnnotations(null, PeteRepeat.class, - SearchStrategy.INHERITED_ANNOTATIONS, RepeatableClass.class); - assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B", - "C"); + INHERITED_ANNOTATIONS, RepeatableClass.class); + assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B", "C"); } @Test void inheritedAnnotationsWhenWhenOnSuperclassReturnsAnnotations() { Set annotations = getAnnotations(null, PeteRepeat.class, - SearchStrategy.INHERITED_ANNOTATIONS, SubRepeatableClass.class); - assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B", - "C"); + INHERITED_ANNOTATIONS, SubRepeatableClass.class); + assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B", "C"); } @Test void inheritedAnnotationsWhenComposedOnClassReturnsAnnotations() { Set annotations = getAnnotations(null, PeteRepeat.class, - SearchStrategy.INHERITED_ANNOTATIONS, ComposedRepeatableClass.class); - assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B", - "C"); + INHERITED_ANNOTATIONS, ComposedRepeatableClass.class); + assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B", "C"); } @Test void inheritedAnnotationsWhenComposedMixedWithContainerOnClassReturnsAnnotations() { Set annotations = getAnnotations(null, PeteRepeat.class, - SearchStrategy.INHERITED_ANNOTATIONS, - ComposedRepeatableMixedWithContainerClass.class); - assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B", - "C"); + INHERITED_ANNOTATIONS, ComposedRepeatableMixedWithContainerClass.class); + assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B", "C"); } @Test void inheritedAnnotationsWhenComposedContainerForRepeatableOnClassReturnsAnnotations() { Set annotations = getAnnotations(null, PeteRepeat.class, - SearchStrategy.INHERITED_ANNOTATIONS, ComposedContainerClass.class); - assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B", - "C"); + INHERITED_ANNOTATIONS, ComposedContainerClass.class); + assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B", "C"); } @Test void inheritedAnnotationsWhenNoninheritedComposedRepeatableOnClassReturnsAnnotations() { Set annotations = getAnnotations(null, Noninherited.class, - SearchStrategy.INHERITED_ANNOTATIONS, NoninheritedRepeatableClass.class); - assertThat(annotations.stream().map(Noninherited::value)).containsExactly("A", - "B", "C"); + INHERITED_ANNOTATIONS, NoninheritedRepeatableClass.class); + assertThat(annotations.stream().map(Noninherited::value)).containsExactly("A", "B", "C"); } @Test void inheritedAnnotationsWhenNoninheritedComposedRepeatableOnSuperclassReturnsAnnotations() { Set annotations = getAnnotations(null, Noninherited.class, - SearchStrategy.INHERITED_ANNOTATIONS, - SubNoninheritedRepeatableClass.class); + INHERITED_ANNOTATIONS, SubNoninheritedRepeatableClass.class); assertThat(annotations).isEmpty(); } @Test void typeHierarchyWhenNonRepeatableThrowsException() { - assertThatIllegalArgumentException().isThrownBy(() -> - getAnnotations(null, NonRepeatable.class, SearchStrategy.TYPE_HIERARCHY, getClass())) - .satisfies(this::nonRepeatableRequirements); + assertThatIllegalArgumentException() + .isThrownBy(() -> getAnnotations(null, NonRepeatable.class, TYPE_HIERARCHY, getClass())) + .satisfies(this::nonRepeatableRequirements); } @Test void typeHierarchyWhenContainerMissingValueAttributeThrowsException() { - assertThatAnnotationConfigurationException().isThrownBy(() -> - getAnnotations(ContainerMissingValueAttribute.class, InvalidRepeatable.class, - SearchStrategy.TYPE_HIERARCHY, getClass())) - .satisfies(this::missingValueAttributeRequirements); + assertThatAnnotationConfigurationException() + .isThrownBy(() -> getAnnotations(ContainerMissingValueAttribute.class, InvalidRepeatable.class, + TYPE_HIERARCHY, getClass())) + .satisfies(this::missingValueAttributeRequirements); } @Test void typeHierarchyWhenWhenNonArrayValueAttributeThrowsException() { - assertThatAnnotationConfigurationException().isThrownBy(() -> - getAnnotations(ContainerWithNonArrayValueAttribute.class, InvalidRepeatable.class, - SearchStrategy.TYPE_HIERARCHY, getClass())) - .satisfies(this::nonArrayValueAttributeRequirements); + assertThatAnnotationConfigurationException() + .isThrownBy(() -> getAnnotations(ContainerWithNonArrayValueAttribute.class, InvalidRepeatable.class, + TYPE_HIERARCHY, getClass())) + .satisfies(this::nonArrayValueAttributeRequirements); } @Test void typeHierarchyWhenWrongComponentTypeThrowsException() { - assertThatAnnotationConfigurationException().isThrownBy(() -> - getAnnotations(ContainerWithArrayValueAttributeButWrongComponentType.class, - InvalidRepeatable.class, SearchStrategy.TYPE_HIERARCHY, getClass())) - .satisfies(this::wrongComponentTypeRequirements); + assertThatAnnotationConfigurationException() + .isThrownBy(() -> getAnnotations(ContainerWithArrayValueAttributeButWrongComponentType.class, + InvalidRepeatable.class, TYPE_HIERARCHY, getClass())) + .satisfies(this::wrongComponentTypeRequirements); } @Test void typeHierarchyWhenOnClassReturnsAnnotations() { Set annotations = getAnnotations(null, PeteRepeat.class, - SearchStrategy.TYPE_HIERARCHY, RepeatableClass.class); - assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B", - "C"); + TYPE_HIERARCHY, RepeatableClass.class); + assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B", "C"); } @Test void typeHierarchyWhenWhenOnSuperclassReturnsAnnotations() { Set annotations = getAnnotations(null, PeteRepeat.class, - SearchStrategy.TYPE_HIERARCHY, SubRepeatableClass.class); - assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B", - "C"); + TYPE_HIERARCHY, SubRepeatableClass.class); + assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B", "C"); } @Test void typeHierarchyWhenComposedOnClassReturnsAnnotations() { Set annotations = getAnnotations(null, PeteRepeat.class, - SearchStrategy.TYPE_HIERARCHY, ComposedRepeatableClass.class); - assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B", - "C"); + TYPE_HIERARCHY, ComposedRepeatableClass.class); + assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B", "C"); } @Test void typeHierarchyWhenComposedMixedWithContainerOnClassReturnsAnnotations() { Set annotations = getAnnotations(null, PeteRepeat.class, - SearchStrategy.TYPE_HIERARCHY, - ComposedRepeatableMixedWithContainerClass.class); - assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B", - "C"); + TYPE_HIERARCHY, ComposedRepeatableMixedWithContainerClass.class); + assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B", "C"); } @Test void typeHierarchyWhenComposedContainerForRepeatableOnClassReturnsAnnotations() { Set annotations = getAnnotations(null, PeteRepeat.class, - SearchStrategy.TYPE_HIERARCHY, ComposedContainerClass.class); - assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B", - "C"); + TYPE_HIERARCHY, ComposedContainerClass.class); + assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B", "C"); } @Test void typeHierarchyAnnotationsWhenNoninheritedComposedRepeatableOnClassReturnsAnnotations() { Set annotations = getAnnotations(null, Noninherited.class, - SearchStrategy.TYPE_HIERARCHY, NoninheritedRepeatableClass.class); - assertThat(annotations.stream().map(Noninherited::value)).containsExactly("A", - "B", "C"); + TYPE_HIERARCHY, NoninheritedRepeatableClass.class); + assertThat(annotations.stream().map(Noninherited::value)).containsExactly("A", "B", "C"); } @Test void typeHierarchyAnnotationsWhenNoninheritedComposedRepeatableOnSuperclassReturnsAnnotations() { Set annotations = getAnnotations(null, Noninherited.class, - SearchStrategy.TYPE_HIERARCHY, SubNoninheritedRepeatableClass.class); - assertThat(annotations.stream().map(Noninherited::value)).containsExactly("A", - "B", "C"); + TYPE_HIERARCHY, SubNoninheritedRepeatableClass.class); + assertThat(annotations.stream().map(Noninherited::value)).containsExactly("A", "B", "C"); } @Test void typeHierarchyAnnotationsWithLocalComposedAnnotationWhoseRepeatableMetaAnnotationsAreFiltered() { Class element = WithRepeatedMetaAnnotationsClass.class; - SearchStrategy searchStrategy = SearchStrategy.TYPE_HIERARCHY; + SearchStrategy searchStrategy = TYPE_HIERARCHY; AnnotationFilter annotationFilter = PeteRepeat.class.getName()::equals; Set annotations = getAnnotations(null, PeteRepeat.class, searchStrategy, element, annotationFilter); @@ -255,32 +241,37 @@ class MergedAnnotationsRepeatableAnnotationTests { } private void nonRepeatableRequirements(Exception ex) { - assertThat(ex.getMessage()).startsWith( - "Annotation type must be a repeatable annotation").contains( - "failed to resolve container type for", - NonRepeatable.class.getName()); + assertThat(ex) + .hasMessageStartingWith("Annotation type must be a repeatable annotation") + .hasMessageContaining("failed to resolve container type for", NonRepeatable.class.getName()); } private void missingValueAttributeRequirements(Exception ex) { - assertThat(ex.getMessage()).startsWith( - "Invalid declaration of container type").contains( + assertThat(ex) + .hasMessageStartingWith("Invalid declaration of container type") + .hasMessageContaining( ContainerMissingValueAttribute.class.getName(), - "for repeatable annotation", InvalidRepeatable.class.getName()); - assertThat(ex).hasCauseInstanceOf(NoSuchMethodException.class); + "for repeatable annotation", + InvalidRepeatable.class.getName()) + .hasCauseInstanceOf(NoSuchMethodException.class); } private void nonArrayValueAttributeRequirements(Exception ex) { - assertThat(ex.getMessage()).startsWith("Container type").contains( - ContainerWithNonArrayValueAttribute.class.getName(), - "must declare a 'value' attribute for an array of type", - InvalidRepeatable.class.getName()); + assertThat(ex) + .hasMessageStartingWith("Container type") + .hasMessageContaining( + ContainerWithNonArrayValueAttribute.class.getName(), + "must declare a 'value' attribute for an array of type", + InvalidRepeatable.class.getName()); } private void wrongComponentTypeRequirements(Exception ex) { - assertThat(ex.getMessage()).startsWith("Container type").contains( - ContainerWithArrayValueAttributeButWrongComponentType.class.getName(), - "must declare a 'value' attribute for an array of type", - InvalidRepeatable.class.getName()); + assertThat(ex) + .hasMessageStartingWith("Container type") + .hasMessageContaining( + ContainerWithArrayValueAttributeButWrongComponentType.class.getName(), + "must declare a 'value' attribute for an array of type", + InvalidRepeatable.class.getName()); } private static ThrowableTypeAssert assertThatAnnotationConfigurationException() { @@ -289,33 +280,28 @@ class MergedAnnotationsRepeatableAnnotationTests { @Retention(RetentionPolicy.RUNTIME) @interface NonRepeatable { - } @Retention(RetentionPolicy.RUNTIME) @interface ContainerMissingValueAttribute { // InvalidRepeatable[] value(); - } @Retention(RetentionPolicy.RUNTIME) @interface ContainerWithNonArrayValueAttribute { InvalidRepeatable value(); - } @Retention(RetentionPolicy.RUNTIME) @interface ContainerWithArrayValueAttributeButWrongComponentType { String[] value(); - } @Retention(RetentionPolicy.RUNTIME) @interface InvalidRepeatable { - } @Retention(RetentionPolicy.RUNTIME) @@ -323,7 +309,6 @@ class MergedAnnotationsRepeatableAnnotationTests { @interface PeteRepeats { PeteRepeat[] value(); - } @Retention(RetentionPolicy.RUNTIME) @@ -332,7 +317,6 @@ class MergedAnnotationsRepeatableAnnotationTests { @interface PeteRepeat { String value(); - } @PeteRepeat("shadowed") @@ -343,7 +327,6 @@ class MergedAnnotationsRepeatableAnnotationTests { @AliasFor(annotation = PeteRepeat.class) String value(); - } @PeteRepeat("shadowed") @@ -354,7 +337,6 @@ class MergedAnnotationsRepeatableAnnotationTests { @AliasFor(annotation = PeteRepeat.class) String value(); - } @PeteRepeats({ @PeteRepeat("B"), @PeteRepeat("C") }) @@ -362,37 +344,31 @@ class MergedAnnotationsRepeatableAnnotationTests { @Retention(RetentionPolicy.RUNTIME) @Inherited @interface ComposedContainer { - } @PeteRepeat("A") @PeteRepeats({ @PeteRepeat("B"), @PeteRepeat("C") }) static class RepeatableClass { - } static class SubRepeatableClass extends RepeatableClass { - } @ForPetesSake("B") @ForTheLoveOfFoo("C") @PeteRepeat("A") static class ComposedRepeatableClass { - } @ForPetesSake("C") @PeteRepeats(@PeteRepeat("A")) @PeteRepeat("B") static class ComposedRepeatableMixedWithContainerClass { - } @PeteRepeat("A") @ComposedContainer static class ComposedContainerClass { - } @Target(ElementType.TYPE) @@ -400,7 +376,6 @@ class MergedAnnotationsRepeatableAnnotationTests { @interface Noninheriteds { Noninherited[] value(); - } @Target(ElementType.TYPE) @@ -413,7 +388,6 @@ class MergedAnnotationsRepeatableAnnotationTests { @AliasFor("value") String name() default ""; - } @Noninherited(name = "shadowed") @@ -423,17 +397,14 @@ class MergedAnnotationsRepeatableAnnotationTests { @AliasFor(annotation = Noninherited.class) String name() default ""; - } @ComposedNoninherited(name = "C") @Noninheriteds({ @Noninherited(value = "A"), @Noninherited(name = "B") }) static class NoninheritedRepeatableClass { - } static class SubNoninheritedRepeatableClass extends NoninheritedRepeatableClass { - } @Retention(RetentionPolicy.RUNTIME)