/******************************************************************************* * Copyright (c) 2004, 2010 BREDEX GmbH. * 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: * BREDEX GmbH - initial API and implementation and/or initial documentation * Oracle Corporation, Copyright (c) 2015, All Rights Reserved *******************************************************************************/ package org.eclipse.jubula.client.core.businessprocess; import java.util.Date; import java.util.Map; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.time.DurationFormatUtils; import org.dom4j.Document; import org.dom4j.DocumentHelper; import org.dom4j.Element; import org.eclipse.jubula.client.core.ClientTest; import org.eclipse.jubula.client.core.i18n.Messages; import org.eclipse.jubula.client.core.model.IAUTMainPO; import org.eclipse.jubula.client.core.model.IAbstractContainerPO; import org.eclipse.jubula.client.core.model.ICapPO; import org.eclipse.jubula.client.core.model.ICommentPO; import org.eclipse.jubula.client.core.model.ICondStructPO; import org.eclipse.jubula.client.core.model.IConditionalStatementPO; import org.eclipse.jubula.client.core.model.IDoWhilePO; import org.eclipse.jubula.client.core.model.IEventExecTestCasePO; import org.eclipse.jubula.client.core.model.IIteratePO; import org.eclipse.jubula.client.core.model.INodePO; import org.eclipse.jubula.client.core.model.ITestCasePO; import org.eclipse.jubula.client.core.model.ITestResult; import org.eclipse.jubula.client.core.model.ITestSuitePO; import org.eclipse.jubula.client.core.model.IWhileDoPO; import org.eclipse.jubula.client.core.model.ProjectVersion; import org.eclipse.jubula.client.core.model.TestResultNode; import org.eclipse.jubula.client.core.model.TestResultParameter; import org.eclipse.jubula.tools.internal.constants.StringConstants; import org.eclipse.jubula.tools.internal.i18n.CompSystemI18n; import org.eclipse.jubula.tools.internal.i18n.I18n; import org.eclipse.jubula.tools.internal.objects.event.TestErrorEvent; import org.eclipse.jubula.tools.internal.utils.TimeUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author BREDEX GmbH * @author Bryan Obright * @created 05.08.2005 */ public abstract class AbstractXMLReportGenerator { /** * Element name for Number of Steps */ public static final String EXPECTED_NUM_STEPS = "expectedNumSteps"; //$NON-NLS-1$ /** * Element name for Number of Steps Tested */ public static final String NUM_STEPS_TESTED = "numStepsTested"; //$NON-NLS-1$ /** * Element name for Number of Failed Steps */ public static final String NUM_FAILED_STEPS = "numFailedSteps"; //$NON-NLS-1$ /** * Element name for Number of Event Handler Steps */ public static final String NUM_EVENT_HANDLER_STEPS = "numEventHandlerSteps"; //$NON-NLS-1$ /** Postfix used to indicate a negated CondStruct */ private static final String NEGATED_POSTFIX = " - NEGATED"; //$NON-NLS-1$ /** The logger */ private static final Logger LOG = LoggerFactory.getLogger( AbstractXMLReportGenerator.class); /** the generated report */ private Document m_document; /** the Test Result for which to write a report */ private ITestResult m_testResult; /** * Constructor * * @param testResult The Test Result for which to write a report. */ public AbstractXMLReportGenerator(ITestResult testResult) { m_document = DocumentHelper.createDocument(); setTestResult(testResult); } /** * generates a readable xml file * @return * XML File as String */ public abstract Document generateXmlReport(); /** * Generates the basic, generic information for an XML report. * * @return The "project" element of the XML document. */ protected Element generateHeader() { if (LOG.isInfoEnabled()) { LOG.info("Generating Result XML Report"); //$NON-NLS-1$ } m_document.normalize(); m_document.addComment("<?xml-stylesheet type=\"text/xsl\" href=\"format.xsl\"?>"); //$NON-NLS-1$ Element root = m_document.addElement("report"); //$NON-NLS-1$ root.addAttribute("style", getStyleName()); //$NON-NLS-1$ Element general = root.addElement("project"); //$NON-NLS-1$ ITestResult testResult = getTestResult(); general.addElement("name").addText(testResult.getProjectName()); //$NON-NLS-1$ ProjectVersion version = new ProjectVersion( testResult.getProjectMajorVersion(), testResult.getProjectMinorVersion(), testResult.getProjectMicroVersion(), testResult.getProjectVersionQualifier()); general.addElement("version").addText(version.toString()); //$NON-NLS-1$ Date startTime = testResult.getStartTime(); if (startTime != null) { general.addElement("test-start").addText(startTime.toString()); //$NON-NLS-1$ } Date endTime = testResult.getEndTime(); if (endTime != null) { general.addElement("test-end").addText(endTime.toString()); //$NON-NLS-1$ } if (startTime != null && endTime != null) { general.addElement("test-length").//$NON-NLS-1$ addText(TimeUtil.getDurationString(startTime, endTime)); int executedCAPs = testResult.getNumberOfTestedSteps(); String averageCAPExecutionTime = Messages.Na; if (executedCAPs > 0) { averageCAPExecutionTime = String.valueOf( (endTime.getTime() - startTime.getTime()) / executedCAPs); } general.addElement("average-cap-duration").//$NON-NLS-1$ addText(averageCAPExecutionTime); } addStepCountElements(testResult, general); return general; } /** * Generates and adds test step count sub-elements to the given * element. * * @param result The Test Result from which to retrieve the step count * information. * @param general The parent element to which the step count elements * will be added. */ private void addStepCountElements(ITestResult result, Element general) { int expectedNumberOfSteps = result.getExpectedNumberOfSteps(); int numberOfStepsExecuted = result.getNumberOfTestedSteps(); int numberOfEventHandlerSteps = result.getNumberOfEventHandlerSteps(); int numberOfFailedTestSteps = result.getNumberOfFailedSteps(); general.addElement(EXPECTED_NUM_STEPS).addText( String.valueOf(expectedNumberOfSteps)); general.addElement(NUM_STEPS_TESTED).addText( String.valueOf(numberOfStepsExecuted)); general.addElement(NUM_EVENT_HANDLER_STEPS).addText( String.valueOf(numberOfEventHandlerSteps)); general.addElement(NUM_FAILED_STEPS).addText( String.valueOf(numberOfFailedTestSteps)); } /** * * @param parent * The XML element to which the result for node will be added. * This can be considered the parent of the returned element. * @return the created element. */ protected Element buildRootElement(Element parent) { return buildElement(parent, getTestResult().getRootResultNode()); } /** * Builds and returns a test report element. Subclasses can create an entire * test result hierarchy by recursively calling this method, using the * returned element as an argument for the next method call. * * @param resultNode * <code>TestResultNode</code> to translate to an XML element. * @param element * The XML element to which the result for node will be added. * This can be considered the parent of the returned element. * @return The <code>Element</code> created. */ protected Element buildElement(Element element, TestResultNode resultNode) { Element insertInto = element; Object node = resultNode.getNode(); if (node instanceof ITestSuitePO) { ITestSuitePO ts = (ITestSuitePO) node; Element suite = element.addElement("testsuite"); //$NON-NLS-1$ insertInto = suite; addGeneralElements(resultNode, insertInto); IAUTMainPO aut = ts.getAut(); Element autEl = suite.addElement("aut"); //$NON-NLS-1$ autEl.addElement("name").addText(aut.getName()); //$NON-NLS-1$ autEl.addElement("config").addText(getTestResult().getAutConfigName()); //$NON-NLS-1$ autEl.addElement("server").addText(getTestResult().getAutAgentHostName()); //$NON-NLS-1$ autEl.addElement("cmdline-parameter").setText(getTestResult().getAutArguments()); //$NON-NLS-1$ insertInto = suite.addElement("test-run"); //$NON-NLS-1$ } else if (node instanceof IEventExecTestCasePO) { insertInto = element.addElement("eventhandler"); //$NON-NLS-1$ addGeneralElements(resultNode, insertInto); Element typeEl = insertInto.addElement("type"); //$NON-NLS-1$ typeEl.addText(I18n.getString( ((IEventExecTestCasePO)node).getEventType())); Element reentryEl = insertInto.addElement("reentry-property"); //$NON-NLS-1$ reentryEl.addText(((IEventExecTestCasePO)node). getReentryProp().toString()); } else if (node instanceof ITestCasePO) { insertInto = element.addElement("testcase"); //$NON-NLS-1$ addGeneralElements(resultNode, insertInto); } else if (node instanceof ICapPO) { insertInto = element.addElement("step"); //$NON-NLS-1$ addGeneralElements(resultNode, insertInto); addCapElements(resultNode, insertInto, (ICapPO)node); } else if (node instanceof ICommentPO) { insertInto = element.addElement("comment"); //$NON-NLS-1$ Element nameElement = insertInto.addElement("name"); //$NON-NLS-1$ nameElement.addCDATA(((ICommentPO) node).getName()); } else if (node instanceof IConditionalStatementPO) { insertInto = element.addElement("ifthenelse"); //$NON-NLS-1$ addGeneralElements(resultNode, insertInto); } else if (node instanceof IAbstractContainerPO) { insertInto = element.addElement("container"); //$NON-NLS-1$ addGeneralElements(resultNode, insertInto); } else if (node instanceof IWhileDoPO) { insertInto = element.addElement("whiledo"); //$NON-NLS-1$ addGeneralElements(resultNode, insertInto); } else if (node instanceof IDoWhilePO) { insertInto = element.addElement("dowhile"); //$NON-NLS-1$ addGeneralElements(resultNode, insertInto); } else if (node instanceof IIteratePO) { insertInto = element.addElement("repeat"); //$NON-NLS-1$ addGeneralElements(resultNode, insertInto); } addParamNodeElements(resultNode, insertInto); return insertInto; } /** * Adds Parameter elements to the given element based on the given * Test Result Node. * * @param resultNode The source for Parameter data. * @param insertInto The target for the Parameter data. */ protected void addParamNodeElements( TestResultNode resultNode, Element insertInto) { for (TestResultParameter parameter : resultNode.getParameters()) { String name = parameter.getName(); String type = parameter.getType(); String value = parameter.getValue(); Element paramEl = insertInto.addElement("parameter"); //$NON-NLS-1$ if (name != null) { Element paramNameEl = paramEl.addElement("parameter-name"); //$NON-NLS-1$ paramNameEl.addText(name); } if (type != null) { Element paramTypeEl = paramEl.addElement("parameter-type"); //$NON-NLS-1$ paramTypeEl.addText(type); } if (value != null) { Element paramValueEl = paramEl.addElement("parameter-value"); //$NON-NLS-1$ paramValueEl.addText(value); } if (!paramEl.hasContent()) { insertInto.remove(paramEl); } } } /** * adds information for a Cap to the XML file * * @param resultNode * the actual node * @param insertInto * where to insert elements in xml * @param node * NodePO */ protected void addCapElements(TestResultNode resultNode, Element insertInto, ICapPO node) { ICapPO cap = node; getTimestampFromResultNode(resultNode, insertInto); Element compEl = insertInto.addElement("component-name"); //$NON-NLS-1$ compEl.addText( StringUtils.defaultString(resultNode.getComponentName())); Element compTypeEl = insertInto.addElement("component-type"); //$NON-NLS-1$ compTypeEl.addText(CompSystemI18n.getString(cap.getComponentType(), true)); double heuristicMatch = resultNode.getOmHeuristicEquivalence(); if (heuristicMatch >= 0) { Element heuristicMatchElement = insertInto .addElement("component-heuristic-match"); //$NON-NLS-1$ heuristicMatchElement.addText(String.valueOf(heuristicMatch)); } Element actionEl = insertInto.addElement("action-type"); //$NON-NLS-1$ actionEl.addText(CompSystemI18n.getString(cap.getActionName(), true)); if (StringUtils.isNotBlank(resultNode.getCommandLog())) { Element commandEl = insertInto.addElement("command-log"); //$NON-NLS-1$ commandEl.addCDATA(resultNode.getCommandLog()); } if (resultNode.getStatus() == TestResultNode.ERROR || resultNode.getStatus() == TestResultNode.RETRYING) { Element error = insertInto.addElement("error"); //$NON-NLS-1$ Element errorType = error.addElement("type"); //$NON-NLS-1$ TestErrorEvent event = resultNode.getEvent(); if (event != null) { errorType.addText(I18n.getString(event.getId(), true)); Map<String, Object> eventProps = event.getProps(); if (eventProps.containsKey( TestErrorEvent.Property.DESCRIPTION_KEY)) { String key = (String) eventProps.get( TestErrorEvent.Property.DESCRIPTION_KEY); Object[] args = (Object[]) eventProps.get( TestErrorEvent.Property.PARAMETER_KEY); args = args != null ? args : new Object[0]; Element mapEntry = error.addElement("description"); //$NON-NLS-1$ if (mapEntry != null && key != null) { mapEntry.addText(resultNode.hasBackingNode() ? String .valueOf(I18n.getString(key, args)) : key); } } else { for (Map.Entry<String, Object> entry : eventProps.entrySet()) { if (!TestErrorEvent.Property.DESCRIPTION_KEY.equals( entry.getKey())) { Element mapEntry = error.addElement(entry.getKey()); mapEntry.addText(String.valueOf(entry.getValue())); } } } } if (ClientTest.instance().isScreenshotForXML()) { final byte[] screenshotData = resultNode.getScreenshot(); if (screenshotData != null) { Element screenshotElement = error.addElement("screenshot"); //$NON-NLS-1$ screenshotElement.addText(new String( Base64.encodeBase64(screenshotData, false))); } } } } /** * @param resultNode * the actual node * @param insertInto * where to insert elements in xml */ private void getTimestampFromResultNode(TestResultNode resultNode, Element insertInto) { Element timestampEL = insertInto.addElement("timestamp"); //$NON-NLS-1$ Date time = resultNode.getTimeStamp(); if (time != null) { String timestamp = time.toString(); timestampEL.addText(timestamp); } else { timestampEL.addText(StringConstants.EMPTY); } } /** * @param resultNode * TestResultNode * @param insertInto * Element */ protected void addGeneralElements(TestResultNode resultNode, Element insertInto) { Element name = insertInto.addElement("name"); //$NON-NLS-1$ final INodePO resNode = resultNode.getNode(); name.addText(resNode.getName()); if (resNode.getComment() != null) { Element comment = insertInto.addElement("comment"); //$NON-NLS-1$ comment.addText(resNode.getComment()); } Element status = insertInto.addElement("status"); //$NON-NLS-1$ status.addText(String.valueOf(resultNode.getStatus())); long durationMillis = resultNode.getDuration(getTestResult().getEndTime()); if (durationMillis != -1) { insertInto.addAttribute("duration", //$NON-NLS-1$ DurationFormatUtils.formatDurationHMS(durationMillis)); } if (resNode instanceof ICondStructPO) { Element negated = insertInto.addElement("negated"); //$NON-NLS-1$ negated.addText(Boolean.toString( ((ICondStructPO) resNode).isNegate())); } } /** * * @return The <code>Document</code> associated with this report generator. */ protected Document getDocument() { return m_document; } /** * Hook method called when generating basic XML data. * * @return A user-readable <code>String</code> representing the style of * this report generator. */ protected abstract String getStyleName(); /** * @return the testResult */ public ITestResult getTestResult() { return m_testResult; } /** * @param testResult the testResult to set */ private void setTestResult(ITestResult testResult) { m_testResult = testResult; } }