blob: e0fdf19ac2311a2c3bb7f1c9b4afe3e5e31934a6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2017 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
* Brock Janiczak (brockj@tpg.com.au)
* - https://bugs.eclipse.org/bugs/show_bug.cgi?id=102236: [JUnit] display execution time next to each test
* Neale Upstone <neale@nealeupstone.com> - [JUnit] JUnit viewer doesn't recognise <skipped/> node - https://bugs.eclipse.org/bugs/show_bug.cgi?id=276068
*******************************************************************************/
package org.eclipse.jdt.internal.junit.model;
import java.util.Arrays;
import java.util.Stack;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
import org.eclipse.osgi.util.NLS;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.jdt.core.IJavaModel;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.internal.junit.model.TestElement.Status;
public class TestRunHandler extends DefaultHandler {
/*
* TODO: validate (currently assumes correct XML)
*/
private int fId;
private TestRunSession fTestRunSession;
private TestSuiteElement fTestSuite;
private TestCaseElement fTestCase;
private Stack<Boolean> fNotRun= new Stack<>();
private StringBuffer fFailureBuffer;
private boolean fInExpected;
private boolean fInActual;
private StringBuffer fExpectedBuffer;
private StringBuffer fActualBuffer;
private Locator fLocator;
private Status fStatus;
private IProgressMonitor fMonitor;
private int fLastReportedLine;
public TestRunHandler() {
}
public TestRunHandler(IProgressMonitor monitor) {
fMonitor= monitor;
}
public TestRunHandler(TestRunSession testRunSession) {
fTestRunSession= testRunSession;
}
@Override
public void setDocumentLocator(Locator locator) {
fLocator= locator;
}
@Override
public void startDocument() throws SAXException {
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (fLocator != null && fMonitor != null) {
int line= fLocator.getLineNumber();
if (line - 20 >= fLastReportedLine) {
line -= line % 20;
fLastReportedLine= line;
fMonitor.subTask(NLS.bind(ModelMessages.TestRunHandler_lines_read, Integer.valueOf(line)));
}
}
if (Thread.interrupted())
throw new OperationCanceledException();
switch (qName) {
case IXMLTags.NODE_TESTRUN:
if (fTestRunSession == null) {
String name= attributes.getValue(IXMLTags.ATTR_NAME);
String project= attributes.getValue(IXMLTags.ATTR_PROJECT);
IJavaProject javaProject= null;
if (project != null) {
IJavaModel javaModel= JavaCore.create(ResourcesPlugin.getWorkspace().getRoot());
javaProject= javaModel.getJavaProject(project);
if (! javaProject.exists())
javaProject= null;
}
fTestRunSession= new TestRunSession(name, javaProject);
String includeTags= attributes.getValue(IXMLTags.ATTR_INCLUDE_TAGS);
if (includeTags != null && includeTags.trim().length() > 0) {
fTestRunSession.setIncludeTags(includeTags);
}
String excludeTags= attributes.getValue(IXMLTags.ATTR_EXCLUDE_TAGS);
if (excludeTags != null && excludeTags.trim().length() > 0) {
fTestRunSession.setExcludeTags(excludeTags);
}
//TODO: read counts?
} else {
fTestRunSession.reset();
}
fTestSuite= fTestRunSession.getTestRoot();
break;
// support Ant's 'junitreport' task; create suite from NODE_TESTSUITE
case IXMLTags.NODE_TESTSUITES:
break;
case IXMLTags.NODE_TESTSUITE:
{
String name= attributes.getValue(IXMLTags.ATTR_NAME);
if (fTestRunSession == null) {
// support standalone suites and Ant's 'junitreport' task:
fTestRunSession= new TestRunSession(name, null);
fTestSuite= fTestRunSession.getTestRoot();
} String pack= attributes.getValue(IXMLTags.ATTR_PACKAGE);
String suiteName= pack == null ? name : pack + "." + name; //$NON-NLS-1$
String displayName= attributes.getValue(IXMLTags.ATTR_DISPLAY_NAME);
String paramTypesStr= attributes.getValue(IXMLTags.ATTR_PARAMETER_TYPES);
String[] paramTypes;
if (paramTypesStr != null && !paramTypesStr.trim().isEmpty()) {
paramTypes= paramTypesStr.split(","); //$NON-NLS-1$
Arrays.parallelSetAll(paramTypes, i -> paramTypes[i].trim());
} else {
paramTypes= null;
} String uniqueId= attributes.getValue(IXMLTags.ATTR_UNIQUE_ID);
if (uniqueId != null && uniqueId.trim().isEmpty()) {
uniqueId= null;
} fTestSuite= (TestSuiteElement) fTestRunSession.createTestElement(fTestSuite, getNextId(), suiteName, true, 0, false, displayName, paramTypes, uniqueId);
readTime(fTestSuite, attributes);
fNotRun.push(Boolean.valueOf(attributes.getValue(IXMLTags.ATTR_INCOMPLETE)));
break;
}
// not interested
case IXMLTags.NODE_PROPERTIES:
case IXMLTags.NODE_PROPERTY:
break;
case IXMLTags.NODE_TESTCASE:
{
String name= attributes.getValue(IXMLTags.ATTR_NAME);
String classname= attributes.getValue(IXMLTags.ATTR_CLASSNAME);
String testName= name + '(' + classname + ')';
boolean isDynamicTest= Boolean.valueOf(attributes.getValue(IXMLTags.ATTR_DYNAMIC_TEST)).booleanValue();
String displayName= attributes.getValue(IXMLTags.ATTR_DISPLAY_NAME);
String paramTypesStr= attributes.getValue(IXMLTags.ATTR_PARAMETER_TYPES);
String[] paramTypes;
if (paramTypesStr != null && !paramTypesStr.trim().isEmpty()) {
paramTypes= paramTypesStr.split(","); //$NON-NLS-1$
Arrays.parallelSetAll(paramTypes, i -> paramTypes[i].trim());
} else {
paramTypes= null;
} String uniqueId= attributes.getValue(IXMLTags.ATTR_UNIQUE_ID);
if (uniqueId != null && uniqueId.trim().isEmpty()) {
uniqueId= null;
} fTestCase= (TestCaseElement) fTestRunSession.createTestElement(fTestSuite, getNextId(), testName, false, 0, isDynamicTest, displayName, paramTypes, uniqueId);
fNotRun.push(Boolean.valueOf(attributes.getValue(IXMLTags.ATTR_INCOMPLETE)));
fTestCase.setIgnored(Boolean.valueOf(attributes.getValue(IXMLTags.ATTR_IGNORED)).booleanValue());
readTime(fTestCase, attributes);
break;
}
case IXMLTags.NODE_ERROR:
//TODO: multiple failures: https://bugs.eclipse.org/bugs/show_bug.cgi?id=125296
fStatus= Status.ERROR;
fFailureBuffer= new StringBuffer();
break;
case IXMLTags.NODE_FAILURE:
//TODO: multiple failures: https://bugs.eclipse.org/bugs/show_bug.cgi?id=125296
fStatus= Status.FAILURE;
fFailureBuffer= new StringBuffer();
break;
case IXMLTags.NODE_EXPECTED:
fInExpected= true;
fExpectedBuffer= new StringBuffer();
break;
case IXMLTags.NODE_ACTUAL:
fInActual= true;
fActualBuffer= new StringBuffer();
break;
// not interested
case IXMLTags.NODE_SYSTEM_OUT:
case IXMLTags.NODE_SYSTEM_ERR:
break;
case IXMLTags.NODE_SKIPPED:
// before Ant 1.9.0: not an Ant JUnit tag, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=276068
// later: child of <suite> or <test>, see https://issues.apache.org/bugzilla/show_bug.cgi?id=43969
fStatus= Status.OK;
fFailureBuffer= new StringBuffer();
String message= attributes.getValue(IXMLTags.ATTR_MESSAGE);
if (message != null) {
fFailureBuffer.append(message).append('\n');
}
break;
default:
throw new SAXParseException("unknown node '" + qName + "'", fLocator); //$NON-NLS-1$//$NON-NLS-2$
}
}
private void readTime(TestElement testElement, Attributes attributes) {
String timeString= attributes.getValue(IXMLTags.ATTR_TIME);
if (timeString != null) {
try {
testElement.setElapsedTimeInSeconds(Double.parseDouble(timeString));
} catch (NumberFormatException e) {
}
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if (fInExpected) {
fExpectedBuffer.append(ch, start, length);
} else if (fInActual) {
fActualBuffer.append(ch, start, length);
} else if (fFailureBuffer != null) {
fFailureBuffer.append(ch, start, length);
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
switch (qName) {
// OK
case IXMLTags.NODE_TESTRUN:
break;
// OK
case IXMLTags.NODE_TESTSUITES:
break;
case IXMLTags.NODE_TESTSUITE:
handleTestElementEnd(fTestSuite);
fTestSuite= fTestSuite.getParent();
//TODO: end suite: compare counters?
break;
// OK
case IXMLTags.NODE_PROPERTIES:
case IXMLTags.NODE_PROPERTY:
break;
case IXMLTags.NODE_TESTCASE:
handleTestElementEnd(fTestCase);
fTestCase= null;
break;
case IXMLTags.NODE_FAILURE:
case IXMLTags.NODE_ERROR:
{
TestElement testElement= fTestCase;
if (testElement == null)
testElement= fTestSuite;
handleFailure(testElement);
break;
}
case IXMLTags.NODE_EXPECTED:
fInExpected= false;
if (fFailureBuffer != null) {
// skip whitespace from before <expected> and <actual> nodes
fFailureBuffer.setLength(0);
}
break;
case IXMLTags.NODE_ACTUAL:
fInActual= false;
if (fFailureBuffer != null) {
// skip whitespace from before <expected> and <actual> nodes
fFailureBuffer.setLength(0);
}
break;
// OK
case IXMLTags.NODE_SYSTEM_OUT:
case IXMLTags.NODE_SYSTEM_ERR:
break;
case IXMLTags.NODE_SKIPPED:
{
TestElement testElement= fTestCase;
if (testElement == null)
testElement= fTestSuite;
if (fFailureBuffer != null && fFailureBuffer.length() > 0) {
handleFailure(testElement);
testElement.setAssumptionFailed(true);
} else if (fTestCase != null) {
fTestCase.setIgnored(true);
} else { // not expected
testElement.setAssumptionFailed(true);
} break;
}
default:
handleUnknownNode(qName);
break;
}
}
private void handleTestElementEnd(TestElement testElement) {
boolean completed= fNotRun.pop() != Boolean.TRUE;
fTestRunSession.registerTestEnded(testElement, completed);
}
private void handleFailure(TestElement testElement) {
if (fFailureBuffer != null) {
fTestRunSession.registerTestFailureStatus(testElement, fStatus, fFailureBuffer.toString(), toString(fExpectedBuffer), toString(fActualBuffer));
fFailureBuffer= null;
fExpectedBuffer= null;
fActualBuffer= null;
fStatus= null;
}
}
private String toString(StringBuffer buffer) {
return buffer != null ? buffer.toString() : null;
}
private void handleUnknownNode(String qName) throws SAXException {
//TODO: just log if debug option is enabled?
String msg= "unknown node '" + qName + "'"; //$NON-NLS-1$//$NON-NLS-2$
if (fLocator != null) {
msg += " at line " + fLocator.getLineNumber() + ", column " + fLocator.getColumnNumber(); //$NON-NLS-1$//$NON-NLS-2$
}
throw new SAXException(msg);
}
@Override
public void error(SAXParseException e) throws SAXException {
throw e;
}
@Override
public void warning(SAXParseException e) throws SAXException {
throw e;
}
private String getNextId() {
return Integer.toString(fId++);
}
/**
* @return the parsed test run session, or <code>null</code>
*/
public TestRunSession getTestRunSession() {
return fTestRunSession;
}
}