mirror of
https://github.com/tristanpenman/valijson.git
synced 2025-03-02 20:30:11 +01:00
Flesh out API for PolyConstraint concept, simplify test cases and add new files to Xcode project
This commit is contained in:
parent
3d5526ec7b
commit
99e870ca5d
@ -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
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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 */,
|
||||
|
Loading…
x
Reference in New Issue
Block a user