From 0b08246760fc5aee5eb4d5fc25ce3a512d6bab16 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Tue, 13 Dec 2022 14:42:26 +0100 Subject: [PATCH] Revise RepeatableContainersTests --- .../annotation/RepeatableContainersTests.java | 272 ++++++++---------- 1 file changed, 127 insertions(+), 145 deletions(-) diff --git a/spring-core/src/test/java/org/springframework/core/annotation/RepeatableContainersTests.java b/spring-core/src/test/java/org/springframework/core/annotation/RepeatableContainersTests.java index b3a9860394a2..7946b1dc4fc4 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/RepeatableContainersTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/RepeatableContainersTests.java @@ -20,7 +20,9 @@ import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -31,189 +33,162 @@ * Tests for {@link RepeatableContainers}. * * @author Phillip Webb + * @author Sam Brannen */ class RepeatableContainersTests { - @Test - void standardRepeatablesWhenNonRepeatableReturnsNull() { - Object[] values = findRepeatedAnnotationValues( - RepeatableContainers.standardRepeatables(), WithNonRepeatable.class, - NonRepeatable.class); - assertThat(values).isNull(); - } + @Nested + class StandardRepeatableContainersTests { - @Test - void standardRepeatablesWhenSingleReturnsNull() { - Object[] values = findRepeatedAnnotationValues( - RepeatableContainers.standardRepeatables(), - WithSingleStandardRepeatable.class, StandardRepeatable.class); - assertThat(values).isNull(); - } + @Test + void standardRepeatablesWhenNonRepeatableReturnsNull() { + Object[] values = findRepeatedAnnotationValues(RepeatableContainers.standardRepeatables(), + NonRepeatableTestCase.class, NonRepeatable.class); + assertThat(values).isNull(); + } - @Test - void standardRepeatablesWhenContainerReturnsRepeats() { - Object[] values = findRepeatedAnnotationValues( - RepeatableContainers.standardRepeatables(), WithStandardRepeatables.class, - StandardContainer.class); - assertThat(values).containsExactly("a", "b"); - } + @Test + void standardRepeatablesWhenSingleReturnsNull() { + Object[] values = findRepeatedAnnotationValues(RepeatableContainers.standardRepeatables(), + SingleStandardRepeatableTestCase.class, StandardRepeatable.class); + assertThat(values).isNull(); + } - @Test - void standardRepeatablesWhenContainerButNotRepeatableReturnsNull() { - Object[] values = findRepeatedAnnotationValues( - RepeatableContainers.standardRepeatables(), WithExplicitRepeatables.class, - ExplicitContainer.class); - assertThat(values).isNull(); - } + @Test + void standardRepeatablesWhenContainerButNotRepeatableReturnsNull() { + Object[] values = findRepeatedAnnotationValues(RepeatableContainers.standardRepeatables(), + ExplicitRepeatablesTestCase.class, ExplicitContainer.class); + assertThat(values).isNull(); + } - @Test - void ofExplicitWhenNonRepeatableReturnsNull() { - Object[] values = findRepeatedAnnotationValues( - RepeatableContainers.of(ExplicitRepeatable.class, - ExplicitContainer.class), - WithNonRepeatable.class, NonRepeatable.class); - assertThat(values).isNull(); + @Test + void standardRepeatablesWhenContainerReturnsRepeats() { + Object[] values = findRepeatedAnnotationValues(RepeatableContainers.standardRepeatables(), + StandardRepeatablesTestCase.class, StandardContainer.class); + assertThat(values).containsExactly("a", "b"); + } } - @Test - void ofExplicitWhenStandardRepeatableContainerReturnsNull() { - Object[] values = findRepeatedAnnotationValues( - RepeatableContainers.of(ExplicitRepeatable.class, - ExplicitContainer.class), - WithStandardRepeatables.class, StandardContainer.class); - assertThat(values).isNull(); - } + @Nested + class ExplicitRepeatableContainerTests { - @Test - void ofExplicitWhenContainerReturnsRepeats() { - Object[] values = findRepeatedAnnotationValues( - RepeatableContainers.of(ExplicitRepeatable.class, - ExplicitContainer.class), - WithExplicitRepeatables.class, ExplicitContainer.class); - assertThat(values).containsExactly("a", "b"); - } + @Test + void ofExplicitWhenNonRepeatableReturnsNull() { + Object[] values = findRepeatedAnnotationValues( + RepeatableContainers.of(ExplicitRepeatable.class, ExplicitContainer.class), + NonRepeatableTestCase.class, NonRepeatable.class); + assertThat(values).isNull(); + } - @Test - void ofExplicitWhenHasNoValueThrowsException() { - assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() -> - RepeatableContainers.of(ExplicitRepeatable.class, InvalidNoValue.class)) - .withMessageContaining("Invalid declaration of container type [" - + InvalidNoValue.class.getName() - + "] for repeatable annotation [" - + ExplicitRepeatable.class.getName() + "]"); - } + @Test + void ofExplicitWhenStandardRepeatableContainerReturnsNull() { + Object[] values = findRepeatedAnnotationValues( + RepeatableContainers.of(ExplicitRepeatable.class, ExplicitContainer.class), + StandardRepeatablesTestCase.class, StandardContainer.class); + assertThat(values).isNull(); + } - @Test - void ofExplicitWhenValueIsNotArrayThrowsException() { - assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() -> - RepeatableContainers.of(ExplicitRepeatable.class, InvalidNotArray.class)) - .withMessage("Container type [" - + InvalidNotArray.class.getName() - + "] must declare a 'value' attribute for an array of type [" - + ExplicitRepeatable.class.getName() + "]"); - } + @Test + void ofExplicitWhenContainerReturnsRepeats() { + Object[] values = findRepeatedAnnotationValues( + RepeatableContainers.of(ExplicitRepeatable.class, ExplicitContainer.class), + ExplicitRepeatablesTestCase.class, ExplicitContainer.class); + assertThat(values).containsExactly("a", "b"); + } - @Test - void ofExplicitWhenValueIsArrayOfWrongTypeThrowsException() { - assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() -> - RepeatableContainers.of(ExplicitRepeatable.class, InvalidWrongArrayType.class)) - .withMessage("Container type [" - + InvalidWrongArrayType.class.getName() - + "] must declare a 'value' attribute for an array of type [" - + ExplicitRepeatable.class.getName() + "]"); - } + @Test + void ofExplicitWhenContainerIsNullDeducesContainer() { + Object[] values = findRepeatedAnnotationValues(RepeatableContainers.of(StandardRepeatable.class, null), + StandardRepeatablesTestCase.class, StandardContainer.class); + assertThat(values).containsExactly("a", "b"); + } - @Test - void ofExplicitWhenAnnotationIsNullThrowsException() { - assertThatIllegalArgumentException().isThrownBy(() -> - RepeatableContainers.of(null, null)) - .withMessage("Repeatable must not be null"); - } + @Test + void ofExplicitWhenHasNoValueThrowsException() { + assertThatExceptionOfType(AnnotationConfigurationException.class) + .isThrownBy(() -> RepeatableContainers.of(ExplicitRepeatable.class, InvalidNoValue.class)) + .withMessageContaining("Invalid declaration of container type [%s] for repeatable annotation [%s]", + InvalidNoValue.class.getName(), ExplicitRepeatable.class.getName()); + } - @Test - void ofExplicitWhenContainerIsNullDeducesContainer() { - Object[] values = findRepeatedAnnotationValues( - RepeatableContainers.of(StandardRepeatable.class, null), - WithStandardRepeatables.class, StandardContainer.class); - assertThat(values).containsExactly("a", "b"); - } + @Test + void ofExplicitWhenValueIsNotArrayThrowsException() { + assertThatExceptionOfType(AnnotationConfigurationException.class) + .isThrownBy(() -> RepeatableContainers.of(ExplicitRepeatable.class, InvalidNotArray.class)) + .withMessage("Container type [%s] must declare a 'value' attribute for an array of type [%s]", + InvalidNotArray.class.getName(), ExplicitRepeatable.class.getName()); + } + + @Test + void ofExplicitWhenValueIsArrayOfWrongTypeThrowsException() { + assertThatExceptionOfType(AnnotationConfigurationException.class) + .isThrownBy(() -> RepeatableContainers.of(ExplicitRepeatable.class, InvalidWrongArrayType.class)) + .withMessage("Container type [%s] must declare a 'value' attribute for an array of type [%s]", + InvalidWrongArrayType.class.getName(), ExplicitRepeatable.class.getName()); + } + + @Test + void ofExplicitWhenAnnotationIsNullThrowsException() { + assertThatIllegalArgumentException() + .isThrownBy(() -> RepeatableContainers.of(null, null)) + .withMessage("Repeatable must not be null"); + } + + @Test + void ofExplicitWhenContainerIsNullAndNotRepeatableThrowsException() { + assertThatIllegalArgumentException() + .isThrownBy(() -> RepeatableContainers.of(ExplicitRepeatable.class, null)) + .withMessage("Annotation type must be a repeatable annotation: failed to resolve container type for %s", + ExplicitRepeatable.class.getName()); + } - @Test - void ofExplicitWhenContainerIsNullAndNotRepeatableThrowsException() { - assertThatIllegalArgumentException().isThrownBy(() -> - RepeatableContainers.of(ExplicitRepeatable.class, null)) - .withMessage("Annotation type must be a repeatable annotation: " + - "failed to resolve container type for " + - ExplicitRepeatable.class.getName()); } @Test void standardAndExplicitReturnsRepeats() { - RepeatableContainers repeatableContainers = RepeatableContainers.standardRepeatables().and( - ExplicitContainer.class, ExplicitRepeatable.class); - assertThat(findRepeatedAnnotationValues(repeatableContainers, - WithStandardRepeatables.class, StandardContainer.class)).containsExactly( - "a", "b"); - assertThat(findRepeatedAnnotationValues(repeatableContainers, - WithExplicitRepeatables.class, ExplicitContainer.class)).containsExactly( - "a", "b"); + RepeatableContainers repeatableContainers = RepeatableContainers.standardRepeatables() + .and(ExplicitContainer.class, ExplicitRepeatable.class); + assertThat(findRepeatedAnnotationValues(repeatableContainers, StandardRepeatablesTestCase.class, StandardContainer.class)) + .containsExactly("a", "b"); + assertThat(findRepeatedAnnotationValues(repeatableContainers, ExplicitRepeatablesTestCase.class, ExplicitContainer.class)) + .containsExactly("a", "b"); } @Test void noneAlwaysReturnsNull() { - Object[] values = findRepeatedAnnotationValues( - RepeatableContainers.none(), WithStandardRepeatables.class, - StandardContainer.class); + Object[] values = findRepeatedAnnotationValues(RepeatableContainers.none(), StandardRepeatablesTestCase.class, + StandardContainer.class); assertThat(values).isNull(); } @Test void equalsAndHashcode() { - RepeatableContainers c1 = RepeatableContainers.of(ExplicitRepeatable.class, - ExplicitContainer.class); - RepeatableContainers c2 = RepeatableContainers.of(ExplicitRepeatable.class, - ExplicitContainer.class); + RepeatableContainers c1 = RepeatableContainers.of(ExplicitRepeatable.class, ExplicitContainer.class); + RepeatableContainers c2 = RepeatableContainers.of(ExplicitRepeatable.class, ExplicitContainer.class); RepeatableContainers c3 = RepeatableContainers.standardRepeatables(); - RepeatableContainers c4 = RepeatableContainers.standardRepeatables().and( - ExplicitContainer.class, ExplicitRepeatable.class); - assertThat(c1.hashCode()).isEqualTo(c2.hashCode()); + RepeatableContainers c4 = RepeatableContainers.standardRepeatables().and(ExplicitContainer.class, ExplicitRepeatable.class); + assertThat(c1).hasSameHashCodeAs(c2); assertThat(c1).isEqualTo(c1).isEqualTo(c2); assertThat(c1).isNotEqualTo(c3).isNotEqualTo(c4); } - private Object[] findRepeatedAnnotationValues(RepeatableContainers containers, + + private static Object[] findRepeatedAnnotationValues(RepeatableContainers containers, Class element, Class annotationType) { - Annotation[] annotations = containers.findRepeatedAnnotations( - element.getAnnotation(annotationType)); + Annotation[] annotations = containers.findRepeatedAnnotations(element.getAnnotation(annotationType)); return extractValues(annotations); } - private Object[] extractValues(Annotation[] annotations) { - try { - if (annotations == null) { - return null; - } - Object[] result = new String[annotations.length]; - for (int i = 0; i < annotations.length; i++) { - result[i] = annotations[i].annotationType().getMethod("value").invoke( - annotations[i]); - } - return result; - } - catch (Exception ex) { - throw new RuntimeException(ex); + private static Object[] extractValues(Annotation[] annotations) { + if (annotations == null) { + return null; } + return Arrays.stream(annotations).map(AnnotationUtils::getValue).toArray(Object[]::new); } - @Retention(RetentionPolicy.RUNTIME) - @interface NonRepeatable { - - String value() default ""; - } @Retention(RetentionPolicy.RUNTIME) - @Repeatable(StandardContainer.class) - @interface StandardRepeatable { + @interface NonRepeatable { String value() default ""; } @@ -225,7 +200,8 @@ private Object[] extractValues(Annotation[] annotations) { } @Retention(RetentionPolicy.RUNTIME) - @interface ExplicitRepeatable { + @Repeatable(StandardContainer.class) + @interface StandardRepeatable { String value() default ""; } @@ -236,6 +212,12 @@ private Object[] extractValues(Annotation[] annotations) { ExplicitRepeatable[] value(); } + @Retention(RetentionPolicy.RUNTIME) + @interface ExplicitRepeatable { + + String value() default ""; + } + @Retention(RetentionPolicy.RUNTIME) @interface InvalidNoValue { } @@ -253,20 +235,20 @@ private Object[] extractValues(Annotation[] annotations) { } @NonRepeatable("a") - static class WithNonRepeatable { + static class NonRepeatableTestCase { } @StandardRepeatable("a") - static class WithSingleStandardRepeatable { + static class SingleStandardRepeatableTestCase { } @StandardRepeatable("a") @StandardRepeatable("b") - static class WithStandardRepeatables { + static class StandardRepeatablesTestCase { } @ExplicitContainer({ @ExplicitRepeatable("a"), @ExplicitRepeatable("b") }) - static class WithExplicitRepeatables { + static class ExplicitRepeatablesTestCase { } }