// Copyright (C) 2003-2009 by Object Mentor, Inc. All rights reserved. // Released under the terms of the CPL Common Public License version 1.0. package fitnesse.reporting.history; import java.io.Closeable; import java.io.IOException; import java.io.Writer; import java.util.Date; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import fitnesse.FitNesseContext; import fitnesse.reporting.BaseFormatter; import fitnesse.testrunner.WikiTestPageUtil; import fitnesse.testsystems.*; import fitnesse.util.DateTimeUtil; import fitnesse.util.TimeMeasurement; import fitnesse.wiki.PageData; import fitnesse.wiki.WikiPage; import fitnesse.wiki.WikiPageUtil; public class TestXmlFormatter extends BaseFormatter implements ExecutionLogListener, Closeable { private static final Logger LOG = Logger.getLogger(TestXmlFormatter.class.getName()); private final FitNesseContext context; private final WriterFactory writerFactory; private TimeMeasurement currentTestStartTime; private TimeMeasurement totalTimeMeasurement; private StringBuilder outputBuffer; protected final TestExecutionReport testResponse; private TestExecutionReport.TestResult currentResult; public TestXmlFormatter(FitNesseContext context, final WikiPage page, WriterFactory writerFactory) { super(page); this.context = context; this.writerFactory = writerFactory; totalTimeMeasurement = new TimeMeasurement().start(); testResponse = new TestExecutionReport(context.version, page.getPageCrawler().getFullPath().toString()); resetTimer(); } public long startedAt() { return totalTimeMeasurement.startedAt(); } public long runTime() { return currentTestStartTime.elapsed(); } @Override public void testStarted(TestPage testPage) { resetTimer(); appendHtmlToBuffer(WikiPageUtil.getHeaderPageHtml(getPage())); currentResult = newTestResult(); currentResult.dateString = DateTimeUtil.formatDate(new Date()); currentResult.relativePageName = testPage.getName(); currentResult.tags = WikiTestPageUtil.getSourcePage(testPage).getData().getAttribute(PageData.PropertySUITES); testResponse.addResult(currentResult); } @Override public void testOutputChunk(String output) { appendHtmlToBuffer(output); } @Override public void testAssertionVerified(Assertion assertion, TestResult testResult) { if (testResult == null) { return; } Instruction instruction = assertion.getInstruction(); Expectation expectation = assertion.getExpectation(); TestExecutionReport.InstructionResult instructionResult = new TestExecutionReport.InstructionResult(); currentResult.addInstruction(instructionResult); String id = instruction.getId(); instructionResult.instruction = instruction.toString(); instructionResult.slimResult = testResult.toString(); try { TestExecutionReport.Expectation expectationResult = new TestExecutionReport.Expectation(); instructionResult.addExpectation(expectationResult); expectationResult.instructionId = id; expectationResult.type = expectation.getClass().getSimpleName(); expectationResult.actual = testResult.getActual(); expectationResult.expected = testResult.getExpected(); expectationResult.evaluationMessage = testResult.getMessage(); if (testResult.getExecutionResult() != null) { expectationResult.status = testResult.getExecutionResult().toString(); } if (expectation instanceof TableCell) { TableCell cell = (TableCell) expectation; expectationResult.col = Integer.toString(cell.getCol()); expectationResult.row = Integer.toString(cell.getRow()); } } catch (Exception e) { LOG.log(Level.WARNING, "Unable to process assertion " + assertion + " with test result " + testResult, e); } } @Override public void testExceptionOccurred(Assertion assertion, ExceptionResult exceptionResult) { Instruction instruction = assertion.getInstruction(); Expectation expectation = assertion.getExpectation(); TestExecutionReport.InstructionResult instructionResult = new TestExecutionReport.InstructionResult(); currentResult.addInstruction(instructionResult); String id = instruction.getId(); instructionResult.instruction = instruction.toString(); try { TestExecutionReport.Expectation expectationResult = new TestExecutionReport.Expectation(); instructionResult.addExpectation(expectationResult); expectationResult.instructionId = id; expectationResult.type = expectation.getClass().getSimpleName(); expectationResult.evaluationMessage = exceptionResult.getMessage(); expectationResult.status = exceptionResult.getExecutionResult().toString(); } catch (Exception e) { LOG.log(Level.WARNING, "Unable to process assertion " + assertion + " with exception result " + exceptionResult, e); } } @Override public void testComplete(TestPage test, TestSummary testSummary) { currentTestStartTime.stop(); super.testComplete(test, testSummary); currentResult.startTime = currentTestStartTime.startedAt(); addCountsToResult(currentResult, testSummary); currentResult.runTimeInMillis = String.valueOf(currentTestStartTime.elapsed()); testResponse.tallyPageCounts(ExecutionResult.getExecutionResult(test.getName(), testSummary)); } @Override public void testSystemStopped(TestSystem testSystem, Throwable cause) { super.testSystemStopped(testSystem, cause); if (cause != null) { testResponse.tallyPageCounts(ExecutionResult.ERROR); } } protected TestExecutionReport.TestResult newTestResult() { return new TestExecutionReport.TestResult(); } @Override public void close() throws IOException { setTotalRunTimeOnReport(totalTimeMeasurement); if (currentResult != null) { currentResult.content = outputBuffer == null ? null : outputBuffer.toString(); outputBuffer = null; } writeResults(); } private void resetTimer() { currentTestStartTime = new TimeMeasurement().start(); } protected void setTotalRunTimeOnReport(TimeMeasurement totalTimeMeasurement) { testResponse.setTotalRunTimeInMillis(totalTimeMeasurement); } protected void writeResults() throws IOException { writeResults(writerFactory.getWriter(context, getPage(), getPageCounts(), totalTimeMeasurement.startedAt())); } @Override public int getErrorCount() { return getPageCounts().getWrong() + getPageCounts().getExceptions(); } protected void writeResults(Writer writer) throws IOException { VelocityContext velocityContext = new VelocityContext(); velocityContext.put("response", testResponse); Template template = context.pageFactory.getVelocityEngine().getTemplate("testResults.vm"); template.merge(velocityContext, writer); writer.close(); } protected TestSummary getPageCounts() { return testResponse.getFinalCounts(); } private void addCountsToResult(TestExecutionReport.TestResult currentResult, TestSummary testSummary) { currentResult.right = Integer.toString(testSummary.getRight()); currentResult.wrong = Integer.toString(testSummary.getWrong()); currentResult.ignores = Integer.toString(testSummary.getIgnores()); currentResult.exceptions = Integer.toString(testSummary.getExceptions()); } private void appendHtmlToBuffer(String output) { if (outputBuffer == null) { outputBuffer = new StringBuilder(); } outputBuffer.append(output); } @Override public void commandStarted(ExecutionContext context) { testResponse.addExecutionContext(context.getCommand(), context.getTestSystemName()); } @Override public void stdOut(String output) { testResponse.addStdOut(output); } @Override public void stdErr(String output) { testResponse.addStdErr(output); } @Override public void exitCode(int exitCode) { testResponse.exitCode(exitCode); } @Override public void exceptionOccurred(Throwable e) { testResponse.exceptionOccurred(e); } public interface WriterFactory { Writer getWriter(FitNesseContext context, WikiPage page, TestSummary counts, long time) throws IOException; } }