Skip to content

Commit

Permalink
Allow repeating @…Source annotations when used as meta annotations
Browse files Browse the repository at this point in the history
Fixes #4063.
  • Loading branch information
marcphilipp committed Oct 9, 2024
1 parent eba399e commit 4ff42a7
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1096,65 +1096,73 @@ 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")))) //
.haveExactly(1, event(test(), displayName("[5] column1=FRUIT = apple, column2=RANK = 1"),
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")))) //
.haveExactly(1,
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")))) //
.haveExactly(1,
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")))) //
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -1799,7 +1807,7 @@ private static Stream<String> test() {
static class FieldSourceTestCase {

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Retention(RUNTIME)
@ParameterizedTest(name = "{arguments}")
@FieldSource
@interface FieldSourceTest {
Expand Down Expand Up @@ -2067,20 +2075,56 @@ 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" })
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)
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 {
}

Expand All @@ -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<Arguments> someArgumentsMethodSource() {
return Stream.of(Arguments.of("some"));
}
Expand All @@ -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<String> someArgumentsContainer = List.of("some");
static List<String> otherArgumentsContainer = List.of("other");

Expand All @@ -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")
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand All @@ -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());

Expand Down

0 comments on commit 4ff42a7

Please sign in to comment.