diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicTest.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicTest.java index 87f4aeae166..b656c1bdeba 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicTest.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicTest.java @@ -226,6 +226,65 @@ public static Stream stream(Stream> inputStr .map(input -> dynamicTest(input.getName(), () -> testExecutor.accept(input.getPayload()))); } + /** + * Generate a stream of dynamic tests based on the given input stream. + * + *

Use this method when the set of dynamic tests is nondeterministic in + * nature or when the input comes from an existing {@link Iterator}. See + * {@link #stream(Stream)} as an alternative. + * + *

The given {@code inputGenerator} is responsible for supplying input values, + * display names, and an executor that executes a test. A {@link DynamicTest} will be + * added to the resulting stream for each dynamically supplied input value. + * + * @param inputGenerator an {@code Iterator} with {@code NamedExecutable} values + * that serves as a dynamic input generator; never {@code null} + * @param the type of input supplied by the {@code inputStream} + * @return a stream of dynamic tests based on the given generator; never {@code null} + * @since 5.11 + * + * @see #dynamicTest(String, Executable) + * @see #stream(Stream) + * @see NamedExecutable + */ + + public static > Stream stream( + Iterator> inputGenerator) { + Preconditions.notNull(inputGenerator, "inputGenerator must not be null"); + + return stream(StreamSupport.stream(spliteratorUnknownSize(inputGenerator, ORDERED), false)); + } + + /** + * Generate a stream of dynamic tests based on the given input stream. + * + *

Use this method when the set of dynamic tests is nondeterministic in + * nature or when the input comes from an existing {@link Stream}. See + * {@link #stream(Iterator)} as an alternative. + * + *

The given {@code inputStream} is responsible for supplying input values, + * display names, and an executor that executes a test. A {@link DynamicTest} will be + * added to the resulting stream for each dynamically supplied input value. + * + * @param inputStream a {@code Stream} that supplies dynamic {@code NamedExecutable} + * input values; never {@code null} + * @param the type of input supplied by the {@code inputStream} + * @return a stream of dynamic tests based on the given generator; never {@code null} + * @since 5.11 + * + * @see #dynamicTest(String, Executable) + * @see #stream(Iterator) + * @see NamedExecutable + */ + + public static > Stream stream( + Stream> inputStream) { + Preconditions.notNull(inputStream, "inputStream must not be null"); + + return inputStream. // + map((input) -> dynamicTest(input.getName(), input)); + } + private final Executable executable; private DynamicTest(String displayName, URI testSourceUri, Executable executable) { diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/DynamicTestTests.java b/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/DynamicTestTests.java index 4fc95d19ca0..cc28108d2fe 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/DynamicTestTests.java +++ b/junit-jupiter-engine/src/test/java/org/junit/jupiter/api/DynamicTestTests.java @@ -37,6 +37,28 @@ */ class DynamicTestTests { + record DummyNamedExecutableForTests(String name, ThrowingConsumer consumer) implements NamedExecutable { + + @Override + public String toString() { + return getName().toLowerCase(); + } + + @Override + public String getName() { + return name; + } + + @Override + public DummyNamedExecutableForTests getPayload() { + return this; + } + + @Override + public void execute() throws Throwable { + consumer.accept(getPayload().toString()); + }} + private static final Executable nix = () -> { }; @@ -90,6 +112,18 @@ void streamFromIteratorWithNamesPreconditions() { assertThrows(PreconditionViolationException.class, () -> DynamicTest.stream(emptyIterator(), null)); } + @Test + void streamFromStreamWithNamedExecutablesPreconditions() { + assertThrows(PreconditionViolationException.class, + () -> DynamicTest.stream((Stream>)null)); + } + + @Test + void streamFromIteratorWithNamedExecutablesPreconditions() { + assertThrows(PreconditionViolationException.class, + () -> DynamicTest.stream((Iterator)null)); + } + @Test void streamFromStream() throws Throwable { Stream stream = DynamicTest.stream(Stream.of("foo", "bar", "baz"), String::toUpperCase, @@ -119,6 +153,24 @@ void streamFromIteratorWithNames() throws Throwable { assertStream(stream); } + @Test + void streamFromStreamWithNamedExecutables() throws Throwable { + Stream stream = DynamicTest.stream( + Stream.of(new DummyNamedExecutableForTests("FOO", this::throwingConsumer), new DummyNamedExecutableForTests("BAR", this::throwingConsumer), new DummyNamedExecutableForTests("BAZ", this::throwingConsumer)) + ); + + assertStream(stream); + } + + @Test + void streamFromIteratorWithNamedExecutables() throws Throwable { + Stream stream = DynamicTest.stream( + List.of(new DummyNamedExecutableForTests("FOO", this::throwingConsumer), new DummyNamedExecutableForTests("BAR", this::throwingConsumer), new DummyNamedExecutableForTests("BAZ", this::throwingConsumer)).iterator() + ); + + assertStream(stream); + } + private void assertStream(Stream stream) throws Throwable { List dynamicTests = stream.collect(Collectors.toList());