blob: 4d8bf66dbd998a61b776a4ac866366842930746c [file] [log] [blame]
/* Copyright 2004 The Apache Software Foundation
*
* Licensed 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 tools.JUnit;
import java.util.*;
import java.io.*;
import java.text.SimpleDateFormat;
import junit.framework.Test;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import noNamespace.TestResultContainerDocument.TestResultContainer;
import noNamespace.*;
import noNamespace.TestResultType.ExecutionOutput;
import org.apache.xmlbeans.XmlOptions;
import tools.io.TeeOutputStream;
import org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner;
/**
* Implementation of JUnitXResultFormatter that records JUnit results
* and publishes the result as a XML Document
*/
public class XmlResultFormatterImpl implements JUnitXResultFormatter
{
public static int TEST_SUCCESS = 0;
public static int TEST_FAILURE = 1;
public static int TEST_ERROR = 2;
public static int TEST_UNKNOWN = 3;
// Lets capture STDOUT and STDERR
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
ByteArrayOutputStream bErr = new ByteArrayOutputStream();
TeeOutputStream tOut;
TeeOutputStream tErr;
PrintStream _out;
PrintStream _err;
private TestRecord testRecord;
Collection records;
// By default write to StdOut
OutputStream logOut = System.out;
// Record Stats
int testCount = 0;
int passCount = 0;
int failCount = 0;
long startTime = 0;
boolean showOutput = false;
public void startRun()
{
records = new ArrayList();
// Reset stats
testCount = 0;
passCount = 0;
failCount = 0;
startTime = System.currentTimeMillis();
}
public void endRun()
{
System.out.println("=================================================");
System.out.println("Tests Ran: " + testCount);
System.out.println("Success: " + passCount);
System.out.println("Failures: " + failCount);
// Generate log:
System.out.println("Starting Publish: " + System.currentTimeMillis());
publishResults();
System.out.println("Finished Publish: " + System.currentTimeMillis());
}
public void setOutput(OutputStream out)
{
if (out != null)
logOut = out;
}
public void showTestOutput(boolean show)
{
showOutput = show;
}
public void info(Object msg)
{
if (showOutput)
System.out.println(msg);
}
/*
* Implementation of TestListener
*/
public synchronized void startTest(Test test)
{
String fullTestName = test.toString();
info("Starting Test: " + fullTestName);
// Lets start the capture
System.out.flush();
System.err.flush();
_out = System.out;
_err = System.err;
bOut.reset();
bErr.reset();
// Redirect Stdout & Stderr to both console and our capture stream
if (showOutput)
{
tOut = new TeeOutputStream(_out, bOut);
tErr = new TeeOutputStream(_err, bErr);
System.setOut(new PrintStream(tOut));
System.setErr(new PrintStream(tErr));
}
else
{
System.setOut(new PrintStream(bOut));
System.setErr(new PrintStream(bErr));
}
// Discard the previous record
testRecord = new TestRecord(fullTestName);
testRecord.setStartTime(System.currentTimeMillis());
}
public synchronized void endTest(Test test)
{
long endTime = System.currentTimeMillis();
System.out.flush();
System.err.flush();
// Update the test record
testRecord.setSysout(bOut.toString());
testRecord.setSyserr(bErr.toString());
testRecord.setEndTime(endTime);
// this is a little hack for our reporting..
// We could be on shaky ground if the behaviour of the JUnit task
// ever changes.. OH Well...
String fullTestName = test.toString();
// Test-unit is between '(' and ')'
int startindex = fullTestName.indexOf("(");
int lastindex = fullTestName.indexOf(")");
String testUnit;
if (startindex >= 0 && lastindex > startindex)
testUnit = fullTestName.substring(startindex+1, lastindex);
else
testUnit = fullTestName;
String testMethod = ((TestCase) test).getName();
// Get the last token from testUnit for the logical name
startindex = testUnit.lastIndexOf(".");
String baseClass = testUnit.substring(startindex+1);
// update the extra fields of TestRecord
testRecord.setTestUnitName(testUnit);
testRecord.setTestLogicalName(baseClass + "." + testMethod);
// If the test did not fail, record it as a success
if (!testRecord.isFailure())
{
testRecord.setStatus(TEST_SUCCESS);
passCount++;
}
else
failCount++;
testCount++;
// Add it to the set
records.add(testRecord);
// set testRecord to null..
// Restore STDOUT and STDERR
System.setOut(_out);
System.setErr(_err);
info("Finished Test: " + fullTestName + " "
+ testRecord.getStatusString() + "\n");
// Reset TestRecord
testRecord = null;
}
public synchronized void addError(final Test test, final Throwable t)
{
if (testRecord == null)
testRecord = getMissingTestRecord();
testRecord.setStatus(TEST_ERROR);
testRecord.setThrowable(t);
// Special case when test class is missing...
if (t.toString().indexOf("ClassNotFoundException") > -1)
{
records.add(testRecord);
testRecord = null;
}
}
public synchronized void addFailure(final Test test, final AssertionFailedError t)
{
if (testRecord == null)
testRecord = getMissingTestRecord();
testRecord.setStatus(TEST_FAILURE);
testRecord.setThrowable(t);
}
/**
* Utility class to record per test data like test name, status, start
* and end time, STDOUT, STDERR from text execution etc.
*/
private class TestRecord
{
public TestRecord(String name)
{
setTestname(name);
}
private String testname;
private String sysout;
private String syserr;
private Throwable t;
private long startTime;
private long endTime;
private int status;
boolean failed = false;
private String testUnitName;
private String testLogicalName;
public void setTestname(String name)
{
this.testname = name;
}
public String getTestname()
{
return testname;
}
public void setStatus(int status)
{
this.status = status;
if (status == TEST_ERROR || status == TEST_FAILURE)
failed = true;
}
public int getStatus()
{
return status;
}
public String getStatusString()
{
return (status == TEST_SUCCESS)?"SUCCESS":
(status == TEST_ERROR)?"ERROR":"FAILURE";
}
public boolean isFailure()
{
return failed;
}
public String getSysout()
{
return sysout;
}
public void setSysout(String sysout)
{
this.sysout = sysout;
}
public String getSyserr()
{
return syserr;
}
public void setSyserr(String syserr)
{
this.syserr = syserr;
}
public Throwable getThrowable()
{
return t;
}
public void setThrowable(Throwable t)
{
this.t = t;
}
public long getStartTime()
{
return startTime;
}
public void setStartTime(long startTime)
{
this.startTime = startTime;
}
public long getEndTime()
{
return endTime;
}
public void setEndTime(long endTime)
{
this.endTime = endTime;
}
public String getTestUnitName()
{
return testUnitName;
}
public void setTestUnitName(String testUnitName)
{
this.testUnitName = testUnitName;
}
public String getTestLogicalName()
{
return testLogicalName;
}
public void setTestLogicalName(String testLogicalName)
{
this.testLogicalName = testLogicalName;
}
}
private TestRecord getMissingTestRecord()
{
TestRecord tr = new TestRecord("Missing");
tr.setStartTime(System.currentTimeMillis());
tr.setEndTime(System.currentTimeMillis());
tr.setStatus(TEST_ERROR);
tr.setTestLogicalName("Missing");
tr.setTestUnitName("Missing");
return tr;
}
public void publishResults()
{
TestLogDocument logDoc = TestLogDocument.Factory.newInstance();
TestLogDocument.TestLog log = logDoc.addNewTestLog();
// Populate the attributes for test-log
// testtype
String testtype = System.getProperty("TESTTYPE", "AUTO");
if (testtype.equalsIgnoreCase("AUTO"))
log.setTesttype(TestLogDocument.TestLog.Testtype.AUTOMATED);
else
log.setTesttype(TestLogDocument.TestLog.Testtype.MANUAL);
// runid
String dateFormatStr = "_yy_MMM_dd_HH_mm_ss_SS";
String dateStr = new SimpleDateFormat(dateFormatStr).format(new java.util.Date(startTime));
String defRunId = System.getProperty("user.name").toUpperCase() + dateStr;
String runId = System.getProperty("RUNID", defRunId);
log.setRunid(runId);
// hostname
String hostname;
try
{
hostname = java.net.InetAddress.getLocalHost().getHostName();
} catch (Exception e)
{
// Ignore.. not critical
hostname = "UNKNOWN_HOST";
}
log.setHostname(hostname);
// TODO: set Defaults/check sysprop for other attributes
// Add <environment> element
EnvironmentType env = log.addNewEnvironment();
Map envMap = new HashMap();
envMap.put("JVM_NAME", System.getProperty("java.vm.name"));
envMap.put("JVM_VENDOR", System.getProperty("java.vm.vendor"));
envMap.put("JVM_VERSION", System.getProperty("java.vm.version"));
envMap.put("OS", System.getProperty("os.name"));
String defFreq = "checkin";
envMap.put("Frequency", System.getProperty("test.run.frequency", defFreq));
Iterator itr = envMap.keySet().iterator();
int envCount = 0;
while (itr.hasNext())
{
EnvironmentType.EnvAttribute envAttr = env.addNewEnvAttribute();
String name = (String) itr.next();
String value = (String) envMap.get(name);
envAttr.setValue(value);
envAttr.setName(name);
}
// Add <header-info> element
TestLogDocument.TestLog.HeaderInfo hdrInfo = log.addNewHeaderInfo();
hdrInfo.setResultcount(Integer.toString(testCount));
hdrInfo.setChecksum(Integer.toString(testCount));
hdrInfo.setExecdate(new java.util.Date(startTime).toString());
hdrInfo.setExecaccount(System.getProperty("user.name"));
// Add test-results
Iterator rItr = records.iterator();
while (rItr.hasNext())
{
TestResultType tr = log.addNewTestResult();
tr.set(getTestResultType((TestRecord) rItr.next()));
}
// Publish it to the outputStream
XmlOptions opts = new XmlOptions().setSavePrettyPrint();
try
{
logOut.write(logDoc.xmlText(opts).getBytes());
} catch (IOException ioe)
{
System.out.println("XmlResultFormatter: Unable to publish results");
System.out.println(ioe.toString());
}
}
/**
* Creates the TestResultDocument and returns the Xml
*/
private TestResultType getTestResultType(TestRecord rec)
{
TestResultType tr = TestResultType.Factory.newInstance();
// Children of TestResult
TestResultType.TestCase tc = tr.addNewTestCase();
TestResultType.ExecutionOutput exo = tr.addNewExecutionOutput();
// Set the logical test name... 'Class.Methodname'
tr.setLogicalname(rec.getTestLogicalName());
// Set the test Start time as a String
tr.setExectime(new java.util.Date(rec.getStartTime()).toString());
String status = rec.getStatusString();
// Set the test result
if (status.equals("SUCCESS"))
tr.setResult(TestResultType.Result.SUCCESS);
else if (status.equals("FAILURE"))
tr.setResult(TestResultType.Result.FAILURE);
else
tr.setResult(TestResultType.Result.ABORT);
// Set the test execution time.. in milliseconds
String dur = java.lang.Long.toString(rec.getEndTime() - rec.getStartTime());
tr.setDuration(dur);
// Set the completion status..
tr.setIsdone(TestResultType.Isdone.TRUE);
// Setup the children elements
// test-case
tc.setTestcasename(rec.getTestLogicalName());
tc.setTestunit(rec.getTestUnitName());
// This should ideally be the whole path to the class...
tc.setTestpath(rec.getTestname());
// execution-output
// if FAILURE.. set erroname attribute
if (rec.isFailure())
{
String exp = rec.getThrowable().toString();
int index = exp.indexOf(":");
// the above line is very flaky..
if (index < 0) index = exp.length();
exo.setErrorname(exp.substring(0, index));
}
StringBuilder output = new StringBuilder();
String eol = System.getProperty("line.separator");
output.append("[STDOUT]").append(eol);
output.append(rec.getSysout()).append(eol);
output.append("[STDERR]").append(eol);
output.append(rec.getSyserr()).append(eol);
if (rec.isFailure())
{
output.append("[EXCEPTION]").append(eol);
output.append(JUnitTestRunner.getFilteredTrace(rec.getThrowable()));
}
exo.setOutputDetails(output.toString());
return tr;
}
}