diff options
Diffstat (limited to 'src/jsontestrunner/main.cpp')
-rw-r--r-- | src/jsontestrunner/main.cpp | 341 |
1 files changed, 203 insertions, 138 deletions
diff --git a/src/jsontestrunner/main.cpp b/src/jsontestrunner/main.cpp index ba98587..3452c59 100644 --- a/src/jsontestrunner/main.cpp +++ b/src/jsontestrunner/main.cpp @@ -1,39 +1,49 @@ -// Copyright 2007-2010 Baptiste Lepilleur +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#elif defined(_MSC_VER) +#pragma warning(disable : 4996) +#endif + /* This executable is used for testing parser/writer using real JSON files. */ -#include <json/json.h> #include <algorithm> // sort -#include <stdio.h> +#include <cstdio> +#include <iostream> +#include <json/json.h> +#include <memory> +#include <sstream> -#if defined(_MSC_VER) && _MSC_VER >= 1310 -#pragma warning(disable : 4996) // disable fopen deprecation warning -#endif +struct Options { + Json::String path; + Json::Features features; + bool parseOnly; + using writeFuncType = Json::String (*)(Json::Value const&); + writeFuncType write; +}; -static std::string normalizeFloatingPointStr(double value) { +static Json::String normalizeFloatingPointStr(double value) { char buffer[32]; -#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) - sprintf_s(buffer, sizeof(buffer), "%.16g", value); -#else - snprintf(buffer, sizeof(buffer), "%.16g", value); -#endif + jsoncpp_snprintf(buffer, sizeof(buffer), "%.16g", value); buffer[sizeof(buffer) - 1] = 0; - std::string s(buffer); - std::string::size_type index = s.find_last_of("eE"); - if (index != std::string::npos) { - std::string::size_type hasSign = + Json::String s(buffer); + Json::String::size_type index = s.find_last_of("eE"); + if (index != Json::String::npos) { + Json::String::size_type hasSign = (s[index + 1] == '+' || s[index + 1] == '-') ? 1 : 0; - std::string::size_type exponentStartIndex = index + 1 + hasSign; - std::string normalized = s.substr(0, exponentStartIndex); - std::string::size_type indexDigit = + Json::String::size_type exponentStartIndex = index + 1 + hasSign; + Json::String normalized = s.substr(0, exponentStartIndex); + Json::String::size_type indexDigit = s.find_first_not_of('0', exponentStartIndex); - std::string exponent = "0"; - if (indexDigit != - std::string::npos) // There is an exponent different from 0 + Json::String exponent = "0"; + if (indexDigit != Json::String::npos) // There is an exponent different + // from 0 { exponent = s.substr(indexDigit); } @@ -42,25 +52,26 @@ static std::string normalizeFloatingPointStr(double value) { return s; } -static std::string readInputTestFile(const char* path) { +static Json::String readInputTestFile(const char* path) { FILE* file = fopen(path, "rb"); if (!file) - return std::string(""); + return ""; fseek(file, 0, SEEK_END); - long size = ftell(file); + auto const size = ftell(file); + auto const usize = static_cast<size_t>(size); fseek(file, 0, SEEK_SET); - std::string text; - char* buffer = new char[size + 1]; + auto buffer = new char[size + 1]; buffer[size] = 0; - if (fread(buffer, 1, size, file) == (unsigned long)size) + Json::String text; + if (fread(buffer, 1, usize, file) == usize) text = buffer; fclose(file); delete[] buffer; return text; } -static void -printValueTree(FILE* fout, Json::Value& value, const std::string& path = ".") { +static void printValueTree(FILE* fout, Json::Value& value, + const Json::String& path = ".") { if (value.hasComment(Json::commentBefore)) { fprintf(fout, "%s\n", value.getComment(Json::commentBefore).c_str()); } @@ -69,21 +80,15 @@ printValueTree(FILE* fout, Json::Value& value, const std::string& path = ".") { fprintf(fout, "%s=null\n", path.c_str()); break; case Json::intValue: - fprintf(fout, - "%s=%s\n", - path.c_str(), + fprintf(fout, "%s=%s\n", path.c_str(), Json::valueToString(value.asLargestInt()).c_str()); break; case Json::uintValue: - fprintf(fout, - "%s=%s\n", - path.c_str(), + fprintf(fout, "%s=%s\n", path.c_str(), Json::valueToString(value.asLargestUInt()).c_str()); break; case Json::realValue: - fprintf(fout, - "%s=%s\n", - path.c_str(), + fprintf(fout, "%s=%s\n", path.c_str(), normalizeFloatingPointStr(value.asDouble()).c_str()); break; case Json::stringValue: @@ -94,14 +99,10 @@ printValueTree(FILE* fout, Json::Value& value, const std::string& path = ".") { break; case Json::arrayValue: { fprintf(fout, "%s=[]\n", path.c_str()); - int size = value.size(); - for (int index = 0; index < size; ++index) { + Json::ArrayIndex size = value.size(); + for (Json::ArrayIndex index = 0; index < size; ++index) { static char buffer[16]; -#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) - sprintf_s(buffer, sizeof(buffer), "[%d]", index); -#else - snprintf(buffer, sizeof(buffer), "[%d]", index); -#endif + jsoncpp_snprintf(buffer, sizeof(buffer), "[%u]", index); printValueTree(fout, value[index], path + buffer); } } break; @@ -109,11 +110,8 @@ printValueTree(FILE* fout, Json::Value& value, const std::string& path = ".") { fprintf(fout, "%s={}\n", path.c_str()); Json::Value::Members members(value.getMemberNames()); std::sort(members.begin(), members.end()); - std::string suffix = *(path.end() - 1) == '.' ? "" : "."; - for (Json::Value::Members::iterator it = members.begin(); - it != members.end(); - ++it) { - const std::string& name = *it; + Json::String suffix = *(path.end() - 1) == '.' ? "" : "."; + for (const auto& name : members) { printValueTree(fout, value[name], path + suffix + name); } } break; @@ -126,152 +124,219 @@ printValueTree(FILE* fout, Json::Value& value, const std::string& path = ".") { } } -static int parseAndSaveValueTree(const std::string& input, - const std::string& actual, - const std::string& kind, - Json::Value& root, - const Json::Features& features, - bool parseOnly) { - Json::Reader reader(features); - bool parsingSuccessful = reader.parse(input, root); - if (!parsingSuccessful) { - printf("Failed to parse %s file: \n%s\n", - kind.c_str(), - reader.getFormattedErrorMessages().c_str()); - return 1; +static int parseAndSaveValueTree(const Json::String& input, + const Json::String& actual, + const Json::String& kind, + const Json::Features& features, bool parseOnly, + Json::Value* root, bool use_legacy) { + if (!use_legacy) { + Json::CharReaderBuilder builder; + + builder.settings_["allowComments"] = features.allowComments_; + builder.settings_["strictRoot"] = features.strictRoot_; + builder.settings_["allowDroppedNullPlaceholders"] = + features.allowDroppedNullPlaceholders_; + builder.settings_["allowNumericKeys"] = features.allowNumericKeys_; + + std::unique_ptr<Json::CharReader> reader(builder.newCharReader()); + Json::String errors; + const bool parsingSuccessful = + reader->parse(input.data(), input.data() + input.size(), root, &errors); + + if (!parsingSuccessful) { + std::cerr << "Failed to parse " << kind << " file: " << std::endl + << errors << std::endl; + return 1; + } + + // We may instead check the legacy implementation (to ensure it doesn't + // randomly get broken). + } else { + Json::Reader reader(features); + const bool parsingSuccessful = + reader.parse(input.data(), input.data() + input.size(), *root); + if (!parsingSuccessful) { + std::cerr << "Failed to parse " << kind << " file: " << std::endl + << reader.getFormatedErrorMessages() << std::endl; + return 1; + } } if (!parseOnly) { FILE* factual = fopen(actual.c_str(), "wt"); if (!factual) { - printf("Failed to create %s actual file.\n", kind.c_str()); + std::cerr << "Failed to create '" << kind << "' actual file." + << std::endl; return 2; } - printValueTree(factual, root); + printValueTree(factual, *root); fclose(factual); } return 0; } - -static int rewriteValueTree(const std::string& rewritePath, - const Json::Value& root, - std::string& rewrite) { - // Json::FastWriter writer; - // writer.enableYAMLCompatibility(); +// static Json::String useFastWriter(Json::Value const& root) { +// Json::FastWriter writer; +// writer.enableYAMLCompatibility(); +// return writer.write(root); +// } +static Json::String useStyledWriter(Json::Value const& root) { Json::StyledWriter writer; - rewrite = writer.write(root); + return writer.write(root); +} +static Json::String useStyledStreamWriter(Json::Value const& root) { + Json::StyledStreamWriter writer; + Json::OStringStream sout; + writer.write(sout, root); + return sout.str(); +} +static Json::String useBuiltStyledStreamWriter(Json::Value const& root) { + Json::StreamWriterBuilder builder; + return Json::writeString(builder, root); +} +static int rewriteValueTree(const Json::String& rewritePath, + const Json::Value& root, + Options::writeFuncType write, + Json::String* rewrite) { + *rewrite = write(root); FILE* fout = fopen(rewritePath.c_str(), "wt"); if (!fout) { - printf("Failed to create rewrite file: %s\n", rewritePath.c_str()); + std::cerr << "Failed to create rewrite file: " << rewritePath << std::endl; return 2; } - fprintf(fout, "%s\n", rewrite.c_str()); + fprintf(fout, "%s\n", rewrite->c_str()); fclose(fout); return 0; } -static std::string removeSuffix(const std::string& path, - const std::string& extension) { +static Json::String removeSuffix(const Json::String& path, + const Json::String& extension) { if (extension.length() >= path.length()) - return std::string(""); - std::string suffix = path.substr(path.length() - extension.length()); + return Json::String(""); + Json::String suffix = path.substr(path.length() - extension.length()); if (suffix != extension) - return std::string(""); + return Json::String(""); return path.substr(0, path.length() - extension.length()); } static void printConfig() { // Print the configuration used to compile JsonCpp #if defined(JSON_NO_INT64) - printf("JSON_NO_INT64=1\n"); + std::cout << "JSON_NO_INT64=1" << std::endl; #else - printf("JSON_NO_INT64=0\n"); + std::cout << "JSON_NO_INT64=0" << std::endl; #endif } static int printUsage(const char* argv[]) { - printf("Usage: %s [--strict] input-json-file", argv[0]); + std::cout << "Usage: " << argv[0] << " [--strict] input-json-file" + << std::endl; return 3; } -int parseCommandLine(int argc, - const char* argv[], - Json::Features& features, - std::string& path, - bool& parseOnly) { - parseOnly = false; +static int parseCommandLine(int argc, const char* argv[], Options* opts) { + opts->parseOnly = false; + opts->write = &useStyledWriter; if (argc < 2) { return printUsage(argv); } - int index = 1; - if (std::string(argv[1]) == "--json-checker") { - features = Json::Features::strictMode(); - parseOnly = true; + if (Json::String(argv[index]) == "--json-checker") { + opts->features = Json::Features::strictMode(); + opts->parseOnly = true; ++index; } - - if (std::string(argv[1]) == "--json-config") { + if (Json::String(argv[index]) == "--json-config") { printConfig(); return 3; } - + if (Json::String(argv[index]) == "--json-writer") { + ++index; + Json::String const writerName(argv[index++]); + if (writerName == "StyledWriter") { + opts->write = &useStyledWriter; + } else if (writerName == "StyledStreamWriter") { + opts->write = &useStyledStreamWriter; + } else if (writerName == "BuiltStyledStreamWriter") { + opts->write = &useBuiltStyledStreamWriter; + } else { + std::cerr << "Unknown '--json-writer' " << writerName << std::endl; + return 4; + } + } if (index == argc || index + 1 < argc) { return printUsage(argv); } - - path = argv[index]; + opts->path = argv[index]; return 0; } -int main(int argc, const char* argv[]) { - std::string path; - Json::Features features; - bool parseOnly; - int exitCode = parseCommandLine(argc, argv, features, path, parseOnly); - if (exitCode != 0) { +static int runTest(Options const& opts, bool use_legacy) { + int exitCode = 0; + + Json::String input = readInputTestFile(opts.path.c_str()); + if (input.empty()) { + std::cerr << "Invalid input file: " << opts.path << std::endl; + return 3; + } + + Json::String basePath = removeSuffix(opts.path, ".json"); + if (!opts.parseOnly && basePath.empty()) { + std::cerr << "Bad input path '" << opts.path + << "'. Must end with '.expected'" << std::endl; + return 3; + } + + Json::String const actualPath = basePath + ".actual"; + Json::String const rewritePath = basePath + ".rewrite"; + Json::String const rewriteActualPath = basePath + ".actual-rewrite"; + + Json::Value root; + exitCode = parseAndSaveValueTree(input, actualPath, "input", opts.features, + opts.parseOnly, &root, use_legacy); + if (exitCode || opts.parseOnly) { + return exitCode; + } + + Json::String rewrite; + exitCode = rewriteValueTree(rewritePath, root, opts.write, &rewrite); + if (exitCode) { return exitCode; } + Json::Value rewriteRoot; + exitCode = parseAndSaveValueTree(rewrite, rewriteActualPath, "rewrite", + opts.features, opts.parseOnly, &rewriteRoot, + use_legacy); + + return exitCode; +} + +int main(int argc, const char* argv[]) { + Options opts; try { - std::string input = readInputTestFile(path.c_str()); - if (input.empty()) { - printf("Failed to read input or empty input: %s\n", path.c_str()); - return 3; + int exitCode = parseCommandLine(argc, argv, &opts); + if (exitCode != 0) { + std::cerr << "Failed to parse command-line." << std::endl; + return exitCode; } - std::string basePath = removeSuffix(argv[1], ".json"); - if (!parseOnly && basePath.empty()) { - printf("Bad input path. Path does not end with '.expected':\n%s\n", - path.c_str()); - return 3; + const int modern_return_code = runTest(opts, false); + if (modern_return_code) { + return modern_return_code; } - std::string actualPath = basePath + ".actual"; - std::string rewritePath = basePath + ".rewrite"; - std::string rewriteActualPath = basePath + ".actual-rewrite"; - - Json::Value root; - exitCode = parseAndSaveValueTree( - input, actualPath, "input", root, features, parseOnly); - if (exitCode == 0 && !parseOnly) { - std::string rewrite; - exitCode = rewriteValueTree(rewritePath, root, rewrite); - if (exitCode == 0) { - Json::Value rewriteRoot; - exitCode = parseAndSaveValueTree(rewrite, - rewriteActualPath, - "rewrite", - rewriteRoot, - features, - parseOnly); - } + const std::string filename = + opts.path.substr(opts.path.find_last_of("\\/") + 1); + const bool should_run_legacy = (filename.rfind("legacy_", 0) == 0); + if (should_run_legacy) { + return runTest(opts, true); } + } catch (const std::exception& e) { + std::cerr << "Unhandled exception:" << std::endl << e.what() << std::endl; + return 1; } - catch (const std::exception& e) { - printf("Unhandled exception:\n%s\n", e.what()); - exitCode = 1; - } - - return exitCode; } + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif |