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(); | |
System.out.println("Starting Test run"); | |
} | |
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 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 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 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 = 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)); | |
} | |
StringBuffer output = new StringBuffer(); | |
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; | |
} | |
} |