diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TemplateExecutor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TemplateExecutor.java index 64988963685f..55453fdfa98c 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TemplateExecutor.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TemplateExecutor.java @@ -21,7 +21,9 @@ import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.engine.execution.JupiterEngineExecutionContext; import org.junit.jupiter.engine.extension.ExtensionRegistry; +import org.junit.platform.commons.util.ExceptionUtils; import org.junit.platform.commons.util.Preconditions; +import org.junit.platform.commons.util.UnrecoverableExceptions; import org.junit.platform.engine.TestDescriptor; import org.junit.platform.engine.UniqueId; import org.junit.platform.engine.support.hierarchical.Node; @@ -52,11 +54,25 @@ private void executeForProvider(P provider, AtomicInteger invocationIndex, int initialValue = invocationIndex.get(); - try (Stream stream = provideContexts(provider, extensionContext)) { + Stream stream = provideContexts(provider, extensionContext); + try { stream.forEach(invocationContext -> createInvocationTestDescriptor(invocationContext, invocationIndex.incrementAndGet()) // .ifPresent(testDescriptor -> execute(dynamicTestExecutor, testDescriptor))); } + catch (Throwable t) { + try { + stream.close(); + } + catch (Throwable t2) { + // ignore exceptions from close() to avoid masking the original failure + UnrecoverableExceptions.rethrowIfUnrecoverable(t2); + } + throw ExceptionUtils.throwAsUncheckedException(t); + } + finally { + stream.close(); + } Preconditions.condition( invocationIndex.get() != initialValue || mayReturnZeroContexts(provider, extensionContext), diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java index 0c0c44842427..092c86428b35 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java @@ -23,7 +23,6 @@ import static org.junit.jupiter.engine.discovery.JupiterUniqueIdBuilder.appendTestTemplateInvocationSegment; import static org.junit.jupiter.engine.discovery.JupiterUniqueIdBuilder.uniqueIdForTestTemplateMethod; import static org.junit.jupiter.params.provider.Arguments.arguments; -import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectIteration; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId; @@ -87,6 +86,7 @@ import org.junit.jupiter.api.extension.ParameterResolutionException; import org.junit.jupiter.api.extension.ParameterResolver; import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.engine.AbstractJupiterTestEngineTests; import org.junit.jupiter.engine.JupiterTestEngine; import org.junit.jupiter.params.ParameterizedTestIntegrationTests.RepeatableSourcesTestCase.Action; import org.junit.jupiter.params.aggregator.AggregateWith; @@ -111,8 +111,8 @@ import org.junit.jupiter.params.support.ParameterDeclarations; import org.junit.platform.commons.PreconditionViolationException; import org.junit.platform.commons.util.ClassUtils; -import org.junit.platform.engine.DiscoverySelector; import org.junit.platform.engine.TestDescriptor; +import org.junit.platform.engine.TestExecutionResult; import org.junit.platform.testkit.engine.EngineExecutionResults; import org.junit.platform.testkit.engine.EngineTestKit; import org.junit.platform.testkit.engine.Event; @@ -122,7 +122,7 @@ /** * @since 5.0 */ -class ParameterizedTestIntegrationTests { +class ParameterizedTestIntegrationTests extends AbstractJupiterTestEngineTests { private final Locale originalLocale = Locale.getDefault(Locale.Category.FORMAT); @@ -394,7 +394,7 @@ void executesLifecycleMethods() { LifecycleTestCase.lifecycleEvents.clear(); LifecycleTestCase.testMethods.clear(); - var results = execute(selectClass(LifecycleTestCase.class)); + var results = executeTestsForClass(LifecycleTestCase.class); results.allEvents().assertThatEvents() // .haveExactly(1, event(test("test1"), displayName("[1] argument=foo"), finishedWithFailure(message("foo")))) // @@ -476,18 +476,30 @@ void failsWhenNoArgumentsSourceIsDeclared() { "Configuration error: You must configure at least one arguments source for this @ParameterizedTest")))); } - private EngineExecutionResults execute(DiscoverySelector... selectors) { - return EngineTestKit.engine(new JupiterTestEngine()).selectors(selectors).execute(); - } + @Test + void reportsExceptionInStaticInitializersWithoutInvocationCountValidation() { + var results = executeTestsForClass(ExceptionInStaticInitializerTestCase.class); - private EngineExecutionResults execute(Class testClass, String methodName, Class... methodParameterTypes) { - return execute(selectMethod(testClass, methodName, ClassUtils.nullSafeToString(methodParameterTypes))); + var failure = results.containerEvents().stream() // + .filter(finishedWithFailure()::matches) // + .findAny() // + .orElseThrow(); + + var throwable = failure.getRequiredPayload(TestExecutionResult.class).getThrowable().orElseThrow(); + + assertThat(throwable) // + .isInstanceOf(ExceptionInInitializerError.class) // + .hasNoSuppressedExceptions(); } private EngineExecutionResults execute(String methodName, Class... methodParameterTypes) { return execute(TestCase.class, methodName, methodParameterTypes); } + private EngineExecutionResults execute(Class testClass, String methodName, Class... methodParameterTypes) { + return executeTests(selectMethod(testClass, methodName, ClassUtils.nullSafeToString(methodParameterTypes))); + } + /** * @since 5.4 */ @@ -915,7 +927,7 @@ void duplicateMethodNames() { // other words, we're not really testing the support for @RepeatedTest // and @TestFactory, but their presence also contributes to the bug // reported in #3001. - ParameterizedTestIntegrationTests.this.execute(selectClass(DuplicateMethodNamesMethodSourceTestCase.class))// + executeTestsForClass(DuplicateMethodNamesMethodSourceTestCase.class)// .testEvents()// .assertStatistics(stats -> stats.started(8).failed(0).finished(8)); } @@ -1334,7 +1346,7 @@ void closeAutoCloseableArgumentsAfterTestDespiteEarlyFailure() { @Test void executesTwoIterationsBasedOnIterationAndUniqueIdSelector() { var methodId = uniqueIdForTestTemplateMethod(TestCase.class, "testWithThreeIterations(int)"); - var results = execute(selectUniqueId(appendTestTemplateInvocationSegment(methodId, 3)), + var results = executeTests(selectUniqueId(appendTestTemplateInvocationSegment(methodId, 3)), selectIteration(selectMethod(TestCase.class, "testWithThreeIterations", "int"), 1)); results.allEvents().assertThatEvents() // @@ -2598,4 +2610,24 @@ void test(AutoCloseableArgument autoCloseable) { } } + static class ExceptionInStaticInitializerTestCase { + + static { + //noinspection ConstantValue + if (true) + throw new RuntimeException("boom"); + } + + private static Stream getArguments() { + return Stream.of("foo", "bar"); + } + + @ParameterizedTest + @MethodSource("getArguments") + void test(String value) { + fail("should not be called: " + value); + } + + } + }