Skip to content

Commit 4ff42a7

Browse files
committed
Allow repeating @…Source annotations when used as meta annotations
Fixes #4063.
1 parent eba399e commit 4ff42a7

File tree

10 files changed

+125
-34
lines changed

10 files changed

+125
-34
lines changed

documentation/src/docs/asciidoc/release-notes/release-notes-5.11.3.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ on GitHub.
3737

3838
* Extensions can once again be registered via multiple `@ExtendWith` meta-annotations on
3939
the same composed annotation on a field within a test class.
40+
* All `@...Source` annotations of parameterized tests can now also be repeated when used
41+
as meta annotations.
4042

4143

4244
[[release-notes-5.11.3-junit-jupiter-deprecations-and-breaking-changes]]

junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ArgumentsSources.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
* @since 5.0
3232
* @see org.junit.jupiter.params.provider.ArgumentsSource
3333
*/
34-
@Target(ElementType.METHOD)
34+
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
3535
@Retention(RetentionPolicy.RUNTIME)
3636
@Documented
3737
@API(status = STABLE, since = "5.7")

junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvFileSources.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
* @see CsvFileSource
3333
* @see java.lang.annotation.Repeatable
3434
*/
35-
@Target(ElementType.METHOD)
35+
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
3636
@Retention(RetentionPolicy.RUNTIME)
3737
@Documented
3838
@API(status = STABLE, since = "5.11")

junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvSources.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
* @see CsvSource
3333
* @see java.lang.annotation.Repeatable
3434
*/
35-
@Target(ElementType.METHOD)
35+
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
3636
@Retention(RetentionPolicy.RUNTIME)
3737
@Documented
3838
@API(status = STABLE, since = "5.11")

junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EnumSources.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
* @see EnumSource
3333
* @see java.lang.annotation.Repeatable
3434
*/
35-
@Target(ElementType.METHOD)
35+
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
3636
@Retention(RetentionPolicy.RUNTIME)
3737
@Documented
3838
@API(status = STABLE, since = "5.11")

junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/FieldSources.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
* @see FieldSource
3333
* @see java.lang.annotation.Repeatable
3434
*/
35-
@Target(ElementType.METHOD)
35+
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
3636
@Retention(RetentionPolicy.RUNTIME)
3737
@Documented
3838
@API(status = EXPERIMENTAL, since = "5.11")

junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodSources.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
* @see MethodSource
3333
* @see java.lang.annotation.Repeatable
3434
*/
35-
@Target(ElementType.METHOD)
35+
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
3636
@Retention(RetentionPolicy.RUNTIME)
3737
@Documented
3838
@API(status = STABLE, since = "5.11")

junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ValueSources.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
* @see ValueSource
3333
* @see java.lang.annotation.Repeatable
3434
*/
35-
@Target(ElementType.METHOD)
35+
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
3636
@Retention(RetentionPolicy.RUNTIME)
3737
@Documented
3838
@API(status = STABLE, since = "5.11")

jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java

Lines changed: 116 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
package org.junit.jupiter.params;
1212

13+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
1314
import static org.assertj.core.api.Assertions.assertThat;
1415
import static org.assertj.core.api.Assertions.within;
1516
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -40,7 +41,6 @@
4041

4142
import java.lang.annotation.ElementType;
4243
import java.lang.annotation.Retention;
43-
import java.lang.annotation.RetentionPolicy;
4444
import java.lang.annotation.Target;
4545
import java.lang.reflect.Constructor;
4646
import java.util.ArrayList;
@@ -1096,65 +1096,73 @@ private EngineExecutionResults execute(String methodName, Class<?>... methodPara
10961096
@Nested
10971097
class RepeatableSourcesIntegrationTests {
10981098

1099-
@Test
1100-
void executesWithRepeatableCsvFileSource() {
1101-
var results = execute("testWithRepeatableCsvFileSource", String.class, String.class);
1099+
@ParameterizedTest
1100+
@ValueSource(strings = { "testWithRepeatableCsvFileSource", "testWithRepeatableCsvFileSourceAsMetaAnnotation" })
1101+
void executesWithRepeatableCsvFileSource(String methodName) {
1102+
var results = execute(methodName, String.class, String.class);
11021103
results.allEvents().assertThatEvents() //
11031104
.haveExactly(1,
11041105
event(test(), displayName("[1] column1=foo, column2=1"), finishedWithFailure(message("foo 1")))) //
11051106
.haveExactly(1, event(test(), displayName("[5] column1=FRUIT = apple, column2=RANK = 1"),
11061107
finishedWithFailure(message("apple 1"))));
11071108
}
11081109

1109-
@Test
1110-
void executesWithRepeatableCsvSource() {
1111-
var results = execute("testWithRepeatableCsvSource", String.class);
1110+
@ParameterizedTest
1111+
@ValueSource(strings = { "testWithRepeatableCsvSource", "testWithRepeatableCsvSourceAsMetaAnnotation" })
1112+
void executesWithRepeatableCsvSource(String methodName) {
1113+
var results = execute(methodName, String.class);
11121114
results.allEvents().assertThatEvents() //
11131115
.haveExactly(1, event(test(), displayName("[1] argument=a"), finishedWithFailure(message("a")))) //
11141116
.haveExactly(1, event(test(), displayName("[2] argument=b"), finishedWithFailure(message("b"))));
11151117
}
11161118

1117-
@Test
1118-
void executesWithRepeatableMethodSource() {
1119-
var results = execute("testWithRepeatableMethodSource", String.class);
1119+
@ParameterizedTest
1120+
@ValueSource(strings = { "testWithRepeatableMethodSource", "testWithRepeatableMethodSourceAsMetaAnnotation" })
1121+
void executesWithRepeatableMethodSource(String methodName) {
1122+
var results = execute(methodName, String.class);
11201123
results.allEvents().assertThatEvents() //
11211124
.haveExactly(1,
11221125
event(test(), displayName("[1] argument=some"), finishedWithFailure(message("some")))) //
11231126
.haveExactly(1,
11241127
event(test(), displayName("[2] argument=other"), finishedWithFailure(message("other"))));
11251128
}
11261129

1127-
@Test
1128-
void executesWithRepeatableEnumSource() {
1129-
var results = execute("testWithRepeatableEnumSource", Action.class);
1130+
@ParameterizedTest
1131+
@ValueSource(strings = { "testWithRepeatableEnumSource", "testWithRepeatableEnumSourceAsMetaAnnotation" })
1132+
void executesWithRepeatableEnumSource(String methodName) {
1133+
var results = execute(methodName, Action.class);
11301134
results.allEvents().assertThatEvents() //
11311135
.haveExactly(1, event(test(), displayName("[1] argument=FOO"), finishedWithFailure(message("FOO")))) //
11321136
.haveExactly(1,
11331137
event(test(), displayName("[2] argument=BAR"), finishedWithFailure(message("BAR"))));
11341138
}
11351139

1136-
@Test
1137-
void executesWithRepeatableValueSource() {
1138-
var results = execute("testWithRepeatableValueSource", String.class);
1140+
@ParameterizedTest
1141+
@ValueSource(strings = { "testWithRepeatableValueSource", "testWithRepeatableValueSourceAsMetaAnnotation" })
1142+
void executesWithRepeatableValueSource(String methodName) {
1143+
var results = execute(methodName, String.class);
11391144
results.allEvents().assertThatEvents() //
11401145
.haveExactly(1, event(test(), displayName("[1] argument=foo"), finishedWithFailure(message("foo")))) //
11411146
.haveExactly(1,
11421147
event(test(), displayName("[2] argument=bar"), finishedWithFailure(message("bar"))));
11431148
}
11441149

1145-
@Test
1146-
void executesWithRepeatableFieldSource() {
1147-
var results = execute("testWithRepeatableFieldSource", String.class);
1150+
@ParameterizedTest
1151+
@ValueSource(strings = { "testWithRepeatableFieldSource", "testWithRepeatableFieldSourceAsMetaAnnotation" })
1152+
void executesWithRepeatableFieldSource(String methodName) {
1153+
var results = execute(methodName, String.class);
11481154
results.allEvents().assertThatEvents() //
11491155
.haveExactly(1,
11501156
event(test(), displayName("[1] argument=some"), finishedWithFailure(message("some")))) //
11511157
.haveExactly(1,
11521158
event(test(), displayName("[2] argument=other"), finishedWithFailure(message("other"))));
11531159
}
11541160

1155-
@Test
1156-
void executesWithRepeatableArgumentsSource() {
1157-
var results = execute("testWithRepeatableArgumentsSource", String.class);
1161+
@ParameterizedTest
1162+
@ValueSource(strings = { "testWithRepeatableArgumentsSource",
1163+
"testWithRepeatableArgumentsSourceAsMetaAnnotation" })
1164+
void executesWithRepeatableArgumentsSource(String methodName) {
1165+
var results = execute(methodName, String.class);
11581166
results.allEvents().assertThatEvents() //
11591167
.haveExactly(1, event(test(), displayName("[1] argument=foo"), finishedWithFailure(message("foo")))) //
11601168
.haveExactly(1, event(test(), displayName("[2] argument=bar"), finishedWithFailure(message("bar")))) //
@@ -1574,7 +1582,7 @@ void testWithNullAndEmptySourceForTwoDimensionalStringArray(String[][] argument)
15741582
static class MethodSourceTestCase {
15751583

15761584
@Target(ElementType.METHOD)
1577-
@Retention(RetentionPolicy.RUNTIME)
1585+
@Retention(RUNTIME)
15781586
@ParameterizedTest(name = "{arguments}")
15791587
@MethodSource
15801588
@interface MethodSourceTest {
@@ -1799,7 +1807,7 @@ private static Stream<String> test() {
17991807
static class FieldSourceTestCase {
18001808

18011809
@Target(ElementType.METHOD)
1802-
@Retention(RetentionPolicy.RUNTIME)
1810+
@Retention(RUNTIME)
18031811
@ParameterizedTest(name = "{arguments}")
18041812
@FieldSource
18051813
@interface FieldSourceTest {
@@ -2067,20 +2075,56 @@ void testWithRepeatableCsvFileSource(String column1, String column2) {
20672075
fail("%s %s".formatted(column1, column2));
20682076
}
20692077

2078+
@CsvFileSource(resources = "two-column.csv")
2079+
@CsvFileSource(resources = "two-column-with-headers.csv", delimiter = '|', useHeadersInDisplayName = true, nullValues = "NIL")
2080+
@Retention(RUNTIME)
2081+
@interface TwoCsvFileSources {
2082+
}
2083+
2084+
@ParameterizedTest
2085+
@TwoCsvFileSources
2086+
void testWithRepeatableCsvFileSourceAsMetaAnnotation(String column1, String column2) {
2087+
fail("%s %s".formatted(column1, column2));
2088+
}
2089+
20702090
@ParameterizedTest
20712091
@CsvSource({ "a" })
20722092
@CsvSource({ "b" })
20732093
void testWithRepeatableCsvSource(String argument) {
20742094
fail(argument);
20752095
}
20762096

2097+
@CsvSource({ "a" })
2098+
@CsvSource({ "b" })
2099+
@Retention(RUNTIME)
2100+
@interface TwoCsvSources {
2101+
}
2102+
2103+
@ParameterizedTest
2104+
@TwoCsvSources
2105+
void testWithRepeatableCsvSourceAsMetaAnnotation(String argument) {
2106+
fail(argument);
2107+
}
2108+
20772109
@ParameterizedTest
20782110
@EnumSource(SmartAction.class)
20792111
@EnumSource(QuickAction.class)
20802112
void testWithRepeatableEnumSource(Action argument) {
20812113
fail(argument.toString());
20822114
}
20832115

2116+
@EnumSource(SmartAction.class)
2117+
@EnumSource(QuickAction.class)
2118+
@Retention(RUNTIME)
2119+
@interface TwoEnumSources {
2120+
}
2121+
2122+
@ParameterizedTest
2123+
@TwoEnumSources
2124+
void testWithRepeatableEnumSourceAsMetaAnnotation(Action argument) {
2125+
fail(argument.toString());
2126+
}
2127+
20842128
interface Action {
20852129
}
20862130

@@ -2099,6 +2143,18 @@ void testWithRepeatableMethodSource(String argument) {
20992143
fail(argument);
21002144
}
21012145

2146+
@MethodSource("someArgumentsMethodSource")
2147+
@MethodSource("otherArgumentsMethodSource")
2148+
@Retention(RUNTIME)
2149+
@interface TwoMethodSources {
2150+
}
2151+
2152+
@ParameterizedTest
2153+
@TwoMethodSources
2154+
void testWithRepeatableMethodSourceAsMetaAnnotation(String argument) {
2155+
fail(argument);
2156+
}
2157+
21022158
public static Stream<Arguments> someArgumentsMethodSource() {
21032159
return Stream.of(Arguments.of("some"));
21042160
}
@@ -2114,6 +2170,18 @@ void testWithRepeatableFieldSource(String argument) {
21142170
fail(argument);
21152171
}
21162172

2173+
@FieldSource("someArgumentsContainer")
2174+
@FieldSource("otherArgumentsContainer")
2175+
@Retention(RUNTIME)
2176+
@interface TwoFieldSources {
2177+
}
2178+
2179+
@ParameterizedTest
2180+
@TwoFieldSources
2181+
void testWithRepeatableFieldSourceAsMetaAnnotation(String argument) {
2182+
fail(argument);
2183+
}
2184+
21172185
static List<String> someArgumentsContainer = List.of("some");
21182186
static List<String> otherArgumentsContainer = List.of("other");
21192187

@@ -2124,6 +2192,18 @@ void testWithRepeatableValueSource(String argument) {
21242192
fail(argument);
21252193
}
21262194

2195+
@ValueSource(strings = "foo")
2196+
@ValueSource(strings = "bar")
2197+
@Retention(RUNTIME)
2198+
@interface TwoValueSources {
2199+
}
2200+
2201+
@ParameterizedTest
2202+
@TwoValueSources
2203+
void testWithRepeatableValueSourceAsMetaAnnotation(String argument) {
2204+
fail(argument);
2205+
}
2206+
21272207
@ParameterizedTest
21282208
@ValueSource(strings = "foo")
21292209
@ValueSource(strings = "foo")
@@ -2149,6 +2229,18 @@ void testWithDifferentRepeatableAnnotations(String argument) {
21492229
void testWithRepeatableArgumentsSource(String argument) {
21502230
fail(argument);
21512231
}
2232+
2233+
@ArgumentsSource(TwoSingleStringArgumentsProvider.class)
2234+
@ArgumentsSource(TwoUnusedStringArgumentsProvider.class)
2235+
@Retention(RUNTIME)
2236+
@interface TwoArgumentsSources {
2237+
}
2238+
2239+
@ParameterizedTest
2240+
@TwoArgumentsSources
2241+
void testWithRepeatableArgumentsSourceAsMetaAnnotation(String argument) {
2242+
fail(argument);
2243+
}
21522244
}
21532245

21542246
static class SpiParameterInjectionTestCase {

platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ArchUnitTests.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import static com.tngtech.archunit.core.domain.JavaClass.Predicates.simpleName;
2020
import static com.tngtech.archunit.core.domain.JavaClass.Predicates.type;
2121
import static com.tngtech.archunit.core.domain.JavaModifier.PUBLIC;
22-
import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.annotatedWith;
2322
import static com.tngtech.archunit.core.domain.properties.HasModifiers.Predicates.modifier;
2423
import static com.tngtech.archunit.core.domain.properties.HasName.Predicates.name;
2524
import static com.tngtech.archunit.core.domain.properties.HasName.Predicates.nameContaining;
@@ -54,7 +53,6 @@
5453
import org.apiguardian.api.API;
5554
import org.junit.jupiter.api.Order;
5655
import org.junit.jupiter.api.extension.ExtendWith;
57-
import org.junit.jupiter.params.provider.ArgumentsSource;
5856

5957
@Order(Integer.MAX_VALUE)
6058
@AnalyzeClasses(locations = ArchUnitTests.AllJars.class)
@@ -77,7 +75,6 @@ class ArchUnitTests {
7775
.and().areAnnotations() //
7876
.and().areAnnotatedWith(Repeatable.class) //
7977
.and(are(not(type(ExtendWith.class)))) // to be resolved in https://github.com/junit-team/junit5/issues/4059
80-
.and(are(not(type(ArgumentsSource.class).or(annotatedWith(ArgumentsSource.class))))) // to be resolved in https://github.com/junit-team/junit5/issues/4063
8178
.should(haveContainerAnnotationWithSameRetentionPolicy()) //
8279
.andShould(haveContainerAnnotationWithSameTargetTypes());
8380

0 commit comments

Comments
 (0)