/*******************************************************************************
* Copyright (c) 2007, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* 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
*******************************************************************************/
package org.phpsrc.eclipse.pti.tools.phpunit.core.model;
import java.util.Stack;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.phpsrc.eclipse.pti.tools.phpunit.PHPUnitPlugin;
import org.phpsrc.eclipse.pti.tools.phpunit.core.model.TestElement.Status;
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;
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 String fFailureType;
private boolean fInExpected;
private boolean fInActual;
private StringBuffer fExpectedBuffer;
private StringBuffer fActualBuffer;
private Locator fLocator;
private Status fStatus;
public TestRunHandler() {
}
public TestRunHandler(TestRunSession testRunSession) {
fTestRunSession = testRunSession;
}
public void setDocumentLocator(Locator locator) {
fLocator = locator;
}
public void startDocument() throws SAXException {
}
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (qName.equals(IXMLTags.NODE_TESTRUN)) {
if (fTestRunSession == null) {
String name = attributes.getValue(IXMLTags.ATTR_NAME);
String project = attributes.getValue(IXMLTags.ATTR_PROJECT);
IProject 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);
// TODO: read counts?
} else {
fTestRunSession.reset();
}
fTestSuite = fTestRunSession.getTestRoot();
} else if (qName.equals(IXMLTags.NODE_TESTSUITES)) {
if (fTestRunSession != null) {
fTestRunSession.reset();
}
} else if (qName.equals(IXMLTags.NODE_TESTSUITE)) {
String name = attributes.getValue(IXMLTags.ATTR_NAME);
if (fTestRunSession == null) {
// support standalone suites and Ant's 'junitreport' task:
IFile testFile = null;
String file = attributes.getValue(IXMLTags.ATTR_FILE);
if (file != null)
testFile = PHPUnitPlugin.resolveProjectFile(file);
fTestRunSession = new TestRunSession(name, testFile);
fTestSuite = fTestRunSession.getTestRoot();
}
String pack = attributes.getValue(IXMLTags.ATTR_PACKAGE);
String suiteName = pack == null ? name : pack + "." + name; //$NON-NLS-1$
fTestSuite = (TestSuiteElement) fTestRunSession.createTestElement(fTestSuite, getNextId(), suiteName, true,
0);
readTime(fTestSuite, attributes);
fNotRun.push(Boolean.valueOf(attributes.getValue(IXMLTags.ATTR_INCOMPLETE)));
} else if (qName.equals(IXMLTags.NODE_PROPERTIES) || qName.equals(IXMLTags.NODE_PROPERTY)) {
// not interested
} else if (qName.equals(IXMLTags.NODE_TESTCASE)) {
String name = attributes.getValue(IXMLTags.ATTR_NAME);
String classname = attributes.getValue(IXMLTags.ATTR_CLASS);
fTestCase = (TestCaseElement) fTestRunSession.createTestElement(fTestSuite, getNextId(), name + '('
+ classname + ')', false, 0);
fNotRun.push(Boolean.valueOf(attributes.getValue(IXMLTags.ATTR_INCOMPLETE)));
fTestCase.setIgnored(Boolean.valueOf(attributes.getValue(IXMLTags.ATTR_IGNORED)).booleanValue());
readTime(fTestCase, attributes);
} else if (qName.equals(IXMLTags.NODE_ERROR)) {
// TODO: multiple failures:
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=125296
fStatus = Status.ERROR;
fFailureBuffer = new StringBuffer();
} else if (qName.equals(IXMLTags.NODE_FAILURE)) {
// TODO: multiple failures:
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=125296
fStatus = Status.FAILURE;
fFailureBuffer = new StringBuffer();
fFailureType = attributes.getValue(IXMLTags.ATTR_TYPE);
} else if (qName.equals(IXMLTags.NODE_EXPECTED)) {
fInExpected = true;
fExpectedBuffer = new StringBuffer();
} else if (qName.equals(IXMLTags.NODE_ACTUAL)) {
fInActual = true;
fActualBuffer = new StringBuffer();
} else if (qName.equals(IXMLTags.NODE_SYSTEM_OUT) || qName.equals(IXMLTags.NODE_SYSTEM_ERR)) {
// not interested
} else {
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) {
}
}
}
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);
}
}
public void endElement(String uri, String localName, String qName) throws SAXException {
if (qName.equals(IXMLTags.NODE_TESTRUN)) {
// OK
} else if (qName.equals(IXMLTags.NODE_TESTSUITES)) {
// OK
} else if (qName.equals(IXMLTags.NODE_TESTSUITE)) {
handleTestElementEnd(fTestSuite);
fTestSuite = fTestSuite.getParent();
// TODO: end suite: compare counters?
} else if (qName.equals(IXMLTags.NODE_PROPERTIES) || qName.equals(IXMLTags.NODE_PROPERTY)) {
// OK
} else if (qName.equals(IXMLTags.NODE_TESTCASE)) {
handleTestElementEnd(fTestCase);
fTestCase = null;
} else if (qName.equals(IXMLTags.NODE_FAILURE) || qName.equals(IXMLTags.NODE_ERROR)) {
TestElement testElement = fTestCase;
if (testElement == null)
testElement = fTestSuite;
handleFailure(testElement);
} else if (qName.equals(IXMLTags.NODE_EXPECTED)) {
fInExpected = false;
} else if (qName.equals(IXMLTags.NODE_ACTUAL)) {
fInActual = false;
} else if (qName.equals(IXMLTags.NODE_SYSTEM_OUT) || qName.equals(IXMLTags.NODE_SYSTEM_ERR)) {
// OK
} else {
handleUnknownNode(qName);
}
}
private void handleTestElementEnd(TestElement testElement) {
boolean completed = fNotRun.pop() != Boolean.TRUE;
fTestRunSession.registerTestEnded(testElement, completed);
}
private void handleFailure(TestElement testElement) {
if (fFailureBuffer != null) {
if (fExpectedBuffer == null && fActualBuffer == null
&& "PHPUnit_Framework_ExpectationFailedException".equals(fFailureType))
determineExpectedAndActualValues(fFailureBuffer.toString());
fTestRunSession.registerTestFailureStatus(testElement, fStatus, fFailureBuffer.toString(),
toString(fExpectedBuffer), toString(fActualBuffer));
fFailureBuffer = null;
fExpectedBuffer = null;
fActualBuffer = null;
fStatus = null;
fFailureType = null;
}
}
private void determineExpectedAndActualValues(String trace) {
int pos = trace.indexOf("@@ @@");
if (pos > 0) {
StringBuffer expected = new StringBuffer();
StringBuffer actual = new StringBuffer();
String[] lines = trace.substring(pos + 5).split("\n");
for (String line : lines) {
if (line.length() > 0 && !" ".equals(line)) {
String str = line.substring(1).trim() + "\n";
switch (line.charAt(0)) {
case ' ':
expected.append(str);
actual.append(str);
break;
case '-':
expected.append(str);
break;
case '+':
actual.append(str);
break;
}
}
}
if (expected.length() > 0 && actual.length() > 0) {
fExpectedBuffer = new StringBuffer(expected.toString().trim());
fActualBuffer = new StringBuffer(actual.toString().trim());
}
}
}
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);
}
public void error(SAXParseException e) throws SAXException {
throw e;
}
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;
}
}