/* * 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.felix.ipojo.junit4osgi.plugin; import java.io.BufferedWriter; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; import junit.framework.Test; import junit.framework.TestResult; import org.codehaus.plexus.util.IOUtil; import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter; import org.codehaus.plexus.util.xml.Xpp3Dom; import org.codehaus.plexus.util.xml.Xpp3DomWriter; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; /** * This class generates test result as XML files compatible with Surefire. * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> */ public class XMLReport extends Report { /** * List of results. */ private List m_results = new ArrayList(); /** * A test ends successfully. * @param test the test executed successfully. */ public void testSucceeded(Test test) { super.testSucceeded(); long runTime = this.m_endTime - this.m_startTime; Xpp3Dom testCase = createTestElement(test, runTime); m_results.add(testCase); } /** * A test throws an unexpected errors. * @param test the test in error * @param e the thrown exception * @param out the output messages printed during the test execution * @param err the error messages printed during the test execution * @param log the messages logged during the test execution */ public void testError(Test test, Throwable e, String out, String err, String log) { super.testError(test); writeTestProblems(test, e, "error", out, err, log); } /** * A test fails. * @param test the failing test * @param e the thrown failure * @param out the output messages printed during the test execution * @param err the error messages printed during the test execution * @param log the messages logged during the test execution */ public void testFailed(Test test, Throwable e, String out, String err, String log) { super.testFailed(test); writeTestProblems(test, e, "failure", out, err, log); } /** * Utility method writing failed and in error test result in the report. * @param test the test * @param e the thrown error * @param name type of failure ("error" or "failure") * @param out the output messages printed during the test execution * @param err the error messages printed during the test execution * @param log the messages logged during the test execution */ private void writeTestProblems(Test test, Throwable e, String name, String out, String err, String log) { long runTime = m_endTime - m_startTime; Xpp3Dom testCase = createTestElement(test, runTime); Xpp3Dom element = createElement(testCase, name); String stackTrace = getStackTrace(test, e); Throwable t = e; if (t != null) { String message = t.getMessage(); if (message != null) { element.setAttribute("message", message); element.setAttribute("type", stackTrace.indexOf(":") > -1 ? stackTrace.substring(0, stackTrace.indexOf(":")) : stackTrace); } else { element.setAttribute("type", new StringTokenizer(stackTrace) .nextToken()); } } if (stackTrace != null) { element.setValue(stackTrace); } addOutputStreamElement(out, "system-out", testCase); addOutputStreamElement(err, "system-err", testCase); if (log != null) { addOutputStreamElement(log, "log-service", testCase); } m_results.add(testCase); } /** * Generates the XML reports. * @param test the test * @param tr the test result * @param reportsDirectory the directory in which reports are created. * @param bc the bundle context (to get installed bundles) * @param configuration the Felix configuration * @throws Exception when the XML report cannot be generated correctly */ public void generateReport(Test test, TestResult tr, File reportsDirectory, BundleContext bc, Map configuration) throws Exception { long runTime = this.m_endTime - this.m_startTime; Xpp3Dom testSuite = createTestSuiteElement(test, runTime); showProperties(testSuite, bc, configuration); testSuite.setAttribute("tests", String.valueOf(tr.runCount())); testSuite.setAttribute("errors", String.valueOf(tr.errorCount())); testSuite.setAttribute("failures", String.valueOf(tr.failureCount())); for (Iterator i = m_results.iterator(); i.hasNext();) { Xpp3Dom testcase = (Xpp3Dom) i.next(); testSuite.addChild(testcase); } File reportFile = new File(reportsDirectory, "TEST-" + getReportName(test).replace(' ', '_') + ".xml"); File reportDir = reportFile.getParentFile(); reportDir.mkdirs(); PrintWriter writer = null; try { writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter( new FileOutputStream(reportFile), "UTF-8"))); writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" + NL); Xpp3DomWriter.write(new PrettyPrintXMLWriter(writer), testSuite); } catch (UnsupportedEncodingException e) { throw new Exception("Unable to use UTF-8 encoding", e); } catch (FileNotFoundException e) { throw new Exception("Unable to create file: " + e.getMessage(), e); } finally { IOUtil.close(writer); } } /** * Creates a XML test case element. * @param test the test * @param runTime the elapsed time to execute the test. * @return the XML element describing the given test. */ private Xpp3Dom createTestElement(Test test, long runTime) { Xpp3Dom testCase = new Xpp3Dom("testcase"); testCase.setAttribute("name", getReportName(test)); double time = (double) runTime / (double) 1000; testCase.setAttribute("time", Double.toString(time)); testCase.setAttribute("classname", test.getClass().getName()); return testCase; } /** * Creates a XML test suite element. * @param test the test * @param runTime the elapsed time to execute the test suite. * @return the XML element describing the given test suite. */ private Xpp3Dom createTestSuiteElement(Test test, long runTime) { Xpp3Dom testCase = new Xpp3Dom("testsuite"); testCase.setAttribute("name", getReportName(test)); testCase.setAttribute("time", Long.toString(runTime)); return testCase; } /** * Computes report name. * @param test the test * @return the report name */ private static String getReportName(Test test) { String report = test.toString(); if (report.indexOf("(") > 0) { report = report.substring(0, report.indexOf("(")); } return report; } /** * Creates an XML element. * @param element the parent element * @param name the name of the element to create * @return the resulting XML tree. */ private Xpp3Dom createElement(Xpp3Dom element, String name) { Xpp3Dom component = new Xpp3Dom(name); element.addChild(component); return component; } /** * Adds system properties to the XML report. * This method also adds installed bundles. * @param testSuite the XML element. * @param bc the bundle context * @param configuration the configuration of the underlying OSGi platform */ private void showProperties(Xpp3Dom testSuite, BundleContext bc, Map configuration) { Xpp3Dom properties = createElement(testSuite, "properties"); Properties systemProperties = System.getProperties(); if (systemProperties != null) { Enumeration propertyKeys = systemProperties.propertyNames(); while (propertyKeys.hasMoreElements()) { String key = (String) propertyKeys.nextElement(); String value = systemProperties.getProperty(key); if (value == null) { value = "null"; } Xpp3Dom property = createElement(properties, "property"); property.setAttribute("name", key); property.setAttribute("value", value); } } if (configuration != null) { Iterator it = configuration.keySet().iterator(); while (it.hasNext()) { String key = (String) it.next(); Object obj = (Object) configuration.get(key); String value = null; if (obj == null) { value = "null"; } else if (obj instanceof String) { value = (String) obj; } else { value = obj.toString(); } Xpp3Dom property = createElement(properties, "property"); property.setAttribute("name", key); property.setAttribute("value", value); } } Xpp3Dom bundle = createElement(properties, "property"); Bundle[] bundles = bc.getBundles(); for (int i = 0; i < bundles.length; i++) { String sn = bundles[i].getSymbolicName(); String state = "UNKNOWN"; switch (bundles[i].getState()) { case Bundle.ACTIVE: state = "ACTIVE"; break; case Bundle.INSTALLED: state = "INSTALLED"; break; case Bundle.RESOLVED: state = "RESOLVED"; break; case Bundle.UNINSTALLED: state = "UNINSTALLED"; break; default: break; } bundle.setAttribute("name", "bundle-" + sn); bundle.setAttribute("value", state); } } /** * Adds messages written during the test execution in the * XML tree. * @param stdOut the messages * @param name the name of the stream (out, error, log) * @param testCase the XML tree */ private void addOutputStreamElement(String stdOut, String name, Xpp3Dom testCase) { if (stdOut != null && stdOut.trim().length() > 0) { createElement(testCase, name).setValue(stdOut); } } }