/**
* Copyright 2013, Landz and its contributors. All rights reserved.
*
* 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 z.testware.benchmark;
import java.io.*;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
/**
* {@link IResultsConsumer} that writes XML files for each benchmark.
*/
public final class XMLConsumer extends AutocloseConsumer implements Closeable
{
/**
* Timestamp format.
*/
public static final String TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
/**
* Output XML writer.
*/
private Writer writer;
/**
* Internal buffer for appends.
*/
private final StringBuilder b = new StringBuilder();
/**
* Number format.
*/
private final NumberFormat nf;
/**
* Instantiate from global options.
*/
public XMLConsumer() throws IOException
{
this(getDefaultOutputFile());
}
/*
*
*/
public XMLConsumer(File fileName) throws IOException
{
writer = new OutputStreamWriter(new FileOutputStream(fileName), "UTF-8");
addAutoclose(this);
writer.write("<benchmark-results tstamp=\"" + tstamp() + "\">\n\n");
nf = NumberFormat.getInstance(Locale.ENGLISH);
nf.setMaximumFractionDigits(3);
nf.setGroupingUsed(false);
}
/**
* Accept a single benchmark result.
*/
public void accept(Result result) throws IOException
{
// We emit XML by hand. If anything more difficult comes up, we can switch
// to SimpleXML or some other XML binding solution.
b.setLength(0);
b.append("\t<testname");
attribute(b, "classname", result.getTestClassName());
attribute(b, "name", result.getTestMethodName());
b.append("\n\t\t");
attribute(b, "benchmark-rounds", Integer.toString(result.benchmarkRounds));
attribute(b, "warmup-rounds", Integer.toString(result.warmupRounds));
b.append("\n\t\t");
attribute(b, "round-avg", nf.format(result.roundAverage.avg));
attribute(b, "round-stddev", nf.format(result.roundAverage.stddev));
b.append("\n\t\t");
attribute(b, "gc-avg", nf.format(result.gcAverage.avg));
attribute(b, "gc-stddev", nf.format(result.gcAverage.stddev));
b.append("\n\t\t");
attribute(b, "gc-invocations", Long.toString(result.gcInfo.accumulatedInvocations()));
attribute(b, "gc-time", nf.format(result.gcInfo.accumulatedTime() / 1000.0));
b.append("\n\t\t");
attribute(b, "benchmark-time-total", nf.format(result.benchmarkTime * 0.001));
attribute(b, "warmup-time-total", nf.format(result.warmupTime * 0.001));
b.append("\n\t\t");
attribute(b, "threads", Integer.toString(result.getThreadCount()));
b.append("/>\n\n");
writer.write(b.toString());
writer.flush();
}
/**
* Close the output XML stream.
*/
public void close()
{
try
{
if (this.writer != null)
{
writer.write("</benchmark-results>");
writer.close();
writer = null;
removeAutoclose(this);
}
}
catch (IOException e)
{
// Ignore.
}
}
/**
* Returns the default output file.
*/
private static File getDefaultOutputFile()
{
final String xmlPath = System.getProperty(BenchmarkOptionsSystemProperties.XML_FILE_PROPERTY);
if (xmlPath != null && !xmlPath.trim().equals(""))
{
return new File(xmlPath);
}
throw new IllegalArgumentException("Missing global property: "
+ BenchmarkOptionsSystemProperties.XML_FILE_PROPERTY);
}
/**
* Unique timestamp for this XML consumer.
*/
private static String tstamp()
{
SimpleDateFormat sdf = new SimpleDateFormat(TIMESTAMP_FORMAT);
return sdf.format(new Date());
}
/**
* Append an attribute to XML.
*/
private void attribute(StringBuilder b, String attrName, String value)
{
b.append(' ');
b.append(attrName);
b.append("=\"");
b.append(Escape.xmlAttrEscape(value));
b.append('"');
}
}