diff options
Diffstat (limited to 'third_party/include/hayai/hayai_json_outputter.hpp')
-rwxr-xr-x | third_party/include/hayai/hayai_json_outputter.hpp | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/third_party/include/hayai/hayai_json_outputter.hpp b/third_party/include/hayai/hayai_json_outputter.hpp new file mode 100755 index 0000000..0f172a5 --- /dev/null +++ b/third_party/include/hayai/hayai_json_outputter.hpp @@ -0,0 +1,355 @@ +#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 |