diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.3.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.3.adoc index 63fb2973d97b..c8ed02a67ceb 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.3.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.3.adoc @@ -37,6 +37,8 @@ on GitHub. * Extensions can once again be registered via multiple `@ExtendWith` meta-annotations on the same composed annotation on a field within a test class. +* All `@...Source` annotations of parameterized tests can now also be repeated when used + as meta annotations. [[release-notes-5.11.3-junit-jupiter-deprecations-and-breaking-changes]] diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ArgumentsSources.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ArgumentsSources.java index a5673bae9d87..ea7dd0494da6 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ArgumentsSources.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ArgumentsSources.java @@ -31,7 +31,7 @@ * @since 5.0 * @see org.junit.jupiter.params.provider.ArgumentsSource */ -@Target(ElementType.METHOD) +@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @API(status = STABLE, since = "5.7") diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvFileSources.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvFileSources.java index bc6bf3503fc9..92928d0ff715 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvFileSources.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvFileSources.java @@ -32,7 +32,7 @@ * @see CsvFileSource * @see java.lang.annotation.Repeatable */ -@Target(ElementType.METHOD) +@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @API(status = STABLE, since = "5.11") diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvSources.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvSources.java index 6c6951a75beb..297a4a8ddda5 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvSources.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvSources.java @@ -32,7 +32,7 @@ * @see CsvSource * @see java.lang.annotation.Repeatable */ -@Target(ElementType.METHOD) +@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @API(status = STABLE, since = "5.11") diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EnumSources.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EnumSources.java index 22feb5aa46d6..6b1a30a68ed5 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EnumSources.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EnumSources.java @@ -32,7 +32,7 @@ * @see EnumSource * @see java.lang.annotation.Repeatable */ -@Target(ElementType.METHOD) +@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @API(status = STABLE, since = "5.11") diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/FieldSources.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/FieldSources.java index 0b46746db5e4..36e9cd57d604 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/FieldSources.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/FieldSources.java @@ -32,7 +32,7 @@ * @see FieldSource * @see java.lang.annotation.Repeatable */ -@Target(ElementType.METHOD) +@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @API(status = EXPERIMENTAL, since = "5.11") diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodSources.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodSources.java index 056453f29820..33fa077567ee 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodSources.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodSources.java @@ -32,7 +32,7 @@ * @see MethodSource * @see java.lang.annotation.Repeatable */ -@Target(ElementType.METHOD) +@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @API(status = STABLE, since = "5.11") diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ValueSources.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ValueSources.java index 8db4dcc5b01f..9b3a489c6a66 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ValueSources.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ValueSources.java @@ -32,7 +32,7 @@ * @see ValueSource * @see java.lang.annotation.Repeatable */ -@Target(ElementType.METHOD) +@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @API(status = STABLE, since = "5.11") diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java index 9fdd334754ed..7733c8fc5104 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java @@ -10,6 +10,7 @@ package org.junit.jupiter.params; +import static java.lang.annotation.RetentionPolicy.RUNTIME; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.within; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -40,7 +41,6 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.ArrayList; import java.util.Arrays; @@ -1093,9 +1093,10 @@ private EngineExecutionResults execute(String methodName, Class... methodPara @Nested class RepeatableSourcesIntegrationTests { - @Test - void executesWithRepeatableCsvFileSource() { - var results = execute("testWithRepeatableCsvFileSource", String.class, String.class); + @ParameterizedTest + @ValueSource(strings = { "testWithRepeatableCsvFileSource", "testWithRepeatableCsvFileSourceAsMetaAnnotation" }) + void executesWithRepeatableCsvFileSource(String methodName) { + var results = execute(methodName, String.class, String.class); results.allEvents().assertThatEvents() // .haveExactly(1, event(test(), displayName("[1] column1=foo, column2=1"), finishedWithFailure(message("foo 1")))) // @@ -1103,17 +1104,19 @@ void executesWithRepeatableCsvFileSource() { finishedWithFailure(message("apple 1")))); } - @Test - void executesWithRepeatableCsvSource() { - var results = execute("testWithRepeatableCsvSource", String.class); + @ParameterizedTest + @ValueSource(strings = { "testWithRepeatableCsvSource", "testWithRepeatableCsvSourceAsMetaAnnotation" }) + void executesWithRepeatableCsvSource(String methodName) { + var results = execute(methodName, String.class); results.allEvents().assertThatEvents() // .haveExactly(1, event(test(), displayName("[1] argument=a"), finishedWithFailure(message("a")))) // .haveExactly(1, event(test(), displayName("[2] argument=b"), finishedWithFailure(message("b")))); } - @Test - void executesWithRepeatableMethodSource() { - var results = execute("testWithRepeatableMethodSource", String.class); + @ParameterizedTest + @ValueSource(strings = { "testWithRepeatableMethodSource", "testWithRepeatableMethodSourceAsMetaAnnotation" }) + void executesWithRepeatableMethodSource(String methodName) { + var results = execute(methodName, String.class); results.allEvents().assertThatEvents() // .haveExactly(1, event(test(), displayName("[1] argument=some"), finishedWithFailure(message("some")))) // @@ -1121,27 +1124,30 @@ void executesWithRepeatableMethodSource() { event(test(), displayName("[2] argument=other"), finishedWithFailure(message("other")))); } - @Test - void executesWithRepeatableEnumSource() { - var results = execute("testWithRepeatableEnumSource", Action.class); + @ParameterizedTest + @ValueSource(strings = { "testWithRepeatableEnumSource", "testWithRepeatableEnumSourceAsMetaAnnotation" }) + void executesWithRepeatableEnumSource(String methodName) { + var results = execute(methodName, Action.class); results.allEvents().assertThatEvents() // .haveExactly(1, event(test(), displayName("[1] argument=FOO"), finishedWithFailure(message("FOO")))) // .haveExactly(1, event(test(), displayName("[2] argument=BAR"), finishedWithFailure(message("BAR")))); } - @Test - void executesWithRepeatableValueSource() { - var results = execute("testWithRepeatableValueSource", String.class); + @ParameterizedTest + @ValueSource(strings = { "testWithRepeatableValueSource", "testWithRepeatableValueSourceAsMetaAnnotation" }) + void executesWithRepeatableValueSource(String methodName) { + var results = execute(methodName, String.class); results.allEvents().assertThatEvents() // .haveExactly(1, event(test(), displayName("[1] argument=foo"), finishedWithFailure(message("foo")))) // .haveExactly(1, event(test(), displayName("[2] argument=bar"), finishedWithFailure(message("bar")))); } - @Test - void executesWithRepeatableFieldSource() { - var results = execute("testWithRepeatableFieldSource", String.class); + @ParameterizedTest + @ValueSource(strings = { "testWithRepeatableFieldSource", "testWithRepeatableFieldSourceAsMetaAnnotation" }) + void executesWithRepeatableFieldSource(String methodName) { + var results = execute(methodName, String.class); results.allEvents().assertThatEvents() // .haveExactly(1, event(test(), displayName("[1] argument=some"), finishedWithFailure(message("some")))) // @@ -1149,9 +1155,11 @@ void executesWithRepeatableFieldSource() { event(test(), displayName("[2] argument=other"), finishedWithFailure(message("other")))); } - @Test - void executesWithRepeatableArgumentsSource() { - var results = execute("testWithRepeatableArgumentsSource", String.class); + @ParameterizedTest + @ValueSource(strings = { "testWithRepeatableArgumentsSource", + "testWithRepeatableArgumentsSourceAsMetaAnnotation" }) + void executesWithRepeatableArgumentsSource(String methodName) { + var results = execute(methodName, String.class); results.allEvents().assertThatEvents() // .haveExactly(1, event(test(), displayName("[1] argument=foo"), finishedWithFailure(message("foo")))) // .haveExactly(1, event(test(), displayName("[2] argument=bar"), finishedWithFailure(message("bar")))) // @@ -1542,7 +1550,7 @@ void testWithNullAndEmptySourceForTwoDimensionalStringArray(String[][] argument) static class MethodSourceTestCase { @Target(ElementType.METHOD) - @Retention(RetentionPolicy.RUNTIME) + @Retention(RUNTIME) @ParameterizedTest(name = "{arguments}") @MethodSource @interface MethodSourceTest { @@ -1767,7 +1775,7 @@ private static Stream test() { static class FieldSourceTestCase { @Target(ElementType.METHOD) - @Retention(RetentionPolicy.RUNTIME) + @Retention(RUNTIME) @ParameterizedTest(name = "{arguments}") @FieldSource @interface FieldSourceTest { @@ -2035,6 +2043,18 @@ void testWithRepeatableCsvFileSource(String column1, String column2) { fail("%s %s".formatted(column1, column2)); } + @CsvFileSource(resources = "two-column.csv") + @CsvFileSource(resources = "two-column-with-headers.csv", delimiter = '|', useHeadersInDisplayName = true, nullValues = "NIL") + @Retention(RUNTIME) + @interface TwoCsvFileSources { + } + + @ParameterizedTest + @TwoCsvFileSources + void testWithRepeatableCsvFileSourceAsMetaAnnotation(String column1, String column2) { + fail("%s %s".formatted(column1, column2)); + } + @ParameterizedTest @CsvSource({ "a" }) @CsvSource({ "b" }) @@ -2042,6 +2062,18 @@ void testWithRepeatableCsvSource(String argument) { fail(argument); } + @CsvSource({ "a" }) + @CsvSource({ "b" }) + @Retention(RUNTIME) + @interface TwoCsvSources { + } + + @ParameterizedTest + @TwoCsvSources + void testWithRepeatableCsvSourceAsMetaAnnotation(String argument) { + fail(argument); + } + @ParameterizedTest @EnumSource(SmartAction.class) @EnumSource(QuickAction.class) @@ -2049,6 +2081,18 @@ void testWithRepeatableEnumSource(Action argument) { fail(argument.toString()); } + @EnumSource(SmartAction.class) + @EnumSource(QuickAction.class) + @Retention(RUNTIME) + @interface TwoEnumSources { + } + + @ParameterizedTest + @TwoEnumSources + void testWithRepeatableEnumSourceAsMetaAnnotation(Action argument) { + fail(argument.toString()); + } + interface Action { } @@ -2067,6 +2111,18 @@ void testWithRepeatableMethodSource(String argument) { fail(argument); } + @MethodSource("someArgumentsMethodSource") + @MethodSource("otherArgumentsMethodSource") + @Retention(RUNTIME) + @interface TwoMethodSources { + } + + @ParameterizedTest + @TwoMethodSources + void testWithRepeatableMethodSourceAsMetaAnnotation(String argument) { + fail(argument); + } + public static Stream someArgumentsMethodSource() { return Stream.of(Arguments.of("some")); } @@ -2082,6 +2138,18 @@ void testWithRepeatableFieldSource(String argument) { fail(argument); } + @FieldSource("someArgumentsContainer") + @FieldSource("otherArgumentsContainer") + @Retention(RUNTIME) + @interface TwoFieldSources { + } + + @ParameterizedTest + @TwoFieldSources + void testWithRepeatableFieldSourceAsMetaAnnotation(String argument) { + fail(argument); + } + static List someArgumentsContainer = List.of("some"); static List otherArgumentsContainer = List.of("other"); @@ -2092,6 +2160,18 @@ void testWithRepeatableValueSource(String argument) { fail(argument); } + @ValueSource(strings = "foo") + @ValueSource(strings = "bar") + @Retention(RUNTIME) + @interface TwoValueSources { + } + + @ParameterizedTest + @TwoValueSources + void testWithRepeatableValueSourceAsMetaAnnotation(String argument) { + fail(argument); + } + @ParameterizedTest @ValueSource(strings = "foo") @ValueSource(strings = "foo") @@ -2117,6 +2197,18 @@ void testWithDifferentRepeatableAnnotations(String argument) { void testWithRepeatableArgumentsSource(String argument) { fail(argument); } + + @ArgumentsSource(TwoSingleStringArgumentsProvider.class) + @ArgumentsSource(TwoUnusedStringArgumentsProvider.class) + @Retention(RUNTIME) + @interface TwoArgumentsSources { + } + + @ParameterizedTest + @TwoArgumentsSources + void testWithRepeatableArgumentsSourceAsMetaAnnotation(String argument) { + fail(argument); + } } private static class TwoSingleStringArgumentsProvider implements ArgumentsProvider { diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ArchUnitTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ArchUnitTests.java index deef517d9f3b..538d0397add3 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ArchUnitTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ArchUnitTests.java @@ -19,7 +19,6 @@ import static com.tngtech.archunit.core.domain.JavaClass.Predicates.simpleName; import static com.tngtech.archunit.core.domain.JavaClass.Predicates.type; import static com.tngtech.archunit.core.domain.JavaModifier.PUBLIC; -import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.annotatedWith; import static com.tngtech.archunit.core.domain.properties.HasModifiers.Predicates.modifier; import static com.tngtech.archunit.core.domain.properties.HasName.Predicates.name; import static com.tngtech.archunit.core.domain.properties.HasName.Predicates.nameContaining; @@ -54,7 +53,6 @@ import org.apiguardian.api.API; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.provider.ArgumentsSource; @Order(Integer.MAX_VALUE) @AnalyzeClasses(locations = ArchUnitTests.AllJars.class) @@ -77,7 +75,6 @@ class ArchUnitTests { .and().areAnnotations() // .and().areAnnotatedWith(Repeatable.class) // .and(are(not(type(ExtendWith.class)))) // to be resolved in https://github.com/junit-team/junit5/issues/4059 - .and(are(not(type(ArgumentsSource.class).or(annotatedWith(ArgumentsSource.class))))) // to be resolved in https://github.com/junit-team/junit5/issues/4063 .should(haveContainerAnnotationWithSameRetentionPolicy()) // .andShould(haveContainerAnnotationWithSameTargetTypes());