|
|
|
|
@ -20,7 +20,9 @@ import java.lang.annotation.Annotation;
@@ -20,7 +20,9 @@ import java.lang.annotation.Annotation;
|
|
|
|
|
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 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
@@ -31,189 +33,162 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
|
|
|
|
* 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<? extends Annotation> 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 @@ class RepeatableContainersTests {
@@ -225,7 +200,8 @@ class RepeatableContainersTests {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Retention(RetentionPolicy.RUNTIME) |
|
|
|
|
@interface ExplicitRepeatable { |
|
|
|
|
@Repeatable(StandardContainer.class) |
|
|
|
|
@interface StandardRepeatable { |
|
|
|
|
|
|
|
|
|
String value() default ""; |
|
|
|
|
} |
|
|
|
|
@ -236,6 +212,12 @@ class RepeatableContainersTests {
@@ -236,6 +212,12 @@ class RepeatableContainersTests {
|
|
|
|
|
ExplicitRepeatable[] value(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Retention(RetentionPolicy.RUNTIME) |
|
|
|
|
@interface ExplicitRepeatable { |
|
|
|
|
|
|
|
|
|
String value() default ""; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Retention(RetentionPolicy.RUNTIME) |
|
|
|
|
@interface InvalidNoValue { |
|
|
|
|
} |
|
|
|
|
@ -253,20 +235,20 @@ class RepeatableContainersTests {
@@ -253,20 +235,20 @@ class RepeatableContainersTests {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@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 { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|