From 4ff42a7069c58c6fa7d481283cef30266440645e Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Wed, 9 Oct 2024 16:52:43 +0200 Subject: [PATCH] =?UTF-8?q?Allow=20repeating=20`@=E2=80=A6Source`=20annota?= =?UTF-8?q?tions=20when=20used=20as=20meta=20annotations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #4063. --- .../release-notes/release-notes-5.11.3.adoc | 2 + .../params/provider/ArgumentsSources.java | 2 +- .../params/provider/CsvFileSources.java | 2 +- .../jupiter/params/provider/CsvSources.java | 2 +- .../jupiter/params/provider/EnumSources.java | 2 +- .../jupiter/params/provider/FieldSources.java | 2 +- .../params/provider/MethodSources.java | 2 +- .../jupiter/params/provider/ValueSources.java | 2 +- .../ParameterizedTestIntegrationTests.java | 140 +++++++++++++++--- .../tooling/support/tests/ArchUnitTests.java | 3 - 10 files changed, 125 insertions(+), 34 deletions(-) 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 4ff8ba90e1e5..56418a7a84f9 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.lang.reflect.Constructor; import java.util.ArrayList; @@ -1096,9 +1096,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")))) // @@ -1106,17 +1107,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")))) // @@ -1124,27 +1127,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")))) // @@ -1152,9 +1158,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")))) // @@ -1574,7 +1582,7 @@ void testWithNullAndEmptySourceForTwoDimensionalStringArray(String[][] argument) static class MethodSourceTestCase { @Target(ElementType.METHOD) - @Retention(RetentionPolicy.RUNTIME) + @Retention(RUNTIME) @ParameterizedTest(name = "{arguments}") @MethodSource @interface MethodSourceTest { @@ -1799,7 +1807,7 @@ private static Stream test() { static class FieldSourceTestCase { @Target(ElementType.METHOD) - @Retention(RetentionPolicy.RUNTIME) + @Retention(RUNTIME) @ParameterizedTest(name = "{arguments}") @FieldSource @interface FieldSourceTest { @@ -2067,6 +2075,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" }) @@ -2074,6 +2094,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) @@ -2081,6 +2113,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 { } @@ -2099,6 +2143,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")); } @@ -2114,6 +2170,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"); @@ -2124,6 +2192,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") @@ -2149,6 +2229,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); + } } static class SpiParameterInjectionTestCase { 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());