package com.skaringa.perftest;
import java.io.File;
import java.util.Date;
import java.util.Hashtable;
import java.util.Map;
import java.util.Vector;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import com.skaringa.javaxml.NoImplementationException;
import com.skaringa.javaxml.ObjectTransformer;
import com.skaringa.javaxml.ObjectTransformerFactory;
import com.skaringa.perftest.config.PerfTestParams;
import com.skaringa.perftest.config.PerfTestSuiteParams;
import com.skaringa.util.Log;
/**
* Program to execute performance tests.
* A test suite contains a number of tests.
* The suite and the tests can be configured by an XML file.
* Tests can be executed in sequential order or parallel in different threads.
* The result is written to XML and HTML files.
*/
public final class PerfTestSuite {
private String _name;
// transformer for xml in/output
private ObjectTransformer _trans = null;
// transformer for html output
private ObjectTransformer _htmlTrans = null;
// name of resource with transformation instructions for html output
private String _xsltResource;
private PerfTestSuiteParams _suite = null;
private Class _testClass;
/**
* ctor
* @param name The name of the suite.
* It must match the name of the config file without .xml.
*
*/
public PerfTestSuite(String name) {
_name = name;
}
/**
* ctor
* @param name The name of the suite.
* It must match the name of the config file without .xml.
* @param xsltResource The name of the resource with
* transformation instructions for html output.
*
*/
public PerfTestSuite(String name, String xsltResource) {
_name = name;
_xsltResource = xsltResource;
}
/**
* Setup this test suite.
*/
public void setup() {
// read the configuration
File configFile = new File(_name + ".xml");
if (!configFile.canRead()) {
Log.error(
"No readable configuration file found for suite with name: " + _name);
return;
}
try {
initObjectTransformer();
_suite =
(PerfTestSuiteParams) _trans.deserialize(new StreamSource(configFile));
if (_suite.name != null && _suite.name.length() > 0) {
_name = _suite.name;
}
if (_suite.testclass == null || _suite.testclass.length() == 0) {
Log.error("Attribute testclass is not defined.");
return;
}
}
catch (Exception e) {
Log.error("Can't setup test: " + e);
}
// load the test class
try {
_testClass = Class.forName(_suite.testclass);
}
catch (ClassNotFoundException e) {
Log.error("Test class " + _suite.testclass + " not found in CLASSPATH.");
}
// obtain info for OS and JVM
_suite.os =
System.getProperty("os.name")
+ " "
+ System.getProperty("os.version")
+ " / "
+ System.getProperty("os.arch");
_suite.jvm =
System.getProperty("java.version")
+ " "
+ System.getProperty("java.vendor");
// obtain info of JAXP implementation
obtainJAXPImplInfo();
Log.info(
"Test suite "
+ _name
+ " for class "
+ _suite.testclass
+ " has been set up.");
}
/**
* Run the test suite.
* Dependend on the attribute parallel, the tests are run in sequential order
* or by executing parallel threads.
*/
public void run() {
if (_testClass == null) {
Log.error("Suite was not set up properly.");
return;
}
_suite.testDate = new Date();
// invoke all defined tests
Map allTests = new Hashtable();
ThreadGroup perftestThreadGroup = new ThreadGroup("perftestThreadGroup");
int numberOfTests = _suite.perftests.length;
for (int i = 0; i < numberOfTests; ++i) {
try {
PerfTest perftest = (PerfTest) _testClass.newInstance();
perftest.setParams(_suite.perftests[i]);
allTests.put(perftest.getTestId(), perftest);
_suite.perftests[i].id = perftest.getTestId();
// run the test
if (_suite.parallel) {
// execute the test as a new thread
perftest.start();
try {
Thread.sleep(100);
}
catch (InterruptedException e) {
}
}
else {
// execute the test in the current thread
perftest.run();
}
}
catch (InstantiationException e) {
Log.error("Failed to create test: " + e);
_suite.perftests[i].error = true;
}
catch (IllegalAccessException e) {
Log.error("Failed to create test: " + e);
_suite.perftests[i].error = true;
}
}
if (_suite.parallel) {
Log.info("waiting for the tests to finish ...");
}
// collect the test results
for (int i = 0; i < numberOfTests; ++i) {
// get the id of the test
String id = _suite.perftests[i].id;
// get the test object
PerfTest perftest = (PerfTest) allTests.get(id);
if (_suite.parallel) {
try {
// join the test thread and wait for it to finish
perftest.join();
}
catch (InterruptedException e) {
Log.error(e);
}
}
// add the result to perftest VO
long time = perftest.getTime() / perftest.getLoopCount();
_suite.perftests[i].time = time;
_suite.perftests[i].error = perftest.getError();
}
}
/**
* Write the XML result file for this suite.
* @param outputFile The result file.
*/
public void writeResultXML(File outputFile) {
if (_testClass == null) {
Log.error("Suite was not set up properly.");
return;
}
try {
Log.info("Writing xml result file " + outputFile.toString());
_trans.serialize(_suite, new StreamResult(outputFile));
Log.info("done.");
}
catch (Exception e) {
Log.error("Can't write resulting XML: " + e);
}
}
/**
* Write a HTML result file for this suite.
* This is done by an XSL transformation.
* @param outputFile The result file.
*/
public void writeResultHTML(File outputFile) {
if (_testClass == null) {
Log.error("Suite was not set up properly.");
return;
}
if (_htmlTrans == null) {
Log.error(
"No HTML output available (possible cause: invalid stylesheet");
}
try {
Log.info("Writing html result file " + outputFile.toString());
_htmlTrans.serialize(_suite, new StreamResult(outputFile));
Log.info("done.");
}
catch (Exception e) {
Log.error("Can't write resulting HTML: " + e);
}
}
/**
* Get and configure the ObjectTransformers for the output of the results.
* @throws NoImplementationException if no ObjectTransformers are available.
*/
private void initObjectTransformer() throws NoImplementationException {
if (_trans == null) {
// Get an ObjectTransformer for reading/writing xml.
_trans = ObjectTransformerFactory.getInstance().getImplementation();
// The transformer should create extra line feeds in the output.
_trans.setProperty(javax.xml.transform.OutputKeys.INDENT, "yes");
// Use western european encoding in the XML output.
_trans.setProperty(javax.xml.transform.OutputKeys.ENCODING, "ISO-8859-1");
// Use a short output format.
_trans.setProperty(
com.skaringa.javaxml.PropertyKeys.OMIT_XSI_TYPE,
"true");
}
if (_htmlTrans == null && _xsltResource != null) {
// Get an ObjectTransformer for writing html.
_htmlTrans = ObjectTransformerFactory.getInstance().getImplementation();
// The transformer should create extra line feeds in the output.
_htmlTrans.setProperty(javax.xml.transform.OutputKeys.INDENT, "yes");
// Use western european encoding in the XML output.
_htmlTrans.setProperty(
javax.xml.transform.OutputKeys.ENCODING,
"ISO-8859-1");
// use a stylesheet for xml->html postprocessing
_htmlTrans.setPostprocessorInstruction(
new StreamSource(ClassLoader.getSystemResourceAsStream(_xsltResource)));
}
}
/**
* Obtain info of JAXP implementation classes.
*/
private void obtainJAXPImplInfo() {
try {
SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
_suite.saxParser = saxParser.getClass().getName();
Transformer trans = SAXTransformerFactory.newInstance().newTransformer();
_suite.transformer = trans.getClass().getName();
}
catch (Exception e) {
Log.warn(e.toString());
}
}
/**
* Write an example configuration file.
*/
private void writeExampleConfigFile() {
PerfTestSuiteParams suite = new PerfTestSuiteParams();
suite.name = "SamplePerfTestSuite";
suite.testclass = "full.name.of.testclass";
suite.parallel = false;
suite.hardware = "Z80, 4 Mhz, 64 KByte Memory";
suite.comment =
"This is a sample configuration file for a performance test suite.";
suite.perftests = new PerfTestParams[5];
for (int i = 0; i < 5; ++i) {
PerfTestParams test = new PerfTestParams();
test.method = "testMethod" + i;
test.loopCount = 10;
test.dataSize = 500;
test.dataType = "full.name.of.class.of.testobject";
test.randomData = false;
test.cleanup = true;
suite.perftests[i] = test;
}
String fileName = _name + ".xml";
String xsdFileName = "perftest_config.xsd";
try {
// get an ObjectTransformer
initObjectTransformer();
// Serialize the example configuration into a file.
_trans.serialize(suite, new StreamResult(new File(fileName)));
// write the schema definition to another file
Vector componentTypes = new Vector();
componentTypes.add(PerfTestParams.class);
_trans.writeXMLSchema(
PerfTestSuiteParams.class,
componentTypes,
new StreamResult(new File(xsdFileName)));
}
catch (Exception e) {
Log.error(e);
return;
}
System.err.println("An example configuration file was created.");
System.err.println("Filename :" + fileName);
System.err.println(
"An XML schema definition of the configuaration file was saved to "
+ xsdFileName);
}
/**
* Main:
* Execute the Test Suite.
* @param args The command line has to contain o single argument
* which is the name of the test suite.
*/
public static void main(String[] args) {
String xsltResource = "com/skaringa/perftest/result.xsl";
if (args.length == 0) {
System.err.println(
"usage: java " + PerfTestSuite.class.getName() + " suite_name");
PerfTestSuite sampleSuite = new PerfTestSuite("perftest_example_config");
sampleSuite.writeExampleConfigFile();
System.exit(1);
}
for (int i = 0; i < args.length; ++i) {
String suiteName = args[i];
File resultXML = new File(suiteName + "-result.xml");
File resultHTML = new File(suiteName + "-result.html");
PerfTestSuite theSuite = new PerfTestSuite(suiteName, xsltResource);
theSuite.setup();
theSuite.run();
theSuite.writeResultXML(resultXML);
theSuite.writeResultHTML(resultHTML);
}
}
}