Flesh out API for PolyConstraint concept, simplify test cases and add new files to Xcode project

This commit is contained in:
Tristan Penman 2016-04-07 09:55:13 +10:00
parent 3d5526ec7b
commit 99e870ca5d
3 changed files with 104 additions and 144 deletions

View File

@ -784,6 +784,40 @@ private:
String pattern;
};
class PolyConstraint : public Constraint
{
public:
virtual bool accept(ConstraintVisitor &visitor) const
{
return visitor.visit(*static_cast<const PolyConstraint*>(this));
}
virtual Constraint * clone(CustomAlloc allocFn, CustomFree freeFn) const
{
void *ptr = allocFn(sizeOf());
if (!ptr) {
throw std::runtime_error(
"Failed to allocate memory for cloned constraint");
}
try {
return cloneInto(ptr);
} catch (...) {
freeFn(ptr);
throw;
}
}
virtual bool validate(const adapters::Adapter &target,
const std::vector<std::string>& context,
valijson::ValidationResults *results) const = 0;
protected:
virtual Constraint * cloneInto(void *) const = 0;
virtual size_t sizeOf() const = 0;
};
/**
* @brief Represents a combination of 'properties', 'patternProperties' and
* 'additionalProperties' constraints
@ -1061,12 +1095,6 @@ public:
: BasicConstraint(allocFn, freeFn) { }
};
class PolyConstraint : public BasicConstraint<PolyConstraint> {
public:
virtual bool validate(const adapters::Adapter &target, const std::vector<std::string>& context, valijson::ValidationResults *results) const {throw std::runtime_error("attempt to validate incomplete"); }
virtual PolyConstraint * clone() const {throw std::runtime_error("attempt to validate incomplete"); }
};
} // namespace constraints
} // namespace valijson

View File

@ -1,171 +1,97 @@
#include <gtest/gtest.h>
#include "json/json.h" // jsoncpp rolled up
#define STRINGY1(arg) #arg
#define STRINGY2(arg) STRINGY1(arg)
#define deblog(args) { \
std::cerr << __FILE__ ":" STRINGY2(__LINE__ ) ": " ; \
std::cerr << args << " "<< __PRETTY_FUNCTION__ << '\n'; \
}
#include <iostream>
#include <regex>
#include <boost/regex.hpp>
#include <valijson/adapters/jsoncpp_adapter.hpp>
#include <valijson/adapters/rapidjson_adapter.hpp>
#include <valijson/schema.hpp>
#include <valijson/schema_parser.hpp>
#include <valijson/validation_results.hpp>
#include <valijson/validator.hpp>
namespace {
using namespace valijson;
using valijson::adapters::Adapter;
using valijson::Schema;
using valijson::SchemaParser;
using valijson::Validator;
using valijson::ValidationResults;
using valijson::adapters::JsonCppAdapter;
using valijson::adapters::RapidJsonAdapter;
using namespace std;
Json::Value jsonParse(const string &jsonStr) {
Json::Value retJson;
Json::Reader json_reader;
if (jsonStr.length() != 0) {
if (!json_reader.parse(jsonStr, retJson)) {
throw runtime_error("json parse failure\n"
+ json_reader.getFormattedErrorMessages());
}
}
return retJson;
}
const static string emplrec (R"(
{
"employee" : {
"John" : {
"fullname": "John Doe"
}
},
"elist" : [
{"rec" : "John"},
{"rec" : "Jane"}
]
}
)");
const static string tschema(R"(
class StubPolyConstraint : public valijson::constraints::PolyConstraint
{
"type" : "object",
"id" : "eroot",
"properties" : {
"employee": {
"type":"object"
},
"elist" : {
"type" : "array",
"items": {
"type" : "object",
"additionalProperties": false,
"required": ["rec"],
"properties" : {
"rec" : {
"type" : "string"
}
}
}
}
},
"additionalProperties": false
}
)");
std::string err2String(ValidationResults& results) {
ValidationResults::Error error;
std::stringstream strstr;
while (results.popError(error)) {
strstr << error.description;
for (std::string& str : error.context)
strstr << str;
strstr << "\n";
}
return strstr.str();
}
using namespace constraints;
Json::Value *root; // needs to be a better way to do this.
class PathConstraint : public valijson::constraints::PolyConstraint {
const std::string path;
bool shouldValidate;
public:
PathConstraint(const std::string &path) : path(path){ }
virtual valijson::constraints::PolyConstraint * clone() const {
deblog("clone !");
return new PathConstraint(path);
};
StubPolyConstraint(bool shouldValidate)
: shouldValidate(shouldValidate) { }
virtual bool validate(const adapters::Adapter &target, const std::vector<std::string> &context, ValidationResults *results) const {
std::string spath(path + "." + target.asString());
const Json::Path jpath(spath);
const Json::Value& find(jpath.resolve(*root));
if (!find) {
std::string estring("Failed to find " + spath + " in input");
if (results) {
results->pushError(context, estring);
}
return false;
} else {
return true;
}
}
virtual Constraint * clone(CustomAlloc allocFn, CustomFree freeFn) const
virtual Constraint * cloneInto(void *ptr) const
{
void *ptr = allocFn(sizeof(PathConstraint));
if (!ptr) {
throw std::runtime_error(
"Failed to allocate memory for cloned constraint");
return new (ptr) StubPolyConstraint(shouldValidate);
}
virtual size_t sizeOf() const
{
return sizeof(StubPolyConstraint);
}
virtual bool validate(const Adapter &target,
const std::vector<std::string> &context,
ValidationResults *results) const
{
if (shouldValidate) {
return true;
}
try {
return new (ptr) PathConstraint(
*static_cast<const PathConstraint*>(this));
} catch (...) {
freeFn(ptr);
throw;
if (results) {
results->pushError(context,
"StubPolyConstraint intentionally failed validation");
}
return false;
}
};
}
} // end anonymous namespace
TEST(PolyConstraint, Insert) {
Json::Value schemaJson(jsonParse(tschema));
class TestPolyConstraint : public testing::Test
{
JsonCppAdapter schemaDocumentAdapter(schemaJson);
};
TEST_F(TestPolyConstraint, ValidationCanPass)
{
StubPolyConstraint stubPolyConstraint(true);
SchemaParser parser;
Schema schema;
parser.populateSchema(schemaDocumentAdapter, schema);
std::string empl(".employee");
PathConstraint empConstraint(empl);
// how to add this to schema?
schema.addConstraintToSubschema(stubPolyConstraint, schema.root());
Json::Value doc = jsonParse(emplrec);
root = &doc;
rapidjson::Document document;
RapidJsonAdapter adapter(document);
JsonCppAdapter targetDocumentAdapter(doc);
deblog("doc " << doc);
deblog("schema" << schemaJson);
ValidationResults results;
Validator validator;
std::regex John("Failed.*John");
std::regex Jane("Failed.*Jane");
EXPECT_FALSE(validator.validate(schema,targetDocumentAdapter, &results));
std::string err(err2String(results));
deblog("err " << err);
EXPECT_FALSE(regex_search(err, John));
EXPECT_TRUE(regex_search(err, Jane));
EXPECT_TRUE(validator.validate(schema, adapter, &results));
EXPECT_EQ(size_t(0), results.numErrors());
}
TEST_F(TestPolyConstraint, ValidationCanFail)
{
StubPolyConstraint stubPolyConstraint(false);
Schema schema;
schema.addConstraintToSubschema(stubPolyConstraint, schema.root());
rapidjson::Document document;
RapidJsonAdapter adapter(document);
ValidationResults results;
Validator validator;
EXPECT_FALSE(validator.validate(schema, adapter, &results));
EXPECT_EQ(size_t(1), results.numErrors());
ValidationResults::Error error;
EXPECT_TRUE(results.popError(error));
EXPECT_STREQ("StubPolyConstraint intentionally failed validation",
error.description.c_str());
}

View File

@ -19,6 +19,7 @@
6A725F4817F6404100D6B2FF /* test_property_tree_adapter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6AB8FEC417E92B100028E147 /* test_property_tree_adapter.cpp */; };
6A725F4917F6404100D6B2FF /* test_rapidjson_adapter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6AC18D3917CC874100FE0EC9 /* test_rapidjson_adapter.cpp */; };
6A725F4A17F6404100D6B2FF /* test_validator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6AC18D3517CC86E000FE0EC9 /* test_validator.cpp */; };
6A773AAF1CB5CA8C007D66FA /* test_poly_constraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6A773AAE1CB5CA8C007D66FA /* test_poly_constraint.cpp */; };
6A9E1856194DC44B003F1C4C /* test_fetch_document_callback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6AA8A5DA17F8BDCA002728A0 /* test_fetch_document_callback.cpp */; };
6AA822FF1C3F38D4007103A7 /* test_picojson_adapter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6AA822FE1C3F38D4007103A7 /* test_picojson_adapter.cpp */; };
6AB2D0EF1C610ACB007F0EFF /* test_nlohmann_json_adapter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6AB2D0EE1C610ACB007F0EFF /* test_nlohmann_json_adapter.cpp */; };
@ -111,6 +112,8 @@
6A725F7917F89CD500D6B2FF /* read_stream.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = read_stream.hpp; sourceTree = "<group>"; };
6A725F7A17F89CD500D6B2FF /* url.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = url.hpp; sourceTree = "<group>"; };
6A725F7F17F89CD500D6B2FF /* urdl.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = urdl.cpp; sourceTree = "<group>"; };
6A773AAD1CB5CA1F007D66FA /* Authors */ = {isa = PBXFileReference; lastKnownFileType = text; name = Authors; path = ../Authors; sourceTree = "<group>"; };
6A773AAE1CB5CA8C007D66FA /* test_poly_constraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = test_poly_constraint.cpp; sourceTree = "<group>"; };
6A8699EC17CD8641006864FA /* additionalItems.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = additionalItems.json; sourceTree = "<group>"; };
6A8699ED17CD8641006864FA /* additionalProperties.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = additionalProperties.json; sourceTree = "<group>"; };
6A8699EE17CD8641006864FA /* dependencies.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = dependencies.json; sourceTree = "<group>"; };
@ -791,6 +794,7 @@
6AB8FE9217E6BE770028E147 /* test_jsoncpp_adapter.cpp */,
6AB2D0EE1C610ACB007F0EFF /* test_nlohmann_json_adapter.cpp */,
6AA822FE1C3F38D4007103A7 /* test_picojson_adapter.cpp */,
6A773AAE1CB5CA8C007D66FA /* test_poly_constraint.cpp */,
6AB8FEC417E92B100028E147 /* test_property_tree_adapter.cpp */,
6AC18D3917CC874100FE0EC9 /* test_rapidjson_adapter.cpp */,
6A725F4317F61B5100D6B2FF /* test_validation_errors.cpp */,
@ -803,6 +807,7 @@
6AC78AC417C5FBBC00674114 = {
isa = PBXGroup;
children = (
6A773AAD1CB5CA1F007D66FA /* Authors */,
6AD030F51B03FD9900B957E5 /* LICENSE */,
6AD030F41B03FD9000B957E5 /* README.md */,
6A506D101AF884D700C2C818 /* doc */,
@ -1132,6 +1137,7 @@
6A725F4617F6404100D6B2FF /* test_adapter_comparison.cpp in Sources */,
6A725F4717F6404100D6B2FF /* test_jsoncpp_adapter.cpp in Sources */,
6A725F4817F6404100D6B2FF /* test_property_tree_adapter.cpp in Sources */,
6A773AAF1CB5CA8C007D66FA /* test_poly_constraint.cpp in Sources */,
6AD3490118FF56FB004BDEE7 /* gtest_main.cc in Sources */,
6AB2D0EF1C610ACB007F0EFF /* test_nlohmann_json_adapter.cpp in Sources */,
6A5C5D2A1C5B13D4004F40ED /* test_json11_adapter.cpp in Sources */,