From 1a59ab50dce05b204c6ab11b2519f1dfd69694f6 Mon Sep 17 00:00:00 2001 From: miloyip Date: Mon, 11 May 2015 23:53:03 +0800 Subject: [PATCH] Add invalid schema keyword --- include/rapidjson/schema.h | 192 +++++++++++++++++++++-------------- test/unittest/schematest.cpp | 163 +++++++++++++++-------------- 2 files changed, 202 insertions(+), 153 deletions(-) diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index 614789e..0011989 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -40,6 +40,12 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif +#define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\ + RAPIDJSON_MULTILINEMACRO_BEGIN\ + context.invalidKeyword = keyword;\ + return false;\ + RAPIDJSON_MULTILINEMACRO_END + RAPIDJSON_NAMESPACE_BEGIN /////////////////////////////////////////////////////////////////////////////// @@ -198,6 +204,7 @@ struct SchemaValidationContext { allocator(a), schema(s), valueSchema(), + invalidKeyword(), hasher(), patternPropertiesSchemas(), notValidator(), @@ -223,6 +230,7 @@ struct SchemaValidationContext { CrtAllocator* allocator; // For allocating memory for context const SchemaType* schema; const SchemaType* valueSchema; + const char* invalidKeyword; HasherType* hasher; SchemaValidatorArray allOfValidators; SchemaValidatorArray anyOfValidators; @@ -528,7 +536,7 @@ public: else if (additionalItems_) context.valueSchema = GetTypeless(); else - return false; + RAPIDJSON_INVALID_KEYWORD_RETURN("items"); } else context.valueSchema = GetTypeless(); @@ -554,14 +562,14 @@ public: if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) { if (!patternValid) - return false; + RAPIDJSON_INVALID_KEYWORD_RETURN("patternProperties"); } else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { if (!patternValid || !otherValid) - return false; + RAPIDJSON_INVALID_KEYWORD_RETURN("patternProperties"); } else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty) - return false; + RAPIDJSON_INVALID_KEYWORD_RETURN("patternProperties"); } if (enum_) { @@ -569,20 +577,20 @@ public: for (SizeType i = 0; i < enumCount_; i++) if (enum_[i] == h) goto foundEnum; - return false; + RAPIDJSON_INVALID_KEYWORD_RETURN("enum"); foundEnum:; } if (allOf_.schemas) for (SizeType i = 0; i < allOf_.count; i++) if (!context.allOfValidators.validators[i]->IsValid()) - return false; + RAPIDJSON_INVALID_KEYWORD_RETURN("allOf"); if (anyOf_.schemas) { for (SizeType i = 0; i < anyOf_.count; i++) if (context.anyOfValidators.validators[i]->IsValid()) goto foundAny; - return false; + RAPIDJSON_INVALID_KEYWORD_RETURN("anyOf"); foundAny:; } @@ -591,86 +599,88 @@ public: for (SizeType i = 0; i < oneOf_.count; i++) if (context.oneOfValidators.validators[i]->IsValid()) { if (oneValid) - return false; + RAPIDJSON_INVALID_KEYWORD_RETURN("oneOf"); else oneValid = true; } if (!oneValid) - return false; + RAPIDJSON_INVALID_KEYWORD_RETURN("oneOf"); } if (not_ && context.notValidator->IsValid()) - return false; + RAPIDJSON_INVALID_KEYWORD_RETURN("not"); - return !ref_ || context.refValidator->IsValid(); + if (ref_ && !context.refValidator->IsValid()) + RAPIDJSON_INVALID_KEYWORD_RETURN("$ref"); + + return true; } bool Null(Context& context) const { - CreateParallelValidator(context); - return (type_ & (1 << kNullSchemaType)); + if (!(type_ & (1 << kNullSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN("type"); + return CreateParallelValidator(context); } bool Bool(Context& context, bool) const { - CreateParallelValidator(context); - return (type_ & (1 << kBooleanSchemaType)); + if (!(type_ & (1 << kBooleanSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN("type"); + return CreateParallelValidator(context); } bool Int(Context& context, int i) const { - CreateParallelValidator(context); - if ((type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))) == 0) - return false; - return CheckDouble(i); + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) + RAPIDJSON_INVALID_KEYWORD_RETURN("type"); + return CheckDouble(context, i) && CreateParallelValidator(context); } bool Uint(Context& context, unsigned u) const { - CreateParallelValidator(context); - if ((type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))) == 0) - return false; - return CheckDouble(u); + if ((type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) + RAPIDJSON_INVALID_KEYWORD_RETURN("type"); + return CheckDouble(context, u) && CreateParallelValidator(context); } bool Int64(Context& context, int64_t i) const { - CreateParallelValidator(context); - if ((type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))) == 0) - return false; - return CheckDouble(i); + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) + RAPIDJSON_INVALID_KEYWORD_RETURN("type"); + return CheckDouble(context, i) && CreateParallelValidator(context); } bool Uint64(Context& context, uint64_t u) const { - CreateParallelValidator(context); - if ((type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))) == 0) - return false; - return CheckDouble(u); + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) + RAPIDJSON_INVALID_KEYWORD_RETURN("type"); + return CheckDouble(context, u) && CreateParallelValidator(context); } bool Double(Context& context, double d) const { - CreateParallelValidator(context); - if ((type_ & (1 << kNumberSchemaType)) == 0) - return false; - return CheckDouble(d); + if (!(type_ & (1 << kNumberSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN("type"); + return CheckDouble(context, d) && CreateParallelValidator(context); } bool String(Context& context, const Ch* str, SizeType length, bool) const { - (void)str; - CreateParallelValidator(context); - if ((type_ & (1 << kStringSchemaType)) == 0) - return false; + if (!(type_ & (1 << kStringSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN("type"); - //if (length < minLength_ || length > maxLength_) - // return false; if (minLength_ != 0 || maxLength_ != SizeType(~0)) { SizeType count; - if (internal::CountStringCodePoint(str, length, &count) && (count < minLength_ || count > maxLength_)) - return false; + if (internal::CountStringCodePoint(str, length, &count)) { + if (count < minLength_) + RAPIDJSON_INVALID_KEYWORD_RETURN("minLength"); + if (count > maxLength_) + RAPIDJSON_INVALID_KEYWORD_RETURN("maxLength"); + } } - return !pattern_ || IsPatternMatch(pattern_, str, length); + if (pattern_ && !IsPatternMatch(pattern_, str, length)) + RAPIDJSON_INVALID_KEYWORD_RETURN("pattern"); + + return CreateParallelValidator(context); } bool StartObject(Context& context) const { - CreateParallelValidator(context); - if ((type_ & (1 << kObjectSchemaType)) == 0) - return false; + if (!(type_ & (1 << kObjectSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN("type"); context.objectRequiredCount = 0; if (hasDependencies_) { @@ -685,7 +695,7 @@ public: std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count); } - return true; + return CreateParallelValidator(context); } bool Key(Context& context, const Ch* str, SizeType len, bool) const { @@ -731,12 +741,21 @@ public: return true; } - return context.patternPropertiesSchemaCount != 0; // patternProperties are not additional properties + if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties + RAPIDJSON_INVALID_KEYWORD_RETURN("additionalProperties"); + + return true; } bool EndObject(Context& context, SizeType memberCount) const { - if (context.objectRequiredCount != requiredCount_ || memberCount < minProperties_ || memberCount > maxProperties_) - return false; + if (context.objectRequiredCount != requiredCount_) + RAPIDJSON_INVALID_KEYWORD_RETURN("required"); + + if (memberCount < minProperties_) + RAPIDJSON_INVALID_KEYWORD_RETURN("minProperties"); + + if (memberCount > maxProperties_) + RAPIDJSON_INVALID_KEYWORD_RETURN("maxProperties"); if (hasDependencies_) { for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) @@ -744,11 +763,11 @@ public: if (properties_[sourceIndex].dependencies) { for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) if (properties_[sourceIndex].dependencies[targetIndex] && !context.objectDependencies[targetIndex]) - return false; + RAPIDJSON_INVALID_KEYWORD_RETURN("dependencies"); } else if (properties_[sourceIndex].dependenciesSchema) if (!context.dependencyValidators.validators[sourceIndex]->IsValid()) - return false; + RAPIDJSON_INVALID_KEYWORD_RETURN("dependencies"); } } @@ -756,21 +775,28 @@ public: } bool StartArray(Context& context) const { - CreateParallelValidator(context); - if ((type_ & (1 << kArraySchemaType)) == 0) - return false; + if (!(type_ & (1 << kArraySchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN("type"); - if (uniqueItems_) + if (uniqueItems_) context.arrayElementHashCodes.SetArray(); context.arrayElementIndex = 0; context.inArray = true; - return true; + + return CreateParallelValidator(context); } bool EndArray(Context& context, SizeType elementCount) const { context.inArray = false; - return elementCount >= minItems_ && elementCount <= maxItems_; + + if (elementCount < minItems_) + RAPIDJSON_INVALID_KEYWORD_RETURN("minItems"); + + if (elementCount > maxItems_) + RAPIDJSON_INVALID_KEYWORD_RETURN("maxItems"); + + return true; } private: @@ -879,14 +905,22 @@ private: else if (type == "number" ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType); } - void CreateParallelValidator(Context& context) const { + bool CreateParallelValidator(Context& context) const { if (enum_ || context.arrayUniqueness) context.hasher = new HasherType; - if (allOf_.schemas) CreateSchemaValidators(context, context.allOfValidators, allOf_); - if (anyOf_.schemas) CreateSchemaValidators(context, context.anyOfValidators, anyOf_); - if (oneOf_.schemas) CreateSchemaValidators(context, context.oneOfValidators, oneOf_); + + if (allOf_.schemas) + CreateSchemaValidators(context, context.allOfValidators, allOf_); + + if (anyOf_.schemas) + CreateSchemaValidators(context, context.anyOfValidators, anyOf_); + + if (oneOf_.schemas) + CreateSchemaValidators(context, context.oneOfValidators, oneOf_); + if (not_ && !context.notValidator) context.notValidator = context.factory->CreateSchemaValidator(*not_); + if (ref_ && !context.refValidator) context.refValidator = context.factory->CreateSchemaValidator(*ref_); @@ -896,6 +930,7 @@ private: for (SizeType i = 0; i < propertyCount_; i++) context.dependencyValidators.validators[i] = properties_[i].dependenciesSchema ? context.factory->CreateSchemaValidator(*properties_[i].dependenciesSchema) : 0; } + return true; } void CreateSchemaValidators(Context& context, typename Context::SchemaValidatorArray& validators, const SchemaArray& schemas) const { @@ -928,15 +963,19 @@ private: return false; } - bool CheckDouble(double d) const { - if (exclusiveMinimum_ ? d <= minimum_ : d < minimum_) return false; - if (exclusiveMaximum_ ? d >= maximum_ : d > maximum_) return false; + bool CheckDouble(Context& context, double d) const { + if (exclusiveMinimum_ ? d <= minimum_ : d < minimum_) + RAPIDJSON_INVALID_KEYWORD_RETURN("minimum"); + + if (exclusiveMaximum_ ? d >= maximum_ : d > maximum_) + RAPIDJSON_INVALID_KEYWORD_RETURN("maximum"); + if (hasMultipleOf_) { double a = std::abs(d), b = std::abs(multipleOf_); double q = std::floor(a / b); double r = a - q * b; if (r > 0.0) - return false; + RAPIDJSON_INVALID_KEYWORD_RETURN("multipleOf"); } return true; } @@ -1222,6 +1261,10 @@ public: return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema()); } + const char* GetInvalidSchemaKeyword() const { + return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword; + } + PointerType GetInvalidDocumentPointer() const { return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); } @@ -1370,12 +1413,6 @@ private: if (!CurrentSchema().EndValue(CurrentContext())) return false; - // *documentStack_.template Push() = '\0'; - // documentStack_.template Pop(1); - // printf("document: %s\n", documentStack_.template Bottom()); - while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/') - ; - uint64_t h = CurrentContext().arrayUniqueness ? CurrentContext().hasher->GetHashCode() : 0; PopSchema(); @@ -1385,11 +1422,17 @@ private: if (context.valueUniqueness) { for (typename Context::HashCodeArray::ConstValueIterator itr = context.arrayElementHashCodes.Begin(); itr != context.arrayElementHashCodes.End(); ++itr) if (itr->GetUint64() == h) - return false; + RAPIDJSON_INVALID_KEYWORD_RETURN("uniqueItems"); context.arrayElementHashCodes.PushBack(h, *context.allocator); } } + // *documentStack_.template Push() = '\0'; + // documentStack_.template Pop(1); + // printf("document: %s\n", documentStack_.template Bottom()); + while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/') + ; + return true; } @@ -1421,6 +1464,7 @@ private: void PopSchema() { schemaStack_.template Pop(1)->~Context(); } const SchemaType& CurrentSchema() const { return *schemaStack_.template Top()->schema; } Context& CurrentContext() { return *schemaStack_.template Top(); } + const Context& CurrentContext() const { return *schemaStack_.template Top(); } static const size_t kDefaultSchemaStackCapacity = 1024; static const size_t kDefaultDocumentStackCapacity = 256; diff --git a/test/unittest/schematest.cpp b/test/unittest/schematest.cpp index 1346460..35662f3 100644 --- a/test/unittest/schematest.cpp +++ b/test/unittest/schematest.cpp @@ -99,17 +99,18 @@ TEST(SchemaValidator, Hasher) { EXPECT_FALSE(d.HasParseError());\ EXPECT_TRUE(expected == d.Accept(validator));\ EXPECT_TRUE(expected == validator.IsValid());\ - /*if (!validator.IsValid()) {\ + if (expected && !validator.IsValid()) {\ StringBuffer sb;\ validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);\ - printf("Error schema: %s\n", sb.GetString());\ + printf("Invalid schema: %s\n", sb.GetString());\ + printf("Invalid keyword: %s\n", validator.GetInvalidSchemaKeyword());\ sb.Clear();\ validator.GetInvalidDocumentPointer().StringifyUriFragment(sb);\ - printf("Error document: %s\n", sb.GetString());\ - }*/\ + printf("Invalid document: %s\n", sb.GetString());\ + }\ } -#define VALIDATE_ERROR(schema, json, invalidSchemaPointer, invalidDocumentPointer) \ +#define INVALIDATE(schema, json, invalidSchemaPointer, invalidSchemaKeyword, invalidDocumentPointer) \ {\ SchemaValidator validator(schema);\ Document d;\ @@ -124,6 +125,10 @@ TEST(SchemaValidator, Hasher) { printf("GetInvalidSchemaPointer() Expected: %s Actual: %s\n", invalidSchemaPointer, sb.GetString());\ ADD_FAILURE();\ }\ + if (strcmp(validator.GetInvalidSchemaKeyword(), invalidSchemaKeyword) != 0) {\ + printf("GetInvalidSchemaKeyword() Expected: %s Actual %s\n", invalidSchemaKeyword, validator.GetInvalidSchemaKeyword());\ + ADD_FAILURE();\ + }\ if (validator.GetInvalidDocumentPointer() != Pointer(invalidDocumentPointer)) {\ StringBuffer sb;\ validator.GetInvalidDocumentPointer().Stringify(sb);\ @@ -149,7 +154,7 @@ TEST(SchemaValidator, MultiType) { VALIDATE(s, "42", true); VALIDATE(s, "\"Life, the universe, and everything\"", true); - VALIDATE_ERROR(s, "[\"Life\", \"the universe\", \"and everything\"]", "", ""); + INVALIDATE(s, "[\"Life\", \"the universe\", \"and everything\"]", "", "type", ""); } TEST(SchemaValidator, Enum_Typed) { @@ -158,7 +163,7 @@ TEST(SchemaValidator, Enum_Typed) { SchemaDocument s(sd); VALIDATE(s, "\"red\"", true); - VALIDATE(s, "\"blue\"", false); + INVALIDATE(s, "\"blue\"", "", "enum", ""); } TEST(SchemaValidator, Enum_Typless) { @@ -169,7 +174,7 @@ TEST(SchemaValidator, Enum_Typless) { VALIDATE(s, "\"red\"", true); VALIDATE(s, "null", true); VALIDATE(s, "42", true); - VALIDATE(s, "0", false); + INVALIDATE(s, "0", "", "enum", ""); } TEST(SchemaValidator, Enum_InvalidType) { @@ -178,7 +183,7 @@ TEST(SchemaValidator, Enum_InvalidType) { SchemaDocument s(sd); VALIDATE(s, "\"red\"", true); - VALIDATE(s, "null", false); + INVALIDATE(s, "null", "", "type", ""); } TEST(SchemaValidator, AllOf) { @@ -188,7 +193,7 @@ TEST(SchemaValidator, AllOf) { SchemaDocument s(sd); VALIDATE(s, "\"ok\"", true); - VALIDATE(s, "\"too long\"", false); + INVALIDATE(s, "\"too long\"", "", "allOf", ""); } { Document sd; @@ -196,7 +201,7 @@ TEST(SchemaValidator, AllOf) { SchemaDocument s(sd); VALIDATE(s, "\"No way\"", false); - VALIDATE(s, "-1", false); + INVALIDATE(s, "-1", "", "allOf", ""); } } @@ -205,9 +210,9 @@ TEST(SchemaValidator, AnyOf) { sd.Parse("{\"anyOf\": [{ \"type\": \"string\" }, { \"type\": \"number\" } ] }"); SchemaDocument s(sd); - //VALIDATE(s, "\"Yes\"", true); - //VALIDATE(s, "42", true); - VALIDATE(s, "{ \"Not a\": \"string or number\" }", false); + VALIDATE(s, "\"Yes\"", true); + VALIDATE(s, "42", true); + INVALIDATE(s, "{ \"Not a\": \"string or number\" }", "", "anyOf", ""); } TEST(SchemaValidator, OneOf) { @@ -217,8 +222,8 @@ TEST(SchemaValidator, OneOf) { VALIDATE(s, "10", true); VALIDATE(s, "9", true); - VALIDATE(s, "2", false); - VALIDATE(s, "15", false); + INVALIDATE(s, "2", "", "oneOf", ""); + INVALIDATE(s, "15", "", "oneOf", ""); } TEST(SchemaValidator, Not) { @@ -227,8 +232,8 @@ TEST(SchemaValidator, Not) { SchemaDocument s(sd); VALIDATE(s, "42", true); - VALIDATE(s, "{ \"key\": \"value\" }", true); // TO FIX - VALIDATE(s, "\"I am a string\"", false); + VALIDATE(s, "{ \"key\": \"value\" }", true); + INVALIDATE(s, "\"I am a string\"", "", "not", ""); } TEST(SchemaValidator, Ref) { @@ -292,7 +297,7 @@ TEST(SchemaValidator, Ref_AllOf) { "}"); SchemaDocument s(sd); - VALIDATE_ERROR(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\"} }", "/properties/shipping_address", "/shipping_address"); + INVALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\"} }", "/properties/shipping_address", "allOf", "/shipping_address"); VALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\", \"type\": \"business\"} }", true); } @@ -302,7 +307,7 @@ TEST(SchemaValidator, String) { SchemaDocument s(sd); VALIDATE(s, "\"I'm a string\"", true); - VALIDATE(s, "42", false); + INVALIDATE(s, "42", "", "type", ""); } TEST(SchemaValidator, String_LengthRange) { @@ -310,10 +315,10 @@ TEST(SchemaValidator, String_LengthRange) { sd.Parse("{\"type\":\"string\",\"minLength\":2,\"maxLength\":3}"); SchemaDocument s(sd); - VALIDATE(s, "\"A\"", false); + INVALIDATE(s, "\"A\"", "", "minLength", ""); VALIDATE(s, "\"AB\"", true); VALIDATE(s, "\"ABC\"", true); - VALIDATE(s, "\"ABCD\"", false); + INVALIDATE(s, "\"ABCD\"", "", "maxLength", ""); } #if RAPIDJSON_SCHEMA_HAS_REGEX @@ -324,8 +329,8 @@ TEST(SchemaValidator, String_Pattern) { VALIDATE(s, "\"555-1212\"", true); VALIDATE(s, "\"(888)555-1212\"", true); - VALIDATE(s, "\"(888)555-1212 ext. 532\"", false); - VALIDATE(s, "\"(800)FLOWERS\"", false); + INVALIDATE(s, "\"(888)555-1212 ext. 532\"", "", "pattern", ""); + INVALIDATE(s, "\"(800)FLOWERS\"", "", "pattern", ""); } #endif @@ -336,8 +341,8 @@ TEST(SchemaValidator, Integer) { VALIDATE(s, "42", true); VALIDATE(s, "-1", true); - VALIDATE(s, "3.1415926", false); - VALIDATE(s, "\"42\"", false); + INVALIDATE(s, "3.1415926", "", "type", ""); + INVALIDATE(s, "\"42\"", "", "type", ""); } TEST(SchemaValidator, Integer_Range) { @@ -345,12 +350,12 @@ TEST(SchemaValidator, Integer_Range) { sd.Parse("{\"type\":\"integer\",\"minimum\":0,\"maximum\":100,\"exclusiveMaximum\":true}"); SchemaDocument s(sd); - VALIDATE(s, "-1", false); + INVALIDATE(s, "-1", "", "minimum", ""); VALIDATE(s, "0", true); VALIDATE(s, "10", true); VALIDATE(s, "99", true); - VALIDATE(s, "100", false); - VALIDATE(s, "101", false); + INVALIDATE(s, "100", "", "maximum", ""); + INVALIDATE(s, "101", "", "maximum", ""); } TEST(SchemaValidator, Integer_MultipleOf) { @@ -361,7 +366,7 @@ TEST(SchemaValidator, Integer_MultipleOf) { VALIDATE(s, "0", true); VALIDATE(s, "10", true); VALIDATE(s, "20", true); - VALIDATE(s, "23", false); + INVALIDATE(s, "23", "", "multipleOf", ""); } TEST(SchemaValidator, Number_Range) { @@ -369,12 +374,12 @@ TEST(SchemaValidator, Number_Range) { sd.Parse("{\"type\":\"number\",\"minimum\":0,\"maximum\":100,\"exclusiveMaximum\":true}"); SchemaDocument s(sd); - VALIDATE(s, "-1", false); + INVALIDATE(s, "-1", "", "minimum", ""); VALIDATE(s, "0", true); VALIDATE(s, "10", true); VALIDATE(s, "99", true); - VALIDATE(s, "100", false); - VALIDATE(s, "101", false); + INVALIDATE(s, "100", "", "maximum", ""); + INVALIDATE(s, "101", "", "maximum", ""); } TEST(SchemaValidator, Number_MultipleOf) { @@ -385,7 +390,7 @@ TEST(SchemaValidator, Number_MultipleOf) { VALIDATE(s, "0", true); VALIDATE(s, "10", true); VALIDATE(s, "20", true); - VALIDATE(s, "23", false); + INVALIDATE(s, "23", "", "multipleOf", ""); } TEST(SchemaValidator, Number_MultipleOfOne) { @@ -395,7 +400,7 @@ TEST(SchemaValidator, Number_MultipleOfOne) { VALIDATE(s, "42", true); VALIDATE(s, "42.0", true); - VALIDATE(s, "3.1415926", false); + INVALIDATE(s, "3.1415926", "", "multipleOf", ""); } TEST(SchemaValidator, Object) { @@ -405,8 +410,8 @@ TEST(SchemaValidator, Object) { VALIDATE(s, "{\"key\":\"value\",\"another_key\":\"another_value\"}", true); VALIDATE(s, "{\"Sun\":1.9891e30,\"Jupiter\":1.8986e27,\"Saturn\":5.6846e26,\"Neptune\":10.243e25,\"Uranus\":8.6810e25,\"Earth\":5.9736e24,\"Venus\":4.8685e24,\"Mars\":6.4185e23,\"Mercury\":3.3022e23,\"Moon\":7.349e22,\"Pluto\":1.25e22}", true); - VALIDATE(s, "[\"An\", \"array\", \"not\", \"an\", \"object\"]", false); - VALIDATE(s, "\"Not an object\"", false); + INVALIDATE(s, "[\"An\", \"array\", \"not\", \"an\", \"object\"]", "", "type", ""); + INVALIDATE(s, "\"Not an object\"", "", "type", ""); } TEST(SchemaValidator, Object_Properties) { @@ -424,7 +429,7 @@ TEST(SchemaValidator, Object_Properties) { SchemaDocument s(sd); VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", true); - VALIDATE(s, "{ \"number\": \"1600\", \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", false); + INVALIDATE(s, "{ \"number\": \"1600\", \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", "/properties/number", "type", "/number"); VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\" }", true); VALIDATE(s, "{}", true); VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"direction\": \"NW\" }", true); @@ -448,7 +453,7 @@ TEST(SchemaValidator, Object_AdditionalPropertiesBoolean) { SchemaDocument s(sd); VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", true); - VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"direction\": \"NW\" }", false); + INVALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"direction\": \"NW\" }", "", "additionalProperties", "/direction"); } TEST(SchemaValidator, Object_AdditionalPropertiesObject) { @@ -456,10 +461,10 @@ TEST(SchemaValidator, Object_AdditionalPropertiesObject) { sd.Parse( "{" " \"type\": \"object\"," - " \"properties\" : {" + " \"properties\" : {" " \"number\": { \"type\": \"number\" }," - " \"street_name\" : { \"type\": \"string\" }," - " \"street_type\" : { \"type\": \"string\"," + " \"street_name\" : { \"type\": \"string\" }," + " \"street_type\" : { \"type\": \"string\"," " \"enum\" : [\"Street\", \"Avenue\", \"Boulevard\"]" " }" " }," @@ -469,7 +474,7 @@ TEST(SchemaValidator, Object_AdditionalPropertiesObject) { VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", true); VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"direction\": \"NW\" }", true); - VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"office_number\": 201 }", false); + INVALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"office_number\": 201 }", "/additionalProperties", "type", "/office_number"); } TEST(SchemaValidator, Object_Required) { @@ -489,7 +494,7 @@ TEST(SchemaValidator, Object_Required) { VALIDATE(s, "{ \"name\": \"William Shakespeare\", \"email\" : \"bill@stratford-upon-avon.co.uk\" }", true); VALIDATE(s, "{ \"name\": \"William Shakespeare\", \"email\" : \"bill@stratford-upon-avon.co.uk\", \"address\" : \"Henley Street, Stratford-upon-Avon, Warwickshire, England\", \"authorship\" : \"in question\"}", true); - VALIDATE(s, "{ \"name\": \"William Shakespeare\", \"address\" : \"Henley Street, Stratford-upon-Avon, Warwickshire, England\" }", false); + INVALIDATE(s, "{ \"name\": \"William Shakespeare\", \"address\" : \"Henley Street, Stratford-upon-Avon, Warwickshire, England\" }", "", "required", ""); } @@ -498,11 +503,11 @@ TEST(SchemaValidator, Object_PropertiesRange) { sd.Parse("{\"type\":\"object\", \"minProperties\":2, \"maxProperties\":3}"); SchemaDocument s(sd); - VALIDATE(s, "{}", false); - VALIDATE(s, "{\"a\":0}", false); + INVALIDATE(s, "{}", "", "minProperties", ""); + INVALIDATE(s, "{\"a\":0}", "", "minProperties", ""); VALIDATE(s, "{\"a\":0,\"b\":1}", true); VALIDATE(s, "{\"a\":0,\"b\":1,\"c\":2}", true); - VALIDATE(s, "{\"a\":0,\"b\":1,\"c\":2,\"d\":3}", false); + INVALIDATE(s, "{\"a\":0,\"b\":1,\"c\":2,\"d\":3}", "", "maxProperties", ""); } TEST(SchemaValidator, Object_PropertyDependencies) { @@ -522,8 +527,8 @@ TEST(SchemaValidator, Object_PropertyDependencies) { "}"); SchemaDocument s(sd); - VALIDATE(s, "{ \"name\": \"John Doe\", \"credit_card\": 5555555555555555, \"billing_address\": \"555 Debtor's Lane\" }", true); - VALIDATE(s, "{ \"name\": \"John Doe\", \"credit_card\": 5555555555555555 }", false); + VALIDATE(s, "{ \"name\": \"John Doe\", \"credit_card\": 5555555555555555, \"billing_address\": \"555 Debtor's Lane\" }", true); + INVALIDATE(s, "{ \"name\": \"John Doe\", \"credit_card\": 5555555555555555 }", "", "dependencies", ""); VALIDATE(s, "{ \"name\": \"John Doe\"}", true); VALIDATE(s, "{ \"name\": \"John Doe\", \"billing_address\": \"555 Debtor's Lane\" }", true); } @@ -534,11 +539,11 @@ TEST(SchemaValidator, Object_SchemaDependencies) { "{" " \"type\": \"object\"," " \"properties\" : {" - " \"name\": { \"type\": \"string\" }," + " \"name\": { \"type\": \"string\" }," " \"credit_card\" : { \"type\": \"number\" }" " }," " \"required\" : [\"name\"]," - " \"dependencies\" : {" + " \"dependencies\" : {" " \"credit_card\": {" " \"properties\": {" " \"billing_address\": { \"type\": \"string\" }" @@ -549,8 +554,8 @@ TEST(SchemaValidator, Object_SchemaDependencies) { "}"); SchemaDocument s(sd); - //VALIDATE(s, "{\"name\": \"John Doe\", \"credit_card\" : 5555555555555555,\"billing_address\" : \"555 Debtor's Lane\"}", true); - VALIDATE(s, "{\"name\": \"John Doe\", \"credit_card\" : 5555555555555555 }", false); + VALIDATE(s, "{\"name\": \"John Doe\", \"credit_card\" : 5555555555555555,\"billing_address\" : \"555 Debtor's Lane\"}", true); + INVALIDATE(s, "{\"name\": \"John Doe\", \"credit_card\" : 5555555555555555 }", "", "dependencies", ""); VALIDATE(s, "{\"name\": \"John Doe\", \"billing_address\" : \"555 Debtor's Lane\"}", true); } @@ -569,8 +574,8 @@ TEST(SchemaValidator, Object_PatternProperties) { VALIDATE(s, "{ \"S_25\": \"This is a string\" }", true); VALIDATE(s, "{ \"I_0\": 42 }", true); - VALIDATE(s, "{ \"S_0\": 42 }", false); - VALIDATE(s, "{ \"I_42\": \"This is a string\" }", false); + INVALIDATE(s, "{ \"S_0\": 42 }", "", "patternProperties", "/S_0"); + INVALIDATE(s, "{ \"I_42\": \"This is a string\" }", "", "patternProperties", "/I_42"); VALIDATE(s, "{ \"keyword\": \"value\" }", true); } @@ -592,7 +597,7 @@ TEST(SchemaValidator, Object_PatternProperties_AdditionalProperties) { VALIDATE(s, "{ \"builtin\": 42 }", true); VALIDATE(s, "{ \"keyword\": \"value\" }", true); - VALIDATE(s, "{ \"keyword\": 42 }", false); + INVALIDATE(s, "{ \"keyword\": 42 }", "/additionalProperties", "type", "/keyword"); } #endif @@ -603,7 +608,7 @@ TEST(SchemaValidator, Array) { VALIDATE(s, "[1, 2, 3, 4, 5]", true); VALIDATE(s, "[3, \"different\", { \"types\" : \"of values\" }]", true); - VALIDATE(s, "{\"Not\": \"an array\"}", false); + INVALIDATE(s, "{\"Not\": \"an array\"}", "", "type", ""); } TEST(SchemaValidator, Array_ItemsList) { @@ -618,7 +623,7 @@ TEST(SchemaValidator, Array_ItemsList) { SchemaDocument s(sd); VALIDATE(s, "[1, 2, 3, 4, 5]", true); - VALIDATE(s, "[1, 2, \"3\", 4, 5]", false); + INVALIDATE(s, "[1, 2, \"3\", 4, 5]", "", "type", "/2"); VALIDATE(s, "[]", true); } @@ -647,8 +652,8 @@ TEST(SchemaValidator, Array_ItemsTuple) { SchemaDocument s(sd); VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\"]", true); - VALIDATE(s, "[24, \"Sussex\", \"Drive\"]", false); - VALIDATE(s, "[\"Palais de l'Elysee\"]", false); + INVALIDATE(s, "[24, \"Sussex\", \"Drive\"]", "/items/2", "enum", "/2"); + INVALIDATE(s, "[\"Palais de l'Elysee\"]", "/items/0", "type", "/0"); VALIDATE(s, "[10, \"Downing\", \"Street\"]", true); VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\", \"Washington\"]", true); } @@ -680,7 +685,7 @@ TEST(SchemaValidator, Array_AdditionalItmes) { VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\"]", true); VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\"]", true); - VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\", \"Washington\"]", false); + INVALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\", \"Washington\"]", "", "items", "/4"); } TEST(SchemaValidator, Array_ItemsRange) { @@ -688,11 +693,11 @@ TEST(SchemaValidator, Array_ItemsRange) { sd.Parse("{\"type\": \"array\",\"minItems\": 2,\"maxItems\" : 3}"); SchemaDocument s(sd); - VALIDATE(s, "[]", false); - VALIDATE(s, "[1]", false); + INVALIDATE(s, "[]", "", "minItems", ""); + INVALIDATE(s, "[1]", "", "minItems", ""); VALIDATE(s, "[1, 2]", true); VALIDATE(s, "[1, 2, 3]", true); - VALIDATE(s, "[1, 2, 3, 4]", false); + INVALIDATE(s, "[1, 2, 3, 4]", "", "maxItems", ""); } TEST(SchemaValidator, Array_UniqueItems) { @@ -701,7 +706,7 @@ TEST(SchemaValidator, Array_UniqueItems) { SchemaDocument s(sd); VALIDATE(s, "[1, 2, 3, 4, 5]", true); - VALIDATE(s, "[1, 2, 3, 3, 4]", false); + INVALIDATE(s, "[1, 2, 3, 3, 4]", "", "uniqueItems", "/3"); VALIDATE(s, "[]", true); } @@ -712,8 +717,8 @@ TEST(SchemaValidator, Boolean) { VALIDATE(s, "true", true); VALIDATE(s, "false", true); - VALIDATE(s, "\"true\"", false); - VALIDATE(s, "0", false); + INVALIDATE(s, "\"true\"", "", "type", ""); + INVALIDATE(s, "0", "", "type", ""); } TEST(SchemaValidator, Null) { @@ -722,9 +727,9 @@ TEST(SchemaValidator, Null) { SchemaDocument s(sd); VALIDATE(s, "null", true); - VALIDATE(s, "false", false); - VALIDATE(s, "0", false); - VALIDATE(s, "\"\"", false); + INVALIDATE(s, "false", "", "type", ""); + INVALIDATE(s, "0", "", "type", ""); + INVALIDATE(s, "\"\"", "", "type", ""); } // Additional tests @@ -735,8 +740,8 @@ TEST(SchemaValidator, ObjectInArray) { SchemaDocument s(sd); VALIDATE(s, "[\"a\"]", true); - VALIDATE(s, "[1]", false); - VALIDATE(s, "[{}]", false); + INVALIDATE(s, "[1]", "", "type", "/0"); + INVALIDATE(s, "[{}]", "", "type", "/0"); } TEST(SchemaValidator, MultiTypeInObject) { @@ -754,7 +759,7 @@ TEST(SchemaValidator, MultiTypeInObject) { VALIDATE(s, "{ \"tel\": 999 }", true); VALIDATE(s, "{ \"tel\": \"123-456\" }", true); - VALIDATE(s, "{ \"tel\": true }", false); + INVALIDATE(s, "{ \"tel\": true }", "/properties/tel", "type", "/tel"); } TEST(SchemaValidator, MultiTypeWithObject) { @@ -772,7 +777,7 @@ TEST(SchemaValidator, MultiTypeWithObject) { VALIDATE(s, "\"Hello\"", true); VALIDATE(s, "{ \"tel\": 999 }", true); - VALIDATE(s, "{ \"tel\": \"fail\" }", false); + INVALIDATE(s, "{ \"tel\": \"fail\" }", "/properties/tel", "type", "/tel"); } TEST(SchemaValidator, AllOf_Nested) { @@ -789,11 +794,11 @@ TEST(SchemaValidator, AllOf_Nested) { VALIDATE(s, "\"ok\"", true); VALIDATE(s, "\"OK\"", true); - VALIDATE(s, "\"okay\"", false); - VALIDATE(s, "\"o\"", false); - VALIDATE(s, "\"n\"", false); - VALIDATE(s, "\"too long\"", false); - VALIDATE(s, "123", false); + INVALIDATE(s, "\"okay\"", "", "allOf", ""); + INVALIDATE(s, "\"o\"", "", "allOf", ""); + INVALIDATE(s, "\"n\"", "", "allOf", ""); + INVALIDATE(s, "\"too long\"", "", "allOf", ""); + INVALIDATE(s, "123", "", "allOf", ""); } static char* ReadFile(const char* filename, size_t& length) {