diff --git a/src/main/java/org/junit/support/testng/engine/ExecutionListener.java b/src/main/java/org/junit/support/testng/engine/ExecutionListener.java index 4ab3866..1099f9e 100644 --- a/src/main/java/org/junit/support/testng/engine/ExecutionListener.java +++ b/src/main/java/org/junit/support/testng/engine/ExecutionListener.java @@ -20,12 +20,14 @@ import java.util.Arrays; import java.util.Iterator; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Stream; import org.junit.platform.engine.EngineExecutionListener; import org.junit.platform.engine.TestExecutionResult; @@ -40,8 +42,8 @@ class ExecutionListener extends DefaultListener { private final TestClassRegistry testClassRegistry = new TestClassRegistry(); private final Map inProgressTestMethods = new ConcurrentHashMap<>(); - private final Set engineFailures = ConcurrentHashMap.newKeySet(); - private final Map> classFailures = new ConcurrentHashMap<>(); + private final Set engineLevelFailureResults = ConcurrentHashMap.newKeySet(); + private final Map> classLevelFailureResults = new ConcurrentHashMap<>(); private final EngineExecutionListener delegate; private final TestNGEngineDescriptor engineDescriptor; @@ -63,13 +65,22 @@ public void onBeforeClass(ITestClass testClass) { @Override public void onConfigurationFailure(ITestResult result) { + handleConfigurationResult(result); + } + + @Override + public void onConfigurationSkip(ITestResult result) { + handleConfigurationResult(result); + } + + private void handleConfigurationResult(ITestResult result) { Optional classDescriptor = testClassRegistry.get(result.getTestClass().getRealClass()); if (classDescriptor.isPresent()) { - classFailures.computeIfAbsent(classDescriptor.get(), __ -> ConcurrentHashMap.newKeySet()) // - .add(result.getThrowable()); + classLevelFailureResults.computeIfAbsent(classDescriptor.get(), __ -> ConcurrentHashMap.newKeySet()) // + .add(result); } else { - engineFailures.add(result.getThrowable()); + engineLevelFailureResults.add(result); } } @@ -78,8 +89,8 @@ public void onAfterClass(ITestClass testClass) { testClassRegistry.finish(testClass.getRealClass(), classDescriptor -> classDescriptor.remainingIterations.decrementAndGet() == 0, classDescriptor -> { finishMethodsNotYetReportedAsFinished(testClass); - Set failures = classFailures.remove(classDescriptor); - delegate.executionFinished(classDescriptor, toTestExecutionResult(failures)); + Set results = classLevelFailureResults.remove(classDescriptor); + delegate.executionFinished(classDescriptor, toTestExecutionResult(results)); }); } @@ -207,14 +218,24 @@ private TestDescriptorFactory getTestDescriptorFactory() { } public TestExecutionResult toEngineResult() { - return toTestExecutionResult(engineFailures); + return toTestExecutionResult(engineLevelFailureResults); + } + + private TestExecutionResult toTestExecutionResult(Set results) { + return results == null || results.isEmpty() ? successful() : abortedOrFailed(results); + } + + private static TestExecutionResult abortedOrFailed(Set results) { + return results.stream().allMatch(it -> it.getStatus() == ITestResult.SKIP) // + ? aborted(chain(throwables(results))) // + : failed(chain(throwables(results))); } - private TestExecutionResult toTestExecutionResult(Set failures) { - return failures == null || failures.isEmpty() ? successful() : failed(chain(failures)); + private static Stream throwables(Set results) { + return results.stream().map(ITestResult::getThrowable).filter(Objects::nonNull); } - private Throwable chain(Set failures) { + private static Throwable chain(Stream failures) { Iterator iterator = failures.iterator(); Throwable throwable = iterator.next(); iterator.forEachRemaining(throwable::addSuppressed); diff --git a/src/test/java/org/junit/support/testng/engine/ReportingIntegrationTests.java b/src/test/java/org/junit/support/testng/engine/ReportingIntegrationTests.java index bcf2987..347a4e0 100644 --- a/src/test/java/org/junit/support/testng/engine/ReportingIntegrationTests.java +++ b/src/test/java/org/junit/support/testng/engine/ReportingIntegrationTests.java @@ -42,6 +42,7 @@ import example.basics.SimpleTestCase; import example.basics.SuccessPercentageTestCase; import example.basics.TimeoutTestCase; +import example.configuration.methods.AbortedBeforeClassConfigurationMethodTestCase; import example.configuration.methods.FailingBeforeClassConfigurationMethodTestCase; import example.dataproviders.DataProviderMethodTestCase; @@ -117,6 +118,17 @@ void reportsFailureFromBeforeClassMethod() { finishedWithFailure(message("boom")))); } + @Test + void reportsAbortedBeforeClassMethod() { + var results = testNGEngine().selectors( + selectClass(AbortedBeforeClassConfigurationMethodTestCase.class)).execute(); + + results.allEvents().debug().assertEventsMatchLooselyInOrder( // + event(testClass(AbortedBeforeClassConfigurationMethodTestCase.class), started()), // + event(testClass(AbortedBeforeClassConfigurationMethodTestCase.class), + abortedWithReason(message("not today")))); + } + @Test void executesNoTestWhenPostDiscoveryFilterExcludesEverything() { var testClass = SimpleTestCase.class; diff --git a/src/testFixtures/java/example/configuration/methods/AbortedBeforeClassConfigurationMethodTestCase.java b/src/testFixtures/java/example/configuration/methods/AbortedBeforeClassConfigurationMethodTestCase.java new file mode 100644 index 0000000..87fa734 --- /dev/null +++ b/src/testFixtures/java/example/configuration/methods/AbortedBeforeClassConfigurationMethodTestCase.java @@ -0,0 +1,28 @@ +/* + * Copyright 2021 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example.configuration.methods; + +import org.testng.SkipException; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class AbortedBeforeClassConfigurationMethodTestCase { + + @BeforeClass + public void beforeClass() { + throw new SkipException("not today"); + } + + @Test + public void test() { + // never called + } +}