Skip to content

Commit

Permalink
Change TestTemplateInvocationContextProvider#provide to return Stream
Browse files Browse the repository at this point in the history
While Iterators cannot be closed, Streams can. Since providers now
return Streams we close them after consumption. Thus, providers may
now read directly from files etc. and can register a hook using
Stream.onClose() to close their resources.
  • Loading branch information
marcphilipp committed Mar 11, 2017
1 parent acabb9a commit 410b47d
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 43 deletions.
Expand Up @@ -13,6 +13,7 @@
import static org.junit.platform.commons.meta.API.Usage.Experimental; import static org.junit.platform.commons.meta.API.Usage.Experimental;


import java.util.Iterator; import java.util.Iterator;
import java.util.stream.Stream;


import org.junit.platform.commons.meta.API; import org.junit.platform.commons.meta.API;


Expand Down Expand Up @@ -67,15 +68,19 @@ public interface TestTemplateInvocationContextProvider extends Extension {
* <p>This method is only called by the framework if {@link #supports} has * <p>This method is only called by the framework if {@link #supports} has
* previously returned {@code true} for the same * previously returned {@code true} for the same
* {@link ContainerExtensionContext}. Thus, it must not return an empty * {@link ContainerExtensionContext}. Thus, it must not return an empty
* {@code Iterator}. * {@code Stream}.
*
* <p>The returned {@code Stream} will be properly closed by calling
* {@link Stream#close()}, making it safe to use a resource such as
* {@link java.nio.file.Files#lines(java.nio.file.Path) Files.lines()}.
* *
* @param context the container extension context for the test template * @param context the container extension context for the test template
* method about to be invoked; never {@code null} * method about to be invoked; never {@code null}
* @return an Iterator of TestTemplateInvocationContext instances for the * @return a Stream of TestTemplateInvocationContext instances for the
* invocation of the test template method; never {@code null} or empty * invocation of the test template method; never {@code null} or empty
* @see #supports * @see #supports
* @see ContainerExtensionContext * @see ContainerExtensionContext
*/ */
Iterator<TestTemplateInvocationContext> provide(ContainerExtensionContext context); Stream<TestTemplateInvocationContext> provide(ContainerExtensionContext context);


} }
Expand Up @@ -14,7 +14,6 @@
import static org.junit.platform.commons.meta.API.Usage.Internal; import static org.junit.platform.commons.meta.API.Usage.Internal;


import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;


Expand Down Expand Up @@ -91,15 +90,12 @@ public JupiterEngineExecutionContext execute(JupiterEngineExecutionContext conte
List<TestTemplateInvocationContextProvider> providers = validateProviders(containerExtensionContext, List<TestTemplateInvocationContextProvider> providers = validateProviders(containerExtensionContext,
context.getExtensionRegistry()); context.getExtensionRegistry());
AtomicInteger invocationIndex = new AtomicInteger(); AtomicInteger invocationIndex = new AtomicInteger();
providers.forEach(provider -> { // @formatter:off
Iterator<TestTemplateInvocationContext> contextIterator = provider.provide(containerExtensionContext); providers.stream()
contextIterator.forEachRemaining(invocationContext -> { .flatMap(provider -> provider.provide(containerExtensionContext))
int index = invocationIndex.incrementAndGet(); .map(invocationContext -> createInvocationTestDescriptor(invocationContext, invocationIndex.incrementAndGet()))
TestDescriptor invocationTestDescriptor = createInvocationTestDescriptor(invocationContext, index); .forEach(invocationTestDescriptor -> execute(dynamicTestExecutor, invocationTestDescriptor));
addChild(invocationTestDescriptor); // @formatter:on
dynamicTestExecutor.execute(invocationTestDescriptor);
});
});
validateWasAtLeastInvokedOnce(invocationIndex); validateWasAtLeastInvokedOnce(invocationIndex);
return context; return context;
} }
Expand All @@ -126,6 +122,11 @@ private TestDescriptor createInvocationTestDescriptor(TestTemplateInvocationCont
invocationContext, index); invocationContext, index);
} }


private void execute(DynamicTestExecutor dynamicTestExecutor, TestDescriptor testDescriptor) {
addChild(testDescriptor);
dynamicTestExecutor.execute(testDescriptor);
}

private void validateWasAtLeastInvokedOnce(AtomicInteger invocationIndex) { private void validateWasAtLeastInvokedOnce(AtomicInteger invocationIndex) {
if (invocationIndex.get() == 0) { if (invocationIndex.get() == 0) {
throw new TestAbortedException("No supporting " throw new TestAbortedException("No supporting "
Expand Down
Expand Up @@ -11,7 +11,6 @@
package org.junit.jupiter.engine; package org.junit.jupiter.engine;


import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.emptyIterator;
import static java.util.Collections.singleton; import static java.util.Collections.singleton;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
Expand All @@ -38,8 +37,8 @@
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Stream; import java.util.stream.Stream;


Expand Down Expand Up @@ -77,7 +76,7 @@
/** /**
* @since 5.0 * @since 5.0
*/ */
public class TestTemplateInvocationTests extends AbstractJupiterTestEngineTests { class TestTemplateInvocationTests extends AbstractJupiterTestEngineTests {


@Test @Test
void templateWithoutRegisteredExtensionReportsFailure() { void templateWithoutRegisteredExtensionReportsFailure() {
Expand Down Expand Up @@ -311,6 +310,25 @@ void templateWithSupportingProviderButNoInvocationsReportsAbortedTest() {
message("No supporting TestTemplateInvocationContextProvider provided an invocation context"))))); message("No supporting TestTemplateInvocationContextProvider provided an invocation context")))));
} }


@Test
void templateWithCloseableStream() {
LauncherDiscoveryRequest request = request().selectors(
selectMethod(MyTestTemplateTestCase.class, "templateWithCloseableStream")).build();

ExecutionEventRecorder eventRecorder = executeTests(request);

assertThat(InvocationContextProviderWithCloseableStream.streamClosed.get()).describedAs(
"streamClosed").isTrue();

assertRecordedExecutionEventsContainsExactly(eventRecorder.getExecutionEvents(), //
wrappedInContainerEvents(MyTestTemplateTestCase.class, //
event(container("templateWithCloseableStream"), started()), //
event(dynamicTestRegistered("test-template-invocation:#1")), //
event(test("test-template-invocation:#1"), started()), //
event(test("test-template-invocation:#1"), finishedSuccessfully()), //
event(container("templateWithCloseableStream"), finishedSuccessfully())));
}

private TestDescriptor findTestDescriptor(ExecutionEventRecorder eventRecorder, private TestDescriptor findTestDescriptor(ExecutionEventRecorder eventRecorder,
Condition<ExecutionEvent> condition) { Condition<ExecutionEvent> condition) {
// @formatter:off // @formatter:off
Expand Down Expand Up @@ -338,10 +356,6 @@ private final Condition<? super ExecutionEvent>[] wrappedInContainerEvents(Class


static class MyTestTemplateTestCase { static class MyTestTemplateTestCase {


@Test
void foo() {
}

@TestTemplate @TestTemplate
void templateWithoutRegisteredExtension() { void templateWithoutRegisteredExtension() {
} }
Expand Down Expand Up @@ -392,10 +406,10 @@ void templateWithDynamicParameterResolver(String parameter) {
@ExtendWith(StringParameterResolvingInvocationContextProvider.class) @ExtendWith(StringParameterResolvingInvocationContextProvider.class)
@TestTemplate @TestTemplate
void templateWithWrongParameterType(int parameter) { void templateWithWrongParameterType(int parameter) {
fail("never called"); fail("never called: " + parameter);
} }


private String parameterInstanceVariable; String parameterInstanceVariable;


@ExtendWith(StringParameterInjectingInvocationContextProvider.class) @ExtendWith(StringParameterInjectingInvocationContextProvider.class)
@TestTemplate @TestTemplate
Expand All @@ -409,6 +423,10 @@ void templateWithSupportingProviderButNoInvocations() {
fail("never called"); fail("never called");
} }


@ExtendWith(InvocationContextProviderWithCloseableStream.class)
@TestTemplate
void templateWithCloseableStream() {
}
} }


static class TestTemplateTestClassWithBeforeAndAfterEach { static class TestTemplateTestClassWithBeforeAndAfterEach {
Expand Down Expand Up @@ -452,8 +470,8 @@ public boolean supports(ContainerExtensionContext context) {
} }


@Override @Override
public Iterator<TestTemplateInvocationContext> provide(ContainerExtensionContext context) { public Stream<TestTemplateInvocationContext> provide(ContainerExtensionContext context) {
return singleton(emptyTestTemplateInvocationContext()).iterator(); return Stream.of(emptyTestTemplateInvocationContext());
} }
} }


Expand All @@ -466,8 +484,8 @@ public boolean supports(ContainerExtensionContext context) {
} }


@Override @Override
public Iterator<TestTemplateInvocationContext> provide(ContainerExtensionContext context) { public Stream<TestTemplateInvocationContext> provide(ContainerExtensionContext context) {
return singleton(emptyTestTemplateInvocationContext()).iterator(); return Stream.of(emptyTestTemplateInvocationContext());
} }
} }


Expand All @@ -479,8 +497,8 @@ public boolean supports(ContainerExtensionContext context) {
} }


@Override @Override
public Iterator<TestTemplateInvocationContext> provide(ContainerExtensionContext context) { public Stream<TestTemplateInvocationContext> provide(ContainerExtensionContext context) {
return asList(emptyTestTemplateInvocationContext(), emptyTestTemplateInvocationContext()).iterator(); return Stream.of(emptyTestTemplateInvocationContext(), emptyTestTemplateInvocationContext());
} }
} }


Expand All @@ -507,13 +525,13 @@ public boolean supports(ContainerExtensionContext context) {
} }


@Override @Override
public Iterator<TestTemplateInvocationContext> provide(ContainerExtensionContext context) { public Stream<TestTemplateInvocationContext> provide(ContainerExtensionContext context) {
return Stream.<TestTemplateInvocationContext> generate(() -> new TestTemplateInvocationContext() { return Stream.<TestTemplateInvocationContext> generate(() -> new TestTemplateInvocationContext() {
@Override @Override
public String getDisplayName(int invocationIndex) { public String getDisplayName(int invocationIndex) {
return invocationIndex + " --> " + context.getDisplayName(); return invocationIndex + " --> " + context.getDisplayName();
} }
}).limit(1).iterator(); }).limit(1);
} }
} }


Expand All @@ -532,8 +550,8 @@ public boolean supports(ContainerExtensionContext context) {
} }


@Override @Override
public Iterator<TestTemplateInvocationContext> provide(ContainerExtensionContext context) { public Stream<TestTemplateInvocationContext> provide(ContainerExtensionContext context) {
return asList(createContext("foo"), createContext("bar")).iterator(); return Stream.of(createContext("foo"), createContext("bar"));
} }


private TestTemplateInvocationContext createContext(String argument) { private TestTemplateInvocationContext createContext(String argument) {
Expand Down Expand Up @@ -572,8 +590,8 @@ public boolean supports(ContainerExtensionContext context) {
} }


@Override @Override
public Iterator<TestTemplateInvocationContext> provide(ContainerExtensionContext context) { public Stream<TestTemplateInvocationContext> provide(ContainerExtensionContext context) {
return asList(createContext("foo"), createContext("bar")).iterator(); return Stream.of(createContext("foo"), createContext("bar"));
} }


private TestTemplateInvocationContext createContext(String argument) { private TestTemplateInvocationContext createContext(String argument) {
Expand Down Expand Up @@ -604,8 +622,8 @@ public boolean supports(ContainerExtensionContext context) {
} }


@Override @Override
public Iterator<TestTemplateInvocationContext> provide(ContainerExtensionContext context) { public Stream<TestTemplateInvocationContext> provide(ContainerExtensionContext context) {
return asList(createContext("foo"), createContext("bar")).iterator(); return Stream.of(createContext("foo"), createContext("bar"));
} }


private TestTemplateInvocationContext createContext(String argument) { private TestTemplateInvocationContext createContext(String argument) {
Expand Down Expand Up @@ -639,7 +657,7 @@ public void beforeTestExecution(TestExtensionContext context) throws Exception {
public void handleTestExecutionException(TestExtensionContext context, Throwable throwable) public void handleTestExecutionException(TestExtensionContext context, Throwable throwable)
throws Throwable { throws Throwable {
TestTemplateTestClassWithDynamicLifecycleCallbacks.lifecycleEvents.add("handleTestExecutionException"); TestTemplateTestClassWithDynamicLifecycleCallbacks.lifecycleEvents.add("handleTestExecutionException");
throw throwable; throw new AssertionError(throwable);
} }


@Override @Override
Expand All @@ -663,8 +681,23 @@ public boolean supports(ContainerExtensionContext context) {
} }


@Override @Override
public Iterator<TestTemplateInvocationContext> provide(ContainerExtensionContext context) { public Stream<TestTemplateInvocationContext> provide(ContainerExtensionContext context) {
return emptyIterator(); return Stream.empty();
}
}

private static class InvocationContextProviderWithCloseableStream implements TestTemplateInvocationContextProvider {

private static AtomicBoolean streamClosed = new AtomicBoolean(false);

@Override
public boolean supports(ContainerExtensionContext context) {
return true;
}

@Override
public Stream<TestTemplateInvocationContext> provide(ContainerExtensionContext context) {
return Stream.of(emptyTestTemplateInvocationContext()).onClose(() -> streamClosed.set(true));
} }
} }


Expand Down
Expand Up @@ -16,7 +16,6 @@


import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Spliterator; import java.util.Spliterator;
import java.util.Spliterators; import java.util.Spliterators;
Expand Down Expand Up @@ -52,7 +51,7 @@ public boolean supports(ContainerExtensionContext context) {
} }


@Override @Override
public Iterator<TestTemplateInvocationContext> provide(ContainerExtensionContext context) { public Stream<TestTemplateInvocationContext> provide(ContainerExtensionContext context) {
Method templateMethod = Preconditions.notNull(context.getTestMethod().orElse(null), Method templateMethod = Preconditions.notNull(context.getTestMethod().orElse(null),
"test method must not be null"); "test method must not be null");
ParameterizedTestNameFormatter formatter = createNameFormatter(templateMethod); ParameterizedTestNameFormatter formatter = createNameFormatter(templateMethod);
Expand All @@ -64,8 +63,7 @@ public Iterator<TestTemplateInvocationContext> provide(ContainerExtensionContext
.peek(provider -> initialize(templateMethod, provider)) .peek(provider -> initialize(templateMethod, provider))
.flatMap(ParameterizedTestExtension::toArgumentsStream) .flatMap(ParameterizedTestExtension::toArgumentsStream)
.map(Arguments::getArguments) .map(Arguments::getArguments)
.map(arguments -> toTestTemplateInvocationContext(formatter, arguments)) .map(arguments -> toTestTemplateInvocationContext(formatter, arguments));
.iterator();
// @formatter:on // @formatter:on
} }


Expand Down

0 comments on commit 410b47d

Please sign in to comment.