Skip to content

Commit

Permalink
Simplify the generation of DynamicTests stream
Browse files Browse the repository at this point in the history
Introduce two methods in the class DynamicTest to generate a stream of
DynamicTests from a Stream/Iterator of NamedExecutables.

Instead of passing an inputStream, a displayNameGenerator and a
testExecutor separately, these two additional methods will allow you
to pass one Iterator/Stream of a NamedExecutable.

Unlike the Named interface, the NamedExecutable can colocate assertion
callbacks in one container using the execute method.

Issue: junit-team#3261
  • Loading branch information
mobounya committed Jan 19, 2024
1 parent 07730c4 commit 4fa5160
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 0 deletions.
Expand Up @@ -226,6 +226,65 @@ public static <T> Stream<DynamicTest> stream(Stream<? extends Named<T>> inputStr
.map(input -> dynamicTest(input.getName(), () -> testExecutor.accept(input.getPayload())));
}

/**
* Generate a stream of dynamic tests based on the given input stream.
*
* <p>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.
*
* <p>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 <em>input generator</em>; never {@code null}
* @param <T> the type of <em>input</em> 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 <T extends NamedExecutable<T>> Stream<DynamicTest> stream(
Iterator<? extends NamedExecutable<T>> 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.
*
* <p>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.
*
* <p>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 <T> the type of <em>input</em> 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 <T extends NamedExecutable<T>> Stream<DynamicTest> stream(
Stream<? extends NamedExecutable<T>> 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) {
Expand Down
Expand Up @@ -37,6 +37,28 @@
*/
class DynamicTestTests {

record DummyNamedExecutableForTests(String name, ThrowingConsumer<String> consumer) implements NamedExecutable<DummyNamedExecutableForTests> {

@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 = () -> {
};

Expand Down Expand Up @@ -90,6 +112,18 @@ void streamFromIteratorWithNamesPreconditions() {
assertThrows(PreconditionViolationException.class, () -> DynamicTest.stream(emptyIterator(), null));
}

@Test
void streamFromStreamWithNamedExecutablesPreconditions() {
assertThrows(PreconditionViolationException.class,
() -> DynamicTest.stream((Stream<NamedExecutable<DummyNamedExecutableForTests>>)null));
}

@Test
void streamFromIteratorWithNamedExecutablesPreconditions() {
assertThrows(PreconditionViolationException.class,
() -> DynamicTest.stream((Iterator<DummyNamedExecutableForTests>)null));
}

@Test
void streamFromStream() throws Throwable {
Stream<DynamicTest> stream = DynamicTest.stream(Stream.of("foo", "bar", "baz"), String::toUpperCase,
Expand Down Expand Up @@ -119,6 +153,24 @@ void streamFromIteratorWithNames() throws Throwable {
assertStream(stream);
}

@Test
void streamFromStreamWithNamedExecutables() throws Throwable {
Stream<DynamicTest> 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<DynamicTest> 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<DynamicTest> stream) throws Throwable {
List<DynamicTest> dynamicTests = stream.collect(Collectors.toList());

Expand Down

0 comments on commit 4fa5160

Please sign in to comment.