Delete jsoncpp fuzzing stuff

This commit is contained in:
Jordan Bayles 2020-11-05 17:42:26 -08:00
parent 2ee7474f96
commit b6b6f167ad
8 changed files with 1 additions and 5113 deletions

2
.gitignore vendored
View File

@ -6,4 +6,4 @@ xcode/valijson.xcodeproj/xcuserdata
doc/html
.idea
cmake-build-*
CMakeFiles/
CMakeFiles/

View File

@ -1,342 +0,0 @@
// 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 <algorithm> // sort
#include <cstdio>
#include <iostream>
#include <json/json.h>
#include <memory>
#include <sstream>
struct Options {
Json::String path;
Json::Features features;
bool parseOnly;
using writeFuncType = Json::String (*)(Json::Value const&);
writeFuncType write;
};
static Json::String normalizeFloatingPointStr(double value) {
char buffer[32];
jsoncpp_snprintf(buffer, sizeof(buffer), "%.16g", value);
buffer[sizeof(buffer) - 1] = 0;
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;
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);
Json::String exponent = "0";
if (indexDigit != Json::String::npos) // There is an exponent different
// from 0
{
exponent = s.substr(indexDigit);
}
return normalized + exponent;
}
return s;
}
static Json::String readInputTestFile(const char* path) {
FILE* file = fopen(path, "rb");
if (!file)
return "";
fseek(file, 0, SEEK_END);
auto const size = ftell(file);
auto const usize = static_cast<size_t>(size);
fseek(file, 0, SEEK_SET);
auto buffer = new char[size + 1];
buffer[size] = 0;
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 Json::String& path = ".") {
if (value.hasComment(Json::commentBefore)) {
fprintf(fout, "%s\n", value.getComment(Json::commentBefore).c_str());
}
switch (value.type()) {
case Json::nullValue:
fprintf(fout, "%s=null\n", path.c_str());
break;
case Json::intValue:
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(),
Json::valueToString(value.asLargestUInt()).c_str());
break;
case Json::realValue:
fprintf(fout, "%s=%s\n", path.c_str(),
normalizeFloatingPointStr(value.asDouble()).c_str());
break;
case Json::stringValue:
fprintf(fout, "%s=\"%s\"\n", path.c_str(), value.asString().c_str());
break;
case Json::booleanValue:
fprintf(fout, "%s=%s\n", path.c_str(), value.asBool() ? "true" : "false");
break;
case Json::arrayValue: {
fprintf(fout, "%s=[]\n", path.c_str());
Json::ArrayIndex size = value.size();
for (Json::ArrayIndex index = 0; index < size; ++index) {
static char buffer[16];
jsoncpp_snprintf(buffer, sizeof(buffer), "[%u]", index);
printValueTree(fout, value[index], path + buffer);
}
} break;
case Json::objectValue: {
fprintf(fout, "%s={}\n", path.c_str());
Json::Value::Members members(value.getMemberNames());
std::sort(members.begin(), members.end());
Json::String suffix = *(path.end() - 1) == '.' ? "" : ".";
for (const auto& name : members) {
printValueTree(fout, value[name], path + suffix + name);
}
} break;
default:
break;
}
if (value.hasComment(Json::commentAfter)) {
fprintf(fout, "%s\n", value.getComment(Json::commentAfter).c_str());
}
}
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) {
std::cerr << "Failed to create '" << kind << "' actual file."
<< std::endl;
return 2;
}
printValueTree(factual, *root);
fclose(factual);
}
return 0;
}
// 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;
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) {
std::cerr << "Failed to create rewrite file: " << rewritePath << std::endl;
return 2;
}
fprintf(fout, "%s\n", rewrite->c_str());
fclose(fout);
return 0;
}
static Json::String removeSuffix(const Json::String& path,
const Json::String& extension) {
if (extension.length() >= path.length())
return Json::String("");
Json::String suffix = path.substr(path.length() - extension.length());
if (suffix != extension)
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)
std::cout << "JSON_NO_INT64=1" << std::endl;
#else
std::cout << "JSON_NO_INT64=0" << std::endl;
#endif
}
static int printUsage(const char* argv[]) {
std::cout << "Usage: " << argv[0] << " [--strict] input-json-file"
<< std::endl;
return 3;
}
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 (Json::String(argv[index]) == "--json-checker") {
opts->features = Json::Features::strictMode();
opts->parseOnly = true;
++index;
}
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);
}
opts->path = argv[index];
return 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 {
int exitCode = parseCommandLine(argc, argv, &opts);
if (exitCode != 0) {
std::cerr << "Failed to parse command-line." << std::endl;
return exitCode;
}
const int modern_return_code = runTest(opts, false);
if (modern_return_code) {
return modern_return_code;
}
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;
}
}
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif

View File

@ -1,54 +0,0 @@
// Copyright 2007-2019 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
#include "fuzz.h"
#include <cstdint>
#include <json/config.h>
#include <json/json.h>
#include <memory>
#include <string>
namespace Json {
class Exception;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
Json::CharReaderBuilder builder;
if (size < sizeof(uint32_t)) {
return 0;
}
const uint32_t hash_settings = static_cast<uint32_t>(data[0]) |
(static_cast<uint32_t>(data[1]) << 8) |
(static_cast<uint32_t>(data[2]) << 16) |
(static_cast<uint32_t>(data[3]) << 24);
data += sizeof(uint32_t);
size -= sizeof(uint32_t);
builder.settings_["failIfExtra"] = hash_settings & (1 << 0);
builder.settings_["allowComments_"] = hash_settings & (1 << 1);
builder.settings_["strictRoot_"] = hash_settings & (1 << 2);
builder.settings_["allowDroppedNullPlaceholders_"] = hash_settings & (1 << 3);
builder.settings_["allowNumericKeys_"] = hash_settings & (1 << 4);
builder.settings_["allowSingleQuotes_"] = hash_settings & (1 << 5);
builder.settings_["failIfExtra_"] = hash_settings & (1 << 6);
builder.settings_["rejectDupKeys_"] = hash_settings & (1 << 7);
builder.settings_["allowSpecialFloats_"] = hash_settings & (1 << 8);
builder.settings_["collectComments"] = hash_settings & (1 << 9);
builder.settings_["allowTrailingCommas_"] = hash_settings & (1 << 10);
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
Json::Value root;
const auto data_str = reinterpret_cast<const char*>(data);
try {
reader->parse(data_str, data_str + size, &root, nullptr);
} catch (Json::Exception const&) {
}
// Whether it succeeded or not doesn't matter.
return 0;
}

View File

@ -1,54 +0,0 @@
#
# AFL dictionary for JSON
# -----------------------
#
# Just the very basics.
#
# Inspired by a dictionary by Jakub Wilk <jwilk@jwilk.net>
#
# https://github.com/rc0r/afl-fuzz/blob/master/dictionaries/json.dict
#
"0"
",0"
":0"
"0:"
"-1.2e+3"
"true"
"false"
"null"
"\"\""
",\"\""
":\"\""
"\"\":"
"{}"
",{}"
":{}"
"{\"\":0}"
"{{}}"
"[]"
",[]"
":[]"
"[0]"
"[[]]"
"''"
"\\"
"\\b"
"\\f"
"\\n"
"\\r"
"\\t"
"\\u0000"
"\\x00"
"\\0"
"\\uD800\\uDC00"
"\\uDBFF\\uDFFF"
"\"\":0"
"//"
"/**/"

View File

@ -1,14 +0,0 @@
// Copyright 2007-2010 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
#ifndef FUZZ_H_INCLUDED
#define FUZZ_H_INCLUDED
#include <cstddef>
#include <stdint.h>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
#endif // ifndef FUZZ_H_INCLUDED

View File

@ -1,430 +0,0 @@
// 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
#define _CRT_SECURE_NO_WARNINGS 1 // Prevents deprecation warning with MSVC
#include "jsontest.h"
#include <cstdio>
#include <string>
#if defined(_MSC_VER)
// Used to install a report hook that prevent dialog on assertion and error.
#include <crtdbg.h>
#endif // if defined(_MSC_VER)
#if defined(_WIN32)
// Used to prevent dialog on memory fault.
// Limits headers included by Windows.h
#define WIN32_LEAN_AND_MEAN
#define NOSERVICE
#define NOMCX
#define NOIME
#define NOSOUND
#define NOCOMM
#define NORPC
#define NOGDI
#define NOUSER
#define NODRIVERS
#define NOLOGERROR
#define NOPROFILER
#define NOMEMMGR
#define NOLFILEIO
#define NOOPENFILE
#define NORESOURCE
#define NOATOM
#define NOLANGUAGE
#define NOLSTRING
#define NODBCS
#define NOKEYBOARDINFO
#define NOGDICAPMASKS
#define NOCOLOR
#define NOGDIOBJ
#define NODRAWTEXT
#define NOTEXTMETRIC
#define NOSCALABLEFONT
#define NOBITMAP
#define NORASTEROPS
#define NOMETAFILE
#define NOSYSMETRICS
#define NOSYSTEMPARAMSINFO
#define NOMSG
#define NOWINSTYLES
#define NOWINOFFSETS
#define NOSHOWWINDOW
#define NODEFERWINDOWPOS
#define NOVIRTUALKEYCODES
#define NOKEYSTATES
#define NOWH
#define NOMENUS
#define NOSCROLL
#define NOCLIPBOARD
#define NOICONS
#define NOMB
#define NOSYSCOMMANDS
#define NOMDI
#define NOCTLMGR
#define NOWINMESSAGES
#include <windows.h>
#endif // if defined(_WIN32)
namespace JsonTest {
// class TestResult
// //////////////////////////////////////////////////////////////////
TestResult::TestResult() {
// The root predicate has id 0
rootPredicateNode_.id_ = 0;
rootPredicateNode_.next_ = nullptr;
predicateStackTail_ = &rootPredicateNode_;
}
void TestResult::setTestName(const Json::String& name) { name_ = name; }
TestResult& TestResult::addFailure(const char* file, unsigned int line,
const char* expr) {
/// Walks the PredicateContext stack adding them to failures_ if not already
/// added.
unsigned int nestingLevel = 0;
PredicateContext* lastNode = rootPredicateNode_.next_;
for (; lastNode != nullptr; lastNode = lastNode->next_) {
if (lastNode->id_ > lastUsedPredicateId_) // new PredicateContext
{
lastUsedPredicateId_ = lastNode->id_;
addFailureInfo(lastNode->file_, lastNode->line_, lastNode->expr_,
nestingLevel);
// Link the PredicateContext to the failure for message target when
// popping the PredicateContext.
lastNode->failure_ = &(failures_.back());
}
++nestingLevel;
}
// Adds the failed assertion
addFailureInfo(file, line, expr, nestingLevel);
messageTarget_ = &(failures_.back());
return *this;
}
void TestResult::addFailureInfo(const char* file, unsigned int line,
const char* expr, unsigned int nestingLevel) {
Failure failure;
failure.file_ = file;
failure.line_ = line;
if (expr) {
failure.expr_ = expr;
}
failure.nestingLevel_ = nestingLevel;
failures_.push_back(failure);
}
TestResult& TestResult::popPredicateContext() {
PredicateContext* lastNode = &rootPredicateNode_;
while (lastNode->next_ != nullptr && lastNode->next_->next_ != nullptr) {
lastNode = lastNode->next_;
}
// Set message target to popped failure
PredicateContext* tail = lastNode->next_;
if (tail != nullptr && tail->failure_ != nullptr) {
messageTarget_ = tail->failure_;
}
// Remove tail from list
predicateStackTail_ = lastNode;
lastNode->next_ = nullptr;
return *this;
}
bool TestResult::failed() const { return !failures_.empty(); }
void TestResult::printFailure(bool printTestName) const {
if (failures_.empty()) {
return;
}
if (printTestName) {
printf("* Detail of %s test failure:\n", name_.c_str());
}
// Print in reverse to display the callstack in the right order
for (const auto& failure : failures_) {
Json::String indent(failure.nestingLevel_ * 2, ' ');
if (failure.file_) {
printf("%s%s(%u): ", indent.c_str(), failure.file_, failure.line_);
}
if (!failure.expr_.empty()) {
printf("%s\n", failure.expr_.c_str());
} else if (failure.file_) {
printf("\n");
}
if (!failure.message_.empty()) {
Json::String reindented = indentText(failure.message_, indent + " ");
printf("%s\n", reindented.c_str());
}
}
}
Json::String TestResult::indentText(const Json::String& text,
const Json::String& indent) {
Json::String reindented;
Json::String::size_type lastIndex = 0;
while (lastIndex < text.size()) {
Json::String::size_type nextIndex = text.find('\n', lastIndex);
if (nextIndex == Json::String::npos) {
nextIndex = text.size() - 1;
}
reindented += indent;
reindented += text.substr(lastIndex, nextIndex - lastIndex + 1);
lastIndex = nextIndex + 1;
}
return reindented;
}
TestResult& TestResult::addToLastFailure(const Json::String& message) {
if (messageTarget_ != nullptr) {
messageTarget_->message_ += message;
}
return *this;
}
TestResult& TestResult::operator<<(Json::Int64 value) {
return addToLastFailure(Json::valueToString(value));
}
TestResult& TestResult::operator<<(Json::UInt64 value) {
return addToLastFailure(Json::valueToString(value));
}
TestResult& TestResult::operator<<(bool value) {
return addToLastFailure(value ? "true" : "false");
}
// class TestCase
// //////////////////////////////////////////////////////////////////
TestCase::TestCase() = default;
TestCase::~TestCase() = default;
void TestCase::run(TestResult& result) {
result_ = &result;
runTestCase();
}
// class Runner
// //////////////////////////////////////////////////////////////////
Runner::Runner() = default;
Runner& Runner::add(TestCaseFactory factory) {
tests_.push_back(factory);
return *this;
}
size_t Runner::testCount() const { return tests_.size(); }
Json::String Runner::testNameAt(size_t index) const {
TestCase* test = tests_[index]();
Json::String name = test->testName();
delete test;
return name;
}
void Runner::runTestAt(size_t index, TestResult& result) const {
TestCase* test = tests_[index]();
result.setTestName(test->testName());
printf("Testing %s: ", test->testName());
fflush(stdout);
#if JSON_USE_EXCEPTION
try {
#endif // if JSON_USE_EXCEPTION
test->run(result);
#if JSON_USE_EXCEPTION
} catch (const std::exception& e) {
result.addFailure(__FILE__, __LINE__, "Unexpected exception caught:")
<< e.what();
}
#endif // if JSON_USE_EXCEPTION
delete test;
const char* status = result.failed() ? "FAILED" : "OK";
printf("%s\n", status);
fflush(stdout);
}
bool Runner::runAllTest(bool printSummary) const {
size_t const count = testCount();
std::deque<TestResult> failures;
for (size_t index = 0; index < count; ++index) {
TestResult result;
runTestAt(index, result);
if (result.failed()) {
failures.push_back(result);
}
}
if (failures.empty()) {
if (printSummary) {
printf("All %zu tests passed\n", count);
}
return true;
}
for (auto& result : failures) {
result.printFailure(count > 1);
}
if (printSummary) {
size_t const failedCount = failures.size();
size_t const passedCount = count - failedCount;
printf("%zu/%zu tests passed (%zu failure(s))\n", passedCount, count,
failedCount);
}
return false;
}
bool Runner::testIndex(const Json::String& testName, size_t& indexOut) const {
const size_t count = testCount();
for (size_t index = 0; index < count; ++index) {
if (testNameAt(index) == testName) {
indexOut = index;
return true;
}
}
return false;
}
void Runner::listTests() const {
const size_t count = testCount();
for (size_t index = 0; index < count; ++index) {
printf("%s\n", testNameAt(index).c_str());
}
}
int Runner::runCommandLine(int argc, const char* argv[]) const {
// typedef std::deque<String> TestNames;
Runner subrunner;
for (int index = 1; index < argc; ++index) {
Json::String opt = argv[index];
if (opt == "--list-tests") {
listTests();
return 0;
}
if (opt == "--test-auto") {
preventDialogOnCrash();
} else if (opt == "--test") {
++index;
if (index < argc) {
size_t testNameIndex;
if (testIndex(argv[index], testNameIndex)) {
subrunner.add(tests_[testNameIndex]);
} else {
fprintf(stderr, "Test '%s' does not exist!\n", argv[index]);
return 2;
}
} else {
printUsage(argv[0]);
return 2;
}
} else {
printUsage(argv[0]);
return 2;
}
}
bool succeeded;
if (subrunner.testCount() > 0) {
succeeded = subrunner.runAllTest(subrunner.testCount() > 1);
} else {
succeeded = runAllTest(true);
}
return succeeded ? 0 : 1;
}
#if defined(_MSC_VER) && defined(_DEBUG)
// Hook MSVCRT assertions to prevent dialog from appearing
static int msvcrtSilentReportHook(int reportType, char* message,
int* /*returnValue*/) {
// The default CRT handling of error and assertion is to display
// an error dialog to the user.
// Instead, when an error or an assertion occurs, we force the
// application to terminate using abort() after display
// the message on stderr.
if (reportType == _CRT_ERROR || reportType == _CRT_ASSERT) {
// calling abort() cause the ReportHook to be called
// The following is used to detect this case and let's the
// error handler fallback on its default behaviour (
// display a warning message)
static volatile bool isAborting = false;
if (isAborting) {
return TRUE;
}
isAborting = true;
fprintf(stderr, "CRT Error/Assert:\n%s\n", message);
fflush(stderr);
abort();
}
// Let's other reportType (_CRT_WARNING) be handled as they would by default
return FALSE;
}
#endif // if defined(_MSC_VER)
void Runner::preventDialogOnCrash() {
#if defined(_MSC_VER) && defined(_DEBUG)
// Install a hook to prevent MSVCRT error and assertion from
// popping a dialog
// This function a NO-OP in release configuration
// (which cause warning since msvcrtSilentReportHook is not referenced)
_CrtSetReportHook(&msvcrtSilentReportHook);
#endif // if defined(_MSC_VER)
// @todo investigate this handler (for buffer overflow)
// _set_security_error_handler
#if defined(_WIN32)
// Prevents the system from popping a dialog for debugging if the
// application fails due to invalid memory access.
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
SEM_NOOPENFILEERRORBOX);
#endif // if defined(_WIN32)
}
void Runner::printUsage(const char* appName) {
printf("Usage: %s [options]\n"
"\n"
"If --test is not specified, then all the test cases be run.\n"
"\n"
"Valid options:\n"
"--list-tests: print the name of all test cases on the standard\n"
" output and exit.\n"
"--test TESTNAME: executes the test case with the specified name.\n"
" May be repeated.\n"
"--test-auto: prevent dialog prompting for debugging on crash.\n",
appName);
}
// Assertion functions
// //////////////////////////////////////////////////////////////////
Json::String ToJsonString(const char* toConvert) {
return Json::String(toConvert);
}
Json::String ToJsonString(Json::String in) { return in; }
#if JSONCPP_USING_SECURE_MEMORY
Json::String ToJsonString(std::string in) {
return Json::String(in.data(), in.data() + in.length());
}
#endif
TestResult& checkStringEqual(TestResult& result, const Json::String& expected,
const Json::String& actual, const char* file,
unsigned int line, const char* expr) {
if (expected != actual) {
result.addFailure(file, line, expr);
result << "Expected: '" << expected << "'\n";
result << "Actual : '" << actual << "'";
}
return result;
}
} // namespace JsonTest

View File

@ -1,288 +0,0 @@
// 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
#ifndef JSONTEST_H_INCLUDED
#define JSONTEST_H_INCLUDED
#include <cstdio>
#include <deque>
#include <iomanip>
#include <json/config.h>
#include <json/value.h>
#include <json/writer.h>
#include <sstream>
#include <string>
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// Mini Unit Testing framework
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
/** \brief Unit testing framework.
* \warning: all assertions are non-aborting, test case execution will continue
* even if an assertion namespace.
* This constraint is for portability: the framework needs to compile
* on Visual Studio 6 and must not require exception usage.
*/
namespace JsonTest {
class Failure {
public:
const char* file_;
unsigned int line_;
Json::String expr_;
Json::String message_;
unsigned int nestingLevel_;
};
/// Context used to create the assertion callstack on failure.
/// Must be a POD to allow inline initialisation without stepping
/// into the debugger.
struct PredicateContext {
using Id = unsigned int;
Id id_;
const char* file_;
unsigned int line_;
const char* expr_;
PredicateContext* next_;
/// Related Failure, set when the PredicateContext is converted
/// into a Failure.
Failure* failure_;
};
class TestResult {
public:
TestResult();
/// \internal Implementation detail for assertion macros
/// Not encapsulated to prevent step into when debugging failed assertions
/// Incremented by one on assertion predicate entry, decreased by one
/// by addPredicateContext().
PredicateContext::Id predicateId_{1};
/// \internal Implementation detail for predicate macros
PredicateContext* predicateStackTail_;
void setTestName(const Json::String& name);
/// Adds an assertion failure.
TestResult& addFailure(const char* file, unsigned int line,
const char* expr = nullptr);
/// Removes the last PredicateContext added to the predicate stack
/// chained list.
/// Next messages will be targed at the PredicateContext that was removed.
TestResult& popPredicateContext();
bool failed() const;
void printFailure(bool printTestName) const;
// Generic operator that will work with anything ostream can deal with.
template <typename T> TestResult& operator<<(const T& value) {
Json::OStringStream oss;
oss << std::setprecision(16) << std::hexfloat << value;
return addToLastFailure(oss.str());
}
// Specialized versions.
TestResult& operator<<(bool value);
// std:ostream does not support 64bits integers on all STL implementation
TestResult& operator<<(Json::Int64 value);
TestResult& operator<<(Json::UInt64 value);
private:
TestResult& addToLastFailure(const Json::String& message);
/// Adds a failure or a predicate context
void addFailureInfo(const char* file, unsigned int line, const char* expr,
unsigned int nestingLevel);
static Json::String indentText(const Json::String& text,
const Json::String& indent);
using Failures = std::deque<Failure>;
Failures failures_;
Json::String name_;
PredicateContext rootPredicateNode_;
PredicateContext::Id lastUsedPredicateId_{0};
/// Failure which is the target of the messages added using operator <<
Failure* messageTarget_{nullptr};
};
class TestCase {
public:
TestCase();
virtual ~TestCase();
void run(TestResult& result);
virtual const char* testName() const = 0;
protected:
TestResult* result_{nullptr};
private:
virtual void runTestCase() = 0;
};
/// Function pointer type for TestCase factory
using TestCaseFactory = TestCase* (*)();
class Runner {
public:
Runner();
/// Adds a test to the suite
Runner& add(TestCaseFactory factory);
/// Runs test as specified on the command-line
/// If no command-line arguments are provided, run all tests.
/// If --list-tests is provided, then print the list of all test cases
/// If --test <testname> is provided, then run test testname.
int runCommandLine(int argc, const char* argv[]) const;
/// Runs all the test cases
bool runAllTest(bool printSummary) const;
/// Returns the number of test case in the suite
size_t testCount() const;
/// Returns the name of the test case at the specified index
Json::String testNameAt(size_t index) const;
/// Runs the test case at the specified index using the specified TestResult
void runTestAt(size_t index, TestResult& result) const;
static void printUsage(const char* appName);
private: // prevents copy construction and assignment
Runner(const Runner& other) = delete;
Runner& operator=(const Runner& other) = delete;
private:
void listTests() const;
bool testIndex(const Json::String& testName, size_t& indexOut) const;
static void preventDialogOnCrash();
private:
using Factories = std::deque<TestCaseFactory>;
Factories tests_;
};
template <typename T, typename U>
TestResult& checkEqual(TestResult& result, T expected, U actual,
const char* file, unsigned int line, const char* expr) {
if (static_cast<U>(expected) != actual) {
result.addFailure(file, line, expr);
result << "Expected: " << static_cast<U>(expected) << "\n";
result << "Actual : " << actual;
}
return result;
}
Json::String ToJsonString(const char* toConvert);
Json::String ToJsonString(Json::String in);
#if JSONCPP_USING_SECURE_MEMORY
Json::String ToJsonString(std::string in);
#endif
TestResult& checkStringEqual(TestResult& result, const Json::String& expected,
const Json::String& actual, const char* file,
unsigned int line, const char* expr);
} // namespace JsonTest
/// \brief Asserts that the given expression is true.
/// JSONTEST_ASSERT( x == y ) << "x=" << x << ", y=" << y;
/// JSONTEST_ASSERT( x == y );
#define JSONTEST_ASSERT(expr) \
if (expr) { \
} else \
result_->addFailure(__FILE__, __LINE__, #expr)
/// \brief Asserts that the given predicate is true.
/// The predicate may do other assertions and be a member function of the
/// fixture.
#define JSONTEST_ASSERT_PRED(expr) \
do { \
JsonTest::PredicateContext _minitest_Context = { \
result_->predicateId_, __FILE__, __LINE__, #expr, NULL, NULL}; \
result_->predicateStackTail_->next_ = &_minitest_Context; \
result_->predicateId_ += 1; \
result_->predicateStackTail_ = &_minitest_Context; \
(expr); \
result_->popPredicateContext(); \
} while (0)
/// \brief Asserts that two values are equals.
#define JSONTEST_ASSERT_EQUAL(expected, actual) \
JsonTest::checkEqual(*result_, expected, actual, __FILE__, __LINE__, \
#expected " == " #actual)
/// \brief Asserts that two values are equals.
#define JSONTEST_ASSERT_STRING_EQUAL(expected, actual) \
JsonTest::checkStringEqual(*result_, JsonTest::ToJsonString(expected), \
JsonTest::ToJsonString(actual), __FILE__, \
__LINE__, #expected " == " #actual)
/// \brief Asserts that a given expression throws an exception
#define JSONTEST_ASSERT_THROWS(expr) \
do { \
bool _threw = false; \
try { \
expr; \
} catch (...) { \
_threw = true; \
} \
if (!_threw) \
result_->addFailure(__FILE__, __LINE__, \
"expected exception thrown: " #expr); \
} while (0)
/// \brief Begin a fixture test case.
#define JSONTEST_FIXTURE(FixtureType, name) \
class Test##FixtureType##name : public FixtureType { \
public: \
static JsonTest::TestCase* factory() { \
return new Test##FixtureType##name(); \
} \
\
public: /* overridden from TestCase */ \
const char* testName() const override { return #FixtureType "/" #name; } \
void runTestCase() override; \
}; \
\
void Test##FixtureType##name::runTestCase()
#define JSONTEST_FIXTURE_FACTORY(FixtureType, name) \
&Test##FixtureType##name::factory
#define JSONTEST_REGISTER_FIXTURE(runner, FixtureType, name) \
(runner).add(JSONTEST_FIXTURE_FACTORY(FixtureType, name))
/// \brief Begin a fixture test case.
#define JSONTEST_FIXTURE_V2(FixtureType, name, collections) \
class Test##FixtureType##name : public FixtureType { \
public: \
static JsonTest::TestCase* factory() { \
return new Test##FixtureType##name(); \
} \
static bool collect() { \
(collections).push_back(JSONTEST_FIXTURE_FACTORY(FixtureType, name)); \
return true; \
} \
\
public: /* overridden from TestCase */ \
const char* testName() const override { return #FixtureType "/" #name; } \
void runTestCase() override; \
}; \
\
static bool test##FixtureType##name##collect = \
Test##FixtureType##name::collect(); \
\
void Test##FixtureType##name::runTestCase()
#endif // ifndef JSONTEST_H_INCLUDED

File diff suppressed because it is too large Load Diff