Part 1: JSON Test Harness: Part 1
Part 2: JSON Test Harness: Part 2
Time to review some test harness code I have written.
https://github.com/Loki-Astari/JsonBenchmark
I wrote a C++ JSON parser/serializer and wanted to compare performance against other JSON parsers. This was originally written by somebody else (credit were credit is due (see README)) but has been highly modified to use better C++ idioms (I hope).
This questions covers the simpler tests. These are all part of the "Conformance" tests. Results found here: https://lokiastari.com/Json/Conformance.linux.html
###PassChecker.h
#ifndef THORS_ANVIL_BENCHMARK_PASS_CHECKER_H
#define THORS_ANVIL_BENCHMARK_PASS_CHECKER_H
#include "CommonReader.h"
#include <memory>
namespace ThorsAnvil
{
namespace Benchmark
{
class PassChecker: public CommonReader
{
public:
using CommonReader::CommonReader;
virtual std::string getDir() const override
{
return "jsonchecker_pass";
}
virtual State executeTest(TestBase const& parser, Test const& test) override
{
std::unique_ptr<ParseResultBase> result(parser.Parse(test.input.c_str(), test.input.size()));
return result != nullptr ? Pass : Fail;
}
virtual void generateConPerData(TestBase const& parser, Test const& test, State state) override
{
std::size_t begin = test.path.str().rfind('/') + 1;
std::size_t end = test.path.str().rfind('.');
std::string fileName = test.path.str().substr(begin, (end - begin));
options.conformance << "1. Parse Validation,"
<< parser.GetName() << ","
<< fileName << ","
<< (state == Pass ? "true" : "false")
<< "\n";
}
};
}
}
#endif
###FailChecker.h
#ifndef THORS_ANVIL_BENCHMARK_FAIL_CHECKER_H
#define THORS_ANVIL_BENCHMARK_FAIL_CHECKER_H
#include "CommonReader.h"
#include <memory>
namespace ThorsAnvil
{
namespace Benchmark
{
class FailChecker: public CommonReader
{
public:
using CommonReader::CommonReader;
virtual std::string getDir() const override
{
return "jsonchecker_fail";
}
virtual State executeTest(TestBase const& parser, Test const& test) override
{
std::unique_ptr<ParseResultBase> result(parser.Parse(test.input.c_str(), test.input.size()));
return result == nullptr ? Pass : Fail;
}
virtual void generateConPerData(TestBase const& parser, Test const& test, State state) override
{
std::size_t begin = test.path.str().rfind('/') + 1;
std::size_t end = test.path.str().rfind('.');
std::string fileName = test.path.str().substr(begin, (end - begin));
options.conformance << "1. Parse Validation,"
<< parser.GetName() << ","
<< fileName << ","
<< (state == Pass ? "true" : "false")
<< "\n";
}
};
}
}
#endif
###RoundTripChecker.h
#ifndef THORS_ANVIL_BENCHMARK_ROUNDTRIP_CHECKER_H
#define THORS_ANVIL_BENCHMARK_ROUNDTRIP_CHECKER_H
#include "CommonReader.h"
#include <string>
#include <memory>
#include <cctype>
namespace ThorsAnvil
{
namespace Benchmark
{
class RoundTripChecker: public CommonReader
{
public:
using CommonReader::CommonReader;
virtual void preloadData(Test& test) override
{
CommonReader::preloadData(test);
test.output = stripSpace(test.input);
}
virtual State executeTest(TestBase const& parser, Test const& test) override
{
std::unique_ptr<ParseResultBase> result(parser.Parse(test.input.c_str(), test.input.size()));
if (!result)
{
return NotImplemented;
}
std::unique_ptr<StringResultBase> stringify(parser.Stringify(result.get()));
std::string output;
if (stringify)
{
output = stringify->c_str();
}
return test.output == stripSpace(output) ? Pass : Fail;
}
virtual std::string getDir() const override
{
return "roundtrip";
}
private:
std::string stripSpace(std::string const from)
{
std::string result;
std::copy_if(std::begin(from), std::end(from),
std::back_inserter(result),
[inString = false, lastChar = ' '](char next) mutable
{
if (next == '"' && lastChar != '\\')
{
inString = !inString;
}
lastChar = next;
return !inString && std::isspace(next);
}
);
return result;
}
virtual void generateConPerData(TestBase const& parser, Test const& test, State state) override
{
std::size_t begin = test.path.str().rfind('/') + 1;
std::size_t end = test.path.str().rfind('.');
std::string fileName = test.path.str().substr(begin, (end - begin));
options.conformance << "4. Roundtrip,"
<< parser.GetName() << ","
<< fileName << ","
<< (state == Pass ? "true" : "false")
<< "\n";
}
};
}
}
#endif
###ValidFloatChecker.h
#ifndef THORS_ANVIL_BENCHMARK_VALIDATE_FLOAT_H
#define THORS_ANVIL_BENCHMARK_VALIDATE_FLOAT_H
#include "TestSuite.h"
#include <fstream>
#include <string>
#include <limits>
#include <cstdlib>
namespace ThorsAnvil
{
namespace Benchmark
{
class ValidateFloat: public TestSuite
{
public:
using TestSuite::TestSuite;
virtual std::string setupName(Test const&) override
{
return "vector-double";
}
virtual std::string getDir() const override
{
return "validate_float";
}
virtual void preloadData(Test& test) override
{
std::ifstream input(test.path.str());
std::getline(input, test.input, '<');
input.ignore(std::numeric_limits<std::streamsize>::max(), '>');
std::getline(input, test.output, '<');
}
virtual State executeTest(TestBase const& parser, Test const& test) override
{
double output;
char *end;
double expected = std::strtod(test.output.c_str(), &end);
bool result = parser.ParseDouble(test.input.c_str(), &output);
return !result ? NotImplemented : output == expected ? Pass: Fail;
}
virtual void generateConPerData(TestBase const& parser, Test const& test, State state) override
{
std::size_t begin = test.path.str().rfind('/') + 1;
std::size_t end = test.path.str().rfind('.');
std::string fileName = test.path.str().substr(begin, (end - begin));
options.conformance << "2. Parse Double,"
<< parser.GetName() << ","
<< fileName << ","
<< (state == Pass ? "true" : "false")
<< "\n";
}
};
}
}
#endif
###ValidStringChecker.h
#ifndef THORS_ANVIL_BENCHMARK_VALIDATE_STRING_H
#define THORS_ANVIL_BENCHMARK_VALIDATE_STRING_H
#include "TestSuite.h"
#include <fstream>
#include <string>
namespace ThorsAnvil
{
namespace Benchmark
{
class ValidateString: public TestSuite
{
public:
using TestSuite::TestSuite;
virtual std::string setupName(Test const&) override
{
return "vector-string";
}
virtual std::string getDir() const override
{
return "validate_string";
}
virtual void preloadData(Test& test) override
{
std::ifstream input(test.path.str());
std::getline(input, test.input, '<');
input.ignore(std::numeric_limits<std::streamsize>::max(), '>');
std::getline(input, test.output, '<');
}
virtual State executeTest(TestBase const& parser, Test const& test) override
{
std::string output;
bool result = parser.ParseString(test.input.c_str(), output);
return !result ? NotImplemented : output == test.output ? Pass: Fail;
}
virtual void generateConPerData(TestBase const& parser, Test const& test, State state) override
{
std::size_t begin = test.path.str().rfind('/') + 1;
std::size_t end = test.path.str().rfind('.');
std::string fileName = test.path.str().substr(begin, (end - begin));
options.conformance << "3. Parse String,"
<< parser.GetName() << ","
<< fileName << ","
<< (state == Pass ? "true" : "false")
<< "\n";
}
};
}
}
#endif