Skip to main content
Tweeted twitter.com/StackCodeReview/status/1508232479873904655
added 59 characters in body
Source Link
Loki Astari
  • 96.6k
  • 5
  • 125
  • 338
Source Link
Loki Astari
  • 96.6k
  • 5
  • 125
  • 338

JSON Test Harness: Part 3

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