| /******************************************************************************* |
| * Copyright (c) 2016, 2018 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.junit5.runner; |
| |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.util.List; |
| import java.util.Optional; |
| |
| import org.junit.platform.engine.TestExecutionResult; |
| import org.junit.platform.engine.TestExecutionResult.Status; |
| import org.junit.platform.engine.reporting.ReportEntry; |
| import org.junit.platform.launcher.TestExecutionListener; |
| import org.junit.platform.launcher.TestIdentifier; |
| import org.junit.platform.launcher.TestPlan; |
| import org.opentest4j.AssertionFailedError; |
| import org.opentest4j.MultipleFailuresError; |
| import org.opentest4j.ValueWrapper; |
| |
| import org.eclipse.jdt.internal.junit.runner.FailedComparison; |
| import org.eclipse.jdt.internal.junit.runner.IListensToTestExecutions; |
| import org.eclipse.jdt.internal.junit.runner.ITestIdentifier; |
| import org.eclipse.jdt.internal.junit.runner.MessageIds; |
| import org.eclipse.jdt.internal.junit.runner.RemoteTestRunner; |
| import org.eclipse.jdt.internal.junit.runner.TestIdMap; |
| import org.eclipse.jdt.internal.junit.runner.TestReferenceFailure; |
| |
| public class JUnit5TestListener implements TestExecutionListener { |
| |
| private final IListensToTestExecutions fNotified; |
| |
| private RemoteTestRunner fRemoteTestRunner; |
| |
| private TestPlan fTestPlan; |
| |
| public JUnit5TestListener(IListensToTestExecutions notified, RemoteTestRunner remoteTestRunner) { |
| fNotified= notified; |
| fRemoteTestRunner= remoteTestRunner; |
| } |
| |
| @Override |
| public void testPlanExecutionStarted(TestPlan testPlan) { |
| fTestPlan= testPlan; |
| } |
| |
| @Override |
| public void testPlanExecutionFinished(TestPlan testPlan) { |
| fTestPlan= null; |
| } |
| |
| @Override |
| public void executionStarted(TestIdentifier testIdentifier) { |
| if (testIdentifier.isTest()) { |
| fNotified.notifyTestStarted(getIdentifier(testIdentifier, false, false)); |
| } |
| } |
| |
| @Override |
| public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) { |
| notifyIfNotSuccessful(testIdentifier, testExecutionResult); |
| if (testIdentifier.isTest()) { |
| fNotified.notifyTestEnded(getIdentifier(testIdentifier, false, false)); |
| } |
| } |
| |
| private void notifyIfNotSuccessful(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) { |
| Status result= testExecutionResult.getStatus(); |
| if (result != Status.SUCCESSFUL) { |
| String trace= ""; //$NON-NLS-1$ |
| FailedComparison comparison= null; |
| String status= MessageIds.TEST_FAILED; |
| |
| boolean assumptionFailed= result == Status.ABORTED; |
| Optional<Throwable> throwableOp= testExecutionResult.getThrowable(); |
| if (throwableOp.isPresent()) { |
| Throwable exception= throwableOp.get(); |
| trace= getTrace(exception); |
| comparison= getFailedComparison(exception); |
| status= (assumptionFailed || exception instanceof AssertionError) ? MessageIds.TEST_FAILED : MessageIds.TEST_ERROR; |
| } |
| |
| ITestIdentifier identifier= getIdentifier(testIdentifier, false, assumptionFailed); |
| fNotified.notifyTestFailed(new TestReferenceFailure(identifier, status, trace, comparison)); |
| } |
| } |
| |
| private String getTrace(Throwable exception) { |
| StringWriter stringWriter= new StringWriter(); |
| exception.printStackTrace(new PrintWriter(stringWriter)); |
| return stringWriter.getBuffer().toString(); |
| } |
| |
| private FailedComparison getFailedComparison(Throwable exception) { |
| if (exception instanceof AssertionFailedError) { |
| AssertionFailedError assertionFailedError= (AssertionFailedError) exception; |
| ValueWrapper expected= assertionFailedError.getExpected(); |
| ValueWrapper actual= assertionFailedError.getActual(); |
| if (expected == null || actual == null) { |
| return null; |
| } |
| return new FailedComparison(expected.getStringRepresentation(), actual.getStringRepresentation()); |
| } |
| |
| if (exception instanceof MultipleFailuresError) { |
| String expectedStr= ""; //$NON-NLS-1$ |
| String actualStr= ""; //$NON-NLS-1$ |
| String delimiter= "\n\n"; //$NON-NLS-1$ |
| List<Throwable> failures= ((MultipleFailuresError) exception).getFailures(); |
| for (Throwable assertionError : failures) { |
| if (assertionError instanceof AssertionFailedError) { |
| AssertionFailedError assertionFailedError= (AssertionFailedError) assertionError; |
| ValueWrapper expected= assertionFailedError.getExpected(); |
| ValueWrapper actual= assertionFailedError.getActual(); |
| if (expected == null || actual == null) { |
| return null; |
| } |
| expectedStr+= expected.getStringRepresentation() + delimiter; |
| actualStr+= actual.getStringRepresentation() + delimiter; |
| } else { |
| return null; |
| } |
| } |
| return new FailedComparison(expectedStr, actualStr); |
| } |
| |
| // Avoid reference to ComparisonFailure initially to avoid NoClassDefFoundError for ComparisonFailure when junit.jar is not on the build path |
| String classname= exception.getClass().getName(); |
| if (classname.equals("junit.framework.ComparisonFailure")) { //$NON-NLS-1$ |
| junit.framework.ComparisonFailure comparisonFailure= (junit.framework.ComparisonFailure) exception; |
| return new FailedComparison(comparisonFailure.getExpected(), comparisonFailure.getActual()); |
| } |
| if (classname.equals("org.junit.ComparisonFailure")) { //$NON-NLS-1$ |
| org.junit.ComparisonFailure comparisonFailure= (org.junit.ComparisonFailure) exception; |
| return new FailedComparison(comparisonFailure.getExpected(), comparisonFailure.getActual()); |
| } |
| |
| return null; |
| } |
| |
| @Override |
| public void executionSkipped(TestIdentifier testIdentifier, String reason) { |
| if (testIdentifier.isContainer() && fTestPlan != null) { |
| fTestPlan.getDescendants(testIdentifier).stream().filter(t -> t.isTest()).forEachOrdered(t -> notifySkipped(t)); |
| } else { |
| notifySkipped(testIdentifier); |
| } |
| } |
| |
| private void notifySkipped(TestIdentifier testIdentifier) { |
| // Send message to listeners which would be stale otherwise |
| ITestIdentifier identifier= getIdentifier(testIdentifier, true, false); |
| fNotified.notifyTestStarted(identifier); |
| fNotified.notifyTestEnded(identifier); |
| } |
| |
| |
| @Override |
| public void dynamicTestRegistered(TestIdentifier testIdentifier) { |
| if (fTestPlan != null) { |
| JUnit5Identifier dynamicTestIdentifier= new JUnit5Identifier(testIdentifier); |
| boolean hasChildren; |
| int testCount; |
| if (testIdentifier.isContainer()) { |
| hasChildren= true; |
| testCount= fTestPlan.getChildren(testIdentifier).size(); |
| } else { |
| hasChildren= false; |
| testCount= 1; |
| } |
| String parentId= getParentId(testIdentifier, fTestPlan); |
| fRemoteTestRunner.visitTreeEntry(dynamicTestIdentifier, hasChildren, testCount, true, parentId); |
| } |
| } |
| |
| /** |
| * @param testIdentifier the test identifier whose parent id is required |
| * @param testPlan the test plan containing the test |
| * @return the parent id from {@link TestIdMap} if the parent is present, otherwise |
| * <code>"-1"</code> |
| */ |
| private String getParentId(TestIdentifier testIdentifier, TestPlan testPlan) { |
| // Same as JUnit5TestReference.getParentId(TestIdentifier testIdentifier, TestPlan testPlan). |
| return testPlan.getParent(testIdentifier).map(parent -> fRemoteTestRunner.getTestId(new JUnit5Identifier(parent))).orElse("-1"); //$NON-NLS-1$ |
| } |
| |
| private ITestIdentifier getIdentifier(TestIdentifier testIdentifier, boolean ignored, boolean assumptionFailed) { |
| if (ignored) { |
| return new IgnoredTestIdentifier(testIdentifier); |
| } |
| if (assumptionFailed) { |
| return new AssumptionFailedTestIdentifier(testIdentifier); |
| } |
| return new JUnit5Identifier(testIdentifier); |
| } |
| |
| private class IgnoredTestIdentifier extends JUnit5Identifier { |
| public IgnoredTestIdentifier(TestIdentifier description) { |
| super(description); |
| } |
| |
| @Override |
| public String getName() { |
| String name= super.getName(); |
| if (name != null) |
| return MessageIds.IGNORED_TEST_PREFIX + name; |
| return null; |
| } |
| } |
| |
| private class AssumptionFailedTestIdentifier extends JUnit5Identifier { |
| public AssumptionFailedTestIdentifier(TestIdentifier description) { |
| super(description); |
| } |
| |
| @Override |
| public String getName() { |
| String name= super.getName(); |
| if (name != null) |
| return MessageIds.ASSUMPTION_FAILED_TEST_PREFIX + name; |
| return null; |
| } |
| } |
| |
| @Override |
| public void reportingEntryPublished(TestIdentifier testIdentifier, ReportEntry entry) { |
| System.out.println(TestIdentifier.class.getSimpleName() + " [" + testIdentifier.getDisplayName() + "]"); //$NON-NLS-1$ //$NON-NLS-2$ |
| System.out.println(entry); |
| } |
| } |