blob: 0f172a5bffc98552d44f3b788f34361947cbe7e9 [file] [log] [blame]
#ifndef __HAYAI_JSONOUTPUTTER
#define __HAYAI_JSONOUTPUTTER
#include <iomanip>
#include <ostream>
#include "hayai_outputter.hpp"
#define JSON_OBJECT_BEGIN "{"
#define JSON_OBJECT_END "}"
#define JSON_ARRAY_BEGIN "["
#define JSON_ARRAY_END "]"
#define JSON_STRING_BEGIN "\""
#define JSON_STRING_END "\""
#define JSON_NAME_SEPARATOR ":"
#define JSON_VALUE_SEPARATOR ","
#define JSON_TRUE "true"
#define JSON_FALSE "false"
namespace hayai
{
/// JSON outputter.
/// Outputs the result of benchmarks in JSON format with the following
/// structure:
///
/// {
/// "format_version": 1,
/// "benchmarks": [{
/// "fixture": "DeliveryMan",
/// "name": "DeliverPackage",
/// "parameters": {
/// "declaration": "std::size_t distance",
/// "value": "1"
/// },
/// "iterations_per_run": 10,
/// "disabled": false,
/// "runs": [{
/// "duration": 3801.889831
/// }, ..]
/// }, {
/// "fixture": "DeliveryMan",
/// "name": "DisabledTest",
/// "iterations_per_run": 10,
/// "disabled": true
/// }, ..]
/// }
///
/// All durations are represented as milliseconds.
class JsonOutputter
: public Outputter
{
public:
/// Initialize JSON outputter.
/// @param stream Output stream. Must exist for the entire duration of
/// the outputter's use.
JsonOutputter(std::ostream& stream)
: _stream(stream),
_firstTest(true)
{
}
virtual void Begin(const std::size_t& enabledCount,
const std::size_t& disabledCount)
{
(void)enabledCount;
(void)disabledCount;
_stream <<
JSON_OBJECT_BEGIN
JSON_STRING_BEGIN "format_version" JSON_STRING_END
JSON_NAME_SEPARATOR
"1"
JSON_VALUE_SEPARATOR
JSON_STRING_BEGIN "benchmarks" JSON_STRING_END
JSON_NAME_SEPARATOR
JSON_ARRAY_BEGIN;
}
virtual void End(const std::size_t& executedCount,
const std::size_t& disabledCount)
{
(void)executedCount;
(void)disabledCount;
_stream <<
JSON_ARRAY_END
JSON_OBJECT_END;
}
virtual void BeginTest(const std::string& fixtureName,
const std::string& testName,
const TestParametersDescriptor& parameters,
const std::size_t& runsCount,
const std::size_t& iterationsCount)
{
BeginTestObject(fixtureName,
testName,
parameters,
runsCount,
iterationsCount,
false);
}
virtual void SkipDisabledTest(const std::string& fixtureName,
const std::string& testName,
const TestParametersDescriptor& parameters,
const std::size_t& runsCount,
const std::size_t& iterationsCount)
{
BeginTestObject(fixtureName,
testName,
parameters,
runsCount,
iterationsCount,
true);
EndTestObject();
}
virtual void EndTest(const std::string& fixtureName,
const std::string& testName,
const TestParametersDescriptor& parameters,
const TestResult& result)
{
(void)fixtureName;
(void)testName;
(void)parameters;
_stream <<
JSON_VALUE_SEPARATOR
JSON_STRING_BEGIN "runs" JSON_STRING_END
JSON_NAME_SEPARATOR
JSON_ARRAY_BEGIN;
const std::vector<uint64_t>& runTimes = result.RunTimes();
for (std::vector<uint64_t>::const_iterator it = runTimes.begin();
it != runTimes.end();
++it)
{
if (it != runTimes.begin())
_stream << JSON_VALUE_SEPARATOR;
_stream << JSON_OBJECT_BEGIN
JSON_STRING_BEGIN "duration" JSON_STRING_END
JSON_NAME_SEPARATOR
<< std::fixed
<< std::setprecision(6)
<< (double(*it) / 1000000.0)
<< JSON_OBJECT_END;
}
_stream <<
JSON_ARRAY_END;
WriteDoubleProperty("mean", result.RunTimeAverage());
WriteDoubleProperty("std_dev", result.RunTimeStdDev());
WriteDoubleProperty("median", result.RunTimeMedian());
WriteDoubleProperty("quartile_1", result.RunTimeQuartile1());
WriteDoubleProperty("quartile_3", result.RunTimeQuartile3());
EndTestObject();
}
private:
void BeginTestObject(const std::string& fixtureName,
const std::string& testName,
const TestParametersDescriptor& parameters,
const std::size_t& runsCount,
const std::size_t& iterationsCount,
bool disabled)
{
(void)runsCount;
if (_firstTest)
_firstTest = false;
else
_stream << JSON_VALUE_SEPARATOR;
_stream <<
JSON_OBJECT_BEGIN
JSON_STRING_BEGIN "fixture" JSON_STRING_END
JSON_NAME_SEPARATOR;
WriteString(fixtureName);
_stream <<
JSON_VALUE_SEPARATOR
JSON_STRING_BEGIN "name" JSON_STRING_END
JSON_NAME_SEPARATOR;
WriteString(testName);
_stream <<
JSON_VALUE_SEPARATOR;
const std::vector<TestParameterDescriptor>& descs =
parameters.Parameters();
if (!descs.empty())
{
_stream <<
JSON_STRING_BEGIN "parameters" JSON_STRING_END
JSON_NAME_SEPARATOR
JSON_ARRAY_BEGIN;
for (std::size_t i = 0; i < descs.size(); ++i)
{
if (i)
_stream << JSON_VALUE_SEPARATOR;
const TestParameterDescriptor& desc = descs[i];
_stream <<
JSON_OBJECT_BEGIN
JSON_STRING_BEGIN "declaration" JSON_STRING_END
JSON_NAME_SEPARATOR;
WriteString(desc.Declaration);
_stream <<
JSON_VALUE_SEPARATOR
JSON_STRING_BEGIN "value" JSON_STRING_END
JSON_NAME_SEPARATOR;
WriteString(desc.Value);
_stream <<
JSON_OBJECT_END;
}
_stream <<
JSON_ARRAY_END
JSON_VALUE_SEPARATOR;
}
_stream <<
JSON_STRING_BEGIN "iterations_per_run" JSON_STRING_END
JSON_NAME_SEPARATOR << iterationsCount <<
JSON_VALUE_SEPARATOR
JSON_STRING_BEGIN "disabled" JSON_STRING_END
JSON_NAME_SEPARATOR << (disabled ? JSON_TRUE : JSON_FALSE);
}
inline void EndTestObject()
{
_stream <<
JSON_OBJECT_END;
}
/// Write an escaped string.
/// The escaping is currently very rudimentary and assumes that names,
/// parameters etc. are ASCII.
///
/// @param str String to write.
void WriteString(const std::string& str)
{
_stream << JSON_STRING_BEGIN;
std::string::const_iterator it = str.begin();
while (it != str.end())
{
char c = *it++;
switch (c)
{
case '\\':
case '"':
case '/':
_stream << "\\" << c;
break;
case '\b':
_stream << "\\b";
break;
case '\f':
_stream << "\\f";
break;
case '\n':
_stream << "\\n";
break;
case '\r':
_stream << "\\r";
break;
case '\t':
_stream << "\\t";
break;
default:
_stream << c;
break;
}
}
_stream << JSON_STRING_END;
}
/// Write a property with a double value.
/// @param key Property key.
/// @param value Property value.
void WriteDoubleProperty(const std::string& key, const double value)
{
_stream << JSON_VALUE_SEPARATOR
<< JSON_STRING_BEGIN
<< key
<< JSON_STRING_END
<< JSON_NAME_SEPARATOR
<< std::fixed
<< std::setprecision(6)
<< (value / 1000000.0);
}
std::ostream& _stream;
bool _firstTest;
};
}
#undef JSON_OBJECT_BEGIN
#undef JSON_OBJECT_END
#undef JSON_ARRAY_BEGIN
#undef JSON_ARRAY_END
#undef JSON_STRING_BEGIN
#undef JSON_STRING_END
#undef JSON_NAME_SEPARATOR
#undef JSON_VALUE_SEPARATOR
#undef JSON_TRUE
#undef JSON_FALSE
#endif