From 3f3aa297413834bcb76b123741342e839248e543 Mon Sep 17 00:00:00 2001 From: Mario khoury Date: Sun, 25 May 2025 00:30:55 +0200 Subject: [PATCH 01/13] Add List-based support to Arguments API (of, arguments, argumentSet, toList) --- .../jupiter/params/provider/Arguments.java | 72 +++++++++++++++++++ .../provider/EmptyArgumentsProvider.java | 2 +- .../params/provider/ArgumentsTests.java | 50 +++++++++++++ 3 files changed, 123 insertions(+), 1 deletion(-) diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java index b1492ec95188..2ca50ef59591 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java @@ -13,6 +13,10 @@ import static org.apiguardian.api.API.Status.EXPERIMENTAL; import static org.apiguardian.api.API.Status.STABLE; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + import org.apiguardian.api.API; import org.jspecify.annotations.Nullable; import org.junit.platform.commons.util.Preconditions; @@ -172,4 +176,72 @@ public String toString() { } + /** + * Factory method for creating an instance of {@code Arguments} based on + * the supplied {@code arguments} as a {@link List}. + * + * @param arguments the arguments as a List to be used for an invocation + * of the test method; must not be {@code null} but may contain {@code null} + * @return an instance of {@code Arguments}; never {@code null} + * @see #arguments(List) + */ + @API(status = EXPERIMENTAL, since = "6.0") + static Arguments of(@Nullable List<@Nullable Object> arguments) { + if (arguments == null) { + return of((Object) null); // Properly wrap null + } + if (arguments.isEmpty()) { + // Must still return empty arguments array + return of(new Object[0]); + } + return () -> arguments.toArray(new Object[0]); + } + + /** + * Factory method for creating an instance of {@code Arguments} based on + * the supplied {@code arguments} as a {@link List}. + * + *

This method is an alias for {@link Arguments#of} and is + * intended to be used when statically imported — for example, via: + * {@code import static org.junit.jupiter.params.provider.Arguments.arguments;} + * + * @param arguments the arguments as a List to be used for an invocation of the test + * method; must not be {@code null} but may contain {@code null} + * @return an instance of {@code Arguments}; never {@code null} + * @since 6.0 + * @see #argumentSet(String, Object...) + */ + @API(status = EXPERIMENTAL, since = "6.0") + static Arguments arguments(List<@Nullable Object> arguments) { + return of(arguments); + } + + /** + * Factory method for creating an {@link ArgumentSet} based on the supplied + * {@code name} and {@code arguments} as a List. + * + * @param name the name of the argument set; must not be {@code null} or blank + * @param arguments the arguments list to be used for an invocation of the test + * method; must not be {@code null} but may contain {@code null} + * @return an {@code ArgumentSet}; never {@code null} + * @since 6.0 + */ + @API(status = EXPERIMENTAL, since = "6.0") + static ArgumentSet argumentSet(String name, List<@Nullable Object> arguments) { + Preconditions.notBlank(name, "name must not be null or blank"); + Preconditions.notNull(arguments, "arguments list must not be null"); + return new ArgumentSet(name, arguments.toArray(new Object[0])); + } + + /** + * Convert the arguments to a mutable List. + * + * @return a mutable List of arguments; never {@code null} but may contain {@code null} + * @since 6.0 + */ + @API(status = EXPERIMENTAL, since = "6.0") + default List<@Nullable Object> toList() { + return new ArrayList<>(Arrays.asList(get())); + } + } diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EmptyArgumentsProvider.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EmptyArgumentsProvider.java index 9c9b4f5a8173..22af73d7596e 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EmptyArgumentsProvider.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EmptyArgumentsProvider.java @@ -57,7 +57,7 @@ public Stream provideArguments(ParameterDeclarations parame return Stream.of(arguments(Collections.emptySet())); } if (List.class.equals(parameterType)) { - return Stream.of(arguments(Collections.emptyList())); + return Stream.of(Arguments.of((Object) Collections.emptyList())); } if (Set.class.equals(parameterType)) { return Stream.of(arguments(Collections.emptySet())); diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java index 76dd591f7101..7a461baaecbb 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java @@ -15,7 +15,11 @@ import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.junit.jupiter.params.provider.Arguments.of; +import java.util.Arrays; +import java.util.List; + import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.provider.Arguments.ArgumentSet; /** * Unit tests for {@link Arguments}. @@ -56,4 +60,50 @@ void argumentsReturnsSameArrayUsedForCreating() { assertThat(arguments.get()).isSameAs(input); } + @Test + void ofSupportsList() { + List input = Arrays.asList(1, "two", null, 3.0); + Arguments arguments = Arguments.of(input); + + assertArrayEquals(new Object[] { 1, "two", null, 3.0 }, arguments.get()); + } + + @Test + void argumentsSupportsListAlias() { + List input = Arrays.asList("a", 2, null); + Arguments arguments = Arguments.arguments(input); + + assertArrayEquals(new Object[] { "a", 2, null }, arguments.get()); + } + + @Test + void argumentSetSupportsList() { + List input = Arrays.asList("x", null, 42); + ArgumentSet argumentSet = Arguments.argumentSet("list-test", input); + + assertArrayEquals(new Object[] { "x", null, 42 }, argumentSet.get()); + assertThat(argumentSet.getName()).isEqualTo("list-test"); + } + + @Test + void toListReturnsMutableListOfArguments() { + Arguments arguments = Arguments.of(Arrays.asList("a", 2, null)); + + List result = arguments.toList(); + + assertThat(result).containsExactly("a", 2, null); // preserves content + result.add("extra"); // confirms mutability + assertThat(result).contains("extra"); + } + + @Test + void toListWorksOnEmptyArguments() { + Arguments arguments = Arguments.of(Arrays.asList()); + + List result = arguments.toList(); + + assertThat(result).isEmpty(); + result.add("extra"); + assertThat(result).containsExactly("extra"); + } } From d566f959f5928db624bb23e720e9e480c8c0a7f1 Mon Sep 17 00:00:00 2001 From: Mario khoury Date: Sun, 25 May 2025 17:36:07 +0200 Subject: [PATCH 02/13] Address feedback: fix argumentsFrom to unpack List properly, update test accordingly --- .../jupiter/params/provider/Arguments.java | 132 +++++++++--------- .../provider/EmptyArgumentsProvider.java | 2 +- .../params/provider/ArgumentsTests.java | 10 +- 3 files changed, 73 insertions(+), 71 deletions(-) diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java index 2ca50ef59591..7268a78e0a79 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java @@ -129,53 +129,6 @@ static ArgumentSet argumentSet(String name, @Nullable Object... arguments) { return new ArgumentSet(name, arguments); } - /** - * Specialization of {@link Arguments} that associates a {@link #getName() name} - * with a set of {@link #get() arguments}. - * - * @since 5.11 - * @see Arguments#argumentSet(String, Object...) - * @see org.junit.jupiter.params.ParameterizedTest#ARGUMENT_SET_NAME_PLACEHOLDER - * @see org.junit.jupiter.params.ParameterizedTest#ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER - */ - @API(status = EXPERIMENTAL, since = "5.11") - final class ArgumentSet implements Arguments { - - private final String name; - - private final @Nullable Object[] arguments; - - private ArgumentSet(String name, @Nullable Object[] arguments) { - Preconditions.notBlank(name, "name must not be null or blank"); - Preconditions.notNull(arguments, "arguments array must not be null"); - this.name = name; - this.arguments = arguments; - } - - /** - * Get the name of this {@code ArgumentSet}. - * @return the name of this {@code ArgumentSet}; never {@code null} or blank - */ - public String getName() { - return this.name; - } - - @Override - public @Nullable Object[] get() { - return this.arguments; - } - - /** - * Return the {@link #getName() name} of this {@code ArgumentSet}. - * @return the name of this {@code ArgumentSet} - */ - @Override - public String toString() { - return getName(); - } - - } - /** * Factory method for creating an instance of {@code Arguments} based on * the supplied {@code arguments} as a {@link List}. @@ -183,27 +136,22 @@ public String toString() { * @param arguments the arguments as a List to be used for an invocation * of the test method; must not be {@code null} but may contain {@code null} * @return an instance of {@code Arguments}; never {@code null} - * @see #arguments(List) + * @since 6.0 + * @see #argumentsFrom(List) */ @API(status = EXPERIMENTAL, since = "6.0") - static Arguments of(@Nullable List<@Nullable Object> arguments) { - if (arguments == null) { - return of((Object) null); // Properly wrap null - } - if (arguments.isEmpty()) { - // Must still return empty arguments array - return of(new Object[0]); - } - return () -> arguments.toArray(new Object[0]); + static Arguments from(List<@Nullable Object> arguments) { + Preconditions.notNull(arguments, "arguments must not be null"); + return of(arguments.toArray()); } /** * Factory method for creating an instance of {@code Arguments} based on * the supplied {@code arguments} as a {@link List}. * - *

This method is an alias for {@link Arguments#of} and is + *

This method is an alias for {@link Arguments#from} and is * intended to be used when statically imported — for example, via: - * {@code import static org.junit.jupiter.params.provider.Arguments.arguments;} + * {@code import static org.junit.jupiter.params.provider.Arguments.argumentsFrom;} * * @param arguments the arguments as a List to be used for an invocation of the test * method; must not be {@code null} but may contain {@code null} @@ -212,29 +160,37 @@ static Arguments of(@Nullable List<@Nullable Object> arguments) { * @see #argumentSet(String, Object...) */ @API(status = EXPERIMENTAL, since = "6.0") - static Arguments arguments(List<@Nullable Object> arguments) { - return of(arguments); + static Arguments argumentsFrom(List<@Nullable Object> arguments) { + return from(arguments); } /** * Factory method for creating an {@link ArgumentSet} based on the supplied - * {@code name} and {@code arguments} as a List. + * {@code name} and {@code arguments} as a {@link List}. + * + *

This method is a convenient alternative to {@link #argumentSet(String, Object...)} + * when working with {@link List} based inputs. * * @param name the name of the argument set; must not be {@code null} or blank * @param arguments the arguments list to be used for an invocation of the test * method; must not be {@code null} but may contain {@code null} * @return an {@code ArgumentSet}; never {@code null} * @since 6.0 + * @see #argumentSet(String, Object...) */ @API(status = EXPERIMENTAL, since = "6.0") - static ArgumentSet argumentSet(String name, List<@Nullable Object> arguments) { + static ArgumentSet argumentSetFrom(String name, List<@Nullable Object> arguments) { Preconditions.notBlank(name, "name must not be null or blank"); Preconditions.notNull(arguments, "arguments list must not be null"); - return new ArgumentSet(name, arguments.toArray(new Object[0])); + return new ArgumentSet(name, arguments.toArray()); } /** - * Convert the arguments to a mutable List. + * Convert the arguments to a new mutable {@link List} containing the same + * elements as {@link #get()}. + * + *

This is useful for test logic that benefits from {@code List} operations such as filtering, + * transformation, or assertions. * * @return a mutable List of arguments; never {@code null} but may contain {@code null} * @since 6.0 @@ -244,4 +200,50 @@ static ArgumentSet argumentSet(String name, List<@Nullable Object> arguments) { return new ArrayList<>(Arrays.asList(get())); } + /** + * Specialization of {@link Arguments} that associates a {@link #getName() name} + * with a set of {@link #get() arguments}. + * + * @since 5.11 + * @see Arguments#argumentSet(String, Object...) + * @see org.junit.jupiter.params.ParameterizedTest#ARGUMENT_SET_NAME_PLACEHOLDER + * @see org.junit.jupiter.params.ParameterizedTest#ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER + */ + @API(status = EXPERIMENTAL, since = "5.11") + final class ArgumentSet implements Arguments { + + private final String name; + + private final @Nullable Object[] arguments; + + private ArgumentSet(String name, @Nullable Object[] arguments) { + Preconditions.notBlank(name, "name must not be null or blank"); + Preconditions.notNull(arguments, "arguments array must not be null"); + this.name = name; + this.arguments = arguments; + } + + /** + * Get the name of this {@code ArgumentSet}. + * @return the name of this {@code ArgumentSet}; never {@code null} or blank + */ + public String getName() { + return this.name; + } + + @Override + public @Nullable Object[] get() { + return this.arguments; + } + + /** + * Return the {@link #getName() name} of this {@code ArgumentSet}. + * @return the name of this {@code ArgumentSet} + */ + @Override + public String toString() { + return getName(); + } + + } } diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EmptyArgumentsProvider.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EmptyArgumentsProvider.java index 22af73d7596e..9c9b4f5a8173 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EmptyArgumentsProvider.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EmptyArgumentsProvider.java @@ -57,7 +57,7 @@ public Stream provideArguments(ParameterDeclarations parame return Stream.of(arguments(Collections.emptySet())); } if (List.class.equals(parameterType)) { - return Stream.of(Arguments.of((Object) Collections.emptyList())); + return Stream.of(arguments(Collections.emptyList())); } if (Set.class.equals(parameterType)) { return Stream.of(arguments(Collections.emptySet())); diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java index 7a461baaecbb..4440ed55741d 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java @@ -63,7 +63,7 @@ void argumentsReturnsSameArrayUsedForCreating() { @Test void ofSupportsList() { List input = Arrays.asList(1, "two", null, 3.0); - Arguments arguments = Arguments.of(input); + Arguments arguments = Arguments.from(input); assertArrayEquals(new Object[] { 1, "two", null, 3.0 }, arguments.get()); } @@ -71,7 +71,7 @@ void ofSupportsList() { @Test void argumentsSupportsListAlias() { List input = Arrays.asList("a", 2, null); - Arguments arguments = Arguments.arguments(input); + Arguments arguments = Arguments.argumentsFrom(input); assertArrayEquals(new Object[] { "a", 2, null }, arguments.get()); } @@ -79,7 +79,7 @@ void argumentsSupportsListAlias() { @Test void argumentSetSupportsList() { List input = Arrays.asList("x", null, 42); - ArgumentSet argumentSet = Arguments.argumentSet("list-test", input); + ArgumentSet argumentSet = Arguments.argumentSetFrom("list-test", input); assertArrayEquals(new Object[] { "x", null, 42 }, argumentSet.get()); assertThat(argumentSet.getName()).isEqualTo("list-test"); @@ -87,7 +87,7 @@ void argumentSetSupportsList() { @Test void toListReturnsMutableListOfArguments() { - Arguments arguments = Arguments.of(Arrays.asList("a", 2, null)); + Arguments arguments = Arguments.from(Arrays.asList("a", 2, null)); List result = arguments.toList(); @@ -98,7 +98,7 @@ void toListReturnsMutableListOfArguments() { @Test void toListWorksOnEmptyArguments() { - Arguments arguments = Arguments.of(Arrays.asList()); + Arguments arguments = Arguments.from(Arrays.asList()); List result = arguments.toList(); From b73ae1102ae54276c03422ad396914b050d321df Mon Sep 17 00:00:00 2001 From: Mario khoury Date: Tue, 27 May 2025 21:49:58 +0200 Subject: [PATCH 03/13] Address feedback: fix documentation and adding test cases --- .../jupiter/params/provider/Arguments.java | 39 ++++++++++--------- .../params/provider/ArgumentsTests.java | 34 ++++++++++++++-- 2 files changed, 51 insertions(+), 22 deletions(-) diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java index 7268a78e0a79..dbd1046de7c4 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java @@ -131,11 +131,11 @@ static ArgumentSet argumentSet(String name, @Nullable Object... arguments) { /** * Factory method for creating an instance of {@code Arguments} based on - * the supplied {@code arguments} as a {@link List}. + * the supplied {@link List} of {@code arguments}. * - * @param arguments the arguments as a List to be used for an invocation - * of the test method; must not be {@code null} but may contain {@code null} - * @return an instance of {@code Arguments}; never {@code null} + * @param arguments the arguments to be used for an invocation of the test + * method; must not be {@code null} but may contain {@code null}. + * @return an instance of {@code Arguments}; never {@code null}. * @since 6.0 * @see #argumentsFrom(List) */ @@ -147,15 +147,15 @@ static Arguments from(List<@Nullable Object> arguments) { /** * Factory method for creating an instance of {@code Arguments} based on - * the supplied {@code arguments} as a {@link List}. + * the supplied {@link List} of {@code arguments}. * *

This method is an alias for {@link Arguments#from} and is * intended to be used when statically imported — for example, via: * {@code import static org.junit.jupiter.params.provider.Arguments.argumentsFrom;} * - * @param arguments the arguments as a List to be used for an invocation of the test - * method; must not be {@code null} but may contain {@code null} - * @return an instance of {@code Arguments}; never {@code null} + * @param arguments the arguments to be used for an invocation of the test + * method; must not be {@code null} but may contain {@code null}. + * @return an instance of {@code Arguments}; never {@code null}. * @since 6.0 * @see #argumentSet(String, Object...) */ @@ -166,15 +166,17 @@ static Arguments argumentsFrom(List<@Nullable Object> arguments) { /** * Factory method for creating an {@link ArgumentSet} based on the supplied - * {@code name} and {@code arguments} as a {@link List}. + * {@code name} and {@link List} of {@code arguments}. * - *

This method is a convenient alternative to {@link #argumentSet(String, Object...)} - * when working with {@link List} based inputs. + *

This method is a convenient alternative to + * {@link #argumentSet(String, Object...)} when working with {@link List} + * based inputs. * - * @param name the name of the argument set; must not be {@code null} or blank - * @param arguments the arguments list to be used for an invocation of the test - * method; must not be {@code null} but may contain {@code null} - * @return an {@code ArgumentSet}; never {@code null} + * @param name the name of the argument set; must not be {@code null} + * or blank. + * @param arguments the arguments to be used for an invocation of the test + * method; must not be {@code null} but may contain {@code null}. + * @return an {@code ArgumentSet}; never {@code null}. * @since 6.0 * @see #argumentSet(String, Object...) */ @@ -189,10 +191,11 @@ static ArgumentSet argumentSetFrom(String name, List<@Nullable Object> arguments * Convert the arguments to a new mutable {@link List} containing the same * elements as {@link #get()}. * - *

This is useful for test logic that benefits from {@code List} operations such as filtering, - * transformation, or assertions. + *

This is useful for test logic that benefits from {@code List} + * operations such as filtering, transformation, or assertions. * - * @return a mutable List of arguments; never {@code null} but may contain {@code null} + * @return a mutable List of arguments; never {@code null} but may contain + * {@code null}. * @since 6.0 */ @API(status = EXPERIMENTAL, since = "6.0") diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java index 4440ed55741d..4ce0dbb61d28 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java @@ -15,6 +15,7 @@ import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.junit.jupiter.params.provider.Arguments.of; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -61,7 +62,7 @@ void argumentsReturnsSameArrayUsedForCreating() { } @Test - void ofSupportsList() { + void fromSupportsList() { List input = Arrays.asList(1, "two", null, 3.0); Arguments arguments = Arguments.from(input); @@ -69,7 +70,20 @@ void ofSupportsList() { } @Test - void argumentsSupportsListAlias() { + void fromSupportsListDefensiveCopy() { + List input = new ArrayList<>(Arrays.asList(1, "two", null, 3.0)); + Arguments arguments = Arguments.from(input); + + // Modify input + input.set(1, "changed"); + input.add("new"); + + // Assert that arguments are unchanged + assertArrayEquals(new Object[] { 1, "two", null, 3.0 }, arguments.get()); + } + + @Test + void argumentsFromSupportsList() { List input = Arrays.asList("a", 2, null); Arguments arguments = Arguments.argumentsFrom(input); @@ -87,7 +101,7 @@ void argumentSetSupportsList() { @Test void toListReturnsMutableListOfArguments() { - Arguments arguments = Arguments.from(Arrays.asList("a", 2, null)); + Arguments arguments = Arguments.of("a", 2, null); List result = arguments.toList(); @@ -96,9 +110,21 @@ void toListReturnsMutableListOfArguments() { assertThat(result).contains("extra"); } + @Test + void toListDoesNotAffectInternalArgumentsState() { + Arguments arguments = Arguments.of("a", 2, null); + + List result = arguments.toList(); + result.add("extra"); // mutate the returned list + + // Confirm that internal state was not modified + List freshCopy = arguments.toList(); + assertThat(freshCopy).containsExactly("a", 2, null); + } + @Test void toListWorksOnEmptyArguments() { - Arguments arguments = Arguments.from(Arrays.asList()); + Arguments arguments = Arguments.of(); List result = arguments.toList(); From b846902c16fbe7135b10f8e069c778f06996a289 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Fri, 5 Dec 2025 00:30:13 +0100 Subject: [PATCH 04/13] Polish contribution * Remove periods on @param and @return doc lines * Reference non-deprecated constants * Trim java doc at 80 character * Update since declarations to 6.1 * Use var in ArgumentsTests --- .../jupiter/params/provider/Arguments.java | 62 ++++++++++--------- .../params/provider/ArgumentsTests.java | 33 +++++----- 2 files changed, 49 insertions(+), 46 deletions(-) diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java index feedc1b9c442..a2d7a132efc8 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java @@ -10,6 +10,7 @@ package org.junit.jupiter.params.provider; +import static org.apiguardian.api.API.Status.EXPERIMENTAL; import static org.apiguardian.api.API.Status.MAINTAINED; import static org.apiguardian.api.API.Status.STABLE; @@ -30,10 +31,11 @@ * * @apiNote

This interface is specifically designed as a simple holder of * arguments for a parameterized test. Therefore, if you end up - * {@linkplain java.util.stream.Stream#map(java.util.function.Function) transforming} or + * {@linkplain java.util.stream.Stream#map(java.util.function.Function) transforming} + * or * {@linkplain java.util.stream.Stream#filter(java.util.function.Predicate) filtering} - * the arguments, you should consider using one of the following in intermediate - * steps: + * the arguments, you should consider using one of the following in + * intermediate steps: * *

    *
  • The standard Java collections
  • @@ -62,8 +64,8 @@ public interface Arguments { * Get the arguments used for an invocation of the * {@code @ParameterizedTest} method. * - * @apiNote If you need a type-safe way to access some or all of the arguments, - * please read the {@linkplain Arguments class-level API note}. + * @apiNote If you need a type-safe way to access some or all of the + * arguments, please read the {@linkplain Arguments class-level API note}. * * @return the arguments; never {@code null} but may contain {@code null} */ @@ -108,21 +110,22 @@ static Arguments arguments(@Nullable Object... arguments) { * {@code name} and {@code arguments}. * *

    Favor this method over {@link Arguments#of Arguments.of(...)} and - * {@link Arguments#arguments arguments(...)} when you wish to assign a name - * to the entire set of arguments. + * {@link Arguments#arguments arguments(...)} when you wish to assign a + * name to the entire set of arguments. * *

    This method is well suited to be used as a static import — for * example, via: * {@code import static org.junit.jupiter.params.provider.Arguments.argumentSet;}. * - * @param name the name of the argument set; must not be {@code null} or blank + * @param name the name of the argument set; must not be {@code null} or + * blank * @param arguments the arguments to be used for an invocation of the test * method; must not be {@code null} but may contain {@code null} * @return an {@code ArgumentSet}; never {@code null} * @since 5.11 * @see ArgumentSet - * @see org.junit.jupiter.params.ParameterizedTest#ARGUMENT_SET_NAME_PLACEHOLDER - * @see org.junit.jupiter.params.ParameterizedTest#ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER + * @see org.junit.jupiter.params.ParameterizedInvocationConstants#ARGUMENT_SET_NAME_PLACEHOLDER + * @see org.junit.jupiter.params.ParameterizedInvocationConstants#ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER */ @API(status = MAINTAINED, since = "5.13.3") static ArgumentSet argumentSet(String name, @Nullable Object... arguments) { @@ -134,12 +137,12 @@ static ArgumentSet argumentSet(String name, @Nullable Object... arguments) { * the supplied {@link List} of {@code arguments}. * * @param arguments the arguments to be used for an invocation of the test - * method; must not be {@code null} but may contain {@code null}. - * @return an instance of {@code Arguments}; never {@code null}. - * @since 6.0 + * method; must not be {@code null} but may contain {@code null} + * @return an instance of {@code Arguments}; never {@code null} + * @since 6.1 * @see #argumentsFrom(List) */ - @API(status = EXPERIMENTAL, since = "6.0") + @API(status = EXPERIMENTAL, since = "6.1") static Arguments from(List<@Nullable Object> arguments) { Preconditions.notNull(arguments, "arguments must not be null"); return of(arguments.toArray()); @@ -154,12 +157,12 @@ static Arguments from(List<@Nullable Object> arguments) { * {@code import static org.junit.jupiter.params.provider.Arguments.argumentsFrom;} * * @param arguments the arguments to be used for an invocation of the test - * method; must not be {@code null} but may contain {@code null}. - * @return an instance of {@code Arguments}; never {@code null}. - * @since 6.0 + * method; must not be {@code null} but may contain {@code null} + * @return an instance of {@code Arguments}; never {@code null} + * @since 6.1 * @see #argumentSet(String, Object...) */ - @API(status = EXPERIMENTAL, since = "6.0") + @API(status = EXPERIMENTAL, since = "6.1") static Arguments argumentsFrom(List<@Nullable Object> arguments) { return from(arguments); } @@ -173,14 +176,14 @@ static Arguments argumentsFrom(List<@Nullable Object> arguments) { * based inputs. * * @param name the name of the argument set; must not be {@code null} - * or blank. + * or blank * @param arguments the arguments to be used for an invocation of the test - * method; must not be {@code null} but may contain {@code null}. - * @return an {@code ArgumentSet}; never {@code null}. - * @since 6.0 + * method; must not be {@code null} but may contain {@code null} + * @return an {@code ArgumentSet}; never {@code null} + * @since 6.1 * @see #argumentSet(String, Object...) */ - @API(status = EXPERIMENTAL, since = "6.0") + @API(status = EXPERIMENTAL, since = "6.1") static ArgumentSet argumentSetFrom(String name, List<@Nullable Object> arguments) { Preconditions.notBlank(name, "name must not be null or blank"); Preconditions.notNull(arguments, "arguments list must not be null"); @@ -195,10 +198,10 @@ static ArgumentSet argumentSetFrom(String name, List<@Nullable Object> arguments * operations such as filtering, transformation, or assertions. * * @return a mutable List of arguments; never {@code null} but may contain - * {@code null}. - * @since 6.0 + * {@code null} + * @since 6.1 */ - @API(status = EXPERIMENTAL, since = "6.0") + @API(status = EXPERIMENTAL, since = "6.1") default List<@Nullable Object> toList() { return new ArrayList<>(Arrays.asList(get())); } @@ -209,8 +212,8 @@ static ArgumentSet argumentSetFrom(String name, List<@Nullable Object> arguments * * @since 5.11 * @see Arguments#argumentSet(String, Object...) - * @see org.junit.jupiter.params.ParameterizedTest#ARGUMENT_SET_NAME_PLACEHOLDER - * @see org.junit.jupiter.params.ParameterizedTest#ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER + * @see org.junit.jupiter.params.ParameterizedInvocationConstants#ARGUMENT_SET_NAME_PLACEHOLDER + * @see org.junit.jupiter.params.ParameterizedInvocationConstants#ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER */ @API(status = MAINTAINED, since = "5.13.3") final class ArgumentSet implements Arguments { @@ -228,7 +231,8 @@ private ArgumentSet(String name, @Nullable Object[] arguments) { /** * Get the name of this {@code ArgumentSet}. - * @return the name of this {@code ArgumentSet}; never {@code null} or blank + * @return the name of this {@code ArgumentSet}; never {@code null} or + * blank */ public String getName() { return this.name; diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java index 4ce0dbb61d28..c0b6dbbd8874 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java @@ -17,10 +17,9 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.List; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.provider.Arguments.ArgumentSet; /** * Unit tests for {@link Arguments}. @@ -63,16 +62,16 @@ void argumentsReturnsSameArrayUsedForCreating() { @Test void fromSupportsList() { - List input = Arrays.asList(1, "two", null, 3.0); - Arguments arguments = Arguments.from(input); + var input = Arrays.<@Nullable Object> asList(1, "two", null, 3.0); + var arguments = Arguments.from(input); assertArrayEquals(new Object[] { 1, "two", null, 3.0 }, arguments.get()); } @Test void fromSupportsListDefensiveCopy() { - List input = new ArrayList<>(Arrays.asList(1, "two", null, 3.0)); - Arguments arguments = Arguments.from(input); + var input = new ArrayList<@Nullable Object>(Arrays.asList(1, "two", null, 3.0)); + var arguments = Arguments.from(input); // Modify input input.set(1, "changed"); @@ -84,16 +83,16 @@ void fromSupportsListDefensiveCopy() { @Test void argumentsFromSupportsList() { - List input = Arrays.asList("a", 2, null); - Arguments arguments = Arguments.argumentsFrom(input); + var input = Arrays.<@Nullable Object> asList("a", 2, null); + var arguments = Arguments.argumentsFrom(input); assertArrayEquals(new Object[] { "a", 2, null }, arguments.get()); } @Test void argumentSetSupportsList() { - List input = Arrays.asList("x", null, 42); - ArgumentSet argumentSet = Arguments.argumentSetFrom("list-test", input); + var input = Arrays.<@Nullable Object> asList("x", null, 42); + var argumentSet = Arguments.argumentSetFrom("list-test", input); assertArrayEquals(new Object[] { "x", null, 42 }, argumentSet.get()); assertThat(argumentSet.getName()).isEqualTo("list-test"); @@ -101,9 +100,9 @@ void argumentSetSupportsList() { @Test void toListReturnsMutableListOfArguments() { - Arguments arguments = Arguments.of("a", 2, null); + var arguments = Arguments.of("a", 2, null); - List result = arguments.toList(); + var result = arguments.toList(); assertThat(result).containsExactly("a", 2, null); // preserves content result.add("extra"); // confirms mutability @@ -112,21 +111,21 @@ void toListReturnsMutableListOfArguments() { @Test void toListDoesNotAffectInternalArgumentsState() { - Arguments arguments = Arguments.of("a", 2, null); + var arguments = Arguments.of("a", 2, null); - List result = arguments.toList(); + var result = arguments.toList(); result.add("extra"); // mutate the returned list // Confirm that internal state was not modified - List freshCopy = arguments.toList(); + var freshCopy = arguments.toList(); assertThat(freshCopy).containsExactly("a", 2, null); } @Test void toListWorksOnEmptyArguments() { - Arguments arguments = Arguments.of(); + var arguments = Arguments.of(); - List result = arguments.toList(); + var result = arguments.toList(); assertThat(result).isEmpty(); result.add("extra"); From f6fa71f15c994afff04bfa99ea8969d02435b9a2 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Fri, 5 Dec 2025 01:48:48 +0100 Subject: [PATCH 05/13] Accept Iterable of arguments --- .../jupiter/params/provider/Arguments.java | 49 ++++++++++++++++--- .../params/provider/ArgumentsTests.java | 46 +++++++++++++++-- 2 files changed, 83 insertions(+), 12 deletions(-) diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java index a2d7a132efc8..bc1c1a26d8d9 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java @@ -16,6 +16,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.List; import org.apiguardian.api.API; @@ -134,18 +135,31 @@ static ArgumentSet argumentSet(String name, @Nullable Object... arguments) { /** * Factory method for creating an instance of {@code Arguments} based on - * the supplied {@link List} of {@code arguments}. + * the supplied {@link Iterable} of {@code arguments}. + * + *

    The iterable supplied to this method should be a finite collection + * and have a reliable iteration order to provide arguments in a consistent + * order to tests. It is therefore recommended that the iterable be a + * {@link java.util.SequencedCollection} (on Java 21 or higher), + * {@link java.util.List}, or similar. * * @param arguments the arguments to be used for an invocation of the test * method; must not be {@code null} but may contain {@code null} * @return an instance of {@code Arguments}; never {@code null} * @since 6.1 - * @see #argumentsFrom(List) + * @see #argumentsFrom(Iterable) */ @API(status = EXPERIMENTAL, since = "6.1") - static Arguments from(List<@Nullable Object> arguments) { + static Arguments from(Iterable arguments) { Preconditions.notNull(arguments, "arguments must not be null"); - return of(arguments.toArray()); + + if (arguments instanceof Collection collection) { + return of(collection.toArray()); + } + + var collection = new ArrayList<>(); + arguments.forEach(collection::add); + return of(collection.toArray()); } /** @@ -156,6 +170,12 @@ static Arguments from(List<@Nullable Object> arguments) { * intended to be used when statically imported — for example, via: * {@code import static org.junit.jupiter.params.provider.Arguments.argumentsFrom;} * + *

    The iterable supplied to this method should be a finite collection + * and have a reliable iteration order to provide arguments in a consistent + * order to tests. It is therefore recommended that the iterable be a + * {@link java.util.SequencedCollection} (on Java 21 or higher), + * {@link java.util.List}, or similar. + * * @param arguments the arguments to be used for an invocation of the test * method; must not be {@code null} but may contain {@code null} * @return an instance of {@code Arguments}; never {@code null} @@ -163,7 +183,7 @@ static Arguments from(List<@Nullable Object> arguments) { * @see #argumentSet(String, Object...) */ @API(status = EXPERIMENTAL, since = "6.1") - static Arguments argumentsFrom(List<@Nullable Object> arguments) { + static Arguments argumentsFrom(Iterable arguments) { return from(arguments); } @@ -175,6 +195,12 @@ static Arguments argumentsFrom(List<@Nullable Object> arguments) { * {@link #argumentSet(String, Object...)} when working with {@link List} * based inputs. * + *

    The iterable supplied to this method should be a finite collection + * and have a reliable iteration order to provide arguments in a consistent + * order to tests. It is therefore recommended that the iterable be a + * {@link java.util.SequencedCollection} (on Java 21 or higher), + * {@link java.util.List}, or similar. + * * @param name the name of the argument set; must not be {@code null} * or blank * @param arguments the arguments to be used for an invocation of the test @@ -184,10 +210,17 @@ static Arguments argumentsFrom(List<@Nullable Object> arguments) { * @see #argumentSet(String, Object...) */ @API(status = EXPERIMENTAL, since = "6.1") - static ArgumentSet argumentSetFrom(String name, List<@Nullable Object> arguments) { + static ArgumentSet argumentSetFrom(String name, Iterable arguments) { Preconditions.notBlank(name, "name must not be null or blank"); Preconditions.notNull(arguments, "arguments list must not be null"); - return new ArgumentSet(name, arguments.toArray()); + + if (arguments instanceof Collection collection) { + return new ArgumentSet(name, collection.toArray()); + } + + var collection = new ArrayList<>(); + arguments.forEach(collection::add); + return new ArgumentSet(name, collection.toArray()); } /** @@ -203,6 +236,8 @@ static ArgumentSet argumentSetFrom(String name, List<@Nullable Object> arguments */ @API(status = EXPERIMENTAL, since = "6.1") default List<@Nullable Object> toList() { + // We could return List here but the unbounded wildcard is painful + // to work with. return new ArrayList<>(Arrays.asList(get())); } diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java index c0b6dbbd8874..da668d5133b9 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java @@ -10,6 +10,7 @@ package org.junit.jupiter.params.provider; +import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.params.provider.Arguments.arguments; @@ -17,7 +18,10 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import org.jetbrains.annotations.NotNull; import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; @@ -70,7 +74,7 @@ void fromSupportsList() { @Test void fromSupportsListDefensiveCopy() { - var input = new ArrayList<@Nullable Object>(Arrays.asList(1, "two", null, 3.0)); + var input = new ArrayList<@Nullable Object>(asList(1, "two", null, 3.0)); var arguments = Arguments.from(input); // Modify input @@ -82,16 +86,33 @@ void fromSupportsListDefensiveCopy() { } @Test - void argumentsFromSupportsList() { - var input = Arrays.<@Nullable Object> asList("a", 2, null); + void argumentsFromSupportsCollection() { + Collection<@Nullable Object> input = asList("a", 2, null); var arguments = Arguments.argumentsFrom(input); assertArrayEquals(new Object[] { "a", 2, null }, arguments.get()); } @Test - void argumentSetSupportsList() { - var input = Arrays.<@Nullable Object> asList("x", null, 42); + void argumentsFromSupportsIterable() { + var input = new IterableWithNullableElements("a", 2, null); + var arguments = Arguments.argumentsFrom(input); + + assertArrayEquals(new Object[] { "a", 2, null }, arguments.get()); + } + + @Test + void argumentSetSupportsCollection() { + Collection<@Nullable Object> input = asList("x", null, 42); + var argumentSet = Arguments.argumentSetFrom("list-test", input); + + assertArrayEquals(new Object[] { "x", null, 42 }, argumentSet.get()); + assertThat(argumentSet.getName()).isEqualTo("list-test"); + } + + @Test + void argumentSetSupportsIterable() { + var input = new IterableWithNullableElements("x", null, 42); var argumentSet = Arguments.argumentSetFrom("list-test", input); assertArrayEquals(new Object[] { "x", null, 42 }, argumentSet.get()); @@ -131,4 +152,19 @@ void toListWorksOnEmptyArguments() { result.add("extra"); assertThat(result).containsExactly("extra"); } + + private static final class IterableWithNullableElements implements Iterable<@Nullable Object> { + + private final Collection<@Nullable Object> collection; + + private IterableWithNullableElements(@Nullable Object... items) { + this.collection = asList(items); + } + + @Override + public @NotNull Iterator<@Nullable Object> iterator() { + return collection.iterator(); + } + } + } From 93ae10394049688f61070a8323a7bc971b4a15b1 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Fri, 5 Dec 2025 02:13:47 +0100 Subject: [PATCH 06/13] Reorder methods - Accessors - Factory methods - Varargs before iterables - Static imports after short-named variants --- .../jupiter/params/provider/Arguments.java | 124 +++++++++--------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java index bc1c1a26d8d9..408699f89f11 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java @@ -74,63 +74,36 @@ public interface Arguments { Object[] get(); /** - * Factory method for creating an instance of {@code Arguments} based on - * the supplied {@code arguments}. + * Convert the arguments to a new mutable {@link List} containing the same + * elements as {@link #get()}. * - * @param arguments the arguments to be used for an invocation of the test - * method; must not be {@code null} but may contain {@code null} - * @return an instance of {@code Arguments}; never {@code null} - * @see #arguments(Object...) - * @see #argumentSet(String, Object...) + *

    This is useful for test logic that benefits from {@code List} + * operations such as filtering, transformation, or assertions. + * + * @return a mutable List of arguments; never {@code null} but may contain + * {@code null} + * @since 6.1 */ - static Arguments of(@Nullable Object... arguments) { - Preconditions.notNull(arguments, "arguments array must not be null"); - return () -> arguments; + @API(status = EXPERIMENTAL, since = "6.1") + default List<@Nullable Object> toList() { + // We could return List here but the unbounded wildcard is painful + // to work with. + return new ArrayList<>(Arrays.asList(get())); } /** * Factory method for creating an instance of {@code Arguments} based on * the supplied {@code arguments}. * - *

    This method is an alias for {@link Arguments#of} and is - * intended to be used when statically imported — for example, via: - * {@code import static org.junit.jupiter.params.provider.Arguments.arguments;} - * * @param arguments the arguments to be used for an invocation of the test * method; must not be {@code null} but may contain {@code null} * @return an instance of {@code Arguments}; never {@code null} - * @since 5.3 + * @see #arguments(Object...) * @see #argumentSet(String, Object...) */ - static Arguments arguments(@Nullable Object... arguments) { - return of(arguments); - } - - /** - * Factory method for creating an {@link ArgumentSet} based on the supplied - * {@code name} and {@code arguments}. - * - *

    Favor this method over {@link Arguments#of Arguments.of(...)} and - * {@link Arguments#arguments arguments(...)} when you wish to assign a - * name to the entire set of arguments. - * - *

    This method is well suited to be used as a static import — for - * example, via: - * {@code import static org.junit.jupiter.params.provider.Arguments.argumentSet;}. - * - * @param name the name of the argument set; must not be {@code null} or - * blank - * @param arguments the arguments to be used for an invocation of the test - * method; must not be {@code null} but may contain {@code null} - * @return an {@code ArgumentSet}; never {@code null} - * @since 5.11 - * @see ArgumentSet - * @see org.junit.jupiter.params.ParameterizedInvocationConstants#ARGUMENT_SET_NAME_PLACEHOLDER - * @see org.junit.jupiter.params.ParameterizedInvocationConstants#ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER - */ - @API(status = MAINTAINED, since = "5.13.3") - static ArgumentSet argumentSet(String name, @Nullable Object... arguments) { - return new ArgumentSet(name, arguments); + static Arguments of(@Nullable Object... arguments) { + Preconditions.notNull(arguments, "arguments array must not be null"); + return () -> arguments; } /** @@ -162,6 +135,24 @@ static Arguments from(Iterable arguments) { return of(collection.toArray()); } + /** + * Factory method for creating an instance of {@code Arguments} based on + * the supplied {@code arguments}. + * + *

    This method is an alias for {@link Arguments#of} and is + * intended to be used when statically imported — for example, via: + * {@code import static org.junit.jupiter.params.provider.Arguments.arguments;} + * + * @param arguments the arguments to be used for an invocation of the test + * method; must not be {@code null} but may contain {@code null} + * @return an instance of {@code Arguments}; never {@code null} + * @since 5.3 + * @see #argumentSet(String, Object...) + */ + static Arguments arguments(@Nullable Object... arguments) { + return of(arguments); + } + /** * Factory method for creating an instance of {@code Arguments} based on * the supplied {@link List} of {@code arguments}. @@ -187,6 +178,33 @@ static Arguments argumentsFrom(Iterable arguments) { return from(arguments); } + /** + * Factory method for creating an {@link ArgumentSet} based on the supplied + * {@code name} and {@code arguments}. + * + *

    Favor this method over {@link Arguments#of Arguments.of(...)} and + * {@link Arguments#arguments arguments(...)} when you wish to assign a + * name to the entire set of arguments. + * + *

    This method is well suited to be used as a static import — for + * example, via: + * {@code import static org.junit.jupiter.params.provider.Arguments.argumentSet;}. + * + * @param name the name of the argument set; must not be {@code null} or + * blank + * @param arguments the arguments to be used for an invocation of the test + * method; must not be {@code null} but may contain {@code null} + * @return an {@code ArgumentSet}; never {@code null} + * @since 5.11 + * @see ArgumentSet + * @see org.junit.jupiter.params.ParameterizedInvocationConstants#ARGUMENT_SET_NAME_PLACEHOLDER + * @see org.junit.jupiter.params.ParameterizedInvocationConstants#ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER + */ + @API(status = MAINTAINED, since = "5.13.3") + static ArgumentSet argumentSet(String name, @Nullable Object... arguments) { + return new ArgumentSet(name, arguments); + } + /** * Factory method for creating an {@link ArgumentSet} based on the supplied * {@code name} and {@link List} of {@code arguments}. @@ -223,24 +241,6 @@ static ArgumentSet argumentSetFrom(String name, Iterable arguments) { return new ArgumentSet(name, collection.toArray()); } - /** - * Convert the arguments to a new mutable {@link List} containing the same - * elements as {@link #get()}. - * - *

    This is useful for test logic that benefits from {@code List} - * operations such as filtering, transformation, or assertions. - * - * @return a mutable List of arguments; never {@code null} but may contain - * {@code null} - * @since 6.1 - */ - @API(status = EXPERIMENTAL, since = "6.1") - default List<@Nullable Object> toList() { - // We could return List here but the unbounded wildcard is painful - // to work with. - return new ArrayList<>(Arrays.asList(get())); - } - /** * Specialization of {@link Arguments} that associates a {@link #getName() name} * with a set of {@link #get() arguments}. From 18f485ac1363b7a8cebe005143ddc875f4cf6d16 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Fri, 5 Dec 2025 02:26:08 +0100 Subject: [PATCH 07/13] Touchups --- .../jupiter/params/provider/Arguments.java | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java index 408699f89f11..ae21ef80a1f5 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java @@ -98,6 +98,7 @@ public interface Arguments { * @param arguments the arguments to be used for an invocation of the test * method; must not be {@code null} but may contain {@code null} * @return an instance of {@code Arguments}; never {@code null} + * @see #from(Iterable) * @see #arguments(Object...) * @see #argumentSet(String, Object...) */ @@ -148,6 +149,7 @@ static Arguments from(Iterable arguments) { * @return an instance of {@code Arguments}; never {@code null} * @since 5.3 * @see #argumentSet(String, Object...) + * @see #argumentsFrom(Iterable) */ static Arguments arguments(@Nullable Object... arguments) { return of(arguments); @@ -155,7 +157,7 @@ static Arguments arguments(@Nullable Object... arguments) { /** * Factory method for creating an instance of {@code Arguments} based on - * the supplied {@link List} of {@code arguments}. + * the supplied {@link Iterable} of {@code arguments}. * *

    This method is an alias for {@link Arguments#from} and is * intended to be used when statically imported — for example, via: @@ -171,7 +173,8 @@ static Arguments arguments(@Nullable Object... arguments) { * method; must not be {@code null} but may contain {@code null} * @return an instance of {@code Arguments}; never {@code null} * @since 6.1 - * @see #argumentSet(String, Object...) + * @see #arguments(Object...) + * @see #argumentSetFrom(String, Iterable) */ @API(status = EXPERIMENTAL, since = "6.1") static Arguments argumentsFrom(Iterable arguments) { @@ -191,12 +194,13 @@ static Arguments argumentsFrom(Iterable arguments) { * {@code import static org.junit.jupiter.params.provider.Arguments.argumentSet;}. * * @param name the name of the argument set; must not be {@code null} or - * blank + * blank * @param arguments the arguments to be used for an invocation of the test * method; must not be {@code null} but may contain {@code null} * @return an {@code ArgumentSet}; never {@code null} * @since 5.11 * @see ArgumentSet + * @see #argumentSetFrom(String, Iterable) * @see org.junit.jupiter.params.ParameterizedInvocationConstants#ARGUMENT_SET_NAME_PLACEHOLDER * @see org.junit.jupiter.params.ParameterizedInvocationConstants#ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER */ @@ -207,12 +211,12 @@ static ArgumentSet argumentSet(String name, @Nullable Object... arguments) { /** * Factory method for creating an {@link ArgumentSet} based on the supplied - * {@code name} and {@link List} of {@code arguments}. - * - *

    This method is a convenient alternative to - * {@link #argumentSet(String, Object...)} when working with {@link List} - * based inputs. - * + * {@code name} and {@link Iterable} of {@code arguments}. + * + *

    This method is well suited to be used as a static import — for + * example, via: + * {@code import static org.junit.jupiter.params.provider.Arguments.argumentSetFrom;}. + * *

    The iterable supplied to this method should be a finite collection * and have a reliable iteration order to provide arguments in a consistent * order to tests. It is therefore recommended that the iterable be a @@ -225,7 +229,10 @@ static ArgumentSet argumentSet(String name, @Nullable Object... arguments) { * method; must not be {@code null} but may contain {@code null} * @return an {@code ArgumentSet}; never {@code null} * @since 6.1 + * @see ArgumentSet * @see #argumentSet(String, Object...) + * @see org.junit.jupiter.params.ParameterizedInvocationConstants#ARGUMENT_SET_NAME_PLACEHOLDER + * @see org.junit.jupiter.params.ParameterizedInvocationConstants#ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER */ @API(status = EXPERIMENTAL, since = "6.1") static ArgumentSet argumentSetFrom(String name, Iterable arguments) { From 54d6f6c8d618726c473eaa0efce97be512bcdc55 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Fri, 5 Dec 2025 02:26:54 +0100 Subject: [PATCH 08/13] Touchups --- .../main/java/org/junit/jupiter/params/provider/Arguments.java | 1 + 1 file changed, 1 insertion(+) diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java index ae21ef80a1f5..c66571fc5a08 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java @@ -254,6 +254,7 @@ static ArgumentSet argumentSetFrom(String name, Iterable arguments) { * * @since 5.11 * @see Arguments#argumentSet(String, Object...) + * @see Arguments#argumentSetFrom(String, Iterable) * @see org.junit.jupiter.params.ParameterizedInvocationConstants#ARGUMENT_SET_NAME_PLACEHOLDER * @see org.junit.jupiter.params.ParameterizedInvocationConstants#ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER */ From 1fe016317f027dac5ae4f7f6025c5943b2d3f299 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Fri, 5 Dec 2025 02:29:35 +0100 Subject: [PATCH 09/13] Touchups --- .../java/org/junit/jupiter/params/provider/Arguments.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java index c66571fc5a08..d4c4898be2cd 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java @@ -213,6 +213,10 @@ static ArgumentSet argumentSet(String name, @Nullable Object... arguments) { * Factory method for creating an {@link ArgumentSet} based on the supplied * {@code name} and {@link Iterable} of {@code arguments}. * + *

    Favor this method over {@link Arguments#from(Iterable) Arguments.from(...)} and + * {@link Arguments#argumentsFrom(Iterable) argumentsFrom(...)} when you wish to assign a + * name to the entire set of arguments. + * *

    This method is well suited to be used as a static import — for * example, via: * {@code import static org.junit.jupiter.params.provider.Arguments.argumentSetFrom;}. From 44173b90f6c290aaca73a04886dc212b12129bcf Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Fri, 5 Dec 2025 02:35:31 +0100 Subject: [PATCH 10/13] Touchups --- .../junit/jupiter/params/provider/Arguments.java | 16 ++++++++-------- .../jupiter/params/provider/ArgumentsTests.java | 12 ++++++++++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java index d4c4898be2cd..b02615d83df5 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java @@ -98,7 +98,7 @@ public interface Arguments { * @param arguments the arguments to be used for an invocation of the test * method; must not be {@code null} but may contain {@code null} * @return an instance of {@code Arguments}; never {@code null} - * @see #from(Iterable) + * @see #from(Iterable) * @see #arguments(Object...) * @see #argumentSet(String, Object...) */ @@ -173,7 +173,7 @@ static Arguments arguments(@Nullable Object... arguments) { * method; must not be {@code null} but may contain {@code null} * @return an instance of {@code Arguments}; never {@code null} * @since 6.1 - * @see #arguments(Object...) + * @see #arguments(Object...) * @see #argumentSetFrom(String, Iterable) */ @API(status = EXPERIMENTAL, since = "6.1") @@ -200,7 +200,7 @@ static Arguments argumentsFrom(Iterable arguments) { * @return an {@code ArgumentSet}; never {@code null} * @since 5.11 * @see ArgumentSet - * @see #argumentSetFrom(String, Iterable) + * @see #argumentSetFrom(String, Iterable) * @see org.junit.jupiter.params.ParameterizedInvocationConstants#ARGUMENT_SET_NAME_PLACEHOLDER * @see org.junit.jupiter.params.ParameterizedInvocationConstants#ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER */ @@ -212,15 +212,15 @@ static ArgumentSet argumentSet(String name, @Nullable Object... arguments) { /** * Factory method for creating an {@link ArgumentSet} based on the supplied * {@code name} and {@link Iterable} of {@code arguments}. - * + * *

    Favor this method over {@link Arguments#from(Iterable) Arguments.from(...)} and * {@link Arguments#argumentsFrom(Iterable) argumentsFrom(...)} when you wish to assign a * name to the entire set of arguments. - * + * *

    This method is well suited to be used as a static import — for * example, via: * {@code import static org.junit.jupiter.params.provider.Arguments.argumentSetFrom;}. - * + * *

    The iterable supplied to this method should be a finite collection * and have a reliable iteration order to provide arguments in a consistent * order to tests. It is therefore recommended that the iterable be a @@ -244,12 +244,12 @@ static ArgumentSet argumentSetFrom(String name, Iterable arguments) { Preconditions.notNull(arguments, "arguments list must not be null"); if (arguments instanceof Collection collection) { - return new ArgumentSet(name, collection.toArray()); + return argumentSet(name, collection.toArray()); } var collection = new ArrayList<>(); arguments.forEach(collection::add); - return new ArgumentSet(name, collection.toArray()); + return argumentSet(name, collection.toArray()); } /** diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java index da668d5133b9..efa6e00b49db 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java @@ -65,8 +65,16 @@ void argumentsReturnsSameArrayUsedForCreating() { } @Test - void fromSupportsList() { - var input = Arrays.<@Nullable Object> asList(1, "two", null, 3.0); + void fromSupportsCollection() { + Collection<@Nullable Object> input = Arrays.asList(1, "two", null, 3.0); + var arguments = Arguments.from(input); + + assertArrayEquals(new Object[] { 1, "two", null, 3.0 }, arguments.get()); + } + + @Test + void fromSupportsIterable() { + var input = new IterableWithNullableElements(1, "two", null, 3.0); var arguments = Arguments.from(input); assertArrayEquals(new Object[] { 1, "two", null, 3.0 }, arguments.get()); From f0123ba562fc8732d2749868b6d64e9a05f2dac0 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Fri, 5 Dec 2025 02:46:30 +0100 Subject: [PATCH 11/13] Update release notes --- .../asciidoc/release-notes/release-notes-6.1.0-M2.adoc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M2.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M2.adoc index 4def1151ca40..b3ad642b2b90 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M2.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M2.adoc @@ -45,7 +45,15 @@ repository on GitHub. [[release-notes-6.1.0-M2-junit-jupiter-new-features-and-improvements]] ==== New Features and Improvements -* ❓ +* Support the creation of `Arguments` from iterables. These additions make it + easier to dynamically build arguments from collections when using + `@ParameterizedTest`: +** `Arguments.of(Iterable)` +** `Arguments.argumentsFrom(Iterable)` (alias) +** `Arguments.argumentSetFrom(String, Iterable)` +** `Arguments.toList()` — returns a mutable `List<@Nullable Object>` + + [[release-notes-6.1.0-M2-junit-vintage]] From c5c8f42412b1aad39a14f72bf85ddd7802e2d119 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Fri, 5 Dec 2025 02:49:09 +0100 Subject: [PATCH 12/13] Minimize diff --- .../src/docs/asciidoc/release-notes/release-notes-6.1.0-M2.adoc | 2 -- .../main/java/org/junit/jupiter/params/provider/Arguments.java | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M2.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M2.adoc index b3ad642b2b90..9d06716bbbae 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M2.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M2.adoc @@ -54,8 +54,6 @@ repository on GitHub. ** `Arguments.toList()` — returns a mutable `List<@Nullable Object>` - - [[release-notes-6.1.0-M2-junit-vintage]] === JUnit Vintage diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java index b02615d83df5..007c6c09d129 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java @@ -300,4 +300,5 @@ public String toString() { } } + } From cbe28c26bbce9f722f4b3066f26a9abac32d8cbe Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Fri, 5 Dec 2025 12:09:53 +0100 Subject: [PATCH 13/13] Remove org.jetbrains.annotations.NotNull --- .../java/org/junit/jupiter/params/provider/ArgumentsTests.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java index efa6e00b49db..3027c72d6109 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java @@ -21,7 +21,6 @@ import java.util.Collection; import java.util.Iterator; -import org.jetbrains.annotations.NotNull; import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; @@ -170,7 +169,7 @@ private IterableWithNullableElements(@Nullable Object... items) { } @Override - public @NotNull Iterator<@Nullable Object> iterator() { + public Iterator<@Nullable Object> iterator() { return collection.iterator(); } }