blob: 98a42fa71c06bdf02fbc0f8950cb3dfc2bc9d154 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.sling.junit.impl.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import junit.runner.BaseTestRunner;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.junit.Renderer;
import org.apache.sling.junit.RendererFactory;
import org.apache.sling.junit.TestSelector;
import org.junit.runner.Description;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
/** XML renderer for JUnit servlet */
@Component(immediate=false)
@Service
public class XmlRenderer extends RunListener implements Renderer, RendererFactory {
/**
* This renderer's extension
*/
public static final String EXTENSION = "xml";
/**
* Writer used for output.
*/
private PrintWriter output;
/**
* The XML document.
*/
private Document doc;
/**
* The wrapper for the testsuites.
*/
private Element suitesElement;
/**
* The wrapper for the whole testsuite.
*/
private Element rootElement;
/**
* Table to track tests.
*/
private Hashtable<Description, Element> testElements = new Hashtable<Description, Element>();
/**
* List to track falures.
*/
private ArrayList<Description> failures = new ArrayList<Description>();
/**
* Table to track test run times.
*/
Hashtable<Description, Long> tests = new Hashtable<Description, Long>();
/**
* Test Suite name.
*/
private String name;
/**
* Start time for the test suite.
*/
private long suiteStartTime = 0;
/**
* Counter of test suites.
*/
private int testSuiteCount = 0;
/** @inheritDoc */
public Renderer createRenderer() {
return new XmlRenderer();
}
/** @inheritDoc */
public boolean appliesTo(TestSelector selector) {
return EXTENSION.equals(selector.getExtension());
}
/** @inheritDoc */
public String getExtension() {
return EXTENSION;
}
/** @inheritDoc */
public void setup(HttpServletResponse response, String pageTitle) throws IOException, UnsupportedEncodingException {
if(output != null) {
throw new IllegalStateException("Output Writer already set");
}
suiteStartTime = System.currentTimeMillis();
response.setContentType("text/xml");
response.setCharacterEncoding("UTF-8");
output = response.getWriter();
doc = getDocumentBuilder().newDocument();
suitesElement = doc.createElement("testsuites");
}
/** @inheritDoc */
public void info(String cssClass, String str) {
}
/** @inheritDoc */
public void list(String cssClass, Collection<String> data) {
}
/** @inheritDoc */
public void title(int level, String title) {
if (level == 3)
name = title;
}
/** @inheritDoc */
public void link(String info, String url, String method) {
}
/** @inheritDoc */
public void cleanup() {
if (testSuiteCount > 1) {
output.println(getStringFromElement(suitesElement));
} else {
output.println(getStringFromElement(rootElement));
}
output = null;
}
/** @inheritDoc */
public RunListener getRunListener() {
return this;
}
@Override
public void testFailure(Failure failure) throws Exception {
super.testFailure(failure);
failures.add(failure.getDescription());
Element nested = doc.createElement("failure");
Element currentTest = testElements.get(failure.getDescription());
currentTest.appendChild(nested);
String message = failure.getMessage();
if (message != null && message.length() > 0) {
nested.setAttribute("message", message);
}
nested.setAttribute("type", failure.getClass().getName());
String strace = getException(failure.getException());
strace = BaseTestRunner.getFilteredTrace(strace);
Text trace = doc.createTextNode(strace);
nested.appendChild(trace);
}
@Override
public void testFinished(Description description) throws Exception {
super.testFinished(description);
Long startTime = tests.get(description);
long totalTime = System.currentTimeMillis() - startTime.longValue();
Element currentTest = (Element) testElements.get(description);
currentTest.setAttribute("time", String.valueOf(totalTime / 1000.0));
}
@Override
public void testIgnored(Description description) throws Exception {
super.testIgnored(description);
}
@Override
public void testRunFinished(Result result) throws Exception {
super.testRunFinished(result);
String cssClass = "testRun ";
if(result.getFailureCount() > 0) {
cssClass += "failure";
} else if(result.getIgnoreCount() > 0) {
cssClass += "ignored";
} else {
cssClass += "success";
}
long suiteEndTime = System.currentTimeMillis();
rootElement.setAttribute("name", name);
rootElement.setAttribute("timestamp", String.valueOf(suiteEndTime));
rootElement.setAttribute("hostname", getHostname());
rootElement.setAttribute("tests", "" + result.getRunCount());
rootElement.setAttribute("failures", "" + result.getFailureCount());
//rootElement.setAttribute("errors", "" + result.getIgnoreCount());
rootElement.setAttribute(
"time", "" + ((suiteEndTime - suiteStartTime) / 1000.0));
}
@Override
public void testRunStarted(Description description)
throws Exception {
super.testRunStarted(description);
testSuiteCount++;
rootElement = doc.createElement("testsuite");
suitesElement.appendChild(rootElement);
// Output properties
Element propsElement = doc.createElement("properties");
rootElement.appendChild(propsElement);
}
@Override
public void testStarted(Description description) throws Exception {
super.testStarted(description);
tests.put(description, new Long(System.currentTimeMillis()));
Element currentTest = doc.createElement("testcase");
String n = description.getDisplayName();
n = n.substring(0, n.indexOf("("));
currentTest.setAttribute("name",
n == null ? "unknown" : n);
currentTest.setAttribute("classname",description.getClassName());
rootElement.appendChild(currentTest);
testElements.put(description, currentTest);
}
/**
* Create a DocumentBuilder.
* @return a DocumentBuilder.
*/
public static DocumentBuilder getDocumentBuilder() {
try {
return DocumentBuilderFactory.newInstance().newDocumentBuilder();
} catch (Exception exc) {
throw new ExceptionInInitializerError(exc);
}
}
/**
* Convert an Element to a String representation
* @param element
* @return a String representation
*/
public static String getStringFromElement(Element element) {
try {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
StringWriter sw = new StringWriter();
trans.transform(new DOMSource(element), new StreamResult(sw));
String elementString = sw.toString();
return elementString;
} catch (TransformerConfigurationException e) {
System.err.println(getException(e));
} catch (TransformerException e) {
System.err.println(getException(e));
}
return "";
}
/**
* get the local hostname
* @return the name of the local host, or "localhost" if we cannot work it out
*/
private String getHostname() {
try {
return InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
return "localhost";
}
}
/**
* Convert a Throwable object to its stack trace representation
*
* @param t Throwable object
* @return String representation of the Throwable object
*/
public static String getException(Throwable t) {
StringWriter sw = new StringWriter();
t.printStackTrace(new PrintWriter(sw));
return sw.getBuffer().toString();
} // getException
}