diff --git a/include/valijson/constraints/concrete_constraints.hpp b/include/valijson/constraints/concrete_constraints.hpp index bc0c542..142aaa1 100644 --- a/include/valijson/constraints/concrete_constraints.hpp +++ b/include/valijson/constraints/concrete_constraints.hpp @@ -784,6 +784,40 @@ private: String pattern; }; +class PolyConstraint : public Constraint +{ +public: + virtual bool accept(ConstraintVisitor &visitor) const + { + return visitor.visit(*static_cast(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& 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 { - public: - virtual bool validate(const adapters::Adapter &target, const std::vector& 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 diff --git a/tests/test_poly_constraint.cpp b/tests/test_poly_constraint.cpp index 083b549..50c409a 100644 --- a/tests/test_poly_constraint.cpp +++ b/tests/test_poly_constraint.cpp @@ -1,171 +1,97 @@ #include -#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 -#include -#include -#include +#include #include -#include #include #include 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 &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 &context, + ValidationResults *results) const + { + if (shouldValidate) { + return true; } - try { - return new (ptr) PathConstraint( - *static_cast(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()); } diff --git a/xcode/valijson.xcodeproj/project.pbxproj b/xcode/valijson.xcodeproj/project.pbxproj index 3e77ce8..3483469 100644 --- a/xcode/valijson.xcodeproj/project.pbxproj +++ b/xcode/valijson.xcodeproj/project.pbxproj @@ -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 = ""; }; 6A725F7A17F89CD500D6B2FF /* url.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = url.hpp; sourceTree = ""; }; 6A725F7F17F89CD500D6B2FF /* urdl.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = urdl.cpp; sourceTree = ""; }; + 6A773AAD1CB5CA1F007D66FA /* Authors */ = {isa = PBXFileReference; lastKnownFileType = text; name = Authors; path = ../Authors; sourceTree = ""; }; + 6A773AAE1CB5CA8C007D66FA /* test_poly_constraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = test_poly_constraint.cpp; sourceTree = ""; }; 6A8699EC17CD8641006864FA /* additionalItems.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = additionalItems.json; sourceTree = ""; }; 6A8699ED17CD8641006864FA /* additionalProperties.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = additionalProperties.json; sourceTree = ""; }; 6A8699EE17CD8641006864FA /* dependencies.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = dependencies.json; sourceTree = ""; }; @@ -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 */,